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
This commit is contained in:
Rob Bygrave
2022-11-09 17:47:16 +13:00
parent a4c3949cfc
commit d29ef8934d
13 changed files with 336 additions and 149 deletions
@@ -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.
*/
+4 -4
View File
@@ -1773,16 +1773,16 @@ public interface Query<T> extends CancelableQuery {
* <p>
* Implemented by query bean properties and expressions based on those properties.
* <p>
* 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 <BT> The base type of the property Number, String, Temporal, Boolean or Object.
* @param <T> The property type.
*/
interface Property<BT> {
interface Property<T> {
/**
* Return a property given the expression.
*/
static <BT> Property<BT> of(String expression) {
static <T> Property<T> of(String expression) {
return new SimpleProperty<>(expression);
}
@@ -1,134 +0,0 @@
package io.ebean;
import io.ebean.Query.Property;
import java.time.temporal.Temporal;
public final class StdFunctions {
public static Property<Number> count(Property<?> property) {
return Property.of("count(" + property + ")");
}
public static Property<Number> sum(Property<Number> property) {
return Property.of("sum(" + property + ")");
}
public static <BT> Property<BT> avg(Property<BT> property) {
return Property.of("avg(" + property + ")");
}
public static <BT> Property<BT> max(Property<BT> property) {
return Property.of("max(" + property + ")");
}
public static <BT> Property<BT> min(Property<BT> property) {
return Property.of("min(" + property + ")");
}
public static <BT> Property<BT> coalesce(Property<BT> property, Object value) {
return Property.of("coalesce(" + property.toString() + "," + sqlValue(value) + ")");
}
public static Property<String> lower(Property<String> property) {
return Property.of("lower(" + property + ")");
}
public static Property<String> upper(Property<String> property) {
return Property.of("upper(" + property + ")");
}
public static Property<String> 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<Number> property, Number value) {
return Expr.eq(property.toString(), value);
}
public static Expression eq(Property<String> property, String value) {
return Expr.eq(property.toString(), value);
}
public static Expression eq(Property<Temporal> property, Temporal value) {
return Expr.eq(property.toString(), value);
}
public static Expression eq(Property<Boolean> property, boolean value) {
return Expr.eq(property.toString(), value);
}
public static Expression eq(Property<Object> property, Object value) {
return Expr.eq(property.toString(), value);
}
//----
public static Expression gt(Property<Number> property, Number value) {
return Expr.gt(property.toString(), value);
}
public static Expression gt(Property<String> property, String value) {
return Expr.gt(property.toString(), value);
}
public static Expression gt(Property<Temporal> property, Temporal value) {
return Expr.gt(property.toString(), value);
}
public static Expression gt(Property<Object> property, Object value) {
return Expr.gt(property.toString(), value);
}
//----
public static Expression like(Property<String> property, String value) {
return Expr.like(property.toString(), value);
}
public static Expression ilike(Property<String> property, String value) {
return Expr.ilike(property.toString(), value);
}
public static Expression startsWith(Property<String> property, String value) {
return Expr.startsWith(property.toString(), value);
}
public static Expression istartsWith(Property<String> property, String value) {
return Expr.istartsWith(property.toString(), value);
}
public static Expression contains(Property<String> property, String value) {
return Expr.contains(property.toString(), value);
}
public static Expression icontains(Property<String> property, String value) {
return Expr.icontains(property.toString(), value);
}
}
@@ -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<Temporal> property, java.util.Calendar value) {
return Expr.eq(property.toString(), value);
}
public static Expression eqOrNull(Property<Temporal> property, java.util.Calendar value) {
return Expr.factory().eqOrNull(property.toString(), value);
}
public static Expression ne(Property<Temporal> property, java.util.Calendar value) {
return Expr.factory().ne(property.toString(), value);
}
public static Expression lt(Property<Temporal> property, java.util.Calendar value) {
return Expr.factory().lt(property.toString(), value);
}
public static Expression ltOrNull(Property<Temporal> property, java.util.Calendar value) {
return Expr.factory().ltOrNull(property.toString(), value);
}
public static Expression le(Property<Temporal> property, java.util.Calendar value) {
return Expr.factory().le(property.toString(), value);
}
public static Expression leOrNull(Property<Temporal> property, java.util.Calendar value) {
return Expr.factory().leOrNull(property.toString(), value);
}
}
@@ -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<Number> sum(Property<? extends Number> property) {
return Property.of("sum(" + property + ")");
}
public static Property<Number> count(Property<?> property) {
return Property.of("count(" + property + ")");
}
public static <T> Property<T> avg(Property<T> property) {
return Property.of("avg(" + property + ")");
}
public static <T> Property<T> max(Property<T> property) {
return Property.of("max(" + property + ")");
}
public static <T> Property<T> min(Property<T> property) {
return Property.of("min(" + property + ")");
}
public static <T> Property<T> coalesce(Property<T> property, Object value) {
return Property.of("coalesce(" + property.toString() + "," + sqlValue(value) + ")");
}
public static Property<String> lower(Property<String> property) {
return Property.of("lower(" + property + ")");
}
public static Property<String> upper(Property<String> property) {
return Property.of("upper(" + property + ")");
}
public static Property<String> 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 <T> Expression eq(Property<T> property, T value) {
return Expr.eq(property.toString(), value);
}
public static <T> Expression eq(Property<T> property, Query<?> subQuery) {
return Expr.in(property.toString(), subQuery);
}
public static Expression eq(Property<Temporal> property, java.util.Date value) {
return Expr.eq(property.toString(), value);
}
public static <T> Expression eqOrNull(Property<T> property, T value) {
return Expr.factory().eqOrNull(property.toString(), value);
}
public static Expression eqOrNull(Property<Temporal> property, java.util.Date value) {
return Expr.factory().eqOrNull(property.toString(), value);
}
public static <T> Expression ne(Property<T> property, T value) {
return Expr.ne(property.toString(), value);
}
public static <T> Expression ne(Property<T> property, Query<?> subQuery) {
return Expr.ne(property.toString(), subQuery);
}
public static Expression ne(Property<Temporal> property, java.util.Date value) {
return Expr.ne(property.toString(), value);
}
public static <T> Expression gt(Property<T> property, T value) {
return Expr.gt(property.toString(), value);
}
public static <T> Expression gt(Property<T> property, Query<?> subQuery) {
return Expr.gt(property.toString(), subQuery);
}
public static Expression gt(Property<Temporal> property, java.util.Date value) {
return Expr.gt(property.toString(), value);
}
public static <T> Expression gtOrNull(Property<T> property, T value) {
return Expr.factory().gtOrNull(property.toString(), value);
}
public static Expression gtOrNull(Property<Temporal> property, java.util.Date value) {
return Expr.factory().gtOrNull(property.toString(), value);
}
public static <T> Expression ge(Property<T> property, T value) {
return Expr.ge(property.toString(), value);
}
public static <T> Expression ge(Property<T> property, Query<?> subQuery) {
return Expr.ge(property.toString(), subQuery);
}
public static Expression ge(Property<Temporal> property, java.util.Date value) {
return Expr.ge(property.toString(), value);
}
public static <T> Expression geOrNull(Property<T> property, T value) {
return Expr.factory().geOrNull(property.toString(), value);
}
public static Expression geOrNull(Property<Temporal> property, java.util.Date value) {
return Expr.factory().geOrNull(property.toString(), value);
}
public static <T> Expression lt(Property<T> property, T value) {
return Expr.lt(property.toString(), value);
}
public static <T> Expression lt(Property<T> property, Query<?> subQuery) {
return Expr.lt(property.toString(), subQuery);
}
public static Expression lt(Property<Temporal> property, java.util.Date value) {
return Expr.lt(property.toString(), value);
}
public static <T> Expression ltOrNull(Property<T> property, T value) {
return Expr.factory().ltOrNull(property.toString(), value);
}
public static Expression ltOrNull(Property<Temporal> property, java.util.Date value) {
return Expr.factory().ltOrNull(property.toString(), value);
}
public static <T> Expression le(Property<T> property, T value) {
return Expr.le(property.toString(), value);
}
public static <T> Expression le(Property<T> property, Query<?> subQuery) {
return Expr.le(property.toString(), subQuery);
}
public static Expression le(Property<Temporal> property, java.util.Date value) {
return Expr.le(property.toString(), value);
}
public static <T> Expression leOrNull(Property<T> property, T value) {
return Expr.factory().leOrNull(property.toString(), value);
}
public static Expression leOrNull(Property<Temporal> property, java.util.Date value) {
return Expr.factory().leOrNull(property.toString(), value);
}
public static <T> Expression inRange(Property<T> property, T lowValue, T highValue) {
return Expr.factory().inRange(property.toString(), lowValue, highValue);
}
public static Expression inRange(Property<Temporal> property, java.util.Date lowValue, java.util.Date highValue) {
return Expr.factory().inRange(property.toString(), lowValue, highValue);
}
public static <T> Expression inRange(Property<T> lowProperty, Property<T> highProperty, T value) {
return Expr.factory().inRangeWith(lowProperty.toString(), highProperty.toString(), value);
}
public static Expression inRange(Property<Temporal> lowProperty, Property<Temporal> highProperty, java.util.Date value) {
return Expr.factory().inRangeWith(lowProperty.toString(), highProperty.toString(), value);
}
public static <T> Expression inRange(Property<T> lowProperty, Property<T> property, Property<T> highProperty) {
return Expr.factory().inRangeWithProperties(lowProperty.toString(), property.toString(), highProperty.toString());
}
public static <T> Expression in(Property<T> property, Collection<T> value) {
return Expr.in(property.toString(), value);
}
public static <T> Expression in(Property<T> property, Query<?> subQuery) {
return Expr.in(property.toString(), subQuery);
}
public static <T> Expression inOrEmpty(Property<T> property, Collection<T> value) {
return Expr.inOrEmpty(property.toString(), value);
}
public static <T> Expression notIn(Property<T> property, Collection<T> value) {
return Expr.factory().notIn(property.toString(), value);
}
public static <T> Expression notIn(Property<T> property, Query<?> subQuery) {
return Expr.factory().notIn(property.toString(), subQuery);
}
// ---- String operators ---- //
public static Expression like(Property<String> property, String value) {
return Expr.like(property.toString(), value);
}
public static Expression ilike(Property<String> property, String value) {
return Expr.ilike(property.toString(), value);
}
public static Expression startsWith(Property<String> property, String value) {
return Expr.startsWith(property.toString(), value);
}
public static Expression istartsWith(Property<String> property, String value) {
return Expr.istartsWith(property.toString(), value);
}
public static Expression contains(Property<String> property, String value) {
return Expr.contains(property.toString(), value);
}
public static Expression icontains(Property<String> property, String value) {
return Expr.icontains(property.toString(), value);
}
}
@@ -118,7 +118,7 @@ public interface UpdateQuery<T> {
* @param property The bean property to be set
* @param value The value to set the property to
*/
UpdateQuery<T> set(Query.Property<?> property, Object value);
<P> UpdateQuery<T> set(Query.Property<P> property, P value);
/**
* Set the property to be null.
@@ -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 {
@@ -7,7 +7,7 @@ package io.ebean.typequery;
* @param <T> the number type
*/
@SuppressWarnings("rawtypes")
public abstract class PBaseNumber<R, T extends Comparable> extends PBaseCompareable<R, T, Number> {
public abstract class PBaseNumber<R, T extends Comparable> extends PBaseCompareable<R, T, T> {
/**
* Construct with a property name and root instance.
@@ -6,7 +6,7 @@ package io.ebean.typequery;
* @param <E> the enum specific type
* @param <R> the root query bean type
*/
public final class PEnum<R, E> extends PBaseValueEqual<R, E, Object> {
public final class PEnum<R, E> extends PBaseValueEqual<R, E, E> {
/**
* Construct with a property name and root instance.
@@ -278,7 +278,7 @@ public abstract class TQRootBean<T, R> {
/**
* 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
*/
@@ -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() {
@@ -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<Order> 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();
@@ -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;
/**