How to not allow lazy loading from outside the transnational method? - spring

I'm using JPA with Hibernate and Spring. I have an entity (Say Employee) with an attribute (Say of type Position) and this attribute is lazy-loaded.
I believe that when you try to access the position attribute, it will be lazy loaded from the DB and this is done inside the transnational method.
Let's say I didn't access the attribute in that transnational method. So if I tried to access it later, I would get "org.hibernate.LazyInitializationException: could not initialize proxy - no Session" which is normal because the session was closed by that transnational method.
At this point, I need it null (or not initialized) wherever I access it later in different method but this is not the case! The question is how can we make it null after committing and closing the session because it is not accessed while the session is open?
Below is a simple code to illustrate the issue.
// In some Service class
#Override
#Transactional(readOnly = true)
public Employee getEmployeeById(Integer id) throws Exception {
Employee emp = employeeDAO.getEmployeeById(id);
// I didn't access the position attribute here because I don't need it for now
return emp;
}
Later I call the above method (Say from some controller):
Employee emp = employeeService.getEmployeeById(904);
System.out.println(emp.getPosition()); // Here, the LazyInitializationException
//would occur, but I need this to be null or at least to prevent the lazy loading,
//thus, avoiding the exception. How?

I think this might be the answer that you're looking for
Hibernate - Avoiding LazyInitializationException - Detach Object From Proxy and Session
Basically
Use Hibernate to check if that field is initialised with Hibernate. isInitialized(fieldName)in the getter and return null if not initialised.
Inside employeeDAO.getEmployeeByIdmethod, create a new Employee object and set the parameters from the one that return from query, which is more work but prevent you to couple your domain to Hibernate.

Related

How to execute save operation immediatly in a #Transactional method

Say I have following code snippet
#Transactional
public void doSthing(){
// save an enetity to db
SomeClass entityA = new entityA();
mapper.save(entityA);
// I got null here!
Integer id = entityA.getId();
anotherEntity.setVal(id);
otherMapper.upate(anotherEntity)
}
as u see, I need the entityA's id to update another entity, but it's null at that time, if I remove the #Transactional it works, but I want the two operations in tansaction, which mean that i need spring rollback the doSthing() method on any opereation failured.
By default, methods annotated with #Transactional will rollback on any RuntimeException. So you can achieve the rollback by throwing some runtime exception under some condition.
If you want to rollback on any exception just add the following:
#Transactional(rollbackFor=Exception.class)
But what #Delinum said in the comment is true in general, that is, if you invoke a save on a dao/repository it should assign an #Id to the value object that you are saving, making it an entity.
I don't know what is type of your 'mapper' instance, but some implementations could work in a way that when you call save it doesn't change the original object, but rather it returns the persisted object. So instead of this:
mapper.save(entityA);
// I got null here!
Integer id = entityA.getId();
Use this:
Integer id = mapper.save(entityA).getId();

Spring JPA Update operation

I am working on Spring JPA. As part of it, I have to update an entity ignoring few attributes. The following code is in effort to implement the update operation.
#Transactional
public void updateDMove(DTCRto jsonRto){
//copyProperties(Object source, Object target, String[] ignoreProperties)
DMove dMoveDB = dMoveRepo.findDMove(jsonRto.getLn(), jsonRto.getDriver(), jsonRto.getType());
DMove dMoveRto = jsonRto.convertToDMove(jsonRto);
BeanUtils.copyProperties(dMoveRto,drayMoveDB, new String[] {"moveId", "created","lastchange","locations","status"});
dMoveRepo.save(dMoveDB);
}
DMove : Model class which needs to be updated.
dMoveRepo : respective repository class.
dMoveRto : incoming object.
dMoveDb : object existing in the database.
moveId : is the PK in the DMove class.
Can anyone suggest me what is the way to implement the update operation in Spring JPA ?
Thanks.
detached entity passed to persist means that hibernate doesn't recognize the entity you passed to update, because dMoveDB isn't a persistent object, you lost that when you used this line BeanUtils.copyProperties(dMoveRto,drayMoveDB, new String[] {"moveId", "created","lastchange","locations","status"});
I suggest you remove the moveId so the entity you try to update keeps its orginal primary key and remains as a persistent object.
One last thing, you have to make sure that the object you get from dMoveRepo.findDMove(...) isn't null

Spring #Cacheable with filter

Every entity class has user.id value, I have filters on all services which filters data by principal.id and entity user.id on database level, simply adds where clause. I started to using #Cacheable spring option. But filters not works with spring-cache. How can I filter data from cache ?
#Override
#Cacheable(value = "countries")
public List<Country> getAll() {
return countryDao.findAll();
}
Different user has access to values other users if values are in cache.
From documentation
"As the name implies, #Cacheable is used to demarcate methods that are cacheable - that is, methods for whom the result is stored into the cache so on subsequent invocations (with the same arguments), the value in the cache is returned without having to actually execute the method."
In your case you don't have arguments therefore every time getAll is invoked it will return the cached version.
If your countryDao.findAll() inject the userid at database level, you have an issue as the first user calling countryDao.findAll() will cause his result to be cached, therefore other users will get the same result of the first user.
In general, if I understood how you designed the service, it is common that you don't inject the user at db level but pass it at service level so that this is decoupled from the current session (for example a web request).
However if you want to keep like that, it could still work by doing:
#Cacheable(value = "countries", key="#user.id")
public List<Country> getAll(User user) {
return countryDao.findAll();
}
All you have to do is pass the user to the method even if you don't use it explicitly (but the caching will).

