11

I have an Entity User that is assigned a system privilege in a function. I have a web page where you select a user from a drop down and submit the form to the server. On the server side I want to update only one field for that entity.

My User entity will have default values for all objects except the one field that gets set and its id. Do I need to do a findById then update the specific field then do the merge or is there a way to tell the to only update that field?

Hash
  • 4,647
  • 5
  • 21
  • 39
Allan
  • 2,889
  • 2
  • 27
  • 38

3 Answers3

9

Do I need to do a findById then update the specific field then do the merge

That would be the regular approach, except that you don't need to merge a managed entity, just let JPA detect the changes and update it automatically.

or is there a way to tell the to only update that field?

No. But you could use a "short" version of your User (with only the field(s) to update). Another option would be to use a Bulk Update Operation but this is IMO really not a good use case. I wouldn't use this approach.

Reference

  • JPA 1.0 specification
    • 4.10 Bulk Update and Delete Operations
Pascal Thivent
  • 562,542
  • 136
  • 1,062
  • 1,124
  • 1
    what do you mean by Short version of User? – geoaxis Oct 17 '10 at 04:12
  • 4
    @geoaxis, `@Entity @Table(name="USER") class ShortUser { ... }` would be an entity that maps to the same table as `User`, but only has a subset of columns. It should be immutable. See [this question/answer](http://stackoverflow.com/questions/1667930/2-jpa-entities-on-the-same-table). – Peter Davis Jun 26 '11 at 06:26
  • you can pre-cache the findById results by implementing a paging technique to lookup batches of entities, then up them one by one. In this way, the findById will not go to the DB for every single row update. – Armand Oct 06 '14 at 16:29
  • Finding the entity and setting the specific field bypasses `@Version` checking acording to my tests in JPA 2.0 (Hibernate 4.2 IIRC). I had to find, detach, set values, and then merge to get `@Version` checking, but that generates (possibly several) superfluous selects. – marcus Sep 02 '16 at 16:31
8

JPA 2.1 now support update criteria that allow you to partial update entity via javax.persistence.criteria.CriteriaUpdate<T>.

Javadoc here.

Example code:

EntityManagerFactory factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
EntityManager em = factory.createEntityManager();

// create user
em.getTransaction().begin();
User user = new User();
user.setUsername("user@example.com");
user.setPassword("password");
user.setCreatedAt(new Date());
em.persist(user);
em.getTransaction().commit();
// end create user

assert user.getId() != 0;

System.out.println("Before update user Date of Birth: " + user.getDateOfBirth());

// update user date of birth
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaUpdate<User> updateCriteria = cb.createCriteriaUpdate(User.class);
Root<User> root = updateCriteria.from(User.class);
// update dateOfBirth property
updateCriteria.set(root.get("dateOfBirth"), new Date());
// set where clause
updateCriteria.where(cb.equal(root.get("id"), user.getId()));
// update
int affected = em.createQuery(updateCriteria).executeUpdate();
System.out.println("Affected row: " + affected);
em.getTransaction().commit();
// end update user date of birth


// select user again to verify
em.getTransaction().begin();
em.refresh(user);

System.out.println("After update User Date of Birth: " + user.getDateOfBirth());

em.getTransaction().commit();
em.close();

More details on JPA 2.1 partial update here.

Pau Kiat Wee
  • 9,485
  • 42
  • 40
  • 2
    `CriteriaUpdate` isn't a good solution for this problem. It is designed for **bulk update** operations, so it's bypassing any optimistic locking checks. Also doc says: *The persistence context is not synchronized with the result of the bulk update*. – G. Demecki Jun 18 '15 at 12:53
0

If you are updating an entity that is selected by a client, you have two options.

1) If the User being selected is represented in the client as a detached entity, then when you receive the user entity in your server bean, you only need to assign the new field value, then perform an em.merge(selectedUser). This has the negative effect where the state of the user may have changed between user selection to your server performing the update, causing undesirable results.

2) Just have the form submit the user ID, then use the findById to lookup the user, modify your entity, then commit the change. If this is a server managed persistence unit, you don't need to commit.

Armand
  • 493
  • 5
  • 7