Search code examples
javaspringhibernatejpalazy-initialization

JPA / Hibernate Spring @Transactional vs. JOIN FETCH


i'am facing with interesting solution of LazyInitializationException. To prevent this (on OneToMany oder ManyToMany) one known solution is, to use JOIN FETCH Query. You can see one of severals examples her: https://thoughts-on-java.org/best-practices-for-many-to-many-associations-with-hibernate-and-jpa/

Other easier solution is, to use @Transactional from Spring. For example like this:

@DeleteMapping(value ="/product/{tagId}")
    @ResponseBody
    @Transactional
    public String deleteProductWithoutRelation(@PathVariable String product, Model model) {     
        Optional<Product> pr = productService.selectProduct(product);
        if (pr.isPresent()) {
            tag.get().getCustomer().size(); //usualy throws LazyInitializationException, 
                                            //without JOIN-FETCH Statment or @Transactional
        return deletedTagId;    
    }

Of course, you can place @Transactional of some method from repository service, to encapsulate this solution. So which Advantages or Disadvantages of both solutions are here?


Solution

  • There's a couple of things we need to untangle here.

    1. @Transactional means nothing more than Spring makes sure to open up a database connection(+ transaction) and closes it again. That's it.
    2. When you select an entity containing a lazy field, you are essentially saying: I am selecting "some" fields from my entity, except the lazy one.
    3. If you, however, need that lazy field later on because you are trying to access it in your views (.html, .ftl, .jsp, whatever) you need to issue another select to the database to fetch it.
    4. The problem: At that point, if you are outside of a @Transactional method you do not have a database connection open anymore, hence the LazyInitException.
    5. To sum up: Your fetch makes sure to issue 1 select for all the data. If you do NOT do that, you need an open database connection/transaction, which @Transactional gives you.

    Recommendation: You should try and fetch all the data you need to render a view, with appropriate JPQL/Criteria/SQL statements and not rely on re-selecting lazy-fields too much.