+ * This is a variation on the normal oneToMany mapping using two {@link JoinColumn} annotations
+ * instead of using the mappedBy
attribute.
+ *
+ *
+ * In this mapping the position each child has in the parent's list is stored in the + * child table and fully managed by JPA. This means that this position index does + * not explicitly show up in the object model. + * + *
+ * Example SQL DDL (h2 syntax) + *
+ * create table Parent ( + * id bigint generated by default as identity, + * + * primary key (id) + * ); + * + * create table Child ( + * id bigint generated by default as identity, + * parent_id bigint, + * children_ORDER integer, + * + * primary key (id), + * foreign key (parent_id) references Parent + * ); + *+ * + * + * @author Arjan Tijms + */ +@RunWith(Arquillian.class) +public class OrderColumnBiJoinTest { + + @Deployment + private static Archive> createDeployment() { + return ShrinkWrap.create(WebArchive.class) + .addPackages(true, Child.class.getPackage()) + .addPackages(true, OrderColumnTesterService.class.getPackage()) + .addAsResource("META-INF/persistence.xml"); + } + + @EJB + private OrderColumnTesterService indexColumnTesterService; + + @Test + public void saveInOneGo() { + Parent parent = new Parent(); + + Child child1 = new Child(); + child1.setParent(parent); + + Child child2 = new Child(); + child2.setParent(parent); + + parent.getChildren().add(child1); + parent.getChildren().add(child2); + + parent = indexColumnTesterService.save(parent); + + // Reload parent fresh from data source again + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + } + + /** + * Saves a parent instance first, then adds two children instances and saves again. + * + *
+ * Example sequence of insert/update statements that may be generated to accomplish this: + *
+ * insert into Parent (id) values (null) + * insert into Child (id) values (null) + * insert into Child (id) values (null) + * + * update Child set parent_id = 1, children_ORDER = 0 where id = 1 + * update Child set parent_id = 1, children_ORDER = 1 where id = 2 + *+ * + */ + @Test + public void saveParentSeparatelyFirst() { + + Parent parent = indexColumnTesterService.save(new Parent()); + + Child child1 = new Child(); + child1.setParent(parent); + + Child child2 = new Child(); + child2.setParent(parent); + + parent.getChildren().add(child1); + parent.getChildren().add(child2); + + parent = indexColumnTesterService.save(parent); + + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + + } + + @Test + public void saveParentWithOneChildFirst() { + + Parent parent = new Parent(); + Child child1 = new Child(); + child1.setParent(parent); + parent.getChildren().add(child1); + + // Save parent with 1 child in one go + parent = indexColumnTesterService.save(parent); + + Child child2 = new Child(); + child2.setParent(parent); + parent.getChildren().add(child2); + + // Save parent again with second child + parent = indexColumnTesterService.save(parent); + + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + } + + @Test + public void saveParentWithChildrenThenDeleteOned() { + + Parent parent = new Parent(); + + Child child1 = new Child(); + child1.setParent(parent); + parent.getChildren().add(child1); + + Child child2 = new Child(); + child2.setParent(parent); + parent.getChildren().add(child2); + + Child child3 = new Child(); + child3.setParent(parent); + parent.getChildren().add(child3); + + // Save parent with 3 children + parent = indexColumnTesterService.save(parent); + + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("3 children added to parent and saved, but after re-loading number of chilren different", + 3, savedParent.getChildren().size() + ); + + // Removing child at position 1 and saving again + savedParent.getChildren().remove(1); + + savedParent = indexColumnTesterService.save(savedParent); + savedParent = indexColumnTesterService.getParentById(savedParent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + + } + +} diff --git a/jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnBiMappedByTest.java b/jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnBiMappedByTest.java new file mode 100644 index 000000000..a149b04a6 --- /dev/null +++ b/jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnBiMappedByTest.java @@ -0,0 +1,132 @@ +package org.javaee7.jpa.ordercolumn; + +import static org.junit.Assert.assertEquals; + +import javax.ejb.EJB; +import javax.persistence.OrderColumn; + +import org.javaee7.jpa.ordercolumn.entity.bidirectionalmappedby.Child; +import org.javaee7.jpa.ordercolumn.entity.bidirectionalmappedby.Parent; +import org.javaee7.jpa.ordercolumn.service.bidirectionalmappedby.OrderColumnTesterService; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * This tests and demonstrates the {@link OrderColumn} annotation when used together with a + * bi-directional parent-child mapping, where the child holds a foreign key to the parent. + * + *
+ * This is the normal oneToMany mapping using the mappedBy
attribute.
+ * Even though this is the most straightforward mapping, because of a mis-interpretation
+ * in the JPA spec it was believed for years that this mapping did not have to be
+ * supported (see https://hibernate.atlassian.net/browse/HHH-5732 among others)
+ *
+ *
+ * In this mapping the position each child has in the parent's list is stored in the + * child table and fully managed by JPA. This means that this position index does + * not explicitly show up in the object model. + * + *
+ * Example SQL DDL (h2 syntax) + *
+ * create table Parent ( + * id bigint generated by default as identity, + * + * primary key (id) + * ); + * + * create table Child ( + * id bigint generated by default as identity, + * parent_id bigint, + * children_ORDER integer, + * + * primary key (id), + * foreign key (parent_id) references Parent + * ); + *+ * + * + * @author Arjan Tijms + */ +@RunWith(Arquillian.class) +public class OrderColumnBiMappedByTest { + + @Deployment + private static Archive> createDeployment() { + return ShrinkWrap.create(WebArchive.class) + .addPackages(true, Parent.class.getPackage()) + .addPackages(true, OrderColumnTesterService.class.getPackage()) + .addAsResource("META-INF/persistence.xml"); + } + + @EJB + private OrderColumnTesterService indexColumnTesterService; + + @Test + public void saveInOneGo() { + + Parent parent = new Parent(); + + Child child1 = new Child(); + child1.setParent(parent); + + Child child2 = new Child(); + child2.setParent(parent); + + parent.getChildren().add(child1); + parent.getChildren().add(child2); + + parent = indexColumnTesterService.save(parent); + + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + } + + /** + * Saves a parent instance first, then adds two children instances and saves again. + * + *
+ * Example sequence of insert/update statements that may be generated to accomplish this: + *
+ * insert into Parent (id) values (null) + * insert into Child (id, parent_id) values (null, 1) + * insert into Child (id, parent_id) values (null, 1) + * + * update Child set children_ORDER = 0 where id = 1 + * update Child set children_ORDER = 1 where id = 2 + *+ * + */ + @Test + public void saveParentSeparatelyFirst() { + + Parent parent = indexColumnTesterService.save(new Parent()); + + Child child1 = new Child(); + child1.setParent(parent); + + Child child2 = new Child(); + child2.setParent(parent); + + parent.getChildren().add(child1); + parent.getChildren().add(child2); + + parent = indexColumnTesterService.save(parent); + + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + + } + +} \ No newline at end of file diff --git a/jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnUniTest.java b/jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnUniTest.java new file mode 100644 index 000000000..67079628d --- /dev/null +++ b/jpa/ordercolumn/src/test/java/org/javaee7/jpa/ordercolumn/OrderColumnUniTest.java @@ -0,0 +1,172 @@ +package org.javaee7.jpa.ordercolumn; + +import static org.junit.Assert.assertEquals; + +import javax.ejb.EJB; +import javax.persistence.OrderColumn; + +import org.javaee7.jpa.ordercolumn.entity.unidirectional.Child; +import org.javaee7.jpa.ordercolumn.entity.unidirectional.Parent; +import org.javaee7.jpa.ordercolumn.service.unidirectional.OrderColumnTesterService; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * This tests and demonstrates the {@link OrderColumn} annotation when used together with a + * uni-directional parent-child mapping, where the child table holds a foreign key to the parent. + * + *
+ * In this mapping the position each child has in the parent's list is stored in the + * child table and fully managed by JPA. This means that this position index does + * not explicitly show up in the object model. + * + *
+ * Example SQL DDL (h2 syntax) + *
+ * create table Parent ( + * id bigint generated by default as identity, + * + * primary key (id) + * ); + * + * create table Child ( + * id bigint generated by default as identity, + * children_id bigint, + * children_ORDER integer, + * + * primary key (id), + * foreign key (children_id) references Parent + * ); + *+ * + * + * @author Arjan Tijms + */ +@RunWith(Arquillian.class) +public class OrderColumnUniTest { + + @Deployment + private static Archive> createDeployment() { + return ShrinkWrap.create(WebArchive.class) + .addPackages(true, Child.class.getPackage()) + .addPackages(true, OrderColumnTesterService.class.getPackage()) + .addAsResource("META-INF/persistence.xml"); + } + + @EJB + private OrderColumnTesterService indexColumnTesterService; + + /** + * Saves a parent instance with 2 children in its children collection in one operation. + * + *
+ * Example sequence of insert/update statements that may be generated to accomplish this: + *
+ * insert into Parent (id) values (null) + * insert into Child (id) values (null) + * insert into Child (id) values (null) + * + * update Child set children_id = 1, children_ORDER = 0 where id = 1 + * update Child set children_id = 1, children_ORDER = 1 where id = 2 + *+ * + */ + @Test + public void saveInOneGo() { + + Parent parent = new Parent(); + Child child1 = new Child(); + Child child2 = new Child(); + + parent.getChildren().add(child1); + parent.getChildren().add(child2); + + parent = indexColumnTesterService.save(parent); + + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + } + + /** + * Saves a parent instance first, then adds two children instances and saves again. + * + *
+ * Example sequence of insert/update statements that may be generated to accomplish this: + *
+ * insert into Parent (id) values (null) + * insert into Child (id) values (null) + * insert into Child (id) values (null) + * + * update Child set children_id = 1, children_ORDER = 0 where id = 1 + * update Child set children_id = 1, children_ORDER = 1 where id = 2 + *+ * + */ + @Test + public void saveParentSeparatelyFirst() { + + Parent parent = indexColumnTesterService.save(new Parent()); + + Child child1 = new Child(); + Child child2 = new Child(); + + parent.getChildren().add(child1); + parent.getChildren().add(child2); + + parent = indexColumnTesterService.save(parent); + + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + + } + + /** + * Saves a parent with one child in its children collection first, then adds another child and saves again. + * + *
+ * Example sequence of insert/update statements that may be generated to accomplish this: + *
+ * insert into Parent (id) values (null) + * insert into Child (id) values (null) + * update Child set children_id = 1, children_ORDER = 0 where id = 1 + * + * insert into Child (id) values (null) + * update Child set children_id = 1, children_ORDER = 1 where id = 2 + *+ */ + @Test + public void saveParentWithOneChildFirst() { + + Parent parent = new Parent(); + Child child1 = new Child(); + parent.getChildren().add(child1); + + // Save parent with 1 child in one go + parent = indexColumnTesterService.save(parent); + + Child child2 = new Child(); + parent.getChildren().add(child2); + + // Save parent again with second child + parent = indexColumnTesterService.save(parent); + + Parent savedParent = indexColumnTesterService.getParentById(parent.getId()); + + assertEquals("2 children added to parent and saved, but after re-loading number of chilren different", + 2, savedParent.getChildren().size() + ); + + } + +} diff --git a/jpa/pom.xml b/jpa/pom.xml index b5f48b112..2b49e6416 100644 --- a/jpa/pom.xml +++ b/jpa/pom.xml @@ -29,6 +29,7 @@