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 <[email protected]>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:[email protected]] > Verzonden: dinsdag 22 februari 2011 9:32 > Aan: '[email protected]' > 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:[email protected]] > Verzonden: maandag 21 februari 2011 23:22 > Aan: [email protected] > 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 <[email protected]> wrote: > > > How are you enhancing your Entities? > > > > Thanks, > > Rick > > > > > > On Mon, Feb 21, 2011 at 11:22 AM, Henno Vermeulen < > [email protected]>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:[email protected]] > >> Verzonden: maandag 21 februari 2011 17:50 > >> Aan: '[email protected]' > >> 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:[email protected]] > >> Verzonden: maandag 21 februari 2011 17:19 > >> Aan: '[email protected]' > >> 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> > >> > >> > > > >
