2016-09-26 21 views
0

私は、LazyをロードしたJPAエンティティを持っています。私は毎回コレクションを必要としません。OpenSessionInViewとトランザクション? (Spring/Hibernate/JPA)

@Entity(name = "Foo") 
@Access(AccessType.FIELD) 
@Table(name = "TEST", schema = "TEST") 
public class Foo implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @OneToMany(mappedBy="foo", targetEntity=Bar.class, fetch=FetchType.LAZY, cascade=CascadeType.ALL) 
    private List<Bar> bars; 
} 

@Entity(name = "Bar") 
@Access(AccessType.FIELD) 
@Table(name = "TEST", schema = "TEST") 
public class Bar implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @ManyToOne(targetEntity = Foo.class) 
    @JoinColumn(name = "FOO_ID", referencedColumnName = "ID") 
    private Foo foo; 
} 

私は、データベースの相互作用の多くを実行し、最後にデータベースへのFooエンティティを保存サービスクラスには、いくつかのメソッドを持っています。私はこれをコレクション内の約100項目について発生させる必要があります。

@Service 
public class FooService { 

    @Autowired 
    private FooRepository fooRepository; 

    public void processAllFoos() { 
     fooRepository.findAll().forEach(foo -> { 
      processFoo(foo); 
     }); 
    } 

    private void processFoo(Foo foo) { 
     foo.getBars().forEach(bar -> { 
      // Do a lot of time consuming stuff here that involves 
      // entities of other types and modify each bar object 
     }); 
     fooRepository.save(foo); 
    } 
} 

processAllFoosそれが要求を受けるたび@RESTControllerから呼び出されます。

しかし、processAllFoosは、すべてのFoosに対してビジネスロジックが実行されるまで、Fooテーブル全体をロックするため、1回のデータベーストランザクションでラップすることは望ましくありません。

processFooの方法を@Transactionalにすると、Hibernateセッションが存在しないと言うLazyInitializationExceptionというメッセージが表示されます。この作業を行うには、呼び出しスタック内のすべてのメソッドを作成する必要があります。@Transactionalネストされたメソッドが呼び出し側メソッドのトランザクションに参加できるようにします。しかし、これは上記のようにFooテーブル全体をロックします。

dispatcher servletOpenSessionInViewFilterを追加することで、私の問題は解決しましたが、パフォーマンスとエンティティの取り外し/再接続(アプリケーションの他の部分で行っています)に問題があることを読んだことがあります。

OpenSessionInViewアプローチを使用せずに私がしたいことができる方法はありますか?このアプローチを使用して他にどのような脆弱性を追加しますか?下の回答に基づいて

Spring/Hibernate 4.x


、私は次の操作を行うことができました:

一般ビュー層(UIコンポーネントまたはページテンプレート)にLazyInitializationの問題を解決するために使用
@Service 
public class FooService { 

    @Autowired 
    private FooRepository fooRepository; 

    @Autowired 
    private TransactionTemplate transactionTemplate; 

    public void processAllFoos() { 
     fooRepository.findAll().forEach(foo -> { 
      transactionTemplate.execute(new TransactionCallback<Object>() { 
       public Object doInTransaction(TransactionStatus status) { 
        try { 
         processFoo(foo); 
         status.flush(); 
        } catch(Exception e) { 
         status.setRollbackOnly(); 
        } 
        return null; 
       } 
      }); 
     }); 
    } 

    private void processBar(Foo foo) { 
     foo.getBars().foreEach(bar -> { 
      // Do a lot of time consuming stuff here that involves 
      // entities of other types and modify each bar object 
     }); 
     fooRepository.save(foo); 
    } 
} 

答えて

2

OpenSessionInViewFilterなぜなら、Viewレイヤーはトランザクションを直接管理することはできないし、管理してはならないからです。 あなたの場合、Barオブジェクトをすべて取得する別の方法を適用することができます。

最初Fooすべてのオブジェクトを取得する代わりに、すべてのオブジェクトIDを取得します。

第2のFooアイデアコレクションを使用して、関連するBarオブジェクトを反復処理します。

第3の大規模なトランザクションが1つでない場合は、Springのトランザクションテンプレートを使用してトランザクションを明示的に管理できます。

あなたのコード例では、次のようになります。以下

@Service 
public class FooService { 

    @Autowired 
    private FooRepository fooRepository; 

    @Autowired 
    private BarRepository barRepository; 

    @Autowired 
    private TransactionTemplate transactionTemplate; 

    public void processAllFoos() { 
     final List <Long> fooIdList = transactionTemplate.execute(new TransactionCallback() { 
      public Object doInTransaction(TransactionStatus status) { 

       return fooRepository.findIdList(); 
      } 
     }); 

     transactionTemplate.execute(new TransactionCallback() { 
      public Object doInTransaction(TransactionStatus status) { 
       barRepository.findByFooIdList(fooIdList).forEach(bar - > { 
        processBar(bar); 
       }); 
       return null; 
      } 
     }); 

    } 

    private void processBar(Bar bar) { 
     // Do a lot of time consuming stuff here that involves 
     // entities of other types and modify each bar object 
     barRepository.save(bar); 
    } 
} 

例はいくつかのパフォーマンスのオーバーヘッドなしであなたのタスクを解決する方法を示しています。しかし、FooBarのテーブルが外部キー制約にリンクされている場合は、テーブルで行を更新するたびにFooテーブルの関連レコードがRDBMSでブロックされる可能性があることを理解する必要があります。

+0

@SergeyBespalovありがとうございました。これは、プロセス全体を信じられないほど速くしました。このアプローチには注意が必要ですか?トランザクションを明示的に管理する際には、すべきことはありませんか? – battle2048

+0

私はこの例に基づいて2つの簡単なルールを提案することができます: 1.宣言型トランザクション管理があなたに合っていない場合は使用しないでください。 2.プロパティが1つだけ必要な場合は完全オブジェクトを使用しないでください。 –

関連する問題