You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@openjpa.apache.org by Henno Vermeulen <he...@huizemolenaar.nl> on 2011/02/21 17:18:45 UTC

adding new entities to an existing List fails when using GenerationType.IDENTITY

Hello,

I think I found a serious issue (in both OpenJPA 2.0.0 and 2.1.0) when I try to add new entities to an existing list and I use a generated identity with GenerationType.IDENTITY.

I start with a fresh database and let OpenJPA create the schema. I have a ProductOrder entity that contains a List of ProductOrderLines, annotated as such:

@Entity
public class ProductOrder {
...
            @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
            private List<ProductOrderLine> products = new ArrayList<ProductOrderLine>();
...
}

The entity in the List (ProductOrderLine) has a generated id with GenerationType.IDENTITY.

@Entity
public class ProductOrderLine {

            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            private Long id;
...
}

I start with a ProductOrder that has these products:
1 - orange
2 - apple

I insert two new products into the list so that I get:
null - banana
null - pear
1 - orange
2 - apple

Then I merge the entity (I work with attach/detach, not sure if this matters).
OpenJPA merge correctly returns a ProductOrder with this list:

3 - banana
4 - pear
1 - orange
2 - apple

However OpenJPA generates the wrong SQL so that the database contains something completely different and indeed selecting the ProductOrder by it's id gives:

3 - banana
4 - pear
4 - pear
4 - pear

