2

I have an application that exposes a series of Web services. One of my entities has a bunch of one-to-many and many-to-many relations to other entities. My transactionality level is at DAO, and I'd like to keep it there to avoid the persistence context being open too long. Thinking of moving it up to the service level or specially leaving it open for the full request gives me the fears. So I had to mark all associations as eager.

The result is of course that when fetching it Hibernate generates a massive query that is starting to go slow. I went with 'premature optimization is the root of all evil' but it's something that needs to be dealt with already.

My problem is that, given that I want to keep @Transactional at DAO level, I am down to two (quite similar) options:

  1. Set the associations as lazy, and manually querying DB and setting them before each get of a corresponding property. Thus, where I have

    // entity
    @OneToMany(fetch = EAGER, mappedBy = "user", cascade = {ALL}, orphanRemoval = true)
    private final Set<Order> orders = new HashSet<>();
    
    // usage
    user.getOrders()
    

I'd change to:

    // entity
    @OneToMany(fetch = LAZY, mappedBy = "user", cascade = {ALL}, orphanRemoval = true)
    private final Set<Order> orders = new HashSet<>();

    // usage
    Set<Order> orders = orderDao.findByUserId(user.getId());
    user.setOrders(orders);
    user.getOrders()

This doesn't feel right, because I understand that the purpose of having an Hibernate relation is to let Hibernate manage it. On the other hand, I would still have the advantage of having Hibernate managing updates and inserts.

  1. Remove the relation at Hibernate level altogether, and manage them completely manually. Of course, this is not extremely, but quite painful.

Is there any other option? What's the usual optimization path in cases like this?

2

Common solutions to this class of problems:

  1. Use HQL. In DAO methods that are for specific purposes, it is easy to use a custom HQL with (left) join fetch to specify exactly what data you want to pre-populate in the model.

  2. Manually reference lazily loaded collections. E.g. manually call user.getOrders().size() in some DAO methods. This has the same effect as #1 but does not require HQL query.

  3. Selectively annotate service methods with @Transactional. You should really know exactly what data is needed for the view layer. Selectively extending the transactions up to the service layer should cover the other cases.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.