Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » EclipseLink » Mysterious duplicate insert
Mysterious duplicate insert [message #781572] Sat, 21 January 2012 17:31 Go to next message
Christoph L is currently offline Christoph L
Messages: 8
Registered: January 2012
Junior Member
Hello,

the following code produces a wrong duplicate insert of the User entity which triggers an sql constraint error.

I've tried with 2.3.3 nightly and 2.4.0 nightly but they have the same behavior. The DB doesn't matter.

The testcase assumes an empty DB.

Am I doing something wrong?


Thanks



package test;

import java.util.HashMap;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;
import javax.persistence.NoResultException;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.Table;

public class Main {

    protected static EntityManager em;
    protected static EntityManager em2;

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("test", new HashMap<Object, Object>());
        em = emf.createEntityManager();
        em2 = emf.createEntityManager();

        User alice = new User("alice");
        Foo foo = new Foo();
        Bar bar = new Bar();
        foo.bar = bar;

        create(alice);
        create(foo);
        create(bar);
    }

    public static User getUser() {
        Query q = em2.createQuery("select x from User x where x.name='alice'");
        try {
            return (User) q.getSingleResult();
        } catch (NoResultException e) {
            return null;
        }
    }

    public static void create(BaseEntity entity) {
        entity.createdBy = getUser();
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        em.persist(entity);
        tx.commit();
    }
}

@MappedSuperclass
abstract class BaseEntity {
    @Id
    @GeneratedValue
    Long id;

    @ManyToOne
    User createdBy;
}

@Entity
@Table(name = "user2")
class User extends BaseEntity {

    String name;

    public User() {
    }

    public User(String name) {
        this.name = name;
    }
}

@Entity
class Foo extends BaseEntity {
    @ManyToOne(cascade = CascadeType.PERSIST)
    Bar bar;
}

@Entity
class Bar extends BaseEntity {

}


<persistence ...>
    <persistence-unit name="test" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

        <class>test.BaseEntity</class>
        <class>test.User</class>
        <class>test.Foo</class>
        <class>test.Bar</class>

        <properties>
            <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost/testdb" />
            <property name="javax.persistence.jdbc.user" value="postgres" />
            <property name="javax.persistence.jdbc.password" value="postgres" />
            <property name="eclipselink.weaving" value="false" />
            <property name="eclipselink.ddl-generation" value="create-tables" />
            <property name="eclipselink.target-database" value="PostgreSQL" />
            <property name="eclipselink.logging.level" value="ALL" />
        </properties>
    </persistence-unit>

</persistence>

