Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,23 @@ final class NaturalKeyEntryBasic implements NaturalKeyEntry {
* Create when query uses an IN PAIRS clause.
*/
NaturalKeyEntryBasic(BeanNaturalKey naturalKey, List<NaturalKeyEq> eqList,
String inMapProperty0, String inMapProperty1, Pairs.Entry pair) {
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);
}

NaturalKeyEntryBasic(BeanNaturalKey naturalKey, List<NaturalKeyEq> eqList,
Map<String, Object> properties, Object[] naturalKeyValue) {
load(eqList);
map.putAll(properties);
this.inValue = naturalKeyValue;
this.key = calculateKey(naturalKey);
}


private void load(List<NaturalKeyEq> eqList) {
if (eqList != null) {
for (NaturalKeyEq eq : eqList) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
import io.ebean.Pairs;
import io.ebeaninternal.server.deploy.BeanNaturalKey;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;

/**
* Collects the data for processing the natural key cache processing.
Expand All @@ -19,8 +16,9 @@ public final class NaturalKeyQueryData<T> {
*/
private boolean hasIn;
// IN Pairs clause - only one allowed
private String inProperty0, inProperty1;
private List<Pairs.Entry> inPairs;
private String[] properties;
private List<Object[]> inTuples;
// IN clause - only one allowed
private List<Object> inValues;
private String inProperty;
Expand All @@ -47,14 +45,39 @@ public List<Pairs.Entry> matchInPairs(String property0, String property1, List<P
}
if (matchProperty(property0) && matchProperty(property1)) {
this.hasIn = true;
this.inProperty0 = property0;
this.inProperty1 = property1;
this.properties = new String[]{property0, property1};
this.inPairs = new ArrayList<>(inPairs); // will be modified
return this.inPairs;
}
return null;
}

/**
* Match for In Tuples expression. We only allow one IN clause.
*/
public List<Object[]> matchInTuples(String[] properties, List<Object[]> inTuples) {
if (hasIn) {
// only 1 IN allowed (to project naturalIds)
return null;
}

boolean matchAll = true;

for (String property : properties) {
if (!matchProperty(property)) {
matchAll = false;
break;
}
}
if (matchAll) {
this.hasIn = true;
this.properties = Arrays.copyOf(properties, properties.length);
this.inTuples = new ArrayList<>(inTuples);
return this.inTuples;
}
return null;
}

/**
* Match for IN expression. We only allow one IN clause.
*/
Expand Down Expand Up @@ -100,6 +123,8 @@ public NaturalKeySet buildKeys() {
addInValues();
} else if (inPairs != null) {
addInPairs();
} else if (inTuples != null) {
addInTuples();
} else {
addEqualsKey();
}
Expand All @@ -110,10 +135,28 @@ 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) {
String inProperty0 = null;
String inProperty1 = null;
if (properties.length == 2) {
inProperty0 = properties[0];
inProperty1 = properties[1];
}

set.add(new NaturalKeyEntryBasic(naturalKey, eqList, inProperty0, inProperty1, entry));
}
}

private void addInTuples() {
for (Object[] inTuple : inTuples) {
Map<String, Object> map = new HashMap<>();
for (int i = 0; i < inTuple.length; i++) {
Object o = inTuple[i];
map.put(properties[i], o);
}
set.add(new NaturalKeyEntryBasic(naturalKey, eqList, map, inTuple));
}
}

