mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-23 01:07:49 +00:00
Add StdFunctions, StdExpressions, TQColumn marker interface
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
package io.ebean.typequery;
|
||||
|
||||
import io.ebean.Expr;
|
||||
import io.ebean.Expression;
|
||||
|
||||
public class StdExpressions {
|
||||
|
||||
public static Expression gt(TQColumn property, Object value) {
|
||||
return Expr.gt(property.toString(), value);
|
||||
}
|
||||
|
||||
public static Expression like(TQColumn property, String value) {
|
||||
return Expr.like(property.toString(), value);
|
||||
}
|
||||
|
||||
public static Expression ilike(TQColumn property, String value) {
|
||||
return Expr.ilike(property.toString(), value);
|
||||
}
|
||||
|
||||
public static Expression startsWith(TQColumn property, String value) {
|
||||
return Expr.startsWith(property.toString(), value);
|
||||
}
|
||||
|
||||
public static Expression istartsWith(TQColumn property, String value) {
|
||||
return Expr.istartsWith(property.toString(), value);
|
||||
}
|
||||
|
||||
public static Expression contains(TQColumn property, String value) {
|
||||
return Expr.contains(property.toString(), value);
|
||||
}
|
||||
|
||||
public static Expression icontains(TQColumn property, String value) {
|
||||
return Expr.icontains(property.toString(), value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package io.ebean.typequery;
|
||||
|
||||
public final class StdFunctions {
|
||||
|
||||
public static TQColumn max(TQColumn property) {
|
||||
return new Standard("max(" + property + ")");
|
||||
}
|
||||
|
||||
public static TQColumn sum(TQColumn property) {
|
||||
return new Standard("sum(" + property + ")");
|
||||
}
|
||||
|
||||
public static TQColumn concat(TQColumn property, Object... values) {
|
||||
StringBuilder expression = new StringBuilder(50);
|
||||
expression.append("concat(").append(property.toString());
|
||||
for (Object value : values) {
|
||||
expression.append(",").append(sqlStringExpression(value));
|
||||
}
|
||||
expression.append(")");
|
||||
return new Standard(expression.toString());
|
||||
}
|
||||
|
||||
public static TQColumn coalesce(TQColumn property, Object value) {
|
||||
StringBuilder expression = new StringBuilder(50);
|
||||
expression.append("coalesce(").append(property.toString()).append(",");
|
||||
expression.append(sqlStringExpression(value));
|
||||
expression.append(")");
|
||||
return new Standard(expression.toString());
|
||||
}
|
||||
|
||||
private static String sqlStringExpression(Object value) {
|
||||
if (value instanceof TQColumn || value instanceof Number) {
|
||||
return value.toString();
|
||||
} else {
|
||||
return "'" + value + "'";
|
||||
}
|
||||
}
|
||||
|
||||
private static class Standard implements TQColumn {
|
||||
|
||||
private final String expression;
|
||||
|
||||
private Standard(String expression) {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package io.ebean.typequery;
|
||||
|
||||
/**
|
||||
* Marker interface for type safe properties and expressions.
|
||||
*/
|
||||
public interface TQColumn {
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import io.ebean.ExpressionList;
|
||||
*
|
||||
* @param <R> The type of the owning root bean
|
||||
*/
|
||||
public class TQProperty<R> {
|
||||
public class TQProperty<R> implements TQColumn {
|
||||
|
||||
protected final String _name;
|
||||
|
||||
@@ -31,6 +31,7 @@ public class TQProperty<R> {
|
||||
this._name = TQPath.add(prefix, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return _name;
|
||||
}
|
||||
|
||||
@@ -233,8 +233,13 @@ public abstract class TQRootBean<T, R> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tune the query by specifying the properties to be loaded on the
|
||||
* 'main' root level entity bean (aka partial object).
|
||||
* Specify the properties to be loaded on the 'main' root level entity bean.
|
||||
* <p>
|
||||
* The resulting entities with be "partially loaded" aka partial objects.
|
||||
* <p>
|
||||
* Alternatively we can use a {@link #select(FetchGroup)} to specify all properties
|
||||
* to load on all parts of the graph.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // alias for the customer properties in select()
|
||||
@@ -245,12 +250,12 @@ public abstract class TQRootBean<T, R> {
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* new QCustomer()
|
||||
* // tune query
|
||||
* // specify the parts of the graph we want to load
|
||||
* .select(cust.id, cust.name)
|
||||
* .contacts.fetch(contact.firstName, contact.lastName, contact.email)
|
||||
*
|
||||
* // predicates
|
||||
* .id.greaterThan(1)
|
||||
* .id.gt(1)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
@@ -263,14 +268,25 @@ public abstract class TQRootBean<T, R> {
|
||||
return root;
|
||||
}
|
||||
|
||||
private Set<String> properties(TQProperty<R>[] properties) {
|
||||
private Set<String> properties(TQColumn[] properties) {
|
||||
Set<String> props = new LinkedHashSet<>();
|
||||
for (TQProperty<R> property : properties) {
|
||||
props.add(property.propertyName());
|
||||
for (TQColumn property : properties) {
|
||||
props.add(property.toString());
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the properties to be loaded on the 'main' root level entity bean
|
||||
* also allowing for functions to be used like {@link StdFunctions#max(TQColumn)}.
|
||||
*
|
||||
* @param properties the list of properties to fetch
|
||||
*/
|
||||
public final R select(TQColumn... properties) {
|
||||
((SpiQueryFetch) query).selectProperties(properties(properties));
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a path to load including all its properties.
|
||||
*
|
||||
@@ -499,6 +515,14 @@ public abstract class TQRootBean<T, R> {
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an expression to the WHERE or HAVING clause.
|
||||
*/
|
||||
public R add(Expression expression) {
|
||||
peekExprList().add(expression);
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set root table alias.
|
||||
*/
|
||||
|
||||
@@ -4,7 +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 io.ebean.typequery.StdExpressions;
|
||||
import org.example.domain.Customer;
|
||||
import org.example.domain.Order;
|
||||
import org.example.domain.OrderDetail;
|
||||
@@ -18,6 +18,9 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static io.ebean.typequery.StdExpressions.gt;
|
||||
import static io.ebean.typequery.StdExpressions.ilike;
|
||||
import static io.ebean.typequery.StdFunctions.*;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class QOrderTest {
|
||||
@@ -218,14 +221,51 @@ public class QOrderTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stdExpression_iLikeConcatCoalesce() {
|
||||
QOrder o = QOrder.alias();
|
||||
|
||||
// LOWER(CONCAT(COALESCE(a.name, ""), ":", a.description)) LIKE LOWER(:param)
|
||||
Query<Order> query = new QOrder()
|
||||
.select(o.id, o.status)
|
||||
.add(ilike(concat(coalesce(o.customer.name, "Not-Provided"), ":", o.status), "rob"))
|
||||
.query();
|
||||
|
||||
query.findList();
|
||||
String generatedSql = query.getGeneratedSql();
|
||||
assertThat(generatedSql).contains("where lower(concat(coalesce(t1.name,'Not-Provided'),':',t0.status)) like ? escape''");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stdExpression_gtCoalesce() {
|
||||
QOrder o = QOrder.alias();
|
||||
|
||||
Query<Order> query = new QOrder()
|
||||
.select(o.id, o.status)
|
||||
.or()
|
||||
.add(gt(coalesce(o.customer.version, 0), 42))
|
||||
.id.lt(12)
|
||||
.endOr()
|
||||
.query();
|
||||
|
||||
query.findList();
|
||||
|
||||
String sql = query.getGeneratedSql();
|
||||
assertThat(sql).contains(" where (coalesce(t1.version,0) > ? or t0.id < ?)");
|
||||
assertThat(sql).isEqualTo("select t0.id, t0.status from o_order t0 join be_customer t1 on t1.id = t0.customer_id where (coalesce(t1.version,0) > ? or t0.id < ?)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void geSubQuery() {
|
||||
var c = QCustomer.alias();
|
||||
var o = QOrder.alias();
|
||||
|
||||
var subQuery = new QOrder()
|
||||
.select(StdFunctions.max(QOrder.Alias.orderDate))
|
||||
.select(max(o.orderDate))
|
||||
.query();
|
||||
|
||||
var query = new QCustomer()
|
||||
.select(QCustomer.Alias.id)
|
||||
.select(c.id)
|
||||
.registered.ge(subQuery)
|
||||
.query();
|
||||
query.findList();
|
||||
@@ -236,22 +276,22 @@ public class QOrderTest {
|
||||
@Test
|
||||
void gtSubQuery() {
|
||||
var subQuery = new QOrder()
|
||||
.select("max(orderDate)")
|
||||
.select(sum(QOrder.Alias.version))
|
||||
.query();
|
||||
|
||||
var query = new QCustomer()
|
||||
.select(QCustomer.Alias.id)
|
||||
.registered.gt(subQuery)
|
||||
.version.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)");
|
||||
assertThat(query.getGeneratedSql()).contains("select t0.id from be_customer t0 where t0.version > (select sum(t0.version) from o_order t0)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void leSubQuery() {
|
||||
var subQuery = new QOrder()
|
||||
.select("max(orderDate)")
|
||||
.select(max(QOrder.Alias.orderDate))
|
||||
.query();
|
||||
|
||||
var query = new QCustomer()
|
||||
@@ -266,7 +306,7 @@ public class QOrderTest {
|
||||
@Test
|
||||
void ltSubQuery() {
|
||||
var subQuery = new QOrder()
|
||||
.select("max(orderDate)")
|
||||
.select(max(QOrder.Alias.orderDate))
|
||||
.query();
|
||||
|
||||
var query = new QCustomer()
|
||||
@@ -296,7 +336,7 @@ public class QOrderTest {
|
||||
@Test
|
||||
void neSubQuery() {
|
||||
var subQuery = new QOrder()
|
||||
.select(StdFunctions.max(QOrder.Alias.orderDate))
|
||||
.select(max(QOrder.Alias.orderDate))
|
||||
.query();
|
||||
|
||||
var query = new QCustomer()
|
||||
@@ -375,9 +415,4 @@ public class QOrderTest {
|
||||
order.save();
|
||||
}
|
||||
|
||||
static class StdFunctions {
|
||||
static String max(TQProperty<?> prop) {
|
||||
return "max(" + prop + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user