From 0da764ed0c5e8c8407ffbee0f9875e92ee9e8b4a Mon Sep 17 00:00:00 2001 From: rbygrave Date: Wed, 13 May 2015 21:00:10 +1200 Subject: [PATCH] Test for #293 - Invalid result rows when ManyToMany "fetch joined" with additional child OneToMany or ManyToMany child relationship fetched --- .../java/com/avaje/tests/level/Level1.java | 56 ++++++ .../java/com/avaje/tests/level/Level2.java | 44 +++++ .../java/com/avaje/tests/level/Level3.java | 32 ++++ .../java/com/avaje/tests/level/Level4.java | 32 ++++ .../tests/level/test/ManyToManyTest.java | 162 ++++++++++++++++++ 5 files changed, 326 insertions(+) create mode 100644 src/test/java/com/avaje/tests/level/Level1.java create mode 100644 src/test/java/com/avaje/tests/level/Level2.java create mode 100644 src/test/java/com/avaje/tests/level/Level3.java create mode 100644 src/test/java/com/avaje/tests/level/Level4.java create mode 100644 src/test/java/com/avaje/tests/level/test/ManyToManyTest.java diff --git a/src/test/java/com/avaje/tests/level/Level1.java b/src/test/java/com/avaje/tests/level/Level1.java new file mode 100644 index 000000000..b01bac056 --- /dev/null +++ b/src/test/java/com/avaje/tests/level/Level1.java @@ -0,0 +1,56 @@ +package com.avaje.tests.level; + +import javax.persistence.*; +import java.util.Set; + +@Entity +public class Level1 { + @Id + Long id; + + @ManyToMany(cascade = CascadeType.ALL) + @JoinTable( + name = "level1_level4", + joinColumns = @JoinColumn(name = "level1_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "level4_id", referencedColumnName = "id") + ) + Set level4s; + + @ManyToMany(cascade = CascadeType.ALL) + @JoinTable( + name = "level1_level2", + joinColumns = @JoinColumn(name = "level1_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "level2_id", referencedColumnName = "id") + ) + Set level2s; + + String name; + + public Level1(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Set getLevel2s() { + return level2s; + } + + public Set getLevel4s() { + return level4s; + } +} diff --git a/src/test/java/com/avaje/tests/level/Level2.java b/src/test/java/com/avaje/tests/level/Level2.java new file mode 100644 index 000000000..2b3eda913 --- /dev/null +++ b/src/test/java/com/avaje/tests/level/Level2.java @@ -0,0 +1,44 @@ +package com.avaje.tests.level; + +import javax.persistence.*; +import java.util.List; + +@Entity +public class Level2 { + @Id + Long id; + + @ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) + @JoinTable( + name = "level2_level3", + joinColumns = @JoinColumn(name = "level2_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "level3_id", referencedColumnName = "id") + ) + List level3s; + + String name; + + public Level2(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public List getLevel3s() { + return level3s; + } +} diff --git a/src/test/java/com/avaje/tests/level/Level3.java b/src/test/java/com/avaje/tests/level/Level3.java new file mode 100644 index 000000000..74a38fa74 --- /dev/null +++ b/src/test/java/com/avaje/tests/level/Level3.java @@ -0,0 +1,32 @@ +package com.avaje.tests.level; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class Level3 { + @Id + Long id; + + String name; + + public Level3(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/src/test/java/com/avaje/tests/level/Level4.java b/src/test/java/com/avaje/tests/level/Level4.java new file mode 100644 index 000000000..72f2c306d --- /dev/null +++ b/src/test/java/com/avaje/tests/level/Level4.java @@ -0,0 +1,32 @@ +package com.avaje.tests.level; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class Level4 { + @Id + Long id; + + String name; + + public Level4(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/src/test/java/com/avaje/tests/level/test/ManyToManyTest.java b/src/test/java/com/avaje/tests/level/test/ManyToManyTest.java new file mode 100644 index 000000000..08a34f022 --- /dev/null +++ b/src/test/java/com/avaje/tests/level/test/ManyToManyTest.java @@ -0,0 +1,162 @@ +package com.avaje.tests.level.test; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.tests.level.Level1; +import com.avaje.tests.level.Level2; +import com.avaje.tests.level.Level3; +import com.avaje.tests.level.Level4; +import org.junit.Test; + +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class ManyToManyTest extends BaseTestCase { + + @Test + public void test() { + + Ebean.beginTransaction(); + try { + Level4 i = new Level4("i"); + Level4 ii = new Level4("ii"); + Level4 iii = new Level4("iii"); + + Ebean.save(i); + Ebean.save(ii); + Ebean.save(iii); + + Level3 a = new Level3("a"); + Level3 b = new Level3("b"); + + Ebean.save(a); + Ebean.save(b); + + Level2 one = new Level2("one"); + Level2 two = new Level2("two"); + + Ebean.save(one); + Ebean.save(two); + + Level1 x1 = new Level1("x1"); + Level1 x2 = new Level1("x2"); + Level1 x3 = new Level1("x3"); + Level1 x4 = new Level1("x4"); + Level1 x5 = new Level1("x5"); + + x1.getLevel2s().add(one); + x2.getLevel2s().add(one); + x3.getLevel2s().add(two); + x4.getLevel2s().add(two); + x5.getLevel2s().add(two); + + x1.getLevel4s().add(i); + x1.getLevel4s().add(ii); + x2.getLevel4s().add(ii); + x2.getLevel4s().add(iii); + + Ebean.save(x1); + Ebean.save(x2); + Ebean.save(x3); + Ebean.save(x4); + Ebean.save(x5); + + // this query had the original problem + List things = Ebean.find(Level1.class) + .fetch("level4s") + .fetch("level2s") + .fetch("level2s.level3s") + .order().asc("id") + .findList(); + + validateObjectGraph(i, ii, iii, one, two, x1, x2, x3, x4, x5, things); + + + things = Ebean.find(Level1.class) + .fetch("level2s") + .fetch("level2s.level3s") + .fetch("level4s") + .order().asc("id") + .findList(); + + validateObjectGraph(i, ii, iii, one, two, x1, x2, x3, x4, x5, things); + + + things = Ebean.find(Level1.class) + .fetch("level2s") + .fetch("level4s") + .order().asc("id") + .findList(); + + validateObjectGraph(i, ii, iii, one, two, x1, x2, x3, x4, x5, things); + + } finally { + Ebean.endTransaction(); + } + } + + private void validateObjectGraph(Level4 i, Level4 ii, Level4 iii, Level2 one, Level2 two, Level1 x1, Level1 x2, Level1 x3, Level1 x4, Level1 x5, List things) { + + assertEquals(5, things.size()); + + // x1's are in order expected + assertEquals(x1.getId(), things.get(0).getId()); + assertEquals(x2.getId(), things.get(1).getId()); + assertEquals(x3.getId(), things.get(2).getId()); + assertEquals(x4.getId(), things.get(3).getId()); + assertEquals(x5.getId(), things.get(4).getId()); + + // x1 to level4s + assertEquals(2, things.get(0).getLevel4s().size()); + assertTrue(contains(things.get(0).getLevel4s(), i)); + assertTrue(contains(things.get(0).getLevel4s(), ii)); + + // x2 to level4s + assertEquals(2, things.get(1).getLevel4s().size()); + assertTrue(contains(things.get(1).getLevel4s(), ii)); + assertTrue(contains(things.get(1).getLevel4s(), iii)); + + // x3 etc to level4s + assertEquals(0, things.get(2).getLevel4s().size()); + assertEquals(0, things.get(3).getLevel4s().size()); + assertEquals(0, things.get(4).getLevel4s().size()); + + // level2 relationships + for (final Level1 curThing : things) { + assertEquals(1, curThing.getLevel2s().size()); + } + assertTrue(contains(things.get(0).getLevel2s(), one)); + assertTrue(contains(things.get(1).getLevel2s(), one)); + assertTrue(contains(things.get(2).getLevel2s(), two)); + assertTrue(contains(things.get(3).getLevel2s(), two)); + assertTrue(contains(things.get(4).getLevel2s(), two)); + } + + /** + * Return true if match exists in level4s. + */ + private boolean contains(Set level4s, Level4 match) { + for (Level4 level4 : level4s) { + if (level4.getId().equals(match.getId()) && level4.getName().equals(match.getName())) { + return true; + } + } + return false; + } + + /** + * Return true if match exists in level2s. + */ + private boolean contains(Set level2s, Level2 match) { + for (Level2 level2 : level2s) { + if (level2.getId().equals(match.getId()) && level2.getName().equals(match.getName())) { + return true; + } + } + return false; + } + +}