私は、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 servlet
のOpenSessionInViewFilter
を追加することで、私の問題は解決しましたが、パフォーマンスとエンティティの取り外し/再接続(アプリケーションの他の部分で行っています)に問題があることを読んだことがあります。
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);
}
}
@SergeyBespalovありがとうございました。これは、プロセス全体を信じられないほど速くしました。このアプローチには注意が必要ですか?トランザクションを明示的に管理する際には、すべきことはありませんか? – battle2048
私はこの例に基づいて2つの簡単なルールを提案することができます: 1.宣言型トランザクション管理があなたに合っていない場合は使用しないでください。 2.プロパティが1つだけ必要な場合は完全オブジェクトを使用しないでください。 –