diff --git a/ebean-api/src/main/java/io/ebean/ExpressionList.java b/ebean-api/src/main/java/io/ebean/ExpressionList.java index 517e6bac42..cb1c380b5d 100644 --- a/ebean-api/src/main/java/io/ebean/ExpressionList.java +++ b/ebean-api/src/main/java/io/ebean/ExpressionList.java @@ -412,6 +412,42 @@ default A findSingleAttribute() { */ Optional findOneOrEmpty(); + /** + * Execute find row count query in a background thread. + *

+ * This returns a Future object which can be used to cancel, check the + * execution status (isDone etc) and get the value (with or without a + * timeout). + *

+ * + * @return a Future object for the row count query + */ + FutureRowCount findFutureCount(); + + /** + * Execute find Id's query in a background thread. + *

+ * This returns a Future object which can be used to cancel, check the + * execution status (isDone etc) and get the value (with or without a + * timeout). + *

+ * + * @return a Future object for the list of Id's + */ + FutureIds findFutureIds(); + + /** + * Execute find list query in a background thread. + *

+ * This returns a Future object which can be used to cancel, check the + * execution status (isDone etc) and get the value (with or without a + * timeout). + *

+ * + * @return a Future object for the list result of the query + */ + FutureList findFutureList(); + /** * Return a PagedList for this query using firstRow and maxRows. *

diff --git a/ebean-api/src/main/java/io/ebean/FutureIds.java b/ebean-api/src/main/java/io/ebean/FutureIds.java new file mode 100644 index 0000000000..92177a5c37 --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/FutureIds.java @@ -0,0 +1,20 @@ +package io.ebean; + +import java.util.List; +import java.util.concurrent.Future; + +/** + * FutureIds represents the result of a background query execution for the Id's. + *

+ * It extends the java.util.concurrent.Future with the ability to get the Id's + * while the query is still executing in the background. + *

+ */ +public interface FutureIds extends Future> { + + /** + * Returns the original query used to fetch the Id's. + */ + Query getQuery(); + +} diff --git a/ebean-api/src/main/java/io/ebean/Query.java b/ebean-api/src/main/java/io/ebean/Query.java index b12dcd5aed..0c78f1e117 100644 --- a/ebean-api/src/main/java/io/ebean/Query.java +++ b/ebean-api/src/main/java/io/ebean/Query.java @@ -1087,6 +1087,41 @@ enum LockWait { */ int findCount(); + /** + * Execute find row count query in a background thread. + *

+ * This returns a Future object which can be used to cancel, check the + * execution status (isDone etc) and get the value (with or without a + * timeout). + *

+ * + * @return a Future object for the row count query + */ + FutureRowCount findFutureCount(); + + /** + * Execute find Id's query in a background thread. + *

+ * This returns a Future object which can be used to cancel, check the + * execution status (isDone etc) and get the value (with or without a + * timeout). + *

+ * + * @return a Future object for the list of Id's + */ + FutureIds findFutureIds(); + + /** + * Execute find list query in a background thread. + *

+ * This query will execute in it's own PersistenceContext and using its own transaction. + * What that means is that it will not share any bean instances with other queries. + *

+ * + * @return a Future object for the list result of the query + */ + FutureList findFutureList(); + /** * Return a PagedList for this query using firstRow and maxRows. *

diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java index e8e1075e8a..27005c5864 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java @@ -336,6 +336,10 @@ public interface SpiEbeanServer extends SpiServer, ExtendedServer, BeanCollectio FutureRowCount findFutureCount(SpiQuery query); + FutureIds findFutureIds(SpiQuery query); + + FutureList findFutureList(SpiQuery query); + PagedList findPagedList(SpiQuery query); Set findSet(SpiQuery query); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index 5692a59f6e..afd4d671f4 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -1200,6 +1200,47 @@ public FutureRowCount findFutureCount(SpiQuery query) { return queryFuture; } + @Override + public FutureIds findFutureIds(SpiQuery query) { + SpiQuery copy = query.copy(); + copy.usingFuture(); + boolean createdTransaction = false; + SpiTransaction transaction = query.transaction(); + if (transaction == null) { + transaction = currentServerTransaction(); + if (transaction == null) { + transaction = (SpiTransaction) createTransaction(); + createdTransaction = true; + } + copy.usingTransaction(transaction); + } + QueryFutureIds queryFuture = new QueryFutureIds<>(new CallableQueryIds<>(this, copy, createdTransaction)); + backgroundExecutor.execute(queryFuture.futureTask()); + return queryFuture; + } + + @Override + public FutureList findFutureList(SpiQuery query) { + SpiQuery spiQuery = query.copy(); + spiQuery.usingFuture(); + // FutureList query always run in it's own persistence content + spiQuery.setPersistenceContext(new DefaultPersistenceContext()); + // Create a new transaction solely to execute the findList() at some future time + boolean createdTransaction = false; + SpiTransaction transaction = query.transaction(); + if (transaction == null) { + transaction = currentServerTransaction(); + if (transaction == null) { + transaction = (SpiTransaction) createTransaction(); + createdTransaction = true; + } + spiQuery.usingTransaction(transaction); + } + QueryFutureList queryFuture = new QueryFutureList<>(new CallableQueryList<>(this, spiQuery, createdTransaction)); + backgroundExecutor.execute(queryFuture.futureTask()); + return queryFuture; + } + @Override public PagedList findPagedList(SpiQuery query) { int maxRows = query.getMaxRows(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java index 7e2b59a3fa..0c34f48450 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java @@ -218,6 +218,21 @@ public int update(Transaction transaction) { return query.update(transaction); } + @Override + public FutureIds findFutureIds() { + return query.findFutureIds(); + } + + @Override + public FutureRowCount findFutureCount() { + return query.findFutureCount(); + } + + @Override + public FutureList findFutureList() { + return query.findFutureList(); + } + @Override public PagedList findPagedList() { return query.findPagedList(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/FilterExpressionList.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/FilterExpressionList.java index ba4ad8af50..34bdb75443 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/FilterExpressionList.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/FilterExpressionList.java @@ -49,6 +49,21 @@ public ExpressionList filterMany(String prop) { return rootQuery.filterMany(prop); } + @Override + public FutureIds findFutureIds() { + return rootQuery.findFutureIds(); + } + + @Override + public FutureList findFutureList() { + return rootQuery.findFutureList(); + } + + @Override + public FutureRowCount findFutureCount() { + return rootQuery.findFutureCount(); + } + @Override public List findList() { return rootQuery.findList(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java index 9a25812588..acc26befce 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java @@ -328,6 +328,21 @@ public boolean exists() { return exprList.exists(); } + @Override + public FutureIds findFutureIds() { + return exprList.findFutureIds(); + } + + @Override + public FutureList findFutureList() { + return exprList.findFutureList(); + } + + @Override + public FutureRowCount findFutureCount() { + return exprList.findFutureCount(); + } + @Override public List findIds() { return exprList.findIds(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultFetchGroupQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultFetchGroupQuery.java index dcf1c7f82a..5f25e9881a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultFetchGroupQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultFetchGroupQuery.java @@ -12,6 +12,9 @@ import io.ebean.FetchConfig; import io.ebean.FetchGroup; import io.ebean.FetchPath; +import io.ebean.FutureIds; +import io.ebean.FutureList; +import io.ebean.FutureRowCount; import io.ebean.OrderBy; import io.ebean.PagedList; import io.ebean.PersistenceContextScope; @@ -348,6 +351,21 @@ public int findCount() { throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup"); } + @Override + public FutureRowCount findFutureCount() { + throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup"); + } + + @Override + public FutureIds findFutureIds() { + throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup"); + } + + @Override + public FutureList findFutureList() { + throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup"); + } + @Override public PagedList findPagedList() { throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup"); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/QueryFutureIds.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/QueryFutureIds.java new file mode 100644 index 0000000000..912075e58f --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/QueryFutureIds.java @@ -0,0 +1,41 @@ +package io.ebeaninternal.server.query; + +import io.ebean.FutureIds; +import io.ebean.Query; +import io.ebean.Transaction; + +import java.util.List; +import java.util.concurrent.FutureTask; + +/** + * Default implementation of FutureIds. + */ +public final class QueryFutureIds extends BaseFuture> implements FutureIds { + + private final CallableQueryIds call; + + public QueryFutureIds(CallableQueryIds call) { + super(new FutureTask<>(call)); + this.call = call; + } + + public FutureTask> futureTask() { + return futureTask; + } + + public Transaction transaction() { + return call.transaction; + } + + @Override + public Query getQuery() { + return call.query; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + call.query.cancel(); + return super.cancel(mayInterruptIfRunning); + } + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/QueryFutureList.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/QueryFutureList.java index e69de29bb2..e334cf7be2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/QueryFutureList.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/QueryFutureList.java @@ -0,0 +1,75 @@ +package io.ebeaninternal.server.query; + +import io.ebean.FutureList; +import io.ebean.Query; +import io.ebean.Transaction; + +import jakarta.persistence.PersistenceException; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * Default implementation for FutureList. + */ +public final class QueryFutureList extends BaseFuture> implements FutureList { + + private final CallableQueryList call; + + public QueryFutureList(CallableQueryList call) { + super(new FutureTask<>(call)); + this.call = call; + } + + public FutureTask> futureTask() { + return futureTask; + } + + public Transaction transaction() { + return call.transaction; + } + + @Override + public Query getQuery() { + return call.query; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + call.query.cancel(); + return super.cancel(mayInterruptIfRunning); + } + + @Override + public List getUnchecked() { + try { + return get(); + + } catch (InterruptedException e) { + // restore the interrupted status (so client can check for that) + Thread.currentThread().interrupt(); + throw new PersistenceException(e); + + } catch (ExecutionException e) { + throw new PersistenceException(e); + } + } + + @Override + public List getUnchecked(long timeout, TimeUnit unit) throws TimeoutException { + try { + return get(timeout, unit); + + } catch (InterruptedException e) { + // restore the interrupted status (so client can check for that) + Thread.currentThread().interrupt(); + throw new PersistenceException(e); + + } catch (ExecutionException e) { + throw new PersistenceException(e); + } + } + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 5c408e3aad..b099b31bc2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1489,6 +1489,21 @@ public final Optional findOneOrEmpty() { return server.findOneOrEmpty(this); } + @Override + public final FutureIds findFutureIds() { + return server.findFutureIds(this); + } + + @Override + public final FutureList findFutureList() { + return server.findFutureList(this); + } + + @Override + public final FutureRowCount findFutureCount() { + return server.findFutureCount(this); + } + @Override public final PagedList findPagedList() { return server.findPagedList(this); 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 7114b0b93c..41838ae7bc 100644 --- a/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java +++ b/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java @@ -1782,6 +1782,47 @@ public int findCount() { return query.findCount(); } + /** + * Execute find row count query in a background thread. + *

+ * This returns a Future object which can be used to cancel, check the + * execution status (isDone etc) and get the value (with or without a + * timeout). + *

+ * + * @return a Future object for the row count query + */ + public FutureRowCount findFutureCount() { + return query.findFutureCount(); + } + + /** + * Execute find Id's query in a background thread. + *

+ * This returns a Future object which can be used to cancel, check the + * execution status (isDone etc) and get the value (with or without a + * timeout). + *

+ * + * @return a Future object for the list of Id's + */ + public FutureIds findFutureIds() { + return query.findFutureIds(); + } + + /** + * Execute find list query in a background thread. + *

+ * This query will execute in it's own PersistenceContext and using its own transaction. + * What that means is that it will not share any bean instances with other queries. + *

+ * + * @return a Future object for the list result of the query + */ + public FutureList findFutureList() { + return query.findFutureList(); + } + /** * Return a PagedList for this query using firstRow and maxRows. *

diff --git a/ebean-test/src/test/java/io/ebean/xtest/internal/api/TDSpiEbeanServer.java b/ebean-test/src/test/java/io/ebean/xtest/internal/api/TDSpiEbeanServer.java index 289360021a..13b75b4799 100644 --- a/ebean-test/src/test/java/io/ebean/xtest/internal/api/TDSpiEbeanServer.java +++ b/ebean-test/src/test/java/io/ebean/xtest/internal/api/TDSpiEbeanServer.java @@ -574,6 +574,16 @@ public FutureRowCount findFutureCount(SpiQuery query) { return null; } + @Override + public FutureIds findFutureIds(SpiQuery query) { + return null; + } + + @Override + public FutureList findFutureList(SpiQuery query) { + return null; + } + @Override public PagedList findPagedList(SpiQuery query) { return null; diff --git a/ebean-test/src/test/java/io/ebean/xtest/internal/server/query/TestFutureRowCountErrorHandling.java b/ebean-test/src/test/java/io/ebean/xtest/internal/server/query/TestFutureRowCountErrorHandling.java new file mode 100644 index 0000000000..9d7360e44a --- /dev/null +++ b/ebean-test/src/test/java/io/ebean/xtest/internal/server/query/TestFutureRowCountErrorHandling.java @@ -0,0 +1,100 @@ +package io.ebean.xtest.internal.server.query; + +import io.ebean.*; +import io.ebean.xtest.BaseTestCase; +import io.ebeaninternal.server.query.QueryFutureIds; +import io.ebeaninternal.server.query.QueryFutureList; +import io.ebeaninternal.server.query.QueryFutureRowCount; +import org.junit.jupiter.api.Test; +import org.tests.model.basic.Customer; +import org.tests.model.basic.ResetBasicData; + +import java.util.concurrent.ExecutionException; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.fail; + +public class TestFutureRowCountErrorHandling extends BaseTestCase { + + @Test + public void testFutureRowCount() throws InterruptedException { + + ResetBasicData.reset(); + + Database server = DB.getDefault(); + + Query query = server.createQuery(Customer.class) + .where().eq("doesNotExist", "this will fail") + .query(); + + FutureRowCount futureRowCount = query.findFutureCount(); + + QueryFutureRowCount internalRowCount = (QueryFutureRowCount) futureRowCount; + Transaction t = internalRowCount.transaction(); + + try { + futureRowCount.get(); + fail("never get here as the SQL is invalid"); + + } catch (ExecutionException e) { + // Confirm the Transaction has been rolled back + assertFalse(t.isActive()); + } + + } + + @Test + public void testFutureIds() throws InterruptedException { + + ResetBasicData.reset(); + + Database server = DB.getDefault(); + + Query query = server.createQuery(Customer.class) + .where().eq("doesNotExist", "this will fail") + .query(); + + FutureIds futureIds = query.findFutureIds(); + + QueryFutureIds internalFuture = (QueryFutureIds) futureIds; + Transaction t = internalFuture.transaction(); + + try { + internalFuture.get(); + fail("never get here as the SQL is invalid"); + + } catch (ExecutionException e) { + // Confirm the Transaction has been rolled back + assertFalse(t.isActive()); + } + + } + + + @Test + public void testFutureList() throws InterruptedException { + + ResetBasicData.reset(); + + Database server = DB.getDefault(); + + Query query = server.createQuery(Customer.class) + .where().eq("doesNotExist", "this will fail") + .query(); + + FutureList futureList = query.findFutureList(); + + QueryFutureList internalFuture = (QueryFutureList) futureList; + Transaction t = internalFuture.transaction(); + + try { + internalFuture.get(); + fail("never get here as the SQL is invalid"); + + } catch (ExecutionException e) { + // Confirm the Transaction has been rolled back + assertFalse(t.isActive()); + } + + } +} diff --git a/ebean-test/src/test/java/org/tests/basic/TestFetchId.java b/ebean-test/src/test/java/org/tests/basic/TestFetchId.java index a151d88b62..00fb022527 100644 --- a/ebean-test/src/test/java/org/tests/basic/TestFetchId.java +++ b/ebean-test/src/test/java/org/tests/basic/TestFetchId.java @@ -2,6 +2,7 @@ import io.ebean.xtest.BaseTestCase; import io.ebean.DB; +import io.ebean.FutureIds; import io.ebean.Query; import org.junit.jupiter.api.Test; import org.tests.model.basic.Order; @@ -29,6 +30,12 @@ public void testFetchId() throws InterruptedException, ExecutionException { List ids = query.findIds(); assertThat(ids).isNotEmpty(); + + FutureIds futureIds = query.findFutureIds(); + + // wait for all the id's to be fetched + List idList = futureIds.get(); + assertThat(idList).isNotEmpty(); } @Test @@ -46,6 +53,11 @@ public void testFetchIdWithExists() throws InterruptedException, ExecutionExcept List ids = query.findIds(); // TODO: assert(query.getGeneratedSql()) assertThat(ids).isNotEmpty(); + FutureIds futureIds = query.findFutureIds(); + + // wait for all the id's to be fetched + List idList = futureIds.get(); + assertThat(idList).isNotEmpty(); } @Test diff --git a/ebean-test/src/test/java/org/tests/query/TestFindFutureRowCount.java b/ebean-test/src/test/java/org/tests/query/TestFindFutureRowCount.java index 0819626705..ac71ca4609 100644 --- a/ebean-test/src/test/java/org/tests/query/TestFindFutureRowCount.java +++ b/ebean-test/src/test/java/org/tests/query/TestFindFutureRowCount.java @@ -4,13 +4,14 @@ import io.ebean.xtest.BaseTestCase; import org.junit.jupiter.api.Test; import org.tests.model.basic.EBasic; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; class TestFindFutureRowCount extends BaseTestCase { @Test - void count_when_inTransaction() { + void count_when_inTransaction() throws Exception { try (Transaction transaction = DB.beginTransaction()) { EBasic basic = new EBasic("count_when_inTransaction"); DB.save(basic); @@ -29,7 +30,118 @@ void count_when_inTransaction() { pagedList.loadCount(); assertThat(pagedList.getList()).hasSize(1); assertThat(pagedList.getTotalCount()).isEqualTo(1); + + FutureRowCount futureCount = DB.find(EBasic.class) + .where().eq("name", "count_when_inTransaction") + .findFutureCount(); + + assertThat(futureCount.get()).isEqualTo(1); + + FutureRowCount futureCountUsingTxn = DB.find(EBasic.class) + .usingTransaction(transaction) + .where().eq("name", "count_when_inTransaction") + .findFutureCount(); + + assertThat(futureCountUsingTxn.get()).isEqualTo(1); + } + } + + @Test + void findFutureIds_when_inTransaction() throws Exception { + try (Transaction transaction = DB.beginTransaction()) { + EBasic basic = new EBasic("findFutureIds_when_inTransaction"); + DB.save(basic); + + List ids = DB.find(EBasic.class) + .where().eq("name", "findFutureIds_when_inTransaction") + .findIds(); + + Object expectedIdValue = ids.get(0); + + FutureIds futureIds = DB.find(EBasic.class) + .where().eq("name", "findFutureIds_when_inTransaction") + .findFutureIds(); + + List fids = futureIds.get(); + assertThat(fids).hasSize(1); + assertThat(fids.get(0)).isEqualTo(expectedIdValue); + + FutureIds futureIdsUsingTxn = DB.find(EBasic.class) + .usingTransaction(transaction) + .where().eq("name", "findFutureIds_when_inTransaction") + .findFutureIds(); + + List fids2 = futureIdsUsingTxn.get(); + assertThat(fids2).hasSize(1); + assertThat(fids2.get(0)).isEqualTo(expectedIdValue); + } + } + + @Test + void findFutureList_when_inTransaction() throws Exception { + try (Transaction transaction = DB.beginTransaction()) { + EBasic basic = new EBasic("findFutureList_when_inTransaction"); + DB.save(basic); + + List list = DB.find(EBasic.class) + .where().eq("name", "findFutureList_when_inTransaction") + .findList(); + + Object expectedIdValue = list.get(0).getId(); + + FutureList futureIds = DB.find(EBasic.class) + .where().eq("name", "findFutureList_when_inTransaction") + .findFutureList(); + + List fids = futureIds.get(); + assertThat(fids).hasSize(1); + assertThat(fids.get(0).getId()).isEqualTo(expectedIdValue); + + FutureList futureUsingTxn = DB.find(EBasic.class) + .usingTransaction(transaction) + .where().eq("name", "findFutureList_when_inTransaction") + .findFutureList(); + + List fids2 = futureUsingTxn.get(); + assertThat(fids2).hasSize(1); + assertThat(fids2.get(0).getId()).isEqualTo(expectedIdValue); } } + @Test + void findFutures_when_newTransaction() throws Exception { + EBasic basic = new EBasic("findFutures_when_newTransaction"); + DB.save(basic); + + List list = DB.find(EBasic.class) + .where().eq("name", "findFutures_when_newTransaction") + .findList(); + + Object expectedIdValue = list.get(0).getId(); + + FutureList futureList = DB.find(EBasic.class) + .where().eq("name", "findFutures_when_newTransaction") + .findFutureList(); + + List flist = futureList.get(); + assertThat(flist).hasSize(1); + assertThat(flist.get(0).getId()).isEqualTo(expectedIdValue); + + FutureIds futureIds = DB.find(EBasic.class) + .where().eq("name", "findFutures_when_newTransaction") + .findFutureIds(); + + List fids = futureIds.get(); + assertThat(fids).hasSize(1); + assertThat(fids.get(0)).isEqualTo(expectedIdValue); + + FutureRowCount futureCount = DB.find(EBasic.class) + .where().eq("name", "findFutures_when_newTransaction") + .findFutureCount(); + + assertThat(futureCount.get()).isEqualTo(1); + + DB.delete(basic); + } + } diff --git a/ebean-test/src/test/java/org/tests/query/TestQueryFindFutureList.java b/ebean-test/src/test/java/org/tests/query/TestQueryFindFutureList.java new file mode 100644 index 0000000000..01b9a0e765 --- /dev/null +++ b/ebean-test/src/test/java/org/tests/query/TestQueryFindFutureList.java @@ -0,0 +1,70 @@ +package org.tests.query; + +import io.ebean.xtest.BaseTestCase; +import io.ebean.DB; +import io.ebean.FutureList; +import io.ebean.Transaction; +import org.junit.jupiter.api.Test; +import org.tests.model.basic.Order; +import org.tests.model.basic.ResetBasicData; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TestQueryFindFutureList extends BaseTestCase { + + @Test + public void test_cancel() throws InterruptedException { + + ResetBasicData.reset(); + + // warm the connection pool + Transaction t0 = DB.createTransaction(); + Transaction t1 = DB.createTransaction(); + Transaction t2 = DB.createTransaction(); + t0.end(); + t1.end(); + t2.end(); + + FutureList futureList = DB.find(Order.class).findFutureList(); + + Thread.sleep(10); + futureList.cancel(true); + // calling again is ignored + futureList.cancel(true); + + // don't shutdown immediately + Thread.sleep(50); + } + + @Test + public void test_findFutureList() throws InterruptedException { + + ResetBasicData.reset(); + + FutureList futureList = DB.find(Order.class).findFutureList(); + + // wait for it to complete + List orders = futureList.getUnchecked(); + + assertEquals(DB.find(Order.class).findCount(), orders.size()); + } + + @Test + public void test_findFutureListWithTimeout() throws InterruptedException, TimeoutException { + + ResetBasicData.reset(); + + FutureList futureList = DB.find(Order.class).findFutureList(); + + // wait for it to complete + List orders = futureList.getUnchecked(1, TimeUnit.SECONDS); + + assertEquals(DB.find(Order.class).findCount(), orders.size()); + } + +} + diff --git a/ebean-test/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java b/ebean-test/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java index db339fbd8e..5e57a7e4c9 100644 --- a/ebean-test/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java +++ b/ebean-test/src/test/java/org/tests/query/cancel/SqlQueryCancelTest.java @@ -99,7 +99,10 @@ public void cancelSqlDuringRun() throws SQLException { @Test public void cancelOrmQueryAtBegin() throws SQLException { doCancelOrmAtBegin(Query::findCount); + doCancelOrmAtBegin(Query::findFutureCount); // We cannot test 'findCount' due H2 restrictions + doCancelOrmAtBegin(Query::findFutureIds); + doCancelOrmAtBegin(Query::findFutureList); doCancelOrmAtBegin(Query::findIds); doCancelOrmAtBegin(Query::findIterate); doCancelOrmAtBegin(Query::findList); @@ -123,6 +126,8 @@ public void cancelOrmDuringRun() throws Throwable { // doCancelOrmDuringRun(Query::findCount); // testDuringRunFuture(Query::findFutureCount); // We cannot test 'findCount' due H2 restrictions + doCancelOrmFutureDuringRun(Query::findFutureIds); + doCancelOrmFutureDuringRun(Query::findFutureList); doCancelOrmDuringRun(Query::findIds); doCancelOrmDuringRun(Query::findIterate); doCancelOrmDuringRun(Query::findList); diff --git a/ebean-test/src/test/java/org/tests/rawsql/TestRawSqlOrmQuery.java b/ebean-test/src/test/java/org/tests/rawsql/TestRawSqlOrmQuery.java index 25608c3615..48a5733432 100644 --- a/ebean-test/src/test/java/org/tests/rawsql/TestRawSqlOrmQuery.java +++ b/ebean-test/src/test/java/org/tests/rawsql/TestRawSqlOrmQuery.java @@ -138,7 +138,10 @@ void testFirstRowsMaxRows() throws InterruptedException, ExecutionException { List list = query.findList(); int rowCount = query.findCount(); + FutureRowCount futureRowCount = query.findFutureCount(); + assertEquals(initialRowCount, rowCount); + assertEquals(initialRowCount, futureRowCount.get().intValue()); // check that lazy loading still executes for (Customer customer : list) {