I have a class which it's only field is an HashMap.
Is it possible to define the #Id of the class to be one of the keys of the HashMap? (which always exists there)
Thanks!
If you have a Class that contains only an HashMap don't define a Class because it not make any sense, instead convert your query result directly in an Map likes Map<String, Object> dbCursor = mongoTemplate.getCollection("articles").find(query.getQueryObject(), Map.class).first();
I suggest you to use the Class in order to define your object, maybe you can use something like
public class Foo {
#Id
private String id;
private Map<String, Object> data;
private Map<String, Object> metadata;
}
in order to maintain flexibility
Related
I am trying to map a Java Map to a POJO but having problems with using Orika.
I have a LinkedHashMap and trying to map it to a POJO. I've been reading this website https://www.baeldung.com/orika-mapping, specifically section 4.2
This is how I define my orika mapper:
factory.classMap(Map.class, TestDto.class)
.field("nest['name']", "name")
.toClassMap();
and this is the LinkedHashMap I'm trying to map:
Map<Object, Object> nest = new LinkedHashMap<>();
nest.put("name", "myname");
Map<Object, Object> obj = new LinkedHashMap<>();
obj.put("nest", nest);
and this is the POJO I'm trying to map to:
public class TestDto {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
when I do:
TestDto testDto = mapper.map(obj, TestDto.class);
I get null as name. Is this scenario possible to map or do I have to do more customization of orika?
Yeah the name will return null for this mapping.
TestDto testDto = mapper.map(obj, TestDto.class);
This is due to variable name in TestDto class does not contain variable name nest.
To map your POJO with different key.
Pleas add #JsonProperty in your get() method.
Example:
#JsonProperty("nest")
public String getName() { return name; }
I figured it out, you can define a mapper like:
factory.classMap(Map.class, TestDto.class)
.field("nest:{get('nest')|type=Map}.name:{get('name')|type=java.lang.String}", "name")
.toClassMap();
which is outlined here:
https://orika-mapper.github.io/orika-docs/advanced-mappings.html
This is really only needed if you don't have control over the Pojo and/or do not want to make a copy of the Pojo
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?
TL; DR;
Do not use Java collection/map types as a base class for your entities.
Repositories are not the right tool for your requirement.
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;
}
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?
I have a Serializable class, with a map property. When the map has a Long as key the code does not work, while with String it works.
This doesn't work:
public class UserSession implements Serializable {
Map<Long, Date> timeQuestionAsked = new HashMap<>();
}
This does work:
public class UserSession implements Serializable {
Map<String, Date> timeQuestionAsked = new HashMap<>();
}
The weird thing I get no exception. This class is loaded in a filter in Jetty (google app engine app), and when I try to use the class with the Long key, I get a weird "Not found" error.
Actually it was caused by the database framework I was using: objectify. It turns out Maps must have string as keys: https://code.google.com/p/objectify-appengine/wiki/Entities#Maps
It has nothing to do with Serializable...
I have a document that can have dynamic key names:
{
"_id" : ObjectId("51a29f6413dc992c24e0283e"),
"envinfo" : {
"appName" : "MyJavaApp",
"environment" : {
"cpuCount" : 12,
"heapMaxBytes" : 5724766208,
"osVersion" : "6.2",
"arch" : "amd64",
"javaVendor" : "Sun Microsystems Inc.",
"pid" : 44996,
"javaVersion" : "1.6.0_38",
"heapInitialBytes" : 402507520,
}
Here envinfo 's keys are not known in advance.
What is the best way to create an entity class in Spring Data Mongodb which will map this document?
This is one way of doing it. There may be other better ways.
Create a map of attributes and store the map in mongo.
public class Env {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private ObjectId id;
#Field
private Envinfo envinfo;
public static class Envinfo {
#Field
private String appName;
#Field
private Map<String, String> attributes;
}
}
If you know the keys in advance, you may add those attributes in Envinfo and keep those out of attributes map.
Here is what I'll do.
class EnvDocuemnt {
#Id
private String id; //getter and setter omitted
#Field(value = "envinfo")
private BasicDBObject infos;
public Map getInfos() {
// some documents don't have any infos, in this case return null...
if ( null!= infos)
return infos.toMap();
return null;
}
public void setInfos(Map infos) {
this.infos = new BasicDBObject( infos );
}
}
This way, getInfos() returns a Map<String,Object> you can explore with String keys when needed, and that can have nested Map.
For your dependencies, it is better not to expose the BasicDBObject field directly, so this can be used via interface in a code not including any MongoDb library.
Note that if there is some frequent accessed fields in envinfo, then it would be better to declare them as fields in your class, to have a direct accessor, and so not to spend to much time in browsing the map again and again.
Here's my class:
#Entity (name = "Client")
public abstract class MyClient
{
private Map<String, String> _properties;
}
Hiberate map my properties object into a class named "MyClient_properties".
How can I modify it so it will be mapped to "Client_properties"?
Thanks
Interestingly I thought that is supposed to be the default. Pretty sure the default naming feature is supposed to take the #Entity#name value rather than the class name if it is supplied.
Anyway, to explicitly name the collection table you'd use (oddly enough) the JPA #CollectionTable annotation:
#CollectionTable( name="Client_properties" )
private Map<String, String> _properties;