Issue with fetchType.Lazy...! - spring

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 :)

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();

Cache key issues with Jcache

I am using JSR107 caching with Springboot. I have following method.
#CacheResult(cacheName = "books.byidAndCat")
public List<Book> getAllBooks(#CacheKey final String bookId, #CacheKey final BookCategory bookCat) {
return <<Make API calls and get actual books>>
}
First time it makes actual API calls, and second time it loads cache without issue. I can see the following part of log.
Computed cache key SimpleKey [cb5bf774-24b4-41e5-b45c-2dd377493445,LT] for operation CacheResultOperation[CacheMethodDetails ...
But the problem is I want to load cache without making even first API call, Simply needs to fill the cache like below.
String cacheKey = SimpleKeyGenerator.generateKey(bookId, bookCategory).toString();
cacheManager.getCache("books.byidAndCat").put(cacheKey, deviceList);
When I am checking, hashcode of cachekeys are same in both cases, But it is making API calls. If the hashcode is same in both cases, why it is making API calls without considering the cache ?
When debugging spring classes identified that, org.springframework.cache.interceptor.SimpleKeyGenerator is used with the cache key generation even #CacheResult is there.
EDIT and enhance the question :
Apart from that if getAllBooks has overloaded methods, and then call this cached method via separate overloaded method, in that case also method caching is not working.
I'm not an expert of JSR107 annotations in the context of Spring. I use the Spring Cache annotations instead.
When using JSR107, the key used is a GeneratedCacheKey. So that's what you should put in your cache. Not the toString() of it. Note that SimpleKeyGenerator isn't returning a GeneratedCacheKey. It returns a SimpleKey which is the key used by Spring when using its own cache annotations instead of JSR-107. For JSR-107, you need a SimpleGeneratedCacheKey.
Then, if you want to preload the cache, just call getAllBooks before needing it.
If you want to preload the cache in some other way, a #javax.cache.annotation.CachePut should do the trick. See its javadoc for an example.
As #Henri suggested, we can use the cacheput. But for that we need methods. With the below we can update the cache very similar to the cacheput,
//overloaded method both id and cat is available.
List<Object> bookIdCatCache = new ArrayList<>();
bookIdCatCache.add(bookId);
bookIdCatCache.add(deviceCat);
Object bookIdCatCacheKey = SimpleKeyGenerator.generateKey(bookIdCatCache.toArray(new Object[bookIdCatCache.size()]));
cacheManager.getCache("books.byidAndCat").put(bookIdCatCacheKey , bookListWithIdAndCat);
//overloaded method only id is there
List<Object> bookIdCache = new ArrayList<>();
String nullKey = null
bookIdCache.add(bookId);
bookIdCache.add(nullKey);
Object bookIdCacheKey = SimpleKeyGenerator.generateKey(bookIdCache.toArray(new Object[bookIdCache.size()]));
cacheManager.getCache("books.byidAndCat").put(bookIdCacheKey , bookListWithId);
//Not correct(My previous implementation)
String cacheKey = SimpleKeyGenerator.generateKey(bookId, bookCategory).toString();
//Correct(This is getting from spring)
Object cacheKey = SimpleKeyGenerator.generateKey(bookIdCatCache.toArray(new Object[bookIdCatCache.size()]));

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

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.

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.

Resources