Return-Path: Delivered-To: apmail-openjpa-users-archive@minotaur.apache.org Received: (qmail 30777 invoked from network); 21 Feb 2011 16:19:41 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 21 Feb 2011 16:19:41 -0000 Received: (qmail 62013 invoked by uid 500); 21 Feb 2011 16:19:41 -0000 Delivered-To: apmail-openjpa-users-archive@openjpa.apache.org Received: (qmail 61757 invoked by uid 500); 21 Feb 2011 16:19:37 -0000 Mailing-List: contact users-help@openjpa.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: users@openjpa.apache.org Delivered-To: mailing list users@openjpa.apache.org Received: (qmail 61749 invoked by uid 99); 21 Feb 2011 16:19:36 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 21 Feb 2011 16:19:36 +0000 X-ASF-Spam-Status: No, hits=2.2 required=5.0 tests=HTML_MESSAGE,RCVD_IN_DNSWL_NONE,SPF_PASS X-Spam-Check-By: apache.org Received-SPF: pass (athena.apache.org: local policy) Received: from [194.109.24.38] (HELO smtp-vbr18.xs4all.nl) (194.109.24.38) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 21 Feb 2011 16:19:29 +0000 Received: from remote.huizemolenaar.nl (D57D0452.static.ziggozakelijk.nl [213.125.4.82]) (authenticated bits=0) by smtp-vbr18.xs4all.nl (8.13.8/8.13.8) with ESMTP id p1LGJ7vn044193 (version=TLSv1/SSLv3 cipher=AES128-SHA bits=128 verify=FAIL) for ; Mon, 21 Feb 2011 17:19:07 +0100 (CET) (envelope-from henno@huizemolenaar.nl) Received: from HMS.hm.local ([fe80::6051:4a91:4c0d:d963]) by HMS.hm.local ([fe80::6051:4a91:4c0d:d963%10]) with mapi; Mon, 21 Feb 2011 17:18:47 +0100 From: Henno Vermeulen To: "'users@openjpa.apache.org'" Date: Mon, 21 Feb 2011 17:18:45 +0100 Subject: adding new entities to an existing List fails when using GenerationType.IDENTITY Thread-Topic: adding new entities to an existing List fails when using GenerationType.IDENTITY Thread-Index: AcvR4wNZYM4Bhk4oRayH1yGi/ZNy/g== Message-ID: <1C448C478A6B4743AF19DBC3C3DCE1320141807D8D6D@HMS.hm.local> Accept-Language: nl-NL Content-Language: nl-NL X-MS-Has-Attach: X-MS-TNEF-Correlator: acceptlanguage: nl-NL Content-Type: multipart/alternative; boundary="_000_1C448C478A6B4743AF19DBC3C3DCE1320141807D8D6DHMShmlocal_" MIME-Version: 1.0 X-Virus-Scanned: by XS4ALL Virus Scanner --_000_1C448C478A6B4743AF19DBC3C3DCE1320141807D8D6DHMShmlocal_ Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable Hello, I think I found a serious issue (in both OpenJPA 2.0.0 and 2.1.0) when I tr= y to add new entities to an existing list and I use a generated identity wi= th GenerationType.IDENTITY. I start with a fresh database and let OpenJPA create the schema. I have a P= roductOrder entity that contains a List of ProductOrderLines, annotated as = such: @Entity public class ProductOrder { ... @OneToMany(cascade =3D CascadeType.ALL, fetch =3D FetchType.EAG= ER) private List products =3D new ArrayList(); ... } The entity in the List (ProductOrderLine) has a generated id with Generatio= nType.IDENTITY. @Entity public class ProductOrderLine { @Id @GeneratedValue(strategy =3D 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 matter= s). 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 somet= hing 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 b= ug https://issues.apache.org/jira/browse/OPENJPA-1066). This problem does not occur when I use GenerationType.AUTO for ProductOrder= Line. My example uses a join table, but foreign key columns seem to have th= e 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 automatic= ally 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 =3D GenerationType.IDENTITY) private Long id; private String name; public ProductOrderLine() { } public ProductOrderLine(String name) { this.name =3D name; } public String getName() { return name; } public void setName(String name) { this.name =3D 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 =3D GenerationType.IDENTITY) private Long id; // Workaround for https://issues.apache.org/jira/browse/OPE= NJPA-1947 private String name; @OneToMany(cascade =3D CascadeType.ALL, fetch =3D FetchType= .EAGER) private List products =3D new ArrayList(); public ProductOrder() { } public Long getId() { return id; } public void setProducts(List products) { this.products =3D products; } public List 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 =3D Persistence.createEntityManagerF= actory("testPU", System .getProperti= es()); } public void testProductOrderLineCopy() { // If we do this, it fails after the first s= ave!!!! // hsqldb seems to convert null to 0 and con= fuse this with an existing // id or something // insertSomeData(); ProductOrder original =3D new ProductOrder()= ; ArrayList products =3D new= ArrayList(); products.addAll(Arrays.asList(new ProductOrd= erLine("orange"), new ProductO= rderLine("apple"))); original.setProducts(products); // START // null - orange // null - apple printProducts("Before first save:", original= ); ProductOrder firstSaved =3D save(original); // 1 - orange // 2 - apple printProducts("After first save:", firstSave= d); ProductOrderLine orange =3D firstSaved.getPr= oducts().get(0); ProductOrderLine apple =3D firstSaved.getPro= ducts().get(1); assertEquals("orange", orange.getName()); assertEquals("apple", apple.getName()); Long orangeId =3D orange.getId(); Long appleId =3D apple.getId(); assertTrue(orangeId !=3D null && appleId != =3D null); ProductOrderLine banana =3D new ProductOrder= Line("banana"); ProductOrderLine pear =3D new ProductOrderLi= ne("pear"); assertTrue(banana.getId() =3D=3D null && pea= r.getId() =3D=3D null); firstSaved.getProducts().addAll(0, Arrays.as= List(banana, pear)); // Changed to // null - banana // null - pear // 1 - orange // 2 - apple printProducts("Before second save:", firstSa= ved); assertExpectedProductLines(firstSaved, orang= eId, appleId, true); ProductOrder secondSaved =3D save(firstSaved= ); // Expected after save: // 3 - banana // 4 - pear // 1 - orange // 2 - apple // On SQL server this is actually returned b= y save, but not by find! printProducts("After second save:", secondSa= ved); assertExpectedProductLines(secondSaved, oran= geId, appleId, false); ProductOrder found =3D 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 before= Merge) { List products =3D new Arra= yList(); products.addAll(line.getProducts()); Collections.sort(products, new Comparator() { @Override public int compare(ProductOr= derLine o1, ProductOrderLine o2) { return o1.ge= tName().compareTo(o2.getName()); } }); assertEquals("apple", products.get(0).getNam= e()); assertEquals(appleId, products.get(0).getId(= )); assertEquals("banana", products.get(1).getNa= me()); assertEquals(beforeMerge, null =3D=3D produc= ts.get(1).getId()); assertEquals("orange", products.get(2).getNa= me()); assertEquals(orangeId, products.get(2).getId= ()); assertEquals("pear", products.get(3).getName= ()); assertEquals(beforeMerge, null =3D=3D produc= ts.get(3).getId()); } private ProductOrder save(ProductOrder order) { EntityManager em =3D factory.createEntityMan= ager(); em.getTransaction().begin(); ProductOrder result; if (order.getId() =3D=3D null) { em.persist(order); result =3D order; } else { result =3D em.merge(order); } em.getTransaction().commit(); em.detach(result); em.close(); return result; } private ProductOrder findById(Long id) { EntityManager em =3D factory.createEntityMan= ager(); em.getTransaction().begin(); ProductOrder result =3D em.find(ProductOrder= .class, id); em.getTransaction().commit(); em.detach(result); em.close(); return result; } } org.apache.openjpa.persistence.Per= sistenceProviderImpl entities.ProductOrder entities.ProductOrderLine true --_000_1C448C478A6B4743AF19DBC3C3DCE1320141807D8D6DHMShmlocal_--