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>
>
>

Reply via email to