4

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

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;
    }
}

在 MySql 中已经有一条 id=1, version=1 的记录

我创建一个具有相同 id+version 的新模型,更改用户名

然后我尝试 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]

我究竟做错了什么?插入新记录有效。所以它在我看来就像一些 ebean 错误,它没有认识到它应该进行更新。还是我在某处忘记了一些配置?

谢谢

4

2 回答 2

10

我认为这与 play 和 ebean 执行的字节码增强有关。

当我将公共字段与 getter 和 setter 混合时,我遇到了与您类似的奇怪问题。有时直接使用该字段时未设置值:

user.id = 1L;   // didn't work
user.setId(1L); // ok

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

Timo的这篇出色的文章阐明了 ebean 和 play 如何发挥它们的字节码魔法。

于 2012-09-03T12:21:47.547 回答
5

如果您有一个自创建的对象并调用save(),那么 Ebean 会以 SQL 形式创建一个插入语句。如果您调用update(),它将生成一条更新语句作为 SQL。

Ebean 本身没有逻辑可以对 DB 进行额外的查询,只是为了检查,如果在 DB 中碰巧有一行具有相似的主键,如果是这样,则使用更新语句,否则使用插入语句。就其本身而言,这只是一种低效的行为,因为通常您会知道要创建哪一个,因此不想执行两条 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.

但是,如果您从 DB 中获取对象,则对其调用save()将使用 SQL 中的更新语句,因为 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.

请参阅Ebean 文档

于 2013-10-28T01:29:18.530 回答