From d29ef8934d3dea8a769c9d9ddf5b04da3e797b89 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 9 Nov 2022 17:47:16 +1300 Subject: [PATCH] Add more operators to StdOperators - stricter use of UpdateQuery.set() - Numbers just use their own type - PBaseNumber - PEnum just uses its own type - Expr.factory() to expose factory --- ebean-api/src/main/java/io/ebean/Expr.java | 7 + ebean-api/src/main/java/io/ebean/Query.java | 8 +- .../src/main/java/io/ebean/StdFunctions.java | 134 ---------- .../src/main/java/io/ebean/StdLegacyOps.java | 37 +++ .../src/main/java/io/ebean/StdOperators.java | 251 ++++++++++++++++++ .../src/main/java/io/ebean/UpdateQuery.java | 2 +- .../test/java/io/ebean/StdFunctionsTest.java | 2 +- .../java/io/ebean/typequery/PBaseNumber.java | 2 +- .../main/java/io/ebean/typequery/PEnum.java | 2 +- .../java/io/ebean/typequery/TQRootBean.java | 2 +- .../java/org/querytest/QCustomerTest.java | 28 ++ .../test/java/org/querytest/QOrderTest.java | 8 +- .../org/tests/query/TestStdFunctions.java | 2 +- 13 files changed, 336 insertions(+), 149 deletions(-) delete mode 100644 ebean-api/src/main/java/io/ebean/StdFunctions.java create mode 100644 ebean-api/src/main/java/io/ebean/StdLegacyOps.java create mode 100644 ebean-api/src/main/java/io/ebean/StdOperators.java diff --git a/ebean-api/src/main/java/io/ebean/Expr.java b/ebean-api/src/main/java/io/ebean/Expr.java index 525bc3272..072eb7e95 100644 --- a/ebean-api/src/main/java/io/ebean/Expr.java +++ b/ebean-api/src/main/java/io/ebean/Expr.java @@ -30,6 +30,13 @@ public final class Expr { private Expr() { } + /** + * Return the underlying expression factory. + */ + public static ExpressionFactory factory() { + return DB.expressionFactory(); + } + /** * Equal To - property equal to the given value. */ diff --git a/ebean-api/src/main/java/io/ebean/Query.java b/ebean-api/src/main/java/io/ebean/Query.java index ad15fc047..ad7a955ee 100644 --- a/ebean-api/src/main/java/io/ebean/Query.java +++ b/ebean-api/src/main/java/io/ebean/Query.java @@ -1773,16 +1773,16 @@ public interface Query extends CancelableQuery { *

* Implemented by query bean properties and expressions based on those properties. *

- * The base type determines which {@link StdFunctions} can be used on the property. + * The base type determines which {@link StdOperators} can be used on the property. * - * @param The base type of the property Number, String, Temporal, Boolean or Object. + * @param The property type. */ - interface Property { + interface Property { /** * Return a property given the expression. */ - static Property of(String expression) { + static Property of(String expression) { return new SimpleProperty<>(expression); } diff --git a/ebean-api/src/main/java/io/ebean/StdFunctions.java b/ebean-api/src/main/java/io/ebean/StdFunctions.java deleted file mode 100644 index 6a36c58d8..000000000 --- a/ebean-api/src/main/java/io/ebean/StdFunctions.java +++ /dev/null @@ -1,134 +0,0 @@ -package io.ebean; - -import io.ebean.Query.Property; - -import java.time.temporal.Temporal; - -public final class StdFunctions { - - public static Property count(Property property) { - return Property.of("count(" + property + ")"); - } - - public static Property sum(Property property) { - return Property.of("sum(" + property + ")"); - } - - public static Property avg(Property property) { - return Property.of("avg(" + property + ")"); - } - - public static Property max(Property property) { - return Property.of("max(" + property + ")"); - } - - public static Property min(Property property) { - return Property.of("min(" + property + ")"); - } - - public static Property coalesce(Property property, Object value) { - return Property.of("coalesce(" + property.toString() + "," + sqlValue(value) + ")"); - } - - public static Property lower(Property property) { - return Property.of("lower(" + property + ")"); - } - - public static Property upper(Property property) { - return Property.of("upper(" + property + ")"); - } - - public static Property concat(Property property, Object... values) { - StringBuilder expression = new StringBuilder(50); - expression.append("concat(").append(property.toString()); - for (Object value : values) { - expression.append(",").append(sqlConcatString(value)); - } - expression.append(")"); - return Property.of(expression.toString()); - } - - private static String sqlConcatString(Object value) { - String asStr = String.valueOf(value); - return (value instanceof Property || isSqlQuoted(asStr)) ? asStr : "'" + value + "'"; - } - - private static boolean isSqlQuoted(String asStr) { - return asStr.length() > 0 && asStr.charAt(0) == '\''; - } - - private static String sqlValue(Object value) { - if (value instanceof Property || value instanceof Number) { - return value.toString(); - } else { - return "'" + value + "'"; - } - } - - // -------------------------------------------------------------------------------------------- // - // ---- Expressions --------------------------------------------------------------------------- // - - public static Expression eq(Property property, Number value) { - return Expr.eq(property.toString(), value); - } - - public static Expression eq(Property property, String value) { - return Expr.eq(property.toString(), value); - } - - public static Expression eq(Property property, Temporal value) { - return Expr.eq(property.toString(), value); - } - - public static Expression eq(Property property, boolean value) { - return Expr.eq(property.toString(), value); - } - - public static Expression eq(Property property, Object value) { - return Expr.eq(property.toString(), value); - } - - //---- - - public static Expression gt(Property property, Number value) { - return Expr.gt(property.toString(), value); - } - - public static Expression gt(Property property, String value) { - return Expr.gt(property.toString(), value); - } - - public static Expression gt(Property property, Temporal value) { - return Expr.gt(property.toString(), value); - } - - public static Expression gt(Property property, Object value) { - return Expr.gt(property.toString(), value); - } - - //---- - - public static Expression like(Property property, String value) { - return Expr.like(property.toString(), value); - } - - public static Expression ilike(Property property, String value) { - return Expr.ilike(property.toString(), value); - } - - public static Expression startsWith(Property property, String value) { - return Expr.startsWith(property.toString(), value); - } - - public static Expression istartsWith(Property property, String value) { - return Expr.istartsWith(property.toString(), value); - } - - public static Expression contains(Property property, String value) { - return Expr.contains(property.toString(), value); - } - - public static Expression icontains(Property property, String value) { - return Expr.icontains(property.toString(), value); - } -} diff --git a/ebean-api/src/main/java/io/ebean/StdLegacyOps.java b/ebean-api/src/main/java/io/ebean/StdLegacyOps.java new file mode 100644 index 000000000..c8da08b0a --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/StdLegacyOps.java @@ -0,0 +1,37 @@ +package io.ebean; + +import io.ebean.Query.Property; + +import java.time.temporal.Temporal; + +@Deprecated(since = "experimental") +public final class StdLegacyOps { + + public static Expression eq(Property property, java.util.Calendar value) { + return Expr.eq(property.toString(), value); + } + + public static Expression eqOrNull(Property property, java.util.Calendar value) { + return Expr.factory().eqOrNull(property.toString(), value); + } + + public static Expression ne(Property property, java.util.Calendar value) { + return Expr.factory().ne(property.toString(), value); + } + + public static Expression lt(Property property, java.util.Calendar value) { + return Expr.factory().lt(property.toString(), value); + } + + public static Expression ltOrNull(Property property, java.util.Calendar value) { + return Expr.factory().ltOrNull(property.toString(), value); + } + + public static Expression le(Property property, java.util.Calendar value) { + return Expr.factory().le(property.toString(), value); + } + + public static Expression leOrNull(Property property, java.util.Calendar value) { + return Expr.factory().leOrNull(property.toString(), value); + } +} diff --git a/ebean-api/src/main/java/io/ebean/StdOperators.java b/ebean-api/src/main/java/io/ebean/StdOperators.java new file mode 100644 index 000000000..e049401d5 --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/StdOperators.java @@ -0,0 +1,251 @@ +package io.ebean; + +import io.ebean.Query.Property; + +import java.time.temporal.Temporal; +import java.util.Collection; + +@Deprecated(since = "experimental") +public final class StdOperators { + + // ---- Functions ---- // + + public static Property sum(Property property) { + return Property.of("sum(" + property + ")"); + } + + public static Property count(Property property) { + return Property.of("count(" + property + ")"); + } + + public static Property avg(Property property) { + return Property.of("avg(" + property + ")"); + } + + public static Property max(Property property) { + return Property.of("max(" + property + ")"); + } + + public static Property min(Property property) { + return Property.of("min(" + property + ")"); + } + + public static Property coalesce(Property property, Object value) { + return Property.of("coalesce(" + property.toString() + "," + sqlValue(value) + ")"); + } + + public static Property lower(Property property) { + return Property.of("lower(" + property + ")"); + } + + public static Property upper(Property property) { + return Property.of("upper(" + property + ")"); + } + + public static Property concat(Property property, Object... values) { + StringBuilder expression = new StringBuilder(50); + expression.append("concat(").append(property.toString()); + for (Object value : values) { + expression.append(",").append(sqlConcatString(value)); + } + expression.append(")"); + return Property.of(expression.toString()); + } + + private static String sqlConcatString(Object value) { + String asStr = String.valueOf(value); + return (value instanceof Property || isSqlQuoted(asStr)) ? asStr : "'" + value + "'"; + } + + private static boolean isSqlQuoted(String asStr) { + return asStr.length() > 0 && asStr.charAt(0) == '\''; + } + + private static String sqlValue(Object value) { + if (value instanceof Property || value instanceof Number) { + return value.toString(); + } else { + return "'" + value + "'"; + } + } + + // ---- Operators ---- // + + public static Expression eq(Property property, T value) { + return Expr.eq(property.toString(), value); + } + + public static Expression eq(Property property, Query subQuery) { + return Expr.in(property.toString(), subQuery); + } + + public static Expression eq(Property property, java.util.Date value) { + return Expr.eq(property.toString(), value); + } + + public static Expression eqOrNull(Property property, T value) { + return Expr.factory().eqOrNull(property.toString(), value); + } + + public static Expression eqOrNull(Property property, java.util.Date value) { + return Expr.factory().eqOrNull(property.toString(), value); + } + + public static Expression ne(Property property, T value) { + return Expr.ne(property.toString(), value); + } + + public static Expression ne(Property property, Query subQuery) { + return Expr.ne(property.toString(), subQuery); + } + + public static Expression ne(Property property, java.util.Date value) { + return Expr.ne(property.toString(), value); + } + + public static Expression gt(Property property, T value) { + return Expr.gt(property.toString(), value); + } + + public static Expression gt(Property property, Query subQuery) { + return Expr.gt(property.toString(), subQuery); + } + + public static Expression gt(Property property, java.util.Date value) { + return Expr.gt(property.toString(), value); + } + + public static Expression gtOrNull(Property property, T value) { + return Expr.factory().gtOrNull(property.toString(), value); + } + + public static Expression gtOrNull(Property property, java.util.Date value) { + return Expr.factory().gtOrNull(property.toString(), value); + } + + public static Expression ge(Property property, T value) { + return Expr.ge(property.toString(), value); + } + + public static Expression ge(Property property, Query subQuery) { + return Expr.ge(property.toString(), subQuery); + } + + public static Expression ge(Property property, java.util.Date value) { + return Expr.ge(property.toString(), value); + } + + public static Expression geOrNull(Property property, T value) { + return Expr.factory().geOrNull(property.toString(), value); + } + + public static Expression geOrNull(Property property, java.util.Date value) { + return Expr.factory().geOrNull(property.toString(), value); + } + + public static Expression lt(Property property, T value) { + return Expr.lt(property.toString(), value); + } + + public static Expression lt(Property property, Query subQuery) { + return Expr.lt(property.toString(), subQuery); + } + + public static Expression lt(Property property, java.util.Date value) { + return Expr.lt(property.toString(), value); + } + + public static Expression ltOrNull(Property property, T value) { + return Expr.factory().ltOrNull(property.toString(), value); + } + + public static Expression ltOrNull(Property property, java.util.Date value) { + return Expr.factory().ltOrNull(property.toString(), value); + } + + public static Expression le(Property property, T value) { + return Expr.le(property.toString(), value); + } + + public static Expression le(Property property, Query subQuery) { + return Expr.le(property.toString(), subQuery); + } + + public static Expression le(Property property, java.util.Date value) { + return Expr.le(property.toString(), value); + } + + public static Expression leOrNull(Property property, T value) { + return Expr.factory().leOrNull(property.toString(), value); + } + + public static Expression leOrNull(Property property, java.util.Date value) { + return Expr.factory().leOrNull(property.toString(), value); + } + + public static Expression inRange(Property property, T lowValue, T highValue) { + return Expr.factory().inRange(property.toString(), lowValue, highValue); + } + + public static Expression inRange(Property property, java.util.Date lowValue, java.util.Date highValue) { + return Expr.factory().inRange(property.toString(), lowValue, highValue); + } + + public static Expression inRange(Property lowProperty, Property highProperty, T value) { + return Expr.factory().inRangeWith(lowProperty.toString(), highProperty.toString(), value); + } + + public static Expression inRange(Property lowProperty, Property highProperty, java.util.Date value) { + return Expr.factory().inRangeWith(lowProperty.toString(), highProperty.toString(), value); + } + + public static Expression inRange(Property lowProperty, Property property, Property highProperty) { + return Expr.factory().inRangeWithProperties(lowProperty.toString(), property.toString(), highProperty.toString()); + } + + public static Expression in(Property property, Collection value) { + return Expr.in(property.toString(), value); + } + + public static Expression in(Property property, Query subQuery) { + return Expr.in(property.toString(), subQuery); + } + + public static Expression inOrEmpty(Property property, Collection value) { + return Expr.inOrEmpty(property.toString(), value); + } + + public static Expression notIn(Property property, Collection value) { + return Expr.factory().notIn(property.toString(), value); + } + + public static Expression notIn(Property property, Query subQuery) { + return Expr.factory().notIn(property.toString(), subQuery); + } + + // ---- String operators ---- // + + public static Expression like(Property property, String value) { + return Expr.like(property.toString(), value); + } + + public static Expression ilike(Property property, String value) { + return Expr.ilike(property.toString(), value); + } + + public static Expression startsWith(Property property, String value) { + return Expr.startsWith(property.toString(), value); + } + + public static Expression istartsWith(Property property, String value) { + return Expr.istartsWith(property.toString(), value); + } + + public static Expression contains(Property property, String value) { + return Expr.contains(property.toString(), value); + } + + public static Expression icontains(Property property, String value) { + return Expr.icontains(property.toString(), value); + } +} diff --git a/ebean-api/src/main/java/io/ebean/UpdateQuery.java b/ebean-api/src/main/java/io/ebean/UpdateQuery.java index bb293c748..a4a874328 100644 --- a/ebean-api/src/main/java/io/ebean/UpdateQuery.java +++ b/ebean-api/src/main/java/io/ebean/UpdateQuery.java @@ -118,7 +118,7 @@ public interface UpdateQuery { * @param property The bean property to be set * @param value The value to set the property to */ - UpdateQuery set(Query.Property property, Object value); +

UpdateQuery set(Query.Property

property, P value); /** * Set the property to be null. diff --git a/ebean-api/src/test/java/io/ebean/StdFunctionsTest.java b/ebean-api/src/test/java/io/ebean/StdFunctionsTest.java index 4cce8ed1a..7c48d7fe7 100644 --- a/ebean-api/src/test/java/io/ebean/StdFunctionsTest.java +++ b/ebean-api/src/test/java/io/ebean/StdFunctionsTest.java @@ -4,7 +4,7 @@ import org.junit.jupiter.api.Test; import java.time.LocalDate; -import static io.ebean.StdFunctions.*; +import static io.ebean.StdOperators.*; import static org.assertj.core.api.Assertions.assertThat; class StdFunctionsTest { diff --git a/ebean-querybean/src/main/java/io/ebean/typequery/PBaseNumber.java b/ebean-querybean/src/main/java/io/ebean/typequery/PBaseNumber.java index 6e2e2a496..140998bc9 100644 --- a/ebean-querybean/src/main/java/io/ebean/typequery/PBaseNumber.java +++ b/ebean-querybean/src/main/java/io/ebean/typequery/PBaseNumber.java @@ -7,7 +7,7 @@ package io.ebean.typequery; * @param the number type */ @SuppressWarnings("rawtypes") -public abstract class PBaseNumber extends PBaseCompareable { +public abstract class PBaseNumber extends PBaseCompareable { /** * Construct with a property name and root instance. diff --git a/ebean-querybean/src/main/java/io/ebean/typequery/PEnum.java b/ebean-querybean/src/main/java/io/ebean/typequery/PEnum.java index 124263f55..d70ffb3e5 100644 --- a/ebean-querybean/src/main/java/io/ebean/typequery/PEnum.java +++ b/ebean-querybean/src/main/java/io/ebean/typequery/PEnum.java @@ -6,7 +6,7 @@ package io.ebean.typequery; * @param the enum specific type * @param the root query bean type */ -public final class PEnum extends PBaseValueEqual { +public final class PEnum extends PBaseValueEqual { /** * Construct with a property name and root instance. diff --git a/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java b/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java index 422110abc..1126b2026 100644 --- a/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java +++ b/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java @@ -278,7 +278,7 @@ public abstract class TQRootBean { /** * Specify the properties to be loaded on the 'main' root level entity bean - * also allowing for functions to be used like {@link StdFunctions#max(Query.Property)}. + * also allowing for functions to be used like {@link StdOperators#max(Query.Property)}. * * @param properties the list of properties to fetch */ diff --git a/ebean-querybean/src/test/java/org/querytest/QCustomerTest.java b/ebean-querybean/src/test/java/org/querytest/QCustomerTest.java index 2db089274..8808df5d8 100644 --- a/ebean-querybean/src/test/java/org/querytest/QCustomerTest.java +++ b/ebean-querybean/src/test/java/org/querytest/QCustomerTest.java @@ -15,11 +15,13 @@ import org.junit.jupiter.api.Test; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; +import java.sql.Timestamp; import java.time.LocalDate; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; +import static io.ebean.StdOperators.*; import static org.assertj.core.api.Assertions.assertThat; import static org.example.domain.query.QAddress.Alias.country; import static org.example.domain.query.QAddress.Alias.line1; @@ -692,6 +694,32 @@ public class QCustomerTest { assertThat(customerExists).isFalse(); } + @Test + void checkingPropertyTypesToEQOperator() { + + QCustomer c = QCustomer.alias(); + + new QCustomer() + .select(c.version, count(c.id)) + .version.gt(0) + .having() + .add(gt(count(c.id), 1)) + .findList(); + + new QCustomer() + .add(in(QCustomer.Alias.name, List.of("foo", "bar"))) + .add(eq(QCustomer.Alias.currentInet, Inet.of("asd").toString())) + .findList(); + + new QCustomer() + //.add(gt(sum(QCustomer.Alias.version), 45)) + .add(eq(QCustomer.Alias.version, 45L)) + .add(eq(QCustomer.Alias.name, "junk")) + .add(eq(QCustomer.Alias.registered, new Date())) + .add(eq(QCustomer.Alias.whenUpdated, new Timestamp(System.currentTimeMillis()))) + .findList(); + } + @Test public void testQuery() { diff --git a/ebean-querybean/src/test/java/org/querytest/QOrderTest.java b/ebean-querybean/src/test/java/org/querytest/QOrderTest.java index 2ed846c0a..16e2c47e4 100644 --- a/ebean-querybean/src/test/java/org/querytest/QOrderTest.java +++ b/ebean-querybean/src/test/java/org/querytest/QOrderTest.java @@ -17,9 +17,7 @@ import org.junit.jupiter.api.Test; import java.util.List; -import static io.ebean.StdFunctions.gt; -import static io.ebean.StdFunctions.ilike; -import static io.ebean.StdFunctions.*; +import static io.ebean.StdOperators.*; import static org.assertj.core.api.Assertions.assertThat; public class QOrderTest { @@ -227,7 +225,7 @@ public class QOrderTest { .status.eq(Order.Status.COMPLETE) .orderDate.gt(new java.sql.Date(System.currentTimeMillis())) .asUpdate() - .set(QOrder.Alias.version, 42L) + .set(QOrder.Alias.version, 56L) .setNull(QOrder.Alias.orderDate) .update(); @@ -258,7 +256,7 @@ public class QOrderTest { Query query = new QOrder() .select(o.id, o.status) .or() - .add(gt(coalesce(o.customer.version, 0), 42)) + .add(gt(coalesce(o.customer.version, 0), 42L)) .id.lt(12) .endOr() .query(); diff --git a/ebean-test/src/test/java/org/tests/query/TestStdFunctions.java b/ebean-test/src/test/java/org/tests/query/TestStdFunctions.java index 940af117a..d75bf14a6 100644 --- a/ebean-test/src/test/java/org/tests/query/TestStdFunctions.java +++ b/ebean-test/src/test/java/org/tests/query/TestStdFunctions.java @@ -5,7 +5,7 @@ import io.ebean.Query; import org.junit.jupiter.api.Test; import org.tests.model.basic.Customer; -import static io.ebean.StdFunctions.*; +import static io.ebean.StdOperators.*; import static org.assertj.core.api.Assertions.assertThat; /**