Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OptimisticLockException with Ebean and Play Framework 2

I am using Ebean with Play Framework 2 and sometimes it falls with OptimisticLockException of such kind:

play.core.ActionInvoker$$anonfun$receive$1$$anon$1: Execution exception [[OptimisticLockException: Data has changed. updated [0] rows sql[update manager set modification_time=?, session_id=?, expiration_date=? where id=? and rating=? and creation_time=? and modification_time=? and name=? and surname=? and login=? and password_hash=? and email=? and session_id=? and expiration_date=?] bind[null]]]

This happen when few actors start to access database.

So, Manager class is:

public class Manager extends Model {
@Getter @Setter
Long id;

@Getter @Setter
private String name;

@Getter @Setter
private String surname;

@Column(unique = true)
@Getter @Setter
private String login;

@Getter @Setter
private String passwordHash;

@Getter @Setter
private String email;

@Embedded
@Getter @Setter
private ManagerSession session;

@Getter
private Timestamp creationTime;

@Getter
private Timestamp modificationTime;

@Override
public void save() {
    this.creationTime       = new Timestamp(System.currentTimeMillis());
    this.modificationTime   = new Timestamp(System.currentTimeMillis());
    super.save();
}

@Override
public void update() {
    this.modificationTime   = new Timestamp(System.currentTimeMillis());
    super.update();
}

}

save() and update() hooks used instead @PrePersist annotations, because of Ebean doesn't support it. As I know @Version annotation allways brings Optimistic lock mode, so I start to use such trick. I know what Optimistick lock is, but how this situation should be solved, when many actors should modify same db record, where last modification wins?

like image 615
Alex Povar Avatar asked Sep 27 '12 08:09

Alex Povar


2 Answers

Solution:

The problem: Saving a detached EBean model directly from the Play Form causes either OptimisticLockException, or when using @Version it causes NullpointerException.

Form<Venue> form = form(Venue.class).bindFromRequest();
form.get().update(id); // this causes the exception

The solution: The form should support supplying it an object from database, before binding from the request parameters. The parameters found in the request should then overwrite the relevant properties on the object. Perhaps call fill() before bindFromRequest():

Form<Venue> form = form(Venue.class).fill(Venue.find.byId(id)).bindFromRequest();
form.get().update(id);
like image 127
msnt Avatar answered Oct 28 '22 03:10

msnt


I resolved this OptmistLockException issue just passing the entity id to the controller when trying to update. To prevent user from changing values, the value was passed as a hidden field.

 <input type="hidden" name="id" value="@formVar(<identifierVariable>).value"/>

On the controller side, I check if the received form has errors. In negative case, I update the attached entity in the form.

 public static Result update() {
     Form<Entity> filledForm = form.bindFromRequest();
     if ( filledForm.hasErrors() ) {
        flashError("app.oh_snap", "app.change_things");
     } else {
        Entity entity = filledForm.get();
        entity.update();
     }
 }
like image 39
Saulo Aguiar Avatar answered Oct 28 '22 03:10

Saulo Aguiar