2

當我嘗試獲取延遲初始化實體時,我在我的IDE中看到以下異常消息(我無法找到它存儲在代理實體中的位置,因此無法提供整個堆棧跟蹤此例外):LazyInitializationException嘗試獲得延遲初始化實例

Method threw 'org.hibernate.LazyInitializationException' exception. Cannot evaluate com.epam.spring.core.domain.UserAccount_$$_jvste6b_4.toString() 

這裏是一個堆棧跟蹤我得到的權利後,我嘗試訪問延遲初始化實體的領域我想用:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session 

    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165) 

    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:286) 

    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185) 

    at com.epam.spring.core.domain.UserAccount_$$_jvstfc9_4.getMoney(UserAccount_$$_jvstfc9_4.java) 

    at com.epam.spring.core.web.rest.controller.BookingController.refill(BookingController.java:128) 

我使用的是春天數據,配置JpaTransactionManager,數據庫是MySql,ORM提供者是Hibernate 4。 Annotation @EnableTransactionManagement開啓,@Transactional放在任何我能想象的地方,但沒有任何效果。

這是一個關係:

@Entity 
public class User extends DomainObject implements Serializable { 

    .. 

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) 
    @JoinColumn(name = "user_fk") 
    private UserAccount userAccount; 

    .. 

@Entity 
public class UserAccount extends DomainObject { 

    .. 

    @OneToOne(mappedBy = "userAccount") 
    private User user; 

    .. 

..一塊配置:

@Bean 
    public DataSource dataSource() { 
     DriverManagerDataSource dataSource = new DriverManagerDataSource(); 
     dataSource.setDriverClassName(env.getRequiredProperty(PROP_NAME_DATABASE_DRIVER)); 
     dataSource.setUrl(env.getRequiredProperty(PROP_NAME_DATABASE_URL)); 
     dataSource.setUsername(env.getRequiredProperty(PROP_NAME_DATABASE_USERNAME)); 
     dataSource.setPassword(env.getRequiredProperty(PROP_NAME_DATABASE_PASSWORD)); 
     return dataSource; 
    } 

    @Bean 
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() { 
     LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); 
     entityManagerFactoryBean.setDataSource(dataSource()); 
     entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class); 
     entityManagerFactoryBean.setPackagesToScan(env.getRequiredProperty(PROP_ENTITYMANAGER_PACKAGES_TO_SCAN)); 
     entityManagerFactoryBean.setJpaProperties(getHibernateProperties());    
     return entityManagerFactoryBean; 
    } 

    @Bean 
    public JpaTransactionManager transactionManager(@Autowired DataSource dataSource, 
                @Autowired EntityManagerFactory entityManagerFactory) { 
     JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(); 
     jpaTransactionManager.setEntityManagerFactory(entityManagerFactory); 
     jpaTransactionManager.setDataSource(dataSource); 

     return jpaTransactionManager; 
    } 

..這是我多麼想找回UserAccount:

@RequestMapping(...) 
    @Transactional() 
    public void refill(@RequestParam Long userId, @RequestParam Long amount) { 
     User user = userService.getById(userId); 
     UserAccount userAccount = user.getUserAccount(); 
     userAccount.setMoney(userAccount.getMoney() + amount); 
    } 

休眠版本是4.3.8.Final,Spring Data 1.3.4.RELEASE和MySql連接器5.1.29。

請問我是否需要其他東西。先謝謝你!

+0

堆棧跟蹤的這部分真的說明不了什麼。 –

+0

@ v.ladynev我應該提供整個堆棧嗎? –

+0

此方法的一部分拋出org.hibernate.LazyInitializationException≠ –

回答

3

首先,你應該明白問題的根源不是交易。我們有一個事務和一個持久的上下文(會話)。 With @Transactional註釋Spring創建一個事務並打開持久化上下文。在調用方法後,持久化上下文變爲關閉。

當你得到一個user.getUserAccount()你有一個代理類包裝UserAccount(如果你沒有加載UserAccountUser)。因此,當持久性上下文關閉時,您在UserAccount的任何方法的調用期間有LazyInitializationException,例如toString()

您有@Transactional僅適用於userService級別。可能這是正確的。您應該使用其他服務方法:updateMoney(userId, amount)

如果要在控制器方法上使用@Transactional,則需要從Spring上下文獲取控制器。 Spring應該明白,它應該用每個@Transactional方法打開和關閉一個持久化上下文。其他方法是使用Session Per Request Antipattern。您將需要添加一個特殊的HTTP過濾器。

+0

謝謝,現在我看到了在初始化時懶惰加載的實體對象的問題。但是我怎樣才能修復初始化? –

+0

@DmitrySenkovich更新我的回答 –

+0

但是爲什麼我不能在控制器方法中使用Transactional?有什麼不同?但讓我試試你的建議。 –

1

由於@ v.ladynev簡要解釋,你的問題是你想在持久化上下文之外初始化一個惰性關係。

我寫了一篇關於這個的文章,你可能會發現:http://blog.arnoldgalovics.com/2017/02/27/lazyinitializationexception-demystified/

+0

謝謝,我會讀它!但是對我而言,最有趣的部分是Spring沒有任何控制器添加事務行爲的代理。 –

+0

有辦法做到這一點,例如,您可以使用Filter爲每個請求自動添加事務上下文。它不是一個好主意的原因是因爲交易應該根據業務邏輯而不是處理請求來表示一個工作單元。 – galovics

+0

是的,現在我明白了,謝謝) –