Hibernate to initialize object in a different transaction - spring

I got the famous LazyInitializationException.
I have an object User which is stored in the session. This object contains an other object Market which is lazy initialized.
When I load the user in the session, I don't load Market because it is too heavy and I don't need it everytime.
When I want to load the market, I am in a different transaction and I don't want to reload the user from the database. How can I retrieve the Market object? Knowing that User.market contains the Hibernate proxy and so the id of the market and that I don't want to hack Hibernate using reflection.
That would be even better if I could load the market without loading it into the user. Since the user is in the session, I don't want to put a lot of stuff in the session.
A JPA compatible solution would be even better.
Cheers

If the eager mode fetching is not acceptable, and if the transaction cannot be maintained up to the Market retrieval, a specific dao method could be implemented to retrieve specifically the market from a user
public List<Market> retrieveMarketFromUser (final User user) {
Query query = session.createQuery("SELECT m FROM User AS u INNER JOIN u.market as m WHERE u.userid = :uid");
query.setParameter("uid", user.getId());
List<Market> list = query.list();
return list;
}
or the short version
Query query = session.createQuery("SELECT u.market FROM User AS u WHERE u.userid = :uid");
This is maybe not the JPA solution you were expecting, just a workaround.

What you have to do is to annotate the accessors instead of the fields. This will allow you to avoid loading of the market object when you initially load the user, but you will have access to the id of the market from the Hibernate proxy object without triggering a lazy loading or getting a LazyInitializationException. Then later on when you want to load the market itself you do a normal entity retrieval based on its id. You can read a detailed explanation of how this works here.

If the relation is bidirectional then you can load the Market independently using a query with clause like where market.user.id = ?.

Why is the obvious solution not good? Like the one ring0 suggested, or simply using the findById or find methods if you already have the id in the User object.
//if using a session factory
session.findById(Market.class, marketId);
//if using the EntityManager
em.find(Market.class, marketId);
Depending on how the current_session_context_class configured (I have it in hibernate.cfg.xml) you might have a new session with each new transaction. If that is the case the you do not need to worry about putting too much stuff in there. You can find more info on contextual sessions here.

Since you have mentioned new transaction , then you would probably working with a new hibernate session belonging to thread ,
Use this incase of direct interaction with hibernate session
obj = session.merge(obj);
In case your using JPA2 Api
obj= entityManager.merge(obj);
Please rate the answer if it helps.
Cheers

Related

Spring Security Hibernate load user with lazy associations

I'm building a Spring Data JPA application and securing my API calls using Spring Security.
Principal is loaded through my custom implementation UserDetailsService.loadByUsername(...)
, retrieving only the User entity itself since all of its associations are LAZY by default.
This is done via a Spring Filter before each controller is hit (I'm doing JWT Auth)
For some requests (say POST /todo), however, I will need to load some of the user's lazy associations (the user's Todos) as well in order to add new data to them and persist it.
Is there a suggested practice to achieve that?
My goal is to have some of those associations already loaded (depending on context) when getting the principal through SecurityContextHolder.getContext().getAuthentication().getPrincipal() without necessarily setting them to EAGER.
Something along the lines of overriding the
UserDetailsService.loadByUsername to JOIN FETCH the associations on demand when I need them.
Thanks
What's the problem with letting these associations just be lazy loaded when requested in code? You can always detach the entity from your entity manager first and then reload it with the fetch joins that you need, based on context. Something like:
User u = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
entityManager.detach(u);
u = entityManager.createQuery("from User u join fetch ... where u.id = :id")
.setParameter("id", u.getId())
.getResultList()
.get(0);
// Work with u
You don't need to load #ManyToOne (like Todos) part of the associations at all.
Just create new TodoEntity, assign a user to it and save.
if you need to modify anything, better to work with all the user's associations separately from a user.

How to actualize entity in Spring JPA? Actualize or create new one?

