#1214 - ENH: Add inPairs() expression - support L2 cache hits for complex natural key with findList() and in pairs expression

This commit is contained in:
Rob Bygrave
2017-11-19 16:47:32 +13:00
parent f0ddf6c7e6
commit ef91c643ed
22 changed files with 765 additions and 87 deletions
@@ -1,8 +1,13 @@
package io.ebeaninternal.server.expression;
import io.ebean.BaseTestCase;
import io.ebean.EbeanServer;
import io.ebean.Query;
import io.ebean.Transaction;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import org.tests.model.basic.Customer;
import org.tests.model.basic.Order;
import static org.assertj.core.api.StrictAssertions.assertThat;
@@ -29,4 +34,56 @@ public abstract class BaseExpressionTest extends BaseTestCase {
protected void different(SpiExpression one, SpiExpression two){
assertThat(hash(one)).isNotEqualTo(hash(two));
}
/**
* Request with Multi-Value support.
*/
protected InExpressionTest.TDQueryRequest<Customer> multi() {
return MULTI_VALUE;
}
/**
* Request with NO Multi-Value support.
*/
protected InExpressionTest.TDQueryRequest<Customer> noMulti() {
return NO_MULTI_VALUE;
}
private static final TDQueryRequest<Customer> MULTI_VALUE= new TDQueryRequest<>(true);
private static final TDQueryRequest<Customer> NO_MULTI_VALUE = new TDQueryRequest<>(false);
static class TDQueryRequest<T> implements BeanQueryRequest<T> {
final boolean supported;
TDQueryRequest(boolean supported) {
this.supported = supported;
}
@Override
public EbeanServer getEbeanServer() {
return null;
}
@Override
public Transaction getTransaction() {
return null;
}
@Override
public Query<T> getQuery() {
return null;
}
@Override
public boolean isMultiValueIdSupported() {
return supported;
}
@Override
public boolean isMultiValueSupported(Class<?> valueType) {
return supported;
}
}
}
@@ -1,11 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebean.EbeanServer;
import io.ebean.Query;
import io.ebean.Transaction;
import io.ebean.event.BeanQueryRequest;
import org.junit.Test;
import org.tests.model.basic.Customer;
import java.util.ArrayList;
import java.util.Arrays;
@@ -15,20 +10,6 @@ import static org.assertj.core.api.StrictAssertions.assertThat;
public class InExpressionTest extends BaseExpressionTest {
/**
* Request with Multi-Value support.
*/
private TDQueryRequest<Customer> multi() {
return MULTI_VALUE;
}
/**
* Request with NO Multi-Value support.
*/
private TDQueryRequest<Customer> noMulti() {
return NO_MULTI_VALUE;
}
@Test
public void queryPlanHash_given_diffPropertyName_should_differentPlanHash() throws Exception {
@@ -182,41 +163,5 @@ public class InExpressionTest extends BaseExpressionTest {
}
private static final TDQueryRequest<Customer> MULTI_VALUE= new TDQueryRequest<>(true);
private static final TDQueryRequest<Customer> NO_MULTI_VALUE = new TDQueryRequest<>(false);
static class TDQueryRequest<T> implements BeanQueryRequest<T> {
final boolean supported;
TDQueryRequest(boolean supported) {
this.supported = supported;
}
@Override
public EbeanServer getEbeanServer() {
return null;
}
@Override
public Transaction getTransaction() {
return null;
}
@Override
public Query<T> getQuery() {
return null;
}
@Override
public boolean isMultiValueIdSupported() {
return supported;
}
@Override
public boolean isMultiValueSupported(Class<?> valueType) {
return supported;
}
}
}
@@ -0,0 +1,92 @@
package io.ebeaninternal.server.expression;
import io.ebean.Pairs;
import org.junit.Test;
import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertTrue;
public class InPairsExpressionTest extends BaseExpressionTest {
private Pairs pairs() {
return pairs("sku", "code");
}
private Pairs pairs(String property0, String property1) {
Pairs pairs = new Pairs(property0, property1)
.add("2", 1000)
.add("2", 1001)
.add("3", 1000);
return pairs;
}
@Test
public void same_samePlan_sameBind() throws Exception {
InPairsExpression e0 = new InPairsExpression(pairs(), false);
InPairsExpression e1 = new InPairsExpression(pairs(), false);
same(e0, e1);
assertTrue(e0.isSameByBind(e1));
}
@Test
public void same_samePlan_diffBind() throws Exception {
InPairsExpression e0 = new InPairsExpression(pairs(), false);
InPairsExpression e1 = new InPairsExpression(pairs().add("4", 1000), false);
e0.prepareExpression(multi());
e1.prepareExpression(multi());
// when multi() ... same as bind count not important
same(e0, e1);
assertFalse(e0.isSameByBind(e1));
}
@Test
public void same_noMulti_diffPlan() throws Exception {
InPairsExpression e0 = new InPairsExpression(pairs(), false);
InPairsExpression e1 = new InPairsExpression(pairs().add("4", 1000), false);
e0.prepareExpression(noMulti());
e1.prepareExpression(noMulti());
// when noMulti() ... bind count different so different plan
different(e0, e1);
}
@Test
public void diffProperty0_diff() throws Exception {
InPairsExpression e0 = new InPairsExpression(pairs(), false);
InPairsExpression e1 = new InPairsExpression(pairs("k", "code"), false);
different(e0, e1);
}
@Test
public void diffProperty1_diff() throws Exception {
InPairsExpression e0 = new InPairsExpression(pairs(), false);
InPairsExpression e1 = new InPairsExpression(pairs("sku", "c"), false);
different(e0, e1);
}
@Test
public void diffSeparator_diff() throws Exception {
InPairsExpression e0 = new InPairsExpression(pairs(), false);
InPairsExpression e1 = new InPairsExpression(pairs().setConcatSeparator(":"), false);
different(e0, e1);
}
@Test
public void diffSuffix_diff() throws Exception {
InPairsExpression e0 = new InPairsExpression(pairs(), false);
InPairsExpression e1 = new InPairsExpression(pairs().setConcatSuffix(":"), false);
different(e0, e1);
}
}
@@ -2,6 +2,7 @@ package org.tests.model.basic.cache;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.Pairs;
import io.ebean.cache.ServerCache;
import io.ebean.cache.ServerCacheManager;
import io.ebean.cache.ServerCacheStatistics;
@@ -247,4 +248,75 @@ public class TestCacheViaComplexNaturalKey3 extends BaseTestCase {
assertBeanCacheHitMiss(0, 0);
}
@Test
public void findList_inPairs_standardConcat() {
setup();
loadSomeIntoCache();
Pairs pairs = new Pairs("sku", "code")
.add("2", 1000)
.add("2", 1001)
.add("3", 1000);
LoggedSqlCollector.start();
List<OCachedNatKeyBean3> list = Ebean.find(OCachedNatKeyBean3.class)
.where()
.eq("store", "def")
.inPairs(pairs)
.setUseCache(true)
.orderBy("sku desc")
.findList();
List<String> sql = LoggedSqlCollector.stop();
assertThat(list).hasSize(3);
assertNaturalKeyHitMiss(1, 2);
assertBeanCacheHitMiss(1, 0);
if (isH2()) {
assertThat(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and (t0.sku||'-'||t0.code) in (?, ? ) order by t0.sku desc; --bind(def,Array[2]={2-1000,3-1000})");
} else {
assertThat(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and (t0.sku||'-'||t0.code)");
}
}
@Test
public void findList_inPairs_userConcat() {
setup();
loadSomeIntoCache();
Pairs pairs = new Pairs("sku", "code")
.setConcatSeparator(":")
.setConcatSuffix("-foo")
.add("2", 1000)
.add("2", 1001)
.add("3", 1000);
LoggedSqlCollector.start();
List<OCachedNatKeyBean3> list = Ebean.find(OCachedNatKeyBean3.class)
.where()
.eq("store", "def")
.inPairs(pairs)
.setUseCache(true)
.orderBy("sku desc")
.findList();
List<String> sql = LoggedSqlCollector.stop();
assertThat(list).hasSize(3);
assertNaturalKeyHitMiss(1, 2);
assertBeanCacheHitMiss(1, 0);
if (isH2()) {
assertThat(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and (t0.sku||':'||t0.code||'-foo') in (?, ? ) order by t0.sku desc; --bind(def,Array[2]={2:1000-foo,3:1000-foo})");
} else {
assertThat(sql.get(0)).contains("from o_cached_natkey3 t0 where t0.store = ? and (t0.sku||':'||t0.code||'-foo')");
}
}
}