开发者

JPA Error: duplicate key error when persisting a relationship

Hello

I'm using the JPA to persist some java classes as shown below. The persistence of Y seems to work fine as long as the database does not contain elements of the class X having the same id as those of Y.x.id. I am baffled by this. It seems that the JPA implementation I'm using (EclipseLink) does not seem to figure out that although Y is new, Y.x is not new i.e. Y should be persisted but the relationship item X should be updated. Any help in figuring out what's causing this would be greatly appreciated. I've quoted relevant snippets of my program source code below if anything is unclear please don't hesitate to ask me for clarification.

Thanks in advance.

public static interface YY {
    Object id();
}
@Entity
@Access(AccessType.FIELD)
@Table(name = "X")
public class X implements YY {

    @Id
    long id;

    protected X() {
        this.id = Test.orderId++;
    }


    @Override
    public Long id() {
        return id;
    }
    }

public class YPK {


    private final Date date;
    private final Long x;

    public YPK(X x, Date date) {
        this.date = date;
        this.x = x.id();
    }

    @Override
    public boolean equals(Object arg0) {
        if (arg0 == this) {
            return true;
        } else if (arg0 instanceof YPK) {
            return date.equals(((YPK) arg0).date)
                && x.equals(((YPK) arg0).x);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return date.hashCode() ^ x.hashCode();
    }
}

@Entity
@Access(AccessType.FIELD)
@Table(name = "Y")
@IdClass(YPK.class)
public class Y implements YY {

    @Id
    @Temporal(TemporalType.TIMESTAMP)
    Date date;


    @Id
    @ManyToOne(cascade = CascadeType.ALL)
    private X x;

    public Y(X x) {
        this.x = x;
        date = new DateTime().toDate();
    }

    protected Y() {
    }

    @Override
    public YPK id() {
        return new YPK(x, date);
    }

}

public class Test {

public static long orderId = 0;
public static long customerId = 0;

public static void main(String[] args) {
    Map<String, String> properties = new HashMap<String, String>(){
        {
        put("javax.persistence.jdbc.url", "jdbc:derby://localhost:1527/myDB;create=false;");
        put("javax.persistence.jdbc.driver", "org.apache.derby.jdbc.ClientDriver");
        put("javax.persistence.jdbc.user", "myDbUser");
        put("javax.persistence.jdbc.password", "passwd");
//        put(""eclipselink.ddl-generation", "drop-and-create-tables");
        put(""eclipselink.ddl-generation", "none");
        }
    };

    EntityManagerFactory emf =
        Persistence.createEntityManagerFactory("myPersistenceUnit", properties);
    final EntityManager em = emf.createEntityManager();

    X x =  new X();;
    saveItem(em, X.class, x);

    Y y =  new Y(x);;


    Y y2 =  new Y(x);;

    saveItems(em, Y.class, Arrays.asList(y, y2));
    saveItems(em, Y.class, Arrays.asList(y, y2));

    Z z =  new Z(y2);;
    saveItems(em, Z.class, Arrays.asList(z));
}

private static <T extends YY> void saveItem(final EntityManager em,
        Class<T> clazz, T item) {
    synchronized(em) {
        EntityTransaction tx = em.getTransaction();
        saveItem(em, clazz, item, tx);
    }
}

private static <T extends YY> void saveItems(final EntityManager em,
        Class<T> clazz, Collection<T> items) {
    synchronized (em) {
        EntityTransaction tx = em.getTransaction();
        try {
            tx.begin();
            for (T item : items) {
                saveItem(em, clazz, item, tx);
            }
            tx.commit();
        } finally {
            if (tx.isActive()) {
                tx.rollback();
            }
        }
    }
}

private static <T extends YY> void saveItem(final EntityManager em,
            Class<T> clazz,T item, EntityTransaction tx) {
    Object id = item.id();
    T existing = em.find(clazz, id);

    if (existing != null) {
        em.merge(item);
    } else {
        em.persist(item);
    }
}

}

I run the program once with "drop-and-create-tables" uncommented and everything is ok, because the database has no X entries. I comment out drop-and-create-tables and run it again but this time i get the following error. Thanks

[EL Warning]: 2011-03-24 06:52:56.047--UnitOfWork(20391510)--Exception [EclipseLink-4002]
(Eclipse Persistence Services -
    2.2.0.v20110202-r8913): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException:
The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'SQL11032开发者_JAVA百科4065226450' defined on 'X'.
 Error Code: -1 Call:
 INSERT INTO X (ID) VALUES (?)  bind => [1 parameter bound]
 Query: InsertObjectQuery(com.test.X@1b6101e)
 Exception in thread "main" javax.persistence.RollbackException: Exception 
[EclipseLink-4002] (Eclipse Persistence Services -
    2.2.0.v20110202-r8913):
 org.eclipse.persistence.exceptions.DatabaseException
 Internal Exception: java.sql.SQLIntegrityConstraintViolationException:
 The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'SQL110324065226450' defined on 'X'.
 Error Code: -1 Call: INSERT INTO X (ID) VALUES (?)     bind => [1 parameter bound]
 Query: InsertObjectQuery(com.test.X@1b6101e)   at
org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:102)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:63)
at com.test.Test.saveItems(Test.java:80)
at com.test.Test.main(Test.java:56)

Logs are here: 1st run successful 2nd run failure


Noticed you are calling,

saveItems(em, Y.class, Arrays.asList(y, y2));

twice? Does the error occur on the first call, or the second?

Please include your log on finest.


First I don't understand why you using protected constructor. Second - Do you using PostgreSQL? Maybe in your database you must using sequence? Look on http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Sequencing

I once had a similar problem in PostgreSQL. I solved it by using these annotations.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