diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml
index 5575c187b..8e3d4b068 100644
--- a/ebean-core/pom.xml
+++ b/ebean-core/pom.xml
@@ -72,7 +72,7 @@
io.ebean
ebean-ddl-generator
- 12.9.0
+ 12.9.3a
test
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/LoadManyRequest.java b/ebean-core/src/main/java/io/ebeaninternal/api/LoadManyRequest.java
index aeb9ff7dd..0343b8579 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/api/LoadManyRequest.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/api/LoadManyRequest.java
@@ -119,7 +119,7 @@ public class LoadManyRequest extends LoadRequest {
if (extraWhere != null) {
// replace special ${ta} placeholder with the base table alias
// which is always t0 and add the extra where clause
- query.where().raw(extraWhere.replace("${ta}", "t0"));
+ query.where().raw(extraWhere.replace("${ta}", "t0").replace("${mta}", "int_"));
}
query.setLazyLoadForParents(many);
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java
index 326cbae00..9b8e7768c 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java
@@ -2009,6 +2009,13 @@ public class BeanDescriptor implements BeanType, STreeType, SpiBeanType {
return owner.getBeanDescriptor(otherType);
}
+ /**
+ * Returns true, if the table is managed (i.e. an existing m2m relation).
+ */
+ public boolean isTableManaged(String tableName) {
+ return owner.isTableManaged(tableName);
+ }
+
/**
* Return the order column property.
*/
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java
index b660c1718..84b412513 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java
@@ -445,6 +445,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTypeMana
return tableToDescMap.get(tableName.toLowerCase());
}
+ @Override
+ public boolean isTableManaged(String tableName) {
+ return tableToDescMap.get(tableName.toLowerCase()) != null
+ || tableToViewDescMap.get(tableName.toLowerCase()) != null;
+ }
+
/**
* Invalidate entity beans based on views via their dependent tables.
*/
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorMap.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorMap.java
index 7c7dd6e0c..94a9fded9 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorMap.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorMap.java
@@ -76,4 +76,10 @@ public interface BeanDescriptorMap {
* Return true if Jackson core is present on the classpath.
*/
boolean isJacksonCorePresent();
+
+ /**
+ * Returns true, if the given table (or view) is managed by ebean
+ * (= an entity exists)
+ */
+ boolean isTableManaged(String tableName);
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java
index 8e1ac1a14..c823e9a7e 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java
@@ -439,6 +439,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST
@Override
public String getAssocIsEmpty(SpiExpressionRequest request, String path) {
boolean softDelete = targetDescriptor.isSoftDelete();
+ boolean needsX2Table = softDelete || getExtraWhere() != null;
StringBuilder sb = new StringBuilder(50);
SpiQuery> query = request.getQueryRequest().getQuery();
if (hasJoinTable()) {
@@ -446,7 +447,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST
} else {
sb.append(targetDescriptor.getBaseTable(query.getTemporalMode()));
}
- if (softDelete && hasJoinTable()) {
+ if (needsX2Table && hasJoinTable()) {
sb.append(" x join ");
sb.append(targetDescriptor.getBaseTable(query.getTemporalMode()));
sb.append(" x2 on ");
@@ -461,6 +462,16 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST
}
exportedProperties[i].appendWhere(sb, "x.", path);
}
+ if (getExtraWhere() != null) {
+ sb.append(" and ");
+ if (hasJoinTable()) {
+ sb.append(getExtraWhere().replace("${ta}", "x2").replace("${mta}", "x"));
+ } else {
+ sb.append(getExtraWhere().replace("${ta}", "x"));
+ }
+ }
+
+
if (softDelete) {
String alias = hasJoinTable() ? "x2" : "x";
sb.append(" and ").append(targetDescriptor.getSoftDeletePredicate(alias));
@@ -1061,4 +1072,16 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST
public void bindElementValue(SqlUpdate insert, Object value) {
targetDescriptor.bindElementValue(insert, value);
}
+
+ /**
+ * Returns true, if we must create a m2m join table.
+ */
+ public boolean createJoinTable() {
+ if (hasJoinTable() && getMappedBy() == null) {
+ // only create on other 'owning' side
+ return !descriptor.isTableManaged(intersectionJoin.getTable());
+ } else {
+ return false;
+ }
+ }
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/DbSqlContext.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/DbSqlContext.java
index 257893820..1e078c0fd 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/DbSqlContext.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/DbSqlContext.java
@@ -10,7 +10,7 @@ public interface DbSqlContext {
/**
* Add a join to the sql query.
*/
- void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2);
+ void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2, String extraWhere);
/**
* Push the current table alias onto the stack.
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/TableJoin.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/TableJoin.java
index e88e56034..8c1624d05 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/TableJoin.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/TableJoin.java
@@ -34,6 +34,8 @@ public final class TableJoin {
private final int queryHash;
private final PropertyForeignKey foreignKey;
+
+ private final String extraWhere;
public TableJoin(DeployTableJoin deploy) {
this(deploy, null);
@@ -44,6 +46,7 @@ public final class TableJoin {
*/
public TableJoin(DeployTableJoin deploy, PropertyForeignKey foreignKey) {
this.foreignKey = foreignKey;
+ this.extraWhere = deploy.getExtraWhere();
this.table = InternString.intern(deploy.getTable());
this.type = deploy.getType();
this.inheritInfo = deploy.getInheritInfo();
@@ -57,6 +60,7 @@ public final class TableJoin {
private TableJoin(TableJoin source, String overrideColumn) {
this.foreignKey = null;
+ this.extraWhere = source.extraWhere;
this.table = source.table;
this.type = source.type;
this.inheritInfo = source.inheritInfo;
@@ -146,7 +150,7 @@ public final class TableJoin {
public SqlJoinType addJoin(SqlJoinType joinType, String a1, String a2, DbSqlContext ctx) {
String joinLiteral = joinType.getLiteral(type);
- ctx.addJoin(joinLiteral, table, columns(), a1, a2);
+ ctx.addJoin(joinLiteral, table, columns(), a1, a2, extraWhere);
return joinType.autoToOuter(type);
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java
index 472a71db3..be4b77aa0 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java
@@ -86,6 +86,7 @@ public abstract class DeployBeanPropertyAssoc extends DeployBeanProperty {
* collection.
*/
public void setExtraWhere(String extraWhere) {
+ this.tableJoin.setExtraWhere(extraWhere);
this.extraWhere = extraWhere;
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployTableJoin.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployTableJoin.java
index d24024774..c3451f9f4 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployTableJoin.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployTableJoin.java
@@ -33,6 +33,8 @@ public class DeployTableJoin {
private ArrayList columns = new ArrayList<>(4);
private InheritInfo inheritInfo;
+
+ private String extraWhere;
/**
* Create a DeployTableJoin.
@@ -137,6 +139,18 @@ public class DeployTableJoin {
this.type = type;
}
+ /**
+ * Returns the clause of an extra @Where annotation.
+ * @return
+ */
+ public String getExtraWhere() {
+ return extraWhere;
+ }
+
+ public void setExtraWhere(String extraWhere) {
+ this.extraWhere = extraWhere;
+ }
+
public DeployTableJoin createInverse(String tableName) {
DeployTableJoin inverse = new DeployTableJoin();
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java
index b47612d65..1ca59dbc8 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java
@@ -118,7 +118,7 @@ class AnnotationAssocManys extends AnnotationAssoc {
Where where = prop.getMetaAnnotationWhere(platform);
if (where != null) {
- prop.setExtraWhere(where.clause());
+ prop.setExtraWhere(processFormula(where.clause()));
}
FetchPreference fetchPreference = get(prop, FetchPreference.class);
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java
index b5929d6a0..36fa99b57 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java
@@ -96,7 +96,7 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
Where where = prop.getMetaAnnotationWhere(platform);
if (where != null) {
// not expecting this to be used on assoc one properties
- prop.setExtraWhere(where.clause());
+ prop.setExtraWhere(processFormula(where.clause()));
}
PrimaryKeyJoinColumn primaryKeyJoin = get(prop, PrimaryKeyJoinColumn.class);
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFields.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFields.java
index 8079f156f..5a0caa287 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFields.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFields.java
@@ -153,7 +153,7 @@ public class AnnotationFields extends AnnotationParser {
Formula formula = prop.getMetaAnnotationFormula(platform);
if (formula != null) {
- prop.setSqlFormula(formula.select(), formula.join());
+ prop.setSqlFormula(processFormula(formula.select()), processFormula(formula.join()));
}
initWhoProperties(prop);
@@ -334,7 +334,7 @@ public class AnnotationFields extends AnnotationParser {
}
Formula formula = prop.getMetaAnnotationFormula(platform);
if (formula != null) {
- prop.setSqlFormula(formula.select(), formula.join());
+ prop.setSqlFormula(processFormula(formula.select()), processFormula(formula.join()));
}
final Aggregation aggregation = prop.getMetaAnnotation(Aggregation.class);
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationParser.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationParser.java
index af015e1f5..ea880a18b 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationParser.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationParser.java
@@ -129,4 +129,11 @@ public abstract class AnnotationParser extends AnnotationBase {
}
return columnNames;
}
+
+ /**
+ * Process any formula from @Formula or @Where.
+ */
+ protected String processFormula(String source) {
+ return source == null ? null : source.replace("${dbTableName}", descriptor.getBaseTable());
+ }
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultDbSqlContext.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultDbSqlContext.java
index 981abae74..9ee2c253c 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultDbSqlContext.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultDbSqlContext.java
@@ -111,7 +111,7 @@ class DefaultDbSqlContext implements DbSqlContext {
}
@Override
- public void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2) {
+ public void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2, String extraWhere) {
if (tableJoins == null) {
tableJoins = new HashSet<>();
@@ -164,6 +164,14 @@ class DefaultDbSqlContext implements DbSqlContext {
if (addAsOfOnClause) {
sb.append(" and ").append(historySupport.getAsOfPredicate(a2));
}
+
+ if (extraWhere != null && !extraWhere.isEmpty()) {
+ sb.append(" and ");
+ // we will also need a many-table alias here
+ sb.append(extraWhere.replace(tableAliasPlaceHolder, a2).replace("${mta}", a1));
+ }
+
+
}
private void appendTable(String table, String draftTable) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyRoot.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyRoot.java
index 8c5517a76..d920b57ef 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyRoot.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyRoot.java
@@ -47,16 +47,6 @@ final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
ctx.popTableAlias();
}
- /**
- * append extraWhere to the join.
- */
- @Override
- protected SqlJoinType appendFromAsJoin(DbSqlContext ctx, SqlJoinType joinType) {
- SqlJoinType join = super.appendFromAsJoin(ctx, joinType);
- super.appendExtraWhere(ctx);
- return join;
- }
-
@Override
protected void appendExtraWhere(DbSqlContext ctx) {
// extraWhere is already appended to the tableJoin
diff --git a/ebean-core/src/test/java/org/tests/model/m2m/MnyEdge.java b/ebean-core/src/test/java/org/tests/model/m2m/MnyEdge.java
new file mode 100644
index 000000000..2e86cb9df
--- /dev/null
+++ b/ebean-core/src/test/java/org/tests/model/m2m/MnyEdge.java
@@ -0,0 +1,57 @@
+package org.tests.model.m2m;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+
+import io.ebean.annotation.Index;
+
+@Entity
+@Index(unique = true, columnNames = { "from_id", "to_id" })
+@Index(unique = true, columnNames = { "to_id", "from_id" })
+public class MnyEdge {
+
+ @Id
+ private Integer id;
+
+ @ManyToOne
+ private MnyNode from;
+
+ @ManyToOne
+ private MnyNode to;
+
+ private int flags;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public MnyNode getFrom() {
+ return from;
+ }
+
+ public void setFrom(MnyNode from) {
+ this.from = from;
+ }
+
+ public MnyNode getTo() {
+ return to;
+ }
+
+ public void setTo(MnyNode to) {
+ this.to = to;
+ }
+
+ public int getFlags() {
+ return flags;
+ }
+
+ public void setFlags(int flags) {
+ this.flags = flags;
+ }
+
+}
diff --git a/ebean-core/src/test/java/org/tests/model/m2m/MnyNode.java b/ebean-core/src/test/java/org/tests/model/m2m/MnyNode.java
new file mode 100644
index 000000000..e51c5f417
--- /dev/null
+++ b/ebean-core/src/test/java/org/tests/model/m2m/MnyNode.java
@@ -0,0 +1,129 @@
+package org.tests.model.m2m;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+
+import io.ebean.annotation.Platform;
+import io.ebean.annotation.Where;
+
+import java.util.List;
+
+@Entity
+public class MnyNode {
+
+ @Id
+ Integer id;
+
+ String name;
+
+ @ManyToMany
+ @JoinTable(name = "mny_edge",
+ joinColumns = @JoinColumn(name = "from_id", referencedColumnName = "id"),
+ inverseJoinColumns = @JoinColumn(name = "to_id", referencedColumnName = "id"))
+ List allRelations;
+
+ @ManyToMany
+ @JoinTable(name = "mny_edge",
+ joinColumns = @JoinColumn(name = "to_id", referencedColumnName = "id"),
+ inverseJoinColumns = @JoinColumn(name = "from_id", referencedColumnName = "id"))
+ List allReverseRelations;
+
+ @ManyToMany
+ @JoinTable(name = "mny_edge",
+ joinColumns = @JoinColumn(name = "from_id", referencedColumnName = "id"),
+ inverseJoinColumns = @JoinColumn(name = "to_id", referencedColumnName = "id"))
+ @Where(clause = "${mta}.flags & 1 != 0")
+ @Where(clause = "BITAND(${mta}.flags, 1) != 0", platforms = Platform.H2)
+ List bit1Relations;
+
+ @ManyToMany
+ @JoinTable(name = "mny_edge",
+ joinColumns = @JoinColumn(name = "to_id", referencedColumnName = "id"),
+ inverseJoinColumns = @JoinColumn(name = "from_id", referencedColumnName = "id"))
+ @Where(clause = "${mta}.flags & 1 != 0")
+ @Where(clause = "BITAND(${mta}.flags, 1) != 0", platforms = Platform.H2)
+ List bit1ReverseRelations;
+
+ @ManyToMany
+ @JoinTable(name = "mny_edge",
+ joinColumns = @JoinColumn(name = "from_id", referencedColumnName = "id"),
+ inverseJoinColumns = @JoinColumn(name = "to_id", referencedColumnName = "id"))
+ @Where(clause = "${mta}.flags & 2 != 0")
+ @Where(clause = "BITAND(${mta}.flags, 2) != 0", platforms = Platform.H2)
+ List bit2Relations;
+
+ @ManyToMany
+ @JoinTable(name = "mny_edge",
+ joinColumns = @JoinColumn(name = "to_id", referencedColumnName = "id"),
+ inverseJoinColumns = @JoinColumn(name = "from_id", referencedColumnName = "id"))
+ @Where(clause = "${mta}.flags & 2 != 0")
+ @Where(clause = "BITAND(${mta}.flags, 2) != 0", platforms = Platform.H2)
+ List bit2ReverseRelations;
+
+ @ManyToMany
+ @JoinTable(name = "mny_edge",
+ joinColumns = @JoinColumn(name = "to_id", referencedColumnName = "id"),
+ inverseJoinColumns = @JoinColumn(name = "from_id", referencedColumnName = "id"))
+ @Where(clause = "'${dbTableName}' = ${ta}.name")
+ List withDbTableName;
+
+ public MnyNode() {
+
+ }
+
+ public MnyNode(String name) {
+ this.name = name;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List getAllRelations() {
+ return allRelations;
+ }
+
+ public List getAllReverseRelations() {
+ return allReverseRelations;
+ }
+
+ public List getBit1Relations() {
+ return bit1Relations;
+ }
+
+ public List getBit1ReverseRelations() {
+ return bit1ReverseRelations;
+ }
+
+ public List getBit2Relations() {
+ return bit2Relations;
+ }
+
+ public List getBit2ReverseRelations() {
+ return bit2ReverseRelations;
+ }
+
+ public List getWithDbTableName() {
+ return withDbTableName;
+ }
+
+ public void setWithDbTableName(List withDbTableName) {
+ this.withDbTableName = withDbTableName;
+ }
+
+}
diff --git a/ebean-core/src/test/java/org/tests/model/m2m/TestM2MWithWhere.java b/ebean-core/src/test/java/org/tests/model/m2m/TestM2MWithWhere.java
new file mode 100644
index 000000000..da8d83dd5
--- /dev/null
+++ b/ebean-core/src/test/java/org/tests/model/m2m/TestM2MWithWhere.java
@@ -0,0 +1,159 @@
+package org.tests.model.m2m;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.List;
+
+import org.junit.Test;
+import org.tests.model.m2m.MnyEdge;
+import org.tests.model.m2m.MnyNode;
+
+import io.ebean.BaseTestCase;
+import io.ebean.DB;
+import io.ebeantest.LoggedSql;
+
+/**
+ * Tests M2M with complex where queries.
+ *
+ * @author Roland Praml, FOCONIS AG
+ *
+ */
+public class TestM2MWithWhere extends BaseTestCase {
+
+ @Test
+ public void testQuery() throws Exception {
+ createTestData();
+ MnyNode node = DB.find(MnyNode.class, 1);
+
+
+ List result = DB.find(MnyNode.class).where().eq("allRelations", node).findList();
+ assertThat(result).extracting(MnyNode::getId).containsExactly(1, 2, 3, 4, 5);
+
+ result = DB.find(MnyNode.class).where().eq("allReverseRelations", node).findList();
+ assertThat(result).extracting(MnyNode::getId).containsExactly(1, 2, 3, 4, 5);
+
+ result = DB.find(MnyNode.class).where().eq("bit1Relations", node).findList();
+ assertThat(result).isEmpty(); // -> to = 1 column: 2 0 2 0 2
+
+ result = DB.find(MnyNode.class).where().eq("bit2Relations", node).findList();
+ assertThat(result).extracting(MnyNode::getId).containsExactly(1, 3, 5);
+
+ result = DB.find(MnyNode.class).where().eq("bit1ReverseRelations", node).findList();
+ // -> from = 1 column: 2 1 3 1 3
+ assertThat(result).extracting(MnyNode::getId).containsExactly(2, 3, 4, 5);
+
+ result = DB.find(MnyNode.class).where().eq("bit2ReverseRelations", node).findList();
+ assertThat(result).hasSize(3).extracting(MnyNode::getId).containsExactly(1, 3, 5);
+
+ result = DB.find(MnyNode.class).where().eq("bit2ReverseRelations", node).findList();
+ assertThat(result).hasSize(3).extracting(MnyNode::getId).containsExactly(1, 3, 5);
+ }
+
+ @Test
+ public void testGetter() throws Exception {
+ createTestData();
+ MnyNode node = DB.find(MnyNode.class, 3);
+
+ assertThat(node.getAllRelations()).extracting(MnyNode::getId).containsExactly(1, 2, 3, 4, 5);
+
+ assertThat(node.getAllReverseRelations()).extracting(MnyNode::getId).containsExactly(1, 2, 3, 4, 5);
+
+ assertThat(node.getBit1Relations()).extracting(MnyNode::getId).containsExactly(4, 5);
+
+ assertThat(node.getBit1ReverseRelations()).extracting(MnyNode::getId).containsExactly(1, 2);
+
+ assertThat(node.getBit2Relations()).extracting(MnyNode::getId).containsExactly(1, 3, 5);
+
+ LoggedSql.start();
+ assertThat(node.getBit2ReverseRelations()).extracting(MnyNode::getId).containsExactly(1, 3, 5);
+ List sqls = LoggedSql.stop();
+ assertThat(sqls).hasSize(1); // lazy load
+
+ // prefetch everything
+ LoggedSql.start();
+ node = DB.find(MnyNode.class)
+ .fetch("bit1Relations","*")
+ .fetch("bit1ReverseRelations","*")
+ .where().idEq(3).findOne();
+ sqls = LoggedSql.stop();
+ assertThat(sqls).hasSize(2);
+
+ // no lazyLoad expected
+ LoggedSql.start();
+ assertThat(node.getBit1Relations()).extracting(MnyNode::getId).containsExactly(4, 5);
+ assertThat(node.getBit1ReverseRelations()).extracting(MnyNode::getId).containsExactly(1, 2);
+ sqls = LoggedSql.stop();
+ assertThat(sqls).hasSize(0);
+
+ }
+
+ // to = | 1 2 3 4 5
+ // ---------+---------------
+ // from = 1 | 2 1 3 1 3
+ // from = 2 | 0 2 1 3 1
+ // from = 3 | 2 0 2 1 3
+ // from = 4 | 0 2 0 2 1
+ // from = 5 | 2 0 2 0 2
+ private void createTestData() {
+ DB.find(MnyEdge.class).delete();
+ DB.find(MnyNode.class).delete();
+ for (int i = 1; i <= 5; i++) {
+ MnyNode node = new MnyNode();
+ node.setId(i);
+ node.setName("Node #" + i);
+ DB.save(node);
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int from = 1; from <= 5; from++) {
+ sb.append("from = ").append(from).append(" |");
+ for (int to = 1; to <= 5; to++) {
+ MnyEdge edge = new MnyEdge();
+ edge.setFrom(DB.getReference(MnyNode.class, from));
+ edge.setTo(DB.getReference(MnyNode.class, to));
+ int flags = 0;
+ if (from < to) {
+ flags |= 1;
+ }
+ if ((from + to) % 2 == 0) {
+ flags |= 2;
+ }
+ edge.setFlags(flags);
+ DB.save(edge);
+ sb.append(" ").append(flags);
+ }
+ sb.append('\n');
+ }
+ // System.out.println(sb); dump the table
+ }
+
+
+ @Test
+ public void testWithDbTableName() {
+ LoggedSql.start();
+ DB.find(MnyNode.class).where().isNotNull("withDbTableName.name").findList();
+ List sql = LoggedSql.stop();
+ assertThat(sql).hasSize(1);
+ assertThat(sql.get(0)).contains("'mny_node' = u1.name");
+
+ LoggedSql.start();
+ DB.find(MnyNode.class).where().isNotEmpty("withDbTableName").findList();
+ sql = LoggedSql.stop();
+ assertThat(sql).hasSize(1);
+ assertThat(sql.get(0)).contains("'mny_node' = x2.name");
+ }
+
+ @Test
+ public void testLazyLoad() throws Exception {
+ MnyNode el = new MnyNode("testLazyLoad");
+ DB.save(el);
+ LoggedSql.start();
+ el = DB.find(MnyNode.class).select("name").where().eq("name", "testLazyLoad").findOne();
+ el.getWithDbTableName().size(); // trigger Lazy load
+ List sql = LoggedSql.stop();
+ assertThat(sql).hasSize(2);
+ assertThat(sql.get(0)).contains("select t0.id, t0.name from mny_node");
+ assertThat(sql.get(1)).contains("where 'mny_node' = t0.name");
+ DB.delete(el);
+ }
+
+}
diff --git a/ebean-core/src/test/java/org/tests/o2m/OmBasicParent.java b/ebean-core/src/test/java/org/tests/o2m/OmBasicParent.java
index 76faeca0e..15b117bd0 100644
--- a/ebean-core/src/test/java/org/tests/o2m/OmBasicParent.java
+++ b/ebean-core/src/test/java/org/tests/o2m/OmBasicParent.java
@@ -4,6 +4,9 @@ import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Version;
+
+import io.ebean.annotation.Where;
+
import java.util.List;
import static javax.persistence.CascadeType.ALL;
@@ -26,6 +29,10 @@ public class OmBasicParent {
@OneToMany(cascade = ALL, mappedBy = "parent")
private List extends OmBasicChild> children;
+ @OneToMany(cascade = ALL, mappedBy = "parent")
+ @Where(clause = "'${dbTableName}' = ${ta}.name")
+ private List extends OmBasicChild> childrenWithWhere;
+
public OmBasicParent(String name) {
this.name = name;
}
@@ -57,4 +64,13 @@ public class OmBasicParent {
public void setChildren(List extends OmBasicChild> children) {
this.children = children;
}
+
+ public List extends OmBasicChild> getChildrenWithWhere() {
+ return childrenWithWhere;
+ }
+
+ public void setChildrenWithWhere(List extends OmBasicChild> childrenWithWhere) {
+ this.childrenWithWhere = childrenWithWhere;
+ }
+
}
diff --git a/ebean-core/src/test/java/org/tests/o2m/TestOneToManyWhere.java b/ebean-core/src/test/java/org/tests/o2m/TestOneToManyWhere.java
new file mode 100644
index 000000000..e2dca664a
--- /dev/null
+++ b/ebean-core/src/test/java/org/tests/o2m/TestOneToManyWhere.java
@@ -0,0 +1,44 @@
+package org.tests.o2m;
+
+import io.ebean.BaseTestCase;
+import io.ebean.DB;
+import io.ebeantest.LoggedSql;
+
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class TestOneToManyWhere extends BaseTestCase {
+
+ @Test
+ public void testWithDbTableName() {
+ LoggedSql.start();
+ DB.find(OmBasicParent.class).where().isNotNull("childrenWithWhere.name").findList();
+ List sql = LoggedSql.stop();
+ assertThat(sql).hasSize(1);
+ assertThat(sql.get(0)).contains("'om_basic_parent' = u1.name");
+
+ LoggedSql.start();
+ DB.find(OmBasicParent.class).where().isNotEmpty("childrenWithWhere").findList();
+ sql = LoggedSql.stop();
+ assertThat(sql).hasSize(1);
+ assertThat(sql.get(0)).contains("'om_basic_parent' = x.name");
+ }
+
+ @Test
+ public void testLazyLoad() throws Exception {
+ OmBasicParent el = new OmBasicParent("testLazyLoad");
+ DB.save(el);
+ LoggedSql.start();
+ el = DB.find(OmBasicParent.class).select("name").where().eq("name", "testLazyLoad").findOne();
+ el.getChildrenWithWhere().size(); // trigger Lazy load
+ List sql = LoggedSql.stop();
+ assertThat(sql).hasSize(2);
+ assertThat(sql.get(0)).contains("select t0.id, t0.name from om_basic_parent");
+ assertThat(sql.get(1)).contains("where 'om_basic_parent' = t0.name");
+ DB.delete(el);
+ }
+
+}
diff --git a/ebean-core/src/test/java/org/tests/query/TestImplicitJoinOnParentRelationship.java b/ebean-core/src/test/java/org/tests/query/TestImplicitJoinOnParentRelationship.java
index c4aa242eb..eac3303fb 100644
--- a/ebean-core/src/test/java/org/tests/query/TestImplicitJoinOnParentRelationship.java
+++ b/ebean-core/src/test/java/org/tests/query/TestImplicitJoinOnParentRelationship.java
@@ -24,11 +24,11 @@ public class TestImplicitJoinOnParentRelationship extends BaseTestCase {
query.findList();
if (isPostgres()) {
- String expectedSql = "select distinct on (t0.id) t0.id, t0.name from o_customer t0 join o_order u1 on u1.kcustomer_id = t0.id join o_order_detail u2 on u2.order_id = u1.id join o_product u3 on u3.id = u2.product_id where u3.name = ?";
+ String expectedSql = "select distinct on (t0.id) t0.id, t0.name from o_customer t0 join o_order u1 on u1.kcustomer_id = t0.id and u1.order_date is not null join o_order_detail u2 on u2.order_id = u1.id and u2.id > 0 join o_product u3 on u3.id = u2.product_id where u3.name = ?";
assertThat(sqlOf(query, 1)).contains(expectedSql);
} else {
- String expectedSql = "select distinct t0.id, t0.name from o_customer t0 join o_order u1 on u1.kcustomer_id = t0.id join o_order_detail u2 on u2.order_id = u1.id join o_product u3 on u3.id = u2.product_id where u3.name = ?";
+ String expectedSql = "select distinct t0.id, t0.name from o_customer t0 join o_order u1 on u1.kcustomer_id = t0.id and u1.order_date is not null join o_order_detail u2 on u2.order_id = u1.id and u2.id > 0 join o_product u3 on u3.id = u2.product_id where u3.name = ?";
assertThat(sqlOf(query, 1)).contains(expectedSql);
}
@@ -55,10 +55,10 @@ public class TestImplicitJoinOnParentRelationship extends BaseTestCase {
query.findList();
if (isPostgres()) {
- String expectedSql = "select distinct on (t0.id) t0.id, t0.name from o_customer t0 left join o_order u1 on u1.kcustomer_id = t0.id left join o_order_detail u2 on u2.order_id = u1.id left join o_product u3 on u3.id = u2.product_id where (u3.name = ? or t0.id = ?)";
+ String expectedSql = "select distinct on (t0.id) t0.id, t0.name from o_customer t0 left join o_order u1 on u1.kcustomer_id = t0.id and u1.order_date is not null left join o_order_detail u2 on u2.order_id = u1.id and u2.id > 0 left join o_product u3 on u3.id = u2.product_id where (u3.name = ? or t0.id = ?)";
assertThat(sqlOf(query, 1)).contains(expectedSql);
} else {
- String expectedSql = "select distinct t0.id, t0.name from o_customer t0 left join o_order u1 on u1.kcustomer_id = t0.id left join o_order_detail u2 on u2.order_id = u1.id left join o_product u3 on u3.id = u2.product_id where (u3.name = ? or t0.id = ?)";
+ String expectedSql = "select distinct t0.id, t0.name from o_customer t0 left join o_order u1 on u1.kcustomer_id = t0.id and u1.order_date is not null left join o_order_detail u2 on u2.order_id = u1.id and u2.id > 0 left join o_product u3 on u3.id = u2.product_id where (u3.name = ? or t0.id = ?)";
assertThat(sqlOf(query, 1)).contains(expectedSql);
}
}
@@ -76,11 +76,11 @@ public class TestImplicitJoinOnParentRelationship extends BaseTestCase {
query.findList();
if (isPostgres()) {
- String expectedSql = "select distinct on (t0.id) t0.id, t0.name from o_customer t0 left join o_order u1 on u1.kcustomer_id = t0.id left join o_order_detail u2 on u2.order_id = u1.id left join o_product u3 on u3.id = u2.product_id where (u3.name = ? or t0.id = ?)";
+ String expectedSql = "select distinct on (t0.id) t0.id, t0.name from o_customer t0 left join o_order u1 on u1.kcustomer_id = t0.id and u1.order_date is not null left join o_order_detail u2 on u2.order_id = u1.id and u2.id > 0 left join o_product u3 on u3.id = u2.product_id where (u3.name = ? or t0.id = ?)";
assertThat(sqlOf(query, 1)).contains(expectedSql);
} else {
- String expectedSql = "select distinct t0.id, t0.name from o_customer t0 left join o_order u1 on u1.kcustomer_id = t0.id left join o_order_detail u2 on u2.order_id = u1.id left join o_product u3 on u3.id = u2.product_id where (u3.name = ? or t0.id = ?)";
+ String expectedSql = "select distinct t0.id, t0.name from o_customer t0 left join o_order u1 on u1.kcustomer_id = t0.id and u1.order_date is not null left join o_order_detail u2 on u2.order_id = u1.id and u2.id > 0 left join o_product u3 on u3.id = u2.product_id where (u3.name = ? or t0.id = ?)";
assertThat(sqlOf(query, 1)).contains(expectedSql);
}
}
diff --git a/ebean-core/src/test/java/org/tests/query/TestManyWhereJoin.java b/ebean-core/src/test/java/org/tests/query/TestManyWhereJoin.java
index 250a32709..b90751c83 100644
--- a/ebean-core/src/test/java/org/tests/query/TestManyWhereJoin.java
+++ b/ebean-core/src/test/java/org/tests/query/TestManyWhereJoin.java
@@ -41,7 +41,7 @@ public class TestManyWhereJoin extends BaseTestCase {
}
assertThat(sql).contains("join o_order ");
assertThat(sql).contains(".status = ?");
- assertThat(sql).contains("t0.id, t0.status from o_customer t0 join o_order u1 on u1.kcustomer_id = t0.id where u1.status = ?");
+ assertThat(sql).contains("t0.id, t0.status from o_customer t0 join o_order u1 on u1.kcustomer_id = t0.id and u1.order_date is not null where u1.status = ?");
}
@Test
diff --git a/ebean-core/src/test/java/org/tests/query/TestQueryFilterMany.java b/ebean-core/src/test/java/org/tests/query/TestQueryFilterMany.java
index be58fb8c4..01275cfa0 100644
--- a/ebean-core/src/test/java/org/tests/query/TestQueryFilterMany.java
+++ b/ebean-core/src/test/java/org/tests/query/TestQueryFilterMany.java
@@ -183,7 +183,7 @@ public class TestQueryFilterMany extends BaseTestCase {
List sqlList = LoggedSqlCollector.stop();
assertEquals(1, sqlList.size());
- assertThat(sqlList.get(0)).contains("from o_customer t0 left join o_order t1 on t1.kcustomer_id = t0.id and t1.order_date is not null left join o_customer t2 on t2.id = t1.kcustomer_id where exists (select 1 from o_order x where x.kcustomer_id = t0.id) and 1=0 order by t0.id");
+ assertThat(sqlList.get(0)).contains("from o_customer t0 left join o_order t1 on t1.kcustomer_id = t0.id and t1.order_date is not null left join o_customer t2 on t2.id = t1.kcustomer_id where exists (select 1 from o_order x where x.kcustomer_id = t0.id and x.order_date is not null) and 1=0 order by t0.id");
}
@Test
diff --git a/ebean-core/src/test/java/org/tests/query/TestQueryIsNull.java b/ebean-core/src/test/java/org/tests/query/TestQueryIsNull.java
index d2689337b..f80aee9f5 100644
--- a/ebean-core/src/test/java/org/tests/query/TestQueryIsNull.java
+++ b/ebean-core/src/test/java/org/tests/query/TestQueryIsNull.java
@@ -1,14 +1,15 @@
package org.tests.query;
import io.ebean.BaseTestCase;
-import io.ebean.Ebean;
+import io.ebean.DB;
import io.ebean.Query;
import org.tests.model.basic.Order;
import org.tests.model.basic.ResetBasicData;
import org.tests.model.m2m.Role;
import org.junit.Test;
-import static org.junit.Assert.assertTrue;
+
+import static org.assertj.core.api.Assertions.assertThat;
public class TestQueryIsNull extends BaseTestCase {
@@ -17,90 +18,90 @@ public class TestQueryIsNull extends BaseTestCase {
public void queryShouldContainIsNullOnColumn() {
ResetBasicData.reset();
- Query query = Ebean.find(Order.class).where().isNull("customerName").query();
+ Query query = DB.find(Order.class).where().isNull("customerName").query();
query.findList();
- assertTrue(query.getGeneratedSql().contains("name is null"));
+ assertThat(query.getGeneratedSql()).contains("name is null");
}
@Test
public void isNotNull_when_OneToMany_expect_existsSubquery() {
ResetBasicData.reset();
- Query query = Ebean.find(Order.class).where().isNotNull("details").query();
+ Query query = DB.find(Order.class).where().isNotNull("details").query();
query.findList();
- assertTrue(query.getGeneratedSql().contains(" where exists (select 1 from o_order_detail x where x.order_id = t0.id)"));
+ assertThat(query.getGeneratedSql()).contains(" where exists (select 1 from o_order_detail x where x.order_id = t0.id and x.id > 0)");
}
@Test
public void isNotEmpty_when_OneToMany_expect_existsSubquery() {
ResetBasicData.reset();
- Query query = Ebean.find(Order.class).where().isNotEmpty("details").query();
+ Query query = DB.find(Order.class).where().isNotEmpty("details").query();
query.findList();
- assertTrue(query.getGeneratedSql().contains(" where exists (select 1 from o_order_detail x where x.order_id = t0.id)"));
+ assertThat(query.getGeneratedSql()).contains(" where exists (select 1 from o_order_detail x where x.order_id = t0.id and x.id > 0)");
}
@Test
public void isNull_when_OneToMany_expect_notExistsSubquery() {
ResetBasicData.reset();
- Query query = Ebean.find(Order.class).where().isNull("details").query();
+ Query query = DB.find(Order.class).where().isNull("details").query();
query.findList();
- assertTrue(query.getGeneratedSql().contains(" where not exists (select 1 from o_order_detail x where x.order_id = t0.id)"));
+ assertThat(query.getGeneratedSql()).contains(" where not exists (select 1 from o_order_detail x where x.order_id = t0.id and x.id > 0)");
}
@Test
public void isEmpty_when_OneToMany_expect_notExistsSubquery() {
ResetBasicData.reset();
- Query query = Ebean.find(Order.class).where().isEmpty("details").query();
+ Query query = DB.find(Order.class).where().isEmpty("details").query();
query.findList();
- assertTrue(query.getGeneratedSql().contains(" where not exists (select 1 from o_order_detail x where x.order_id = t0.id)"));
+ assertThat(query.getGeneratedSql()).contains(" where not exists (select 1 from o_order_detail x where x.order_id = t0.id and x.id > 0)");
}
@Test
public void isEmpty_when_ManyToMany_expect_notExistsSubqueryAndNoJoin() {
ResetBasicData.reset();
- Query query = Ebean.find(Role.class).where().isEmpty("permissions").query();
+ Query query = DB.find(Role.class).where().isEmpty("permissions").query();
query.findList();
- assertTrue(query.getGeneratedSql().contains("from mt_role t0 where not exists (select 1 from mt_role_permission x where x.mt_role_id = t0.id)"));
+ assertThat(query.getGeneratedSql()).contains("from mt_role t0 where not exists (select 1 from mt_role_permission x where x.mt_role_id = t0.id)");
}
@Test
public void isNull_when_ManyToMany_expect_notExistsSubqueryAndNoJoin() {
ResetBasicData.reset();
- Query query = Ebean.find(Role.class).where().isNull("permissions").query();
+ Query query = DB.find(Role.class).where().isNull("permissions").query();
query.findList();
- assertTrue(query.getGeneratedSql().contains("from mt_role t0 where not exists (select 1 from mt_role_permission x where x.mt_role_id = t0.id)"));
+ assertThat(query.getGeneratedSql()).contains("from mt_role t0 where not exists (select 1 from mt_role_permission x where x.mt_role_id = t0.id)");
}
@Test
public void isNotEmpty_when_ManyToMany_expect_existsSubqueryAndNoJoin() {
ResetBasicData.reset();
- Query query = Ebean.find(Role.class).where().isNotEmpty("permissions").query();
+ Query query = DB.find(Role.class).where().isNotEmpty("permissions").query();
query.findList();
- assertTrue(query.getGeneratedSql().contains("from mt_role t0 where exists (select 1 from mt_role_permission x where x.mt_role_id = t0.id)"));
+ assertThat(query.getGeneratedSql()).contains("from mt_role t0 where exists (select 1 from mt_role_permission x where x.mt_role_id = t0.id)");
}
@Test
public void isNotNull_when_ManyToMany_expect_existsSubqueryAndNoJoin() {
ResetBasicData.reset();
- Query query = Ebean.find(Role.class).where().isNotNull("permissions").query();
+ Query query = DB.find(Role.class).where().isNotNull("permissions").query();
query.findList();
- assertTrue(query.getGeneratedSql().contains("from mt_role t0 where exists (select 1 from mt_role_permission x where x.mt_role_id = t0.id)"));
+ assertThat(query.getGeneratedSql()).contains("from mt_role t0 where exists (select 1 from mt_role_permission x where x.mt_role_id = t0.id)");
}
}
diff --git a/ebean-core/src/test/java/org/tests/query/other/TestQueryRawExpressionMany.java b/ebean-core/src/test/java/org/tests/query/other/TestQueryRawExpressionMany.java
index af52302d2..55901a720 100644
--- a/ebean-core/src/test/java/org/tests/query/other/TestQueryRawExpressionMany.java
+++ b/ebean-core/src/test/java/org/tests/query/other/TestQueryRawExpressionMany.java
@@ -30,6 +30,6 @@ public class TestQueryRawExpressionMany extends BaseTestCase {
query.findCount();
List sql = LoggedSqlCollector.stop();
- assertThat(trimSql(sql.get(0), 1)).contains("select count(*) from ( select distinct t0.id from o_order t0 left join o_order_detail t1 on t1.order_id = t0.id where t1.order_qty = ?)");
+ assertThat(trimSql(sql.get(0), 1)).contains("select count(*) from ( select distinct t0.id from o_order t0 left join o_order_detail t1 on t1.order_id = t0.id and t1.id > 0 where t1.order_qty = ?)");
}
}
diff --git a/ebean-core/src/test/java/org/tests/query/other/TestQueryRowCountWithMany.java b/ebean-core/src/test/java/org/tests/query/other/TestQueryRowCountWithMany.java
index a9d18476b..44c8a78f0 100644
--- a/ebean-core/src/test/java/org/tests/query/other/TestQueryRowCountWithMany.java
+++ b/ebean-core/src/test/java/org/tests/query/other/TestQueryRowCountWithMany.java
@@ -69,7 +69,7 @@ public class TestQueryRowCountWithMany extends BaseTestCase {
assertEquals(list.size(), rowCount);
assertEquals(2, sqlLogged.size());
assertThat(trimSql(sqlLogged.get(1), 1)).contains(
- "select count(*) from ( select distinct t0.id from o_order t0 join o_order_detail u1 on u1.order_id = t0.id where u1.product_id = ?)");
+ "select count(*) from ( select distinct t0.id from o_order t0 join o_order_detail u1 on u1.order_id = t0.id and u1.id > 0 where u1.product_id = ?)");
}
@@ -95,7 +95,7 @@ public class TestQueryRowCountWithMany extends BaseTestCase {
List sqlLogged = LoggedSqlCollector.stop();
assertEquals(1, sqlLogged.size());
- assertThat(trimSql(sqlLogged.get(0), 1)).contains("select count(*) from ( select distinct t0.id from o_order t0 join o_order_detail u1 on u1.order_id = t0.id where u1.product_id = ?)");
+ assertThat(trimSql(sqlLogged.get(0), 1)).contains("select count(*) from ( select distinct t0.id from o_order t0 join o_order_detail u1 on u1.order_id = t0.id and u1.id > 0 where u1.product_id = ?)");
query.findList();
}
diff --git a/ebean-core/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java b/ebean-core/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java
index f476ed084..dd230b866 100644
--- a/ebean-core/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java
+++ b/ebean-core/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java
@@ -64,7 +64,7 @@ public class TestQuerySingleAttribute extends BaseTestCase {
assertThat(sqlOf(query)).contains("select r1.attribute_, count(*) " +
"from (select distinct t0.id, t0.name as attribute_ " +
- "from o_customer t0 left join contact u1 on u1.customer_id = t0.id left join o_order u2 on u2.kcustomer_id = t0.id " +
+ "from o_customer t0 left join contact u1 on u1.customer_id = t0.id left join o_order u2 on u2.kcustomer_id = t0.id and u2.order_date is not null " +
"where t0.name = ? and (u2.status = ? or u1.first_name = ?)) r1 " +
"group by r1.attribute_ " +
"order by count(*) desc, r1.attribute_");
diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/build/ModelBuildPropertyVisitor.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/build/ModelBuildPropertyVisitor.java
index 3a7457a56..6530611dd 100644
--- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/build/ModelBuildPropertyVisitor.java
+++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/build/ModelBuildPropertyVisitor.java
@@ -149,7 +149,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
@Override
public void visitMany(BeanPropertyAssocMany> p) {
- if (p.hasJoinTable() && p.getMappedBy() == null) {
+ if (p.createJoinTable()) {
// only create on other 'owning' side
// build the create table and fkey constraints