2011-11-09 2 views
1

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は、recordoldRecordが全く同じオブジェクトになるため、常にが私の新たに変更された日付と同じになります。

evict()への呼び出しを追加しました。これは、オブジェクトがキャッシュから削除され、Hibernateがデータベースに移動してMemberRecordを取得することを意味すると理解することができました。私がその変更を行った後、MemberRecordDAOuniqueResult()というAssertionFailed: possible nonthreadsafe access to sessionと呼ぶときに例外をスローします。デバッガを実行すると、LogicManagerMemberRecordDAOの両方が同じSessionを使用していることがわかります。これは正しいと思います。

だから、私の質問:

  1. は私の思考/アルゴリズムは正しいですか? evict()は正しいことですか?より良い方法がありますか?私はセッション、キャッシュまたはevict()にあまり精通していません。スレッドの問題を扱う前に、このロジックが正しいことを確認したい。
  2. DAOからのSessionへのアクセスがスレッドセーフではないのはなぜですか?
+1

tl;タイトルから判断すると、 'session.refresh(...)'と 'session.merge(...)'を見たいかもしれません。 – Martin

+0

@Martin - 'refresh()'は有望です。私はまた、オブジェクトの分離と再取り付けについても検討しています。 – RustyTheBoyRobot

答えて

0

これは私のテストに合格したソリューションでしたが、それはまだ私には少し総だ:

マネージャー

public void updateRecord(MemberRecord record) { 
    MemberRecord oldRecord = record; 
    record = record.clone(); //Added a clone() to MemberRecord 
    getSession().evict(record); 
    getSession().evict(oldRecord); 
    getSession().refresh(oldRecord); 
    // At this point, record has all of the new values, but none of the Hibernate 
    // data attached to it, due to the clone(). 
    // oldRecord is populated with the data currently in the database. 

    Date oldEffectiveDate = oldRecord.getEffectiveDate(); 
    if (isEffective(oldEffectiveDate) && 
     !oldEffectiveDate.equals(record.getEffectiveDate)) { 
     throw new IllegalArgumentException("Cannot change date"); 
    } 


    // << Other data checks >> 
    memberRecordDAO.update(record); 
    } 

事のこのタイプはきれいに行うことができれば、教えてください。

2

追い出しは()アプローチは動作しますが、私は「物事の優先休止道」を信じているにようSession.mergeを()、使用することです:ちょうど心に留めておく

public MemberRecord updateRecord(MemberRecord newRecord) { 

    MemberRecord oldRecord = memberRecordDAO.findById(record.getId()); 

    Date oldEffectiveDate = oldRecord.getEffectiveDate(); 
    if (isEffective(oldEffectiveDate) && 
    !oldEffectiveDate.equals(newRecord.getEffectiveDate)) { 
     throw new IllegalArgumentException("Cannot change date"); 
    } else { 
     MemberRecord merged = (MemberRecord) session.merge(newRecord); 
     return merged; 
    } 
} 

Session.merge()はoldRecordのすべてのフィールドをnewRecordの値で更新します。

+0

私はあなたの例が 'newRecord'が' oldRecord'と同じであるという問題を回避しているのを見ていません。なぜなら私は最初に 'evict()'を使っていたのです。 – RustyTheBoyRobot

関連する問題