Compare commits

..
Author SHA1 Message Date
Robin Bygrave d31547fb69 [maven-release-plugin] prepare release avaje-ebeanorm-6.16.1 2016-01-19 19:49:08 +13:00
Robin Bygrave 93adaa12bc Bump pom to 6.16.1-SNAPSHOT 2016-01-19 19:48:22 +13:00
Robin Bygrave d1cdbe94de #529 - @OneToOne with @JoinColumn to non primary key throws Data conversion error 2016-01-19 19:46:41 +13:00
Robin Bygrave e7c6223799 #530 - Bad join with raw() expression on a @ManyToMany path. 2016-01-19 13:18:28 +13:00
Robin Bygrave d33afd85de #533 - DDL - Invalid foreign key constraint name for @ManyToMany when using explicit schema in @Table - Temp testing removed 2016-01-19 10:01:04 +13:00
Robin Bygrave 77bcb4aef9 #533 - DDL - Invalid foreign key constraint name for @ManyToMany when using explicit schema in @Table - Temp testing 2016-01-19 09:57:49 +13:00
Robin Bygrave a70fe184c2 #533 - DDL - Invalid foreign key constraint name for @ManyToMany when using explicit schema in @Table - Fix 2016-01-19 09:56:27 +13:00
Robin Bygrave 7238f39be5 #531 - AutoTune - On shutdown provide ability to saving profiling without garbage collection 2016-01-19 08:16:17 +13:00
Robin Bygrave ae44b59c5f #532 - Change example expression such that it supports nested beans 2016-01-18 23:52:28 +13:00
Robin Bygrave ced4e71307 [maven-release-plugin] prepare for next development iteration 2016-01-13 16:13:59 +13:00
Robin Bygrave 9cb43bd5dd [maven-release-plugin] prepare release avaje-ebeanorm-6.15.2 2016-01-13 16:13:30 +13:00
Robin Bygrave 9665de2dbe #502 - jsonContext.toJson(o, generator, path) can not apply path on @Transient field that is an entity bean 2016-01-13 13:29:06 +13:00
Robin Bygrave e2666f0c4c No effective change - turn off autotune profiling/tuning flags for test running 2016-01-13 12:38:04 +13:00
Robin Bygrave 89c83c90d1 No effective change - add test to show findRowCount with joins 2016-01-13 12:35:52 +13:00
Robin Bygrave cdd25351a6 No effective change - test code static imports 2016-01-13 11:40:23 +13:00
Robin Bygrave 17e0413837 No effective change - code format 2016-01-13 11:38:53 +13:00
Robin Bygrave b04419d9c7 #527 - @OrderBy on child-of-child property 2016-01-13 11:37:38 +13:00
Robin Bygrave 0453892ebd #526 - findPagedList with @EmbeddedId broken - automatically adds orderby t0.null 2016-01-13 10:43:56 +13:00
Robin Bygrave 55493c5b7d #524 - ExplicitJdbcTransaction leaves dangling “begin” - comment update 2016-01-13 00:17:22 +13:00
Robin Bygrave eb2f7cd852 #525 - Automatic autotune data 2016-01-13 00:08:00 +13:00
Robin Bygrave f44eb234c8 #525 - Automatic autotune data - initial wip 2016-01-12 11:45:24 +13:00
Robin Bygrave 404b57ab21 #524 - ExplicitJdbcTransaction leaves dangling “begin” - expected fix 2016-01-12 08:37:12 +13:00
Robin Bygrave a54f09ca4b #524 - ExplicitJdbcTransaction leaves dangling “begin” - expected fix 2016-01-11 14:04:14 +13:00
Robin Bygrave a3bb909308 [maven-release-plugin] prepare for next development iteration 2016-01-08 00:22:06 +13:00
52 changed files with 1465 additions and 511 deletions
+1 -1
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>6.15.1</version>
<version>6.16.1</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -11,4 +11,14 @@ public interface AutoTune {
*/
void collectProfiling();
/**
* Output the profiling.
* <p>
* When profiling updates are applied to tuning at runtime this reports all tuning and profiling combined.
* When profiling is not applied at runtime then this reports the diff report with new and diff entries relative
* to the existing tuning.
* </p>
*/
void reportProfiling();
}
@@ -21,9 +21,13 @@ public class AutoTuneConfig {
private double profilingRate = 0.01;
private int profilingUpdateFrequency;
private int garbageCollectionWait = 100;
private boolean skipCollectionOnShutdown;
private boolean skipGarbageCollectionOnShutdown;
private boolean skipProfileReportingOnShutdown;
public AutoTuneConfig() {
}
@@ -56,6 +60,20 @@ public class AutoTuneConfig {
this.profilingFile = profilingFile;
}
/**
* Return the frequency in seconds the profiling should be collected and automatically applied to the tuning.
*/
public int getProfilingUpdateFrequency() {
return profilingUpdateFrequency;
}
/**
* Set the frequency in seconds the profiling should be collected and automatically applied to the tuning.
*/
public void setProfilingUpdateFrequency(int profilingUpdateFrequency) {
this.profilingUpdateFrequency = profilingUpdateFrequency;
}
/**
* Return the mode used when autoTune has not been explicit defined on a
* query.
@@ -184,17 +202,33 @@ public class AutoTuneConfig {
}
/**
* Return true if profiling collection should be skipped on shutdown.
* Return true if triggering garbage collection should be skipped on shutdown.
* You might set this when System.GC() slows a application shutdown too much.
*/
public boolean isSkipCollectionOnShutdown() {
return skipCollectionOnShutdown;
public boolean isSkipGarbageCollectionOnShutdown() {
return skipGarbageCollectionOnShutdown;
}
/**
* Set to true if profiling collection should be skipped on shutdown.
* Set to true if triggering garbage collection should be skipped on shutdown.
* You might set this when System.GC() slows a application shutdown too much.
*/
public void setSkipCollectionOnShutdown(boolean skipCollectionOnShutdown) {
this.skipCollectionOnShutdown = skipCollectionOnShutdown;
public void setSkipGarbageCollectionOnShutdown(boolean skipGarbageCollectionOnShutdown) {
this.skipGarbageCollectionOnShutdown = skipGarbageCollectionOnShutdown;
}
/**
* Return true if profile reporting should be skipped on shutdown.
*/
public boolean isSkipProfileReportingOnShutdown() {
return skipProfileReportingOnShutdown;
}
/**
* Set to true if profile reporting should be skipped on shutdown.
*/
public void setSkipProfileReportingOnShutdown(boolean skipProfileReportingOnShutdown) {
this.skipProfileReportingOnShutdown = skipProfileReportingOnShutdown;
}
/**
@@ -206,13 +240,15 @@ public class AutoTuneConfig {
queryTuningAddVersion = p.getBoolean("autoTune.queryTuningAddVersion", queryTuningAddVersion);
queryTuningFile = p.get("autoTune.queryTuningFile", queryTuningFile);
skipCollectionOnShutdown = p.getBoolean("autoTune.skipCollectionOnShutdown", skipCollectionOnShutdown);
skipGarbageCollectionOnShutdown = p.getBoolean("autoTune.skipGarbageCollectionOnShutdown", skipGarbageCollectionOnShutdown);
skipProfileReportingOnShutdown = p.getBoolean("autoTune.skipProfileReportingOnShutdown", skipProfileReportingOnShutdown);
mode = p.getEnum(AutoTuneMode.class, "autoTune.mode", mode);
profiling = p.getBoolean("autoTune.profiling", profiling);
profilingBase = p.getInt("autoTune.profilingBase", profilingBase);
profilingRate = p.getDouble("autoTune.profilingRate", profilingRate);
profilingFile = p.get("autoTune.profilingFile", profilingFile);
profilingUpdateFrequency = p.getInt("autoTune.profilingUpdateFrequency", profilingUpdateFrequency);
}
}
@@ -43,6 +43,10 @@ public class ModelBuildContext {
model.adjustDraftReferences();
}
public String normaliseTable(String baseTable) {
return constraintNaming.normaliseTable(baseTable);
}
public String primaryKeyName(String tableName) {
return maxLength(constraintNaming.primaryKeyName(tableName), 0);
}
@@ -64,8 +64,7 @@ public class ModelBuildIntersectionTable {
private void buildFkConstraints(BeanDescriptor<?> desc, TableJoinColumn[] columns, boolean direction) {
String tableName = intersectionTableJoin.getTable();
String baseTable = desc.getBaseTable();
String baseTable = ctx.normaliseTable(desc.getBaseTable());
String fkName = ctx.foreignKeyConstraintName(tableName, baseTable, ++countForeignKey);
String fkIndex = ctx.foreignKeyIndexName(tableName, baseTable, countForeignKey);
@@ -1,153 +1,153 @@
package com.avaje.ebeaninternal.api;
import java.io.Serializable;
import java.util.Collection;
import java.util.TreeMap;
import java.util.TreeSet;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
import com.avaje.ebeaninternal.server.query.SplitName;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
import java.io.Serializable;
import java.util.Collection;
import java.util.TreeMap;
import java.util.TreeSet;
/**
* Holds the joins needs to support the many where predicates.
* These joins are independent of any 'fetch' joins on the many.
*/
public class ManyWhereJoins implements Serializable {
private static final long serialVersionUID = -6490181101871795417L;
private final TreeMap<String,PropertyJoin> joins = new TreeMap<String,PropertyJoin>();
private static final long serialVersionUID = -6490181101871795417L;
private StringBuilder formulaProperties = new StringBuilder();
private boolean formulaWithJoin;
private final TreeMap<String, PropertyJoin> joins = new TreeMap<String, PropertyJoin>();
private StringBuilder formulaProperties = new StringBuilder();
private boolean formulaWithJoin;
/**
* 'Mode' indicating that joins added while this is true are required to be outer joins.
*/
private boolean requireOuterJoins;
/**
* Return the current 'mode' indicating if outer joins are currently required or not.
*/
public boolean isRequireOuterJoins() {
return requireOuterJoins;
}
private boolean requireOuterJoins;
/**
* Set the 'mode' to be that joins added are required to be outer joins.
* This is set during the evaluation of disjunction predicates.
*/
public void setRequireOuterJoins(boolean requireOuterJoins) {
this.requireOuterJoins = requireOuterJoins;
}
/**
* Return the current 'mode' indicating if outer joins are currently required or not.
*/
public boolean isRequireOuterJoins() {
return requireOuterJoins;
}
/**
* Add a many where join.
*/
public void add(ElPropertyDeploy elProp) {
String join = elProp.getElPrefix();
BeanProperty p = elProp.getBeanProperty();
if (p instanceof BeanPropertyAssocMany<?>){
join = addManyToJoin(join, p.getName());
}
if (join != null){
addJoin(join);
if (p != null) {
String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix();
if (secondaryTableJoinPrefix != null) {
addJoin(join+"."+secondaryTableJoinPrefix);
}
}
addParentJoins(join);
}
}
/**
* For 'many' properties we also need to add the name of the
* many property to get the full logical name of the join.
*/
private String addManyToJoin(String join, String manyPropName){
if (join == null){
return manyPropName;
} else {
return join+"."+manyPropName;
}
}
private void addParentJoins(String join) {
String[] split = SplitName.split(join);
if (split[0] != null){
addJoin(split[0]);
addParentJoins(split[0]);
}
}
/**
* Set the 'mode' to be that joins added are required to be outer joins.
* This is set during the evaluation of disjunction predicates.
*/
public void setRequireOuterJoins(boolean requireOuterJoins) {
this.requireOuterJoins = requireOuterJoins;
}
private void addJoin(String property) {
SqlJoinType joinType = (requireOuterJoins) ? SqlJoinType.OUTER: SqlJoinType.INNER;
joins.put(property, new PropertyJoin(property, joinType));
}
/**
* Return true if there are no extra many where joins.
*/
public boolean isEmpty() {
return joins.isEmpty();
}
/**
* Return the set of many where joins.
*/
public Collection<PropertyJoin> getPropertyJoins() {
return joins.values();
}
/**
* Add a many where join.
*/
public void add(ElPropertyDeploy elProp) {
/**
* Return the set of property names for the many where joins.
*/
public TreeSet<String> getPropertyNames() {
TreeSet<String> propertyNames = new TreeSet<String>();
for (PropertyJoin join : joins.values()) {
propertyNames.add(join.getProperty());
String join = elProp.getElPrefix();
BeanProperty p = elProp.getBeanProperty();
if (p instanceof BeanPropertyAssocMany<?>) {
join = addManyToJoin(join, p.getName());
}
if (join != null) {
addJoin(join);
if (p != null) {
String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix();
if (secondaryTableJoinPrefix != null) {
addJoin(join + "." + secondaryTableJoinPrefix);
}
}
return propertyNames;
addParentJoins(join);
}
}
/**
* In findRowCount query found a formula property with a join clause so building a select clause
* specifically for the findRowCount query.
*/
public void addFormulaWithJoin(String propertyName) {
if (formulaWithJoin) {
formulaProperties.append(",");
} else {
formulaProperties = new StringBuilder();
formulaWithJoin = true;
}
formulaProperties.append(propertyName);
/**
* For 'many' properties we also need to add the name of the
* many property to get the full logical name of the join.
*/
private String addManyToJoin(String join, String manyPropName) {
if (join == null) {
return manyPropName;
} else {
return join + "." + manyPropName;
}
public boolean isHasMany() {
return formulaWithJoin || !joins.isEmpty();
}
private void addParentJoins(String join) {
String[] split = SplitName.split(join);
if (split[0] != null) {
addJoin(split[0]);
addParentJoins(split[0]);
}
/**
* Return true if the findRowCount query just needs the id property in the select clause.
*/
public boolean isSelectId() {
return !formulaWithJoin;
}
private void addJoin(String property) {
SqlJoinType joinType = (requireOuterJoins) ? SqlJoinType.OUTER : SqlJoinType.INNER;
joins.put(property, new PropertyJoin(property, joinType));
}
/**
* Return true if there are no extra many where joins.
*/
public boolean isEmpty() {
return joins.isEmpty();
}
/**
* Return the set of many where joins.
*/
public Collection<PropertyJoin> getPropertyJoins() {
return joins.values();
}
/**
* Return the set of property names for the many where joins.
*/
public TreeSet<String> getPropertyNames() {
TreeSet<String> propertyNames = new TreeSet<String>();
for (PropertyJoin join : joins.values()) {
propertyNames.add(join.getProperty());
}
/**
* Return the formula properties to build the select clause for a findRowCount query.
*/
public String getFormulaProperties() {
return formulaProperties.toString();
return propertyNames;
}
/**
* In findRowCount query found a formula property with a join clause so building a select clause
* specifically for the findRowCount query.
*/
public void addFormulaWithJoin(String propertyName) {
if (formulaWithJoin) {
formulaProperties.append(",");
} else {
formulaProperties = new StringBuilder();
formulaWithJoin = true;
}
formulaProperties.append(propertyName);
}
public boolean isHasMany() {
return formulaWithJoin || !joins.isEmpty();
}
/**
* Return true if the findRowCount query just needs the id property in the select clause.
*/
public boolean isSelectId() {
return !formulaWithJoin;
}
/**
* Return the formula properties to build the select clause for a findRowCount query.
*/
public String getFormulaProperties() {
return formulaProperties.toString();
}
}
@@ -0,0 +1,61 @@
package com.avaje.ebeaninternal.server.autotune.service;
import com.avaje.ebeaninternal.server.autotune.model.Autotune;
import java.util.Collection;
/**
* Event where all tuned query information is collected.
* <p>
* This is for writing the "all" file on shutdown when using runtime tuning.
* </p>
*/
public class AutoTuneAllCollection {
final Autotune document = new Autotune();
final BaseQueryTuner queryTuner;
/**
* Construct to collect/report all tuned queries.
*/
public AutoTuneAllCollection(BaseQueryTuner queryTuner) {
this.queryTuner = queryTuner;
loadAllTuned();
}
/**
* Return the number of origin elements in the document.
*/
public int size() {
return document.getOrigin().size();
}
/**
* Return the Autotune document object.
*/
public Autotune getDocument() {
return document;
}
/**
* Write the document as an xml file.
*/
public void writeFile(String filePrefix) {
AutoTuneXmlWriter writer = new AutoTuneXmlWriter();
writer.write(document, filePrefix);
}
/**
* Loads all the existing query tuning into the document.
*/
private void loadAllTuned() {
Collection<TunedQueryInfo> all = queryTuner.getAll();
for (TunedQueryInfo tuned: all) {
document.getOrigin().add(tuned.getOrigin());
}
}
}
@@ -0,0 +1,162 @@
package com.avaje.ebeaninternal.server.autotune.service;
import com.avaje.ebean.bean.ObjectGraphOrigin;
import com.avaje.ebeaninternal.server.autotune.AutoTuneCollection;
import com.avaje.ebeaninternal.server.autotune.model.Autotune;
import com.avaje.ebeaninternal.server.autotune.model.Origin;
import com.avaje.ebeaninternal.server.autotune.model.ProfileDiff;
import com.avaje.ebeaninternal.server.autotune.model.ProfileNew;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
/**
* Event where profiling information is collected and processed for differences
* relative to the current query tuning.
*/
public class AutoTuneDiffCollection {
final Autotune document = new Autotune();
final AutoTuneCollection profiling;
final BaseQueryTuner queryTuner;
final boolean updateTuning;
int newCount;
int diffCount;
/**
* Construct to collect/report the new/diff query tuning entries.
*/
public AutoTuneDiffCollection(AutoTuneCollection profiling, BaseQueryTuner queryTuner, boolean updateTuning) {
this.profiling = profiling;
this.queryTuner = queryTuner;
this.updateTuning = updateTuning;
}
/**
* Return true if there are no new or diff entries.
*/
public boolean isEmpty() {
return newCount == 0 && diffCount == 0;
}
/**
* Return the underlying Autotune document object.
*/
public Autotune getDocument() {
return document;
}
/**
* Return the number of diff entries.
*/
public int getDiffCount() {
return diffCount;
}
/**
* Return the number of new entries.
*/
public int getNewCount() {
return newCount;
}
/**
* Return the total new and diff entries.
*/
public int getChangeCount() {
return newCount + diffCount;
}
/**
* Write the underlying document as an xml file.
*/
public void writeFile(String filePrefix) {
AutoTuneXmlWriter writer = new AutoTuneXmlWriter();
writer.write(document, filePrefix);
}
/**
* Process checking profiling entries against existing query tuning.
*/
public void process() {
for (AutoTuneCollection.Entry entry : profiling.getEntries()) {
addToDocument(entry);
}
}
/**
* Check if the entry is new or diff and add as necessary.
*/
private void addToDocument(AutoTuneCollection.Entry entry) {
ObjectGraphOrigin point = entry.getOrigin();
OrmQueryDetail profileDetail = entry.getDetail();
// compare with the existing query tuning entry
OrmQueryDetail tuneDetail = queryTuner.get(point.getKey());
if (tuneDetail == null) {
addToDocumentNewEntry(entry, point);
} else if (!tuneDetail.isAutoTuneEqual(profileDetail)) {
addToDocumentDiffEntry(entry, point, tuneDetail);
}
}
/**
* Add as a diff entry.
*/
private void addToDocumentDiffEntry(AutoTuneCollection.Entry entry, ObjectGraphOrigin point, OrmQueryDetail tuneDetail) {
diffCount++;
Origin origin = createOrigin(entry, point, tuneDetail.toString());
ProfileDiff diff = document.getProfileDiff();
if (diff == null) {
diff = new ProfileDiff();
document.setProfileDiff(diff);
}
diff.getOrigin().add(origin);
}
/**
* Add as a "new" entry.
*/
private void addToDocumentNewEntry(AutoTuneCollection.Entry entry, ObjectGraphOrigin point) {
newCount++;
ProfileNew profileNew = document.getProfileNew();
if (profileNew == null) {
profileNew = new ProfileNew();
document.setProfileNew(profileNew);
}
Origin origin = createOrigin(entry, point, entry.getOriginalQuery());
profileNew.getOrigin().add(origin);
}
/**
* Create the XML Origin bean for the given entry and ObjectGraphOrigin.
*/
private Origin createOrigin(AutoTuneCollection.Entry entry, ObjectGraphOrigin point, String query) {
Origin origin = new Origin();
origin.setKey(point.getKey());
origin.setBeanType(point.getBeanType());
origin.setDetail(entry.getDetail().toString());
origin.setCallStack(point.getCallStack().description("\n"));
origin.setOriginal(query);
if (updateTuning) {
queryTuner.put(origin);
}
return origin;
}
}
@@ -7,12 +7,29 @@ import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Simple writer for output of the AutoTune Profiling as an XML document.
*/
public class AutoTuneXmlWriter {
/**
* Write the document as xml file with the given prefix.
*/
public void write(Autotune document, String filePrefix) {
SortAutoTuneDocument.sort(document);
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss");
String now = df.format(new Date());
// write the file with serverName and now suffix as we can output the profiling many times
File file = new File(filePrefix + "-" + now + ".xml");
write(document, file);
}
/**
* Write Profiling to a file as xml.
*/
@@ -7,9 +7,11 @@ import com.avaje.ebean.config.AutoTuneMode;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.autotune.ProfilingListener;
import com.avaje.ebeaninternal.server.autotune.model.Origin;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import javax.persistence.PersistenceException;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -48,6 +50,21 @@ public class BaseQueryTuner {
this.skipAll = !queryTuning && !profiling;
}
/**
* Return all the current tuned query entries.
*/
public Collection<TunedQueryInfo> getAll() {
return tunedQueryInfoMap.values();
}
/**
* Put a query tuning entry.
*/
public void put(Origin origin) {
tunedQueryInfoMap.put(origin.getKey(), new TunedQueryInfo(origin));
}
/**
* Load the tuned query information.
*/
@@ -1,6 +1,5 @@
package com.avaje.ebeaninternal.server.autotune.service;
import com.avaje.ebean.bean.ObjectGraphOrigin;
import com.avaje.ebean.config.AutoTuneConfig;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
@@ -9,24 +8,11 @@ import com.avaje.ebeaninternal.server.autotune.AutoTuneCollection;
import com.avaje.ebeaninternal.server.autotune.AutoTuneService;
import com.avaje.ebeaninternal.server.autotune.model.Autotune;
import com.avaje.ebeaninternal.server.autotune.model.Origin;
import com.avaje.ebeaninternal.server.autotune.model.ProfileDiff;
import com.avaje.ebeaninternal.server.autotune.model.ProfileEmpty;
import com.avaje.ebeaninternal.server.autotune.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.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.TimeUnit;
/**
* Implementation of the AutoTuneService which is comprised of profiling and query tuning.
@@ -35,9 +21,13 @@ public class DefaultAutoTuneService implements AutoTuneService {
private static final Logger logger = LoggerFactory.getLogger(DefaultAutoTuneService.class);
private final SpiEbeanServer server;
private final long defaultGarbageCollectionWait;
private final boolean skipCollectionOnShutdown;
private final boolean skipGarbageCollectionOnShutdown;
private final boolean skipProfileReportingOnShutdown;
private final BaseQueryTuner queryTuner;
@@ -53,18 +43,25 @@ public class DefaultAutoTuneService implements AutoTuneService {
private final String serverName;
private final int profilingUpdateFrequency;
private long runtimeChangeCount;
public DefaultAutoTuneService(SpiEbeanServer server, ServerConfig serverConfig) {
AutoTuneConfig config = serverConfig.getAutoTuneConfig();
this.server = server;
this.queryTuning = config.isQueryTuning();
this.profiling = config.isProfiling();
this.tuningFile = config.getQueryTuningFile();
this.profilingFile = config.getProfilingFile();
this.profilingUpdateFrequency = config.getProfilingUpdateFrequency();
this.serverName = server.getName();
this.profileManager = new ProfileManager(config, server);
this.queryTuner = new BaseQueryTuner(config, server, profileManager);
this.skipCollectionOnShutdown = config.isSkipCollectionOnShutdown();
this.skipGarbageCollectionOnShutdown = config.isSkipGarbageCollectionOnShutdown();
this.skipProfileReportingOnShutdown = config.isSkipProfileReportingOnShutdown();
this.defaultGarbageCollectionWait = (long) config.getGarbageCollectionWait();
}
@@ -75,169 +72,122 @@ public class DefaultAutoTuneService implements AutoTuneService {
public void startup() {
if (queryTuning) {
File file = new File(tuningFile);
if (!file.exists()) {
logger.warn("AutoTune file {} not found - no automatic tuning will be applied", file.getAbsolutePath());
} else {
AutoTuneXmlReader reader = new AutoTuneXmlReader();
Autotune profiling = reader.read(file);
logger.info("AutoTune loading {} tuning entries", profiling.getOrigin().size());
for (Origin origin : profiling.getOrigin()) {
queryTuner.load(origin.getKey(), createTunedQueryInfo(origin));
}
loadTuningFile();
if (isRuntimeTuningUpdates()) {
// periodically gather and update query tuning
server.getBackgroundExecutor().executePeriodically(new ProfilingUpdate(), profilingUpdateFrequency, TimeUnit.SECONDS);
}
}
}
@NotNull
private TunedQueryInfo createTunedQueryInfo(Origin origin) {
OrmQueryDetail detail = new OrmQueryDetailParser(origin.getDetail()).parse();
return new TunedQueryInfo(detail);
/**
* Return true if the tuning should update periodically at runtime.
*/
private boolean isRuntimeTuningUpdates() {
return profilingUpdateFrequency > 0;
}
private void saveProfiling(boolean reset) {
private class ProfilingUpdate implements Runnable {
Autotune document = new Autotune();
AutoTuneCollection autoTuneCollection = profileManager.profilingCollection(reset);
List<AutoTuneCollection.Entry> entries = autoTuneCollection.getEntries();
// count "new" and "diff" profiling entries
AtomicInteger newCounter = new AtomicInteger();
AtomicInteger diffCounter = new AtomicInteger();
Set<String> profileKeys = new HashSet<String>();
for (AutoTuneCollection.Entry entry : entries) {
saveProfilingEntry(document, entry, newCounter, diffCounter);
profileKeys.add(entry.getOrigin().getKey());
@Override
public void run() {
runtimeTuningUpdate();
}
}
// report the origin keys that we didn't collect any profiling on
Set<String> tunerKeys = queryTuner.keySet();
for (String tuneKey : tunerKeys) {
if (!profileKeys.contains(tuneKey)) {
ProfileEmpty profileEmpty = document.getProfileEmpty();
if (profileEmpty == null) {
profileEmpty = new ProfileEmpty();
document.setProfileEmpty(profileEmpty);
}
Origin emptyOrigin = new Origin();
emptyOrigin.setKey(tuneKey);
profileEmpty.getOrigin().add(emptyOrigin);
}
}
int totalNew = newCounter.get();
int totalDiff = diffCounter.get();
if (totalNew == 0 && totalDiff == 0) {
logger.info("No new or diff entries for profiling server:{}", serverName);
/**
* Load tuning information from an existing tuning file.
*/
private void loadTuningFile() {
File file = new File(tuningFile);
if (!file.exists()) {
logger.warn("AutoTune file {} not found - no initial automatic query tuning", file.getAbsolutePath());
} else {
sortDocument(document);
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss");
String now = df.format(new Date());
// write the file with serverName and now suffix as we can output the profiling many times
File file = new File(profilingFile + "-" + serverName + "-" + now + ".xml");
AutoTuneXmlWriter writer = new AutoTuneXmlWriter();
writer.write(document, file);
logger.info("writing new:{} diff:{} profiling entries for server:{}", totalNew, totalDiff, serverName);
}
}
/**
* Set the diff and new entries by bean type followed by key.
*/
private void sortDocument(Autotune document) {
ProfileDiff profileDiff = document.getProfileDiff();
if (profileDiff != null) {
Collections.sort(profileDiff.getOrigin(), new OriginNameKeySort());
}
ProfileNew profileNew = document.getProfileNew();
if (profileNew != null) {
Collections.sort(profileNew.getOrigin(), new OriginNameKeySort());
}
ProfileEmpty profileEmpty = document.getProfileEmpty();
if (profileEmpty != null) {
Collections.sort(profileEmpty.getOrigin(), new OriginKeySort());
}
}
/**
* Comparator sort by bean type then key.
*/
class OriginNameKeySort implements Comparator<Origin> {
@Override
public int compare(Origin o1, Origin o2) {
int comp = o1.getBeanType().compareTo(o2.getBeanType());
if (comp == 0) {
comp = o1.getKey().compareTo(o2.getKey());
AutoTuneXmlReader reader = new AutoTuneXmlReader();
Autotune profiling = reader.read(file);
logger.info("AutoTune loading {} tuning entries", profiling.getOrigin().size());
for (Origin origin : profiling.getOrigin()) {
queryTuner.put(origin);
}
return comp;
}
}
/**
* Comparator sort by bean type then key.
* Collect profiling, check for new/diff to existing tuning and apply changes.
*/
class OriginKeySort implements Comparator<Origin> {
private void runtimeTuningUpdate() {
@Override
public int compare(Origin o1, Origin o2) {
return o1.getKey().compareTo(o2.getKey());
synchronized (this) {
try {
long start = System.currentTimeMillis();
AutoTuneCollection profiling = profileManager.profilingCollection(false);
AutoTuneDiffCollection event = new AutoTuneDiffCollection(profiling, queryTuner, true);
event.process();
if (event.isEmpty()) {
long exeMillis = System.currentTimeMillis() - start;
logger.debug("No query tuning updates for server:{} executionMillis:{}", serverName, exeMillis);
} else {
// report the query tuning changes that have been made
runtimeChangeCount += event.getChangeCount();
event.writeFile(profilingFile + "-" + serverName + "-update");
long exeMillis = System.currentTimeMillis() - start;
logger.info("query tuning updates - new:{} diff:{} for server:{} executionMillis:{}", event.getNewCount(), event.getDiffCount(), serverName, exeMillis);
}
} catch (Throwable e) {
logger.error("Error collecting or applying automatic query tuning", e);
}
}
}
private void saveProfilingEntry(Autotune document, AutoTuneCollection.Entry entry, AtomicInteger newCount, AtomicInteger diffCount) {
private void saveProfilingOnShutdown(boolean reset) {
ObjectGraphOrigin point = entry.getOrigin();
OrmQueryDetail profileDetail = entry.getDetail();
synchronized (this) {
if (isRuntimeTuningUpdates()) {
runtimeTuningUpdate();
outputAllTuning();
// compare with the existing query tuning entry
OrmQueryDetail tuneDetail = queryTuner.get(point.getKey());
if (tuneDetail == null) {
// New entry
newCount.incrementAndGet();
ProfileNew profileNew = document.getProfileNew();
if (profileNew == null) {
profileNew = new ProfileNew();
document.setProfileNew(profileNew);
} else {
AutoTuneCollection profiling = profileManager.profilingCollection(reset);
AutoTuneDiffCollection event = new AutoTuneDiffCollection(profiling, queryTuner, false);
event.process();
if (event.isEmpty()) {
logger.info("No new or diff entries for profiling server:{}", serverName);
} else {
event.writeFile(profilingFile + "-" + serverName);
logger.info("writing new:{} diff:{} profiling entries for server:{}", event.getNewCount(), event.getDiffCount(), serverName);
}
}
Origin origin = createOrigin(entry, point);
origin.setOriginal(entry.getOriginalQuery());
profileNew.getOrigin().add(origin);
} else if (!tuneDetail.isAutoTuneEqual(profileDetail)) {
// Diff entry
diffCount.incrementAndGet();
Origin origin = createOrigin(entry, point);
origin.setOriginal(tuneDetail.toString());
ProfileDiff diff = document.getProfileDiff();
if (diff == null) {
diff = new ProfileDiff();
document.setProfileDiff(diff);
}
diff.getOrigin().add(origin);
}
}
/**
* Create the XML Origin bean for the given entry and ObjectGraphOrigin.
* Output all the query tuning (the "all" file).
* <p>
* This is the originally loaded tuning plus any tuning changes picked up and applied at runtime.
* </p>
* <p>
* This "all" file can be used as the next "ebean-autotune.xml" file.
* </p>
*/
@NotNull
private Origin createOrigin(AutoTuneCollection.Entry entry, ObjectGraphOrigin point) {
Origin origin = new Origin();
origin.setKey(point.getKey());
origin.setBeanType(point.getBeanType());
origin.setDetail(entry.getDetail().toString());
origin.setCallStack(point.getCallStack().description("\n"));
return origin;
private void outputAllTuning() {
if (runtimeChangeCount == 0) {
logger.info("no runtime query tuning changes for server:{}", serverName);
} else {
AutoTuneAllCollection event = new AutoTuneAllCollection(queryTuner);
int size = event.size();
event.writeFile(profilingFile + "-" + serverName + "-all");
logger.info("query tuning detected [{}] changes, writing all [{}] tuning entries for server:{}", runtimeChangeCount, size, serverName);
}
}
/**
@@ -250,12 +200,29 @@ public class DefaultAutoTuneService implements AutoTuneService {
*/
@Override
public void shutdown() {
if (profiling && !skipCollectionOnShutdown) {
collectProfiling(-1);
saveProfiling(false);
if (profiling) {
if (!skipGarbageCollectionOnShutdown && !skipProfileReportingOnShutdown) {
// trigger GC to update profiling information on recently executed queries
collectProfiling(-1);
}
if (!skipProfileReportingOnShutdown) {
saveProfilingOnShutdown(false);
}
}
}
/**
* Output the profiling.
* <p>
* When profiling updates are applied to tuning at runtime this reports all tuning and profiling combined.
* When profiling is not applied at runtime then this reports the diff report with new and diff entries relative
* to the existing tuning.
* </p>
*/
public void reportProfiling() {
saveProfilingOnShutdown(false);
}
/**
* Ask for a System.gc() so that we gather node usage information.
* <p>
@@ -118,7 +118,6 @@ public class ProfileManager implements ProfilingListener {
AutoTuneCollection req = new AutoTuneCollection();
for (ProfileOrigin origin : profileMap.values()) {
BeanDescriptor<?> desc = server.getBeanDescriptorById(origin.getOrigin().getBeanType());
if (desc != null) {
origin.profilingCollection(desc, req, reset);
@@ -0,0 +1,71 @@
package com.avaje.ebeaninternal.server.autotune.service;
import com.avaje.ebeaninternal.server.autotune.model.Autotune;
import com.avaje.ebeaninternal.server.autotune.model.Origin;
import com.avaje.ebeaninternal.server.autotune.model.ProfileDiff;
import com.avaje.ebeaninternal.server.autotune.model.ProfileEmpty;
import com.avaje.ebeaninternal.server.autotune.model.ProfileNew;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Sorts Autotune document by
*/
public class SortAutoTuneDocument {
/**
* Set the diff and new entries by bean type followed by key.
*/
public static void sort(Autotune document) {
ProfileDiff profileDiff = document.getProfileDiff();
if (profileDiff != null) {
Collections.sort(profileDiff.getOrigin(), NAME_KEY_SORT);
}
ProfileNew profileNew = document.getProfileNew();
if (profileNew != null) {
Collections.sort(profileNew.getOrigin(), NAME_KEY_SORT);
}
ProfileEmpty profileEmpty = document.getProfileEmpty();
if (profileEmpty != null) {
Collections.sort(profileEmpty.getOrigin(), KEY_SORT);
}
List<Origin> origins = document.getOrigin();
if (!origins.isEmpty()) {
Collections.sort(origins, NAME_KEY_SORT);
}
}
private static final OriginNameKeySort NAME_KEY_SORT = new OriginNameKeySort();
private static final OriginKeySort KEY_SORT = new OriginKeySort();
/**
* Comparator sort by bean type then key.
*/
private static class OriginNameKeySort implements Comparator<Origin> {
@Override
public int compare(Origin o1, Origin o2) {
int comp = o1.getBeanType().compareTo(o2.getBeanType());
if (comp == 0) {
comp = o1.getKey().compareTo(o2.getKey());
}
return comp;
}
}
/**
* Comparator sort by bean type then key.
*/
private static class OriginKeySort implements Comparator<Origin> {
@Override
public int compare(Origin o1, Origin o2) {
return o1.getKey().compareTo(o2.getKey());
}
}
}
@@ -1,7 +1,9 @@
package com.avaje.ebeaninternal.server.autotune.service;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.autotune.model.Origin;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetailParser;
import java.io.Serializable;
@@ -11,10 +13,20 @@ import java.io.Serializable;
*/
public class TunedQueryInfo implements Serializable {
private final Origin origin;
private final OrmQueryDetail tunedDetail;
public TunedQueryInfo(OrmQueryDetail tunedDetail) {
this.tunedDetail = tunedDetail;
public TunedQueryInfo(Origin origin) {
this.origin = origin;
this.tunedDetail = new OrmQueryDetailParser(origin.getDetail()).parse();
}
/**
* Return the origin entry (includes call stack and bean type).
*/
public Origin getOrigin() {
return origin;
}
/**
@@ -2316,7 +2316,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
*/
public void appendOrderById(SpiQuery<T> query) {
if (idProperty != null) {
if (idProperty != null && !idProperty.isEmbedded()) {
OrderBy<T> orderBy = query.getOrderBy();
if (orderBy == null || orderBy.isEmpty()) {
query.order().asc(idProperty.getName());
@@ -85,8 +85,8 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
public void initialise() {
// this *MUST* execute after the BeanDescriptor is
// put into the map to stop infinite recursion
if (!isTransient){
targetDescriptor = descriptor.getBeanDescriptor(targetType);
targetDescriptor = descriptor.getBeanDescriptor(targetType);
if (!isTransient){
targetIdBinder = targetDescriptor.getIdBinder();
targetInheritInfo = targetDescriptor.getInheritInfo();
saveRecurseSkippable = targetDescriptor.isSaveRecurseSkippable();
@@ -887,7 +887,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
if (help != null) {
help.jsonWrite(ctx, name, value, include != null);
} else {
if (isTransient) {
if (isTransient && targetDescriptor == null) {
ctx.writeValueUsingObjectMapper(name, value);
} else {
Collection<?> collection = (Collection<?>)value;
@@ -101,14 +101,23 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
// no imported or exported information
} else if (!oneToOneExported) {
importedId = createImportedId(this, targetDescriptor, tableJoin);
if (importedId.isScalar()) {
// limit JoinColumn mapping to the @Id / primary key
TableJoinColumn[] columns = tableJoin.columns();
String foreignJoinColumn = columns[0].getForeignDbColumn();
String foreignIdColumn = targetDescriptor.getIdProperty().getDbColumn();
if (!foreignJoinColumn.equalsIgnoreCase(foreignIdColumn)) {
throw new PersistenceException("Mapping limitation - @OneToOne @JoinColumn needs to map to a primary key as per Issue #529 "
+ " - joining to " + foreignJoinColumn + " and not " + foreignIdColumn);
}
}
} else {
exportedProperties = createExported();
String delStmt = "delete from " + targetDescriptor.getBaseTable() + " where ";
deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false);
deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true);
}
}
}
@@ -14,6 +14,8 @@ import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.query.SplitName;
/**
* A "Query By Example" type of expression.
@@ -207,25 +209,41 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
OrmQueryRequest<?> r = (OrmQueryRequest<?>) request;
BeanDescriptor<?> beanDescriptor = r.getBeanDescriptor();
for (BeanProperty beanProperty : beanDescriptor.propertiesAll()) {
String propName = beanProperty.getName();
Object value = beanProperty.getValue(entity);
addExpressions(list, beanDescriptor, entity, null);
if (beanProperty.isScalar() && value != null) {
if (value instanceof String) {
list.add(new LikeExpression(propName, (String) value, caseInsensitive, likeType));
} else {
// exclude the zero values typically to weed out
// primitive int and long that initialise to 0
if (includeZeros || !isZero(value)) {
list.add(new SimpleExpression(propName, Op.EQ, value));
return list;
}
/**
* Add expressions to the list for all the non-null properties (and do this recursively).
*/
private void addExpressions(ArrayList<SpiExpression> list, BeanDescriptor<?> beanDescriptor, EntityBean bean, String prefix) {
for (BeanProperty beanProperty : beanDescriptor.propertiesAll()) {
if (!beanProperty.isTransient()) {
Object value = beanProperty.getValue(bean);
if (value != null) {
String propName = SplitName.add(prefix, beanProperty.getName());
if (beanProperty.isScalar()) {
if (value instanceof String) {
list.add(new LikeExpression(propName, (String) value, caseInsensitive, likeType));
} else {
// exclude the zero values typically to weed out
// primitive int and long that initialise to 0
if (includeZeros || !isZero(value)) {
list.add(new SimpleExpression(propName, Op.EQ, value));
}
}
} else if ((beanProperty instanceof BeanPropertyAssocOne) && (value instanceof EntityBean)) {
BeanPropertyAssocOne assocOne = (BeanPropertyAssocOne)beanProperty;
BeanDescriptor targetDescriptor = assocOne.getTargetDescriptor();
addExpressions(list, targetDescriptor, (EntityBean)value, propName);
}
}
}
}
return list;
}
/**
@@ -603,7 +603,7 @@ public class SqlTreeBuilder {
// no extra join required for embedded beans
return null;
}
SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, assocProp);
SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, assocProp, elGetValue.containsMany());
joinRegister.put(propertyName, extraJoin);
return extraJoin;
}
@@ -29,11 +29,14 @@ public class SqlTreeNodeExtraJoin implements SqlTreeNode {
private final boolean manyJoin;
private final boolean pathContainsMany;
private List<SqlTreeNodeExtraJoin> children;
public SqlTreeNodeExtraJoin(String prefix, BeanPropertyAssoc<?> assocBeanProperty) {
public SqlTreeNodeExtraJoin(String prefix, BeanPropertyAssoc<?> assocBeanProperty, boolean pathContainsMany) {
this.prefix = prefix;
this.assocBeanProperty = assocBeanProperty;
this.pathContainsMany = pathContainsMany;
this.manyJoin = assocBeanProperty instanceof BeanPropertyAssocMany<?>;
}
@@ -95,13 +98,17 @@ public class SqlTreeNodeExtraJoin implements SqlTreeNode {
}
}
if (pathContainsMany) {
// "promote" to left outer as the path contains a many
joinType = SqlJoinType.OUTER;
}
if (!manyToMany) {
assocBeanProperty.addJoin(joinType, prefix, ctx);
}
if (children != null) {
if (manyJoin) {
if (manyJoin || pathContainsMany) {
// if AUTO then make all descendants use OUTER JOIN
joinType = joinType.autoToOuter();
}
@@ -2,11 +2,13 @@ package com.avaje.ebeaninternal.server.transaction;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.core.BootupClasses;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import javax.sql.DataSource;
import java.sql.Connection;
/**
@@ -29,4 +31,23 @@ public class ExplicitTransactionManager extends TransactionManager {
return new ExplicitJdbcTransaction(prefix + id, explicit, c, this);
}
/**
* Override the initialise of OnQueryOnly with the intention not to use CLOSE with ExplicitJdbcTransaction.
*/
@Override
protected DatabasePlatform.OnQueryOnly initOnQueryOnly(DatabasePlatform.OnQueryOnly dbPlatformOnQueryOnly, DataSource ds) {
// first check for a system property 'override'
String systemPropertyValue = System.getProperty("ebean.transaction.onqueryonly");
if (systemPropertyValue != null) {
return DatabasePlatform.OnQueryOnly.valueOf(systemPropertyValue.trim().toUpperCase());
}
if (DatabasePlatform.OnQueryOnly.CLOSE.equals(dbPlatformOnQueryOnly)) {
// Not using OnQueryOnly.CLOSE with ExplicitJdbcTransaction
return DatabasePlatform.OnQueryOnly.COMMIT;
}
// default to commit if not defined on the platform
return dbPlatformOnQueryOnly == null ? DatabasePlatform.OnQueryOnly.COMMIT : dbPlatformOnQueryOnly;
}
}
@@ -164,7 +164,7 @@ public class TransactionManager {
* just for queries do need to be committed or rollback after the query.
* </p>
*/
private OnQueryOnly initOnQueryOnly(OnQueryOnly dbPlatformOnQueryOnly, DataSource ds) {
protected OnQueryOnly initOnQueryOnly(OnQueryOnly dbPlatformOnQueryOnly, DataSource ds) {
// first check for a system property 'override'
String systemPropertyValue = System.getProperty("ebean.transaction.onqueryonly");
@@ -190,7 +190,7 @@ public class TransactionManager {
/**
* Return true if the isolation level is read committed.
*/
private boolean isReadCommittedIsolation(DataSource ds) {
protected boolean isReadCommittedIsolation(DataSource ds) {
if (DbOffline.isSet()) {
return true;
@@ -0,0 +1,72 @@
package com.avaje.ebeaninternal.server.expression;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.LikeType;
import com.avaje.ebean.Query;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.tests.model.basic.Address;
import com.avaje.tests.model.basic.Customer;
import com.avaje.tests.model.basic.ResetBasicData;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class DefaultExampleExpressionTest extends BaseTestCase {
@Test
public void test() {
SpiEbeanServer server = (SpiEbeanServer)Ebean.getDefaultServer();
BeanDescriptor<Customer> desc = server.getBeanDescriptor(Customer.class);
SpiQuery<Customer> query = (SpiQuery<Customer>)server.find(Customer.class);
Address address = new Address();
address.setCity("billingAddress.city");
Customer customer = new Customer();
customer.setName("name");
customer.setBillingAddress(address);
DefaultExampleExpression expr = new DefaultExampleExpression((EntityBean)customer, false, LikeType.EQUAL_TO);
BeanQueryRequest<?> request = create(query, desc);
HashQueryPlanBuilder builder = new HashQueryPlanBuilder();
expr.queryPlanHash(request, builder);
TDSpiExpressionRequest req = new TDSpiExpressionRequest(desc);
expr.addBindValues(req);
assertThat(req.bindValues).contains("name", "billingAddress.city");
address.setCity("Auckland");
customer.setName("Rob");
ResetBasicData.reset();
Query<Customer> query1 = server.find(Customer.class)
.where().exampleLike(customer)
.query();
query1.findList();
assertThat(query1.getGeneratedSql()).contains("(t0.name like ? and t1.city like ? )");
}
private <T> OrmQueryRequest<T> create(SpiQuery<T> query, BeanDescriptor<T> desc) {
return new OrmQueryRequest<T>(null, null, query, desc, null);
}
}
@@ -0,0 +1,78 @@
package com.avaje.ebeaninternal.server.expression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
import com.avaje.ebeaninternal.server.core.JsonExpressionHandler;
import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import java.util.ArrayList;
import java.util.List;
/**
* Test double for testing with SpiExpressionRequest.
*/
public class TDSpiExpressionRequest implements SpiExpressionRequest {
List<Object> bindValues = new ArrayList<Object>();
final BeanDescriptor<?> descriptor;
public TDSpiExpressionRequest(BeanDescriptor<?> descriptor) {
this.descriptor = descriptor;
}
@Override
public JsonExpressionHandler getJsonHandler() {
return null;
}
@Override
public String parseDeploy(String logicalProp) {
return null;
}
@Override
public BeanDescriptor<?> getBeanDescriptor() {
return descriptor;
}
@Override
public SpiOrmQueryRequest<?> getQueryRequest() {
return null;
}
@Override
public SpiExpressionRequest append(String sql) {
return null;
}
@Override
public void addBindEncryptKey(Object encryptKey) {
}
@Override
public void addBindValue(Object bindValue) {
bindValues.add(bindValue);
}
@Override
public String getSql() {
return null;
}
@Override
public ArrayList<Object> getBindValues() {
return null;
}
@Override
public int nextParameter() {
return 0;
}
@Override
public void appendLike() {
}
}
@@ -3,7 +3,9 @@ package com.avaje.tests.autofetch;
import java.util.List;
import java.util.Set;
import com.avaje.ebeaninternal.server.autotune.model.Origin;
import org.avaje.ebeantest.LoggedSqlCollector;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
import org.junit.Test;
@@ -33,34 +35,6 @@ public class TunedQueryInfoTest extends BaseTestCase {
serverCacheManager.setCaching(Order.class, false);
}
@Test
public void withSelectNull() {
init();
OrmQueryDetail tunedDetail = new OrmQueryDetail();
tunedDetail.select(null);
TunedQueryInfo tunedInfo = new TunedQueryInfo(tunedDetail);
Query<Order> query = server.find(Order.class).setId(1);
tunedInfo.tuneQuery((SpiQuery<?>) query);
Order order = query.findUnique();
EntityBean eb = (EntityBean)order;
EntityBeanIntercept ebi = eb._ebean_getIntercept();
Assert.assertTrue(ebi.isFullyLoadedBean());
Set<String> loadedPropertyNames = ebi.getLoadedPropertyNames();
Assert.assertNull(loadedPropertyNames);
// invoke lazy loading
order.getCustomer();
}
@Test
public void withSelectEmpty() {
@@ -68,9 +42,9 @@ public class TunedQueryInfoTest extends BaseTestCase {
OrmQueryDetail tunedDetail = new OrmQueryDetail();
tunedDetail.select("");
TunedQueryInfo tunedInfo = new TunedQueryInfo(tunedDetail);
TunedQueryInfo tunedInfo = createTunedQueryInfo(tunedDetail);
Query<Order> query = server.find(Order.class).setId(1);
tunedInfo.tuneQuery((SpiQuery<?>) query);
@@ -95,9 +69,9 @@ public class TunedQueryInfoTest extends BaseTestCase {
OrmQueryDetail tunedDetail = new OrmQueryDetail();
tunedDetail.select("somethingThatDoesNotExist");
TunedQueryInfo tunedInfo = new TunedQueryInfo(tunedDetail);
TunedQueryInfo tunedInfo = createTunedQueryInfo(tunedDetail);
Query<Order> query = server.find(Order.class).setId(1);
tunedInfo.tuneQuery((SpiQuery<?>) query);
@@ -123,7 +97,14 @@ public class TunedQueryInfoTest extends BaseTestCase {
Assert.assertTrue(loggedSql.get(0).contains("select t0.id c0, t0.id c1 from o_order t0 where t0.id = ?"));
Assert.assertTrue(loggedSql.get(1).contains("select t0.id c0, t0.status c1,"));
}
@NotNull
private TunedQueryInfo createTunedQueryInfo(OrmQueryDetail tunedDetail) {
Origin origin = new Origin();
origin.setDetail(tunedDetail.toString());
return new TunedQueryInfo(origin);
}
@Test
public void withSelectSomeIncludeLazyLoaded() {
@@ -132,7 +113,7 @@ public class TunedQueryInfoTest extends BaseTestCase {
OrmQueryDetail tunedDetail = new OrmQueryDetail();
tunedDetail.select("status, customer");
TunedQueryInfo tunedInfo = new TunedQueryInfo(tunedDetail);
TunedQueryInfo tunedInfo = createTunedQueryInfo(tunedDetail);
Query<Order> query = server.find(Order.class).setId(1);
@@ -167,7 +148,7 @@ public class TunedQueryInfoTest extends BaseTestCase {
OrmQueryDetail tunedDetail = new OrmQueryDetail();
tunedDetail.select("status");
TunedQueryInfo tunedInfo = new TunedQueryInfo(tunedDetail);
TunedQueryInfo tunedInfo = createTunedQueryInfo(tunedDetail);
Query<Order> query = server.find(Order.class).setId(1);
@@ -33,6 +33,7 @@ public class TestBatchLazy extends BaseTestCase {
}
Ebean.getDefaultServer().getAutoTune().collectProfiling();
Ebean.getDefaultServer().getAutoTune().reportProfiling();
}
@@ -2,6 +2,7 @@ package com.avaje.tests.basic;
import java.util.List;
import com.avaje.ebean.Query;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -42,4 +43,16 @@ public class TestM2MCascadeOne extends BaseTestCase {
Ebean.save(u1);
}
@Test
public void testRawPredicate_with_ManyToManyPath() {
Query<MUser> query = Ebean.find(MUser.class)
.select("userid")
.where().raw("roles.roleid in (?)", 24)
.query();
query.findList();
}
}
@@ -1,5 +1,7 @@
package com.avaje.tests.cache;
import com.avaje.ebeaninternal.server.autotune.model.Origin;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
import org.junit.Test;
@@ -14,6 +16,13 @@ import com.avaje.tests.model.basic.FeatureDescription;
public class TestL2CacheWithSharedBean extends BaseTestCase {
@NotNull
private TunedQueryInfo createTunedQueryInfo(OrmQueryDetail tunedDetail) {
Origin origin = new Origin();
origin.setDetail(tunedDetail.toString());
return new TunedQueryInfo(origin);
}
@Test
public void test() {
@@ -28,7 +37,7 @@ public class TestL2CacheWithSharedBean extends BaseTestCase {
OrmQueryDetail tunedDetail = new OrmQueryDetail();
tunedDetail.select("name");
TunedQueryInfo tunedInfo = new TunedQueryInfo(tunedDetail);
TunedQueryInfo tunedInfo = createTunedQueryInfo(tunedDetail);
Query<FeatureDescription> query = Ebean.find(FeatureDescription.class).setId(f1.getId());
@@ -3,6 +3,7 @@ package com.avaje.tests.compositekeys;
import java.util.ArrayList;
import java.util.List;
import com.avaje.ebean.PagedList;
import org.junit.Assert;
import org.junit.Test;
@@ -51,6 +52,8 @@ public class TestCKeyLazyLoad extends BaseTestCase {
Ebean.save(p2);
exerciseMaxRowsQuery_with_embeddedId();
CKeyParentId searchId = new CKeyParentId(1, "one");
CKeyParent found = Ebean.find(CKeyParent.class).where().idEq(searchId).findUnique();
@@ -79,4 +82,15 @@ public class TestCKeyLazyLoad extends BaseTestCase {
Assert.assertTrue(idInTestList.size() == 2);
}
/**
* Exercise paging/maxRows type query with EmbeddedId.
*/
private void exerciseMaxRowsQuery_with_embeddedId() {
PagedList<CKeyParent> siteUserPage = Ebean.find(CKeyParent.class).where()
.orderBy("name asc")
.findPagedList(0, 10);
siteUserPage.getList();
}
}
@@ -2,7 +2,8 @@ package com.avaje.tests.inheritance;
import java.util.List;
import org.junit.Assert;
import com.avaje.tests.model.basic.CarAccessory;
import com.avaje.tests.model.basic.CarFuse;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
@@ -13,6 +14,11 @@ import com.avaje.tests.model.basic.Truck;
import com.avaje.tests.model.basic.Vehicle;
import com.avaje.tests.model.basic.VehicleDriver;
import static org.assertj.core.api.StrictAssertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class TestInheritInsert extends BaseTestCase {
@Test
@@ -25,11 +31,11 @@ public class TestInheritInsert extends BaseTestCase {
Vehicle v = Ebean.find(Vehicle.class, t.getId());
if (v instanceof Truck) {
Truck t0 = (Truck) v;
Assert.assertEquals(Double.valueOf(10d), t0.getCapacity());
Assert.assertEquals(Double.valueOf(10d), ((Truck) v).getCapacity());
Assert.assertNotNull(t0.getId());
assertEquals(Double.valueOf(10d), t0.getCapacity());
assertEquals(Double.valueOf(10d), ((Truck) v).getCapacity());
assertNotNull(t0.getId());
} else {
Assert.assertTrue("v not a Truck?", false);
assertTrue("v not a Truck?", false);
}
VehicleDriver driver = new VehicleDriver();
@@ -42,17 +48,17 @@ public class TestInheritInsert extends BaseTestCase {
v = d1.getVehicle();
if (v instanceof Truck) {
Double capacity = ((Truck) v).getCapacity();
Assert.assertEquals(Double.valueOf(10d), capacity);
Assert.assertNotNull(v.getId());
assertEquals(Double.valueOf(10d), capacity);
assertNotNull(v.getId());
} else {
Assert.assertTrue("v not a Truck?", false);
assertTrue("v not a Truck?", false);
}
List<VehicleDriver> list = Ebean.find(VehicleDriver.class).findList();
for (VehicleDriver vehicleDriver : list) {
if (vehicleDriver.getVehicle() instanceof Truck) {
Double capacity = ((Truck) vehicleDriver.getVehicle()).getCapacity();
Assert.assertEquals(Double.valueOf(10d), capacity);
assertEquals(Double.valueOf(10d), capacity);
}
}
}
@@ -73,12 +79,12 @@ public class TestInheritInsert extends BaseTestCase {
query.where().eq("vehicle.licenseNumber", "MARIOS_CAR_LICENSE");
List<VehicleDriver> drivers = query.findList();
Assert.assertNotNull(drivers);
Assert.assertEquals(1, drivers.size());
Assert.assertNotNull(drivers.get(0));
assertNotNull(drivers);
assertEquals(1, drivers.size());
assertNotNull(drivers.get(0));
Assert.assertEquals("Mario", drivers.get(0).getName());
Assert.assertEquals("MARIOS_CAR_LICENSE", drivers.get(0).getVehicle().getLicenseNumber());
assertEquals("Mario", drivers.get(0).getName());
assertEquals("MARIOS_CAR_LICENSE", drivers.get(0).getVehicle().getLicenseNumber());
Vehicle car2 = Ebean.find(Vehicle.class, car.getId());
@@ -86,4 +92,34 @@ public class TestInheritInsert extends BaseTestCase {
Ebean.save(car);
}
@Test
public void test_AtOrderBy_on_ChildOfChild() {
Car car = new Car();
car.setLicenseNumber("ABC");
Ebean.save(car);
CarFuse fuse = new CarFuse();
fuse.setLocationCode("xdfg");
Ebean.save(fuse);
CarAccessory accessory = new CarAccessory(car, fuse);
Ebean.save(accessory);
Query<Car> query = Ebean.find(Car.class)
.fetch("accessories")
.where()
.eq("id", car.getId())
.query();
Car result = query.findUnique();
assertThat(query.getGeneratedSql()).contains("order by t0.id, t2.location_code");
assertThat(query.getGeneratedSql()).contains("left outer join car_fuse t2 on t2.id = t1.fuse_id");
assertNotNull(result);
}
}
@@ -2,12 +2,15 @@ package com.avaje.tests.json.include;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.config.JsonConfig;
import com.avaje.ebean.text.PathProperties;
import com.avaje.ebean.text.json.JsonWriteOptions;
import com.avaje.tests.json.transientproperties.EJsonTransientEntityList;
import com.avaje.tests.json.transientproperties.EJsonTransientList;
import org.junit.Test;
import java.util.ArrayList;
import static org.assertj.core.api.StrictAssertions.assertThat;
import static org.junit.Assert.assertEquals;
public class TestJsonExcludeTransientEmptyList {
@@ -45,4 +48,18 @@ public class TestJsonExcludeTransientEmptyList {
assertEquals(expectedJson, asJson);
}
@Test
public void testToJson_with_transientExcludeFromPathProperties() throws Exception {
EJsonTransientEntityList bean = new EJsonTransientEntityList();
bean.setId(99L);
bean.setName("John");
PathProperties pathProps = PathProperties.parse("id,name");
String asJson = Ebean.json().toJson(bean, pathProps);
assertThat(asJson).isEqualTo("{\"id\":99,\"name\":\"John\"}");
}
}
@@ -0,0 +1,57 @@
package com.avaje.tests.json.transientproperties;
import com.avaje.ebean.annotation.Sql;
import com.avaje.tests.model.basic.Order;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Transient;
import java.util.List;
@Sql
@Entity
public class EJsonTransientEntityList {
@Id
private Long id;
private String name;
@Transient
private Boolean basic;
@Transient
private List<Order> orders;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getBasic() {
return basic;
}
public void setBasic(Boolean basic) {
this.basic = basic;
}
public List<Order> getOrders() {
return orders;
}
public void setOrders(List<Order> orders) {
this.orders = orders;
}
}
@@ -0,0 +1,46 @@
package com.avaje.tests.json.transientproperties;
import com.avaje.ebean.annotation.Sql;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Transient;
import java.util.List;
@Sql
@Entity
public class ModelA {
@Id
int id;
String a;
// transient mapping to an entity bean
@Transient
List<ModelB> list;
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public List<ModelB> getList() {
return list;
}
public void setList(List<ModelB> list) {
this.list = list;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
@@ -0,0 +1,31 @@
package com.avaje.tests.json.transientproperties;
import com.avaje.ebean.annotation.Sql;
import javax.persistence.Entity;
@Sql
@Entity
public class ModelB {
Integer oneField;
Integer twoField;
public Integer getOneField() {
return oneField;
}
public void setOneField(Integer oneField) {
this.oneField = oneField;
}
public Integer getTwoField() {
return twoField;
}
public void setTwoField(Integer twoField) {
this.twoField = twoField;
}
}
@@ -0,0 +1,34 @@
package com.avaje.tests.json.transientproperties;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.text.PathProperties;
import org.junit.Test;
import java.util.ArrayList;
import static org.assertj.core.api.StrictAssertions.assertThat;
public class TestModelAJson {
@Test
public void test() {
ModelA a = new ModelA();
a.setId(1);
a.setA("a");
ModelB b = new ModelB();
b.setOneField(1);
b.setTwoField(1);
a.setList(new ArrayList<ModelB>());
a.getList().add(b);
PathProperties pathProperties = PathProperties.parse("(a,list(oneField))");
String json = Ebean.json().toJson(a, pathProperties);
assertThat(json).isEqualTo("{\"a\":\"a\",\"list\":[{\"oneField\":1}]}");
}
}
@@ -1,51 +1,51 @@
package com.avaje.tests.model.basic;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import java.util.HashSet;
import java.util.Set;
@Entity
@Inheritance
@DiscriminatorValue("C")
public class Car extends Vehicle {
private static final long serialVersionUID = 4716705779684333446L;
private static final long serialVersionUID = 4716705779684333446L;
private String driver;
private String driver;
@ManyToOne
TruckRef carRef;
@OneToMany(mappedBy="car")
private Set<CarAccessory> accessories = new HashSet<CarAccessory>();
public String getDriver() {
return driver;
}
@ManyToOne
TruckRef carRef;
public void setDriver(String driver) {
this.driver = driver;
}
@OneToMany(mappedBy = "car")
@OrderBy("fuse.locationCode")
private Set<CarAccessory> accessories = new HashSet<CarAccessory>();
public TruckRef getCarRef() {
return carRef;
}
public String getDriver() {
return driver;
}
public void setCarRef(TruckRef carRef) {
this.carRef = carRef;
}
public void setDriver(String driver) {
this.driver = driver;
}
public Set<CarAccessory> getAccessories() {
return accessories;
}
public TruckRef getCarRef() {
return carRef;
}
public void setAccessories(Set<CarAccessory> accessories) {
this.accessories = accessories;
}
public void setCarRef(TruckRef carRef) {
this.carRef = carRef;
}
public Set<CarAccessory> getAccessories() {
return accessories;
}
public void setAccessories(Set<CarAccessory> accessories) {
this.accessories = accessories;
}
}
@@ -4,28 +4,44 @@ import javax.persistence.Entity;
import javax.persistence.ManyToOne;
@Entity
public class CarAccessory extends BasicDomain{
private static final long serialVersionUID = 1L;
public class CarAccessory extends BasicDomain {
private String name;
@ManyToOne
private Car car;
private static final long serialVersionUID = 1L;
public String getName() {
return name;
}
private String name;
public void setName(String name) {
this.name = name;
}
@ManyToOne(optional = false)
private CarFuse fuse;
public Car getCar() {
return car;
}
@ManyToOne
private Car car;
public void setCar(Car car) {
this.car = car;
}
public CarAccessory(Car car, CarFuse fuse) {
this.car = car;
this.fuse = fuse;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public CarFuse getFuse() {
return fuse;
}
public void setFuse(CarFuse fuse) {
this.fuse = fuse;
}
}
@@ -0,0 +1,29 @@
package com.avaje.tests.model.basic;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class CarFuse {
@Id
Long id;
String locationCode;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getLocationCode() {
return locationCode;
}
public void setLocationCode(String locationCode) {
this.locationCode = locationCode;
}
}
@@ -1,95 +1,88 @@
package com.avaje.tests.model.basic;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import java.util.List;
@Entity
@Table(name = "mrole")
public class MRole {
@Id
Integer roleid;
String roleName;
@Id
Integer roleid;
@ManyToMany(cascade=CascadeType.ALL
// @JoinTable(name="myint_table"//,
// joinColumns={
// @JoinColumn(name="mroleid", referencedColumnName="roleid")
// }//,
// inverseJoinColumns={
// @JoinColumn(name = "muserid", referencedColumnName="userid")
// }
)
List<MUser> users;
public MRole() {
String roleName;
@ManyToMany(cascade = CascadeType.ALL)
List<MUser> users;
public MRole() {
}
public MRole(String roleName) {
this.roleName = roleName;
}
public Integer getRoleid() {
return roleid;
}
public void setRoleid(Integer roleid) {
this.roleid = roleid;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public List<MUser> getUsers() {
return users;
}
public void setUsers(List<MUser> users) {
this.users = users;
}
@Override
public String toString() {
return "MRole [roleName=" + roleName + "]";
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
public MRole(String roleName) {
this.roleName = roleName;
// Make sure other is not null and has the same class as this
if (other != null && getClass().equals(other.getClass())) {
final MRole rhs = (MRole) other;
if (roleid.equals(rhs.roleid)) {
if (roleid == 0) {
return false;
} else {
return true;
}
}
}
public Integer getRoleid() {
return roleid;
}
return false;
}
public void setRoleid(Integer roleid) {
this.roleid = roleid;
}
@Override
public int hashCode() {
if (roleid != null && roleid != 0) {
int rid = roleid;
return (int) (rid ^ (rid >>> 32));
}
return super.hashCode();
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public List<MUser> getUsers() {
return users;
}
public void setUsers(List<MUser> users) {
this.users = users;
}
@Override
public String toString() {
return "MRole [roleName=" + roleName + "]";
}
@Override
public boolean equals(Object other) {
if (this == other){
return true;
}
// Make sure other is not null and has the same class as this
if (other != null && getClass().equals(other.getClass())){
final MRole rhs = (MRole)other;
if ( roleid.equals(rhs.roleid)){
if (roleid == 0){
return false;
}else{
return true;
}
}
}
return false;
}
@Override
public int hashCode() {
if (roleid != null && roleid != 0){
int rid = roleid;
return (int)( rid ^ (rid >>> 32) );
}
return super.hashCode();
}
}
@@ -6,6 +6,7 @@ import java.util.List;
import javax.persistence.*;
@Entity
@Table(name="muser")
public class MUser {
@Id
@@ -2,8 +2,10 @@ package com.avaje.tests.model.basic;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="muser_type")
public class MUserType {
@Id
@@ -21,7 +21,7 @@ public class OrderDetail implements Serializable {
@Id
Integer id;
@ManyToOne
@ManyToOne(optional = false)
Order order;
Integer orderQty;
@@ -0,0 +1,21 @@
package com.avaje.tests.model.onetoone;
import com.avaje.tests.model.basic.BasicDomain;
import javax.persistence.Column;
import javax.persistence.Entity;
@Entity
public class OCompany extends BasicDomain {
@Column(length = 50, unique = true)
public String corpId;
public String getCorpId() {
return corpId;
}
public void setCorpId(String corpId) {
this.corpId = corpId;
}
}
@@ -0,0 +1,24 @@
package com.avaje.tests.model.onetoone;
import com.avaje.tests.model.basic.BasicDomain;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
@Entity
public class ORoadShowMsg extends BasicDomain {
@OneToOne(cascade = CascadeType.ALL, optional = false)
@JoinColumn()//(name = "corp_id", nullable = false, referencedColumnName = "corp_id")
public OCompany company;
public OCompany getCompany() {
return company;
}
public void setCompany(OCompany company) {
this.company = company;
}
}
@@ -0,0 +1,20 @@
package com.avaje.tests.model.onetoone;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import org.junit.Test;
public class TestOneToOneJoinColumn extends BaseTestCase {
@Test
public void test() {
ORoadShowMsg msg = new ORoadShowMsg();
OCompany company = new OCompany();
company.setCorpId("corp_id_1000000");
msg.setCompany(company);
Ebean.save(msg);
Ebean.find(ORoadShowMsg.class, msg.getId());
}
}
@@ -7,23 +7,52 @@ import com.avaje.tests.model.basic.Customer;
import com.avaje.tests.model.basic.Order;
import com.avaje.tests.model.basic.OrderDetail;
import com.avaje.tests.model.basic.ResetBasicData;
import org.junit.Ignore;
import org.junit.Test;
import java.util.List;
import java.util.Random;
public class TestAutoTuneProfiling extends BaseTestCase {
@Ignore
@Test
public void test() {
public void test() throws InterruptedException {
ResetBasicData.reset();
System.out.println("Start .......");
for (int i = 0; i < 1; i++) {
execute();
}
collectUsage();
System.out.println("Sleeping ...");
sortOfBusy();
System.out.println("Run after collection");
for (int i = 0; i < 10; i++) {
execute();
}
collectUsage();
System.out.println("Sleeping ...");
sortOfBusy();
System.out.println("Run after collection");
for (int i = 0; i < 1; i++) {
execute();
}
}
private void sortOfBusy() {
for (int i = 0; i < 90000000; i++) {
new Random().nextLong();
}
}
@@ -1,15 +1,17 @@
package com.avaje.tests.rawsql;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.Query;
import com.avaje.tests.model.basic.OrderAggregate;
import com.avaje.tests.model.basic.OrderDetail;
import com.avaje.tests.model.basic.ResetBasicData;
import org.junit.Test;
import java.util.List;
import static org.assertj.core.api.StrictAssertions.assertThat;
import static org.junit.Assert.assertNotNull;
public class TestOrderReportTotal extends BaseTestCase {
@@ -21,14 +23,28 @@ public class TestOrderReportTotal extends BaseTestCase {
Query<OrderAggregate> query = Ebean.createQuery(OrderAggregate.class);
List<OrderAggregate> list = query.findList();
Assert.assertNotNull(list);
assertNotNull(list);
Query<OrderAggregate> q2 = Ebean.createQuery(OrderAggregate.class);
q2.where().gt("id", 1);
q2.having().gt("totalItems", 1);
List<OrderAggregate> l2 = q2.findList();
Assert.assertNotNull(l2);
assertNotNull(l2);
}
@Test
public void testOrderDetailCount() {
ResetBasicData.reset();
int detailsCount = Ebean.find(OrderDetail.class)
.where()
.gt("order.id", 2)
.istartsWith("order.customer.name","rob")
.findRowCount();
assertThat(detailsCount).isGreaterThan(0);
}
}
@@ -3,6 +3,8 @@ package com.avaje.tests.text.json;
import java.io.IOException;
import java.util.List;
import com.avaje.tests.model.basic.CarAccessory;
import com.avaje.tests.model.basic.CarFuse;
import org.junit.Assert;
import org.junit.Test;
@@ -38,6 +40,8 @@ public class TestTextJsonInheritance extends BaseTestCase {
private void setupData() {
Ebean.createUpdate(CarAccessory.class, "delete from CarAccessory").execute();
Ebean.createUpdate(CarFuse.class, "delete from CarFuse").execute();
Ebean.createUpdate(Trip.class, "delete from trip").execute();
Ebean.createUpdate(VehicleDriver.class, "delete from vehicleDriver").execute();
Ebean.createUpdate(Vehicle.class, "delete from vehicle").execute();
+3 -3
View File
@@ -11,9 +11,9 @@
ebean.encryptKeyManager=com.avaje.tests.basic.encrypt.BasicEncyptKeyManager
ebean.autotune.querytuning=true
ebean.autotune.profiling=true
#ebean.autotune.querytuning=true
#ebean.autotune.profiling=true
#ebean.autotune.profilingUpdateFrequency=5
ebean.ddl.generate=true
ebean.ddl.run=true
+1 -1
View File
@@ -86,6 +86,6 @@
<logger name="org.avaje.ebean.cache.NATKEY" level="TRACE"/>
<logger name="com.avaje.tests" level="DEBUG"/>
<logger name="com.avaje.ebean.config.dbplatform.H2HistoryTrigger" level="DEBUG"/>
<!--<logger name="com.avaje.ebean.config.dbplatform.H2HistoryTrigger" level="DEBUG"/>-->
</configuration>
+1 -1
View File
@@ -1 +1 @@
datasource.h2.username=sa
datasource.h2.username=sa