diff --git a/ebean-querybean/src/main/java/io/ebean/typequery/PBaseValueEqual.java b/ebean-querybean/src/main/java/io/ebean/typequery/PBaseValueEqual.java index 8fd7cf2d7..7d4edc0e9 100644 --- a/ebean-querybean/src/main/java/io/ebean/typequery/PBaseValueEqual.java +++ b/ebean-querybean/src/main/java/io/ebean/typequery/PBaseValueEqual.java @@ -83,6 +83,17 @@ public abstract class PBaseValueEqual extends TQPropertyBase { return _root; } + /** + * Is equal to another property. + * + * @param other the other property to compare + * @return the root query bean instance + */ + public final R eq(TQProperty other) { + expr().raw(_name + " = " + other.propertyName()); + return _root; + } + /** * Is equal to if value is non-null and otherwise no expression is added to the query. *

@@ -135,6 +146,17 @@ public abstract class PBaseValueEqual extends TQPropertyBase { return _root; } + /** + * Is not equal to another property. + * + * @param other the other property to compare + * @return the root query bean instance + */ + public final R ne(TQProperty other) { + expr().raw(_name + " <> " + other.propertyName()); + return _root; + } + /** * Is in a list of values. * 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 ef3565711..5680185fb 100644 --- a/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java +++ b/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java @@ -693,6 +693,22 @@ public abstract class TQRootBean { return root; } + /** + * Add EXISTS sub-query predicate. + */ + public R exists(Query subQuery) { + query.where().exists(subQuery); + return root; + } + + /** + * Add NOT EXISTS sub-query predicate. + */ + public R notExists(Query subQuery) { + query.where().notExists(subQuery); + return root; + } + /** * Execute using "for update" clause which results in the DB locking the record. */ diff --git a/ebean-querybean/src/test/java/org/querytest/QCustomerTest.java b/ebean-querybean/src/test/java/org/querytest/QCustomerTest.java index 7cb4c630f..cf32f9af2 100644 --- a/ebean-querybean/src/test/java/org/querytest/QCustomerTest.java +++ b/ebean-querybean/src/test/java/org/querytest/QCustomerTest.java @@ -155,6 +155,39 @@ public class QCustomerTest { } } + @Test + void equalTo_byProperty() { + Query query = new QCustomer() + .select(QCustomer.Alias.id) + .billingAddress.city.eq(QCustomer.Alias.shippingAddress.city) + .query(); + + query.findList(); + String generatedSql = query.getGeneratedSql(); + + // select t0.id from be_customer t0 + // left join o_address t2 on t2.id = t0.shipping_address_id + // left join o_address t1 on t1.id = t0.billing_address_id + // where t1.city = t2.city + assertThat(generatedSql).contains("where t1.city = t2.city"); + } + + @Test + void notEqual_byProperty() { + Query query = new QCustomer() + .select(QCustomer.Alias.id) + .billingAddress.city.ne(QCustomer.Alias.shippingAddress.city) + .query(); + + query.findList(); + String generatedSql = query.getGeneratedSql(); + + // select t0.id from be_customer t0 + // left join o_address t2 on t2.id = t0.shipping_address_id + // left join o_address t1 on t1.id = t0.billing_address_id + // where t1.city <> t2.city + assertThat(generatedSql).contains("where t1.city <> t2.city"); + } @Test public void isEmpty() { diff --git a/ebean-querybean/src/test/java/org/querytest/QOrderTest.java b/ebean-querybean/src/test/java/org/querytest/QOrderTest.java index 7cc9d3127..13d34bf1e 100644 --- a/ebean-querybean/src/test/java/org/querytest/QOrderTest.java +++ b/ebean-querybean/src/test/java/org/querytest/QOrderTest.java @@ -2,12 +2,15 @@ package org.querytest; import io.ebean.DB; import io.ebean.FetchGroup; +import io.ebean.Query; import io.ebean.test.LoggedSql; import org.example.domain.Customer; import org.example.domain.Order; +import org.example.domain.OrderDetail; import org.example.domain.otherpackage.PhoneNumber; import org.example.domain.query.QCustomer; import org.example.domain.query.QOrder; +import org.example.domain.query.QOrderDetail; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -214,6 +217,52 @@ public class QOrderTest { } + @Test + void subQueryExists() { + Query subQuery = new QOrderDetail() + .alias("od") + .orderQty.gt(10) + .id.eq(QOrder.Alias.details.id) + //.where().raw("details.id = od.id") + .query(); + + Query query = new QOrder() + .exists(subQuery) + .orderBy("orderDate").query(); + + query.findList(); + + // select distinct t0.id, t0.status, t0.order_date, t0.ship_date, t0.version, t0.when_created, t0.when_updated, t0.customer_id, t0.shipping_address_id, t0.order_date + // from o_order t0 + // left join o_order_detail t1 on t1.order_id = t0.id + // where exists (select 1 from o_order_detail od where od.order_qty > ? and od.id = t1.id) order by t0.order_date + String sql = query.getGeneratedSql(); + assertThat(sql).contains(" exists (select 1 from o_order_detail od where od.order_qty > ? and od.id = t1.id)"); + } + + @Test + void subQueryNotExists() { + Query subQuery = new QOrderDetail() + .alias("od") + .orderQty.gt(10) + .id.eq(QOrder.Alias.details.id) + //.where().raw("details.id = od.id") + .query(); + + Query query = new QOrder() + .notExists(subQuery) + .orderBy("orderDate") + .query(); + query.findList(); + + // select distinct t0.id, t0.status, t0.order_date, t0.ship_date, t0.version, t0.when_created, t0.when_updated, t0.customer_id, t0.shipping_address_id, t0.order_date + // from o_order t0 + // left join o_order_detail t1 on t1.order_id = t0.id + // where not exists (select 1 from o_order_detail od where od.order_qty > ? and od.id = t1.id) order by t0.order_date + String sql = query.getGeneratedSql(); + assertThat(sql).contains(" not exists (select 1 from o_order_detail od where od.order_qty > ? and od.id = t1.id)"); + } + private static void setupData() { customer = new Customer(); customer.name = "Fred";