Я обновил проект с Spring 4.3.9.RELEASE + Hibernate 4.3.11. Окончательная версия до Spring Boot 2.1.4.RELEASE и Hibernate 5.3.9.Final. Запросы все еще работают нормально, но я получаю LazyInitializationException
с некоторыми @OneToMany
членами класса.
Сначала я извлекаю объект, имеющий ссылку на список @OneToMany
, из службы @Transaction
. Коллекция возвращается контроллеру и оттуда возвращается в Spring для сериализации в json. Контроллер имеет @RestController
, поэтому он знает, что делать.
Весной 4.3.9.RELEASE + Hibernate 4.3.11. Наконец-то все было хорошо, хотя OpenEntityManagerInView
не был включен конфигурацией и коллекция не была загружена в режиме EAGER. Но в Spring Boot 2.1.4.RELEASE и Hibernate 5.3.9.Final то же самое больше не работает. Я попытался включить OEMIV, установив spring.jpa.open-in-view=true
, но даже это, похоже, не работает или где-то переопределяется.
Если я включаю режим загрузки EAGER для этой коллекции, все работает нормально.
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
@Entity
@JsonSerialize(using = TemplateSerializer.class)
public class Template implements Serializable {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne
private ObjFormat objFormat;
@OneToOne
@JoinColumn(name = "event_id")
@OnDelete(action = OnDeleteAction.CASCADE)
private Event event;
@OneToMany
@JoinColumn(name = "category_id")
private List<Category> linkToCategories;
Проблема вызвана полем linkToCategories. Если я настрою @OneToMany (fetch = FetchType.EAGER), все будет нормально.
Конфигурация приложения:
@Bean
public LocalSessionFactoryBean sessionFactory(DataSource dataSource) throws ClassNotFoundException {
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource(dataSource);
localSessionFactoryBean.setPackagesToScan("com.project.backend.model",
"com.project.backend.hibernate.converters");
return localSessionFactoryBean;
}
@Bean
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}
Позже редактировать:
После многих отладок разница между старой и новой функциональностью Hibernate заключается в HibernateTransactionManager. В методе doGetTransaction () в Hibernate 4 он находит объект SessionHolder при вызове
TransactionSynchronizationManager.getResource (getSessionFactory ())
в то время как в Hibernate 5 это не так.
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
if (sessionHolder != null) {
if (logger.isDebugEnabled()) {
logger.debug("Found thread-bound Session [" + sessionHolder.getSession() + "] for Hibernate transaction");
}
txObject.setSessionHolder(sessionHolder);
}
else if (this.hibernateManagedSession) {
try {
Session session = this.sessionFactory.getCurrentSession();
if (logger.isDebugEnabled()) {
logger.debug("Found Hibernate-managed Session [" + session + "] for Spring-managed transaction");
}
txObject.setExistingSession(session);
}
catch (HibernateException ex) {
throw new DataAccessResourceFailureException(
"Could not obtain Hibernate-managed Session for Spring-managed transaction", ex);
}
}
В методе doBegin новый сеанс создается и устанавливается для txObject для каждого запроса.
if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
Interceptor entityInterceptor = getEntityInterceptor();
Session newSession = (entityInterceptor != null ?
getSessionFactory().withOptions().interceptor(entityInterceptor).openSession() :
getSessionFactory().openSession());
if (logger.isDebugEnabled()) {
logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction");
}
txObject.setSession(newSession);
}
Мой опыт работы с Hibernate довольно небольшой, поэтому я застрял. Это, вероятно, вещь конфигурации, но я не могу ее найти.