package io.ebean;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

Holds a list of value object pairs.

This feature is to enable use of L2 cache with complex natural keys with findList() queries in cases where the IN clause is not a single property but instead a pair of properties.

These queries can have predicates that can be translated into a list of complex natural keys such that the L2 cache can be hit with these keys to obtain some or all of the beans from L2 cache rather than the DB.


  // where a bean is annotated with a complex
  // natural key made of several properties
  @Cache(naturalKey = {"store","code","sku"})
  Pairs pairs = new Pairs("sku", "code");
  pairs.add("sj2", 1000);
  pairs.add("sj2", 1001);
  pairs.add("pf3", 1000);
  List<OCachedNatKeyBean3> list = DB.find(OCachedNatKeyBean3.class)
  .where()
  .eq("store", "def")
  .inPairs(pairs)       // IN clause with 'pairs' of values
  .orderBy("sku desc")
  // query expressions cover the natural key properties
  // so we can choose to hit the L2 bean cache if we want
  .setUseCache(true)
  .findList();

Important implementation Note

When binding many pairs of values we want to be able to utilise a DB index (as this type of query usually means the pairs are a unique key/index or part of a unique key/index and highly selective). Currently we know we can do this on any DB that supports expression/formula based indexes. using a DB string concatenation formula

This means, the implementation converts the list of pairs into a list of strings via concatenation and we use a DB concatenation formula to match. We see SQL like:

sql
  ...
  where t0.store = ?  and (t0.sku||'-'||t0.code) in (?, ? )
  // bind values like: "sj2-1000", "pf3-1000"

We often create a DB expression index to match the DB concat formula like:

sql
  create index ix_name on table_name ((sku || '-' || code));
/** * Holds a list of value object pairs. * <p> * This feature is to enable use of L2 cache with complex natural keys with findList() queries in cases where the * IN clause is not a single property but instead a pair of properties. * </p> * <p> * These queries can have predicates that can be translated into a list of complex natural keys such that the L2 * cache can be hit with these keys to obtain some or all of the beans from L2 cache rather than the DB. * </p> * <pre>{@code * * // where a bean is annotated with a complex * // natural key made of several properties * @Cache(naturalKey = {"store","code","sku"}) * * * Pairs pairs = new Pairs("sku", "code"); * pairs.add("sj2", 1000); * pairs.add("sj2", 1001); * pairs.add("pf3", 1000); * * List<OCachedNatKeyBean3> list = DB.find(OCachedNatKeyBean3.class) * .where() * .eq("store", "def") * .inPairs(pairs) // IN clause with 'pairs' of values * .orderBy("sku desc") * * // query expressions cover the natural key properties * // so we can choose to hit the L2 bean cache if we want * .setUseCache(true) * .findList(); * * }</pre> * <h3>Important implementation Note</h3> * <p> * When binding many pairs of values we want to be able to utilise a DB index (as this type of query usually means the * pairs are a unique key/index or part of a unique key/index and highly selective). Currently we know we can do this * on any DB that supports expression/formula based indexes. * using a DB string concatenation formula * </p> * <p> * This means, the implementation converts the list of pairs into a list of strings via concatenation and we use a * DB concatenation formula to match. We see SQL like: * </p> * <pre>{@code sql * * ... * where t0.store = ? and (t0.sku||'-'||t0.code) in (?, ? ) * * // bind values like: "sj2-1000", "pf3-1000" * * }</pre> * <p> * We often create a DB expression index to match the DB concat formula like: * </p> * <pre>{@code sql * * create index ix_name on table_name ((sku || '-' || code)); * * }</pre> */
public class Pairs { private final String property0; private final String property1; private final List<Entry> entries = new ArrayList<>();
Character between the values when combined via DB varchar concatenation.
/** * Character between the values when combined via DB varchar concatenation. */
private String concatSeparator = "-";
Optional suffix added to DB varchar concatenation formula.
/** * Optional suffix added to DB varchar concatenation formula. */
private String concatSuffix;
Create with 2 property names.
Params:
  • property0 – The property of the first value
  • property1 – The property of the second value
/** * Create with 2 property names. * * @param property0 The property of the first value * @param property1 The property of the second value */
public Pairs(String property0, String property1) { this.property0 = property0; this.property1 = property1; }
Add a pair of value objects.

Both values are expected to be immutable with equals and hashCode implementations.

Params:
  • a – Value of the first property
  • b – Value of the second property
/** * Add a pair of value objects. * <p> * Both values are expected to be immutable with equals and hashCode implementations. * </p> * * @param a Value of the first property * @param b Value of the second property */
public Pairs add(Object a, Object b) { entries.add(new Entry(a, b)); return this; }
Return the first property name.
/** * Return the first property name. */
public String getProperty0() { return property0; }
Return the second property name.
/** * Return the second property name. */
public String getProperty1() { return property1; }
Return all the value pairs.
/** * Return all the value pairs. */
public List<Entry> getEntries() { return Collections.unmodifiableList(entries); }
Return the separator character used with DB varchar concatenation to combine the 2 values.
/** * Return the separator character used with DB varchar concatenation to combine the 2 values. */
public String getConcatSeparator() { return concatSeparator; }
Set the separator character used with DB varchar concatenation to combine the 2 values.
/** * Set the separator character used with DB varchar concatenation to combine the 2 values. */
public Pairs setConcatSeparator(String concatSeparator) { this.concatSeparator = concatSeparator; return this; }
Return a suffix used with DB varchar concatenation to combine the 2 values.
/** * Return a suffix used with DB varchar concatenation to combine the 2 values. */
public String getConcatSuffix() { return concatSuffix; }
Add a suffix used with DB varchar concatenation to combine the 2 values.
/** * Add a suffix used with DB varchar concatenation to combine the 2 values. */
public Pairs setConcatSuffix(String concatSuffix) { this.concatSuffix = concatSuffix; return this; } @Override public String toString() { return "p0:" + property0 + " p1:" + property1 + " entries:" + entries; }
A pair of 2 value objects.

Used to support inPairs() expression.

/** * A pair of 2 value objects. * <p> * Used to support inPairs() expression. */
public static class Entry { private final Object a; private final Object b;
Create with values for property0 and property1 respectively.
Params:
  • a – Value of the first property
  • b – Value of the second property
/** * Create with values for property0 and property1 respectively. * * @param a Value of the first property * @param b Value of the second property */
public Entry(Object a, Object b) { this.a = a; this.b = b; } @Override public String toString() { return "{" + a + "," + b + "}"; }
Return the value for the first property.
/** * Return the value for the first property. */
public Object getA() { return a; }
Return the value for the second property.
/** * Return the value for the second property. */
public Object getB() { return b; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Entry that = (Entry) o; return a.equals(that.a) && b.equals(that.b); } @Override public int hashCode() { int result = a.hashCode(); result = 92821 * result + b.hashCode(); return result; } } }