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.
Related
I was lately working on optimizing my application performance, and I noticed that when I lazy load a dependency with MazyToOne relationship, object that hibernate provides is not just lazy loads the object itself, but also all of its fields - so, it has made me think if I maybe can use this to my advantage
Let's imagine the situation like this
#Transactional
public void updateUserNameToHarry(Long userId){
User u = dao.findById(userId);
u.setName("Harry");
}
So we have opened a transaction, loaded Harry into our persistence context, and updated his name.
Once the transaction is closed Hibernate will do its magic and update the name of the user entity we have.
But, in this scenario, I don`t really need to parse Harry db row into entity graph, load Harry into application context, and I definitely do not need to do all of this for the eagerly loaded relationships of Harry.
So here is the question - can I avoid this somehow?
Ideally, I would like Harry to be a lazy loaded object that upon calling setName method adds a single update query, that is going to be executed once the transaction commits.
I am currently using Spring boot 2.0 stack, but my question applies to any other versions and approaches to ORM with java.
If I understand correctly, these options came to my mind:
1 - obvious one - don't load User at all, just perform update query yourself (UPDATE user SET name = 'Harry' WHERE id = :userId) - number of ways to achieve this, named query, spring method with annotation etc.
2 - there is a getReference method in EntityManager, it allows you to get User proxy with only it's ID filled, unless you perform some actions on it, then the fields are loaded. It's not gonna help with such as simple case as you posted, but if your User had relations to other entities, then you could benefit from it - have a look at this, it's a perfect explanation
Let's say I use some entity, in pseudocode: Article { id, author, text, date } with all properties initialized lazily. I have repository method:
#Transactional
Article loadArticle(int id) {
return articeRepository.find(id);
}
which is used by different controllers. PreviewController requires author and date. ViewController requires text and date.
Should I pass boolean parameters initAuthor, initText, initDate to loadArticle() method to call Hibernate.initialize() for each property there? Or should I move #Transactional to controllers' methods? What are the best practices dealing with lazy initialization?
If you are aiming to have a highly performant data access layer, then you should keep one thing in your mind: every use-case is different thus needs different kind of data.
What I wanted to suggest with this is that you want to have different methods for different use-cases, which fetches only the data it needs.
There are multiple ways to do this:
Fetching the entity relation with a fetch join
Create read-only projections
I prefer the latter as it clearly shows that it's for showing data rather than modifying it.
For projections, Spring Data JPA can help as it has an out-of-the-box solution for it which is basically defining an interface.
If you use Hibernate.initialize() or any other way to trigger loading an entity relation, practically you will trigger a database query. If you think about let's say 100 entities, that means you will execute 101 queries. Use fetching or read-only projections.
I have many issues with LazyLoadingException in a Spring web application wherever I try to access fields that are annotated with FetchType.LAZY
There is no session configured in Spring because the requirement is that the API should be stateless.
All the service layer methods have #Transactional annotations properly set.
However when I try to access the Lazy fields on any domain object, I get the famous LazyInitializationException (...) could not initialize proxy - no Session
I thought that Hibernate would automatically load the lazy fields when needed when I'm in a #Transactional method but it appears it doesn't.
I have spent several days looking for answers but nothing fits my needs. I found that Spring could be configured with openSessionInViewFilter but it appears to cause many issues, moreover I don't have any session.
How to automatically load lazy fields in #Transactionalannotated service methods with such a stateless API ?
I'm sure I'm missing something obvious here, but I'm not very familiar with Spring and Hibernate.
Please tell me if there are missing information in my question I should give you.
LazyInitializationExceptions are a code smell in a same way EAGER fetching is too.
First of all, the fetching policy should be query-based on a business case basis. Tha DAO layer is solely responsible for fetching the right associations, so:
You should use the FETCH directive for all many-to-one associations and at most one one-to-many association. If you try to JOIN FETCH more than one one-to-many associations, you'll get a Cartesian Product and your application performance will be affected.
If you need to fetch multiple collections, then a multi-level fetching is more appropriate.
You should ask yourself why you want to return entities from the DAO layer. Using DTOs is a much better alternative, as it reduces both the amount of data that's fetched from the DB and it doesn't leak the Entity abstraction into the UI layer.
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.
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.