WIP AutoTune refactor

This commit is contained in:
Robin Bygrave
2015-09-04 08:48:25 +12:00
parent 5c1de0a886
commit 5b8ce8eaa0
27 changed files with 1092 additions and 34 deletions
@@ -87,6 +87,17 @@ public final class CallStack implements Serializable {
return zeroHash + ":" + pathHash + ":" + callStack[0];
}
/**
* Return the call stack lines appended with the given newLine string.
*/
public String description(String newLine) {
StringBuilder sb = new StringBuilder(400);
for (int i = 0; i < callStack.length; i++) {
sb.append(callStack[i].toString()).append(newLine);
}
return sb.toString();
}
public String getOriginKey(int queryHash) {
return zeroHash + "." + enc(queryHash) + "." + pathHash;
}
@@ -19,6 +19,10 @@ public class AutoTuneCollection {
return entry;
}
public List<Entry> getEntries() {
return entries;
}
/**
* Profiling entry at a given origin point.
*/
@@ -0,0 +1,123 @@
package com.avaje.ebeaninternal.server.autofetch.model;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element ref="{http://ebean-orm.github.io/xml/ns/autotune}origin" maxOccurs="unbounded" minOccurs="0"/>
* &lt;element ref="{http://ebean-orm.github.io/xml/ns/autotune}profileDiff" minOccurs="0"/>
* &lt;element ref="{http://ebean-orm.github.io/xml/ns/autotune}profileNew" minOccurs="0"/>
* &lt;/sequence>
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"origin",
"profileDiff",
"profileNew"
})
@XmlRootElement(name = "autotune")
public class Autotune {
protected List<Origin> origin;
protected ProfileDiff profileDiff;
protected ProfileNew profileNew;
/**
* Gets the value of the origin property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the origin property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getOrigin().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link Origin }
*
*
*/
public List<Origin> getOrigin() {
if (origin == null) {
origin = new ArrayList<Origin>();
}
return this.origin;
}
/**
* Gets the value of the profileDiff property.
*
* @return
* possible object is
* {@link ProfileDiff }
*
*/
public ProfileDiff getProfileDiff() {
return profileDiff;
}
/**
* Sets the value of the profileDiff property.
*
* @param value
* allowed object is
* {@link ProfileDiff }
*
*/
public void setProfileDiff(ProfileDiff value) {
this.profileDiff = value;
}
/**
* Gets the value of the profileNew property.
*
* @return
* possible object is
* {@link ProfileNew }
*
*/
public ProfileNew getProfileNew() {
return profileNew;
}
/**
* Sets the value of the profileNew property.
*
* @param value
* allowed object is
* {@link ProfileNew }
*
*/
public void setProfileNew(ProfileNew value) {
this.profileNew = value;
}
}
@@ -0,0 +1,64 @@
package com.avaje.ebeaninternal.server.autofetch.model;
import javax.xml.bind.annotation.XmlRegistry;
/**
* This object contains factory methods for each
* Java content interface and Java element interface
* generated in the com.avaje.ebeaninternal.server.autofetch.model package.
* <p>An ObjectFactory allows you to programatically
* construct new instances of the Java representation
* for XML content. The Java representation of XML
* content can consist of schema derived interfaces
* and classes representing the binding of schema
* type definitions, element declarations and model
* groups. Factory methods for each of these are
* provided in this class.
*
*/
@XmlRegistry
public class ObjectFactory {
/**
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.avaje.ebeaninternal.server.autofetch.model
*
*/
public ObjectFactory() {
}
/**
* Create an instance of {@link ProfileNew }
*
*/
public ProfileNew createProfileNew() {
return new ProfileNew();
}
/**
* Create an instance of {@link Origin }
*
*/
public Origin createOrigin() {
return new Origin();
}
/**
* Create an instance of {@link Autotune }
*
*/
public Autotune createAutotune() {
return new Autotune();
}
/**
* Create an instance of {@link ProfileDiff }
*
*/
public ProfileDiff createProfileDiff() {
return new ProfileDiff();
}
}
@@ -0,0 +1,171 @@
package com.avaje.ebeaninternal.server.autofetch.model;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element name="callStack" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* &lt;/sequence>
* &lt;attribute name="key" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="beanType" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="detail" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="tuneDetail" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"callStack"
})
@XmlRootElement(name = "origin")
public class Origin {
protected String callStack;
@XmlAttribute(name = "key", required = true)
protected String key;
@XmlAttribute(name = "beanType")
protected String beanType;
@XmlAttribute(name = "detail")
protected String detail;
@XmlAttribute(name = "tuneDetail")
protected String tuneDetail;
/**
* Gets the value of the callStack property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getCallStack() {
return callStack;
}
/**
* Sets the value of the callStack property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setCallStack(String value) {
this.callStack = value;
}
/**
* Gets the value of the key property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getKey() {
return key;
}
/**
* Sets the value of the key property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setKey(String value) {
this.key = value;
}
/**
* Gets the value of the beanType property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getBeanType() {
return beanType;
}
/**
* Sets the value of the beanType property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setBeanType(String value) {
this.beanType = value;
}
/**
* Gets the value of the detail property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getDetail() {
return detail;
}
/**
* Sets the value of the detail property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setDetail(String value) {
this.detail = value;
}
/**
* Gets the value of the tuneDetail property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getTuneDetail() {
return tuneDetail;
}
/**
* Sets the value of the tuneDetail property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setTuneDetail(String value) {
this.tuneDetail = value;
}
}
@@ -0,0 +1,69 @@
package com.avaje.ebeaninternal.server.autofetch.model;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element ref="{http://ebean-orm.github.io/xml/ns/autotune}origin" maxOccurs="unbounded" minOccurs="0"/>
* &lt;/sequence>
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"origin"
})
@XmlRootElement(name = "profileDiff")
public class ProfileDiff {
protected List<Origin> origin;
/**
* Gets the value of the origin property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the origin property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getOrigin().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link Origin }
*
*
*/
public List<Origin> getOrigin() {
if (origin == null) {
origin = new ArrayList<Origin>();
}
return this.origin;
}
}
@@ -0,0 +1,69 @@
package com.avaje.ebeaninternal.server.autofetch.model;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element ref="{http://ebean-orm.github.io/xml/ns/autotune}origin" maxOccurs="unbounded" minOccurs="0"/>
* &lt;/sequence>
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"origin"
})
@XmlRootElement(name = "profileNew")
public class ProfileNew {
protected List<Origin> origin;
/**
* Gets the value of the origin property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the origin property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getOrigin().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link Origin }
*
*
*/
public List<Origin> getOrigin() {
if (origin == null) {
origin = new ArrayList<Origin>();
}
return this.origin;
}
}
@@ -0,0 +1,2 @@
@javax.xml.bind.annotation.XmlSchema(namespace = "http://ebean-orm.github.io/xml/ns/autotune", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.avaje.ebeaninternal.server.autofetch.model;
@@ -0,0 +1,57 @@
package com.avaje.ebeaninternal.server.autofetch.service;
import com.avaje.ebeaninternal.server.autofetch.model.Autotune;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Reads a profiling xml document.
*/
public class AutoTuneXmlReader {
/**
* Read and return a Profiling from an xml file.
*/
public Autotune read(File file) {
try {
return readFile(file);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
protected Autotune readFile(File file) throws IOException {
if (!file.exists()) {
return new Autotune();
}
FileInputStream is = new FileInputStream(file);
try {
return read(is);
} finally {
is.close();
}
}
/**
* Read and return a Profiling from an xml document.
*/
public Autotune read(InputStream is) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Autotune.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
return (Autotune) unmarshaller.unmarshal(is);
} catch (JAXBException e) {
throw new IllegalStateException(e);
}
}
}
@@ -0,0 +1,32 @@
package com.avaje.ebeaninternal.server.autofetch.service;
import com.avaje.ebeaninternal.server.autofetch.model.Autotune;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.File;
/**
* Simple writer for output of the AutoTune Profiling as an XML document.
*/
public class AutoTuneXmlWriter {
/**
* Write Profiling to a file as xml.
*/
public void write(Autotune profiling, File file) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Autotune.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(profiling, file);
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}
}
@@ -1,14 +1,25 @@
package com.avaje.ebeaninternal.server.autofetch.service;
import com.avaje.ebean.bean.ObjectGraphOrigin;
import com.avaje.ebean.config.AutofetchConfig;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.autofetch.AutoTuneService;
import com.avaje.ebeaninternal.server.autofetch.AutoTuneCollection;
import com.avaje.ebeaninternal.server.autofetch.AutoTuneService;
import com.avaje.ebeaninternal.server.autofetch.model.Autotune;
import com.avaje.ebeaninternal.server.autofetch.model.Origin;
import com.avaje.ebeaninternal.server.autofetch.model.ProfileDiff;
import com.avaje.ebeaninternal.server.autofetch.model.ProfileNew;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetailParser;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.List;
/**
* Implementation of the AutoTuneService which is comprised of profiling and query tuning.
*/
@@ -40,12 +51,65 @@ public class BaseAutoTuneService implements AutoTuneService {
*/
public void startup() {
File file = new File("ebean-autotune.xml");
AutoTuneXmlReader reader = new AutoTuneXmlReader();
Autotune profiling = reader.read(file);
List<Origin> originList = profiling.getOrigin();
for (Origin origin : originList) {
String key = origin.getKey();
String detail = origin.getDetail();
OrmQueryDetailParser parser = new OrmQueryDetailParser(detail);
OrmQueryDetail fetchDetail = parser.parse();
TunedQueryInfo tunedQueryInfo = new TunedQueryInfo(fetchDetail);
queryTuner.load(key, tunedQueryInfo);
}
}
private void saveProfiling() {
Autotune document = new Autotune();
AutoTuneCollection autoTuneCollection = profileManager.profilingCollection(false);
List<AutoTuneCollection.Entry> entries = autoTuneCollection.getEntries();
for (AutoTuneCollection.Entry entry : entries) {
ObjectGraphOrigin point = entry.getOrigin();
OrmQueryDetail profileDetail = entry.getDetail();
OrmQueryDetail tuneDetail = queryTuner.get(point.getKey());
if (tuneDetail == null) {
ProfileNew profileNew = document.getProfileNew();
if (profileNew == null) {
profileNew = new ProfileNew();
document.setProfileNew(profileNew);
}
profileNew.getOrigin().add( createOrigin(entry, point));
} else if (!tuneDetail.isAutoTuneEqual(profileDetail)) {
Origin origin1 = createOrigin(entry, point);
origin1.setTuneDetail(tuneDetail.toString());
ProfileDiff diff = document.getProfileDiff();
if (diff == null) {
diff = new ProfileDiff();
document.setProfileDiff(diff);
}
diff.getOrigin().add(origin1);
}
}
File file = new File("ebean-autotune-profiling.xml");
AutoTuneXmlWriter writer = new AutoTuneXmlWriter();
writer.write(document, file);
}
@NotNull
private Origin createOrigin(AutoTuneCollection.Entry entry, ObjectGraphOrigin point) {
Origin origin1 = new Origin();
origin1.setKey(point.getKey());
origin1.setBeanType(point.getBeanType());
origin1.setDetail(entry.getDetail().toString());
origin1.setCallStack(point.getCallStack().description("\n"));
return origin1;
}
/**
@@ -7,6 +7,7 @@ import com.avaje.ebean.config.AutofetchMode;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.autofetch.ProfilingListener;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import javax.persistence.PersistenceException;
import java.util.Map;
@@ -48,6 +49,17 @@ public class BaseQueryTuner {
tunedQueryInfoMap.put(key, queryInfo);
}
/**
* Return the detail currently used for tuning.
* This returns null if there is currently no matching tuning.
*/
public OrmQueryDetail get(String key) {
TunedQueryInfo info = tunedQueryInfoMap.get(key);
return (info == null) ? null : info.getTunedDetail();
}
boolean fullProfiling = true;
/**
* Auto tune the query and enable profiling.
*/
@@ -58,7 +70,11 @@ public class BaseQueryTuner {
}
if (!useAutoTune(query)) {
// not using autoFetch for this query
// not tuning this query but maybe profiling
if (fullProfiling) {
CallStack stack = server.createCallStack();
profiling(query, stack);
}
return false;
}
@@ -88,6 +104,16 @@ public class BaseQueryTuner {
return false;
}
private void profiling(SpiQuery<?> query, CallStack stack) {
// create a query point to identify the query
ObjectGraphNode origin = query.setOrigin(stack);
if (profilingListener.isProfileRequest(origin)) {
// collect more profiling based on profiling rate etc
query.setProfilingListener(profilingListener);
}
}
/**
* Return true if we should try to tune this query.
*/
@@ -16,6 +16,8 @@ import java.util.concurrent.atomic.AtomicLong;
public class ProfileOrigin {
//private static final Logger logger = LoggerFactory.getLogger(ProfileOrigin.class);
private static final long RESET_COUNT = -1000000000L;
private final ObjectGraphOrigin origin;
@@ -89,10 +91,10 @@ public class ProfileOrigin {
}
private OrmQueryDetail buildDetail(BeanDescriptor<?> rootDesc) {
PathProperties pathProps = new PathProperties();
PathProperties pathProps = new PathProperties();
for (ProfileOriginNodeUsage statsNode : nodeUsageMap.values()) {
statsNode.buildTunedFetch(pathProps, rootDesc);
statsNode.buildTunedFetch(pathProps, rootDesc, queryTuningAddVersion);
}
OrmQueryDetail detail = new OrmQueryDetail();
@@ -142,10 +144,10 @@ public class ProfileOrigin {
*/
public void collectUsageInfo(NodeUsageCollector profile) {
if (!profile.isEmpty()) {
ObjectGraphNode node = profile.getNode();
//logger.info("COLLECT USAGE {}", profile.toString());
ProfileOriginNodeUsage nodeStats = getNodeStats(node.getPath());
if (!profile.isEmpty()) {
ProfileOriginNodeUsage nodeStats = getNodeStats(profile.getNode().getPath());
nodeStats.collectUsageInfo(profile);
}
}
@@ -153,9 +155,11 @@ public class ProfileOrigin {
private ProfileOriginNodeUsage getNodeStats(String path) {
synchronized (monitor) {
// handle null paths as using ConcurrentHashMap
path = (path == null) ? "" : path;
ProfileOriginNodeUsage nodeStats = nodeUsageMap.get(path);
if (nodeStats == null) {
nodeStats = new ProfileOriginNodeUsage(path, queryTuningAddVersion);
nodeStats = new ProfileOriginNodeUsage(path);
nodeUsageMap.put(path, nodeStats);
}
return nodeStats;
@@ -24,8 +24,6 @@ public class ProfileOriginNodeUsage {
private final String path;
private final boolean queryTuningAddVersion;
private int profileCount;
private int profileUsedCount;
@@ -34,12 +32,12 @@ public class ProfileOriginNodeUsage {
private final Set<String> aggregateUsed = new LinkedHashSet<String>();
public ProfileOriginNodeUsage(String path, boolean queryTuningAddVersion) {
this.path = path;
this.queryTuningAddVersion = queryTuningAddVersion;
public ProfileOriginNodeUsage(String path) {
// handle null paths as using ConcurrentHashMap
this.path = "".equals(path) ? null : path;
}
public void buildTunedFetch(PathProperties pathProps, BeanDescriptor<?> rootDesc) {
protected void buildTunedFetch(PathProperties pathProps, BeanDescriptor<?> rootDesc, boolean addVersionProperty) {
synchronized (monitor) {
@@ -59,6 +57,7 @@ public class ProfileOriginNodeUsage {
}
for (String propName : aggregateUsed) {
//propName = "".equals(propName) ? null : propName;
BeanProperty beanProp = desc.getBeanPropertyFromPath(propName);
if (beanProp == null) {
logger.warn("AutoTune: Can't find property[" + propName + "] for " + desc.getName());
@@ -81,7 +80,7 @@ public class ProfileOriginNodeUsage {
}
}
if ((modified || queryTuningAddVersion) && desc != null) {
if ((modified || addVersionProperty) && desc != null) {
BeanProperty versionProp = desc.getVersionProperty();
if (versionProp != null) {
pathProps.addToPath(path, versionProp.getName());
@@ -93,7 +92,7 @@ public class ProfileOriginNodeUsage {
/**
* Collect usage from a node.
*/
public void collectUsageInfo(NodeUsageCollector profile) {
protected void collectUsageInfo(NodeUsageCollector profile) {
synchronized (monitor) {
@@ -21,6 +21,18 @@ public class TunedQueryInfo implements Serializable {
this.tunedDetail = tunedDetail;
}
public TunedQueryInfo(OrmQueryDetail tunedDetail) {
this.origin = null;
this.tunedDetail = tunedDetail;
}
/**
* Return the tuned detail (for comparison with profiling information).
*/
public OrmQueryDetail getTunedDetail() {
return tunedDetail;
}
/**
* Tune the query by replacing its OrmQueryDetail with a tuned one.
*
@@ -47,7 +59,7 @@ public class TunedQueryInfo implements Serializable {
}
public String toString() {
return origin.getBeanType() + " " + origin.getKey() + " " + tunedDetail;
return tunedDetail.toString();
}
}
@@ -236,6 +236,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private void configureServerPlugins() {
autoTuneService.startup();
for (SpiServerPlugin plugin : serverPlugins) {
plugin.configure(this);
}
@@ -9,6 +9,7 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.PersistenceException;
@@ -83,25 +84,48 @@ public class OrmQueryDetail implements Serializable {
* Return true if equal in terms of autofetch (select and joins).
*/
public boolean isAutoTuneEqual(OrmQueryDetail otherDetail) {
return autofetchPlanHash() == otherDetail.autofetchPlanHash();
}
/**
* Calculate the hash for the query plan.
*/
private int autofetchPlanHash() {
int hc = (baseProps == null ? 1 : baseProps.autofetchPlanHash());
if (fetchPaths != null) {
for (OrmQueryProperties p : fetchPaths.values()) {
hc = hc * 31 + p.autofetchPlanHash();
if (!isSame(baseProps, otherDetail.baseProps)) {
return false;
}
if (fetchPaths == null) {
return otherDetail.fetchPaths == null;
}
Set<Map.Entry<String, OrmQueryProperties>> entries = fetchPaths.entrySet();
for (Map.Entry<String, OrmQueryProperties> entry : entries) {
OrmQueryProperties chunk = otherDetail.getChunk(entry.getKey(), false);
if (!isSame(entry.getValue(), chunk)) {
return false;
}
}
return hc;
return true;
//return autofetchPlanHash() == otherDetail.autofetchPlanHash();
}
private boolean isSame(OrmQueryProperties p1, OrmQueryProperties p2) {
if (p1 == null) {
return p2 == null;
}
return p1.isSame(p2);
}
// /**
// * Calculate the hash for the query plan.
// */
// private int autofetchPlanHash() {
//
// int hc = (baseProps == null ? 1 : baseProps.autofetchPlanHash());
//
// if (fetchPaths != null) {
// for (OrmQueryProperties p : fetchPaths.values()) {
// hc = hc * 31 + p.autofetchPlanHash();
// }
// }
//
// return hc;
// }
public String toString() {
StringBuilder sb = new StringBuilder();
if (baseProps != null) {
@@ -27,10 +27,11 @@ public class OrmQueryDetailParser {
this.parser = new SimpleTextParser(oql);
}
public void parse() throws PersistenceException {
public OrmQueryDetail parse() throws PersistenceException {
parser.nextWord();
processInitial();
return detail;
}
protected void assign(DefaultOrmQuery<?> query) {
@@ -42,7 +43,9 @@ public class OrmQueryDetailParser {
}
private void processInitial() {
if (parser.isMatch("find")) {
if (parser.isMatch("select")) {
readSelect();
} else if (parser.isMatch("find")) {
OrmQueryProperties props = readFindFetch();
detail.setBase(props);
} else {
@@ -149,6 +152,19 @@ public class OrmQueryDetailParser {
}
}
private void readSelect() {
String path = null;
String props = parser.nextWord();
if (props.startsWith("(")) {
props = props.substring(1, props.length() - 1);
OrmQueryProperties base = new OrmQueryProperties(path, props);
detail.setBase(base);
parser.nextWord();
} else {
process();
}
}
private OrmQueryProperties readFindFetch() {
boolean readAlias = false;
@@ -573,4 +573,11 @@ public class OrmQueryProperties implements Serializable {
return Collections.unmodifiableSet(set);
}
public boolean isSame(OrmQueryProperties p2) {
if (included == null) {
return p2.included == null;
}
return included.equals(p2.included);
}
}