EBean没有做更新!它试图插入和失败

Or *_*Gal 4 playframework ebean playframework-2.0

我有一个像这样的简单用户模型:

package models;

import java.util.*;
import javax.persistence.*;
import play.db.ebean.*;

@Entity
public class User extends Model {

        @Id
    public Long id;
    public String userName;
    public String email;
    public String workPlace;
    public Date birthDate;
    @Version
    public Long version;

    public static Finder<Long,User> find = new Finder<Long,User>(
        Long.class, User.class
    );

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getWorkPlace() {
        return workPlace;
    }

    public void setWorkPlace(String workPlace) {
        this.workPlace = workPlace;
    }

    public Date getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }

    public Long getVersion() {
        return version;
    }

    public void setVersion(Long version) {
        this.version = version;
    }
}
Run Code Online (Sandbox Code Playgroud)

在MySql中已经存在id = 1,version = 1的记录

我创建一个具有相同id +版本的新模型,更改userName

然后我尝试save()方法

它失败了:

javax.persistence.PersistenceException: ERROR executing DML bindLog[] error[Duplicate entry '1' for key 'PRIMARY']
        at com.avaje.ebeaninternal.server.persist.dml.DmlBeanPersister.execute(DmlBeanPersister.java:116) ~[ebean.jar:na]
        at com.avaje.ebeaninternal.server.persist.dml.DmlBeanPersister.insert(DmlBeanPersister.java:76) ~[ebean.jar:na]
        at com.avaje.ebeaninternal.server.persist.DefaultPersistExecute.executeInsertBean(DefaultPersistExecute.java:91) ~[ebean.jar:na]
        at com.avaje.ebeaninternal.server.core.PersistRequestBean.executeNow(PersistRequestBean.java:527) ~[ebean.jar:na]
        at com.avaje.ebeaninternal.server.core.PersistRequestBean.executeOrQueue(PersistRequestBean.java:557) ~[ebean.jar:na]
        at com.avaje.ebeaninternal.server.persist.DefaultPersister.insert(DefaultPersister.java:404) ~[ebean.jar:na]
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'PRIMARY'
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) [na:1.7.0_05]
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source) [na:1.7.0_05]
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source) [na:1.7.0_05]
        at java.lang.reflect.Constructor.newInstance(Unknown Source) [na:1.7.0_05]
        at com.mysql.jdbc.Util.handleNewInstance(Util.java:411) ~[mysql-connector-java-5.1.18.jar:na]
        at com.mysql.jdbc.Util.getInstance(Util.java:386) ~[mysql-connector-java-5.1.18.jar:na]
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?插入新记录有效.所以它看起来像一些ebean bug,它没有意识到它应该做更新.或者是我忘记某处的配置?

谢谢

Joe*_*l S 10

我认为它与播放和ebean执行的字节码增强有关.

当我把公共场地与吸气剂和制定者混合在一起时,我遇到了类似于你的奇怪问题.有时直接使用字段时未设置值:

user.id = 1L;   // didn't work
user.setId(1L); // ok
Run Code Online (Sandbox Code Playgroud)

这感觉就像一个潜在的死亡陷阱所以我决定保持一致,只使用公共领域.缺点是ebeans autofetch停止工作,但另一方面,这迫使我用ebean提取调整我的查询.

Timo的这篇优秀文章阐明了ebean和play如何执行字节码魔术.


t0m*_*ppa 5

如果你有一个自己创建的对象并调用save(),那么Ebean将一个insert语句作为SQL.如果调用update(),它将使用SQL作为更新语句.

如果在DB中发生了一个具有类似主键的行,那么Ebean本身没有逻辑可以对DB进行额外的查询以检查,如果是,则使用update语句,否则使用insert语句.这本身就是效率低下的行为,因为通常你会知道要做哪一个,因此不想执行两个SQL语句,只需要一个.在这种情况下,您实际上必须告诉它使用更新,而不是尝试再次插入行.

// Create user.
User user1 = new User();
user1.setId(1L);
user1.setVersion(1L);
user1.setUserName("foo");
user1.setWorkPlace("bar");
user1.setEmail("foo@bar.com");
user1.save();

// Change details later and update DB.
User user2 = new User();
user2.setId(1L);
user2.setVersion(1L);
user2.setUserName("scooby_doo");
user2.save(); // This will crash.
user2.update(); // This will work.
Run Code Online (Sandbox Code Playgroud)

但是,如果从DB获取对象,则在其上调用save()将在SQL中使用update语句,因为Ebean知道它的状态不是新的而不是现有的.

// Find user (and let Ebean know its state as an existing row).
User user3 = User.find.byId(1L);

// Now you can change details and save without issues.
user3.setEmail("foo@gmail.com");
user3.setVersion(2L);
user3.save(); // This will work.
Run Code Online (Sandbox Code Playgroud)

请参阅Ebean文档.