I tested this with sql server. (I tried hsqldb but this also suffers from bug https://issues.apache.org/jira/browse/OPENJPA-1066).
This problem does not occur when I use GenerationType.AUTO for ProductOrderLine. My example uses a join table, but foreign key columns seem to have the same problem. Should I use another generation type? If so which one, and is it compatible with existing sql server data that had id values automatically generated by sql server?

Please let me know if I should file a bug report and if I should attempt to convert my unit test to one accepted by OpenJPA standards.

Regards,
Henno Vermeulen


package entities;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class ProductOrderLine {

                @Id
                @GeneratedValue(strategy = GenerationType.IDENTITY)
                private Long id;

                private String name;

                public ProductOrderLine() {
                }

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

                public String getName() {
                               return name;
                }

                public void setName(String name) {
                               this.name = name;
                }

                public Long getId() {
                               return id;
                }

}


package entities;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class ProductOrder {

                @Id
                @GeneratedValue(strategy = GenerationType.IDENTITY)
                private Long id;

                // Workaround for https://issues.apache.org/jira/browse/OPENJPA-1947
                private String name;

                @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
                private List<ProductOrderLine> products = new ArrayList<ProductOrderLine>();

                public ProductOrder() {
                }

                public Long getId() {
                               return id;
                }

                public void setProducts(List<ProductOrderLine> products) {
                               this.products = products;
                }

                public List<ProductOrderLine> getProducts() {
                               return products;
                }

}

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import junit.framework.TestCase;
import entities.ProductOrder;
import entities.ProductOrderLine;

/**
* I tested this with MSSQL SERVER (I tried hsqldb but this suffers from an
* additional problem where it mixes up id 0 with id null, see
* https://issues.apache.org/jira/browse/OPENJPA-1066)/
*
 * @author Henno Vermeulen
*/
public class ProductOrderTest extends TestCase {

                private EntityManagerFactory factory;

                public void setUp() {
                               factory = Persistence.createEntityManagerFactory("testPU", System
                                                               .getProperties());
                }

                public void testProductOrderLineCopy() {
                               // If we do this, it fails after the first save!!!!
                               // hsqldb seems to convert null to 0 and confuse this with an existing
                               // id or something
                               // insertSomeData();

                               ProductOrder original = new ProductOrder();
                               ArrayList<ProductOrderLine> products = new ArrayList<ProductOrderLine>();
                               products.addAll(Arrays.asList(new ProductOrderLine("orange"),
                                                               new ProductOrderLine("apple")));
                               original.setProducts(products);

                               // START
                               // null - orange
                               // null - apple
                               printProducts("Before first save:", original);
                               ProductOrder firstSaved = save(original);
                               // 1 - orange
                               // 2 - apple
                               printProducts("After first save:", firstSaved);

                               ProductOrderLine orange = firstSaved.getProducts().get(0);
                               ProductOrderLine apple = firstSaved.getProducts().get(1);
                               assertEquals("orange", orange.getName());
                               assertEquals("apple", apple.getName());
                               Long orangeId = orange.getId();
                               Long appleId = apple.getId();
                               assertTrue(orangeId != null && appleId != null);

                               ProductOrderLine banana = new ProductOrderLine("banana");
                               ProductOrderLine pear = new ProductOrderLine("pear");
                               assertTrue(banana.getId() == null && pear.getId() == null);

                               firstSaved.getProducts().addAll(0, Arrays.asList(banana, pear));
                               // Changed to
                               // null - banana
                               // null - pear
                               // 1 - orange
                               // 2 - apple
                               printProducts("Before second save:", firstSaved);
                               assertExpectedProductLines(firstSaved, orangeId, appleId, true);

                               ProductOrder secondSaved = save(firstSaved);
                               // Expected after save:
                               // 3 - banana
                               // 4 - pear
                               // 1 - orange
                               // 2 - apple

                               // On SQL server this is actually returned by save, but not by find!
                               printProducts("After second save:", secondSaved);
                               assertExpectedProductLines(secondSaved, orangeId, appleId, false);

                               ProductOrder found = findById(secondSaved.getId());
                               printProducts("Found by id after second save:", found);
                               // This one fails!!!!
                               // On SQL server this returns
                               // 3 - banana
                               // 4 - pear
                               // 4 - pear
                               // 4 - pear
                               assertExpectedProductLines(found, orangeId, appleId, false);
                }

                private void printProducts(String description, ProductOrder order) {
                               System.out.println(description);
                               for (ProductOrderLine p : order.getProducts()) {
                                               System.out.println(p.getId() + " - " + p.getName());
                               }
                }

                private void assertExpectedProductLines(ProductOrder line, Long orangeId,
                                               Long appleId, boolean beforeMerge) {
                               List<ProductOrderLine> products = new ArrayList<ProductOrderLine>();
                               products.addAll(line.getProducts());
                               Collections.sort(products, new Comparator<ProductOrderLine>() {
                                               @Override
                                               public int compare(ProductOrderLine o1, ProductOrderLine o2) {
                                                               return o1.getName().compareTo(o2.getName());
                                               }
                               });
                               assertEquals("apple", products.get(0).getName());
                               assertEquals(appleId, products.get(0).getId());
                               assertEquals("banana", products.get(1).getName());
                               assertEquals(beforeMerge, null == products.get(1).getId());
                               assertEquals("orange", products.get(2).getName());
                               assertEquals(orangeId, products.get(2).getId());
                               assertEquals("pear", products.get(3).getName());
                               assertEquals(beforeMerge, null == products.get(3).getId());
                }

                private ProductOrder save(ProductOrder order) {
                               EntityManager em = factory.createEntityManager();
                               em.getTransaction().begin();

                               ProductOrder result;
                               if (order.getId() == null) {
                                               em.persist(order);
                                               result = order;
                               } else {
                                               result = em.merge(order);
                               }

                               em.getTransaction().commit();
                               em.detach(result);
                               em.close();
                               return result;
                }

                private ProductOrder findById(Long id) {
                               EntityManager em = factory.createEntityManager();
                               em.getTransaction().begin();
                               ProductOrder result = em.find(ProductOrder.class, id);
                               em.getTransaction().commit();
                               em.detach(result);
                               em.close();
                               return result;
                }

}

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="http://java.sun.com/xml/ns/persistence persistence_1_0.xsd"
                version="1.0">

                <persistence-unit name="testPU">
                               <provider>org.apache.openjpa.persistence.PersistenceProviderImpl
                               </provider>
                               <class>entities.ProductOrder</class>
                               <class>entities.ProductOrderLine</class>
                               <exclude-unlisted-classes>true</exclude-unlisted-classes>
                               <properties>
                                               <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema" />
                                               <property name="openjpa.ConnectionDriverName"
                                                               value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
                                               <property name="openjpa.ConnectionURL"
                                                               value="jdbc:sqlserver://localhost\\SQL2008:1433;databaseName=obliprototypeunittest" />
                                               <property name="openjpa.ConnectionUserName" value="obliprototype" />
                                               <property name="openjpa.ConnectionPassword" value="hidden" />
                                               <property name="openjpa.Log" value="DefaultLevel=INFO" />
                               </properties>
                </persistence-unit>
</persistence>

RE: adding new entities to an existing List fails when using GenerationType.IDENTITY

Posted by Henno Vermeulen <he...@huizemolenaar.nl>.
Allright, done!

Henno

-----Oorspronkelijk bericht-----
Van: Rick Curtis [mailto:curtisr7@gmail.com]
Verzonden: dinsdag 22 februari 2011 16:02
Aan: users@openjpa.apache.org
Onderwerp: Re: adding new entities to an existing List fails when using GenerationType.IDENTITY

Henno -

I'll reformat your test case, but first can I have you re-upload your test
case and make sure to allow " Grant license to ASF for inclusion in ASF
works (as per the Apache
License<http://www.apache.org/licenses/LICENSE-2.0>§5)  ?
Also, please remove the @Author tag from ProductOrderTest.

Thanks,
Rick

On Tue, Feb 22, 2011 at 2:55 AM, Henno Vermeulen <he...@huizemolenaar.nl>wrote:

> I created https://issues.apache.org/jira/browse/OPENJPA-1949
>
> I attached my testcase, however it does not yet extend from
> SingleEMFTestCase because it feels a bit daunting to get started with that
> (and I have other work todo).
>
> Regards,
> Henno
>
> -----Oorspronkelijk bericht-----
> Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
> Verzonden: dinsdag 22 februari 2011 9:32
> Aan: 'users@openjpa.apache.org'
> Onderwerp: RE: adding new entities to an existing List fails when using
> GenerationType.IDENTITY
>
> Alright I will open a JIRA issue fort his.
>
> In my test I use the javaagent, in my app I use compile-time enhancement.
>
> Thank you for the suggestion. However in my original problem I have a list
> of product order lines with @OrderColumn (transparent to the application)
> and orhpanRemoval = true. A UI shows the ordered list of product lines and I
> am making copy/paste so the user can copy them (either to the same or new
> product order) or reorder them so the order is really important. (On a copy
> I make a new order line with id and version set to null).
>
> Regards,
> Henno
>
> -----Oorspronkelijk bericht-----
> Van: Rick Curtis [mailto:curtisr7@gmail.com]
> Verzonden: maandag 21 februari 2011 23:22
> Aan: users@openjpa.apache.org
> Onderwerp: Re: adding new entities to an existing List fails when using
> GenerationType.IDENTITY
>
> Henno --
>
> Try changing :
>
> firstSaved.getProducts().addAll(0, Arrays.asList(banana, pear));
> to
> firstSaved.getProducts().addAll(Arrays.asList(banana, pear));
>
> In my tests it seemed to make the problem go away... we're somehow getting
> confused when writing the join table on the second update. Please go ahead
> and open a JIRA for this issue.
>
> Thanks,
> Rick
>
> On Mon, Feb 21, 2011 at 1:47 PM, Rick Curtis <cu...@gmail.com> wrote:
>
> > How are you enhancing your Entities?
> >
> > Thanks,
> > Rick
> >
> >
> > On Mon, Feb 21, 2011 at 11:22 AM, Henno Vermeulen <
> henno@huizemolenaar.nl>wrote:
> >
> >> Although this workaround works in my toy example, it does not seem to
> work
> >> in our application, switching to GenerationType.TABLE does.
> >>
> >> Henno
> >>
> >> -----Oorspronkelijk bericht-----
> >> Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
> >> Verzonden: maandag 21 februari 2011 17:50
> >> Aan: 'users@openjpa.apache.org'
> >> Onderwerp: RE: adding new entities to an existing List fails when using
> >> GenerationType.IDENTITY
> >>
> >> I found a workaround:
> >>
> >> In my test I directly added the new ProductOrderLines to the List in the
> >> ProductOrder. I do not know the implementation of this List because it
> is
> >> the one returned by OpenJPA (at least after detaching).
> >> When I set the list reference to a new arraylist and add both the
> existing
> >> elements and the new ones to this list, the test passes.
> >> I.e. change the line
> >>
> >> firstSaved.getProducts().addAll(0, Arrays.asList(banana, pear));
> >>
> >> to
> >>
> >>                List<ProductOrderLine> newProducts = new
> >> ArrayList<ProductOrderLine>();
> >>                firstSaved.getProducts().addAll(Arrays.asList(banana,
> >> pear));
> >>                newProducts.addAll(firstSaved.getProducts());
> >>                firstSaved.setProducts(newProducts);
> >>
> >> Makes the test work.
> >>
> >> Regards, Henno
> >>
> >>
> >> -----Oorspronkelijk bericht-----
> >> Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
> >> Verzonden: maandag 21 februari 2011 17:19
> >> Aan: 'users@openjpa.apache.org'
> >> Onderwerp: adding new entities to an existing List fails when using
> >> GenerationType.IDENTITY
> >>
> >> Hello,
> >>
> >> I think I found a serious issue (in both OpenJPA 2.0.0 and 2.1.0) when I
> >> try to add new entities to an existing list and I use a generated
> identity
> >> with GenerationType.IDENTITY.
> >>
> >> I start with a fresh database and let OpenJPA create the schema. I have
> a
> >> ProductOrder entity that contains a List of ProductOrderLines, annotated
> as
> >> such:
> >>
> >> @Entity
> >> public class ProductOrder {
> >> ...
> >>            @OneToMany(cascade = CascadeType.ALL, fetch =
> FetchType.EAGER)
> >>            private List<ProductOrderLine> products = new
> >> ArrayList<ProductOrderLine>();
> >> ...
> >> }
> >>
> >> The entity in the List (ProductOrderLine) has a generated id with
> >> GenerationType.IDENTITY.
> >>
> >> @Entity
> >> public class ProductOrderLine {
> >>
> >>            @Id
> >>            @GeneratedValue(strategy = GenerationType.IDENTITY)
> >>            private Long id;
> >> ...
> >> }
> >>
> >> I start with a ProductOrder that has these products:
> >> 1 - orange
> >> 2 - apple
> >>
> >> I insert two new products into the list so that I get:
> >> null - banana
> >> null - pear
> >> 1 - orange
> >> 2 - apple
> >>
> >> Then I merge the entity (I work with attach/detach, not sure if this
> >> matters).
> >> OpenJPA merge correctly returns a ProductOrder with this list:
> >>
> >> 3 - banana
> >> 4 - pear
> >> 1 - orange
> >> 2 - apple
> >>
> >> However OpenJPA generates the wrong SQL so that the database contains
> >> something completely different and indeed selecting the ProductOrder by
> it's
> >> id gives:
> >>
> >> 3 - banana
> >> 4 - pear
> >> 4 - pear
> >> 4 - pear
> >>
> >> I tested this with sql server. (I tried hsqldb but this also suffers
> from
> >> bug https://issues.apache.org/jira/browse/OPENJPA-1066).
> >> This problem does not occur when I use GenerationType.AUTO for
> >> ProductOrderLine. My example uses a join table, but foreign key columns
> seem
> >> to have the same problem. Should I use another generation type? If so
> which
> >> one, and is it compatible with existing sql server data that had id
> values
> >> automatically generated by sql server?
> >>
> >> Please let me know if I should file a bug report and if I should attempt
> >> to convert my unit test to one accepted by OpenJPA standards.
> >>
> >> Regards,
> >> Henno Vermeulen
> >>
> >>
> >> package entities;
> >>
> >> import javax.persistence.Entity;
> >> import javax.persistence.GeneratedValue;
> >> import javax.persistence.GenerationType;
> >> import javax.persistence.Id;
> >>
> >> @Entity
> >> public class ProductOrderLine {
> >>
> >>                @Id
> >>                @GeneratedValue(strategy = GenerationType.IDENTITY)
> >>                private Long id;
> >>
> >>                private String name;
> >>
> >>                public ProductOrderLine() {
> >>                }
> >>
> >>                public ProductOrderLine(String name) {
> >>                               this.name = name;
> >>                }
> >>
> >>                public String getName() {
> >>                               return name;
> >>                }
> >>
> >>                public void setName(String name) {
> >>                               this.name = name;
> >>                }
> >>
> >>                public Long getId() {
> >>                               return id;
> >>                }
> >>
> >> }
> >>
> >>
> >> package entities;
> >>
> >> import java.util.ArrayList;
> >> import java.util.List;
> >>
> >> import javax.persistence.CascadeType;
> >> import javax.persistence.Entity;
> >> import javax.persistence.FetchType;
> >> import javax.persistence.GeneratedValue;
> >> import javax.persistence.GenerationType;
> >> import javax.persistence.Id;
> >> import javax.persistence.OneToMany;
> >>
> >> @Entity
> >> public class ProductOrder {
> >>
> >>                @Id
> >>                @GeneratedValue(strategy = GenerationType.IDENTITY)
> >>                private Long id;
> >>
> >>                // Workaround for
> >> https://issues.apache.org/jira/browse/OPENJPA-1947
> >>                private String name;
> >>
> >>                @OneToMany(cascade = CascadeType.ALL, fetch =
> >> FetchType.EAGER)
> >>                private List<ProductOrderLine> products = new
> >> ArrayList<ProductOrderLine>();
> >>
> >>                public ProductOrder() {
> >>                }
> >>
> >>                public Long getId() {
> >>                               return id;
> >>                }
> >>
> >>                public void setProducts(List<ProductOrderLine> products)
> {
> >>                               this.products = products;
> >>                }
> >>
> >>                public List<ProductOrderLine> getProducts() {
> >>                               return products;
> >>                }
> >>
> >> }
> >>
> >> import java.util.ArrayList;
> >> import java.util.Arrays;
> >> import java.util.Collections;
> >> import java.util.Comparator;
> >> import java.util.List;
> >>
> >> import javax.persistence.EntityManager;
> >> import javax.persistence.EntityManagerFactory;
> >> import javax.persistence.Persistence;
> >>
> >> import junit.framework.TestCase;
> >> import entities.ProductOrder;
> >> import entities.ProductOrderLine;
> >>
> >> /**
> >> * I tested this with MSSQL SERVER (I tried hsqldb but this suffers from
> an
> >> * additional problem where it mixes up id 0 with id null, see
> >> * https://issues.apache.org/jira/browse/OPENJPA-1066)/
> >> *
> >>  * @author Henno Vermeulen
> >> */
> >> public class ProductOrderTest extends TestCase {
> >>
> >>                private EntityManagerFactory factory;
> >>
> >>                public void setUp() {
> >>                               factory =
> >> Persistence.createEntityManagerFactory("testPU", System
> >>
> >> .getProperties());
> >>                }
> >>
> >>                public void testProductOrderLineCopy() {
> >>                               // If we do this, it fails after the first
> >> save!!!!
> >>                               // hsqldb seems to convert null to 0 and
> >> confuse this with an existing
> >>                               // id or something
> >>                               // insertSomeData();
> >>
> >>                               ProductOrder original = new
> ProductOrder();
> >>                               ArrayList<ProductOrderLine> products = new
> >> ArrayList<ProductOrderLine>();
> >>                               products.addAll(Arrays.asList(new
> >> ProductOrderLine("orange"),
> >>                                                               new
> >> ProductOrderLine("apple")));
> >>                               original.setProducts(products);
> >>
> >>                               // START
> >>                               // null - orange
> >>                               // null - apple
> >>                               printProducts("Before first save:",
> >> original);
> >>                               ProductOrder firstSaved = save(original);
> >>                               // 1 - orange
> >>                               // 2 - apple
> >>                               printProducts("After first save:",
> >> firstSaved);
> >>
> >>                               ProductOrderLine orange =
> >> firstSaved.getProducts().get(0);
> >>                               ProductOrderLine apple =
> >> firstSaved.getProducts().get(1);
> >>                               assertEquals("orange", orange.getName());
> >>                               assertEquals("apple", apple.getName());
> >>                               Long orangeId = orange.getId();
> >>                               Long appleId = apple.getId();
> >>                               assertTrue(orangeId != null && appleId !=
> >> null);
> >>
> >>                               ProductOrderLine banana = new
> >> ProductOrderLine("banana");
> >>                               ProductOrderLine pear = new
> >> ProductOrderLine("pear");
> >>                               assertTrue(banana.getId() == null &&
> >> pear.getId() == null);
> >>
> >>                               firstSaved.getProducts().addAll(0,
> >> Arrays.asList(banana, pear));
> >>                               // Changed to
> >>                               // null - banana
> >>                               // null - pear
> >>                               // 1 - orange
> >>                               // 2 - apple
> >>                               printProducts("Before second save:",
> >> firstSaved);
> >>                               assertExpectedProductLines(firstSaved,
> >> orangeId, appleId, true);
> >>
> >>                               ProductOrder secondSaved =
> save(firstSaved);
> >>                               // Expected after save:
> >>                               // 3 - banana
> >>                               // 4 - pear
> >>                               // 1 - orange
> >>                               // 2 - apple
> >>
> >>                               // On SQL server this is actually returned
> >> by save, but not by find!
> >>                               printProducts("After second save:",
> >> secondSaved);
> >>                               assertExpectedProductLines(secondSaved,
> >> orangeId, appleId, false);
> >>
> >>                               ProductOrder found =
> >> findById(secondSaved.getId());
> >>                               printProducts("Found by id after second
> >> save:", found);
> >>                               // This one fails!!!!
> >>                               // On SQL server this returns
> >>                               // 3 - banana
> >>                               // 4 - pear
> >>                               // 4 - pear
> >>                               // 4 - pear
> >>                               assertExpectedProductLines(found,
> orangeId,
> >> appleId, false);
> >>                }
> >>
> >>                private void printProducts(String description,
> ProductOrder
> >> order) {
> >>                               System.out.println(description);
> >>                               for (ProductOrderLine p :
> >> order.getProducts()) {
> >>
> System.out.println(p.getId()
> >> + " - " + p.getName());
> >>                               }
> >>                }
> >>
> >>                private void assertExpectedProductLines(ProductOrder
> line,
> >> Long orangeId,
> >>                                               Long appleId, boolean
> >> beforeMerge) {
> >>                               List<ProductOrderLine> products = new
> >> ArrayList<ProductOrderLine>();
> >>                               products.addAll(line.getProducts());
> >>                               Collections.sort(products, new
> >> Comparator<ProductOrderLine>() {
> >>                                               @Override
> >>                                               public int
> >> compare(ProductOrderLine o1, ProductOrderLine o2) {
> >>                                                               return
> >> o1.getName().compareTo(o2.getName());
> >>                                               }
> >>                               });
> >>                               assertEquals("apple",
> >> products.get(0).getName());
> >>                               assertEquals(appleId,
> >> products.get(0).getId());
> >>                               assertEquals("banana",
> >> products.get(1).getName());
> >>                               assertEquals(beforeMerge, null ==
> >> products.get(1).getId());
> >>                               assertEquals("orange",
> >> products.get(2).getName());
> >>                               assertEquals(orangeId,
> >> products.get(2).getId());
> >>                               assertEquals("pear",
> >> products.get(3).getName());
> >>                               assertEquals(beforeMerge, null ==
> >> products.get(3).getId());
> >>                }
> >>
> >>                private ProductOrder save(ProductOrder order) {
> >>                               EntityManager em =
> >> factory.createEntityManager();
> >>                               em.getTransaction().begin();
> >>
> >>                               ProductOrder result;
> >>                               if (order.getId() == null) {
> >>                                               em.persist(order);
> >>                                               result = order;
> >>                               } else {
> >>                                               result = em.merge(order);
> >>                               }
> >>
> >>                               em.getTransaction().commit();
> >>                               em.detach(result);
> >>                               em.close();
> >>                               return result;
> >>                }
> >>
> >>                private ProductOrder findById(Long id) {
> >>                               EntityManager em =
> >> factory.createEntityManager();
> >>                               em.getTransaction().begin();
> >>                               ProductOrder result =
> >> em.find(ProductOrder.class, id);
> >>                               em.getTransaction().commit();
> >>                               em.detach(result);
> >>                               em.close();
> >>                               return result;
> >>                }
> >>
> >> }
> >>
> >> <persistence xmlns="http://java.sun.com/xml/ns/persistence"
> >>                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
> >>                xsi:schemaLocation="
> http://java.sun.com/xml/ns/persistencepersistence_1_0.xsd"
> >>                version="1.0">
> >>
> >>                <persistence-unit name="testPU">
> >>
> >> <provider>org.apache.openjpa.persistence.PersistenceProviderImpl
> >>                               </provider>
> >>                               <class>entities.ProductOrder</class>
> >>                               <class>entities.ProductOrderLine</class>
> >>
> >> <exclude-unlisted-classes>true</exclude-unlisted-classes>
> >>                               <properties>
> >>                                               <property
> >> name="openjpa.jdbc.SynchronizeMappings" value="buildSchema" />
> >>                                               <property
> >> name="openjpa.ConnectionDriverName"
> >>
> >> value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
> >>                                               <property
> >> name="openjpa.ConnectionURL"
> >>
> >>
> value="jdbc:sqlserver://localhost\\SQL2008:1433;databaseName=obliprototypeunittest"
> >> />
> >>                                               <property
> >> name="openjpa.ConnectionUserName" value="obliprototype" />
> >>                                               <property
> >> name="openjpa.ConnectionPassword" value="hidden" />
> >>                                               <property
> name="openjpa.Log"
> >> value="DefaultLevel=INFO" />
> >>                               </properties>
> >>                </persistence-unit>
> >> </persistence>
> >>
> >>
> >
>
>

Re: adding new entities to an existing List fails when using GenerationType.IDENTITY

Posted by Rick Curtis <cu...@gmail.com>.
Henno -

I'll reformat your test case, but first can I have you re-upload your test
case and make sure to allow " Grant license to ASF for inclusion in ASF
works (as per the Apache
License<http://www.apache.org/licenses/LICENSE-2.0>§5)  ?
Also, please remove the @Author tag from ProductOrderTest.

Thanks,
Rick

On Tue, Feb 22, 2011 at 2:55 AM, Henno Vermeulen <he...@huizemolenaar.nl>wrote:

> I created https://issues.apache.org/jira/browse/OPENJPA-1949
>
> I attached my testcase, however it does not yet extend from
> SingleEMFTestCase because it feels a bit daunting to get started with that
> (and I have other work todo).
>
> Regards,
> Henno
>
> -----Oorspronkelijk bericht-----
> Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
> Verzonden: dinsdag 22 februari 2011 9:32
> Aan: 'users@openjpa.apache.org'
> Onderwerp: RE: adding new entities to an existing List fails when using
> GenerationType.IDENTITY
>
> Alright I will open a JIRA issue fort his.
>
> In my test I use the javaagent, in my app I use compile-time enhancement.
>
> Thank you for the suggestion. However in my original problem I have a list
> of product order lines with @OrderColumn (transparent to the application)
> and orhpanRemoval = true. A UI shows the ordered list of product lines and I
> am making copy/paste so the user can copy them (either to the same or new
> product order) or reorder them so the order is really important. (On a copy
> I make a new order line with id and version set to null).
>
> Regards,
> Henno
>
> -----Oorspronkelijk bericht-----
> Van: Rick Curtis [mailto:curtisr7@gmail.com]
> Verzonden: maandag 21 februari 2011 23:22
> Aan: users@openjpa.apache.org
> Onderwerp: Re: adding new entities to an existing List fails when using
> GenerationType.IDENTITY
>
> Henno --
>
> Try changing :
>
> firstSaved.getProducts().addAll(0, Arrays.asList(banana, pear));
> to
> firstSaved.getProducts().addAll(Arrays.asList(banana, pear));
>
> In my tests it seemed to make the problem go away... we're somehow getting
> confused when writing the join table on the second update. Please go ahead
> and open a JIRA for this issue.
>
> Thanks,
> Rick
>
> On Mon, Feb 21, 2011 at 1:47 PM, Rick Curtis <cu...@gmail.com> wrote:
>
> > How are you enhancing your Entities?
> >
> > Thanks,
> > Rick
> >
> >
> > On Mon, Feb 21, 2011 at 11:22 AM, Henno Vermeulen <
> henno@huizemolenaar.nl>wrote:
> >
> >> Although this workaround works in my toy example, it does not seem to
> work
> >> in our application, switching to GenerationType.TABLE does.
> >>
> >> Henno
> >>
> >> -----Oorspronkelijk bericht-----
> >> Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
> >> Verzonden: maandag 21 februari 2011 17:50
> >> Aan: 'users@openjpa.apache.org'
> >> Onderwerp: RE: adding new entities to an existing List fails when using
> >> GenerationType.IDENTITY
> >>
> >> I found a workaround:
> >>
> >> In my test I directly added the new ProductOrderLines to the List in the
> >> ProductOrder. I do not know the implementation of this List because it
> is
> >> the one returned by OpenJPA (at least after detaching).
> >> When I set the list reference to a new arraylist and add both the
> existing
> >> elements and the new ones to this list, the test passes.
> >> I.e. change the line
> >>
> >> firstSaved.getProducts().addAll(0, Arrays.asList(banana, pear));
> >>
> >> to
> >>
> >>                List<ProductOrderLine> newProducts = new
> >> ArrayList<ProductOrderLine>();
> >>                firstSaved.getProducts().addAll(Arrays.asList(banana,
> >> pear));
> >>                newProducts.addAll(firstSaved.getProducts());
> >>                firstSaved.setProducts(newProducts);
> >>
> >> Makes the test work.
> >>
> >> Regards, Henno
> >>
> >>
> >> -----Oorspronkelijk bericht-----
> >> Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
> >> Verzonden: maandag 21 februari 2011 17:19
> >> Aan: 'users@openjpa.apache.org'
> >> Onderwerp: adding new entities to an existing List fails when using
> >> GenerationType.IDENTITY
> >>
> >> Hello,
> >>
> >> I think I found a serious issue (in both OpenJPA 2.0.0 and 2.1.0) when I
> >> try to add new entities to an existing list and I use a generated
> identity
> >> with GenerationType.IDENTITY.
> >>
> >> I start with a fresh database and let OpenJPA create the schema. I have
> a
> >> ProductOrder entity that contains a List of ProductOrderLines, annotated
> as
> >> such:
> >>
> >> @Entity
> >> public class ProductOrder {
> >> ...
> >>            @OneToMany(cascade = CascadeType.ALL, fetch =
> FetchType.EAGER)
> >>            private List<ProductOrderLine> products = new
> >> ArrayList<ProductOrderLine>();
> >> ...
> >> }
> >>
> >> The entity in the List (ProductOrderLine) has a generated id with
> >> GenerationType.IDENTITY.
> >>
> >> @Entity
> >> public class ProductOrderLine {
> >>
> >>            @Id
> >>            @GeneratedValue(strategy = GenerationType.IDENTITY)
> >>            private Long id;
> >> ...
> >> }
> >>
> >> I start with a ProductOrder that has these products:
> >> 1 - orange
> >> 2 - apple
> >>
> >> I insert two new products into the list so that I get:
> >> null - banana
> >> null - pear
> >> 1 - orange
> >> 2 - apple
> >>
> >> Then I merge the entity (I work with attach/detach, not sure if this
> >> matters).
> >> OpenJPA merge correctly returns a ProductOrder with this list:
> >>
> >> 3 - banana
> >> 4 - pear
> >> 1 - orange
> >> 2 - apple
> >>
> >> However OpenJPA generates the wrong SQL so that the database contains
> >> something completely different and indeed selecting the ProductOrder by
> it's
> >> id gives:
> >>
> >> 3 - banana
> >> 4 - pear
> >> 4 - pear
> >> 4 - pear
> >>
> >> I tested this with sql server. (I tried hsqldb but this also suffers
> from
> >> bug https://issues.apache.org/jira/browse/OPENJPA-1066).
> >> This problem does not occur when I use GenerationType.AUTO for
> >> ProductOrderLine. My example uses a join table, but foreign key columns
> seem
> >> to have the same problem. Should I use another generation type? If so
> which
> >> one, and is it compatible with existing sql server data that had id
> values
> >> automatically generated by sql server?
> >>
> >> Please let me know if I should file a bug report and if I should attempt
> >> to convert my unit test to one accepted by OpenJPA standards.
> >>
> >> Regards,
> >> Henno Vermeulen
> >>
> >>
> >> package entities;
> >>
> >> import javax.persistence.Entity;
> >> import javax.persistence.GeneratedValue;
> >> import javax.persistence.GenerationType;
> >> import javax.persistence.Id;
> >>
> >> @Entity
> >> public class ProductOrderLine {
> >>
> >>                @Id
> >>                @GeneratedValue(strategy = GenerationType.IDENTITY)
> >>                private Long id;
> >>
> >>                private String name;
> >>
> >>                public ProductOrderLine() {
> >>                }
> >>
> >>                public ProductOrderLine(String name) {
> >>                               this.name = name;
> >>                }
> >>
> >>                public String getName() {
> >>                               return name;
> >>                }
> >>
> >>                public void setName(String name) {
> >>                               this.name = name;
> >>                }
> >>
> >>                public Long getId() {
> >>                               return id;
> >>                }
> >>
> >> }
> >>
> >>
> >> package entities;
> >>
> >> import java.util.ArrayList;
> >> import java.util.List;
> >>
> >> import javax.persistence.CascadeType;
> >> import javax.persistence.Entity;
> >> import javax.persistence.FetchType;
> >> import javax.persistence.GeneratedValue;
> >> import javax.persistence.GenerationType;
> >> import javax.persistence.Id;
> >> import javax.persistence.OneToMany;
> >>
> >> @Entity
> >> public class ProductOrder {
> >>
> >>                @Id
> >>                @GeneratedValue(strategy = GenerationType.IDENTITY)
> >>                private Long id;
> >>
> >>                // Workaround for
> >> https://issues.apache.org/jira/browse/OPENJPA-1947
> >>                private String name;
> >>
> >>                @OneToMany(cascade = CascadeType.ALL, fetch =
> >> FetchType.EAGER)
> >>                private List<ProductOrderLine> products = new
> >> ArrayList<ProductOrderLine>();
> >>
> >>                public ProductOrder() {
> >>                }
> >>
> >>                public Long getId() {
> >>                               return id;
> >>                }
> >>
> >>                public void setProducts(List<ProductOrderLine> products)
> {
> >>                               this.products = products;
> >>                }
> >>
> >>                public List<ProductOrderLine> getProducts() {
> >>                               return products;
> >>                }
> >>
> >> }
> >>
> >> import java.util.ArrayList;
> >> import java.util.Arrays;
> >> import java.util.Collections;
> >> import java.util.Comparator;
> >> import java.util.List;
> >>
> >> import javax.persistence.EntityManager;
> >> import javax.persistence.EntityManagerFactory;
> >> import javax.persistence.Persistence;
> >>
> >> import junit.framework.TestCase;
> >> import entities.ProductOrder;
> >> import entities.ProductOrderLine;
> >>
> >> /**
> >> * I tested this with MSSQL SERVER (I tried hsqldb but this suffers from
> an
> >> * additional problem where it mixes up id 0 with id null, see
> >> * https://issues.apache.org/jira/browse/OPENJPA-1066)/
> >> *
> >>  * @author Henno Vermeulen
> >> */
> >> public class ProductOrderTest extends TestCase {
> >>
> >>                private EntityManagerFactory factory;
> >>
> >>                public void setUp() {
> >>                               factory =
> >> Persistence.createEntityManagerFactory("testPU", System
> >>
> >> .getProperties());
> >>                }
> >>
> >>                public void testProductOrderLineCopy() {
> >>                               // If we do this, it fails after the first
> >> save!!!!
> >>                               // hsqldb seems to convert null to 0 and
> >> confuse this with an existing
> >>                               // id or something
> >>                               // insertSomeData();
> >>
> >>                               ProductOrder original = new
> ProductOrder();
> >>                               ArrayList<ProductOrderLine> products = new
> >> ArrayList<ProductOrderLine>();
> >>                               products.addAll(Arrays.asList(new
> >> ProductOrderLine("orange"),
> >>                                                               new
> >> ProductOrderLine("apple")));
> >>                               original.setProducts(products);
> >>
> >>                               // START
> >>                               // null - orange
> >>                               // null - apple
> >>                               printProducts("Before first save:",
> >> original);
> >>                               ProductOrder firstSaved = save(original);
> >>                               // 1 - orange
> >>                               // 2 - apple
> >>                               printProducts("After first save:",
> >> firstSaved);
> >>
> >>                               ProductOrderLine orange =
> >> firstSaved.getProducts().get(0);
> >>                               ProductOrderLine apple =
> >> firstSaved.getProducts().get(1);
> >>                               assertEquals("orange", orange.getName());
> >>                               assertEquals("apple", apple.getName());
> >>                               Long orangeId = orange.getId();
> >>                               Long appleId = apple.getId();
> >>                               assertTrue(orangeId != null && appleId !=
> >> null);
> >>
> >>                               ProductOrderLine banana = new
> >> ProductOrderLine("banana");
> >>                               ProductOrderLine pear = new
> >> ProductOrderLine("pear");
> >>                               assertTrue(banana.getId() == null &&
> >> pear.getId() == null);
> >>
> >>                               firstSaved.getProducts().addAll(0,
> >> Arrays.asList(banana, pear));
> >>                               // Changed to
> >>                               // null - banana
> >>                               // null - pear
> >>                               // 1 - orange
> >>                               // 2 - apple
> >>                               printProducts("Before second save:",
> >> firstSaved);
> >>                               assertExpectedProductLines(firstSaved,
> >> orangeId, appleId, true);
> >>
> >>                               ProductOrder secondSaved =
> save(firstSaved);
> >>                               // Expected after save:
> >>                               // 3 - banana
> >>                               // 4 - pear
> >>                               // 1 - orange
> >>                               // 2 - apple
> >>
> >>                               // On SQL server this is actually returned
> >> by save, but not by find!
> >>                               printProducts("After second save:",
> >> secondSaved);
> >>                               assertExpectedProductLines(secondSaved,
> >> orangeId, appleId, false);
> >>
> >>                               ProductOrder found =
> >> findById(secondSaved.getId());
> >>                               printProducts("Found by id after second
> >> save:", found);
> >>                               // This one fails!!!!
> >>                               // On SQL server this returns
> >>                               // 3 - banana
> >>                               // 4 - pear
> >>                               // 4 - pear
> >>                               // 4 - pear
> >>                               assertExpectedProductLines(found,
> orangeId,
> >> appleId, false);
> >>                }
> >>
> >>                private void printProducts(String description,
> ProductOrder
> >> order) {
> >>                               System.out.println(description);
> >>                               for (ProductOrderLine p :
> >> order.getProducts()) {
> >>
> System.out.println(p.getId()
> >> + " - " + p.getName());
> >>                               }
> >>                }
> >>
> >>                private void assertExpectedProductLines(ProductOrder
> line,
> >> Long orangeId,
> >>                                               Long appleId, boolean
> >> beforeMerge) {
> >>                               List<ProductOrderLine> products = new
> >> ArrayList<ProductOrderLine>();
> >>                               products.addAll(line.getProducts());
> >>                               Collections.sort(products, new
> >> Comparator<ProductOrderLine>() {
> >>                                               @Override
> >>                                               public int
> >> compare(ProductOrderLine o1, ProductOrderLine o2) {
> >>                                                               return
> >> o1.getName().compareTo(o2.getName());
> >>                                               }
> >>                               });
> >>                               assertEquals("apple",
> >> products.get(0).getName());
> >>                               assertEquals(appleId,
> >> products.get(0).getId());
> >>                               assertEquals("banana",
> >> products.get(1).getName());
> >>                               assertEquals(beforeMerge, null ==
> >> products.get(1).getId());
> >>                               assertEquals("orange",
> >> products.get(2).getName());
> >>                               assertEquals(orangeId,
> >> products.get(2).getId());
> >>                               assertEquals("pear",
> >> products.get(3).getName());
> >>                               assertEquals(beforeMerge, null ==
> >> products.get(3).getId());
> >>                }
> >>
> >>                private ProductOrder save(ProductOrder order) {
> >>                               EntityManager em =
> >> factory.createEntityManager();
> >>                               em.getTransaction().begin();
> >>
> >>                               ProductOrder result;
> >>                               if (order.getId() == null) {
> >>                                               em.persist(order);
> >>                                               result = order;
> >>                               } else {
> >>                                               result = em.merge(order);
> >>                               }
> >>
> >>                               em.getTransaction().commit();
> >>                               em.detach(result);
> >>                               em.close();
> >>                               return result;
> >>                }
> >>
> >>                private ProductOrder findById(Long id) {
> >>                               EntityManager em =
> >> factory.createEntityManager();
> >>                               em.getTransaction().begin();
> >>                               ProductOrder result =
> >> em.find(ProductOrder.class, id);
> >>                               em.getTransaction().commit();
> >>                               em.detach(result);
> >>                               em.close();
> >>                               return result;
> >>                }
> >>
> >> }
> >>
> >> <persistence xmlns="http://java.sun.com/xml/ns/persistence"
> >>                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
> >>                xsi:schemaLocation="
> http://java.sun.com/xml/ns/persistencepersistence_1_0.xsd"
> >>                version="1.0">
> >>
> >>                <persistence-unit name="testPU">
> >>
> >> <provider>org.apache.openjpa.persistence.PersistenceProviderImpl
> >>                               </provider>
> >>                               <class>entities.ProductOrder</class>
> >>                               <class>entities.ProductOrderLine</class>
> >>
> >> <exclude-unlisted-classes>true</exclude-unlisted-classes>
> >>                               <properties>
> >>                                               <property
> >> name="openjpa.jdbc.SynchronizeMappings" value="buildSchema" />
> >>                                               <property
> >> name="openjpa.ConnectionDriverName"
> >>
> >> value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
> >>                                               <property
> >> name="openjpa.ConnectionURL"
> >>
> >>
> value="jdbc:sqlserver://localhost\\SQL2008:1433;databaseName=obliprototypeunittest"
> >> />
> >>                                               <property
> >> name="openjpa.ConnectionUserName" value="obliprototype" />
> >>                                               <property
> >> name="openjpa.ConnectionPassword" value="hidden" />
> >>                                               <property
> name="openjpa.Log"
> >> value="DefaultLevel=INFO" />
> >>                               </properties>
> >>                </persistence-unit>
> >> </persistence>
> >>
> >>
> >
>
>

RE: adding new entities to an existing List fails when using GenerationType.IDENTITY

Posted by Henno Vermeulen <he...@huizemolenaar.nl>.
I created https://issues.apache.org/jira/browse/OPENJPA-1949

I attached my testcase, however it does not yet extend from SingleEMFTestCase because it feels a bit daunting to get started with that (and I have other work todo).

Regards,
Henno

-----Oorspronkelijk bericht-----
Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
Verzonden: dinsdag 22 februari 2011 9:32
Aan: 'users@openjpa.apache.org'
Onderwerp: RE: adding new entities to an existing List fails when using GenerationType.IDENTITY

Alright I will open a JIRA issue fort his.

In my test I use the javaagent, in my app I use compile-time enhancement.

Thank you for the suggestion. However in my original problem I have a list of product order lines with @OrderColumn (transparent to the application) and orhpanRemoval = true. A UI shows the ordered list of product lines and I am making copy/paste so the user can copy them (either to the same or new product order) or reorder them so the order is really important. (On a copy I make a new order line with id and version set to null).

Regards,
Henno

-----Oorspronkelijk bericht-----
Van: Rick Curtis [mailto:curtisr7@gmail.com]
Verzonden: maandag 21 februari 2011 23:22
Aan: users@openjpa.apache.org
Onderwerp: Re: adding new entities to an existing List fails when using GenerationType.IDENTITY

Henno --

Try changing :

firstSaved.getProducts().addAll(0, Arrays.asList(banana, pear));
to
firstSaved.getProducts().addAll(Arrays.asList(banana, pear));

In my tests it seemed to make the problem go away... we're somehow getting
confused when writing the join table on the second update. Please go ahead
and open a JIRA for this issue.

Thanks,
Rick

On Mon, Feb 21, 2011 at 1:47 PM, Rick Curtis <cu...@gmail.com> wrote:

> How are you enhancing your Entities?
>
> Thanks,
> Rick
>
>
> On Mon, Feb 21, 2011 at 11:22 AM, Henno Vermeulen <he...@huizemolenaar.nl>wrote:
>
>> Although this workaround works in my toy example, it does not seem to work
>> in our application, switching to GenerationType.TABLE does.
>>
>> Henno
>>
>> -----Oorspronkelijk bericht-----
>> Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
>> Verzonden: maandag 21 februari 2011 17:50
>> Aan: 'users@openjpa.apache.org'
>> Onderwerp: RE: adding new entities to an existing List fails when using
>> GenerationType.IDENTITY
>>
>> I found a workaround:
>>
>> In my test I directly added the new ProductOrderLines to the List in the
>> ProductOrder. I do not know the implementation of this List because it is
>> the one returned by OpenJPA (at least after detaching).
>> When I set the list reference to a new arraylist and add both the existing
>> elements and the new ones to this list, the test passes.
>> I.e. change the line
>>
>> firstSaved.getProducts().addAll(0, Arrays.asList(banana, pear));
>>
>> to
>>
>>                List<ProductOrderLine> newProducts = new
>> ArrayList<ProductOrderLine>();
>>                firstSaved.getProducts().addAll(Arrays.asList(banana,
>> pear));
>>                newProducts.addAll(firstSaved.getProducts());
>>                firstSaved.setProducts(newProducts);
>>
>> Makes the test work.
>>
>> Regards, Henno
>>
>>
>> -----Oorspronkelijk bericht-----
>> Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
>> Verzonden: maandag 21 februari 2011 17:19
>> Aan: 'users@openjpa.apache.org'
>> Onderwerp: adding new entities to an existing List fails when using
>> GenerationType.IDENTITY
>>
>> Hello,
>>
>> I think I found a serious issue (in both OpenJPA 2.0.0 and 2.1.0) when I
>> try to add new entities to an existing list and I use a generated identity
>> with GenerationType.IDENTITY.
>>
>> I start with a fresh database and let OpenJPA create the schema. I have a
>> ProductOrder entity that contains a List of ProductOrderLines, annotated as
>> such:
>>
>> @Entity
>> public class ProductOrder {
>> ...
>>            @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
>>            private List<ProductOrderLine> products = new
>> ArrayList<ProductOrderLine>();
>> ...
>> }
>>
>> The entity in the List (ProductOrderLine) has a generated id with
>> GenerationType.IDENTITY.
>>
>> @Entity
>> public class ProductOrderLine {
>>
>>            @Id
>>            @GeneratedValue(strategy = GenerationType.IDENTITY)
>>            private Long id;
>> ...
>> }
>>
>> I start with a ProductOrder that has these products:
>> 1 - orange
>> 2 - apple
>>
>> I insert two new products into the list so that I get:
>> null - banana
>> null - pear
>> 1 - orange
>> 2 - apple
>>
>> Then I merge the entity (I work with attach/detach, not sure if this
>> matters).
>> OpenJPA merge correctly returns a ProductOrder with this list:
>>
>> 3 - banana
>> 4 - pear
>> 1 - orange
>> 2 - apple
>>
>> However OpenJPA generates the wrong SQL so that the database contains
>> something completely different and indeed selecting the ProductOrder by it's
>> id gives:
>>
>> 3 - banana
>> 4 - pear
>> 4 - pear
>> 4 - pear
>>
>> I tested this with sql server. (I tried hsqldb but this also suffers from
>> bug https://issues.apache.org/jira/browse/OPENJPA-1066).
>> This problem does not occur when I use GenerationType.AUTO for
>> ProductOrderLine. My example uses a join table, but foreign key columns seem
>> to have the same problem. Should I use another generation type? If so which
>> one, and is it compatible with existing sql server data that had id values
>> automatically generated by sql server?
>>
>> Please let me know if I should file a bug report and if I should attempt
>> to convert my unit test to one accepted by OpenJPA standards.
>>
>> Regards,
>> Henno Vermeulen
>>
>>
>> package entities;
>>
>> import javax.persistence.Entity;
>> import javax.persistence.GeneratedValue;
>> import javax.persistence.GenerationType;
>> import javax.persistence.Id;
>>
>> @Entity
>> public class ProductOrderLine {
>>
>>                @Id
>>                @GeneratedValue(strategy = GenerationType.IDENTITY)
>>                private Long id;
>>
>>                private String name;
>>
>>                public ProductOrderLine() {
>>                }
>>
>>                public ProductOrderLine(String name) {
>>                               this.name = name;
>>                }
>>
>>                public String getName() {
>>                               return name;
>>                }
>>
>>                public void setName(String name) {
>>                               this.name = name;
>>                }
>>
>>                public Long getId() {
>>                               return id;
>>                }
>>
>> }
>>
>>
>> package entities;
>>
>> import java.util.ArrayList;
>> import java.util.List;
>>
>> import javax.persistence.CascadeType;
>> import javax.persistence.Entity;
>> import javax.persistence.FetchType;
>> import javax.persistence.GeneratedValue;
>> import javax.persistence.GenerationType;
>> import javax.persistence.Id;
>> import javax.persistence.OneToMany;
>>
>> @Entity
>> public class ProductOrder {
>>
>>                @Id
>>                @GeneratedValue(strategy = GenerationType.IDENTITY)
>>                private Long id;
>>
>>                // Workaround for
>> https://issues.apache.org/jira/browse/OPENJPA-1947
>>                private String name;
>>
>>                @OneToMany(cascade = CascadeType.ALL, fetch =
>> FetchType.EAGER)
>>                private List<ProductOrderLine> products = new
>> ArrayList<ProductOrderLine>();
>>
>>                public ProductOrder() {
>>                }
>>
>>                public Long getId() {
>>                               return id;
>>                }
>>
>>                public void setProducts(List<ProductOrderLine> products) {
>>                               this.products = products;
>>                }
>>
>>                public List<ProductOrderLine> getProducts() {
>>                               return products;
>>                }
>>
>> }
>>
>> import java.util.ArrayList;
>> import java.util.Arrays;
>> import java.util.Collections;
>> import java.util.Comparator;
>> import java.util.List;
>>
>> import javax.persistence.EntityManager;
>> import javax.persistence.EntityManagerFactory;
>> import javax.persistence.Persistence;
>>
>> import junit.framework.TestCase;
>> import entities.ProductOrder;
>> import entities.ProductOrderLine;
>>
>> /**
>> * I tested this with MSSQL SERVER (I tried hsqldb but this suffers from an
>> * additional problem where it mixes up id 0 with id null, see
>> * https://issues.apache.org/jira/browse/OPENJPA-1066)/
>> *
>>  * @author Henno Vermeulen
>> */
>> public class ProductOrderTest extends TestCase {
>>
>>                private EntityManagerFactory factory;
>>
>>                public void setUp() {
>>                               factory =
>> Persistence.createEntityManagerFactory("testPU", System
>>
>> .getProperties());
>>                }
>>
>>                public void testProductOrderLineCopy() {
>>                               // If we do this, it fails after the first
>> save!!!!
>>                               // hsqldb seems to convert null to 0 and
>> confuse this with an existing
>>                               // id or something
>>                               // insertSomeData();
>>
>>                               ProductOrder original = new ProductOrder();
>>                               ArrayList<ProductOrderLine> products = new
>> ArrayList<ProductOrderLine>();
>>                               products.addAll(Arrays.asList(new
>> ProductOrderLine("orange"),
>>                                                               new
>> ProductOrderLine("apple")));
>>                               original.setProducts(products);
>>
>>                               // START
>>                               // null - orange
>>                               // null - apple
>>                               printProducts("Before first save:",
>> original);
>>                               ProductOrder firstSaved = save(original);
>>                               // 1 - orange
>>                               // 2 - apple
>>                               printProducts("After first save:",
>> firstSaved);
>>
>>                               ProductOrderLine orange =
>> firstSaved.getProducts().get(0);
>>                               ProductOrderLine apple =
>> firstSaved.getProducts().get(1);
>>                               assertEquals("orange", orange.getName());
>>                               assertEquals("apple", apple.getName());
>>                               Long orangeId = orange.getId();
>>                               Long appleId = apple.getId();
>>                               assertTrue(orangeId != null && appleId !=
>> null);
>>
>>                               ProductOrderLine banana = new
>> ProductOrderLine("banana");
>>                               ProductOrderLine pear = new
>> ProductOrderLine("pear");
>>                               assertTrue(banana.getId() == null &&
>> pear.getId() == null);
>>
>>                               firstSaved.getProducts().addAll(0,
>> Arrays.asList(banana, pear));
>>                               // Changed to
>>                               // null - banana
>>                               // null - pear
>>                               // 1 - orange
>>                               // 2 - apple
>>                               printProducts("Before second save:",
>> firstSaved);
>>                               assertExpectedProductLines(firstSaved,
>> orangeId, appleId, true);
>>
>>                               ProductOrder secondSaved = save(firstSaved);
>>                               // Expected after save:
>>                               // 3 - banana
>>                               // 4 - pear
>>                               // 1 - orange
>>                               // 2 - apple
>>
>>                               // On SQL server this is actually returned
>> by save, but not by find!
>>                               printProducts("After second save:",
>> secondSaved);
>>                               assertExpectedProductLines(secondSaved,
>> orangeId, appleId, false);
>>
>>                               ProductOrder found =
>> findById(secondSaved.getId());
>>                               printProducts("Found by id after second
>> save:", found);
>>                               // This one fails!!!!
>>                               // On SQL server this returns
>>                               // 3 - banana
>>                               // 4 - pear
>>                               // 4 - pear
>>                               // 4 - pear
>>                               assertExpectedProductLines(found, orangeId,
>> appleId, false);
>>                }
>>
>>                private void printProducts(String description, ProductOrder
>> order) {
>>                               System.out.println(description);
>>                               for (ProductOrderLine p :
>> order.getProducts()) {
>>                                               System.out.println(p.getId()
>> + " - " + p.getName());
>>                               }
>>                }
>>
>>                private void assertExpectedProductLines(ProductOrder line,
>> Long orangeId,
>>                                               Long appleId, boolean
>> beforeMerge) {
>>                               List<ProductOrderLine> products = new
>> ArrayList<ProductOrderLine>();
>>                               products.addAll(line.getProducts());
>>                               Collections.sort(products, new
>> Comparator<ProductOrderLine>() {
>>                                               @Override
>>                                               public int
>> compare(ProductOrderLine o1, ProductOrderLine o2) {
>>                                                               return
>> o1.getName().compareTo(o2.getName());
>>                                               }
>>                               });
>>                               assertEquals("apple",
>> products.get(0).getName());
>>                               assertEquals(appleId,
>> products.get(0).getId());
>>                               assertEquals("banana",
>> products.get(1).getName());
>>                               assertEquals(beforeMerge, null ==
>> products.get(1).getId());
>>                               assertEquals("orange",
>> products.get(2).getName());
>>                               assertEquals(orangeId,
>> products.get(2).getId());
>>                               assertEquals("pear",
>> products.get(3).getName());
>>                               assertEquals(beforeMerge, null ==
>> products.get(3).getId());
>>                }
>>
>>                private ProductOrder save(ProductOrder order) {
>>                               EntityManager em =
>> factory.createEntityManager();
>>                               em.getTransaction().begin();
>>
>>                               ProductOrder result;
>>                               if (order.getId() == null) {
>>                                               em.persist(order);
>>                                               result = order;
>>                               } else {
>>                                               result = em.merge(order);
>>                               }
>>
>>                               em.getTransaction().commit();
>>                               em.detach(result);
>>                               em.close();
>>                               return result;
>>                }
>>
>>                private ProductOrder findById(Long id) {
>>                               EntityManager em =
>> factory.createEntityManager();
>>                               em.getTransaction().begin();
>>                               ProductOrder result =
>> em.find(ProductOrder.class, id);
>>                               em.getTransaction().commit();
>>                               em.detach(result);
>>                               em.close();
>>                               return result;
>>                }
>>
>> }
>>
>> <persistence xmlns="http://java.sun.com/xml/ns/persistence"
>>                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>>                xsi:schemaLocation="http://java.sun.com/xml/ns/persistencepersistence_1_0.xsd"
>>                version="1.0">
>>
>>                <persistence-unit name="testPU">
>>
>> <provider>org.apache.openjpa.persistence.PersistenceProviderImpl
>>                               </provider>
>>                               <class>entities.ProductOrder</class>
>>                               <class>entities.ProductOrderLine</class>
>>
>> <exclude-unlisted-classes>true</exclude-unlisted-classes>
>>                               <properties>
>>                                               <property
>> name="openjpa.jdbc.SynchronizeMappings" value="buildSchema" />
>>                                               <property
>> name="openjpa.ConnectionDriverName"
>>
>> value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
>>                                               <property
>> name="openjpa.ConnectionURL"
>>
>> value="jdbc:sqlserver://localhost\\SQL2008:1433;databaseName=obliprototypeunittest"
>> />
>>                                               <property
>> name="openjpa.ConnectionUserName" value="obliprototype" />
>>                                               <property
>> name="openjpa.ConnectionPassword" value="hidden" />
>>                                               <property name="openjpa.Log"
>> value="DefaultLevel=INFO" />
>>                               </properties>
>>                </persistence-unit>
>> </persistence>
>>
>>
>


RE: adding new entities to an existing List fails when using GenerationType.IDENTITY

Posted by Henno Vermeulen <he...@huizemolenaar.nl>.
Alright I will open a JIRA issue fort his.

In my test I use the javaagent, in my app I use compile-time enhancement.

Thank you for the suggestion. However in my original problem I have a list of product order lines with @OrderColumn (transparent to the application) and orhpanRemoval = true. A UI shows the ordered list of product lines and I am making copy/paste so the user can copy them (either to the same or new product order) or reorder them so the order is really important. (On a copy I make a new order line with id and version set to null).

Regards,
Henno

-----Oorspronkelijk bericht-----
Van: Rick Curtis [mailto:curtisr7@gmail.com]
Verzonden: maandag 21 februari 2011 23:22
Aan: users@openjpa.apache.org
Onderwerp: Re: adding new entities to an existing List fails when using GenerationType.IDENTITY

Henno --

Try changing :

firstSaved.getProducts().addAll(0, Arrays.asList(banana, pear));
to
firstSaved.getProducts().addAll(Arrays.asList(banana, pear));

In my tests it seemed to make the problem go away... we're somehow getting
confused when writing the join table on the second update. Please go ahead
and open a JIRA for this issue.

Thanks,
Rick

On Mon, Feb 21, 2011 at 1:47 PM, Rick Curtis <cu...@gmail.com> wrote:

> How are you enhancing your Entities?
>
> Thanks,
> Rick
>
>
> On Mon, Feb 21, 2011 at 11:22 AM, Henno Vermeulen <he...@huizemolenaar.nl>wrote:
>
>> Although this workaround works in my toy example, it does not seem to work
>> in our application, switching to GenerationType.TABLE does.
>>
>> Henno
>>
>> -----Oorspronkelijk bericht-----
>> Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
>> Verzonden: maandag 21 februari 2011 17:50
>> Aan: 'users@openjpa.apache.org'
>> Onderwerp: RE: adding new entities to an existing List fails when using
>> GenerationType.IDENTITY
>>
>> I found a workaround:
>>
>> In my test I directly added the new ProductOrderLines to the List in the
>> ProductOrder. I do not know the implementation of this List because it is
>> the one returned by OpenJPA (at least after detaching).
>> When I set the list reference to a new arraylist and add both the existing
>> elements and the new ones to this list, the test passes.
>> I.e. change the line
>>
>> firstSaved.getProducts().addAll(0, Arrays.asList(banana, pear));
>>
>> to
>>
>>                List<ProductOrderLine> newProducts = new
>> ArrayList<ProductOrderLine>();
>>                firstSaved.getProducts().addAll(Arrays.asList(banana,
>> pear));
>>                newProducts.addAll(firstSaved.getProducts());
>>                firstSaved.setProducts(newProducts);
>>
>> Makes the test work.
>>
>> Regards, Henno
>>
>>
>> -----Oorspronkelijk bericht-----
>> Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
>> Verzonden: maandag 21 februari 2011 17:19
>> Aan: 'users@openjpa.apache.org'
>> Onderwerp: adding new entities to an existing List fails when using
>> GenerationType.IDENTITY
>>
>> Hello,
>>
>> I think I found a serious issue (in both OpenJPA 2.0.0 and 2.1.0) when I
>> try to add new entities to an existing list and I use a generated identity
>> with GenerationType.IDENTITY.
>>
>> I start with a fresh database and let OpenJPA create the schema. I have a
>> ProductOrder entity that contains a List of ProductOrderLines, annotated as
>> such:
>>
>> @Entity
>> public class ProductOrder {
>> ...
>>            @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
>>            private List<ProductOrderLine> products = new
>> ArrayList<ProductOrderLine>();
>> ...
>> }
>>
>> The entity in the List (ProductOrderLine) has a generated id with
>> GenerationType.IDENTITY.
>>
>> @Entity
>> public class ProductOrderLine {
>>
>>            @Id
>>            @GeneratedValue(strategy = GenerationType.IDENTITY)
>>            private Long id;
>> ...
>> }
>>
>> I start with a ProductOrder that has these products:
>> 1 - orange
>> 2 - apple
>>
>> I insert two new products into the list so that I get:
>> null - banana
>> null - pear
>> 1 - orange
>> 2 - apple
>>
>> Then I merge the entity (I work with attach/detach, not sure if this
>> matters).
>> OpenJPA merge correctly returns a ProductOrder with this list:
>>
>> 3 - banana
>> 4 - pear
>> 1 - orange
>> 2 - apple
>>
>> However OpenJPA generates the wrong SQL so that the database contains
>> something completely different and indeed selecting the ProductOrder by it's
>> id gives:
>>
>> 3 - banana
>> 4 - pear
>> 4 - pear
>> 4 - pear
>>
>> I tested this with sql server. (I tried hsqldb but this also suffers from
>> bug https://issues.apache.org/jira/browse/OPENJPA-1066).
>> This problem does not occur when I use GenerationType.AUTO for
>> ProductOrderLine. My example uses a join table, but foreign key columns seem
>> to have the same problem. Should I use another generation type? If so which
>> one, and is it compatible with existing sql server data that had id values
>> automatically generated by sql server?
>>
>> Please let me know if I should file a bug report and if I should attempt
>> to convert my unit test to one accepted by OpenJPA standards.
>>
>> Regards,
>> Henno Vermeulen
>>
>>
>> package entities;
>>
>> import javax.persistence.Entity;
>> import javax.persistence.GeneratedValue;
>> import javax.persistence.GenerationType;
>> import javax.persistence.Id;
>>
>> @Entity
>> public class ProductOrderLine {
>>
>>                @Id
>>                @GeneratedValue(strategy = GenerationType.IDENTITY)
>>                private Long id;
>>
>>                private String name;
>>
>>                public ProductOrderLine() {
>>                }
>>
>>                public ProductOrderLine(String name) {
>>                               this.name = name;
>>                }
>>
>>                public String getName() {
>>                               return name;
>>                }
>>
>>                public void setName(String name) {
>>                               this.name = name;
>>                }
>>
>>                public Long getId() {
>>                               return id;
>>                }
>>
>> }
>>
>>
>> package entities;
>>
>> import java.util.ArrayList;
>> import java.util.List;
>>
>> import javax.persistence.CascadeType;
>> import javax.persistence.Entity;
>> import javax.persistence.FetchType;
>> import javax.persistence.GeneratedValue;
>> import javax.persistence.GenerationType;
>> import javax.persistence.Id;
>> import javax.persistence.OneToMany;
>>
>> @Entity
>> public class ProductOrder {
>>
>>                @Id
>>                @GeneratedValue(strategy = GenerationType.IDENTITY)
>>                private Long id;
>>
>>                // Workaround for
>> https://issues.apache.org/jira/browse/OPENJPA-1947
>>                private String name;
>>
>>                @OneToMany(cascade = CascadeType.ALL, fetch =
>> FetchType.EAGER)
>>                private List<ProductOrderLine> products = new
>> ArrayList<ProductOrderLine>();
>>
>>                public ProductOrder() {
>>                }
>>
>>                public Long getId() {
>>                               return id;
>>                }
>>
>>                public void setProducts(List<ProductOrderLine> products) {
>>                               this.products = products;
>>                }
>>
>>                public List<ProductOrderLine> getProducts() {
>>                               return products;
>>                }
>>
>> }
>>
>> import java.util.ArrayList;
>> import java.util.Arrays;
>> import java.util.Collections;
>> import java.util.Comparator;
>> import java.util.List;
>>
>> import javax.persistence.EntityManager;
>> import javax.persistence.EntityManagerFactory;
>> import javax.persistence.Persistence;
>>
>> import junit.framework.TestCase;
>> import entities.ProductOrder;
>> import entities.ProductOrderLine;
>>
>> /**
>> * I tested this with MSSQL SERVER (I tried hsqldb but this suffers from an
>> * additional problem where it mixes up id 0 with id null, see
>> * https://issues.apache.org/jira/browse/OPENJPA-1066)/
>> *
>>  * @author Henno Vermeulen
>> */
>> public class ProductOrderTest extends TestCase {
>>
>>                private EntityManagerFactory factory;
>>
>>                public void setUp() {
>>                               factory =
>> Persistence.createEntityManagerFactory("testPU", System
>>
>> .getProperties());
>>                }
>>
>>                public void testProductOrderLineCopy() {
>>                               // If we do this, it fails after the first
>> save!!!!
>>                               // hsqldb seems to convert null to 0 and
>> confuse this with an existing
>>                               // id or something
>>                               // insertSomeData();
>>
>>                               ProductOrder original = new ProductOrder();
>>                               ArrayList<ProductOrderLine> products = new
>> ArrayList<ProductOrderLine>();
>>                               products.addAll(Arrays.asList(new
>> ProductOrderLine("orange"),
>>                                                               new
>> ProductOrderLine("apple")));
>>                               original.setProducts(products);
>>
>>                               // START
>>                               // null - orange
>>                               // null - apple
>>                               printProducts("Before first save:",
>> original);
>>                               ProductOrder firstSaved = save(original);
>>                               // 1 - orange
>>                               // 2 - apple
>>                               printProducts("After first save:",
>> firstSaved);
>>
>>                               ProductOrderLine orange =
>> firstSaved.getProducts().get(0);
>>                               ProductOrderLine apple =
>> firstSaved.getProducts().get(1);
>>                               assertEquals("orange", orange.getName());
>>                               assertEquals("apple", apple.getName());
>>                               Long orangeId = orange.getId();
>>                               Long appleId = apple.getId();
>>                               assertTrue(orangeId != null && appleId !=
>> null);
>>
>>                               ProductOrderLine banana = new
>> ProductOrderLine("banana");
>>                               ProductOrderLine pear = new
>> ProductOrderLine("pear");
>>                               assertTrue(banana.getId() == null &&
>> pear.getId() == null);
>>
>>                               firstSaved.getProducts().addAll(0,
>> Arrays.asList(banana, pear));
>>                               // Changed to
>>                               // null - banana
>>                               // null - pear
>>                               // 1 - orange
>>                               // 2 - apple
>>                               printProducts("Before second save:",
>> firstSaved);
>>                               assertExpectedProductLines(firstSaved,
>> orangeId, appleId, true);
>>
>>                               ProductOrder secondSaved = save(firstSaved);
>>                               // Expected after save:
>>                               // 3 - banana
>>                               // 4 - pear
>>                               // 1 - orange
>>                               // 2 - apple
>>
>>                               // On SQL server this is actually returned
>> by save, but not by find!
>>                               printProducts("After second save:",
>> secondSaved);
>>                               assertExpectedProductLines(secondSaved,
>> orangeId, appleId, false);
>>
>>                               ProductOrder found =
>> findById(secondSaved.getId());
>>                               printProducts("Found by id after second
>> save:", found);
>>                               // This one fails!!!!
>>                               // On SQL server this returns
>>                               // 3 - banana
>>                               // 4 - pear
>>                               // 4 - pear
>>                               // 4 - pear
>>                               assertExpectedProductLines(found, orangeId,
>> appleId, false);
>>                }
>>
>>                private void printProducts(String description, ProductOrder
>> order) {
>>                               System.out.println(description);
>>                               for (ProductOrderLine p :
>> order.getProducts()) {
>>                                               System.out.println(p.getId()
>> + " - " + p.getName());
>>                               }
>>                }
>>
>>                private void assertExpectedProductLines(ProductOrder line,
>> Long orangeId,
>>                                               Long appleId, boolean
>> beforeMerge) {
>>                               List<ProductOrderLine> products = new
>> ArrayList<ProductOrderLine>();
>>                               products.addAll(line.getProducts());
>>                               Collections.sort(products, new
>> Comparator<ProductOrderLine>() {
>>                                               @Override
>>                                               public int
>> compare(ProductOrderLine o1, ProductOrderLine o2) {
>>                                                               return
>> o1.getName().compareTo(o2.getName());
>>                                               }
>>                               });
>>                               assertEquals("apple",
>> products.get(0).getName());
>>                               assertEquals(appleId,
>> products.get(0).getId());
>>                               assertEquals("banana",
>> products.get(1).getName());
>>                               assertEquals(beforeMerge, null ==
>> products.get(1).getId());
>>                               assertEquals("orange",
>> products.get(2).getName());
>>                               assertEquals(orangeId,
>> products.get(2).getId());
>>                               assertEquals("pear",
>> products.get(3).getName());
>>                               assertEquals(beforeMerge, null ==
>> products.get(3).getId());
>>                }
>>
>>                private ProductOrder save(ProductOrder order) {
>>                               EntityManager em =
>> factory.createEntityManager();
>>                               em.getTransaction().begin();
>>
>>                               ProductOrder result;
>>                               if (order.getId() == null) {
>>                                               em.persist(order);
>>                                               result = order;
>>                               } else {
>>                                               result = em.merge(order);
>>                               }
>>
>>                               em.getTransaction().commit();
>>                               em.detach(result);
>>                               em.close();
>>                               return result;
>>                }
>>
>>                private ProductOrder findById(Long id) {
>>                               EntityManager em =
>> factory.createEntityManager();
>>                               em.getTransaction().begin();
>>                               ProductOrder result =
>> em.find(ProductOrder.class, id);
>>                               em.getTransaction().commit();
>>                               em.detach(result);
>>                               em.close();
>>                               return result;
>>                }
>>
>> }
>>
>> <persistence xmlns="http://java.sun.com/xml/ns/persistence"
>>                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>>                xsi:schemaLocation="http://java.sun.com/xml/ns/persistencepersistence_1_0.xsd"
>>                version="1.0">
>>
>>                <persistence-unit name="testPU">
>>
>> <provider>org.apache.openjpa.persistence.PersistenceProviderImpl
>>                               </provider>
>>                               <class>entities.ProductOrder</class>
>>                               <class>entities.ProductOrderLine</class>
>>
>> <exclude-unlisted-classes>true</exclude-unlisted-classes>
>>                               <properties>
>>                                               <property
>> name="openjpa.jdbc.SynchronizeMappings" value="buildSchema" />
>>                                               <property
>> name="openjpa.ConnectionDriverName"
>>
>> value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
>>                                               <property
>> name="openjpa.ConnectionURL"
>>
>> value="jdbc:sqlserver://localhost\\SQL2008:1433;databaseName=obliprototypeunittest"
>> />
>>                                               <property
>> name="openjpa.ConnectionUserName" value="obliprototype" />
>>                                               <property
>> name="openjpa.ConnectionPassword" value="hidden" />
>>                                               <property name="openjpa.Log"
>> value="DefaultLevel=INFO" />
>>                               </properties>
>>                </persistence-unit>
>> </persistence>
>>
>>
>

Re: adding new entities to an existing List fails when using GenerationType.IDENTITY

Posted by Rick Curtis <cu...@gmail.com>.
Henno --

Try changing :

firstSaved.getProducts().addAll(0, Arrays.asList(banana, pear));
to
firstSaved.getProducts().addAll(Arrays.asList(banana, pear));

In my tests it seemed to make the problem go away... we're somehow getting
confused when writing the join table on the second update. Please go ahead
and open a JIRA for this issue.

Thanks,
Rick

On Mon, Feb 21, 2011 at 1:47 PM, Rick Curtis <cu...@gmail.com> wrote:

> How are you enhancing your Entities?
>
> Thanks,
> Rick
>
>
> On Mon, Feb 21, 2011 at 11:22 AM, Henno Vermeulen <he...@huizemolenaar.nl>wrote:
>
>> Although this workaround works in my toy example, it does not seem to work
>> in our application, switching to GenerationType.TABLE does.
>>
>> Henno
>>
>> -----Oorspronkelijk bericht-----
>> Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
>> Verzonden: maandag 21 februari 2011 17:50
>> Aan: 'users@openjpa.apache.org'
>> Onderwerp: RE: adding new entities to an existing List fails when using
>> GenerationType.IDENTITY
>>
>> I found a workaround:
>>
>> In my test I directly added the new ProductOrderLines to the List in the
>> ProductOrder. I do not know the implementation of this List because it is
>> the one returned by OpenJPA (at least after detaching).
>> When I set the list reference to a new arraylist and add both the existing
>> elements and the new ones to this list, the test passes.
>> I.e. change the line
>>
>> firstSaved.getProducts().addAll(0, Arrays.asList(banana, pear));
>>
>> to
>>
>>                List<ProductOrderLine> newProducts = new
>> ArrayList<ProductOrderLine>();
>>                firstSaved.getProducts().addAll(Arrays.asList(banana,
>> pear));
>>                newProducts.addAll(firstSaved.getProducts());
>>                firstSaved.setProducts(newProducts);
>>
>> Makes the test work.
>>
>> Regards, Henno
>>
>>
>> -----Oorspronkelijk bericht-----
>> Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
>> Verzonden: maandag 21 februari 2011 17:19
>> Aan: 'users@openjpa.apache.org'
>> Onderwerp: adding new entities to an existing List fails when using
>> GenerationType.IDENTITY
>>
>> Hello,
>>
>> I think I found a serious issue (in both OpenJPA 2.0.0 and 2.1.0) when I
>> try to add new entities to an existing list and I use a generated identity
>> with GenerationType.IDENTITY.
>>
>> I start with a fresh database and let OpenJPA create the schema. I have a
>> ProductOrder entity that contains a List of ProductOrderLines, annotated as
>> such:
>>
>> @Entity
>> public class ProductOrder {
>> ...
>>            @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
>>            private List<ProductOrderLine> products = new
>> ArrayList<ProductOrderLine>();
>> ...
>> }
>>
>> The entity in the List (ProductOrderLine) has a generated id with
>> GenerationType.IDENTITY.
>>
>> @Entity
>> public class ProductOrderLine {
>>
>>            @Id
>>            @GeneratedValue(strategy = GenerationType.IDENTITY)
>>            private Long id;
>> ...
>> }
>>
>> I start with a ProductOrder that has these products:
>> 1 - orange
>> 2 - apple
>>
>> I insert two new products into the list so that I get:
>> null - banana
>> null - pear
>> 1 - orange
>> 2 - apple
>>
>> Then I merge the entity (I work with attach/detach, not sure if this
>> matters).
>> OpenJPA merge correctly returns a ProductOrder with this list:
>>
>> 3 - banana
>> 4 - pear
>> 1 - orange
>> 2 - apple
>>
>> However OpenJPA generates the wrong SQL so that the database contains
>> something completely different and indeed selecting the ProductOrder by it's
>> id gives:
>>
>> 3 - banana
>> 4 - pear
>> 4 - pear
>> 4 - pear
>>
>> I tested this with sql server. (I tried hsqldb but this also suffers from
>> bug https://issues.apache.org/jira/browse/OPENJPA-1066).
>> This problem does not occur when I use GenerationType.AUTO for
>> ProductOrderLine. My example uses a join table, but foreign key columns seem
>> to have the same problem. Should I use another generation type? If so which
>> one, and is it compatible with existing sql server data that had id values
>> automatically generated by sql server?
>>
>> Please let me know if I should file a bug report and if I should attempt
>> to convert my unit test to one accepted by OpenJPA standards.
>>
>> Regards,
>> Henno Vermeulen
>>
>>
>> package entities;
>>
>> import javax.persistence.Entity;
>> import javax.persistence.GeneratedValue;
>> import javax.persistence.GenerationType;
>> import javax.persistence.Id;
>>
>> @Entity
>> public class ProductOrderLine {
>>
>>                @Id
>>                @GeneratedValue(strategy = GenerationType.IDENTITY)
>>                private Long id;
>>
>>                private String name;
>>
>>                public ProductOrderLine() {
>>                }
>>
>>                public ProductOrderLine(String name) {
>>                               this.name = name;
>>                }
>>
>>                public String getName() {
>>                               return name;
>>                }
>>
>>                public void setName(String name) {
>>                               this.name = name;
>>                }
>>
>>                public Long getId() {
>>                               return id;
>>                }
>>
>> }
>>
>>
>> package entities;
>>
>> import java.util.ArrayList;
>> import java.util.List;
>>
>> import javax.persistence.CascadeType;
>> import javax.persistence.Entity;
>> import javax.persistence.FetchType;
>> import javax.persistence.GeneratedValue;
>> import javax.persistence.GenerationType;
>> import javax.persistence.Id;
>> import javax.persistence.OneToMany;
>>
>> @Entity
>> public class ProductOrder {
>>
>>                @Id
>>                @GeneratedValue(strategy = GenerationType.IDENTITY)
>>                private Long id;
>>
>>                // Workaround for
>> https://issues.apache.org/jira/browse/OPENJPA-1947
>>                private String name;
>>
>>                @OneToMany(cascade = CascadeType.ALL, fetch =
>> FetchType.EAGER)
>>                private List<ProductOrderLine> products = new
>> ArrayList<ProductOrderLine>();
>>
>>                public ProductOrder() {
>>                }
>>
>>                public Long getId() {
>>                               return id;
>>                }
>>
>>                public void setProducts(List<ProductOrderLine> products) {
>>                               this.products = products;
>>                }
>>
>>                public List<ProductOrderLine> getProducts() {
>>                               return products;
>>                }
>>
>> }
>>
>> import java.util.ArrayList;
>> import java.util.Arrays;
>> import java.util.Collections;
>> import java.util.Comparator;
>> import java.util.List;
>>
>> import javax.persistence.EntityManager;
>> import javax.persistence.EntityManagerFactory;
>> import javax.persistence.Persistence;
>>
>> import junit.framework.TestCase;
>> import entities.ProductOrder;
>> import entities.ProductOrderLine;
>>
>> /**
>> * I tested this with MSSQL SERVER (I tried hsqldb but this suffers from an
>> * additional problem where it mixes up id 0 with id null, see
>> * https://issues.apache.org/jira/browse/OPENJPA-1066)/
>> *
>>  * @author Henno Vermeulen
>> */
>> public class ProductOrderTest extends TestCase {
>>
>>                private EntityManagerFactory factory;
>>
>>                public void setUp() {
>>                               factory =
>> Persistence.createEntityManagerFactory("testPU", System
>>
>> .getProperties());
>>                }
>>
>>                public void testProductOrderLineCopy() {
>>                               // If we do this, it fails after the first
>> save!!!!
>>                               // hsqldb seems to convert null to 0 and
>> confuse this with an existing
>>                               // id or something
>>                               // insertSomeData();
>>
>>                               ProductOrder original = new ProductOrder();
>>                               ArrayList<ProductOrderLine> products = new
>> ArrayList<ProductOrderLine>();
>>                               products.addAll(Arrays.asList(new
>> ProductOrderLine("orange"),
>>                                                               new
>> ProductOrderLine("apple")));
>>                               original.setProducts(products);
>>
>>                               // START
>>                               // null - orange
>>                               // null - apple
>>                               printProducts("Before first save:",
>> original);
>>                               ProductOrder firstSaved = save(original);
>>                               // 1 - orange
>>                               // 2 - apple
>>                               printProducts("After first save:",
>> firstSaved);
>>
>>                               ProductOrderLine orange =
>> firstSaved.getProducts().get(0);
>>                               ProductOrderLine apple =
>> firstSaved.getProducts().get(1);
>>                               assertEquals("orange", orange.getName());
>>                               assertEquals("apple", apple.getName());
>>                               Long orangeId = orange.getId();
>>                               Long appleId = apple.getId();
>>                               assertTrue(orangeId != null && appleId !=
>> null);
>>
>>                               ProductOrderLine banana = new
>> ProductOrderLine("banana");
>>                               ProductOrderLine pear = new
>> ProductOrderLine("pear");
>>                               assertTrue(banana.getId() == null &&
>> pear.getId() == null);
>>
>>                               firstSaved.getProducts().addAll(0,
>> Arrays.asList(banana, pear));
>>                               // Changed to
>>                               // null - banana
>>                               // null - pear
>>                               // 1 - orange
>>                               // 2 - apple
>>                               printProducts("Before second save:",
>> firstSaved);
>>                               assertExpectedProductLines(firstSaved,
>> orangeId, appleId, true);
>>
>>                               ProductOrder secondSaved = save(firstSaved);
>>                               // Expected after save:
>>                               // 3 - banana
>>                               // 4 - pear
>>                               // 1 - orange
>>                               // 2 - apple
>>
>>                               // On SQL server this is actually returned
>> by save, but not by find!
>>                               printProducts("After second save:",
>> secondSaved);
>>                               assertExpectedProductLines(secondSaved,
>> orangeId, appleId, false);
>>
>>                               ProductOrder found =
>> findById(secondSaved.getId());
>>                               printProducts("Found by id after second
>> save:", found);
>>                               // This one fails!!!!
>>                               // On SQL server this returns
>>                               // 3 - banana
>>                               // 4 - pear
>>                               // 4 - pear
>>                               // 4 - pear
>>                               assertExpectedProductLines(found, orangeId,
>> appleId, false);
>>                }
>>
>>                private void printProducts(String description, ProductOrder
>> order) {
>>                               System.out.println(description);
>>                               for (ProductOrderLine p :
>> order.getProducts()) {
>>                                               System.out.println(p.getId()
>> + " - " + p.getName());
>>                               }
>>                }
>>
>>                private void assertExpectedProductLines(ProductOrder line,
>> Long orangeId,
>>                                               Long appleId, boolean
>> beforeMerge) {
>>                               List<ProductOrderLine> products = new
>> ArrayList<ProductOrderLine>();
>>                               products.addAll(line.getProducts());
>>                               Collections.sort(products, new
>> Comparator<ProductOrderLine>() {
>>                                               @Override
>>                                               public int
>> compare(ProductOrderLine o1, ProductOrderLine o2) {
>>                                                               return
>> o1.getName().compareTo(o2.getName());
>>                                               }
>>                               });
>>                               assertEquals("apple",
>> products.get(0).getName());
>>                               assertEquals(appleId,
>> products.get(0).getId());
>>                               assertEquals("banana",
>> products.get(1).getName());
>>                               assertEquals(beforeMerge, null ==
>> products.get(1).getId());
>>                               assertEquals("orange",
>> products.get(2).getName());
>>                               assertEquals(orangeId,
>> products.get(2).getId());
>>                               assertEquals("pear",
>> products.get(3).getName());
>>                               assertEquals(beforeMerge, null ==
>> products.get(3).getId());
>>                }
>>
>>                private ProductOrder save(ProductOrder order) {
>>                               EntityManager em =
>> factory.createEntityManager();
>>                               em.getTransaction().begin();
>>
>>                               ProductOrder result;
>>                               if (order.getId() == null) {
>>                                               em.persist(order);
>>                                               result = order;
>>                               } else {
>>                                               result = em.merge(order);
>>                               }
>>
>>                               em.getTransaction().commit();
>>                               em.detach(result);
>>                               em.close();
>>                               return result;
>>                }
>>
>>                private ProductOrder findById(Long id) {
>>                               EntityManager em =
>> factory.createEntityManager();
>>                               em.getTransaction().begin();
>>                               ProductOrder result =
>> em.find(ProductOrder.class, id);
>>                               em.getTransaction().commit();
>>                               em.detach(result);
>>                               em.close();
>>                               return result;
>>                }
>>
>> }
>>
>> <persistence xmlns="http://java.sun.com/xml/ns/persistence"
>>                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>>                xsi:schemaLocation="http://java.sun.com/xml/ns/persistencepersistence_1_0.xsd"
>>                version="1.0">
>>
>>                <persistence-unit name="testPU">
>>
>> <provider>org.apache.openjpa.persistence.PersistenceProviderImpl
>>                               </provider>
>>                               <class>entities.ProductOrder</class>
>>                               <class>entities.ProductOrderLine</class>
>>
>> <exclude-unlisted-classes>true</exclude-unlisted-classes>
>>                               <properties>
>>                                               <property
>> name="openjpa.jdbc.SynchronizeMappings" value="buildSchema" />
>>                                               <property
>> name="openjpa.ConnectionDriverName"
>>
>> value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
>>                                               <property
>> name="openjpa.ConnectionURL"
>>
>> value="jdbc:sqlserver://localhost\\SQL2008:1433;databaseName=obliprototypeunittest"
>> />
>>                                               <property
>> name="openjpa.ConnectionUserName" value="obliprototype" />
>>                                               <property
>> name="openjpa.ConnectionPassword" value="hidden" />
>>                                               <property name="openjpa.Log"
>> value="DefaultLevel=INFO" />
>>                               </properties>
>>                </persistence-unit>
>> </persistence>
>>
>>
>

Re: adding new entities to an existing List fails when using GenerationType.IDENTITY

Posted by Rick Curtis <cu...@gmail.com>.
How are you enhancing your Entities?

Thanks,
Rick

On Mon, Feb 21, 2011 at 11:22 AM, Henno Vermeulen <he...@huizemolenaar.nl>wrote:

> Although this workaround works in my toy example, it does not seem to work
> in our application, switching to GenerationType.TABLE does.
>
> Henno
>
> -----Oorspronkelijk bericht-----
> Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
> Verzonden: maandag 21 februari 2011 17:50
> Aan: 'users@openjpa.apache.org'
> Onderwerp: RE: adding new entities to an existing List fails when using
> GenerationType.IDENTITY
>
> I found a workaround:
>
> In my test I directly added the new ProductOrderLines to the List in the
> ProductOrder. I do not know the implementation of this List because it is
> the one returned by OpenJPA (at least after detaching).
> When I set the list reference to a new arraylist and add both the existing
> elements and the new ones to this list, the test passes.
> I.e. change the line
>
> firstSaved.getProducts().addAll(0, Arrays.asList(banana, pear));
>
> to
>
>                List<ProductOrderLine> newProducts = new
> ArrayList<ProductOrderLine>();
>                firstSaved.getProducts().addAll(Arrays.asList(banana,
> pear));
>                newProducts.addAll(firstSaved.getProducts());
>                firstSaved.setProducts(newProducts);
>
> Makes the test work.
>
> Regards, Henno
>
>
> -----Oorspronkelijk bericht-----
> Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
> Verzonden: maandag 21 februari 2011 17:19
> Aan: 'users@openjpa.apache.org'
> Onderwerp: adding new entities to an existing List fails when using
> GenerationType.IDENTITY
>
> Hello,
>
> I think I found a serious issue (in both OpenJPA 2.0.0 and 2.1.0) when I
> try to add new entities to an existing list and I use a generated identity
> with GenerationType.IDENTITY.
>
> I start with a fresh database and let OpenJPA create the schema. I have a
> ProductOrder entity that contains a List of ProductOrderLines, annotated as
> such:
>
> @Entity
> public class ProductOrder {
> ...
>            @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
>            private List<ProductOrderLine> products = new
> ArrayList<ProductOrderLine>();
> ...
> }
>
> The entity in the List (ProductOrderLine) has a generated id with
> GenerationType.IDENTITY.
>
> @Entity
> public class ProductOrderLine {
>
>            @Id
>            @GeneratedValue(strategy = GenerationType.IDENTITY)
>            private Long id;
> ...
> }
>
> I start with a ProductOrder that has these products:
> 1 - orange
> 2 - apple
>
> I insert two new products into the list so that I get:
> null - banana
> null - pear
> 1 - orange
> 2 - apple
>
> Then I merge the entity (I work with attach/detach, not sure if this
> matters).
> OpenJPA merge correctly returns a ProductOrder with this list:
>
> 3 - banana
> 4 - pear
> 1 - orange
> 2 - apple
>
> However OpenJPA generates the wrong SQL so that the database contains
> something completely different and indeed selecting the ProductOrder by it's
> id gives:
>
> 3 - banana
> 4 - pear
> 4 - pear
> 4 - pear
>
> I tested this with sql server. (I tried hsqldb but this also suffers from
> bug https://issues.apache.org/jira/browse/OPENJPA-1066).
> This problem does not occur when I use GenerationType.AUTO for
> ProductOrderLine. My example uses a join table, but foreign key columns seem
> to have the same problem. Should I use another generation type? If so which
> one, and is it compatible with existing sql server data that had id values
> automatically generated by sql server?
>
> Please let me know if I should file a bug report and if I should attempt to
> convert my unit test to one accepted by OpenJPA standards.
>
> Regards,
> Henno Vermeulen
>
>
> package entities;
>
> import javax.persistence.Entity;
> import javax.persistence.GeneratedValue;
> import javax.persistence.GenerationType;
> import javax.persistence.Id;
>
> @Entity
> public class ProductOrderLine {
>
>                @Id
>                @GeneratedValue(strategy = GenerationType.IDENTITY)
>                private Long id;
>
>                private String name;
>
>                public ProductOrderLine() {
>                }
>
>                public ProductOrderLine(String name) {
>                               this.name = name;
>                }
>
>                public String getName() {
>                               return name;
>                }
>
>                public void setName(String name) {
>                               this.name = name;
>                }
>
>                public Long getId() {
>                               return id;
>                }
>
> }
>
>
> package entities;
>
> import java.util.ArrayList;
> import java.util.List;
>
> import javax.persistence.CascadeType;
> import javax.persistence.Entity;
> import javax.persistence.FetchType;
> import javax.persistence.GeneratedValue;
> import javax.persistence.GenerationType;
> import javax.persistence.Id;
> import javax.persistence.OneToMany;
>
> @Entity
> public class ProductOrder {
>
>                @Id
>                @GeneratedValue(strategy = GenerationType.IDENTITY)
>                private Long id;
>
>                // Workaround for
> https://issues.apache.org/jira/browse/OPENJPA-1947
>                private String name;
>
>                @OneToMany(cascade = CascadeType.ALL, fetch =
> FetchType.EAGER)
>                private List<ProductOrderLine> products = new
> ArrayList<ProductOrderLine>();
>
>                public ProductOrder() {
>                }
>
>                public Long getId() {
>                               return id;
>                }
>
>                public void setProducts(List<ProductOrderLine> products) {
>                               this.products = products;
>                }
>
>                public List<ProductOrderLine> getProducts() {
>                               return products;
>                }
>
> }
>
> import java.util.ArrayList;
> import java.util.Arrays;
> import java.util.Collections;
> import java.util.Comparator;
> import java.util.List;
>
> import javax.persistence.EntityManager;
> import javax.persistence.EntityManagerFactory;
> import javax.persistence.Persistence;
>
> import junit.framework.TestCase;
> import entities.ProductOrder;
> import entities.ProductOrderLine;
>
> /**
> * I tested this with MSSQL SERVER (I tried hsqldb but this suffers from an
> * additional problem where it mixes up id 0 with id null, see
> * https://issues.apache.org/jira/browse/OPENJPA-1066)/
> *
>  * @author Henno Vermeulen
> */
> public class ProductOrderTest extends TestCase {
>
>                private EntityManagerFactory factory;
>
>                public void setUp() {
>                               factory =
> Persistence.createEntityManagerFactory("testPU", System
>
> .getProperties());
>                }
>
>                public void testProductOrderLineCopy() {
>                               // If we do this, it fails after the first
> save!!!!
>                               // hsqldb seems to convert null to 0 and
> confuse this with an existing
>                               // id or something
>                               // insertSomeData();
>
>                               ProductOrder original = new ProductOrder();
>                               ArrayList<ProductOrderLine> products = new
> ArrayList<ProductOrderLine>();
>                               products.addAll(Arrays.asList(new
> ProductOrderLine("orange"),
>                                                               new
> ProductOrderLine("apple")));
>                               original.setProducts(products);
>
>                               // START
>                               // null - orange
>                               // null - apple
>                               printProducts("Before first save:",
> original);
>                               ProductOrder firstSaved = save(original);
>                               // 1 - orange
>                               // 2 - apple
>                               printProducts("After first save:",
> firstSaved);
>
>                               ProductOrderLine orange =
> firstSaved.getProducts().get(0);
>                               ProductOrderLine apple =
> firstSaved.getProducts().get(1);
>                               assertEquals("orange", orange.getName());
>                               assertEquals("apple", apple.getName());
>                               Long orangeId = orange.getId();
>                               Long appleId = apple.getId();
>                               assertTrue(orangeId != null && appleId !=
> null);
>
>                               ProductOrderLine banana = new
> ProductOrderLine("banana");
>                               ProductOrderLine pear = new
> ProductOrderLine("pear");
>                               assertTrue(banana.getId() == null &&
> pear.getId() == null);
>
>                               firstSaved.getProducts().addAll(0,
> Arrays.asList(banana, pear));
>                               // Changed to
>                               // null - banana
>                               // null - pear
>                               // 1 - orange
>                               // 2 - apple
>                               printProducts("Before second save:",
> firstSaved);
>                               assertExpectedProductLines(firstSaved,
> orangeId, appleId, true);
>
>                               ProductOrder secondSaved = save(firstSaved);
>                               // Expected after save:
>                               // 3 - banana
>                               // 4 - pear
>                               // 1 - orange
>                               // 2 - apple
>
>                               // On SQL server this is actually returned by
> save, but not by find!
>                               printProducts("After second save:",
> secondSaved);
>                               assertExpectedProductLines(secondSaved,
> orangeId, appleId, false);
>
>                               ProductOrder found =
> findById(secondSaved.getId());
>                               printProducts("Found by id after second
> save:", found);
>                               // This one fails!!!!
>                               // On SQL server this returns
>                               // 3 - banana
>                               // 4 - pear
>                               // 4 - pear
>                               // 4 - pear
>                               assertExpectedProductLines(found, orangeId,
> appleId, false);
>                }
>
>                private void printProducts(String description, ProductOrder
> order) {
>                               System.out.println(description);
>                               for (ProductOrderLine p :
> order.getProducts()) {
>                                               System.out.println(p.getId()
> + " - " + p.getName());
>                               }
>                }
>
>                private void assertExpectedProductLines(ProductOrder line,
> Long orangeId,
>                                               Long appleId, boolean
> beforeMerge) {
>                               List<ProductOrderLine> products = new
> ArrayList<ProductOrderLine>();
>                               products.addAll(line.getProducts());
>                               Collections.sort(products, new
> Comparator<ProductOrderLine>() {
>                                               @Override
>                                               public int
> compare(ProductOrderLine o1, ProductOrderLine o2) {
>                                                               return
> o1.getName().compareTo(o2.getName());
>                                               }
>                               });
>                               assertEquals("apple",
> products.get(0).getName());
>                               assertEquals(appleId,
> products.get(0).getId());
>                               assertEquals("banana",
> products.get(1).getName());
>                               assertEquals(beforeMerge, null ==
> products.get(1).getId());
>                               assertEquals("orange",
> products.get(2).getName());
>                               assertEquals(orangeId,
> products.get(2).getId());
>                               assertEquals("pear",
> products.get(3).getName());
>                               assertEquals(beforeMerge, null ==
> products.get(3).getId());
>                }
>
>                private ProductOrder save(ProductOrder order) {
>                               EntityManager em =
> factory.createEntityManager();
>                               em.getTransaction().begin();
>
>                               ProductOrder result;
>                               if (order.getId() == null) {
>                                               em.persist(order);
>                                               result = order;
>                               } else {
>                                               result = em.merge(order);
>                               }
>
>                               em.getTransaction().commit();
>                               em.detach(result);
>                               em.close();
>                               return result;
>                }
>
>                private ProductOrder findById(Long id) {
>                               EntityManager em =
> factory.createEntityManager();
>                               em.getTransaction().begin();
>                               ProductOrder result =
> em.find(ProductOrder.class, id);
>                               em.getTransaction().commit();
>                               em.detach(result);
>                               em.close();
>                               return result;
>                }
>
> }
>
> <persistence xmlns="http://java.sun.com/xml/ns/persistence"
>                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>                xsi:schemaLocation="http://java.sun.com/xml/ns/persistencepersistence_1_0.xsd"
>                version="1.0">
>
>                <persistence-unit name="testPU">
>
> <provider>org.apache.openjpa.persistence.PersistenceProviderImpl
>                               </provider>
>                               <class>entities.ProductOrder</class>
>                               <class>entities.ProductOrderLine</class>
>
> <exclude-unlisted-classes>true</exclude-unlisted-classes>
>                               <properties>
>                                               <property
> name="openjpa.jdbc.SynchronizeMappings" value="buildSchema" />
>                                               <property
> name="openjpa.ConnectionDriverName"
>
> value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
>                                               <property
> name="openjpa.ConnectionURL"
>
> value="jdbc:sqlserver://localhost\\SQL2008:1433;databaseName=obliprototypeunittest"
> />
>                                               <property
> name="openjpa.ConnectionUserName" value="obliprototype" />
>                                               <property
> name="openjpa.ConnectionPassword" value="hidden" />
>                                               <property name="openjpa.Log"
> value="DefaultLevel=INFO" />
>                               </properties>
>                </persistence-unit>
> </persistence>
>
>

RE: adding new entities to an existing List fails when using GenerationType.IDENTITY

Posted by Henno Vermeulen <he...@huizemolenaar.nl>.
Although this workaround works in my toy example, it does not seem to work in our application, switching to GenerationType.TABLE does.

Henno

-----Oorspronkelijk bericht-----
Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
Verzonden: maandag 21 februari 2011 17:50
Aan: 'users@openjpa.apache.org'
Onderwerp: RE: adding new entities to an existing List fails when using GenerationType.IDENTITY

I found a workaround:

In my test I directly added the new ProductOrderLines to the List in the ProductOrder. I do not know the implementation of this List because it is the one returned by OpenJPA (at least after detaching).
When I set the list reference to a new arraylist and add both the existing elements and the new ones to this list, the test passes.
I.e. change the line

firstSaved.getProducts().addAll(0, Arrays.asList(banana, pear));

to

                List<ProductOrderLine> newProducts = new ArrayList<ProductOrderLine>();
                firstSaved.getProducts().addAll(Arrays.asList(banana, pear));
                newProducts.addAll(firstSaved.getProducts());
                firstSaved.setProducts(newProducts);

Makes the test work.

Regards, Henno


-----Oorspronkelijk bericht-----
Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
Verzonden: maandag 21 februari 2011 17:19
Aan: 'users@openjpa.apache.org'
Onderwerp: adding new entities to an existing List fails when using GenerationType.IDENTITY

Hello,

I think I found a serious issue (in both OpenJPA 2.0.0 and 2.1.0) when I try to add new entities to an existing list and I use a generated identity with GenerationType.IDENTITY.

I start with a fresh database and let OpenJPA create the schema. I have a ProductOrder entity that contains a List of ProductOrderLines, annotated as such:

@Entity
public class ProductOrder {
...
            @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
            private List<ProductOrderLine> products = new ArrayList<ProductOrderLine>();
...
}

The entity in the List (ProductOrderLine) has a generated id with GenerationType.IDENTITY.

@Entity
public class ProductOrderLine {

            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            private Long id;
...
}

I start with a ProductOrder that has these products:
1 - orange
2 - apple

I insert two new products into the list so that I get:
null - banana
null - pear
1 - orange
2 - apple

Then I merge the entity (I work with attach/detach, not sure if this matters).
OpenJPA merge correctly returns a ProductOrder with this list:

3 - banana
4 - pear
1 - orange
2 - apple

However OpenJPA generates the wrong SQL so that the database contains something completely different and indeed selecting the ProductOrder by it's id gives:

3 - banana
4 - pear
4 - pear
4 - pear

I tested this with sql server. (I tried hsqldb but this also suffers from bug https://issues.apache.org/jira/browse/OPENJPA-1066).
This problem does not occur when I use GenerationType.AUTO for ProductOrderLine. My example uses a join table, but foreign key columns seem to have the same problem. Should I use another generation type? If so which one, and is it compatible with existing sql server data that had id values automatically generated by sql server?

Please let me know if I should file a bug report and if I should attempt to convert my unit test to one accepted by OpenJPA standards.

Regards,
Henno Vermeulen


package entities;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class ProductOrderLine {

                @Id
                @GeneratedValue(strategy = GenerationType.IDENTITY)
                private Long id;

                private String name;

                public ProductOrderLine() {
                }

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

                public String getName() {
                               return name;
                }

                public void setName(String name) {
                               this.name = name;
                }

                public Long getId() {
                               return id;
                }

}


package entities;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class ProductOrder {

                @Id
                @GeneratedValue(strategy = GenerationType.IDENTITY)
                private Long id;

                // Workaround for https://issues.apache.org/jira/browse/OPENJPA-1947
                private String name;

                @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
                private List<ProductOrderLine> products = new ArrayList<ProductOrderLine>();

                public ProductOrder() {
                }

                public Long getId() {
                               return id;
                }

                public void setProducts(List<ProductOrderLine> products) {
                               this.products = products;
                }

                public List<ProductOrderLine> getProducts() {
                               return products;
                }

}

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import junit.framework.TestCase;
import entities.ProductOrder;
import entities.ProductOrderLine;

/**
* I tested this with MSSQL SERVER (I tried hsqldb but this suffers from an
* additional problem where it mixes up id 0 with id null, see
* https://issues.apache.org/jira/browse/OPENJPA-1066)/
*
 * @author Henno Vermeulen
*/
public class ProductOrderTest extends TestCase {

                private EntityManagerFactory factory;

                public void setUp() {
                               factory = Persistence.createEntityManagerFactory("testPU", System
                                                               .getProperties());
                }

                public void testProductOrderLineCopy() {
                               // If we do this, it fails after the first save!!!!
                               // hsqldb seems to convert null to 0 and confuse this with an existing
                               // id or something
                               // insertSomeData();

                               ProductOrder original = new ProductOrder();
                               ArrayList<ProductOrderLine> products = new ArrayList<ProductOrderLine>();
                               products.addAll(Arrays.asList(new ProductOrderLine("orange"),
                                                               new ProductOrderLine("apple")));
                               original.setProducts(products);

                               // START
                               // null - orange
                               // null - apple
                               printProducts("Before first save:", original);
                               ProductOrder firstSaved = save(original);
                               // 1 - orange
                               // 2 - apple
                               printProducts("After first save:", firstSaved);

                               ProductOrderLine orange = firstSaved.getProducts().get(0);
                               ProductOrderLine apple = firstSaved.getProducts().get(1);
                               assertEquals("orange", orange.getName());
                               assertEquals("apple", apple.getName());
                               Long orangeId = orange.getId();
                               Long appleId = apple.getId();
                               assertTrue(orangeId != null && appleId != null);

                               ProductOrderLine banana = new ProductOrderLine("banana");
                               ProductOrderLine pear = new ProductOrderLine("pear");
                               assertTrue(banana.getId() == null && pear.getId() == null);

                               firstSaved.getProducts().addAll(0, Arrays.asList(banana, pear));
                               // Changed to
                               // null - banana
                               // null - pear
                               // 1 - orange
                               // 2 - apple
                               printProducts("Before second save:", firstSaved);
                               assertExpectedProductLines(firstSaved, orangeId, appleId, true);

                               ProductOrder secondSaved = save(firstSaved);
                               // Expected after save:
                               // 3 - banana
                               // 4 - pear
                               // 1 - orange
                               // 2 - apple

                               // On SQL server this is actually returned by save, but not by find!
                               printProducts("After second save:", secondSaved);
                               assertExpectedProductLines(secondSaved, orangeId, appleId, false);

                               ProductOrder found = findById(secondSaved.getId());
                               printProducts("Found by id after second save:", found);
                               // This one fails!!!!
                               // On SQL server this returns
                               // 3 - banana
                               // 4 - pear
                               // 4 - pear
                               // 4 - pear
                               assertExpectedProductLines(found, orangeId, appleId, false);
                }

                private void printProducts(String description, ProductOrder order) {
                               System.out.println(description);
                               for (ProductOrderLine p : order.getProducts()) {
                                               System.out.println(p.getId() + " - " + p.getName());
                               }
                }

                private void assertExpectedProductLines(ProductOrder line, Long orangeId,
                                               Long appleId, boolean beforeMerge) {
                               List<ProductOrderLine> products = new ArrayList<ProductOrderLine>();
                               products.addAll(line.getProducts());
                               Collections.sort(products, new Comparator<ProductOrderLine>() {
                                               @Override
                                               public int compare(ProductOrderLine o1, ProductOrderLine o2) {
                                                               return o1.getName().compareTo(o2.getName());
                                               }
                               });
                               assertEquals("apple", products.get(0).getName());
                               assertEquals(appleId, products.get(0).getId());
                               assertEquals("banana", products.get(1).getName());
                               assertEquals(beforeMerge, null == products.get(1).getId());
                               assertEquals("orange", products.get(2).getName());
                               assertEquals(orangeId, products.get(2).getId());
                               assertEquals("pear", products.get(3).getName());
                               assertEquals(beforeMerge, null == products.get(3).getId());
                }

                private ProductOrder save(ProductOrder order) {
                               EntityManager em = factory.createEntityManager();
                               em.getTransaction().begin();

                               ProductOrder result;
                               if (order.getId() == null) {
                                               em.persist(order);
                                               result = order;
                               } else {
                                               result = em.merge(order);
                               }

                               em.getTransaction().commit();
                               em.detach(result);
                               em.close();
                               return result;
                }

                private ProductOrder findById(Long id) {
                               EntityManager em = factory.createEntityManager();
                               em.getTransaction().begin();
                               ProductOrder result = em.find(ProductOrder.class, id);
                               em.getTransaction().commit();
                               em.detach(result);
                               em.close();
                               return result;
                }

}

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="http://java.sun.com/xml/ns/persistence persistence_1_0.xsd"
                version="1.0">

                <persistence-unit name="testPU">
                               <provider>org.apache.openjpa.persistence.PersistenceProviderImpl
                               </provider>
                               <class>entities.ProductOrder</class>
                               <class>entities.ProductOrderLine</class>
                               <exclude-unlisted-classes>true</exclude-unlisted-classes>
                               <properties>
                                               <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema" />
                                               <property name="openjpa.ConnectionDriverName"
                                                               value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
                                               <property name="openjpa.ConnectionURL"
                                                               value="jdbc:sqlserver://localhost\\SQL2008:1433;databaseName=obliprototypeunittest" />
                                               <property name="openjpa.ConnectionUserName" value="obliprototype" />
                                               <property name="openjpa.ConnectionPassword" value="hidden" />
                                               <property name="openjpa.Log" value="DefaultLevel=INFO" />
                               </properties>
                </persistence-unit>
</persistence>


RE: adding new entities to an existing List fails when using GenerationType.IDENTITY

Posted by Henno Vermeulen <he...@huizemolenaar.nl>.
I found a workaround:

In my test I directly added the new ProductOrderLines to the List in the ProductOrder. I do not know the implementation of this List because it is the one returned by OpenJPA (at least after detaching).
When I set the list reference to a new arraylist and add both the existing elements and the new ones to this list, the test passes.
I.e. change the line

firstSaved.getProducts().addAll(0, Arrays.asList(banana, pear));

to

                List<ProductOrderLine> newProducts = new ArrayList<ProductOrderLine>();
                firstSaved.getProducts().addAll(Arrays.asList(banana, pear));
                newProducts.addAll(firstSaved.getProducts());
                firstSaved.setProducts(newProducts);

Makes the test work.

Regards, Henno


-----Oorspronkelijk bericht-----
Van: Henno Vermeulen [mailto:henno@huizemolenaar.nl]
Verzonden: maandag 21 februari 2011 17:19
Aan: 'users@openjpa.apache.org'
Onderwerp: adding new entities to an existing List fails when using GenerationType.IDENTITY

Hello,

I think I found a serious issue (in both OpenJPA 2.0.0 and 2.1.0) when I try to add new entities to an existing list and I use a generated identity with GenerationType.IDENTITY.

I start with a fresh database and let OpenJPA create the schema. I have a ProductOrder entity that contains a List of ProductOrderLines, annotated as such:

@Entity
public class ProductOrder {
...
            @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
            private List<ProductOrderLine> products = new ArrayList<ProductOrderLine>();
...
}

The entity in the List (ProductOrderLine) has a generated id with GenerationType.IDENTITY.

@Entity
public class ProductOrderLine {

            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            private Long id;
...
}

I start with a ProductOrder that has these products:
1 - orange
2 - apple

I insert two new products into the list so that I get:
null - banana
null - pear
1 - orange
2 - apple

Then I merge the entity (I work with attach/detach, not sure if this matters).
OpenJPA merge correctly returns a ProductOrder with this list:

3 - banana
4 - pear
1 - orange
2 - apple

However OpenJPA generates the wrong SQL so that the database contains something completely different and indeed selecting the ProductOrder by it's id gives:

3 - banana
4 - pear
4 - pear
4 - pear

I tested this with sql server. (I tried hsqldb but this also suffers from bug https://issues.apache.org/jira/browse/OPENJPA-1066).
This problem does not occur when I use GenerationType.AUTO for ProductOrderLine. My example uses a join table, but foreign key columns seem to have the same problem. Should I use another generation type? If so which one, and is it compatible with existing sql server data that had id values automatically generated by sql server?

Please let me know if I should file a bug report and if I should attempt to convert my unit test to one accepted by OpenJPA standards.

Regards,
Henno Vermeulen


package entities;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class ProductOrderLine {

                @Id
                @GeneratedValue(strategy = GenerationType.IDENTITY)
                private Long id;

                private String name;

                public ProductOrderLine() {
                }

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

                public String getName() {
                               return name;
                }

                public void setName(String name) {
                               this.name = name;
                }

                public Long getId() {
                               return id;
                }

}


package entities;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class ProductOrder {

                @Id
                @GeneratedValue(strategy = GenerationType.IDENTITY)
                private Long id;

                // Workaround for https://issues.apache.org/jira/browse/OPENJPA-1947
                private String name;

                @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
                private List<ProductOrderLine> products = new ArrayList<ProductOrderLine>();

                public ProductOrder() {
                }

                public Long getId() {
                               return id;
                }

                public void setProducts(List<ProductOrderLine> products) {
                               this.products = products;
                }

                public List<ProductOrderLine> getProducts() {
                               return products;
                }

}

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import junit.framework.TestCase;
import entities.ProductOrder;
import entities.ProductOrderLine;

/**
* I tested this with MSSQL SERVER (I tried hsqldb but this suffers from an
* additional problem where it mixes up id 0 with id null, see
* https://issues.apache.org/jira/browse/OPENJPA-1066)/
*
 * @author Henno Vermeulen
*/
public class ProductOrderTest extends TestCase {

                private EntityManagerFactory factory;

                public void setUp() {
                               factory = Persistence.createEntityManagerFactory("testPU", System
                                                               .getProperties());
                }

                public void testProductOrderLineCopy() {
                               // If we do this, it fails after the first save!!!!
                               // hsqldb seems to convert null to 0 and confuse this with an existing
                               // id or something
                               // insertSomeData();

                               ProductOrder original = new ProductOrder();
                               ArrayList<ProductOrderLine> products = new ArrayList<ProductOrderLine>();
                               products.addAll(Arrays.asList(new ProductOrderLine("orange"),
                                                               new ProductOrderLine("apple")));
                               original.setProducts(products);

                               // START
                               // null - orange
                               // null - apple
                               printProducts("Before first save:", original);
                               ProductOrder firstSaved = save(original);
                               // 1 - orange
                               // 2 - apple
                               printProducts("After first save:", firstSaved);

                               ProductOrderLine orange = firstSaved.getProducts().get(0);
                               ProductOrderLine apple = firstSaved.getProducts().get(1);
                               assertEquals("orange", orange.getName());
                               assertEquals("apple", apple.getName());
                               Long orangeId = orange.getId();
                               Long appleId = apple.getId();
                               assertTrue(orangeId != null && appleId != null);

                               ProductOrderLine banana = new ProductOrderLine("banana");
                               ProductOrderLine pear = new ProductOrderLine("pear");
                               assertTrue(banana.getId() == null && pear.getId() == null);

                               firstSaved.getProducts().addAll(0, Arrays.asList(banana, pear));
                               // Changed to
                               // null - banana
                               // null - pear
                               // 1 - orange
                               // 2 - apple
                               printProducts("Before second save:", firstSaved);
                               assertExpectedProductLines(firstSaved, orangeId, appleId, true);

                               ProductOrder secondSaved = save(firstSaved);
                               // Expected after save:
                               // 3 - banana
                               // 4 - pear
                               // 1 - orange
                               // 2 - apple

                               // On SQL server this is actually returned by save, but not by find!
                               printProducts("After second save:", secondSaved);
                               assertExpectedProductLines(secondSaved, orangeId, appleId, false);

                               ProductOrder found = findById(secondSaved.getId());
                               printProducts("Found by id after second save:", found);
                               // This one fails!!!!
                               // On SQL server this returns
                               // 3 - banana
                               // 4 - pear
                               // 4 - pear
                               // 4 - pear
                               assertExpectedProductLines(found, orangeId, appleId, false);
                }

                private void printProducts(String description, ProductOrder order) {
                               System.out.println(description);
                               for (ProductOrderLine p : order.getProducts()) {
                                               System.out.println(p.getId() + " - " + p.getName());
                               }
                }

                private void assertExpectedProductLines(ProductOrder line, Long orangeId,
                                               Long appleId, boolean beforeMerge) {
                               List<ProductOrderLine> products = new ArrayList<ProductOrderLine>();
                               products.addAll(line.getProducts());
                               Collections.sort(products, new Comparator<ProductOrderLine>() {
                                               @Override
                                               public int compare(ProductOrderLine o1, ProductOrderLine o2) {
                                                               return o1.getName().compareTo(o2.getName());
                                               }
                               });
                               assertEquals("apple", products.get(0).getName());
                               assertEquals(appleId, products.get(0).getId());
                               assertEquals("banana", products.get(1).getName());
                               assertEquals(beforeMerge, null == products.get(1).getId());
                               assertEquals("orange", products.get(2).getName());
                               assertEquals(orangeId, products.get(2).getId());
                               assertEquals("pear", products.get(3).getName());
                               assertEquals(beforeMerge, null == products.get(3).getId());
                }

                private ProductOrder save(ProductOrder order) {
                               EntityManager em = factory.createEntityManager();
                               em.getTransaction().begin();

                               ProductOrder result;
                               if (order.getId() == null) {
                                               em.persist(order);
                                               result = order;
                               } else {
                                               result = em.merge(order);
                               }

                               em.getTransaction().commit();
                               em.detach(result);
                               em.close();
                               return result;
                }

                private ProductOrder findById(Long id) {
                               EntityManager em = factory.createEntityManager();
                               em.getTransaction().begin();
                               ProductOrder result = em.find(ProductOrder.class, id);
                               em.getTransaction().commit();
                               em.detach(result);
                               em.close();
                               return result;
                }

}

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="http://java.sun.com/xml/ns/persistence persistence_1_0.xsd"
                version="1.0">

                <persistence-unit name="testPU">
                               <provider>org.apache.openjpa.persistence.PersistenceProviderImpl
                               </provider>
                               <class>entities.ProductOrder</class>
                               <class>entities.ProductOrderLine</class>
                               <exclude-unlisted-classes>true</exclude-unlisted-classes>
                               <properties>
                                               <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema" />
                                               <property name="openjpa.ConnectionDriverName"
                                                               value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
                                               <property name="openjpa.ConnectionURL"
                                                               value="jdbc:sqlserver://localhost\\SQL2008:1433;databaseName=obliprototypeunittest" />
                                               <property name="openjpa.ConnectionUserName" value="obliprototype" />
                                               <property name="openjpa.ConnectionPassword" value="hidden" />
                                               <property name="openjpa.Log" value="DefaultLevel=INFO" />
                               </properties>
                </persistence-unit>
</persistence>