openjpa-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Henno Vermeulen <he...@huizemolenaar.nl>
Subject RE: adding new entities to an existing List fails when using GenerationType.IDENTITY
Date Mon, 21 Feb 2011 16:50:27 GMT
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>

Mime
View raw message