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);
}
}
DAO
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);
}
Managerにevict()
コールを追加する前に、マネージャが予期しない動作をしていたことに気付きました。レコードを更新するには、最初にfindById()
を呼び出してそのレコードを取得しなければなりません。レコードをセッションキャッシュに入れます。私はそのオブジェクトに変更を加え、updateRecord()
を呼び出して、findById()
と呼んで(永久に)永続化されたデータを取得します。私はfindById()
へのこの2回目の呼び出しがではなく、であることを認識しましたが、データベースのデータを見てください。これにより、oldEffectiveDate
は、record
とoldRecord
が全く同じオブジェクトになるため、常にが私の新たに変更された日付と同じになります。
evict()
への呼び出しを追加しました。これは、オブジェクトがキャッシュから削除され、Hibernateがデータベースに移動してMemberRecord
を取得することを意味すると理解することができました。私がその変更を行った後、MemberRecordDAO
はuniqueResult()
というAssertionFailed: possible nonthreadsafe access to session
と呼ぶときに例外をスローします。デバッガを実行すると、LogicManager
とMemberRecordDAO
の両方が同じSession
を使用していることがわかります。これは正しいと思います。
だから、私の質問:
- は私の思考/アルゴリズムは正しいですか?
evict()
は正しいことですか?より良い方法がありますか?私はセッション、キャッシュまたはevict()
にあまり精通していません。スレッドの問題を扱う前に、このロジックが正しいことを確認したい。 - DAOからの
Session
へのアクセスがスレッドセーフではないのはなぜですか?
tl;タイトルから判断すると、 'session.refresh(...)'と 'session.merge(...)'を見たいかもしれません。 – Martin
@Martin - 'refresh()'は有望です。私はまた、オブジェクトの分離と再取り付けについても検討しています。 – RustyTheBoyRobot