简体   繁体   中英

Concurrent Writing & Reading with JPA2/Hibernate

I'd appreciate help in understanding why my application fails to find an entity, even though it exists in the database; I believe the issue is related to concurrent writing/reading. I'm using jpa2/hibernate 4 and spring 3.

I have a method that creates a user then sends the id, as a json object message, to a message queue where the user is further processed. Problem occurs when the message handler (UserProcessor.class) attempts to find the user (see below).

Registration.class

@Transactional
public Response createUser(String firstName, String lastName) {
  User tmpUser = new User(firstName, lastName);
  User savedUser = this.em.merge(tmpUser);

  this.em.flush();

  if (savedUser != null) {
    processUser(savedUser.getId()); // message sent to queue.
  } else {
    // Throw exception...
  }
}

UserProcessor.class

@Transactional(rollbackFor={javax.ws.rs.WebApplicationException.class})
public void processUser(Long id) {
  User user = this.em.find(User.class, id); // No user entity is found, "user" is null.
  if (user == null) {
    // throw exception
  }
  ...
}

I think you may have a concurrency problem.

As far as I understand your code, it works this way:

Registration.createUser:

  • Step A1) opens a transaction (I)
  • Step A2) create a user and stores it with that transaction (I) in the database
  • Step A3) puts the user id in the queue
  • Step A4) commits the transaction (I)

    UserProcessor.processUser(Long id)

  • Step B1) takes the user id from the queue

  • Step B2) opens a transaction (II)
  • Step B3) load the user by its id within transaction (II)
  • Step B4) do stuff
  • Step B5) commit transaction (II)

You know (depending on your transaction isolation level) the data written in a transaction (I) can read in an other transaction (II) only if the first transaction (I) is committed.

So if the UserProcessor.processUser try to process step B3 before transaction (I) is committed in step A4, it will not see the user in the database. (If you use higher transaction isolation levels, than may you even need to do step A4 before B2.)

One workaround would be switching the order of step A3 and A4. One important point: If there method Response.createUser is called in the context of then other (outer) transaction, then it will committed with the outer transaction!

You could use two methods :

@Transactional(rollbackFor={javax.ws.rs.WebApplicationException.class})
public void processUser(Long id) { 
   processUser(this.em.find(User.class, id)); 
}

public void processUser(User aUser) {
 ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM