I'm currently in the process of converting a large project from Grails to Spring: I'm somewhat stuck when attempting to lazy-load objects id's without having to select the entire table.
The way I would like it to work, /Object/:
Object
List<child> children
to be output like so:
{children:[{id:1},{id:2}]}
So that from another controller I can then access that inner object if required using regular CRUD methods which I have implemented across all of my controllers. /child/1
With the JSON output in mind, I'm having trouble with no Session being available during serialization which is understandable, I've seen methods such as calling object.child.getId() in the service layer seems incredibly hacky to me and would also cause the entire object to be Lazily-loaded.
Overall, I'm looking to create a simple Rest Application with easy customization of the json output, something grails did with the JSON Marshalling plugin really quite well: https://grails.org/plugin/marshallers
Edit:
I don't really wish to set eager loading, but Eager loading is a possibility. Is there a way of getting Eager Loading with a max depth?
{
children: { //depth 1
children-children: { //depth 2 (Ignore this)
}
}
}
You could either use (this is basically a wrapper for object.child.getId hack):
Hibernate.initialize(object.getChildren());
Or create a special query for this (this would be eager loading with max depth of 1):
#assuming you use hql, it would look like this
SELECT e FROM Entity e
JOIN FETCH e.children...
It is possible to do with CriteriaAPI and DetachedCriteria as well.
You can use a #Fetch(FetchMode.SUBSELECT), so that all uninitialized entities are fetched with a single secondary query, upon accessing.
You only need to make sure, the Hibernate Session is still opened when the uninitialized LAZY association gets accessed.
Related
I'm currently in the process of migrating an API built in Laravel/PHP to Spring Boot and had a question on best practice/how to approach the problem.
In laravel, I have a User model, with 3 child relationships: roles, communities and profiles. In laravel, I'd use Resources to dictate the data I'm returning in an API response, for example a GET /id would return the user with all of the child relationships, where as on a GET / list operation, it would return the base model with no child relationships.
What is the best way to replicate this with Spring Boot JPA? I had looked into Jackson JsonViews, however this only seems to reduce the object returned by the API, rather than limiting the amount of SQL queries being ran.
Any suggestions are appreciated. Thanks!
If all of your relationships are Lazy (as they normally should be), then JsonViews is the right solution.
Whenever the joins are lazy, there is no query for the child data until the field has been accessed. So if the JsonView on / excludes the roles member of the User object, then the Json serializer will never call user.getRoles(), so the query is never run.
But if the JsonView for /id does include roles, then the serializer will have to call user.getRoles(), and at that time the query is run.
If you need even more optimization, then you can also attach an EntityGraph to a function on your repository like this:
public class UserRepository<User,Long> {
#EntityGraph(attributePaths = {"roles", "communities", "profiles"})
User getDetailedUserById(Long id);
}
This will force Eager loading for those otherwise lazy attributes, which means there will only be one query run.
In my mapper class, I always have to retrieve a list of departments from my company entity :
#OneToMany(mappedBy = "company", orphanRemoval = true, cascade = CascadeType.ALL)
public List<CompanyDepartments> getDepartments()
{
return departments; //java.util.List variable.
}
There are a couple thousand departments(about 2000+). I am trying to reduce the time taken to fetch company data and want to begin with the departments which are going to be fairly static in nature.
Option 1 : I can always have a private method in the mapper which populates a cache on the first load and return from the cache all the time.
But is there anything more simpler? I am probably not the first one to face this and wanted to know how I could approach this. Is there a known annotation that can be used. Like a #Singleton annotation on the variable in the entity ? (there is no such annotation obviously). What is the simplest way to make this list a singleton.
Just a typical spring mvc 3 application using spring data jpa for db interaction.
I suggest this link that solves the n +1 query problem ...
How can i resolve the N+1 Selects problem?
Moreover you could put a cache (lvl 2) on your search service,
if the data does not change during the life cycle of the application.
http://docs.spring.io/spring/docs/3.1.0.M1/spring-framework-reference/html/cache.html
Another approach is to add to indexes on the db.
I hope I've given you all the answers about your question.
What you're experiencing is part of the "object-relational impedance mismatch". One solution would be to extend your object model so that you can use a left join fetch to load multiple companies including their departments using only one SQL statement.
The problem using this technique is that you can populate only one list at a time. Although it's nice & easy to define many sub-lists in the world of objects, it's hard to load all these lists at the same time (and therefor efficiently) from a relational database. However it can be done on Oracle using object- or XMLType queries.
The straight-forward way to get such data out of a RDBMS is by writing custom queries that match exactly the task at hand and provide only the data that is actually needed. Therefor you'd need not only one company class, but many - one for each task. - you'd actually do inheritance instead of attribution
BTW, that is why ORM is still considered the Vietnam of Computer science - easy to get started, but hard to succeed with.
I'm trying to figure out the best way to use JPA in the context of a restful web service. The input comes in as JSON and I can use Jackson/JAX-RS to convert that to a POJO. This gets passed to a service where I need to somehow merge into a JPA entity.
These are the options I've found so far with pros and cons.
1. JPA merge()
The first thing I tried was probably the simplest. The GET action returns the JPA entity which is easily serialized into JSON. On the update the object is passed back is JSON which can be used to populate a detached entity. This can be saved to the DB using the JPA merge() method.
Pros
Simple architecture with less code duplication (i.e. no DTO's)
Cons
As far as I can tell this only works if you pass the whole model around. If you try to hide certain fields, like the maybe the password on a User entity, then the merge thinks you're trying to set these fields to null in the DB. Not good!
2. DTO's using JPA find() and dozer
Next I thought I'd look at using data transfer objects. Apparently an anti-pattern but worth a look. The service now creates a DTO instance based on the entity and it is this DTO that is serialized to JSON. The update then gets the entity from the DB using a find() method and the values need to be copied across from the DTO to the entity. I tried automating this mapping using the dozer framework.
Pros
You don't have to return the entire model. If you have certain fields you don't want to be updated you can leave them off the DTO and they can't be copied to the entity by mistake. Using dozer means you don't have to manually copy attributes from dto to entity and vice versa.
Cons
It feels like repeating yourself when writing the DTO's. Somehow you have to map between entities and DTO's. I tried to automate this with dozer but it was a bit disappointing. It was nulling out things it shouldn't have been and to get full control you have to write xml.
3. DTO's using manual merge
A third way would be to abandon dozer and just copy the properties across from the DTO to the entity in the service. Everybody seems to say anti-pattern but it's pretty much how every non-trivial application that I've seen in the past has worked.
Summary
It seems to be a decision between keeping things simple for the developer but not having control over the input/output or making a more robust web service but having to use an anti-pattern in the process...
Have I missed anything? Perhaps there's an elusive alternative?
Using JPA merge looks the simplest, cleanest and with very less effort but as correctly discovered creates problems with detached entity attributes set to null.
Another problem which turned out to be big in one of my experiences was that if you rely on JPA merge operation you must be using Cascade feature as well.
For simple and less nested relation this works reasonably well, but for deeply nested domain objects and lots of relations, this becomes a big impact on performance. The reason being that the ORM tool (Hibernate in my experience) upfront caches the SQL to load the merge entity ( 'merge path' in Hibernate parlance) and if the nesting is too deep with Cascade mappings the joins in the SQL becomes too big. Marking realtions Lazy does not help here as the merge path is determined by the Cascades in relations. This problem becomes apparent slowly as your model evolves. Plus the prospect of angry DBA waving a huge join query on our face prompted us to do something different :-)
There is an interesting issue related to Hibernate regarding Merging of Lazy relations still unresolved (actually rejected but the discussion is very enjoyable to read) in Hibernate JIRA.
We then moved towards the DTO approach where we refrained from using merge and relied on doing it manually. Yes it was tedious and required the knowledge of
what state is actally coming from the detached entity, but to us it was worth. This way we do not touch the Lazy relations and attributes not meant to change. and set only what is required. The automatic state detection of Hibernate does the rest on transaction commit.
This is approach I am using:
suppress serialization of certain fields with XmlTransient annotation
when updating the record from the client, get the entity from the database and use ModelMapper with custom property mapping to copy the updated values without changing the fields that are not in the JSON representation.
For example:
public class User {
#Id
private long id;
private String email;
#XmlTransient
private String password;
...
}
public class UserService {
...
public User updateUser(User dto) {
User entity = em.find(User.class, dto.getId());
ModelMapper modelMapper = new ModelMapper();
modelMapper.addMappings(new UserMap());
modelMapper.map(userDto, user);
return user;
}
}
public class UserMap extends PropertyMap<User, User> {
protected void configure() {
skip().setPassword(null);
}
}
BeanUtils is an alternative to ModelMapper.
It would be nice if these libraries could recognize the XmlTransient annotation so the programmer can avoid creating the custom property map.
I currently have working code to save children to a parent entity. But I'm wondering if I'm doing things right since I now have an overload on select statements going thru hibernate. I do use caching so atm I don't have delay problems but I'm wondering if I can't be more efficient. Take this little extract as example
MbaLog.debugLog(logger, "Saving CodeType");
Site site = codeType.getSite();
if (site != null && site.isProxy())
codeType.setSite(siteRepository.loadSiteById(site.getId()));
Long recordId = codeRepository.saveCodeType(codeType);
I have an entity CodeType that I'm saving that has a child Site. This child is passed to the method as a proxy object with just it's id filled in. Then I fetch a fully loaded Site object from the database and set it on codetype. Next up I save the codeType with the sessionfactory of hibernate to the database (code not visible here, but it's behind the codeRepository).
This works but I'm loading a full site, that has childs of it's own so I see at least 5 queries passing before the insert. I could put a lot of stuff lazy on site, but for the time being I rather not do that due to possible code complications in deeper layers. I had to learn hibernate and JPA on the job and never had much training from experts in the past. So I'm wondering, is there a shortcut to save the site on codetype ? Do I need to have it fully loaded or is the id enough ? or just the id and version (I'm using #version annotation on all my entities for optimistic locking)
Thanks in advance
Instead of using Session.get() (or EntityManager.find()) to get a reference to the SIte entity, use Session.load() (or EntityManager.getReference()) to get this reference.
These methods will return a lazy-loaded proxy on the entity rather than executing a query to get the data of the site.
If all you want to persist is the relationship between Site and CodeType, a lazy instance is probably enough. So you could use EntityManager.getReference() (lazy load) instead of EntityManager.find().
I'm having a problem where JPA is trying to lazily load my data when I don't want it to. Essentially what is happening is I'm using a Service to retrieve some data, and when I go to parse that data into JSON, the JSON library is triggering hibernate to try and lazily load the data. Is there any way to stop this? I've given an example below.
// Web Controller method
public String getEmployeesByQuery(String query) {
Gson gson = new Gson();
List<Employee> employees = employeeService.findEmployeesByQuery(query);
// Here is where the problem is occurring - the gson.toJSON() method is (I imagine)
// using my getters to format the JSON output, which is triggering hibernate to
// try and lazily load my data...
return gson.toJSON(employees);
}
Is it possible to set JPA/hibernate to not try and lazily load the data?
UPDATE: I realize that you can use FetchType.EAGER - but what if I don't want to eager load that data? I just want to stop hibernate from trying to retrieve more data - I already have the data I want. Right now whenever I try and access a get() method hibernate will throw a "no session or session is closed" error, which makes sense because my transaction was already committed from my service.
Thanks!
There are several options:
If you always need to load your collection eagerly, you can specify fetch = FetchType.EAGER in your mapping, as suggested in other answers.
Otherwise you can enable eager fetching for particular query:
By using JOIN FETCH clause in HQL/JPQL query:
SELECT e FROM Employee e JOIN FETCH e.children WHERE ...
By using fetch profiles (in JPA you can access Hibernate Session via em.unwrap(Session.class))
You really have two options:
You can copy the data from employee to one that is not being proxied by hibernate.
See if there is a way to not have the toJSON library reflect the entire object graph. I know some JSON libraries allow you to only serialize some properties of an object to JSON.
Personally I would think #1 would be easier if your library only uses reflection.
As others have stated, this is not an issue with JPA/hibernate but rather with the json serialization library you are using. You should instruct gson to exclude the properties you don't want traversed.
Yes:
#*ToMany(fetch=FetchType.EAGER)
I suggest you to make a fetched copy of the entities you want to use outside of a transaction. That way, the lazy loading will occur from within a transaction and you can pass to Gson a plain, not enhanced, POJO.
You can use Doozer to do this. It is very flexible and through a little configuration (read you'll gonna loose your hair configuring it) you can even retrieve only partially the data you want to send to Gson.
You could always change the fetch attribute to FetchType.EAGER, but it is also worth considering if you have your transactions have the right scope. Collections will be correctly loaded if they are accessed within a transaction.
Your problem is that you are serializing the data. We ran into the same sort of problem with Flex and JPA/Hibernate. The trick is, depending on how much you want to mangle things, either
Change your data model to not chase after the data you don't want.
Copy the data you do want into some sort of DTO that has no relationships to worry about.
Assuming you're using Hibernate, add the Session-in-view filter....its something like that, it will keep the session open while you serialize the entire database. ;)
Option one is what we did for the first big project we did, but it ruined the data access library we had for any sort of general purpose use. Since that time we've tended more toward option two.
YMMV
The easy and straight forward thing to do is create new Data classes (something like DTO)
use Hibernate.isInitialized() to check if the object is initialized by hibernate or not.
I am checking gson if i can override anything. I will post it here if I find anything new.