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 :
We're using annotation configuration, so no xml configuration here.
For each action using the database, an EntityManager (defined as an attribute and @Autowired in the service) is created and then deleted (I can clearly see it in the logs when adding the configuration to see them), which apparently is normal according to the Spring documentation.
Using @PersistenceContext or @PersistenceUnit, either with a EntityManagerFactory or with EntityManager doesn't work.
I can load the lazy-loaded attribute I want to use with Hibernate.initialize(), and it then doesn't spawn a LazyInitializationException.
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());
}
}
}
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.