I'm wondering what is best practice to update JPA entity in Spring project - update original entity or create new? I see these two approaches:
Use original - Actualize necessary fields in original entity and save this updated entity back to the repository.
Use copy - manually create new instance of entity, set all field from original entity (+ updated fields) into new entity and save the entity back to the repository.
What approach do you use / is recommended? And why?
When it comes to updating, the standard way would be to retrieve the entity reference(read below) and make changes within a transactional method:
private JpaRepository repo;
#Transactional(readOnly = false)
public void performChanges(Integer id){
Entity e = repo.getOne(id);
// alter the entity object
}
Few things regarding the example:
You would want to use the getOne method of JpaRepository as much as possible as it is in general faster than the findOne of the CrudRepository. The only trick is that you have to be sure that entity actually exists in the database with the given id. Otherwise you would get an exception. This does not occur regarding the findOne method so you would need to make that decision regarding each transactional method which alters a single entity within your application.
You do not need to trigger any persist or save methods on the EntityManager as the changes will be automatically flushed when the transaction is commited.. and that is on method return.
Regarding your second option, I dont think thats much of a use as you would need to get the data using above method anyway. If you intend to use that entity outside of the transaction, then again you could use the one retrieved from the exmaple above and then perform merge once it is again needed within the transactional context and thus Persistence Provider.
Getting an entity and then just updating that entity is the easiest way to do that. Also this is faster than a creation of a copy since EntityManager manages an entity and know that managed entity already exists in DB (so no need to execute additional query).
Anyway, there is third and the fastest approach: using executeUpdate on Query object.
entityManager
.createQuery("update EntityName set fieldName = :fieldName where id = :id")
.setParameter("fieldName", "test")
.setParameter("id", id)
.executeUpdate();
It is faster due to bypassing the persistent context

Spring framework. How to store some data to prevent each time access db

I have in my application list of customers and users. I would like get the list of them only on start. Then use data that is stored locally, not from DB.
Can You advice me some methods with examples?
I think about HttpSession object? But I am not sure is it ok?
Cause this data should be available only for logged user, that access it on start.
List of customers will be available on each page off application!
Take a look at Spring cache http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html
You can annotate your repository methods:
#Cacheable(value="customer", key="#prsonalNum")
public Customer findCustomer(String prsonalNum) {
...
}
The application will enter the method body only the first time. After that the value will be taken from the cache.
You can also evict the cache when you update some customer for example.
#CacheEvict(value="customer", allEntries=true)
public void addCustomer(Customer cust)
For synchronizing insert and update operations with the cache use #CachePut annotation and for synchronizing delete operations with the cache use #CacheEvict annotation.
Use the same cache name (value paramter) and same key value
And you should enable caching with #EnableCaching annotation on one of your configuration classes.
You can still use the HttpSession object, however, only put it in the session once, the user has logged in... and you can remove it from the session on page close...
If you want to reuse the data multiple times and do not want to query each time. i would suggested to create a simple Cache suing HashMap or HashTable.
You can easily save the list of customers across every id in such data structure.
and in spring you can easily create a singleton bean which will hold this hashmap and accessible across the application.

How to link an entity with database?

I have a complex entity with many relations, so I need many forms in many pages to create one. Therefore, I use a session to keep my entity.
Everything is going okay, but when comes the time to flush, the entity manager returns the "entity through relationship is not configured to cascade persist" thinking that some entities are new but they're actually stored in db !
For instance, I create a User with a ManyToOne Group, using $u->setGroup(Group $group); ($group being an existing group from the db). When I put it in session, then get it back in another page and then flush it, the entity manager tries to create a whole new group, not knowing that it is an existing one in db.
I use a little trick to overcome this :
$u = $this->get('session')->get('userToAdd');
$group = $em->getRepository('MyBundle\Entity\Group')->find($u->getGroup()->getId());
$u->setGroup($group);
With this, EM will recognize the group stored in db and the flush will go just fine, but with my entity having so much relationships like this, it is very convenient to do this for every single one.
Any ideas for this issue ?
Before find group try to refresh $u object.
$em->refresh($u)
You have to do:
$em->merge($u);

doctrine query in model or controller?

I'm using Codeigniter and Doctrine together for a project. I've gotten everything set up with both of these tools. But I'm not sure where I should have this bit of code:
$query = $em->createQuery('SELECT u FROM sessions u');
$sessions = $query->getResult(); // array of User objects
Should I be putting this in the controller or in the models/entities? At first thought, I figured I should put this kind of logic in the Sessions model, but it requires the entities manager $em, which I had thought should have been in the controller.
Thanks, this has been driving me crazy for the past half hour.
A lot of people like to create objects called DAOs or Data Access Objects to store this type of information.
The DAO contains the entity manager and methods that can be called and return the data you need. For example this function would reside in a DAO:
function findEmployeeById($emp_id)
And it would contain the query used to retrieve an employee from the database. In your controller you would just use the DAO instead of having an entity manager and dealing with it at that level.
But it really depends on preference and how large your project is.

Resources