2

IDEで遅延初期化されたエンティティを取得しようとすると、次の例外メッセージが表示されます(スタック全体を提供できないため、この例外のトレース):遅延初期化されたインスタンスを取得しようとするLazyInitializationException

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) 

私は春を使用しています:ここで

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

は私が使用したい怠惰な初期化エンティティのフィールドにアクセスしようとした直後に私が取得スタックトレースですデータ、JpaTransactionManagerの設定、データベースはMySql、ORMプロバイダはHibernate 4です。注釈@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

まず、問題の根本原因はトランザクションではないことを理解する必要があります。私たちはトランザクションと永続的なコンテキスト(セッション)を持っています。 @Transactionalアノテーションを使用すると、Springはトランザクションを作成し、永続コンテキストをオープンします。メソッドが呼び出されると、永続コンテキストが閉じられます。

あなたがuser.getUserAccount()を取得すると(あなたがUserUserAccountをロードしない場合)あなたはUserAccountをラップするプロキシクラスを持っています。したがって、永続コンテキストが閉じられると、UserAccountの任意のメソッドの呼び出し中にLazyInitializationExceptionがあります。たとえば、toString()です。

@Transactionalは、userServiceレベルでのみ動作します。おそらく、これは正しいです。他のサービス方法:updateMoney(userId, amount)を使用する必要があります。

コントローラーメソッドで@Transactionalを使用する場合は、Springコンテキストからコントローラーを取得する必要があります。そしてSpringは、永続的なコンテキストを開いたり閉じたりする特別な方法で、すべての@Transactionalメソッドをラップする必要があることを理解する必要があります。他の方法は、Session Per Request Antipatternを使用することです。特別なHTTPフィルターを追加する必要があります。

+0

ありがとう、今私は、初期化のエンティティオブジェクトが遅延読み込まれている問題を参照してください。しかし、どのように初期化を修正できますか? –

+0

@DmitrySenkovich私の答えを更新 –

+0

しかし、コントローラメソッドでTransactionalを使用できないのはなぜですか?違いはなんですか?しかし、あなたの提案を試してみましょう。 –

1

@ vladimev簡単に説明すると、永続コンテキスト外の遅延関係を初期化したいという問題がありました。

私はこのことについて記事を書いて、あなたはそれが役に立つかもしれません:http://blog.arnoldgalovics.com/2017/02/27/lazyinitializationexception-demystified/

+0

ありがとう、私はそれを読むでしょう!しかし私にとってここで最も興味深いのは、Springがトランザクションの振る舞いを追加するコントローラーのためのプロキシーを持たないということでした。 –

+0

これを行う方法があります。たとえば、リクエストごとにトランザクションコンテキストを自動的に追加するフィルタを使用できます。それが良い考えではない理由は、トランザクションがビジネスロジックの観点から作業単位を表し、要求を処理する点ではないからです。 – galovics

+0

はい、今私はそれを見る、ありがとう) –

関連する問題