diff --git a/src/main/java/io/ebeaninternal/api/NaturalKeyEntry.java b/src/main/java/io/ebeaninternal/api/NaturalKeyEntry.java index 25fae5444..ffee04f37 100644 --- a/src/main/java/io/ebeaninternal/api/NaturalKeyEntry.java +++ b/src/main/java/io/ebeaninternal/api/NaturalKeyEntry.java @@ -1,77 +1,17 @@ package io.ebeaninternal.api; - -import io.ebean.Pairs; -import io.ebeaninternal.server.deploy.BeanNaturalKey; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - /** - * Natural key entry with name value pairs for each of the properties making up the key. + * An entry for natural key lookup. */ -public class NaturalKeyEntry { - - private final Map map = new HashMap<>(); - private final Object key; - private Object inValue; - - /** - * Used when query query just has a series of EQ expressions (no IN clause). - */ - NaturalKeyEntry(BeanNaturalKey naturalKey, List eqList) { - load(eqList); - this.key = calculateKey(naturalKey); - } - - /** - * Create when query uses an IN clause. - */ - NaturalKeyEntry(BeanNaturalKey naturalKey, List eqList, String inProperty, Object inValue) { - load(eqList); - if (inProperty != null) { - map.put(inProperty, inValue); - this.inValue = inValue; - } - this.key = calculateKey(naturalKey); - } - - /** - * Create when query uses an IN PAIRS clause. - */ - NaturalKeyEntry(BeanNaturalKey naturalKey, List eqList, - String inMapProperty0, String inMapProperty1, Pairs.Entry pair) { - load(eqList); - map.put(inMapProperty0, pair.getA()); - map.put(inMapProperty1, pair.getB()); - this.inValue = pair; - this.key = calculateKey(naturalKey); - } - - private void load(List eqList) { - if (eqList != null) { - for (NaturalKeyEq eq : eqList) { - map.put(eq.property, eq.value); - } - } - } - - private Object calculateKey(BeanNaturalKey naturalKey) { - return naturalKey.calculateKey(map); - } +public interface NaturalKeyEntry { /** * Return the natural cache key (String concatenation of values). */ - public Object key() { - return key; - } + Object key(); /** * Return the inValue (used to remove from IN clause of original query). */ - Object getInValue() { - return inValue; - } + Object getInValue(); } diff --git a/src/main/java/io/ebeaninternal/api/NaturalKeyEntryBasic.java b/src/main/java/io/ebeaninternal/api/NaturalKeyEntryBasic.java new file mode 100644 index 000000000..e4e4d9acb --- /dev/null +++ b/src/main/java/io/ebeaninternal/api/NaturalKeyEntryBasic.java @@ -0,0 +1,73 @@ +package io.ebeaninternal.api; + + +import io.ebean.Pairs; +import io.ebeaninternal.server.deploy.BeanNaturalKey; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Natural key entry with name value pairs for each of the properties making up the key. + */ +class NaturalKeyEntryBasic implements NaturalKeyEntry { + + private final Map map = new HashMap<>(); + private final Object key; + private Object inValue; + + /** + * Used when query query just has a series of EQ expressions (no IN clause). + */ + NaturalKeyEntryBasic(BeanNaturalKey naturalKey, List eqList) { + load(eqList); + this.key = calculateKey(naturalKey); + } + + /** + * Create when query uses an IN clause. + */ + NaturalKeyEntryBasic(BeanNaturalKey naturalKey, List eqList, String inProperty, Object inValue) { + load(eqList); + if (inProperty != null) { + map.put(inProperty, inValue); + this.inValue = inValue; + } + this.key = calculateKey(naturalKey); + } + + /** + * Create when query uses an IN PAIRS clause. + */ + NaturalKeyEntryBasic(BeanNaturalKey naturalKey, List eqList, + String inMapProperty0, String inMapProperty1, Pairs.Entry pair) { + load(eqList); + map.put(inMapProperty0, pair.getA()); + map.put(inMapProperty1, pair.getB()); + this.inValue = pair; + this.key = calculateKey(naturalKey); + } + + private void load(List eqList) { + if (eqList != null) { + for (NaturalKeyEq eq : eqList) { + map.put(eq.property, eq.value); + } + } + } + + private Object calculateKey(BeanNaturalKey naturalKey) { + return naturalKey.calculateKey(map); + } + + @Override + public Object key() { + return key; + } + + @Override + public Object getInValue() { + return inValue; + } +} diff --git a/src/main/java/io/ebeaninternal/api/NaturalKeyEntrySimple.java b/src/main/java/io/ebeaninternal/api/NaturalKeyEntrySimple.java new file mode 100644 index 000000000..b3daf0a59 --- /dev/null +++ b/src/main/java/io/ebeaninternal/api/NaturalKeyEntrySimple.java @@ -0,0 +1,20 @@ +package io.ebeaninternal.api; + +class NaturalKeyEntrySimple implements NaturalKeyEntry { + + private final Object key; + + NaturalKeyEntrySimple(Object key) { + this.key = key; + } + + @Override + public Object key() { + return key; + } + + @Override + public Object getInValue() { + return key; + } +} diff --git a/src/main/java/io/ebeaninternal/api/NaturalKeyQueryData.java b/src/main/java/io/ebeaninternal/api/NaturalKeyQueryData.java index 21e17269c..6966787d5 100644 --- a/src/main/java/io/ebeaninternal/api/NaturalKeyQueryData.java +++ b/src/main/java/io/ebeaninternal/api/NaturalKeyQueryData.java @@ -103,26 +103,46 @@ public class NaturalKeyQueryData { this.set = new NaturalKeySet(); if (inValues != null) { - // a findList() with an IN clause so we project - // for every IN value a natural key combination - for (Object inValue : inValues) { - set.add(new NaturalKeyEntry(naturalKey, eqList, inProperty, inValue)); - } + addInValues(); } else if (inPairs != null) { - // a findList() with an IN Map clause so we project - // for every IN value a natural key combination - for (Pairs.Entry entry : inPairs) { - set.add(new NaturalKeyEntry(naturalKey, eqList, inProperty0, inProperty1, entry)); - } - + addInPairs(); } else { - // only one - a findOne() - set.add(new NaturalKeyEntry(naturalKey, eqList)); + addEqualsKey(); } - return set; } + private void addInPairs() { + // a findList() with an IN Map clause so we project + // for every IN value a natural key combination + for (Pairs.Entry entry : inPairs) { + set.add(new NaturalKeyEntryBasic(naturalKey, eqList, inProperty0, inProperty1, entry)); + } + } + + private void addInValues() { + if (eqList == null) { + // a single property IN expression + for (Object inValue : inValues) { + set.add(new NaturalKeyEntrySimple(inValue)); + } + } else { + // IN expression + EQ expression(s) + for (Object inValue : inValues) { + set.add(new NaturalKeyEntryBasic(naturalKey, eqList, inProperty, inValue)); + } + } + } + + private void addEqualsKey() { + if (eqList.size() == 1) { + // a single property EQ expression + set.add(new NaturalKeyEntrySimple(eqList.get(0).value)); + } else { + set.add(new NaturalKeyEntryBasic(naturalKey, eqList)); + } + } + /** * Return true if the properties match the natural key properties. */ diff --git a/src/main/java/io/ebeaninternal/api/NaturalKeySet.java b/src/main/java/io/ebeaninternal/api/NaturalKeySet.java index 64ac20aff..cf31afbf9 100644 --- a/src/main/java/io/ebeaninternal/api/NaturalKeySet.java +++ b/src/main/java/io/ebeaninternal/api/NaturalKeySet.java @@ -9,7 +9,7 @@ public class NaturalKeySet { private final Map map = new LinkedHashMap<>(); - public NaturalKeySet() { + NaturalKeySet() { } public int size() { @@ -24,8 +24,7 @@ public class NaturalKeySet { return map.keySet(); } - public Object getInValue(Object naturalKey) { - NaturalKeyEntry entry = map.get(naturalKey); - return entry.getInValue(); + Object getInValue(Object naturalKey) { + return map.get(naturalKey).getInValue(); } } diff --git a/src/test/java/org/tests/model/basic/cache/TestNatKeyCacheWithForeignKey.java b/src/test/java/org/tests/model/basic/cache/TestNatKeyCacheWithForeignKey.java index 4f9117476..d40c14ac5 100644 --- a/src/test/java/org/tests/model/basic/cache/TestNatKeyCacheWithForeignKey.java +++ b/src/test/java/org/tests/model/basic/cache/TestNatKeyCacheWithForeignKey.java @@ -21,6 +21,10 @@ public class TestNatKeyCacheWithForeignKey extends BaseTestCase { return getBeanCacheStats(OCachedAppDetail.class, true); } + private ServerCacheStatistics appStats() { + return getBeanCacheStats(OCachedApp.class, true); + } + @Test public void test_findOne() { @@ -111,6 +115,52 @@ public class TestNatKeyCacheWithForeignKey extends BaseTestCase { .findList(); } + @Test + public void findSimple() { + + setupData(); + clearAllL2Cache(); + + OCachedApp app0 = findAppByName("app0"); + assertThat(app0).isNotNull(); + assertThat(appStats().getHitCount()).isEqualTo(0); + + app0 = findAppByName("app0"); + assertThat(app0).isNotNull(); + assertThat(appStats().getHitCount()).isEqualTo(1); + } + + private OCachedApp findAppByName(String appName) { + + return DB.find(OCachedApp.class) + .where() + .eq("appName", appName) + .findOne(); + } + + @Test + public void findApp_many() { + + setupData(); + clearAllL2Cache(); + + List result = findAppByNames("app0", "app1"); + assertThat(result).hasSize(2); + assertThat(appStats().getHitCount()).isEqualTo(0); + + result = findAppByNames("app0", "app1"); + assertThat(result).hasSize(2); + assertThat(appStats().getHitCount()).isEqualTo(2); + } + + private List findAppByNames(String... appNames) { + + return DB.find(OCachedApp.class) + .setUseCache(true) + .where() + .in("appName", appNames) + .findList(); + } private static void setupData() { if (!seededData) { diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index 5a1e9b956..7dec55bb6 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -79,11 +79,11 @@ - - - + + + - +