I've got the following models in Play Framework.
#Entity
public class Parent extends Model {
#Id
public Long id;
public String name;
public List<Child> children = new ArrayList<Child>();
}
#Entity
public class Child extends Model {
// Entity 2
#Id
public Long id
public String name;
#ManyToOne
public Parent parent;
}
Executing the following query gives me more information than I need.
toJson(Child.find.all());
As an example, I get all children as well as their parents and parent's attributes and any other adjoining information.
I've tried setting the fetch=FetchType.LAZY, but it doesn't make a difference.
Can anyone help?
Jackson's toJson() method always fetches all data while serializing Ebean's objects, so it can be real performance killer, that was discussed my proposition is using some dedicated object (not stored in DB) and filling it only with required data from 'original' object.
Check other answer, which describes this approach.
Ebean has built in JSON support and you can use that if you want exact control over the parts of the object graph to include in JSON:
JsonContext jsonContext = Ebean.json();
JsonWriteOptions options = JsonWriteOptions.parsePath("id,name");
String json = jsonContext.toJson(list2, options);
And alternatively you can apply PathProperties to both the query and json like:
PathProperties pathProperties = PathProperties.parse("id,name");
query.apply(pathProperties);
// fetch only id and name
List<App> list3 = query.findList();
// json output only id and name
JsonWriteOptions options2 = new JsonWriteOptions();
options2.setPathProperties(pathProperties);
String json2 = jsonContext.toJson(list3, options2);
Note that Ebean 4.3 had it's json support refactored to use Jackon core for parsing and generation (so it's using Jackson under the hood). You are using Play so currently stuck on an old version of Ebean so instead of using query.apply() you'd use pathProperties.apply(query) I think.
Regarding #JsonIgnore ... obviously different use cases require different JSON which is why this feature exists in Ebean (when you get a use case that needs to include parent like)
PathProperties pathProperties = PathProperties.parse("id,name,parent(id,name)");
...
I simply opted for the #JsonIgnore annotation.
Related
I have entities that represent my database - User, Recipe and Tag.
For data manipulation I use DTO. So UserDTO, RecipeDTO, TagDTO. When I define a relationship between entities, I use its basic User, Recipe, Tag form, but when I define these relationships in a DTO class, I use its DTO form.
For example:
DTO Class looks like this
public class UserDTO{
private String name;
private String email
private List<RecipeDTO>
}
public class RecipeDTO{
private String title;
private String description;
private UserDTO user;
}
I know how to map a DTO to an entity so that I can perform operations (CRUD) on the data in the database.
private Recipe convertToEntity(RecipeDTO recipeDTO){
Recipe recipe = new Recipe();
recipe.setTitle(recipeDTO.getTitle);
recipe.setDescription(recipeDTO.getDescription);
}
But the RecipeDTO also has a UserDTO in it, which I also need to map to an entity. How do I do this?
So I am trying to achieve a mapping inside the mapping .... (??)
I can think of the following solution.
Create method that converts UserDTO to User:
private User convertUser(UserDTO userDTO){
User user = new User();
user.setName(userDTO.getName());
user.setEmail(userDTO.getEmail());
}
And then use it while mapping RecipeDTO to Recipe.
private Recipe convertToEntity(RecipeDTO recipeDTO){
Recipe recipe = new Recipe();
recipe.setTitle(recipeDTO.getTitle());
recipe.setDescription(recipeDTO.getDescription());
//Convert UserDTO
recipe.setUser(convertUser(recipeDTO.getUser()));
}
I'm not sure if this is the right solution, as there will be more and more mappings as the code gets bigger.
The approach you described is not wrong and will work, but doing it that way will indeed involve a lot of hard work.
The way this is usually done in the industry is by letting a library do that work for you.
The two most popular mapping libraries for java are:
https://mapstruct.org/ (which uses annotation processing at compile time and auto-generates basically the same mapping code as in your example)
and
http://modelmapper.org/ (which uses black magic and reflection)
They are both easy to setup/learn and either will do the job (including mapping nested objects as in your example), so take a look at the “getting started“ section and pick the one you find more intuitive to use.
My personal recommendation would be to pick Mapstruct, as it has way fewer gotchas, generates clean human-readable code and avoids using reflection.
I have a POJO called User which is also being used for inserting documents in MongoDb.
#Data
#Document(collection = Constants.COLLECTION_USERS)
public class User {
public ObjectId _id;
public String userID;
public String email;
public String name;
public String sex;
public String dob;
public String photo;
//have more variables
}
I have a simple application where a user registers by giving in a subset of data listed in the User class. The signature of the register method in my controller is as follows.
public GenericResponse registerUser(#RequestBody User userRegistrationRequest)
It can be noticed that I am using the same POJO for the registration request. Until now everything is fine. I can persist the data user object just fine.
This registration API is just used to persist a small set of a user's data. There would be other information as well in the MongoDb document, which would be accessed/persisted from some other APIs.
Suppose a user has registered with the basic information and also has persisted other information via APIs other than the registration one.
How would I make an API which can just get me some selective data from the User document again using the same User Pojo? If I call the repository to give data for a specific userID, it will give me the whole document mapped to the User class. I don't want my API to give all the information stored in the document.
One approach is to make another POJO with the details I want, and map the information selectively using a Converter. But, I want to avoid this approach, as I want to use the same class.
Second approach: Modify the Mongo query to return data selectively as given in the docs. But here I would have to specify all the fields I want in the result set. This would again be a length query.
Is there a better way to filter out data from the object?
How would I make an API which can just get me some selective data from the User document again using the same User Pojo?
How would I go off-road with a car I would like to take me girl to the restaurant at the evening? I would not - if I would have the same car for everything I would look stupid next to the restaurant, coming out in a suite or I would stuck in a swamp.
The biggest Java advantage is object creation time - you should not be afraid of it. Just create another model for registration, another as DTO for saving data, another for front-end presentation etc
Never mix responsibility of objects. You will finish with something like
#Entity
class ThePileOfShit {
#Id
private Long id;
#my.audit.framework.Id
private String anotherId;
#JsonIgnore
// just a front-end flag ignore
private boolean flag;
#Column
// not a field but getter because of any-weird-issue-you-want-to-put-here
public String getWeirdStuff() { ... }
// Useless converters
public ModelA getAsModelA() { ... }
public ModelB getAsModelB() { ... }
// etc
// etc
}
Four frameworks, five technologies - nobody knows what's going on.
If you are afraid of converting stuff use ModelMapper or another tool but keep your POJOs as simple as possible
You can use Gson's #Expose annotation only on the fields you want to return in the API.
To serialize the data, use:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String json = gson.toJson(userData);
CONTEXT:
I am working on a java spring web application backed by a Neo4j database. I have an object "Student" that has taken a number of "Modules" and "Courses". The relationship is connected via "rich relationship" nodes "RR_TakenModule" and "RR_TakenCourse" that specify a grade property.
public class Student extends DomainObject {
String fullName;
Gender gender;
Collection<RR_TakenModule> modulesTaken;
Collection<RR_TakenCourse> coursesTaken;
DateTime yearStarted;
that way I could ask for a Student that got 74% in a Module
PROBLEM:
I cannot return a "deep" object, from the GraphRepository set up. Ie I cannot return a Student with populated "modulesTaken" and "coursesTaken" properties. I've seen several approaches online such as trying the cypher query :
MATCH (student:Student) -[:MODULES_TAKEN]-> (rr:RR_TakenModule) -[:MODULE]-> (m:Module) RETURN student, COLLECT(rr) as modulesTaken
Which is claimed to map the RR_TakenModules into the object dynamically via the property name. It does not do this for me and returns a "Error mapping GraphModel to instance of com.domain.Actors.Student" error code. Although note that it does properly group when running the cypher query in the localhost:7474 interface. Clearly mapping is the issue.
Currently I have adopted the template and Map approach. Using Neo4jOperations and the Result object. This works, however means that I have to write out iterators that go through and assign values based on the key/value pairs in the result object. This leads to high maintenance and a larger chance of errors.
Looking around their used to be options such as #Fetch, and specifying depth in Neo4jTemplate queries, however non of these methods seem to be present in my version (most appear to be depreciated)
QUESTION :
Is there a way to map sub-objects (ie "collection prop" and "set prop") of a Neo4j entity object via Graph Repositories.
I realise there are predefined methods such as "findOne" that have a depth parameter, but I want to apply a similar implementation to my custom queries.
Alternatively, is there a solution for dynamically mapping a Neo4jOperations Result object to a java object without defining some custom json parser
VERSIONS :
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.1.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
<spring-data-neo4j.version>4.0.0.RELEASE</spring-data-neo4j.version>
<spring-data-releasetrain.version>Gosling-RELEASE</spring-data-releasetrain.version>
</properties>
Spring Data Neo4j (SDN) does not populate the referenced (by relationship) nodes in the returned node. It will, however populate the fullName and gender properties (assuming here that Gender is an Enum such that a string can be easily mapped).
In your query above you return a Student node and a collection of RR_TakenModule, but Spring does not know how to map this since there's no class that comprises these two results. You need to define an explicit result for this case. I suggest the following:
#NodeEntity(label = "Student")
public class Student extends DomainObject {
#Property(name = "studentId")
private String studentId; //you want to have this
#Property(name = "fullName")
private String fullName;
#Property(name = "gender")
private Gender gender; //assuming its an Enum
#Relationship(type = "MODULES_TAKEN") // Relationship.OUTGOING is default
private Set<RR_TakenModule) modulesTaken;
}
Then define a repository
public interface StudentRepository extends GraphRepository<Student> {
#Query("MATCH (s:Student {studentId:{studentId}})-[:MODULES_TAKEN]->(m:RR_TakenModule) RETURN s AS student, collect(DISTINCT m) AS modulesTaken")
public StudentWithModulesTakenResult getByUserIdWithModulesTaken(#Param("studentId") String studentId)
}
and the respective result
#QueryResult
public class StudentWithModulesTakenResult {
private Student student;
private Set<RR_TakenModule> modulesTaken;
//only getters, no setters
}
After looking into the default "findOne(nodeid, depth)" method GraphRepositor offers I have found a solution that works for me.
I format the query as MATCH (n:Entity) WITH n MATCH p=(n)-[*0..4]->(m) RETURN p
where Entity is the name of your base entity. I cannot justify why, but this format will dynamically map the result to your respective POJO.
Note you can specify the depth (*0..4) to specify how deep a POJO is populated. It is also worth noting that the relationship is using -[]-> not -[]- which is the default relationship for findOne(nodeid, depth). If this does not work it might be possible that you have not got the latest Neo4j OGM version.
My Issue entity was created from a DB table that has several fields (id, etc...). Each issue has as a field a list of Articles, which are stored in a separate DB table. Articles have a int issueID field, which is used to map them to the appropriate Issue (there is no corresponding column in the issues table): Ultimately, when an Issue object is constructed, I'm going to have it pull all of the articles whose issueID matches its ID, so that I can return a single serialized object that contains the issue data as well as a JSONArray representing its list of articles.
At this point, though, I'm just doing some testing - creating a few dummy Article objects and adding them to the articles collection. The problem is that, when I test GET requests on the Issue object, the JSONObject returned includes only the fields stored in the database (id, etc...) - no sign of the Article collection. Why is that?
I'm equally interested to know what other code you would need to see to answer this question: I've just begun teaching myself how to write web services and am still in the phase of wrapping my head around the broad concepts, so figuring out which of the moving parts has affects which behaviors - and which annotations are needed where - is ultimately what I'm trying to do.
That being the case, broader-based advice is welcomed.
#Entity
#Table(name = "issues")
#XmlRootElement
public class Issue implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "id")
private Integer id;
....//other fields
#OneToMany(mappedBy = "issueID")
private Collection<Articles> articlesCollection;
public Issue() {
articlesCollection = new ArrayList<Articles>();
Articles a = new Articles();
a.setHeadline("butt cheese");
articlesCollection.add(a);
Articles b = new Articles();
articlesCollection.add(b);
Articles c = new Articles();
articlesCollection.add(c);
}
By default the relationship initialization is lazy so when the Issue object is loaded the articlesCollection is not fetched unless used.
In your case seems its the same situation.
Explore OpenEntityManagerInViewFilter if you do not intend to explicitly load articlesCollection. When your object serializes the articlesCollection will be loaded if you have configured OpenEntityManagerInViewFilter.
Does your Articles Class also has #XmlType or #XMLRootElement Tag?
Onany generic class like List<T> jaxb expects that T is annotated with #XMLType or #XMLRootElelemt
What is appropriate way of creating objects with One-to-Many relationship using Objectify and RequestFactory? I've read documentation for these libraries, and also reviewed number of sample projects such as listwidget and gwtgae2011. All of them use #Embedded annotation which is not what I want because it stores everything within one entity. Another option according to documentation would be to use #Parent property in child classes. In my example (getters/setters removed for simplicity) I have entities Person and Organization which defined as
#Entity
public class Person extends DatastoreObject
{
private String name;
private String phoneNumber;
private String email;
#Parent private Key<Organization> organizationKey;
}
and
#Entity
public class Organization extends DatastoreObject
{
private String name;
private List<Person> contactPeople;
private String address;
}
Now if I understood documentation correctly in order to persist Organization with one Person I have to persist Organization first, then set organizationKey to ObjectifyService.factory().getKey(organization) for Person object and then persist it. I already don't like that I have to iterate through every child object manually but using RequestFactory makes everything is more convoluted due to presence of proxy classes. How would I define Organization and OrganizationProxy classes - with Key<> or without it ? Will I have to define something like this in Organization ?
public void setContactPeople(List<Person> contactPeople)
{
for (int i = 0; i < contactPeople.size(); ++i)
{
DAOBase dao = new DAOBase();
Key<Organization> key = dao.ofy().put(this);
contactPeople.get(i).setOrganizationKey(key);
}
this.contactPeople = contactPeople;
}
And how would I load Organization with its children from Datastore ? Will I have to manually fetch every Person and fill out Organization.contactPeople in #PostLoad method ?
It seems like I'll have to write A LOT of maintenance code just to do what JPA/JDO does behind the scene. I simply don't get it :(
Am I missing something or it's the only way to implement it ?
Thanks a lot for answers in advance!!!
You need to make it as #Parent only when you going to use it in transaction against all Person in this Organization. I'm sure it's not what you want.
It's enough to save just private Key<Organization> organizationKey, and filter by this field when you need to find Person for specified Organization
As about loading all referenced objects - yes, it is, you have to load it manually. It's pita, but it's not a lot of code.
Also, there is a different way to store this relationship, if your organization are small enough, and consists of few hundreds of people. At this case you can have List<Key<Person>> contactPeopleKey;, and load all this people by existing Key, manually, it much be much faster than loading by new Query