diff --git a/src/main/java/io/ebean/ExpressionList.java b/src/main/java/io/ebean/ExpressionList.java index a001bcfdb..a7d655fc5 100644 --- a/src/main/java/io/ebean/ExpressionList.java +++ b/src/main/java/io/ebean/ExpressionList.java @@ -226,6 +226,32 @@ public interface ExpressionList { */ int update(Transaction transaction); + /** + * Execute the query returning true if a row is found. + *

+ * The query is executed using max rows of 1 and will only select the id property. + * This method is really just a convenient way to optimise a query to perform a + * 'does a row exist in the db' check. + *

+ * + *

Example:

+ *
{@code
+   *
+   *   boolean userExists = query().where().eq("email", "rob@foo.com").exists();
+   *
+   * }
+ * + *

Example using a query bean:

+ *
{@code
+   *
+   *   boolean userExists = new QContact().email.equalTo("rob@foo.com").exists();
+   *
+   * }
+ * + * @return True if the query finds a matching row in the database + */ + boolean exists(); + /** * Execute the query iterating over the results. * diff --git a/src/main/java/io/ebean/ExtendedServer.java b/src/main/java/io/ebean/ExtendedServer.java index 0b159f877..deb3582f2 100644 --- a/src/main/java/io/ebean/ExtendedServer.java +++ b/src/main/java/io/ebean/ExtendedServer.java @@ -1,5 +1,8 @@ package io.ebean; +import io.ebeaninternal.api.SpiQuery; +import io.ebeaninternal.api.SpiTransaction; + import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.persistence.NonUniqueResultException; @@ -42,6 +45,32 @@ public interface ExtendedServer { */ void setClock(Clock clock); + /** + * Execute the query returning true if a row is found. + *

+ * The query is executed using max rows of 1 and will only select the id property. + * This method is really just a convenient way to optimise a query to perform a + * 'does a row exist in the db' check. + *

+ * + *

Example:

+ *
{@code
+   *
+   *   boolean userExists = query().where().eq("email", "rob@foo.com").exists();
+   *
+   * }
+ * + *

Example using a query bean:

+ *
{@code
+   *
+   *   boolean userExists = new QContact().email.equalTo("rob@foo.com").exists();
+   *
+   * }
+ * + * @return True if the query finds a matching row in the database + */ + boolean exists(SpiQuery ormQuery, SpiTransaction transaction); + /** * Return the number of 'top level' or 'root' entities this query should return. * diff --git a/src/main/java/io/ebean/Query.java b/src/main/java/io/ebean/Query.java index 601e3a889..4f0fab7de 100644 --- a/src/main/java/io/ebean/Query.java +++ b/src/main/java/io/ebean/Query.java @@ -868,6 +868,32 @@ public interface Query { */ boolean isCountDistinct(); + /** + * Execute the query returning true if a row is found. + *

+ * The query is executed using max rows of 1 and will only select the id property. + * This method is really just a convenient way to optimise a query to perform a + * 'does a row exist in the db' check. + *

+ * + *

Example:

+ *
{@code
+   *
+   *   boolean userExists = query().where().eq("email", "rob@foo.com").exists();
+   *
+   * }
+ * + *

Example using a query bean:

+ *
{@code
+   *
+   *   boolean userExists = new QContact().email.equalTo("rob@foo.com").exists();
+   *
+   * }
+ * + * @return True if the query finds a matching row in the database + */ + boolean exists(); + /** * Execute the query returning either a single bean or null (if no matching * bean is found). diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index 5b9e2893c..77f0e7d0d 100644 --- a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -1363,6 +1363,21 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { return !ids.isEmpty(); } + @Override + public boolean exists(SpiQuery ormQuery, SpiTransaction transaction) { + + ormQuery.setMaxRows(1); + SpiOrmQueryRequest request = createQueryRequest(Type.ID_LIST, ormQuery, transaction); + try { + request.initTransIfRequired(); + List ids = request.findIds(); + return !ids.isEmpty(); + + } finally { + request.endTransIfRequired(); + } + } + @Override public List findIds(Query query, Transaction t) { @@ -1444,10 +1459,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { return queryFuture; } - public FutureRowCount findFutureRowCount(Query q, Transaction t) { - return findFutureCount(q, t); - } - @Override public FutureIds findFutureIds(Query query, Transaction t) { diff --git a/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java b/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java index 85a8ffb26..4d4d6c432 100644 --- a/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java +++ b/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java @@ -443,6 +443,11 @@ public class DefaultExpressionList implements SpiExpressionList { return query.findSingleAttributeList(); } + @Override + public boolean exists() { + return query.exists(); + } + @Override public T findOne() { return query.findOne(); diff --git a/src/main/java/io/ebeaninternal/server/expression/FilterExpressionList.java b/src/main/java/io/ebeaninternal/server/expression/FilterExpressionList.java index a6578ed01..ffaa52702 100644 --- a/src/main/java/io/ebeaninternal/server/expression/FilterExpressionList.java +++ b/src/main/java/io/ebeaninternal/server/expression/FilterExpressionList.java @@ -99,6 +99,11 @@ public class FilterExpressionList extends DefaultExpressionList { return rootQuery.findOneOrEmpty(); } + @Override + public boolean exists() { + return rootQuery.exists(); + } + @Override public ExpressionList having() { throw new PersistenceException(notAllowedMessage); diff --git a/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java b/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java index e1c5a5a79..cd74c635d 100644 --- a/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java +++ b/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java @@ -375,6 +375,11 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression return exprList.apply(fetchPath); } + @Override + public boolean exists() { + return exprList.exists(); + } + @Override public FutureIds findFutureIds() { return exprList.findFutureIds(); diff --git a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 4b4b906dd..07608ff51 100644 --- a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1398,6 +1398,11 @@ public class DefaultOrmQuery implements SpiQuery { return server.findIds(this, null); } + @Override + public boolean exists() { + return server.exists(this, null); + } + @Override public int findCount() { // a copy of this query is made in the server diff --git a/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java b/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java index afa192641..2a7026e04 100644 --- a/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java +++ b/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java @@ -632,6 +632,11 @@ public class TDSpiEbeanServer implements SpiEbeanServer { return false; } + @Override + public boolean exists(SpiQuery ormQuery, SpiTransaction transaction) { + return false; + } + @Override public T find(Class beanType, Object uid) { return null; diff --git a/src/test/java/org/tests/query/TestQueryExists.java b/src/test/java/org/tests/query/TestQueryExists.java index ea0b19298..84aac4fb6 100644 --- a/src/test/java/org/tests/query/TestQueryExists.java +++ b/src/test/java/org/tests/query/TestQueryExists.java @@ -3,11 +3,10 @@ package org.tests.query; import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.Query; +import org.junit.Test; import org.tests.model.basic.Customer; import org.tests.model.basic.Order; import org.tests.model.basic.ResetBasicData; -import org.junit.Assert; -import org.junit.Test; import java.util.List; @@ -15,6 +14,27 @@ import static org.assertj.core.api.Assertions.assertThat; public class TestQueryExists extends BaseTestCase { + @Test + public void testExistsBoolean_basic() { + + ResetBasicData.reset(); + + Query query = Ebean.find(Order.class) + .where().gt("id", 1) + .query(); + + boolean check = query.exists(); + assertThat(check).isTrue(); + + String sql = sqlOf(query); + if (isH2() || isPostgres()) { + assertThat(sql).contains("select t0.id from o_order t0 where t0.id > ? limit 1"); + } + + assertThat(Ebean.find(Order.class).where().gt("id", 1).exists()).isTrue(); + assertThat(Ebean.find(Order.class).where().or().gt("id", 1).isNull("shipDate").exists()).isTrue(); + } + @Test public void testExists_orders_onOneToMany() { @@ -73,7 +93,7 @@ public class TestQueryExists extends BaseTestCase { query.findList(); String sql = query.getGeneratedSql(); - Assert.assertTrue(sql.indexOf("exists (") > 0); + assertThat(sql).contains("exists ("); } @Test @@ -86,6 +106,6 @@ public class TestQueryExists extends BaseTestCase { query.findList(); String sql = query.getGeneratedSql(); - Assert.assertTrue(sql.indexOf("not exists (") > 0); + assertThat(sql).contains("not exists ("); } }