#2847 - Add support for Sub-query EQ, NE, LT, LE, GT, GE expressions

This commit is contained in:
Rob Bygrave
2022-11-03 00:22:48 +13:00
parent 6f5e144a93
commit 0a61852711
18 changed files with 373 additions and 62 deletions
@@ -117,6 +117,11 @@ public interface ExpressionFactory {
*/
Expression arrayIsNotEmpty(String propertyName);
/**
* Equal To the result of a sub-query.
*/
Expression eq(String propertyName, Query<?> subQuery);
/**
* Equal To - property equal to the given value.
*/
@@ -127,6 +132,11 @@ public interface ExpressionFactory {
*/
Expression eqOrNull(String propertyName, Object value);
/**
* Not Equal To the result of a sub-query.
*/
Expression ne(String propertyName, Query<?> subQuery);
/**
* Not Equal To - property not equal to the given value.
*/
@@ -224,11 +234,21 @@ public interface ExpressionFactory {
*/
Expression geOrNull(String propertyName, Object value);
/**
* Greater Than the result of a sub-query.
*/
Expression gt(String propertyName, Query<?> subQuery);
/**
* Greater Than - property greater than the given value.
*/
Expression gt(String propertyName, Object value);
/**
* Greater Than or Equal to the result of a sub-query.
*/
Expression ge(String propertyName, Query<?> subQuery);
/**
* Greater Than or Equal to - property greater than or equal to the given
* value.
@@ -251,11 +271,21 @@ public interface ExpressionFactory {
*/
Expression leOrNull(String propertyName, Object value);
/**
* Less Than the result of a sub-query.
*/
Expression lt(String propertyName, Query<?> subQuery);
/**
* Less Than - property less than the given value.
*/
Expression lt(String propertyName, Object value);
/**
* Less Than or Equal to the result of a sub-query.
*/
Expression le(String propertyName, Query<?> subQuery);
/**
* Less Than or Equal to - property less than or equal to the given value.
*/
@@ -830,6 +830,11 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> addAll(ExpressionList<T> exprList);
/**
* Equal To the result of a sub-query.
*/
ExpressionList<T> eq(String propertyName, Query<?> subQuery);
/**
* Equal To - property is equal to a given value.
*/
@@ -854,6 +859,11 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> eqOrNull(String propertyName, Object value);
/**
* Not Equal To the result of a sub-query.
*/
ExpressionList<T> ne(String propertyName, Query<?> subQuery);
/**
* Not Equal To - property not equal to the given value.
*/
@@ -925,6 +935,11 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> betweenProperties(String lowProperty, String highProperty, Object value);
/**
* Greater Than the result of a sub-query.
*/
ExpressionList<T> gt(String propertyName, Query<?> subQuery);
/**
* Greater Than - property greater than the given value.
*/
@@ -940,12 +955,22 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> geOrNull(String propertyName, Object value);
/**
* Greater Than or Equal to the result of a sub-query.
*/
ExpressionList<T> ge(String propertyName, Query<?> subQuery);
/**
* Greater Than or Equal to - property greater than or equal to the given
* value.
*/
ExpressionList<T> ge(String propertyName, Object value);
/**
* Less Than the result of a sub-query.
*/
ExpressionList<T> lt(String propertyName, Query<?> subQuery);
/**
* Less Than - property less than the given value.
*/
@@ -961,6 +986,11 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> leOrNull(String propertyName, Object value);
/**
* Less Than or Equal to the result of a sub-query.
*/
ExpressionList<T> le(String propertyName, Query<?> subQuery);
/**
* Less Than or Equal to - property less than or equal to the given value.
*/
@@ -148,7 +148,7 @@ public class BaseQueryTuner {
case UPDATE:
case DELETE:
case SQ_EXISTS:
case SQ_IN:
case SQ_EX:
return false;
default:
// not using autoTune when explicitly loading the l2 bean cache
@@ -103,14 +103,14 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
COUNT(FIND_COUNT, "findCount"),
/**
* A subquery used as part of an exists where clause.
* A sub-query used as part of an exists where clause.
*/
SQ_EXISTS(FIND_SUBQUERY, "sqExists", false, false),
/**
* A subquery used as part of an in where clause.
* A sub-query expression used as part of where clause.
*/
SQ_IN(FIND_SUBQUERY, "sqIn", false, false),
SQ_EX(FIND_SUBQUERY, "sqEx", false, false),
/**
* Delete query.
@@ -1,21 +1,11 @@
package io.ebeaninternal.server.expression;
import io.ebean.ExampleExpression;
import io.ebean.Expression;
import io.ebean.ExpressionFactory;
import io.ebean.ExpressionList;
import io.ebean.Junction;
import io.ebean.LikeType;
import io.ebean.Pairs;
import io.ebean.Query;
import io.ebean.*;
import io.ebean.bean.EntityBean;
import io.ebean.search.Match;
import io.ebean.search.MultiMatch;
import io.ebean.search.TextCommonTerms;
import io.ebean.search.TextQueryString;
import io.ebean.search.TextSimple;
import io.ebean.search.*;
import io.ebeaninternal.api.SpiExpressionFactory;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.expression.SubQueryExpression.SQOp;
import io.ebeaninternal.server.grammer.EqlParser;
import java.util.Arrays;
@@ -157,6 +147,11 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
EqlParser.parseWhere(expressions, list, this, params);
}
@Override
public Expression eq(String propertyName, Query<?> subQuery) {
return new SubQueryExpression(SQOp.EQ, propertyName, (SpiQuery<?>) subQuery);
}
/**
* Equal To - property equal to the given value.
*/
@@ -173,6 +168,11 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
return or(eq(propertyName, value), isNull(propertyName));
}
@Override
public Expression ne(String propertyName, Query<?> subQuery) {
return new SubQueryExpression(SQOp.NE, propertyName, (SpiQuery<?>) subQuery);
}
/**
* Not Equal To - property not equal to the given value.
*/
@@ -258,6 +258,11 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
return new BetweenPropertyExpression(lowProperty, highProperty, value);
}
@Override
public Expression gt(String propertyName, Query<?> subQuery) {
return new SubQueryExpression(SQOp.GT, propertyName, (SpiQuery<?>) subQuery);
}
/**
* Greater Than - property greater than the given value.
*/
@@ -279,6 +284,11 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
return or(ge(propertyName, value), isNull(propertyName));
}
@Override
public Expression ge(String propertyName, Query<?> subQuery) {
return new SubQueryExpression(SQOp.GE, propertyName, (SpiQuery<?>) subQuery);
}
/**
* Greater Than or Equal to - property greater than or equal to the given
* value.
@@ -301,6 +311,11 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
return or(le(propertyName, value), isNull(propertyName));
}
@Override
public Expression lt(String propertyName, Query<?> subQuery) {
return new SubQueryExpression(SQOp.LT, propertyName, (SpiQuery<?>) subQuery);
}
/**
* Less Than - property less than the given value.
*/
@@ -309,6 +324,11 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
return new SimpleExpression(propertyName, Op.LT, value);
}
@Override
public Expression le(String propertyName, Query<?> subQuery) {
return new SubQueryExpression(SQOp.LE, propertyName, (SpiQuery<?>) subQuery);
}
/**
* Less Than or Equal to - property less than or equal to the given value.
*/
@@ -465,7 +485,7 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
*/
@Override
public Expression in(String propertyName, Query<?> subQuery) {
return new InQueryExpression(propertyName, (SpiQuery<?>) subQuery, false);
return new SubQueryExpression(SQOp.IN, propertyName, (SpiQuery<?>) subQuery);
}
/**
@@ -507,7 +527,7 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
*/
@Override
public Expression notIn(String propertyName, Query<?> subQuery) {
return new InQueryExpression(propertyName, (SpiQuery<?>) subQuery, true);
return new SubQueryExpression(SQOp.NOTIN, propertyName, (SpiQuery<?>) subQuery);
}
/**
@@ -788,6 +788,11 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return add(expr.bitwiseAnd(propertyName, flags, match));
}
@Override
public ExpressionList<T> eq(String propertyName, Query<?> subQuery) {
return add(expr.eq(propertyName, subQuery));
}
@Override
public ExpressionList<T> eq(String propertyName, Object value) {
return add(expr.eq(propertyName, value));
@@ -813,6 +818,11 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return add(expr.ine(propertyName, value));
}
@Override
public ExpressionList<T> ne(String propertyName, Query<?> subQuery) {
return add(expr.ne(propertyName, subQuery));
}
@Override
public ExpressionList<T> ne(String propertyName, Object value) {
return add(expr.ne(propertyName, value));
@@ -863,10 +873,19 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return add(expr.endsWith(propertyName, value));
}
@Override
public ExpressionList<T> ge(String propertyName, Query<?> subQuery) {
return add(expr.ge(propertyName, subQuery));
}
@Override
public ExpressionList<T> ge(String propertyName, Object value) {
add(expr.ge(propertyName, value));
return this;
return add(expr.ge(propertyName, value));
}
@Override
public ExpressionList<T> gt(String propertyName, Query<?> subQuery) {
return add(expr.gt(propertyName, subQuery));
}
@Override
@@ -876,14 +895,12 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
@Override
public ExpressionList<T> gtOrNull(String propertyName, Object value) {
add(expr.gtOrNull(propertyName, value));
return this;
return add(expr.gtOrNull(propertyName, value));
}
@Override
public ExpressionList<T> geOrNull(String propertyName, Object value) {
add(expr.geOrNull(propertyName, value));
return this;
return add(expr.geOrNull(propertyName, value));
}
@Override
@@ -999,6 +1016,11 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return add(expr.istartsWith(propertyName, value));
}
@Override
public ExpressionList<T> le(String propertyName, Query<?> subQuery) {
return add(expr.le(propertyName, subQuery));
}
@Override
public ExpressionList<T> le(String propertyName, Object value) {
return add(expr.le(propertyName, value));
@@ -1019,6 +1041,11 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return add(expr.like(propertyName, value));
}
@Override
public ExpressionList<T> lt(String propertyName, Query<?> subQuery) {
return add(expr.lt(propertyName, subQuery));
}
@Override
public ExpressionList<T> lt(String propertyName, Object value) {
return add(expr.lt(propertyName, value));
@@ -309,6 +309,11 @@ final class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expr
return exprList.endsWith(propertyName, value);
}
@Override
public ExpressionList<T> eq(String propertyName, Query<?> subQuery) {
return exprList.eq(propertyName, subQuery);
}
@Override
public ExpressionList<T> eq(String propertyName, Object value) {
return exprList.eq(propertyName, value);
@@ -642,11 +647,21 @@ final class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expr
return exprList.bitwiseNot(propertyName, flags);
}
@Override
public ExpressionList<T> ge(String propertyName, Query<?> subQuery) {
return exprList.ge(propertyName, subQuery);
}
@Override
public ExpressionList<T> ge(String propertyName, Object value) {
return exprList.ge(propertyName, value);
}
@Override
public ExpressionList<T> gt(String propertyName, Query<?> subQuery) {
return exprList.gt(propertyName, subQuery);
}
@Override
public ExpressionList<T> gt(String propertyName, Object value) {
return exprList.gt(propertyName, value);
@@ -787,6 +802,11 @@ final class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expr
return exprList.istartsWith(propertyName, value);
}
@Override
public ExpressionList<T> le(String propertyName, Query<?> subQuery) {
return exprList.le(propertyName, subQuery);
}
@Override
public ExpressionList<T> le(String propertyName, Object value) {
return exprList.le(propertyName, value);
@@ -797,6 +817,11 @@ final class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expr
return exprList.like(propertyName, value);
}
@Override
public ExpressionList<T> lt(String propertyName, Query<?> subQuery) {
return exprList.lt(propertyName, subQuery);
}
@Override
public ExpressionList<T> lt(String propertyName, Object value) {
return exprList.lt(propertyName, value);
@@ -812,6 +837,11 @@ final class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expr
return exprList.leOrNull(propertyName, value);
}
@Override
public ExpressionList<T> ne(String propertyName, Query<?> subQuery) {
return exprList.ne(propertyName, subQuery);
}
@Override
public ExpressionList<T> ne(String propertyName, Object value) {
return exprList.ne(propertyName, value);
@@ -8,32 +8,47 @@ import io.ebeaninternal.server.query.CQuery;
import java.util.List;
/**
* In expression using a sub query.
* Sub-Query expression.
*/
final class InQueryExpression extends AbstractExpression implements UnsupportedDocStoreExpression {
final class SubQueryExpression extends AbstractExpression implements UnsupportedDocStoreExpression {
private final boolean not;
enum SQOp {
EQ(" = "),
NE(" <> "),
GT(" > "),
GE(" >= "),
LT(" < "),
LE(" <= "),
IN(" in "),
NOTIN(" not in ");
final String expression;
SQOp(String expression) {
this.expression = expression;
}
}
private final SQOp op;
private final SpiQuery<?> subQuery;
private List<Object> bindParams;
private String sql;
InQueryExpression(String propertyName, SpiQuery<?> subQuery, boolean not) {
SubQueryExpression(SQOp op, String propertyName, SpiQuery<?> subQuery) {
super(propertyName);
this.op = op;
this.subQuery = subQuery;
this.not = not;
}
InQueryExpression(String propertyName, boolean not, String sql, List<Object> bindParams) {
SubQueryExpression(SQOp op, String propertyName, String sql, List<Object> bindParams) {
super(propertyName);
this.op = op;
this.subQuery = null;
this.not = not;
this.sql = sql;
this.bindParams = bindParams;
}
@Override
public SpiExpression copy() {
return subQuery == null ? this : new InQueryExpression(propName, subQuery.copy(), not);
return subQuery == null ? this : new SubQueryExpression(op, propName, subQuery.copy());
}
@Override
@@ -55,8 +70,8 @@ final class InQueryExpression extends AbstractExpression implements UnsupportedD
@Override
public void queryPlanHash(StringBuilder builder) {
builder.append("InQuery[").append(propName)
.append(" not:").append(not).append(" sql:").append(sql)
builder.append("SubQuery[").append(propName).append(op.expression)
.append(" sql:").append(sql)
.append(" ?:").append(bindParams.size()).append("]");
}
@@ -65,7 +80,7 @@ final class InQueryExpression extends AbstractExpression implements UnsupportedD
*/
private CQuery<?> compileSubQuery(BeanQueryRequest<?> queryRequest) {
SpiEbeanServer ebeanServer = (SpiEbeanServer) queryRequest.database();
return ebeanServer.compileQuery(Type.SQ_IN, subQuery, queryRequest.transaction());
return ebeanServer.compileQuery(Type.SQ_EX, subQuery, queryRequest.transaction());
}
@Override
@@ -75,13 +90,7 @@ final class InQueryExpression extends AbstractExpression implements UnsupportedD
@Override
public void addSql(SpiExpressionRequest request) {
request.append(propName);
if (not) {
request.append(" not");
}
request.append(" in (");
request.append(sql);
request.append(")");
request.append(propName).append(op.expression).append("(").append(sql).append(")");
}
@Override
@@ -93,7 +102,7 @@ final class InQueryExpression extends AbstractExpression implements UnsupportedD
@Override
public boolean isSameByBind(SpiExpression other) {
InQueryExpression that = (InQueryExpression) other;
SubQueryExpression that = (SubQueryExpression) other;
if (this.bindParams.size() != that.bindParams.size()) {
return false;
}
@@ -86,7 +86,7 @@ public final class SqlTreeBuilder {
this.disableLazyLoad = query.isDisableLazyLoading();
this.readOnly = Boolean.TRUE.equals(query.isReadOnly());
this.subQuery = Type.SQ_EXISTS == query.getType()
|| Type.SQ_IN == query.getType()
|| Type.SQ_EX == query.getType()
|| Type.ID_LIST == query.getType()
|| Type.DELETE == query.getType()
|| query.isCountDistinct();
@@ -35,7 +35,7 @@ public class DefaultExpressionFactoryTest {
public void testEq() {
DefaultExpressionFactory factory = new DefaultExpressionFactory(false, false);
Expression expression = factory.eq("name", null);
Expression expression = factory.eq("name", (String)null);
assertThat(expression).isInstanceOf(NullExpression.class);
assertThat(toQueryPlanHash(expression)).isEqualTo("Null[name]");
}
@@ -44,7 +44,7 @@ public class DefaultExpressionFactoryTest {
public void testNe() {
DefaultExpressionFactory factory = new DefaultExpressionFactory(false, false);
Expression expression = factory.ne("name", null);
Expression expression = factory.ne("name", (String)null);
assertThat(expression).isInstanceOf(NullExpression.class);
assertThat(toQueryPlanHash(expression)).isEqualTo("NotNull[name]");
}
@@ -71,7 +71,7 @@ public class DefaultExpressionFactoryTest {
public void testEq_with_equalsWithNullAsNoop() {
DefaultExpressionFactory factory = new DefaultExpressionFactory(true, false);
Expression expression = factory.eq("name", null);
Expression expression = factory.eq("name", (String)null);
assertThat(expression).isInstanceOf(NoopExpression.class);
}
@@ -79,7 +79,7 @@ public class DefaultExpressionFactoryTest {
public void testNe_with_equalsWithNullAsNoop() {
DefaultExpressionFactory factory = new DefaultExpressionFactory(true, false);
Expression expression = factory.ne("name", null);
Expression expression = factory.ne("name", (String)null);
assertThat(expression).isInstanceOf(NoopExpression.class);
}
@@ -87,7 +87,7 @@ public class DefaultExpressionFactoryTest {
public void testIeq_with_equalsWithNullAsNoop() {
DefaultExpressionFactory factory = new DefaultExpressionFactory(true, false);
Expression expression = factory.ieq("name", null);
Expression expression = factory.ieq("name", (String)null);
assertThat(expression).isInstanceOf(NoopExpression.class);
}
}
@@ -12,14 +12,15 @@ import static org.mockito.Mockito.verify;
public class InQueryExpressionTest extends BaseExpressionTest {
private InQueryExpression exp(String propertyName, boolean not, String sql, Object... bindValues) {
return new InQueryExpression(propertyName, not, sql, Arrays.asList(bindValues));
private SubQueryExpression exp(String propertyName, boolean not, String sql, Object... bindValues) {
var op = not ? SubQueryExpression.SQOp.NOTIN : SubQueryExpression.SQOp.IN;
return new SubQueryExpression(op, propertyName, sql, Arrays.asList(bindValues));
}
@Test
void copy_subQuery_expectNewInstance() {
SpiQuery<?> subQuery = mock(SpiQuery.class);
var orig = new InQueryExpression("name", subQuery, false);
var orig = new SubQueryExpression(SubQueryExpression.SQOp.IN, "name", subQuery);
SpiExpression copy = orig.copy();
assertThat(copy).isNotSameAs(orig);
verify(subQuery).copy();
@@ -27,9 +27,9 @@ public class NoopExpressionTest extends BaseExpressionTest {
initTables();
Query<Customer> query = DB.find(Customer.class)
.select("id")
.where().eq("name", null)
.where().eq("name", (String) null)
.add(NoopExpression.INSTANCE)
.ne("status", null)
.ne("status", (String) null)
.query();
query.findList();
@@ -1,6 +1,8 @@
package io.ebean.typequery;
import io.ebean.Query;
/**
* Base property for all comparable types.
*
@@ -301,4 +303,47 @@ public class PBaseCompareable<R, T> extends PBaseValueEqual<R, T> {
return _root;
}
/**
* Property is Less Than or Equal To the result of a sub-query.
*
* @param subQuery value provided by a subQuery
* @return the root query bean instance
*/
public final R le(Query<?> subQuery) {
expr().le(_name, subQuery);
return _root;
}
/**
* Property is Less Than the result of a sub-query.
*
* @param subQuery value provided by a subQuery
* @return the root query bean instance
*/
public final R lt(Query<?> subQuery) {
expr().lt(_name, subQuery);
return _root;
}
/**
* Property is Greater Than or Equal To the result of a sub-query.
*
* @param subQuery value provided by a subQuery
* @return the root query bean instance
*/
public final R ge(Query<?> subQuery) {
expr().ge(_name, subQuery);
return _root;
}
/**
* Property is Greater Than the result of a sub-query.
*
* @param subQuery value provided by a subQuery
* @return the root query bean instance
*/
public final R gt(Query<?> subQuery) {
expr().gt(_name, subQuery);
return _root;
}
}
@@ -281,7 +281,7 @@ public abstract class PBaseValueEqual<R, T> extends TQPropertyBase<R> {
}
/**
* Is in the result of a subquery. Synonym for in().
* Is in the result of a sub-query. Synonym for in().
*
* @param subQuery values provided by a subQuery
* @return the root query bean instance
@@ -291,7 +291,7 @@ public abstract class PBaseValueEqual<R, T> extends TQPropertyBase<R> {
}
/**
* Is NOT in the result of a subquery.
* Is NOT in the result of a sub-query.
*
* @param subQuery values provided by a subQuery
* @return the root query bean instance
@@ -300,4 +300,26 @@ public abstract class PBaseValueEqual<R, T> extends TQPropertyBase<R> {
expr().notIn(_name, subQuery);
return _root;
}
/**
* Property is equal to the result of a sub-query.
*
* @param subQuery value provided by a subQuery
* @return the root query bean instance
*/
public final R eq(Query<?> subQuery) {
expr().eq(_name, subQuery);
return _root;
}
/**
* Property is not equal to the result of a sub-query.
*
* @param subQuery value provided by a subQuery
* @return the root query bean instance
*/
public final R ne(Query<?> subQuery) {
expr().ne(_name, subQuery);
return _root;
}
}
@@ -4,6 +4,7 @@ import io.ebean.DB;
import io.ebean.FetchGroup;
import io.ebean.Query;
import io.ebean.test.LoggedSql;
import io.ebean.typequery.TQProperty;
import org.example.domain.Customer;
import org.example.domain.Order;
import org.example.domain.OrderDetail;
@@ -217,6 +218,96 @@ public class QOrderTest {
}
@Test
void geSubQuery() {
var subQuery = new QOrder()
.select(StdFunctions.max(QOrder.Alias.orderDate))
.query();
var query = new QCustomer()
.select(QCustomer.Alias.id)
.registered.ge(subQuery)
.query();
query.findList();
assertThat(query.getGeneratedSql()).contains("select t0.id from be_customer t0 where t0.registered >= (select max(t0.order_date) from o_order t0)");
}
@Test
void gtSubQuery() {
var subQuery = new QOrder()
.select("max(orderDate)")
.query();
var query = new QCustomer()
.select(QCustomer.Alias.id)
.registered.gt(subQuery)
.query();
query.findList();
assertThat(query.getGeneratedSql()).contains("select t0.id from be_customer t0 where t0.registered > (select max(t0.order_date) from o_order t0)");
}
@Test
void leSubQuery() {
var subQuery = new QOrder()
.select("max(orderDate)")
.query();
var query = new QCustomer()
.select(QCustomer.Alias.id)
.registered.le(subQuery)
.query();
query.findList();
assertThat(query.getGeneratedSql()).contains("select t0.id from be_customer t0 where t0.registered <= (select max(t0.order_date) from o_order t0)");
}
@Test
void ltSubQuery() {
var subQuery = new QOrder()
.select("max(orderDate)")
.query();
var query = new QCustomer()
.select(QCustomer.Alias.id)
.registered.lt(subQuery)
.query();
query.findList();
assertThat(query.getGeneratedSql()).contains("select t0.id from be_customer t0 where t0.registered < (select max(t0.order_date) from o_order t0)");
}
@Test
void eqSubQuery() {
var subQuery = new QOrder()
.select("max(orderDate)")
.query();
var query = new QCustomer()
.select(QCustomer.Alias.id)
.registered.eq(subQuery)
.query();
query.findList();
assertThat(query.getGeneratedSql()).contains("select t0.id from be_customer t0 where t0.registered = (select max(t0.order_date) from o_order t0)");
}
@Test
void neSubQuery() {
var subQuery = new QOrder()
.select(StdFunctions.max(QOrder.Alias.orderDate))
.query();
var query = new QCustomer()
.select(QCustomer.Alias.id)
.registered.ne(subQuery)
.query();
query.findList();
assertThat(query.getGeneratedSql()).contains("select t0.id from be_customer t0 where t0.registered <> (select max(t0.order_date) from o_order t0)");
}
@Test
void propertyCompare() {
Query<Order> query = new QOrder()
@@ -283,4 +374,10 @@ public class QOrderTest {
order.setCustomer(customer);
order.save();
}
static class StdFunctions {
static String max(TQProperty<?> prop) {
return "max(" + prop + ")";
}
}
}
@@ -28,7 +28,7 @@ public class TestQueryIsNull extends BaseTestCase {
ResetBasicData.reset();
Query<Order> query = DB.find(Order.class).select("id, status")
.where().eq("status", null).query();
.where().eq("status", (String) null).query();
query.findList();
assertThat(query.getGeneratedSql()).isEqualTo("select t0.id, t0.status from o_order t0 where t0.status is null");
@@ -1,8 +1,8 @@
package org.tests.query;
import io.ebean.xtest.BaseTestCase;
import io.ebean.DB;
import io.ebean.Query;
import io.ebean.xtest.BaseTestCase;
import org.junit.jupiter.api.Test;
import org.tests.model.basic.Order;
import org.tests.model.basic.ResetBasicData;
@@ -19,13 +19,13 @@ public class TestQueryNullAssocOne extends BaseTestCase {
ResetBasicData.reset();
Query<Order> q0 = DB.find(Order.class).where().eq("customer", null).query();
Query<Order> q0 = DB.find(Order.class).where().eq("customer", (String) null).query();
List<Order> orders = q0.findList();
assertNotNull(orders);
assertTrue(q0.getGeneratedSql().contains("where t0.kcustomer_id is null"));
Query<Order> q1 = DB.find(Order.class).where().eq("customer.id", null).query();
Query<Order> q1 = DB.find(Order.class).where().eq("customer.id", (String) null).query();
List<Order> o1 = q1.findList();
assertTrue(o1.size() == orders.size());
@@ -38,7 +38,7 @@ public class TestSelfParent extends BaseTestCase {
DB.save(child21);
DB.save(child22);
List<SelfParent> roots = DB.find(SelfParent.class).where().eq("parent", null).findList();
List<SelfParent> roots = DB.find(SelfParent.class).where().eq("parent", (String) null).findList();
assertEquals(1, roots.size());