Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » EclipseLink » Duplicate key error when persisting a relationship(Duplicate key error when persisting a relationship - EclipseLink doesn't seem to check whether the relationship entity exists before attempting to insert)
Duplicate key error when persisting a relationship [message #661347] Thu, 24 March 2011 10:39 Go to next message
loudsight is currently offline loudsightFriend
Messages: 2
Registered: March 2011
Junior Member
Hello
I'm using the EclipseLink 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 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 error below when the program attempts to save a Y.
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 'SQL110324065226450' defined on 'X'.
     Error Code: -1 Call:
     INSERT IGNORE 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 IGNORE 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 in case this helps...
http://pastebin.com/mKtDNYga
http://pastebin.com/b72Vmhgs

[Updated on: Thu, 24 March 2011 13:51]

Report message to a moderator

Re: Duplicate key error when persisting a relationship [message #661386 is a reply to message #661347] Thu, 24 March 2011 13:44 Go to previous messageGo to next message
Chris Delahunt is currently offline Chris DelahuntFriend
Messages: 1039
Registered: July 2009
Senior Member
Hello,

The Y->X relationship is marked as cascade all. This means when you call persist on Y, it will cascade the persist operation to the referenced X, causing X to be inserted if it is not already a managed entity instance. When X exists, the saveItem(x) calls merge(x), which returns the managed entity copy of X. When it doesn't exist it calls persist, which makes X the managed instance.

Net effect is that when you create Y you are passing in the managed instance of X if it is new, or a detached copy of X if is existing. Two options are
1) change the Y->X relationship cascade option so it no longer uses cascade persist.
2) Use the X instance returned from merge(x) when creating the Y instance, so that the managed X instance is always used when creating Y.

Best Regards,
Chris

[Updated on: Thu, 24 March 2011 13:45]

Report message to a moderator

Re: Duplicate key error when persisting a relationship [message #661390 is a reply to message #661386] Thu, 24 March 2011 13:48 Go to previous message
Chris Delahunt is currently offline Chris DelahuntFriend
Messages: 1039
Registered: July 2009
Senior Member
Option 3) always use merge instead of persist. This will cause merge to be called on X instead of persist, which will check if it exists and perform the appropriate update.
Previous Topic:EclipseLink DBWS @PostConstruct Exception
Next Topic:OneToMany list contains null entries when using OrderBy and shared cache
Goto Forum:
  


Current Time: Sat Dec 20 06:51:19 GMT 2014

Powered by FUDForum. Page generated in 0.01677 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software