簡體   English   中英

Hibernate 4-> 5升級導致LazyInitializationException

[英]Hibernate 4 -> 5 upgrade leads to LazyInitializationException

我將項目從Spring 4.3.9.RELEASE + Hibernate 4.3.11.Final升級到了Spring Boot 2.1.4.RELEASE和Hibernate 5.3.9.Final。 查詢仍然可以正常工作,但是我收到了帶有@OneToMany類成員的LazyInitializationException

首先,我從@Transaction服務檢索該對象,該對象具有對@OneToMany列表的引用。 集合返回到控制器,然后從那里回到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。 在方法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方面的經驗非常少,因此我陷入了困境。 可能是配置問題,但我找不到。

正如M.Deinum所說的那樣,Spring 4.3.9.RELEASE + Hibernate 4.3.11.Final配置正在加載OpenSessionInViewFilter,這解釋了為什么所有查詢都成功進行的原因。 在Spring Boot中配置相同的過濾器后,一切恢復正常。 添加以下bean以注冊過濾器:

@Bean
public FilterRegistrationBean<OpenSessionInViewFilter> registerOpenSessionInViewFilterBean() {
    FilterRegistrationBean<OpenSessionInViewFilter> registrationBean = new FilterRegistrationBean<>();
    OpenSessionInViewFilter filter = new OpenSessionInViewFilter();
    registrationBean.setFilter(filter);
    return registrationBean;
}

下一步是將純Hibernate替換為JPA,將OpenSessionInViewFilter替換為OpenEntityManagerInViewFilter。

謝謝M. Deinum。

@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來實現。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM