我将项目从Spring 4.3.9.RELEASE + Hibernate 4.3.11.Final升级到了Spring Boot 2.1.4.RELEASE和Hibernate 5.3.9.Final。查询仍然可以正常工作,但是我得到了一些LazyInitializationException
类成员的@OneToMany
。
首先,我从@OneToMany
服务中检索引用了@Transaction
列表的对象。集合返回到控制器,然后从那里回到Spring序列化为json。控制器具有@RestController
,因此它知道该怎么办。
在Spring 4.3.9.RELEASE + Hibernate 4.3.11.Final中,一切都很好,即使OpenEntityManagerInView
未通过配置启用,并且集合未使用EAGER模式加载。但是在Spring Boot 2.1.4.RELEASE和Hibernate 5.3.9.Final中,相同的东西不再起作用。我尝试通过设置spring.jpa.open-in-view=true
来启用OEMIV,但即使这样似乎也不起作用或被某个地方覆盖。
如果我为该集合启用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。在Hibernate 4的doGetTransaction()方法中,它在调用
时会找到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方面的经验非常少,因此我陷入了困境。可能是配置问题,但我找不到。
答案 0 :(得分:1)
from groups.api.views.memberViews import MemberViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'(?P<reference>[-\w]+)', MemberViewSet, base_name='member')
urlpatterns = router.urls
下一步是将纯Hibernate替换为JPA,将OpenSessionInViewFilter替换为OpenEntityManagerInViewFilter。
感谢Deinum。
答案 1 :(得分:0)
@xxxToMany
注释表示默认情况下,访存类型为LAZY
。这意味着您需要初始化实体引用的集合。
例如。
@Entity
public class Book {
@OneToMany
public List<Author> authors;
}
解决此问题的方法很少。您可以使用以下方法修改@OneToMany
批注:
@OneToMany(FetcType=FetchType.EAGER)
或者创建一个方法来初始化authors
,例如:
public void initializeAuthors(Book book) {
Book b = em.find(Book.class, book.getId());
List<Author> authors = new ArrayList<>(b.getAuthors());
book.setAuthors(authors);
}
如果您的实体上有@NamedQueries
,则可以通过在集合上添加LEFT JOIN FETCH
来实现。