Re: Mysterious duplicate insert [message #781763 is a reply to message #781572] Sun, 22 January 2012 07:14 Go to previous messageGo to next message
Tom Eugelink is currently offline Tom Eugelink
Messages: 807
Registered: July 2009
Senior Member
I recently also had a truck load of unexpected (automagic) behavior that made life difficult, so I stripped away all cascading. I now have to call persist on each entity explicitly (not such a big problem, these actions are often well know) and added a delayed merge call (something I added on my own) in my property change events on all entities: whenever an object changes, and it is already persisted, it should be stored to the db.

Seems to work like a charm (still fishing out some missing persist calls here and there, but overall it's a go.) So advice: get rid of the cascade and try again.

Tom



On 2012-01-21 18:31, Christoph L wrote:
> Hello,
>
> the following code produces a wrong duplicate insert of the User entity which triggers an sql constraint error.
>
> I've tried with 2.3.3 nightly and 2.4.0 nightly but they have the same behavior. The DB doesn't matter.
>
> The testcase assumes an empty DB.
>
> Am I doing something wrong?
>
>
> Thanks
>
>
>
>
> package test;
>
> import java.util.HashMap;
>
> import javax.persistence.CascadeType;
> import javax.persistence.Entity;
> import javax.persistence.EntityManager;
> import javax.persistence.EntityManagerFactory;
> import javax.persistence.EntityTransaction;
> import javax.persistence.GeneratedValue;
> import javax.persistence.Id;
> import javax.persistence.ManyToOne;
> import javax.persistence.MappedSuperclass;
> import javax.persistence.NoResultException;
> import javax.persistence.Persistence;
> import javax.persistence.Query;
> import javax.persistence.Table;
>
> public class Main {
>
> protected static EntityManager em;
> protected static EntityManager em2;
>
> public static void main(String[] args) {
> EntityManagerFactory emf = Persistence.createEntityManagerFactory("test", new HashMap<Object, Object>());
> em = emf.createEntityManager();
> em2 = emf.createEntityManager();
>
> User alice = new User("alice");
> Foo foo = new Foo();
> Bar bar = new Bar();
> foo.bar = bar;
>
> create(alice);
> create(foo);
> create(bar);
> }
>
> public static User getUser() {
> Query q = em2.createQuery("select x from User x where x.name='alice'");
> try {
> return (User) q.getSingleResult();
> } catch (NoResultException e) {
> return null;
> }
> }
>
> public static void create(BaseEntity entity) {
> entity.createdBy = getUser();
> EntityTransaction tx = em.getTransaction();
> tx.begin();
> em.persist(entity);
> tx.commit();
> }
> }
>
> @MappedSuperclass
> abstract class BaseEntity {
> @Id
> @GeneratedValue
> Long id;
>
> @ManyToOne
> User createdBy;
> }
>
> @Entity
> @Table(name = "user2")
> class User extends BaseEntity {
>
> String name;
>
> public User() {
> }
>
> public User(String name) {
> this.name = name;
> }
> }
>
> @Entity
> class Foo extends BaseEntity {
> @ManyToOne(cascade = CascadeType.PERSIST)
> Bar bar;
> }
>
> @Entity
> class Bar extends BaseEntity {
>
> }
>
>
>
> <persistence ...>
> <persistence-unit name="test" transaction-type="RESOURCE_LOCAL">
> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
>
> <class>test.BaseEntity</class>
> <class>test.User</class>
> <class>test.Foo</class>
> <class>test.Bar</class>
>
> <properties>
> <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
> <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost/testdb" />
> <property name="javax.persistence.jdbc.user" value="postgres" />
> <property name="javax.persistence.jdbc.password" value="postgres" />
> <property name="eclipselink.weaving" value="false" />
> <property name="eclipselink.ddl-generation" value="create-tables" />
> <property name="eclipselink.target-database" value="PostgreSQL" />
> <property name="eclipselink.logging.level" value="ALL" />
> </properties>
> </persistence-unit>
>
> </persistence>
>
>
Re: Mysterious duplicate insert [message #782349 is a reply to message #781763] Mon, 23 January 2012 15:29 Go to previous messageGo to next message
James Sutherland is currently offline James Sutherland
Messages: 1939
Registered: July 2009
Location: Ottawa, Canada
Senior Member

You are using two different persistence contexts and objects from each and associating them to each-other.

i.e. your user is from em2 and you assign it to the foo that you persist to em, but never merge or find this object.

you need to find the user from em not em2, or merge it and used the merged instance of em.

Also you are persisting bar twice, once from Foo and once on its own in a new transaction, which is invalid, as you cannot call persist on a non managed existing object, only new objects.

There is a persistence unit property, "eclipselink.validate-existence"="true", which will not make this work, but will throw a validation error when you attempt to do this, instead of a constraint error.





James : Wiki : Book : Blog : Twitter
Re: Mysterious duplicate insert [message #782351 is a reply to message #781763] Mon, 23 January 2012 15:29 Go to previous messageGo to next message
James Sutherland is currently offline James Sutherland
Messages: 1939
Registered: July 2009
Location: Ottawa, Canada
Senior Member

You are using two different persistence contexts and objects from each and associating them to each-other.

i.e. your user is from em2 and you assign it to the foo that you persist to em, but never merge or find this object.

you need to find the user from em not em2, or merge it and used the merged instance of em.

Also you are persisting bar twice, once from Foo and once on its own in a new transaction, which is invalid, as you cannot call persist on a non managed existing object, only new objects.

There is a persistence unit property, "eclipselink.validate-existence"="true", which will not make this work, but will throw a validation error when you attempt to do this, instead of a constraint error.




--
James : http://wiki.eclipse.org/EclipseLink : http://en.wikibooks.org/wiki/Java_Persistence : http://java-persistence-performance.blogspot.com/


James : Wiki : Book : Blog : Twitter
Re: Mysterious duplicate insert [message #782376 is a reply to message #782351] Mon, 23 January 2012 16:12 Go to previous message
Christoph L is currently offline Christoph L
Messages: 8
Registered: January 2012
Junior Member
Thank you James, that's what I wanted to know. I use two different persistent contexts because I get two from Spring (I use LocalContainerEntityManagerFactoryBean there). I'll try to configure it so that I get the same instance every time.
Re: Mysterious duplicate insert [message #782378 is a reply to message #782351] Mon, 23 January 2012 16:12 Go to previous message
Christoph L is currently offline Christoph L
Messages: 8
Registered: January 2012
Junior Member
Thank you James, that's what I wanted to know. I use two different persistent contexts because I get two from Spring (I use LocalContainerEntityManagerFactoryBean there). I'll try to configure it so that I get the same instance every time.
Previous Topic:Documentation of nonstandard features
Next Topic:EclipseLink weaving fragment require-bundle version
Goto Forum:
  


Current Time: Wed Oct 01 00:03:26 GMT 2014

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

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