2012-07-28 116 views
4

我有一个简单的用户模型是这样的:EBean没有更新!它试图执行插入和失败

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,版本记录= 1

我创建一个新模式相同的id +版本,更改用户名

然后我尝试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 bug,它不承认它应该进行更新。或者它是一些配置,我忘了什么地方?

感谢

+0

请显示您调用'save()'方法的代码。 – 2012-07-28 10:36:00

+0

为什么不调用'update()' – adis 2012-07-30 08:21:08

回答

8

我认为这是相关的字节码增强,无论播放和ebean执行。

当我将公有字段与getter和setter混合使用时,我有类似于你的奇怪问题。有时在直接使用字段时没有设置数值:

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

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

This excellent post Timo对ebean和play如何执行他们的字节码魔术做了一些说明。

+0

有一个类似的问题,试图在Eclipse中运行EBean测试,它会抱怨,因为该类没有增强(说没有setter/getters)。所以我添加了它们,但添加它们会导致一些EBean测试在从播放命令行运行时失败。你不能在eclipse中运行单元测试。 – 2013-08-01 19:34:43

+0

我刚刚有一个非常类似的问题,很奇怪的行为。这就是我喜欢Spring JdbcTemplate的原因。真的帮助 – 2014-02-08 16:54:33

+0

乔尔S,你的解决方案为我工作。 – 2014-10-25 01:35:01

5

如果您有自己创建的对象,并且调用save(),那么Ebean将插入语句作为SQL。如果您致电更新()它将更新语句为SQL。

Ebean本身没有任何逻辑可以为数据库创建额外的查询来检查数据库中是否有类似主键的行,如果是,则使用更新语句,否则使用insert语句。这只会是低效率的行为,因为通常你会知道要制作哪一个,因此不想执行两条SQL语句,只有一条。在这种情况下,您实际上必须告诉它使用更新,而不是尝试再次插入行。

// Create user. 
User user1 = new User(); 
user1.setId(1L); 
user1.setVersion(1L); 
user1.setUserName("foo"); 
user1.setWorkPlace("bar"); 
user1.setEmail("[email protected]"); 
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. 

但是,如果从数据库获取对象,然后调用保存()它会在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("[email protected]"); 
user3.setVersion(2L); 
user3.save(); // This will work. 

请参阅Ebean documentation

+0

谢谢!在我看来,'.save()'的行为是不一致的(例如.save()会更新,下一个会尝试INSERT),但也许我只是在没有意识到的情况下手动删除数据。无论如何,这个解决方案工作正常! 在我的情况下,我能够测试以查看该项目是否具有“id”属性集,如果它确实存在,我知道它已经存在,所以我可以调用'.update()',否则我调用'.save()'。这节省了我的数据库电话。 – 2014-02-19 14:26:37

+0

嗯。像这样的问题使得EBean很难与Scala/Play一起使用。 – Ashesh 2016-04-20 13:30:06

+0

@Ashesh:所以你宁愿Ebean做额外的SQL查询,以确定它是否应该插入或更新'save()'?那只会是糟糕的表现。 – t0mppa 2016-04-20 14:31:50