OptimisticLockException with Ebean and Play Framew

2019-03-27 16:06发布

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?

2条回答
2楼-- · 2019-03-27 16:44

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();
     }
 }
查看更多
劫难
3楼-- · 2019-03-27 16:46

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);
查看更多
登录 后发表回答