12

I'm on Spring boot 1.4.x branch and Spring Data MongoDB.

I want to extend a Pojo from HashMap to give it the possibility to save new properties dynamically.

I know I can create a Map<String, Object> properties in the Entry class to save inside it my dynamics values but I don't want to have an inner structure. My goal is to have all fields at the root's entry class to serialize it like that:

{
   "id":"12334234234",
   "dynamicField1": "dynamicValue1",
   "dynamicField2": "dynamicValue2"
}

So I created this Entry class:

@Document
public class Entry extends HashMap<String, Object> {

    @Id
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

And the repository like this:

public interface EntryRepository extends MongoRepository<Entry, String> {
}

When I launch my app I have this error:

Error creating bean with name 'entryRepository': Invocation of init method failed; nested exception is org.springframework.data.mapping.model.MappingException: Could not lookup mapping metadata for domain class java.util.HashMap!

Any idea?

3
  • Franck - having answered your question, please acknowledge by marking the answer as correct if it helped your understanding :) Commented Sep 28, 2017 at 11:16
  • Have you tried extending BasicDBObject ? Something like public class Entry extends BasicDBObject Commented Sep 28, 2017 at 13:29
  • Yep same error. Commented Sep 28, 2017 at 14:56

2 Answers 2

19

TL; DR;

  1. Do not use Java collection/map types as a base class for your entities.
  2. Repositories are not the right tool for your requirement.
  3. Use DBObject with MongoTemplate if you need dynamic top-level properties.

Explanation

Spring Data Repositories are repositories in the DDD sense acting as persistence gateway for your well-defined aggregates. They inspect domain classes to derive the appropriate queries. Spring Data excludes collection and map types from entity analysis, and that's why extending your entity from a Map fails.

Repository query methods for dynamic properties are possible, but it's not the primary use case. You would have to use SpEL queries to express your query:

public interface EntryRepository extends MongoRepository<Entry, String> {

    @Query("{ ?0 : ?1 }")
    Entry findByDynamicField(String field, Object value);
}

This method does not give you any type safety regarding the predicate value and only an ugly alias for a proper, individual query.

Rather use DBObject with MongoTemplate and its query methods directly:

List<DBObject> result = template.find(new Query(Criteria.where("your_dynamic_field")
                                          .is(theQueryValue)), DBObject.class);

DBObject is a Map that gives you full access to properties without enforcing a pre-defined structure. You can create, read, update and delete DBObjects objects via the Template API.

A last thing

You can declare dynamic properties on a nested level using a Map, if your aggregate root declares some static properties:

@Document
public class Data {

    @Id
    private String id;
    private Map<String, Object> details;
}
Sign up to request clarification or add additional context in comments.

1 Comment

It's a nice explanation so I upvote. Finally, I have made a Map in my POJO and serializer/deserializer in jackson to put details content at the root level.
3

Here we can achieve using JSONObject

The entity will be like this

@Document
public class Data {
    @Id
    private String id;
    private JSONObject details;
    //getters and setters
}

The POJO will be like this

public class DataDTO {
    private String id;
    private JSONObject details;
        //getters and setters
}

In service

Data formData = new Data();
JSONObject details = dataDTO.getDetails();
details.put("dynamicField1", "dynamicValue1");
details.put("dynamicField2", "dynamicValue2");
formData.setDetails(details);
mongoTemplate.save(formData );

i have done as per my business,refer this code and do it yours. Is this helpful?

3 Comments

If I do that, is it always possible to query fields ?$
yeah,you don't have the inner structure also(if you use above code format). So easily you can query the fields.
@jai - How to retrieve the values back and map it to a pojo?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.