From 3eff16e07afd3ce6252574ded5a54920b2965c1f Mon Sep 17 00:00:00 2001 From: Jeremy Bauer Date: Fri, 8 May 2009 19:53:16 +0000 Subject: [PATCH] OPENJPA-1074 Maintain contiguous index for collection element removal git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@773070 13f79535-47bb-0310-9956-ffa450edef68 --- .../HandlerCollectionTableFieldStrategy.java | 9 +- .../RelationToManyTableFieldStrategy.java | 9 +- .../jdbc/order/TestOrderColumn.java | 262 +++++++++++++++++- 3 files changed, 273 insertions(+), 7 deletions(-) diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerCollectionTableFieldStrategy.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerCollectionTableFieldStrategy.java index 8e615181a..90c9fc830 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerCollectionTableFieldStrategy.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerCollectionTableFieldStrategy.java @@ -197,8 +197,12 @@ public class HandlerCollectionTableFieldStrategy ct = proxy.getChangeTracker(); } - // if no fine-grained change tracking then just delete and reinsert - if (ct == null || !ct.isTracking()) { + Column order = field.getOrderColumn(); + + // if no fine-grained change tracking or if an item was removed + // from an ordered collection, delete and reinsert + if (ct == null || !ct.isTracking() || + (order != null && !ct.getRemoved().isEmpty())) { delete(sm, store, rm); insert(sm, store, rm, obj); return; @@ -227,7 +231,6 @@ public class HandlerCollectionTableFieldStrategy field.getJoinColumnIO(), sm); int seq = ct.getNextSequence(); - Column order = field.getOrderColumn(); boolean setOrder = field.getOrderColumnIO().isInsertable(order, false); for (Iterator itr = add.iterator(); itr.hasNext(); seq++) { diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyTableFieldStrategy.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyTableFieldStrategy.java index 4cf621b51..9736d58ca 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyTableFieldStrategy.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyTableFieldStrategy.java @@ -186,8 +186,12 @@ public abstract class RelationToManyTableFieldStrategy ct = proxy.getChangeTracker(); } - // if no fine-grained change tracking then just delete and reinsert - if (ct == null || !ct.isTracking()) { + Column order = field.getOrderColumn(); + + // if no fine-grained change tracking or if an item was removed + // from an ordered collection, delete and reinsert + if (ct == null || !ct.isTracking() || + (order != null && !ct.getRemoved().isEmpty())) { delete(sm, store, rm); insert(sm, rm, obj); return; @@ -220,7 +224,6 @@ public abstract class RelationToManyTableFieldStrategy field.getJoinColumnIO(), sm); int seq = ct.getNextSequence(); - Column order = field.getOrderColumn(); boolean setOrder = field.getOrderColumnIO().isInsertable(order, false); for (Iterator itr = add.iterator(); itr.hasNext(); seq++) { diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/order/TestOrderColumn.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/order/TestOrderColumn.java index ce7b6a36a..c1378a91f 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/order/TestOrderColumn.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/order/TestOrderColumn.java @@ -74,6 +74,266 @@ public class TestOrderColumn extends SingleEMFTestCase { } } + /* + * Verifies that a collection remains contiguous and element + * indexes are reordered if an element is removed for a + * OneToMany relationship + */ + public void testOneToManyElementRemoval() { + OpenJPAEntityManagerSPI em = emf.createEntityManager(); + + // Verify field name is the default via fm + validateOrderColumnName(BattingOrder.class, "batters", + "batters_ORDER");// "batters_ORDER"); + + // Create some data + Player[] players = new Player[10]; + ArrayList playersArr = new ArrayList(); + em.getTransaction().begin(); + for (int i = 0; i < 10 ; i++) { + players[i] = new Player("Player" + i, i+100); + em.persist(players[i]); + playersArr.add(players[i]); + } + em.getTransaction().commitAndResume(); + + // Persist the related entities + BattingOrder order = new BattingOrder(); + order.setBatters(playersArr); + em.persist(order); + em.getTransaction().commit(); + em.refresh(order); + em.clear(); + + // Verify order is correct. + BattingOrder newOrder = em.find(BattingOrder.class, order.id); + assertNotNull(newOrder); + for (int i = 0; i < 10 ; i++) { + assertEquals(newOrder.getBatters().get(i), (players[i])); + } + + // Remove some items + em.getTransaction().begin(); + newOrder.getBatters().remove(1); + playersArr.remove(1); + newOrder.getBatters().remove(5); + playersArr.remove(5); + em.getTransaction().commit(); + em.clear(); + + // Simple assertion via find + newOrder = em.find(BattingOrder.class, order.id); + assertNotNull(newOrder); + assertNotNull(newOrder.getBatters()); + assertEquals(playersArr.size(), newOrder.getBatters().size()); + for (int i = 0; i < playersArr.size() ; i++) { + assertEquals(newOrder.getBatters().get(i), (playersArr.get(i))); + } + + // Stronger assertion via INDEX value + validateIndexAndValues(em, "BattingOrder", "batters", 0, + playersArr.toArray(), "id", + order.id); + + em.close(); + } + + /* + * Verifies that a collection remains contiguous and element + * indexes are reordered if an element is removed for an + * ElementCollection + */ + public void testElementCollectionElementRemoval() { + OpenJPAEntityManagerSPI em = emf.createEntityManager(); + Game game = new Game(); + + // Verify field name is the default via fm + validateOrderColumnName(Game.class, "rainDates", + "dateOrder"); + + // Create a list of basic types + java.sql.Date dates[] = new java.sql.Date[10]; + ArrayList rainDates = new ArrayList(10); + Calendar today = Calendar.getInstance(); + for (int i = 0; i < 10; i++) { + today.set(2009, 1, i+1); + dates[i] = new java.sql.Date(today.getTimeInMillis()); + } + // Add in reverse order + for (int i = 9; i >= 0; i--) { + rainDates.add(dates[i]); + } + game.setRainDates(rainDates); + + em.getTransaction().begin(); + em.persist(game); + em.getTransaction().commit(); + + em.clear(); + + Game newGame = em.find(Game.class, game.getId()); + assertNotNull(newGame); + // Verify the order + for (int i = 0; i < 10; i++) { + assertEquals(game.getRainDates().get(i), + rainDates.get(i)); + } + + // Remove some dates + em.getTransaction().begin(); + game.getRainDates().remove(4); + rainDates.remove(4); + game.getRainDates().remove(2); + rainDates.remove(2); + em.getTransaction().commit(); + em.clear(); + + newGame = em.find(Game.class, game.getId()); + assertNotNull(newGame); + assertNotNull(game.getRainDates()); + assertEquals(8, game.getRainDates().size()); + // Verify the order + for (int i = 0; i < game.getRainDates().size(); i++) { + assertEquals(game.getRainDates().get(i), + rainDates.get(i)); + } + + em.close(); + } + /* + * Verifies that a collection remains contiguous and element + * indexes are reordered if an element is inserted into the collection. + */ + public void testOneToManyElementInsert() { + OpenJPAEntityManagerSPI em = emf.createEntityManager(); + + // Verify field name is the default via fm + validateOrderColumnName(BattingOrder.class, "batters", + "batters_ORDER");// "batters_ORDER"); + + // Create some data + Player[] players = new Player[10]; + ArrayList playersArr = new ArrayList(); + em.getTransaction().begin(); + for (int i = 0; i < 10 ; i++) { + players[i] = new Player("Player" + i, i+100); + em.persist(players[i]); + playersArr.add(players[i]); + } + em.getTransaction().commitAndResume(); + + // Persist the related entities + BattingOrder order = new BattingOrder(); + order.setBatters(playersArr); + em.persist(order); + em.getTransaction().commitAndResume(); + em.refresh(order); + + em.getTransaction().commit(); + em.clear(); + + // Verify order is correct. + BattingOrder newOrder = em.find(BattingOrder.class, order.id); + assertNotNull(newOrder); + for (int i = 0; i < 10 ; i++) { + assertEquals(newOrder.getBatters().get(i), (players[i])); + } + + Player p = new Player("PlayerNew", 150); + playersArr.add(2, p); + + Player p2 = new Player("PlayerNew2", 151); + playersArr.add(p2); + // Add an item at index 2 and at the end of the list + em.getTransaction().begin(); + newOrder.getBatters().add(2, p); + newOrder.getBatters().add(p2); + em.getTransaction().commit(); + em.clear(); + + // Simple assertion via find + newOrder = em.find(BattingOrder.class, order.id); + assertNotNull(newOrder); + assertNotNull(newOrder.getBatters()); + assertEquals(playersArr.size(), newOrder.getBatters().size()); + for (int i = 0; i < playersArr.size() ; i++) { + assertEquals(newOrder.getBatters().get(i), (playersArr.get(i))); + } + + // Stronger assertion via INDEX value + validateIndexAndValues(em, "BattingOrder", "batters", 0, + playersArr.toArray(), "id", + order.id); + + em.close(); + } + /* + * Verifies that a collection remains contiguous and element + * indexes are reordered if an element is inserted into an + * ElementCollection + */ + public void testElementCollectionElementInsert() { + OpenJPAEntityManagerSPI em = emf.createEntityManager(); + Game game = new Game(); + + // Verify field name is the default via fm + validateOrderColumnName(Game.class, "rainDates", + "dateOrder"); + + // Create a list of basic types + java.sql.Date dates[] = new java.sql.Date[10]; + ArrayList rainDates = new ArrayList(10); + Calendar today = Calendar.getInstance(); + for (int i = 0; i < 10; i++) { + today.set(2009, 1, i+1); + dates[i] = new java.sql.Date(today.getTimeInMillis()); + } + // Add in reverse order + for (int i = 9; i >= 0; i--) { + rainDates.add(dates[i]); + } + game.setRainDates(rainDates); + + em.getTransaction().begin(); + em.persist(game); + em.getTransaction().commit(); + + em.clear(); + + Game newGame = em.find(Game.class, game.getId()); + assertNotNull(newGame); + // Verify the order + for (int i = 0; i < 10; i++) { + assertEquals(game.getRainDates().get(i), + rainDates.get(i)); + } + + // Add some dates + today.set(2009, 1, 15); + rainDates.add(1, new java.sql.Date(today.getTimeInMillis())); + today.set(2009, 1, 20); + rainDates.add(6, new java.sql.Date(today.getTimeInMillis())); + + em.getTransaction().begin(); + game.getRainDates().add(1, rainDates.get(1)); + game.getRainDates().add(6, rainDates.get(6)); + em.getTransaction().commit(); + em.clear(); + + newGame = em.find(Game.class, game.getId()); + assertNotNull(newGame); + assertNotNull(game.getRainDates()); + assertEquals(12, game.getRainDates().size()); + // Verify the order + for (int i = 0; i < game.getRainDates().size(); i++) { + assertEquals(game.getRainDates().get(i), + rainDates.get(i)); + } + + em.close(); + } + + /* * Validates use of OrderColumn with OneToMany using the default * order column name @@ -851,7 +1111,7 @@ public class TestOrderColumn extends SingleEMFTestCase { List rlist = qry.getResultList(); assertNotNull(rlist); - assertEquals(rlist.size(), objs.length); + assertEquals(objs.length, rlist.size()); TreeMap objMap = new TreeMap(); for (int i = 0; i < objs.length; i++) {