package io.ebeaninternal.api; import io.ebean.Pairs; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Collects the data for processing the natural key cache processing. */ public class NaturalKeyQueryData { private final String[] naturalKey; /** * Only one of IN or IN PAIRS is allowed. */ private boolean hasIn; // IN Pairs clause - only one allowed private String inProperty0, inProperty1; private List inPairs; // IN clause - only one allowed private List inValues; private String inProperty; // normal EQ expressions private List eqList; private NaturalKeySet set; private int hitCount; public NaturalKeyQueryData(String[] naturalKey) { this.naturalKey = naturalKey; } private boolean matchProperty(String propName) { for (String key : naturalKey) { if (key.equals(propName)) { return true; } } return false; } /** * Match for In Pairs expression. We only allow one IN clause. */ public List matchInPairs(String property0, String property1, List inPairs) { if (hasIn) { // only 1 IN allowed (to project naturalIds) return null; } if (matchProperty(property0) && matchProperty(property1)) { this.hasIn = true; this.inProperty0 = property0; this.inProperty1 = property1; this.inPairs = new ArrayList<>(inPairs); // will be modified return this.inPairs; } return null; } /** * Match for IN expression. We only allow one IN clause. */ public List matchIn(String propName, List sourceValues) { if (hasIn) { // only 1 IN allowed (to project naturalIds) return null; } if (matchProperty(propName)) { this.hasIn = true; this.inProperty = propName; this.inValues = new ArrayList<>(sourceValues); return this.inValues; } return null; } /** * Match for an EQ expression. */ public boolean matchEq(String propName, Object bindValue) { if (matchProperty(propName)) { if (eqList == null) { eqList = new ArrayList<>(); } eqList.add(new NaturalKeyEq(propName, bindValue)); return true; } return false; } /** * Build and return the set of natural keys we will use. */ public NaturalKeySet buildKeys() { if (!expressionCount() || !matchProperties()) { return null; } 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)); } } 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)); } } else { // only one - a findOne() set.add(new NaturalKeyEntry(naturalKey, eqList)); } return set; } /** * Return true if the properties match the natural key properties. */ private boolean matchProperties() { if (naturalKey.length == 1) { // simple single property case if (inProperty != null) { return inProperty.equals(naturalKey[0]); } else { return eqList.get(0).property.equals(naturalKey[0]); } } // multiple properties case Set exprProps = new HashSet<>(); if (inProperty != null) { exprProps.add(inProperty); } if (inProperty0 != null) { exprProps.add(inProperty0); } if (inProperty1 != null) { exprProps.add(inProperty1); } if (eqList != null) { for (NaturalKeyEq eq : eqList) { exprProps.add(eq.property); } } if (exprProps.size() != naturalKey.length) { return false; } for (String key : naturalKey) { if (!exprProps.remove(key)) { return false; } } return exprProps.isEmpty(); } /** * Check that all the natural key properties are defined. */ private boolean expressionCount() { int defined = (inValues == null) ? 0 : 1; defined += (inPairs == null) ? 0 : 2; defined += (eqList == null) ? 0 : eqList.size(); return defined == naturalKey.length; } /** * Return the number of entries in the IN clause left remaining (to hit the DB with). */ public boolean allHits() { return hitCount > 0 && hitCount == set.size() && (inValues == null || inValues.isEmpty()); } /** * Adjust the IN clause removing the hit entry. */ public List removeHits(BeanCacheResult cacheResult) { List> hits = cacheResult.hits(); this.hitCount = hits.size(); List beans = new ArrayList<>(hitCount); for (BeanCacheResult.Entry hit : hits) { if (inValues != null) { Object naturalKey = hit.getKey(); Object inValue = set.getInValue(naturalKey); inValues.remove(inValue); } else if (inPairs != null) { Object naturalKey = hit.getKey(); Pairs.Entry inValue = (Pairs.Entry)set.getInValue(naturalKey); inPairs.remove(inValue); } beans.add(hit.getBean()); } return beans; } }