javahibernatespring-bootlazy-loading

LazyInitializationException : Why can't Hibernate create a session on lazy loading?


My project use to have a lot of @Transactional method everywhere. Now because of business logic I do not want to rollback when I have an issue, but want to set my object to an error status (aka saved in the db, so definitly not rollback), so I removed a few @Transactional to start.

Now the issue is where there is a lazy loading there is no session, thus spawning a LazyInitializationException.

Now here are my following trouble-shooting and solution seeking so far :

Now my question is : Why can't hibernate do it by itself ? It's seems trivial to me that if I'm using a lazy loading, I want Hibernate to create a session (which he seems perfectly able to do when doing Hibernate.initialize()) to load the date automatically.

Would there be a way to spawn a new entity manager to be use inside my method so Hibernate doesn't create and recreate one all the time ? I really feel like I'm missing something basic about Hibernate, lazy loading and sessions that makes this whole mess a lot less complicated.

Here is an example :

@Entity
@Table(name = "tata")
public class Tata {

    @Id
    @Column(name = "tata_id")
    private Long id;

    // getter / setter etc
}

@Entity
@Table(name = "toto")
public class Toto {

    @Id
    @Column(name = "toto_id")
    private Long id;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "tata_id")
    private Tata tata;

    // getter / setter etc
}

@Service("totoManager")
public class TotoManager extends GenericManagerImpl {

    @Autowired
    private EntityManager entityManager;
    @Autowired
    private TotoRepository totoRepository;

    public void doSomethingWithTotos() throws XDExceptionImpl {

        List<Toto> totos = this.totoRepository.findAll();

        for (toto toto : totos) {
        // LazyInitializationException here
            LOGGER.info("tata : " + toto.getTata().getId());
        }
    }
}

Solution

  • Hibernate can do it by itself. By setting property hibernate.enable_lazy_load_no_trans=true (for spring boot it should be spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true), you can load any lazy property when the transaction is closed. This approach has a huge drawback: each time you load a lazy property, Hibernate opens a session and creates a transaction in the background.

    I would recommend fetching lazy properties by entityGraphs (archived). So you don't have to move persistent context to an upper level or change the fetch type in your entities.