Test for #293 - Invalid result rows when ManyToMany "fetch joined" with additional child OneToMany or ManyToMany child relationship fetched

This commit is contained in:
rbygrave
2015-05-13 21:00:10 +12:00
parent 43003f4a3a
commit 0da764ed0c
5 changed files with 326 additions and 0 deletions
@@ -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<Level4> level4s;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(
name = "level1_level2",
joinColumns = @JoinColumn(name = "level1_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "level2_id", referencedColumnName = "id")
)
Set<Level2> 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<Level2> getLevel2s() {
return level2s;
}
public Set<Level4> getLevel4s() {
return level4s;
}
}
@@ -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<Level3> 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<Level3> getLevel3s() {
return level3s;
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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<Level1> 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<Level1> 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<Level4> 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<Level2> level2s, Level2 match) {
for (Level2 level2 : level2s) {
if (level2.getId().equals(match.getId()) && level2.getName().equals(match.getName())) {
return true;
}
}
return false;
}
}