2017-01-18 5 views
0

Hibernateはトランザクションメソッドで変更されたエンティティを持続しますが、セッション#evict(エンティティ)を使用することで回避できます。Hibernateの永続コンテキストからエンティティ内のエンティティをデタッチ

永続コンテキストからデタッチすると、そのエンティティもデタッチされますか?アドレスは、トランザクションの終了時に保持されます、私はユーザーオブジェクトを切り離す場合

@Entity 
public class User extends BaseEntity{ 
     @Column(name = "email") 
     private String email; 

     @OneToMany(fetch = FetchType.LAZY, mappedBy = "user") 
     private List<Address> addresses; 

     // getters and setters 
} 

@Entity 
public class Address extends BaseEntity{ 
     @Column(name = "email") 
     private String email; 

     @ManyToOne(fetch = FetchType.LAZY) 
     @JoinColumn(name = "USER_ID") 
     private User user;  

     // getters and setters 
} 

が、それにアドレスオブジェクトを変更します。たとえば

、私はこのクラスを持っていますか?このように:

User user = userDAO.getById(id); 
session.evict(user); 
Address address = user.getAddresses().get(0); 
address.setNumber(number); 
addressDAO.saveOrUpdate(address); //will this work? 
+0

なぜ(明示的に)ユーザーを切り離したいですか? JPAは、ロードされたすべてのエンティティのコピーをメモリに保持します。トランザクションがコミットすると、永続コンテキストが実行され、変更されたエンティティが検出されます。この方法では、ユーザーを変更しない限り、ユーザーには何も起こりません。 –

+0

私はユーザーを変更する行を省略しました。おそらく例で追加する必要があります。しかし、私は一括更新を行っているので、私はそれを切り離しているので、私は各エンティティを個別に保存したくありません。 –

+0

'' 'CascadeType.DETACH/ALL'''が伝播しない限り、個々のエンティティごとにデタッチが行われます。アドレス変更がデータベースに届くようにsaveOrUpdate()で十分でなければなりません(ただし、JPAのように動作する場合は必須ではありません。しかし、CascadeType.UPDATEを持つエンティティを更新すると、これは分離されたエンティティに伝播するというトラブルにつながる可能性があります。 *一括更新*とはどういう意味ですか?私は500MBのサイズの取引をしており、切り離す必要はありませんでした。 –

答えて

1

エンティティは永続コンテキストにロードされていない、これが唯一の選択クエリに起こる、あなたはfind()merge()を使用する場合。

更新または削除クエリを実行すると、永続コンテキストに既に読み込まれているエンティティが更新されないため、永続コンテキストがデータベースと実際には同期していない可能性があります変更を確認するにはrefresh())。

永続コンテキストに複数のユーザーをロードしてから後でUpdate User set status='active' where id IN (:ids)を実行した場合、永続コンテキスト内のユーザーは変更されていないため、データベースのみが変更されています。ユーザを変更するには、 `aUser.setStatus( 'active')を呼び出して実際に管理されるEntityを変更する必要があります。トランザクションがコミットすると、JPAはすべての管理対象エンティティをロード時に作成されたコピーに対してチェックし、更新を行います。

Persistenceに5000個のオブジェクトをロードする場合、JPAがエンティティグラフを使用して実行されるまでに時間がかかる場合があり、トランザクションがコミットすると変更が検出されます。何も修正しておらず、変更検出のスピードを上げたい場合、これを行うには2つの方法があります。読取り専用の問合せを使用してエンティティをロードすると、JPAにはロードされたエンティティのコピーを保持する必要がないことがわかります。もう1つの方法は、EntityManager.clear()に電話して、すべての管理対象エンティティを破棄することです。ただし、パフォーマンスに関心がある場合は、エンティティを永続コンテキストにロードすることを避けることをお勧めします。私があなたの問題を理解しているので、Update User set ... where id IN (:ids)を実行する必要があります。そのためには、ユーザーIDを入力する必要はありません。idを入力するだけです。List<Long> ids = em.createQuery("select u.id from User u where ...", Long.class).getResultList();

希望するあなたのために何かを明確に:)

EDIT:これはJPAの視点から書かれているが、動作は全く同じfind()を除いて、記述されているので、ちょうど前方に直接SessionImplに休止EntityManager、ネイティブのHibernateでget()と呼ばれています。

+0

です。読取り専用の問合せでは、@Transactional(readOnly = true)というアノテーションを意味しますか?それから、私はその中で "select id"クエリを実行し、readOnlyメソッドではないパラメータに渡します。これで十分でしょうか? –

+0

トランザクション全体が読み取り専用の場合、一括更新は機能しません。単一のクエリを読み込み専用にしたい場合は、おそらく '@QueryHint()'を使う必要がありますが、クエリヒントはベンダー固有のものです。 'User.id'だけを選択した場合、EntityManagerには何もロードされないので(あなたのidフィールドはエンティティではないので)、読み取り専用のクエリは必要ありません。 Hibernateを使用する場合、いつでもブレークポイントを設定し、 'Session.persistenceContext.entitiesByKey'フィールドをチェックすることができます。これは、現在Persistence Contextにロードされているものを表示します。 –

0

JPA 2.0

はあなたがもしパラメータ

void detach(Object entity) 

より here

として取り外すことにしたいエンティティを切り離す呼び出すことができますのEntityManagerを与えられたので、インジェクションを使用すると、必要なエンティティをデタッチするサービスにEntityMangerを挿入できます。更新またはEntityManager.createQuery()を使用して削除され

+0

残念ながらEntityManagerは使用していませんが、休止状態のsessionFactoryは –

関連する問題