6

私はまだ hibernate/hql を学習中ですが、半分はベスト プラクティスの質問、半分はサニティ チェックという質問があります。

クラスAがあるとしましょう:

@Entity
public class A
{
    @Id @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    @Column(unique=true)
    private String name = "";

    //getters, setters, etc. omitted for brevity
}

保存される A のすべてのインスタンスに一意の名前 (したがって @Column アノテーション) を付けるように強制したいのですが、その名前を持つ A インスタンスが既に保存されている場合も処理できるようにしたいと考えています。これを行うには2つの方法があります。

1) session.saveOrUpdate() 呼び出し中にスローされる可能性のある org.hibernate.exception.ConstraintViolationException をキャッチして、処理を試みることができます。

2) session.saveOrUpdate() を呼び出す前に、DAO で既にその名前を持つ A の既存のインスタンスを照会できます。

アプローチ1では、違反した制約をプログラムで把握する方法がわからないため、現在、アプローチ2に傾いています(Aには他にもいくつかの一意のメンバーがあります)。現在、私の DAO.save() コードはおおよそ次のようになっています。

public void save(A a) throws DataAccessException, NonUniqueNameException
{
    Session session = sessionFactory.getCurrentSession();

    try
    {
        session.beginTransaction();

        Query query = null;

        //if id isn't null, make sure we don't count this object as a duplicate
        if(obj.getId() == null)
        {
            query = session.createQuery("select count(a) from A a where a.name = :name").setParameter("name", obj.getName());
        }
        else
        {
            query = session.createQuery("select count(a) from A a where a.name = :name " + 
                "and a.id != :id").setParameter("name", obj.getName()).setParameter("name", obj.getName());
        }

        Long numNameDuplicates = (Long)query.uniqueResult();
        if(numNameDuplicates > 0)
            throw new NonUniqueNameException();

        session.saveOrUpdate(a);
        session.getTransaction().commit();
    }
    catch(RuntimeException e)
    {
            session.getTransaction().rollback();
            throw new DataAccessException(e); //my own class
    }
}

私はこれを正しい方法で行っていますか?hibernate は、一意性制約に違反している値をプログラムで (つまり、エラー文字列としてではなく) 教えてくれますか? クエリをコミットから分離することで、スレッド セーフ エラーを招きますか?それとも安全でしょうか? これは通常どのように行われますか?

ありがとう!

4

2 に答える 2

4

2番目のアプローチが最適だと思います。

この特定のオブジェクトが原因であることが確実にConstraintViolation例外をキャッチできるようにするには、saveOrUpdateの呼び出しの直後にセッションをフラッシュする必要があります。これらのオブジェクトを一度に多数挿入する必要がある場合、これによりパフォーマンスの問題が発生する可能性があります。

すべての保存アクションで名前がテーブルにすでに存在するかどうかをテストする場合でも、これはすべての挿入後にフラッシュするよりも高速です。(いつでもベンチマークで確認できます。)

これにより、別のレイヤーから「バリデーター」を呼び出すことができるようにコードを構造化することもできます。たとえば、この一意のプロパティが新しいユーザーの電子メールである場合、Webインターフェイスから検証メソッドを呼び出して、電子メールアドレスが受け入れ可能かどうかを判断できます。最初のオプションを選択した場合は、電子メールを挿入しようとした後に、その電子メールが受け入れ可能かどうかしかわかりません。

于 2010-03-23T16:50:33.427 に答える
1

次の場合は、アプローチ 1 で問題ありません。

  • エンティティには 1 つの制約しかありません。
  • セッション内のダーティ オブジェクトは 1 つだけです。

flush()が呼び出されるか、トランザクションがコミットされるまで、オブジェクトは保存されない可能性があることに注意してください。

最良のエラー報告を行うには、次のことを行います。

  1. 制約違反ごとにアプローチ2を使用して、それぞれに特定のエラーを与えることができます..
  2. 制約例外が発生した場合にトランザクションを再試行する (最大回数) インターセプターを実装して、いずれかのテストで違反をキャッチできないようにします。これは、トランザクション分離レベルに応じてのみ必要です。
于 2010-03-22T22:22:01.473 に答える