Add filterManyRaw() and Deprecate filterMany( <string expressions> )

The filterMany( <string expressions> ) uses the language parser which
ultimately will we probably remove in ebean 15.x

Code can migrate to filterManyRaw() instead or better yet change to
use the improved query beans filterMany( <closure> ) option that will
be included in this same ebean release.
This commit is contained in:
Rob Bygrave
2023-08-21 11:15:39 +12:00
parent 3e41c5973b
commit fbcd4c3133
6 changed files with 140 additions and 1 deletions
@@ -508,6 +508,8 @@ public interface ExpressionList<T> {
ExpressionList<T> filterMany(String manyProperty);
/**
* Deprecated for removal - migrate to filterManyRaw()
* <p>
* Add filter expressions to the many property.
*
* <pre>{@code
@@ -524,8 +526,29 @@ public interface ExpressionList<T> {
* @param expressions Filter expressions with and, or and ? or ?1 type bind parameters
* @param params Bind parameters used in the expressions
*/
@Deprecated(forRemoval = true)
ExpressionList<T> filterMany(String manyProperty, String expressions, Object... params);
/**
* Add filter expressions for the many path. The expressions can include SQL functions if
* desired and the property names are translated to column names.
* <p>
* The expressions can contain placeholders for bind values using <code>?</code> or <code>?1</code> style.
*
* <pre>{@code
*
* new QCustomer()
* .name.startsWith("Postgres")
* .contacts.filterMany("status = ? and firstName like ?", Contact.Status.NEW, "Rob%")
* .findList();
*
* }</pre>
*
* @param rawExpressions The raw expressions which can include ? and ?1 style bind parameter placeholders
* @param params The parameter values to bind
*/
ExpressionList<T> filterManyRaw(String manyProperty, String rawExpressions, Object... params);
/**
* Specify specific properties to fetch on the main/root bean (aka partial
* object).
@@ -444,6 +444,11 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return query.filterMany(manyProperty).where(expressions, params);
}
@Override
public ExpressionList<T> filterManyRaw(String manyProperty, String rawExpression, Object... params) {
return query.filterMany(manyProperty).raw(rawExpression, params);
}
@Override
public Query<T> withLock(Query.LockType lockType) {
return query.withLock(lockType);
@@ -317,6 +317,11 @@ final class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expr
throw new IllegalStateException("filterMany not allowed on Junction expression list");
}
@Override
public ExpressionList<T> filterManyRaw(String manyProperty, String rawExpression, Object... params) {
throw new IllegalStateException("filterMany not allowed on Junction expression list");
}
@Override
public Query<T> usingTransaction(Transaction transaction) {
return exprList.usingTransaction(transaction);
@@ -188,6 +188,8 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
}
/**
* Deprecated for removal - migrate to filterManyRaw()
* <p>
* Apply a filter when fetching these beans.
* <p>
* The expressions can use any valid Ebean expression and contain
@@ -215,11 +217,35 @@ public abstract class TQAssocBean<T, R, QB> extends TQAssoc<T, R> {
* @param expressions The expressions including and, or, not etc with ? and ?1 bind params.
* @param params The bind parameter values
*/
@Deprecated(forRemoval = true)
public final R filterMany(String expressions, Object... params) {
expr().filterMany(_name, expressions, params);
return _root;
}
/**
* Add filter expressions for the many path. The expressions can include SQL functions if
* desired and the property names are translated to column names.
* <p>
* The expressions can contain placeholders for bind values using <code>?</code> or <code>?1</code> style.
*
* <pre>{@code
*
* new QCustomer()
* .name.startsWith("Shrek")
* .contacts.filterMany("status = ? and firstName like ?", Contact.Status.NEW, "Rob%")
* .findList();
*
* }</pre>
*
* @param rawExpressions The raw expressions which can include ? and ?1 style bind parameter placeholders
* @param params The parameter values to bind
*/
public final R filterManyRaw(String rawExpressions, Object... params) {
expr().filterManyRaw(_name, rawExpressions, params);
return _root;
}
/**
* Is empty for a collection property.
* <p>
@@ -280,6 +280,41 @@ public class QCustomerTest {
assertThat(q.getGeneratedSql()).isEqualTo("select t0.id, t0.name, t1.id, t1.first_name, t1.last_name from be_customer t0 left join be_contact t1 on t1.customer_id = t0.id where t1.first_name like ? escape'|' order by t0.id");
}
@Test
void filterManySeparateQuery() {
Customer cust = new Customer();
cust.setName("filterManySeparateQuery");
cust.setStatus(Customer.Status.GOOD);
cust.save();
var q = new QCustomer()
.select(FGCustomerContacts)
.contacts.filterManyRaw("firstName like ?", "R%")
.contacts.filterMany(c -> c.firstName.startsWith("R")) // same as filterManyRaw() expression
.setMaxRows(10) // force the ToMany path to be in a separate secondary query
.query();
q.findList();
assertThat(q.getGeneratedSql()).isEqualTo("select t0.id, t0.name from be_customer t0 limit 10");
}
@Test
void filterManySingleQuery() {
Customer cust = new Customer();
cust.setName("filterManySingleQuery");
cust.setStatus(Customer.Status.GOOD);
cust.save();
var q = new QCustomer()
.select(FGCustomerContacts)
.contacts.filterManyRaw("firstName like ?", "R%")
.contacts.filterMany(c -> c.firstName.startsWith("R")) // same as filterManyRaw() expression
.query();
q.findList();
assertThat(q.getGeneratedSql()).isEqualTo("select t0.id, t0.name from be_customer t0 limit 10");
}
@Test
public void testIdIn() {
@@ -124,7 +124,7 @@ public class TestQueryFilterMany extends BaseTestCase {
List<String> sqlList = LoggedSql.stop();
assertEquals(2, sqlList.size());
assertThat(sqlList.get(0)).contains("lower(t0.name) = ?");
assertThat(sqlList.get(1)).contains("status = ?");
assertThat(sqlList.get(1)).contains("t0.status = ?");
if (isH2() || isPostgresCompatible()) {
assertThat(sqlList.get(0)).doesNotContain("offset");
@@ -133,6 +133,51 @@ public class TestQueryFilterMany extends BaseTestCase {
}
}
@Test
public void filterManyRaw_firstMaxRows_expressionFluidStyle() {
ResetBasicData.reset();
LoggedSql.start();
final Query<Customer> query = DB.find(Customer.class)
.where().ieq("name", "Rob")
// use expression + fluid style adding maxRows/firstRow to filterMany
.filterManyRaw("orders", "status = ?", Order.Status.NEW)
.setMaxRows(100).setFirstRow(3).order("orderDate desc, id")
.order().asc("id").setMaxRows(5);
final List<Customer> customers = query.findList();
assertThat(customers).isNotEmpty();
List<String> sqlList = LoggedSql.stop();
assertEquals(2, sqlList.size());
assertThat(sqlList.get(0)).contains("lower(t0.name) = ?");
assertThat(sqlList.get(1)).contains("t0.status = ?");
if (isH2() || isPostgresCompatible()) {
assertThat(sqlList.get(0)).doesNotContain("offset");
assertThat(sqlList.get(0)).contains(" limit 5");
assertThat(sqlList.get(1)).contains(" order by t0.order_date desc, t0.id limit 100 offset 3");
}
}
@Test
public void filterManyRaw_singleQuery() {
ResetBasicData.reset();
LoggedSql.start();
final Query<Customer> query = DB.find(Customer.class)
.where().ieq("name", "Rob")
.filterManyRaw("orders", "status = ?", Order.Status.NEW)
.order().asc("id");
final List<Customer> customers = query.findList();
assertThat(customers).isNotEmpty();
List<String> sqlList = LoggedSql.stop();
assertEquals(1, sqlList.size());
assertThat(sqlList.get(0)).contains(" where lower(t0.name) = ? and t1.status = ? order by t0.id");
}
@Test
public void test_with_findOne_rawSeparateQuery() {
ResetBasicData.reset();