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 8c6ddaf3d..8d3a6ba70 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 @@ -1091,7 +1091,13 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { @Override @SuppressWarnings({"unchecked", "rawtypes"}) public Set findSet(Query query, Transaction transaction) { - SpiOrmQueryRequest request = createQueryRequest(Type.SET, query, transaction); + SpiOrmQueryRequest request = buildQueryRequest(Type.SET, query, transaction); + request.resetBeanCacheAutoMode(false); + if ((transaction == null || !transaction.isSkipCache()) && request.getFromBeanCache()) { + // hit bean cache and got all results from cache + return request.beanCacheHitsAsSet(); + } + request.prepareQuery(); Object result = request.getFromQueryCache(); if (result != null) { return (Set) result; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java index 29dd4ac1d..473380c3b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java @@ -515,12 +515,12 @@ public final class OrmQueryRequest extends BeanRequest implements SpiOrmQuery if (query.getType() == Type.MAP) { mergeCacheHitsToMap(result); } else { - mergeCacheHitsToList(result); + mergeCacheHitsToCollection(result); } } } - private void mergeCacheHitsToList(BeanCollection result) { + private void mergeCacheHitsToCollection(BeanCollection result) { for (T hit : cacheBeans) { result.internalAdd(hit); } @@ -579,6 +579,15 @@ public final class OrmQueryRequest extends BeanRequest implements SpiOrmQuery return property; } + @Override + public Set beanCacheHitsAsSet() { + OrderBy orderBy = query.getOrderBy(); + if (orderBy != null && !orderBy.isEmpty()) { + beanDescriptor.sort(cacheBeans, orderBy.toStringFormat()); + } + return new LinkedHashSet<>(cacheBeans); + } + @Override public boolean getFromBeanCache() { if (!query.isBeanCacheGet()) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/SpiOrmQueryRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/SpiOrmQueryRequest.java index 174392918..ff2c0ef54 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/SpiOrmQueryRequest.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/SpiOrmQueryRequest.java @@ -153,6 +153,11 @@ public interface SpiOrmQueryRequest extends BeanQueryRequest, DocQueryRequ */ Map beanCacheHitsAsMap(); + /** + * Return the bean cache hits for findMap (when all hits / no misses). + */ + Set beanCacheHitsAsSet(); + /** * Reset Bean cache mode AUTO - require explicit setting for bean cache use with findList(). */ diff --git a/ebean-test/src/test/java/org/tests/cache/TestBeanCache.java b/ebean-test/src/test/java/org/tests/cache/TestBeanCache.java index 7a81635cd..d1742249f 100644 --- a/ebean-test/src/test/java/org/tests/cache/TestBeanCache.java +++ b/ebean-test/src/test/java/org/tests/cache/TestBeanCache.java @@ -16,6 +16,7 @@ import org.tests.model.basic.OCachedBean; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; @@ -187,6 +188,80 @@ public class TestBeanCache extends BaseTestCase { } } + @Test + public void testFindSet() { + + List beans = createBeans(Arrays.asList("z0", "z1", "z2")); + List ids = beans.stream().map(OCachedBean::getId).collect(Collectors.toList()); + + beanCache.clear(); + beanCache.statistics(true); + + LoggedSql.start(); + + log.info("All misses (0 of 3) ..."); + Set set = DB.find(OCachedBean.class) + .where().idIn(ids) + .setUseCache(true) + .findSet(); + + assertThat(set).hasSize(3); + assertBeanCacheHitMiss(0, 3); + List sql = LoggedSql.collect(); + assertThat(sql).hasSize(1); + if (isH2()) { + assertSql(sql.get(0)).contains("from o_cached_bean t0 where t0.id in (?,?,?,?,?)"); + } + + log.info("All hits (3 of 3) ..."); + set = DB.find(OCachedBean.class) + .where().idIn(ids) + .setUseCache(true) + .findSet(); + + assertBeanCacheHitMiss(3, 0); + assertThat(set).hasSize(3); + sql = LoggedSql.collect(); + assertThat(sql).hasSize(0); // no misses + + // remove a bean so that we get a "partial" hit (2 out of 3 in cache) + beanCache.remove(beans.get(0).getId().toString()); + + log.info("Partial hits (2 of 3) ..."); + set = DB.find(OCachedBean.class) + .where().idIn(ids) + .setUseCache(true) + .findSet(); + + assertBeanCacheHitMiss(2, 1); + assertThat(set).hasSize(3); + sql = LoggedSql.collect(); + assertThat(sql).hasSize(1); + if (isH2()) { + // fetch the miss from DB + assertSql(sql.get(0)).contains("from o_cached_bean t0 where t0.id in (?)"); + } + + // remove beans so that we get a "partial" hit (1 out of 3 in cache) + beanCache.remove(beans.get(1).getId().toString()); + beanCache.remove(beans.get(2).getId().toString()); + + log.info("Partial hits (1 of 3) ..."); + set = DB.find(OCachedBean.class) + .where().idIn(ids) + .setUseCache(true) + .findSet(); + + assertBeanCacheHitMiss(1, 2); + assertThat(set).hasSize(3); + sql = LoggedSql.stop(); + assertThat(sql).hasSize(1); + if (isH2()) { + // fetch the misses from DB + assertSql(sql.get(0)).contains("from o_cached_bean t0 where t0.id in (?,?,?,?,?)"); + } + } + private void assertBeanCacheHitMiss(int hitCount, int missCount) { ServerCacheStatistics statistics = beanCache.statistics(true); assertThat(statistics.getHitCount()).isEqualTo(hitCount);