private void addInValues() {
if (eqList == null) {
// a single property IN expression
Expand Down Expand Up @@ -152,11 +195,8 @@ private boolean matchProperties() {
if (inProperty != null) {
exprProps.add(inProperty);
}
if (inProperty0 != null) {
exprProps.add(inProperty0);
}
if (inProperty1 != null) {
exprProps.add(inProperty1);
if (properties != null) {
exprProps.addAll(Arrays.asList(properties));
}
if (eqList != null) {
for (NaturalKeyEq eq : eqList) {
Expand All @@ -173,6 +213,7 @@ private boolean expressionCount() {
int defined = (inValues == null) ? 0 : 1;
defined += (inPairs == null) ? 0 : 2;
defined += (eqList == null) ? 0 : eqList.size();
defined += (inTuples == null) ? 0 : properties.length;
return defined == naturalKey.length();
}

Expand Down Expand Up @@ -206,6 +247,9 @@ private void removeKey(Object inValue) {
} else if (inPairs != null) {
//noinspection SuspiciousMethodCalls
inPairs.remove(inValue);
} else if (inTuples != null) {
//noinspection SuspiciousMethodCalls
inTuples.remove(inValue);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ final class InTuplesExpression extends AbstractExpression {

private final boolean not;
private final String[] properties;
private final List<Object[]> entries;
private List<Object[]> entries;

InTuplesExpression(InTuples pairs, boolean not) {
super("");
Expand All @@ -25,7 +25,15 @@ final class InTuplesExpression extends AbstractExpression {

@Override
public boolean naturalKey(NaturalKeyQueryData<?> data) {
return false;
if (not) {
return false;
}
List<Object[]> copy = data.matchInTuples(properties, entries);
if (copy == null) {
return false;
}
entries = copy;
return true;
}

@Override
Expand Down
72 changes: 72 additions & 0 deletions ebean-redis/src/test/java/org/integration/IntegrationTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.integration;

import io.ebean.DB;
import io.ebean.InTuples;
import io.ebean.Pairs;
import io.ebean.cache.ServerCache;
import io.ebean.cache.ServerCacheStatistics;
import org.domain.*;
Expand Down Expand Up @@ -177,6 +179,76 @@ private static OtherOne findOther(String a, String b) {
.findOne();
}

@Test
void naturalKey_inPairs() throws InterruptedException {
DB.save(new OtherOne("ip_A", "ip_1", "ip_A1"));
DB.save(new OtherOne("ip_A", "ip_2", "ip_A2"));
DB.save(new OtherOne("ip_B", "ip_1", "ip_B1"));

ServerCache nkeyCache = DB.cacheManager().naturalKeyCache(OtherOne.class);
nkeyCache.clear();

Pairs pairs = new Pairs("one", "two")
.add("ip_A", "ip_1")
.add("ip_A", "ip_2")
.add("ip_B", "ip_1");

// first fetch — miss, populates natural key + bean cache
List<OtherOne> list0 = DB.find(OtherOne.class)
.where()
.inPairs(pairs)
.setUseCache(true)
.findList();
assertThat(list0).hasSize(3);
nkeyCache.statistics(true); // reset stats

Thread.sleep(5);

// second fetch — all three should hit the natural key cache
List<OtherOne> list1 = DB.find(OtherOne.class)
.where()
.inPairs(pairs)
.setUseCache(true)
.findList();
assertThat(list1).hasSize(3);
assertThat(nkeyCache.statistics(true).getHitCount()).isEqualTo(3);
}

@Test
void naturalKey_inTuples() throws InterruptedException {
DB.save(new OtherOne("it_A", "it_1", "it_A1"));
DB.save(new OtherOne("it_A", "it_2", "it_A2"));
DB.save(new OtherOne("it_B", "it_1", "it_B1"));

ServerCache nkeyCache = DB.cacheManager().naturalKeyCache(OtherOne.class);
nkeyCache.clear();

InTuples tuples = InTuples.of("one", "two")
.add("it_A", "it_1")
.add("it_A", "it_2")
.add("it_B", "it_1");

// first fetch — miss, populates natural key + bean cache
List<OtherOne> list0 = DB.find(OtherOne.class)
.where()
.inTuples(tuples)
.setUseCache(true)
.findList();
assertThat(list0).hasSize(3);
nkeyCache.statistics(true); // reset stats

Thread.sleep(5);

// second fetch — all three should hit the natural key cache
List<OtherOne> list1 = DB.find(OtherOne.class)
.where()
.inTuples(tuples)
.setUseCache(true)
.findList();
assertThat(list1).hasSize(3);
assertThat(nkeyCache.statistics(true).getHitCount()).isEqualTo(3);
}

@Test
void test() throws InterruptedException {

Expand Down
Loading