javax.persistence.EntityNotFoundException: deleted entity passed to persist

I am using spring + JPA as orm framework. My project layer structure is like web --> Service --> Domain DAO --> genericDAO.
In genericDAO I am injecting EntityManager using #PersistenceContext.
genericDAO.delete(Object o) {
o = entityManager.merge(o);
entityManager.remove(o);
}
genericDAO.saveOrUpdate(Object o) {
entityManager.merge(o);
entityManager.flush();
}
In one method in service layer, I have following operations.
// delete order item if already exists.
Order order = getOrderFromSession();
if (CollectionUtils.isNotEmpty(orderItems)) {
Iterator<OrderItem> iterator = orderItems.iterator();
while (iterator.hasNext()) {
OrderItem orderItem = iterator.next();
iterator.remove();
orderDAO.deleteOrderItem(orderItem); // Which internall calls genericDAO.delete()
}
}
//orderDAO.saveOrder(order) // line Y
//Now create fresh order items submitted by jsp form.
for (ProductVO productVO : productList) {
if (productVO.getQuantity() > 0) {
orderItem = new OrderItem();
Product product = productDAO.getProductByCode(productVO.getCode()); // line X
orderItem.populateOrderItemByProduct(product, productVO.getQuantity(), order);
order.addOrderItem(orderItem);
}
}
Line X retrieve product entity using hql. But when line X is executed, I get below error.
javax.persistence.EntityNotFoundException: deleted entity passed to persist: [core.entity.OrderItem#].
I do not understand if order item is already marked as deleted in entity manager, why it tries to persist.
When I uncomment line Y, which internally flush the entity manager, it works fine. I do not understand why it requires entity manager to be flushed before executing line X
Here is a quote from hibernate documentation
Transactional persistent instances (i.e. objects loaded, saved,
created or queried by the Session) can be manipulated by the
application, and any changes to persistent state will be persisted
when the Session is flushed. There is no need to call a particular method (like update(), which has
a different purpose) to make your modifications persistent. The most
straightforward way to update the state of an object is to load() it
and then manipulate it directly while the Session is open.
Sometimes this programming model is
inefficient, as it requires in the same session both an SQL SELECT to
load an object and an SQL UPDATE to persist its updated state.
Hibernate offers an alternate approach by using detached instances.
But I'll try to explain simplier. Your method getOrderFromSession() is transactional and hibernate objects have session open inside it, but when object order is returned to you, it has been detached from session and hibernate doesn't know what you are doing with it, until you persist him again. So for deleted items hibernate will find out when you save that object, until then object in hibernate have same state as it was in a moment when getOrderFromSession() has return it.
Here you have detailed explanation
UPDATE:
When you delete object in hibernate, object in java becomes transient. It still exist in java and after delete you can use it.
Session.delete() will remove an object's state from the database. Your
application, however, can still hold a reference to a deleted object.
It is best to think of delete() as making a persistent instance,
transient.

Issue with fetchType.Lazy...!

I am new to annotation based Transaction management. I am developing web application with spring 3.1, Hibernate 3.2.
I am have some issues with #Transactional.
I am going to load POJO which having set of child as below:
Parent class is DealerMaster that has set of DealerMember so I declared as
in DealerMaster:
#OneToMany(fetch = FetchType.LAZY,mappedBy = "dealerMaster")
#Cascade({ CascadeType.SAVE_UPDATE, CascadeType.DELETE_ORPHAN})
public Set<DealerMember> getDealerMembers() {
return this.dealerMembers;
}
and i am using my HibernateUtil class to execute any query, so I am using load method:
public static Object loadObject(Session paramSession, Object paramObject)
throws Exception
{
try
{
paramObject = paramSession.load(paramObject.getClass(), getId(paramObject));
}
catch (HibernateException localHibernateException)
{
setErrorMessage(paramObject, localHibernateException.getMessage());
localHibernateException.printStackTrace();
}
Now I the problem is my load method runs successfully but when move courser on paramObject it shows :
com.sun.jdi.InvocationException occurred invoking method.
and all values are accessible at service layer only when i try to access it at controller side it throw error:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.lbt.model.DealerMaster.dealerMember, no session or session was closed
When i googled on same issue I found that this related to lazy loading but i tried as lazy = false also fetchType.Eager but same issue.
You must understand one thing about LazyInitialzation exception .It occurs when hibernate is unable to initialize object that have be set to fetch lazy and it mostly happens when session is closed on an object that contains sets of other objects that are set to lazy fetch.
While setting fetch to EAGER works, it might not work in some cases when there is hierarchy of objects involved.
For example: Users-->Contain set of Roles and Roles---> Contain set of Permissions.
NOw if all are on lazy fetch. IF I get user object and close session and then try to get roles out of it, I will get exception.If I set fetch eager for role in user POJO, still I get that exception because role POJO contains permission which is on lazy fetch.
So either set all in hierarchy to EAGER(have performance issues but will work).
OR close session after you have finished working on object(keeping lazy fetch).
Things will work :)

Resources