Marcelo Abiarraj
Marcelo Abiarraj

Reputation: 309

Detach entity within entity from persistence context in Hibernate

Hibernate persists modified entities at the of transactional methods, I can avoid by using session#evict(entity).

If I detach it from the persistence context, the entities whithin it will also be detached?

For instance, I have this classes:

@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
}

If I detach a user object, but change the address object in it, will the address be persisted at the end of transaction? Like this:

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

Upvotes: 0

Views: 8817

Answers (2)

Klaus Groenbaek
Klaus Groenbaek

Reputation: 5035

Entities that are updated or deleted using a EntityManager.createQuery() are not loaded into the Persistence Context, this only happens for select queries, and when you use find()or merge().

After you do an update or delete query your persistence context may actually be out-of-sync with the database, because the query doesn't update the entities which has already been loaded into the persistence context (you need to call refresh() to see the changes).

If you load a number of user (into the persistence context), and later doUpdate User set status='active' where id IN (:ids), then you have not modified any of the users in the persistence context, you have only modified the database. To modify a user, you must modify the actually managed Entity by calling `aUser.setStatus('active'), when the transaction commits, JPA will check all managed entities against a copy created when it was loaded, and if anything has changed it will do an Update.

If you are loading 5000 objects into the Persistence it may take some time for JPA to run though the entity graph, and detect the changes when the transaction commits. If you didn't modify anything, and would like to speed up the change-detection, there are two ways to do this. Load your entities using a read-only query, this tells JPA that it does not need to keep a copy of the loaded entity. The other option is to call EntityManager.clear() to throw away all managed entities. However, if you are interested in performance, the best solution is probably to avoid loading the entities into the persistence context. As I understand you problem, you need to do a Update User set ... where id IN (:ids)and for that you only need the user's id so you don't need to load the user, you just need the ids, and therefore you can do List<Long> ids = em.createQuery("select u.id from User u where ...", Long.class).getResultList();

Hope this clarifies things for you :)

EDIT: this is written from a JPA perspective, but for hibernate EntityManager just forwards directly to SessionImpl, so the behavior is exactly as described, except for find() being called get()in native Hibernate.

Upvotes: 1

aurelius
aurelius

Reputation: 4076

Since JPA 2.0

given an EntityManager you can call detach with the entity you want to be detached as parameter

void detach(Object entity)

more here

if you use injection then you can inject an EntityManger in the service where you want to detach the required entity.

Upvotes: 0

Related Questions