From a2e8f098cfc830ac3bf704ca2e3f1885a8c97d90 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 4 Jan 2023 17:11:24 +0100 Subject: [PATCH] NEW: BeanCache for findSet --- .../server/core/DefaultServer.java | 8 +- .../server/core/OrmQueryRequest.java | 13 ++- .../server/core/SpiOrmQueryRequest.java | 5 ++ .../java/org/tests/cache/TestBeanCache.java | 81 ++++++++++++++++++- 4 files changed, 101 insertions(+), 6 deletions(-) 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 07e3ccaf0..ef2fbab62 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 16c38fa30..c2afca243 100644 --- a/ebean-test/src/test/java/org/tests/cache/TestBeanCache.java +++ b/ebean-test/src/test/java/org/tests/cache/TestBeanCache.java @@ -1,11 +1,11 @@ package org.tests.cache; -import io.ebean.xtest.BaseTestCase; import io.ebean.DB; import io.ebean.Transaction; import io.ebean.cache.ServerCache; import io.ebean.cache.ServerCacheStatistics; import io.ebean.test.LoggedSql; +import io.ebean.xtest.BaseTestCase; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,6 +15,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; @@ -57,7 +58,7 @@ public class TestBeanCache extends BaseTestCase { @Test public void idsIn_explicitCache_expect_cachePut() { - List beans = createBeans(Arrays.asList("k0","k1")); + List beans = createBeans(Arrays.asList("k0", "k1")); List ids = beans.stream().map(OCachedBean::getId).collect(Collectors.toList()); beanCache.clear(); @@ -157,6 +158,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); @@ -183,7 +258,7 @@ public class TestBeanCache extends BaseTestCase { Country country = DB.find(Country.class) .where() - .eq("name","NotValid") + .eq("name", "NotValid") .findOne(); assertThat(country).isNull();