7

次のように@Cascade(CascadeType.SAVE_UPDATE)と@OneToManyの関係がある場合

public class One {

    private Integer id;

    private List<Many> manyList = new ArrayList<Many>();

    @Id
    @GeneratedValue
    public Integer getId() {
        return this.id;
    }

    @OneToMany
    @JoinColumn(name="ONE_ID", updateable=false, nullable=false)
    @Cascade(CascadeType.SAVE_UPDATE)
    public List<Many> getManyList() {
        return this.manyList;
    }        

}

そして多くのクラス

public class Many {

    private Integer id;

    /**
      * required no-arg constructor
      */ 
    public Many() {}

    public Many(Integer uniqueId) {
        this.id = uniqueId
    }

    /**
      * Without @GeneratedValue annotation
      * Hibernate will use assigned Strategy
      */ 
    @Id
    public Integer getId() {
        return this.id;
    }

}

私が持っている場合次のシナリオ

One one = new One();

/**
  * generateUniqueId method will Take care of assigning unique id for each Many instance
  */
one.getManyList().add(new Many(generateUniqueId()));
one.getManyList().add(new Many(generateUniqueId()));
one.getManyList().add(new Many(generateUniqueId()));
one.getManyList().add(new Many(generateUniqueId()));

そして私は電話します

sessionFactory.getCurrentSession().save(one);

先に進む前に

推移的永続性Hibernateリファレンスドキュメントによると、次のように表示されます。

がsave()、update()、またはsaveOrUpdate()に渡されると、すべての子がsaveOrUpdate()に渡されます。

わかった。それでは、Java PersistenceWithHibernateの本がsaveOrUpdateメソッドについてどのように語っているのか見てみましょう。

Hibernateは指定されたIDについてMANYテーブルにクエリを実行し、見つかった場合、Hibernateは行を更新します見つからない場合は、新しい行を挿入する必要があります。

に従って翻訳することができます

INSERT INTO ONE (ID) VALUES (?)

/**
  * I have four Many instances added To One instance
  * So four select-before-saving
  *
  * I DO NOT NEED select-before-saving 
  * Because i know i have a Fresh Transient instance
  */
SELECT * FROM MANY WHERE MANY.ID = ?
SELECT * FROM MANY WHERE MANY.ID = ?
SELECT * FROM MANY WHERE MANY.ID = ?
SELECT * FROM MANY WHERE MANY.ID = ?

INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)

保存前の選択を回避するための回避策??? はい、どちらでもかまいません

  • @Version列を追加します(適用されません)
  • Hibernateインターセプターによって提供されるisTransientメソッドを実装します(私が持っているオプション

したがって、この種のカスケードを使用するときにデフォルトの動作を保存する前に選択することを回避する方法として、トランザクションがSpringによって管理されるHibernateセッションにHibernateインターセプターを割り当てることでコードを改善しました。

これが私のリポジトリです

以前(Hibernateインターセプターなし):正常に動作します!

@Repository
public class SomeEntityRepository extends AbstractRepository<SomeEntity, Integer> {

    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public void add(SomeEntity instance) {
        sessionFactory.getCurrentSession().save(instance);
    }

}

(Hibernate Inteceptorを使用):問題が発生しました(SQLクエリは実行されません-INSERTもSELECT-BEFORE-SAVINGも実行されません)

@Repository
public class SomeEntityRepository extends AbstractRepository<SomeEntity, Integer> {

    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public void add(SomeEntity instance) {
        sessionFactory.openSession(new EmptyInterceptor() {
            /**
              * To avoid select-before-saving
              */
            @Override
            public Boolean isTransient(Object o) {
                return true;
            }
        }).save(instance);
    }

}

私の質問は、Hibernate Interceptorを使用しているときにSpringがエンティティとその関係を保持しないのはなぜですか?また、正常に機能するための回避策として何をすべきですか?

4

2 に答える 2

3

Springは、現在のセッションと現在のトランザクションの間の関連付けを維持します(SessionFactoryUtils.javaを参照)。現在のDAOメソッド呼び出しに関連付けられたセッションがすでに存在するため、このセッションを使用するか、暗闇に巻き込まれる必要があります。新しいセッションを以前のトランザクションコンテキストに関連付ける詳細。それはおそらく可能ですが、かなりのリスクがあり、絶対にお勧めできません。休止状態では、セッションがすでに開いている場合は、それを使用する必要があります。

そうは言っても、Springを利用して新しいセッションを作成し、それを現在のトランザクションコンテキストに関連付けることができる場合があります。を使用しSessionFactoryUtils.getNewSession(SessionFactory, Interceptor)ます。hibernateのsessionFactoryではなくこれを使用する場合、これによりトランザクションとの関連付けが維持されます。

最初は、これをDAOで直接コーディングできます。試してテストし、うまくいけば機能していることがわかったら、AOPを使用して新しいセッションを作成およびクリーンアップするadd()メソッドにアドバイスを追加するなど、DAOからSpringコードを移動する手順を実行できます。

もう1つの方法は、グローバルインターセプターを使用することです。グローバルですが、ローカルで制御可能な動作を与えることができます。TransientInterceptorには。が含まれていthreadLocal<Boolean>ます。これは、インターセプターが。に対してtrueを返す必要があるかどうかを示す現在のスレッドのフラグですisTransient。add()メソッドの開始時にtrueに設定し、終了時にクリアします。例えば

   class TransientInterceptor extends EntityInterceptor {
      ThreadLocal<Boolean> transientFlag = new ThreadLocal<Boolean)();
      public boolean isTransient() {
         return transientFlag.get()==Boolean.TRUE;
      }
      static public setTransient(boolean b) {
          transientFlag.set(b);
      }
   }

そしてあなたのDAOで:

@Override
public void add(SomeEntity instance) {
   try {
       TransientInterceptor.set(true);
       sessionFactory.getCurrentSession().save(instance);
   }
   finally {
      TransientInterceptor.set(false);   
   }
}

次に、TransientInterceptorをSessionFactoryのグローバルインターセプターとして設定できます(例LocalSessionFactoryBean)。これを低侵襲にするために、必要に応じて、この動作をすべてのDAO追加メソッドに適用するアドバイスに関するAOPを作成できます。

于 2010-06-24T20:24:01.563 に答える
0

'after'メソッドでは、新しいセッションを作成し、それをフラッシュしないため、更新はデータベースに送信されません。これはSpringとは何の関係もありませんが、純粋な休止状態の動作です。

おそらく必要なのは、おそらくSpringを使用して構成された(エンティティ)インターセプターをsessionFactoryに追加することです。その後、以前と同じようにリポジトリのadd()メソッドを保持できます。http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/orm/hibernate3/LocalSessionFactoryBean.html#setEntityInterceptor%28org.hibernate.Interceptor%29を参照してください

于 2010-06-21T13:26:59.340 に答える