update()
への呼び出しで、メモリ内の変更されたオブジェクトをデータベースに既に保存されているデータと比較する必要がある Hibernate プロジェクトがあります。たとえば、私のビジネス ロジックでは、レコードが「有効」である (有効日が今日またはそれ以前である) 場合、更新によって有効日を変更することはできません。これを達成するために、次のコードがあります (少し長くて複雑です)。
マネジャー
public class LogicManager {
@Autowired
SessionFactory sessionFactory
private Session getSession() {
return sessionFactory.getCurrentSession();
}
public MemberRecord findRecord(Integer id) {
// << Code to check authorization >>
return memberRecordDAO.findById(id);
}
public void updateRecord(MemberRecord record) {
getSession().evict(record);
MemberRecord oldRecord = memberRecordDAO.findById(record.getId());
Date oldEffectiveDate = oldRecord.getEffectiveDate();
if ( isEffective(oldEffectiveDate) &&
!oldEffectiveDate.equals(record.getEffectiveDate)) {
throw new IllegalArgumentException("Cannot change date");
}
// << Other data checks >>
memberRecordDAO.update(record);
}
}
ダオ
public class MemberRecordDAO {
@Autowired
private SessionFactory sessionFactory;
private Session getSession() {
return sessionFactory.getCurrentSession();
}
public MemberRecord findById(Integer id) {
return (MemberRecord)getSession()
.getNamedQuery("findMemberById")
.setInteger("id", id)
.uniqueResult();
}
}
クライアントコード
// ...
public void changeEffectiveDate(Integer recordId, Date newDate) {
LogicManager manager = getBean("logicManager");
MemberRecord record = manager.findById(recordId);
record.setEffectiveDate(newDate);
manager.updateRecord(record);
}
マネージャーに呼び出しを追加する前にevict()
、マネージャーが予期しない方法で動作していることに気付きました。レコードを更新するには、まず を呼び出してそのレコードを取得する必要があります。これによりfindById()
、レコードがセッション キャッシュに入れられます。そのオブジェクトに変更を加えてから、(おそらく)永続化されたデータを取得するためにupdateRecord()
呼び出す呼び出しを行います。この 2 回目の呼び出しは、データベース データを参照するのではなく、キャッシュからオブジェクトをプルするだけであるfindById()
ことに気付きました。これにより、とがまったく同じオブジェクトになるため、常に新しく変更された日付と同じになります。findById()
oldEffectiveDate
record
oldRecord
これに対抗するために、 への呼び出しを追加しましたevict()
。これは、オブジェクトがキャッシュから削除され、Hibernate がデータベースにアクセスしてMemberRecord
. その変更を行った後、 をMemberRecordDAO
呼び出すと例外がスローuniqueResult()
されますAssertionFailed: possible nonthreadsafe access to session
。デバッガーを実行すると、 と の両方が同じ を使用していることがわかりLogicManager
ますMemberRecordDAO
。Session
これは正しいと思いました。
だから、私の質問:
- 私の考え方/アルゴリズムは正しいですか? 正しい
evict()
ことはありますか?より良い方法はありますか?私はセッション、キャッシング、またはevict()
. スレッドの問題に対処する前に、このロジックが正しいことを確認したいと思います。 Session
DAO からのアクセスがスレッドセーフでないのはなぜですか?