Dani Sancas
Dani Sancas

Reputation: 1375

Chain DAO methods in Spring/Hibernate for Lazy initializations

I have some model classes with relations to each other (User, Group, Message, etc). For many reasons (I'm not giving the details, but this is not a flexible decision) the relations are Lazy, and I want them to remain Lazy.

Sometimes I want to load some class collections. P.e. user.getGroups() or user.getMessages() but, because of the Lazy load, I need to call Hibernate.initialize() in the method of the DAO class, which is OK for me.

The question is, Is there a strategy to avoid declaring many DAO methods to load several combination of collections, reusing the methods?

Here is an example of what I want to avoid:

UserController:

public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/user/view/{id}", method = RequestMethod.GET)
    public String view(@PathVariable Long id, Model model) {
        // Choose one between the following, depending on the case
        User user = userService.getUser(id);
        User user = userService.getUserWithGroups(id);
        User user = userService.getUserWithGroupsAndMessages(id);
        //...
    }
}

And the UserDAOImpl:

@Repository
public class UserDAOImpl implements UserDAO {
    @Override
    public User getUser(long id) {
        return (User) this.getCurrentSession().get(User.class, id);
    }

    @Override
    public User getUserWithGroups(long id) {
        User user = (User) this.getCurrentSession().get(User.class, id);
        Hibernate.initialize(user.getGroups());
        return user;
    }

    @Override
    public User getUserWithGroupsAndMessages(long id) {
        User user = (User) this.getCurrentSession().get(User.class, id);
        Hibernate.initialize(user.getGroups());
        Hibernate.initialize(user.getMessages());
        return user;
    }
}

The point is I want to avoid creating multiple DAO methods for each combination of collections that must be loaded for each case. I'd like to achieve a call syntaxis in the Controller similar to User user = userService.getUser(id).initGroups().initMessages();, to chain only the specific methods I need in every case.

But in this particular example, the initXXX() methods would be in the model class User, which may not contain any @Autowired service, and because of that is not a possible solution.

Any ideas?

--EDIT--

Some non-working ideas:

Option 1. Declare initGroups() in User model class:

User

@Entity
@Table(name="user")
public class User implements Serializable {

    //...

    public User initGroups() {
        Hibernate.initialize(getGroups());
        return this;
    }
}

Called from Controller this way: User user = userService.getUser(id).initGroups();

Option 2. Declare initGroups() in UserService and UserDAO:

UserServiceImpl

@Service
@Transactional
public class UserServiceImpl implements UserService {

    //...

    @Override
    public User initGrupos(User user) {
        return userDAO.initGroups(user);
    }
}

UserDAOImpl

@Repository
public class UserDAOImpl implements UserDAO {

    //...

    @Override
    public User initGroups(User user) {
        Hibernate.initialize(user.getGroups());
        return user;
    }
}

Called from Controller this way:

User user = userService.getUser(id);
user = userService.initGroups(user);

Result (both the same):

org.hibernate.HibernateException: collection is not associated with any session
    org.hibernate.collection.internal.AbstractPersistentCollection.forceInitialization(AbstractPersistentCollection.java:484)
    org.hibernate.Hibernate.initialize(Hibernate.java:78)
    ...

Upvotes: 2

Views: 948

Answers (1)

dunni
dunni

Reputation: 44545

The init methods don't need the service, they just need to call Hibernate.initialize while maintaining the previous Hibernate session which loaded the object in the first place (that is the most important part).

Upvotes: 2

Related Questions