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 で新しいモデルを作成し、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]

私は何を間違っていますか?新しいレコードを挿入すると機能します。そのため、更新を行う必要があることを認識していない ebean バグのように見えます。それともどこかで忘れた設定ですか?

ありがとう

4

2 に答える 2

10

play と ebean の両方が実行するバイトコード拡張に関連していると思います。

public フィールドと getter および setter を混在させたときに、あなたと同様の奇妙な問題が発生しました。フィールドを直接使用すると、値が設定されないことがありました。

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

これは潜在的な死の罠のように感じたので、一貫性を保ち、public フィールドのみを使用することにしました。欠点は、ebeans autofetch が機能しなくなったことですが、その一方で、ebean fetchs を使用してクエリを調整する必要がありました。

Timo によるこの優れた投稿は、ebean と play がバイトコード マジックを実行する方法に光を当てています。

于 2012-09-03T12:21:47.547 に答える
5

自分で作成したオブジェクトがあり、save()を呼び出すと、Ebean は挿入ステートメントを SQL として作成します。update()を呼び出すと、更新ステートメントが SQL として作成されます。

Ebean 自体には、DB に同様の主キーを持つ行があるかどうかを確認するためだけに DB に追加のクエリを作成し、その場合は update ステートメントを使用するロジックはありません。通常、どちらを作成するかはわかっているため、2 つの SQL ステートメントを実行するのではなく、1 つのみ実行する必要があるため、これは単純に非効率的な動作です。その場合、行を再度挿入するのではなく、更新を使用するように実際に指示する必要があります。

// 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 からオブジェクトをフェッチする場合、Ebean はその状態が既存ではなく新しいものではないことを認識しているため、そのオブジェクトに対してsave()を呼び出すと、SQL で update ステートメントが使用されます。

// 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 に答える