Lazy Initialization Hibernate in Struts Spring Hibernate - spring

I save current user in session, and when i use current user (example:user.getRole.getRoleName()), i got LIE. How can i solve this problem, my code is like this
Controller:
public String home(){
Users users = userService.getCurrentUser();
if(users.getRole().getRoleName().equals("admin")){ //causing LIE
....
}
UserService :
#Override
public Users getCurrentUser(){
session = ActionContext.getContext().getSession();
return (Users) session.get("user");
}
But, when i change userService.getCurrentUser() to be like this, error is resolved but i think this is not a right manner, because it need connection to database every time i use current user.
#Override
public Users getCurrentUser(){
session = ActionContext.getContext().getSession();
return daoManager.getDetailUser(((Users) session.get("user")).getUsername());
}
DaoManager.getDetailUser is like this
#Override
public Users getDetailUser(String username) {
try {
Users user = (Users)sessionFactory.getCurrentSession().createQuery("SELECT U FROM Users U WHERE U.username=:USERNAME")
.setParameter("USERNAME", username)
.uniqueResult();
return user;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
is there any other better way to solve this problem? Thank you.

The easiest way to resolve this is just to access the lazy fetched field before the session is closed:
#Override
public Users getCurrentUser(){
session = ActionContext.getContext().getSession();
Users user = (Users) session.get("user");
user.getRole(); //Accessed to resolve lazy field
return user;
}
I do not recommend FetchType.EAGER. With that you cannot control access, it is always fetched whether you need it or not.. Add a few EAGER fields to your data model and suddenly your fetching the entire database for the simplest requests..
For queries you can also use JOIN FETCH

The most likely explanation will be that Spring closes the current session when you are exiting the service layer (UserService), however, after this happens, because Hibernate attempts to lazily load the children objects to avoid unnecessarily loading data (see also What is lazy loading in Hibernate?).
To avoid this, you could either ensure that hibernate does not do lazy loading by specifying fetch=FetchType.EAGER on the role, or use the OpenSessionInView pattern (however, that's sometimes considered an antipattern, see Why is Hibernate Open Session in View considered a bad practice?).

Related

Find Object in List Cacheable using Ehcache in spring boot

I want to use cache in my application using ehcache with spring boot.
So i want to cache a list of users and when admin want to find user by email for example not use JpaRepository but i want to find in list of users cachable.
To cache list of users i write below code
#Override
#Cacheable(cacheNames = "users")
public List<User> getList() {
return userRepository.findAll();
}
To find a user by email i use for instant code like below :
List<User> users = getList();
User userByEmail(String email){
for(User user: users){
if(user.getEmail().equals(email)){
return user;
}
}
return null;
}
I know this is not a good why, but i don't find a good solution.
Anyone help me to use cache correctly and find user using Cacheable list of users.
You should have a method which takes email as an input and returns user with that email from database.
Add #cacheable on that method so that it will only execute an expensive query to the database first time and add the result to the cache .For any subsequent call to the method it will return the data from cache without actually executing the body of the method.
#Cacheable("users")
public User getUserByEmail(String email) {
return userRepository.findUserByEmail(email);
}
You can create a new method as below
#Cacheable(value = "user", key = "#email")
public User findUserByEmail(String email) {
//Logic to retrieve the data
}
The first time you call this method it will fetch the data from the source and popular the Cache. Next time you will get it from the Cache.

#cacheput is not updating the existing cache

I am working with Spring 4 and Hazelcast 3.2. I am trying to add a new record to existing cache with below code. somehow cache is not getting updated and at the same time I don't see any errors also. below is the code snippet for reference.
Note:- Cacheable is working fine, only cacheput is not working. Please throw light on this
#SuppressWarnings("unchecked")`enter code here`
#Transactional(readOnly = true, propagation = Propagation.REQUIRED)
#Cacheable(value="user-role-data")
public List<User> getUsersList() {
// Business Logic
List<User> users= criteriaQuery.list();
}
#SuppressWarnings("unchecked")
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
#CachePut(value = "user-role-data")
public User addUser(User user) {
return user;
}
I had the same issue and managed to solved it. The issue seemed to be tied to the transaction management.
Bascially updating the cache in the same method where you are creating or updating the new record does not work because the transaction was not committed. Here's how I solved it.
Service layer calls repo to insert user
Then go back to service layer
After the insert /update db call
In the service layer I called a refresh cache method
That returned the user data and this method has the cacheput annotation
After that it worked.
An alternative approach is you could use #CacheEvict(allEntries = true) on the method used to Save or Update or Delete the records. It will flush the existing cache.
Example:
#CacheEvict(allEntries = true)
public void saveOrUpdate(Person person)
{
personRepository.save(person);
}
A new cache will be formed with updated result the next time you call a #Cacheable method
Example:
#Cacheable // caches the result of getAllPersons() method
public List<Person> getAllPersons()
{
return personRepository.findAll();
}

Spring + Hibernate : LazyInitializationException

I am getting the LazyInitializationException when i try to retrieve information inside a POJO.
User.java
public class User implements java.io.Serializable {
private Set groups = new HashSet(0);
public Set getGroups() {
return this.groups;
}
}
UserController.java
#RequestMapping(value = "/home", method = RequestMethod.GET)
public ModelAndView getHome(HttpServletRequest request) throws Exception {
ModelAndView mv;
User user = SessionUtil.getSessionUser(request);
if (user == null) {
mv = new ModelAndView("redirect:/user/login");
} else {
mv = new ModelAndView("home");
user = this.userService.getUserById(user.getId());
// Exception here
Set<Group> groups = user.getGroups();
mv.addObject("groups", groups);
// This work fine
List<Group> invitation_groups = this.userService.getInvitationGroups(user);
mv.addObject("invitation_groups", invitation_groups);
// This work fine
List<Group> subscription_groups = this.userService.getSubscriptionGroups(user);
mv.addObject("subscription_groups", subscription_groups);
}
return mv;
}
Database
=====
-User-
id
login
=====
-Goup-
id
user (Foreign key to user)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:285)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
at model.pojo.User_$$_jvst464_2.getGroups(User_$$_jvst464_2.java)
at controller.UserController.getHome(UserController.java:151)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
I think I understand why I get this exception : I always close the HibernateSession after all my transaction in my DAO so the session can't be open for the POJO request.
In an other hand user.getLogin() for exemple works. I think i dont understand well where the problem is. Is that because it uses a foreign key ?
I think i found a workaround here but I dont know how to implement it and if it's really efficient.
I know that if I remove session.close() from my DAO it will works but it's not the solution.
I hope someone can help me. Thanks.
Solution
Remove all the hand made transactions
Add transactionnal annotation
User OpenSessionInView filter.
Thanks guys.
Why are you handling your session manually? Do you need that?
If not, you should use OpenSessionInView pattern. It will keep your session open until the request ends, but, be careful, you can run in trouble with lots of queries made to the database because the lazy load of collections. So whenever you can, try to fetch your data eagerly if you know that they will be used.
Your user.getLogin() returns a string right? Even if it was the one side of a relationship mapping, it would be fetched eagerly by default.
I'm not used with spring but I think spring has an OpenSessionInView filter to manage your session.
Its normal to handle transaction in API layer and using DTO,
So you have: API -> Service -> DAO.
But since you only have transactional in DAO its probably okai, but then you have to take care of lazyload object in DAO., before transaction is closed.
// after this the transaction is open and closed, user object is hibernate jpa entity you usually get this.
user = this.userService.getUserById(user.getId());
The simplest solution is to loop through and do getId() in DAO, before returning user.
Set<Group> groups = user.getGroups();
for (Group group in groups){
group.getId();
}

Apache Wicket: Injecting dependencies in Session (using Guice)

I'm using the Wicket Auth/Roles and I ran into the same problem as the OP of this thread.
I need to access the DB service layer in the AuthenticatedWebSession (for user authentication). I followed Steve Flasby's suggestion and did the following:
#Override
public Session newSession(Request request, Response response) {
Session s = new MySession(request);
mInjector.inject(s);
return s;
}
Unfortunately this results in
java.lang.IllegalStateException: EntityManager is closed
(presumably due to the fact that (a) I'm using open session in view, and (b) the session spans over several requests).
I solved this by moving the injection into the AuthenticatedWebSession.authenticate method.
#Override
public boolean authenticate(String username, String pass) {
Injector.get().inject(this);
...
}
I suspect that this is not best practice, because now I need to access to the service layer in other methods too, and it doesn't seem like a good idea to add Injector.get().inject(this) in each such method.
My question:
How do I perform injection into the session object upon each request? (Or, if this is a bad approach all together, how do I access the service layer in the AuthenticatedWebSession?)
You can implement IRequestCycleListener (extend AbstractRequestCycleListener) and implement:
#Override
public void onBeginRequest(RequestCycle cycle)
{
if (Session.exists()) {
Injector.get().inject(Session.get());
}
}
Register your IRequestCycleListener in Application#init() with getRequestCycleListeners().add(new YourRequestCycleListener()).

spring hibernate - share session beeween DAOs

I want to use Spring + Hibernate in my web application.
My application is written without Spring.
When "open page" action is called I open Hibernate Session, store it in Http Session and share it between my DAOs. When save action is called I start transaction using my old session.
But now I want to migrate my old DAOs to HibernateDaoSupport based DAOs.
How can I share session in this case? If my DAOs reference to the one SessionFactory in beans.xml will they share the same session?
How can I manage session in this case(open new or use old)?
I have write the following code but I get
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
on page System.out.println(obj.getCategory().getName());
public class CategoryObjectDAOSpringImpl extends HibernateDaoSupport implements CategoryObjectDAO {
#Override
public CategoryObject get(int id) throws Exception {
CategoryObject obj = getHibernateTemplate().get(CategoryObject.class, id);
System.out.println(obj.getId());
System.out.println(obj.getCategory().getName());
for (ObjAttrCommon objAttr : obj.getAttributes()) {
//objAttr.setSession(getSession());
System.out.println(objAttr.getId());
}
return obj;
}
It is strange that if I add
getSessionFactory().openSession();
call at the top I have the same exeption.
I think the same awswer is valid for this post:
If your "unit of work" cannot be automatically per request, I think you can create it manually in your service layer using a transaction. Something like this:
public Object serviceMethod(params) {
TransactionTemplate transactionTemplate = getHibernateTemplate().get(CategoryObject.class, id);
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus status) {
try {
// call your DAO's to update/delete/... and set values to service
} catch (DAOException e) {
LOGGER.error(e);
throw new ServiceException(e);
}
}
});
}
EDITED: So extrange that the exception appears within a transaction. Make sure that the problem is not in your view. If after you retrieve your entites using HibernateTemplate you access it from the view it will explain the LazyException, because when you access your objects from the view, your Hibernate session will be already closed.
In you're using your models in your JSP/JSF your need to extend your unit of work to cover all request context. You have to include a filter that manages your Hibernate session opening, what is called Open Session In view Pattern, take a look at this.
Copy the filter implementation that appears in this post and include it in your application, it should solve the problem.

Resources