Compare commits

..
Author SHA1 Message Date
Robin Bygrave 9a17a7752c [maven-release-plugin] prepare release avaje-ebeanorm-7.16.2 2016-06-28 07:13:06 +12:00
Robin Bygrave 92474c2b24 #754 - Logging output for bind log of UpdateQuery does not include bind values for set clause ... 2016-06-28 07:10:59 +12:00
Robin Bygrave 9a00b9df0e #753 - @History "as of" query errors when executed a second time - invalid bind parameter 2016-06-28 07:04:39 +12:00
Robin Bygrave 697f67c14a #753 - @History "as of" query errors when executed a second time - invalid bind parameter 2016-06-28 06:46:16 +12:00
Robin Bygrave 1f9ccf1c8b [maven-release-plugin] prepare for next development iteration 2016-06-23 21:39:03 +12:00
Robin Bygrave a1f10b8704 [maven-release-plugin] prepare release avaje-ebeanorm-7.16.1 2016-06-23 21:38:42 +12:00
Robin Bygrave 3c21950b91 Bump pom to 7.16.1-SNAPSHOT 2016-06-23 21:36:57 +12:00
Robin Bygrave 3b45308ac7 Merge branch 'DevFactory-release/collection-isempty-1' 2016-06-23 13:40:23 +12:00
Robin Bygrave 00e69e1b5d No effective change - remove extra whitespace 2016-06-23 13:39:34 +12:00
Robin Bygrave 11b3f7a813 Merge branch 'release/collection-isempty-1' of https://github.com/DevFactory/avaje-ebeanorm into DevFactory-release/collection-isempty-1
Conflicts:
	src/main/java/com/avaje/ebeaninternal/server/query/DefaultDbSqlContext.java
2016-06-23 13:24:58 +12:00
Robin Bygrave 2185c8a886 #752 - Refactor: SQL generation for @History support with Postgres, MySql (views/triggers based) such that it more closely aligns with SQL 2011 based SQL 2016-06-23 11:33:01 +12:00
Robin Bygrave f8bda05b37 #751 - @History with @ManyToMany ... intersection table not getting associated history support 2016-06-23 11:05:16 +12:00
Artyom Melnikov 0a3b819991 Fixing squid:S1155 - Collection.isEmpty() should be used to test for emptiness 2016-06-22 13:43:15 +03:00
Rob BygraveandGitHub 5c8b1eb3bb Merge pull request #748 from yuqiyi/master
fix DeployBeanDescriptor.java of the method setView
2016-06-22 22:21:52 +12:00
yuqiyi 824f6dcc7e fix DeployBeanDescriptor.java of the method setView 2016-06-22 18:11:37 +08:00
Robin Bygrave 06e23b85ad #747 - @History findVersionsBetween query with Oracle has incorrect ordering 2016-06-22 20:16:35 +12:00
Robin Bygrave 09219c759b [maven-release-plugin] prepare for next development iteration 2016-06-20 22:57:36 +12:00
Robin Bygrave b861038568 [maven-release-plugin] prepare release avaje-ebeanorm-7.15.1 2016-06-20 22:57:16 +12:00
Robin Bygrave bfd8445bcd Bump pom to 7.15.1-SNAPSHOT 2016-06-20 22:56:04 +12:00
Robin Bygrave c30e3bf78c #746 - SqlQuery with named parameter and limit/offset gives SQL exception 2016-06-20 22:54:08 +12:00
Robin Bygrave c793d6652f #745 - ElasticSearch - internally translate where().id.eq(42) ... to where().setId(42) 2016-06-20 22:29:38 +12:00
Robin Bygrave d12e55c20b #744 - ElasticSearch - isEmpty() expression needs to have outer not:{} negation to work as per .. 2016-06-20 21:32:05 +12:00
Robin Bygrave 3477f590dd #743 - Fix issue with ElasticSearch nested path query with multiple nested path expressions 2016-06-20 20:57:45 +12:00
Robin Bygrave 6bbf93aba8 No effective change - extract private constant in DefaultExpressionList 2016-06-20 13:14:28 +12:00
Robin Bygrave 42d02cc521 [maven-release-plugin] prepare for next development iteration 2016-06-17 22:27:25 +12:00
108 changed files with 1312 additions and 236 deletions
+2 -2
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>7.14.1</version>
<version>7.16.2</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -37,7 +37,7 @@
<scm>
<developerConnection>scm:git:https://github.com/ebean-orm/avaje-ebeanorm.git</developerConnection>
<tag>avaje-ebeanorm-7.14.1</tag>
<tag>avaje-ebeanorm-7.16.2</tag>
</scm>
<dependencies>
@@ -49,7 +49,7 @@ final class DRawSqlColumnsParser {
if (split.length > 1) {
ArrayList<String> tmp = new ArrayList<String>(split.length);
for (int i = 0; i < split.length; i++) {
if (split[i].trim().length() > 0) {
if (!split[i].trim().isEmpty()) {
tmp.add(split[i].trim());
}
}
+2 -2
View File
@@ -161,7 +161,7 @@ public final class Ebean {
// look to see if there is a default server defined
String defaultName = PrimaryServer.getDefaultServerName();
logger.debug("defaultName:" + defaultName);
if (defaultName != null && defaultName.trim().length() > 0) {
if (defaultName != null && !defaultName.trim().isEmpty()) {
defaultServer = getWithCreate(defaultName.trim());
}
}
@@ -182,7 +182,7 @@ public final class Ebean {
}
private EbeanServer get(String name) {
if (name == null || name.length() == 0) {
if (name == null || name.isEmpty()) {
return defaultServer;
}
// non-synchronized read
+1 -1
View File
@@ -400,6 +400,6 @@ public final class OrderBy<T> implements Serializable {
}
private boolean isEmptyString(String s) {
return s == null || s.length() == 0;
return s == null || s.isEmpty();
}
}
@@ -78,6 +78,6 @@ class PrimaryServer {
* Return true if the string is null or empty.
*/
private static boolean isEmpty(String value) {
return value == null || value.trim().length() == 0;
return value == null || value.trim().isEmpty();
}
}
@@ -274,7 +274,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
* Checks string is null or empty .
*/
protected boolean isEmpty(String s) {
return s == null || s.trim().length() == 0;
return s == null || s.trim().isEmpty();
}
/**
@@ -2460,7 +2460,7 @@ public class ServerConfig {
String[] split = classNames.split("[ ,;]");
for (int i = 0; i < split.length; i++) {
String cn = split[i].trim();
if (cn.length() > 0 && !"class".equalsIgnoreCase(cn)) {
if (!cn.isEmpty() && !"class".equalsIgnoreCase(cn)) {
try {
classes.add(Class.forName(cn));
} catch (ClassNotFoundException e) {
@@ -146,6 +146,6 @@ public final class TableName {
* @return true, if is valid
*/
public boolean isValid() {
return name != null && name.length() > 0;
return name != null && !name.isEmpty();
}
}
@@ -492,7 +492,7 @@ public class DatabasePlatform {
*/
public String convertQuotedIdentifiers(String dbName) {
// Ignore null values e.g. schema name or catalog
if (dbName != null && dbName.length() > 0) {
if (dbName != null && !dbName.isEmpty()) {
if (dbName.charAt(0) == BACK_TICK) {
if (dbName.charAt(dbName.length() - 1) == BACK_TICK) {
@@ -6,11 +6,13 @@ package com.avaje.ebean.config.dbplatform;
public interface DbHistorySupport {
/**
* Return true if the 'As of' predicate is part of the from clause
* (more standard sql2011). So true for Oracle total recall and false
* for Postgres and MySql (where we use views and history tables).
* Return true if the implementation is SQL2011 standards based.
* <p>
* Non standards based means we need to add additional predicates into the
* JOIN ON clause and add an additional predicate for the base table.
* </p>
*/
boolean isBindWithFromClause();
boolean isStandardsBased();
/**
* Return the number of columns bound in a 'As Of' predicate.
@@ -5,11 +5,8 @@ package com.avaje.ebean.config.dbplatform;
*/
public abstract class DbStandardHistorySupport implements DbHistorySupport {
/**
* Return true as with sql2011 the 'as of timestamp' clause included in from or join clause.
*/
@Override
public boolean isBindWithFromClause() {
public boolean isStandardsBased() {
return true;
}
@@ -9,11 +9,8 @@ package com.avaje.ebean.config.dbplatform;
*/
public abstract class DbViewHistorySupport implements DbHistorySupport {
/**
* Return false for view based implementations where we append extra 'as of' predicates to the end.
*/
@Override
public boolean isBindWithFromClause() {
public boolean isStandardsBased() {
return false;
}
@@ -85,6 +85,11 @@ public class ModelBuildIntersectionTable {
String tableName = intersectionTableJoin.getTable();
MTable table = new MTable(tableName);
if (!manyProp.isExcludedFromHistory()) {
if (localDesc.isHistorySupport()) {
table.setWithHistory(true);
}
}
table.setPkName(ctx.primaryKeyName(tableName));
TableJoinColumn[] columns = intersectionTableJoin.columns();
@@ -109,7 +109,7 @@ public class PathProperties implements FetchPath {
String path = entry.getKey();
String props = entry.getValue().getPropertiesAsString();
if (path == null || path.length() == 0) {
if (path == null || path.isEmpty()) {
query.select(props);
} else {
query.fetch(path, props);
@@ -108,7 +108,7 @@ class PathPropertiesParser {
private void addCurrentProperty() {
String w = currentWord();
if (w.length() > 0) {
if (!w.isEmpty()) {
currentPathProps.addProperty(w);
}
}
@@ -23,7 +23,7 @@ public final class TimeStringParser implements StringParser {
*/
@SuppressWarnings("deprecation")
public Object parse(String value) {
if (value == null || value.trim().length() == 0) {
if (value == null || value.trim().isEmpty()) {
return null;
}
@@ -123,7 +123,7 @@ public class StringHelper {
String listDelimiter, String nameValueSeparator) {
HashMap<String, String> params = new HashMap<String, String>();
if ((allNameValuePairs == null) || (allNameValuePairs.length() == 0)) {
if ((allNameValuePairs == null) || (allNameValuePairs.isEmpty())) {
return params;
}
// trim off any leading listDelimiter...
@@ -155,7 +155,7 @@ public class StringHelper {
* Return true if the value is null or an empty string.
*/
public static boolean isNull(String value) {
return value == null || value.trim().length() == 0;
return value == null || value.trim().isEmpty();
}
/**
@@ -189,7 +189,7 @@ public class StringHelper {
// there is a key without a value?
String key = allNameValuePairs.substring(pos, delimPos);
key = key.trim();
if (key.length() > 0) {
if (!key.isEmpty()) {
map.put(key, null);
}
return getKeyValue(map, delimPos + 1, allNameValuePairs, listDelimiter,
@@ -251,7 +251,7 @@ public class StringHelper {
if (endPos == -1) {
if (startPos <= str.length()) {
String lastValue = str.substring(startPos, str.length());
if (keepEmpties || lastValue.length() != 0) {
if (keepEmpties || !lastValue.isEmpty()) {
list.add(lastValue);
}
}
@@ -260,7 +260,7 @@ public class StringHelper {
} else {
// get the delimited value... add it..
String value = str.substring(startPos, endPos);
if (keepEmpties || value.length() != 0) {
if (keepEmpties || !value.isEmpty()) {
list.add(value);
}
// recursively search as we are not at the end yet...
@@ -18,8 +18,13 @@ public interface SpiExpression extends Expression {
*/
void writeDocQuery(DocQueryContext context) throws IOException;
/**
* Process "Many" properties populating ManyWhereJoins.
/**
* Return the nested path for this expression.
*/
String nestedPath(BeanDescriptor<?> desc);
/**
* Process "Many" properties populating ManyWhereJoins.
* <p>
* Predicates on Many properties require an extra independent join clause.
* </p>
@@ -89,4 +94,8 @@ public interface SpiExpression extends Expression {
*/
SpiExpression copyForPlanKey();
/**
* Return the bind Id value if this is a "equal to" expression for the id property.
*/
Object getIdEqualTo(String idName);
}
@@ -133,6 +133,11 @@ public interface SpiQuery<T> extends Query<T> {
}
}
/**
* Check for a single "equal to" expression for the Id.
*/
void checkIdEqualTo();
/**
* Return true if AutoTune should be attempted on this query.
*/
@@ -218,14 +223,14 @@ public interface SpiQuery<T> extends Query<T> {
Timestamp getAsOf();
/**
* Add a table alias for a @History entity involved in a 'As Of' query.
* Increment the counter of tables used in 'As Of' query.
*/
void addAsOfTableAlias(String tableAlias);
void incrementAsOfTableCount();
/**
* Return the list of table alias involved in a 'As Of' query that have @History support.
* Return the table alias used for the base table.
*/
List<String> getAsOfTableAlias();
int getAsOfTableCount();
void addSoftDeletePredicate(String softDeletePredicate);
@@ -301,6 +306,11 @@ public interface SpiQuery<T> extends Query<T> {
*/
NaturalKeyBindParam getNaturalKeyBindParam();
/**
* Prepare the query for docstore execution with nested paths.
*/
void prepareDocNested();
/**
* Set the query to be a delete query.
*/
@@ -40,7 +40,7 @@ public class ExtraDdlXmlReader {
* @param platforms The platforms (comma delimited) this script should run for
*/
public static boolean matchPlatform(String platformName, String platforms) {
if (platforms == null || platforms.trim().length() == 0) {
if (platforms == null || platforms.trim().isEmpty()) {
return true;
}
String[] names = platforms.split("[,;]");
@@ -1043,21 +1043,20 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
public <T> T findUnique(Query<T> query, Transaction transaction) {
Object id = query.getId();
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
spiQuery.checkIdEqualTo();
Object id = spiQuery.getId();
if (id != null) {
// actually a find by Id query
return findId(query, transaction);
}
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
BeanDescriptor<T> desc = spiQuery.getBeanDescriptor();
SpiTransaction t = (SpiTransaction) transaction;
if (t == null) {
t = getCurrentServerTransaction();
}
if (t == null || !t.isSkipCache()) {
id = desc.cacheNaturalKeyIdLookup(spiQuery);
id = spiQuery.getBeanDescriptor().cacheNaturalKeyIdLookup(spiQuery);
if (id != null) {
T bean = findIdCheckPersistenceContextAndCache(t, spiQuery, id);
if (bean != null) {
@@ -235,7 +235,7 @@ public class InternalConfiguration {
if (historySupport == null) {
return new Binder(typeManager, 0, false, jsonHandler, dataTimeZone);
}
return new Binder(typeManager, historySupport.getBindCount(), historySupport.isBindWithFromClause(), jsonHandler, dataTimeZone);
return new Binder(typeManager, historySupport.getBindCount(), historySupport.isStandardsBased(), jsonHandler, dataTimeZone);
}
/**
@@ -513,4 +513,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
public boolean isAuditReads() {
return !query.isDisableReadAudit() && beanDescriptor.isReadAuditing();
}
/**
* Return the base table alias for this query.
*/
public String getBaseTableAlias() {
return query.getAlias() == null ? beanDescriptor.getBaseTableAlias() : query.getAlias();
}
}
@@ -219,7 +219,7 @@ public final class RelationalQueryRequest {
int maxRows = query.getMaxRows();
if (firstRow > 0 || maxRows > 0) {
return ebeanServer.getDatabasePlatform().getBasicSqlLimiter()
.limit(query.getQuery(), firstRow, maxRows);
.limit(sql, firstRow, maxRows);
}
return sql;
}
@@ -1768,6 +1768,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
pc.deleted(rootBeanType, idValue);
}
/**
* Return the Id property name or null if no Id property exists.
*/
public String getIdName() {
return (idProperty == null) ? null : idProperty.getName();
}
/**
* Helper method to return the unique property. If only one property makes up
* the unique id then it's value is returned. If there is a concatenated
@@ -1141,7 +1141,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
// already assigned (So custom or UUID)
return;
}
if (desc.propertiesId().size() == 0) {
if (desc.propertiesId().isEmpty()) {
// bean doesn't have an Id property
if (desc.isBaseTableType() && desc.getBeanFinder() == null) {
// expecting an id property
@@ -283,7 +283,7 @@ public class DeployBeanDescriptor<T> {
docStoreUpdate = docStore.update();
docStoreDelete = docStore.delete();
String doc = docStore.doc();
if (doc.length() > 0) {
if (!doc.isEmpty()) {
docStorePathProperties = PathProperties.parse(doc);
}
}
@@ -388,7 +388,7 @@ public class DeployBeanDescriptor<T> {
public void setCache(Cache cache) {
String naturalKey = null;
if (cache.naturalKey().length() > 0) {
if (!cache.naturalKey().isEmpty()) {
// find the property and mark as natural key property
String propName = cache.naturalKey().trim();
DeployBeanProperty beanProperty = getBeanProperty(propName);
@@ -583,7 +583,7 @@ public class DeployBeanDescriptor<T> {
*/
public void setView(String viewName, String[] dependentTables) {
this.entityType = EntityType.VIEW;
this.dependentTables = this.dependentTables;
this.dependentTables = dependentTables;
setBaseTable(new TableName(viewName), "", "");
}
@@ -835,7 +835,7 @@ public class DeployBeanDescriptor<T> {
return null;
}
String selectClause = sb.toString();
if (selectClause.length() == 0) {
if (selectClause.isEmpty()) {
throw new IllegalStateException("Bean " + getFullName() + " has no properties?");
}
return selectClause.substring(0, selectClause.length() - 1);
@@ -386,7 +386,7 @@ public class DeployBeanProperty {
* Set a specific DB column definition.
*/
public void setDbColumnDefn(String dbColumnDefn) {
if (dbColumnDefn == null || dbColumnDefn.trim().length() == 0) {
if (dbColumnDefn == null || dbColumnDefn.trim().isEmpty()) {
this.dbColumnDefn = null;
} else {
this.dbColumnDefn = InternString.intern(dbColumnDefn);
@@ -178,7 +178,7 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
* Set the default mapKey to use when returning a Map.
*/
public void setMapKey(String mapKey) {
if (mapKey != null && mapKey.length() > 0) {
if (mapKey != null && !mapKey.isEmpty()) {
this.mapKey = mapKey;
}
}
@@ -188,7 +188,7 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
* list, set or map.
*/
public void setFetchOrderBy(String orderBy) {
if (orderBy != null && orderBy.length() > 0) {
if (orderBy != null && !orderBy.isEmpty()) {
fetchOrderBy = orderBy;
}
}
@@ -32,7 +32,7 @@ public abstract class AnnotationBase {
* Checks string is null or empty .
*/
protected boolean isEmpty(String s) {
return s == null || s.trim().length() == 0;
return s == null || s.trim().isEmpty();
}
@@ -188,7 +188,7 @@ public class DeployInheritInfo {
public void setDiscriminatorValue(String value) {
if (value != null) {
value = value.trim();
if (value.length() != 0) {
if (!value.isEmpty()) {
discriminatorStringValue = value;
// convert the value if desired
if (discriminatorType == Types.INTEGER) {
@@ -70,7 +70,7 @@ public class SqlReservedWords {
public static synchronized void addKeyword(String keyword){
if (keyword != null){
keyword = keyword.trim().toUpperCase();
if (keyword.length() > 0){
if (!keyword.isEmpty()){
keywords.add(keyword);
}
}
@@ -8,6 +8,7 @@ import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.query.SplitName;
/**
* Base class for simple expressions.
@@ -20,11 +21,32 @@ public abstract class AbstractExpression implements SpiExpression {
this.propName = propName;
}
@Override
public Object getIdEqualTo(String idName) {
// override on SimpleExpression
return null;
}
@Override
public SpiExpression copyForPlanKey() {
return this;
}
@Override
public String nestedPath(BeanDescriptor<?> desc) {
return propertyNestedPath(propName, desc);
}
protected String propertyNestedPath(String propertyName, BeanDescriptor<?> desc) {
if (propertyName != null) {
ElPropertyDeploy elProp = desc.getElPropertyDeploy(propertyName);
if (elProp != null && elProp.containsMany()) {
return SplitName.begin(propName);
}
}
return null;
}
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
@@ -16,6 +16,12 @@ public abstract class AbstractTextExpression extends AbstractExpression {
super(propName);
}
@Override
public Object getIdEqualTo(String idName) {
// always null for this expression
return null;
}
@Override
public void addSql(SpiExpressionRequest request) {
// do nothing, only execute against document store
@@ -30,6 +30,11 @@ class AllEqualsExpression extends NonPrepareExpression {
context.writeAllEquals(propMap);
}
@Override
public String nestedPath(BeanDescriptor<?> desc) {
return null;
}
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
if (propMap != null) {
@@ -7,6 +7,7 @@ import com.avaje.ebeaninternal.api.SpiExpressionRequest;
import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
import com.avaje.ebeaninternal.server.query.SplitName;
import java.io.IOException;
@@ -39,6 +40,16 @@ class BetweenPropertyExpression extends NonPrepareExpression {
context.endBool();
}
@Override
public String nestedPath(BeanDescriptor<?> desc) {
ElPropertyDeploy elProp = desc.getElPropertyDeploy(name(lowProperty));
if (elProp != null && elProp.containsMany()) {
// assumes highProperty is also nested property which seems reasonable
return SplitName.begin(lowProperty);
}
return null;
}
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
@@ -1,8 +1,5 @@
package com.avaje.ebeaninternal.server.expression;
import java.io.IOException;
import java.util.ArrayList;
import com.avaje.ebean.ExampleExpression;
import com.avaje.ebean.LikeType;
import com.avaje.ebean.bean.EntityBean;
@@ -12,33 +9,34 @@ import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
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;
import java.io.IOException;
import java.util.ArrayList;
/**
* A "Query By Example" type of expression.
* <p>
* Pass in an example entity and for each non-null scalar properties an
* expression is added.
* </p>
*
* <pre class="code">
* <p>
* <pre>{@code
* // create an example bean and set the properties
* // with the query parameters you want
* Customer example = new Customer();
* example.setName(&quot;Rob%&quot;);
* example.setNotes(&quot;%something%&quot;);
*
* List&lt;Customer&gt; list = Ebean.find(Customer.class).where()
* // pass the bean into the where() clause
* .exampleLike(example)
* // you can add other expressions to the same query
* .gt(&quot;id&quot;, 2).findList();
*
* </pre>
* example.setName("Rob%");
* example.setNotes("%something%");
*
* List<Customer> list = Ebean.find(Customer.class)
* .where()
* .exampleLike(example)
* .findList();
*
* }</pre>
*/
public class DefaultExampleExpression implements SpiExpression, ExampleExpression {
@@ -71,13 +69,10 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
/**
* Construct the query by example expression.
*
* @param entity
* the example entity with non null property values
* @param caseInsensitive
* if true use case insensitive expressions
* @param likeType
* the type of Like wild card used
*
* @param entity the example entity with non null property values
* @param caseInsensitive if true use case insensitive expressions
* @param likeType the type of Like wild card used
*/
public DefaultExampleExpression(EntityBean entity, boolean caseInsensitive, LikeType likeType) {
this.entity = entity;
@@ -104,11 +99,22 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
}
}
@Override
public Object getIdEqualTo(String idName) {
// always return null for this expression
return null;
}
@Override
public SpiExpression copyForPlanKey() {
return new DefaultExampleExpression(list);
}
@Override
public String nestedPath(BeanDescriptor<?> desc) {
return null;
}
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins) {
list = buildExpressions(desc);
@@ -290,9 +296,9 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
}
} else if ((beanProperty instanceof BeanPropertyAssocOne) && (value instanceof EntityBean)) {
BeanPropertyAssocOne assocOne = (BeanPropertyAssocOne)beanProperty;
BeanPropertyAssocOne assocOne = (BeanPropertyAssocOne) beanProperty;
BeanDescriptor targetDescriptor = assocOne.getTargetDescriptor();
addExpressions(list, targetDescriptor, (EntityBean)value, propName);
addExpressions(list, targetDescriptor, (EntityBean) value, propName);
}
}
}
@@ -29,7 +29,9 @@ import java.util.Set;
*/
public class DefaultExpressionList<T> implements SpiExpressionList<T> {
protected final List<SpiExpression> list;
private static final String AND = " and ";
protected List<SpiExpression> list;
protected final Query<T> query;
@@ -37,9 +39,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
protected transient ExpressionFactory expr;
private final String listAndStart;
private final String listAndEnd;
private final String listAndJoin;
protected String allDocNestedPath;
/**
* Set to true for the "Text" root expression list.
@@ -71,16 +71,32 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
this.query = query;
this.expr = expr;
this.parentExprList = parentExprList;
this.listAndStart = "";
this.listAndEnd = "";
this.listAndJoin = " and ";
}
private DefaultExpressionList() {
this(null, null, null, new ArrayList<SpiExpression>());
}
/**
* Wrap the expression list as a Junction or top level DefaultExpressionList.
*
* @param list The list of expressions grouped by nested path
* @param nestedPath The doc store nested path
* @param type The junction type (or null for top level expression list).
* @return A single SpiExpression that has the nestedPath set
*/
SpiExpression wrap(List<SpiExpression> list, String nestedPath, Junction.Type type) {
DefaultExpressionList<T> wrapper = new DefaultExpressionList<T>(query, expr, null, list, false);
wrapper.setAllDocNested(nestedPath);
if (type != null) {
return new JunctionExpression<T>(type, wrapper);
} else {
return wrapper;
}
}
/**
* Write being aware if it is the Top level "text" expressions.
* <p>
@@ -100,6 +116,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
// this is a Top level "text" expressions so we may need to wrap in Bool SHOULD etc.
if (list.isEmpty()) throw new IllegalStateException("empty expression list?");
if (allDocNestedPath!=null) context.startNested(allDocNestedPath);
int size = list.size();
SpiExpression first = list.get(0);
@@ -130,11 +147,13 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
if (implicitBool || explicitBool) {
context.endBoolGroup();
}
if (allDocNestedPath!=null) context.endNested();
}
}
public void writeDocQuery(DocQueryContext context, SpiExpression idEquals) throws IOException {
if (allDocNestedPath!=null) context.startNested(allDocNestedPath);
int size = list.size();
if (size == 1 && idEquals == null) {
// only 1 expression - skip bool
@@ -153,6 +172,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
}
context.endBool();
}
if (allDocNestedPath!=null) context.endNested();
}
@Override
@@ -184,6 +204,12 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return copy;
}
@Override
public Object getIdEqualTo(String idName) {
// always return null for this expression
return null;
}
/**
* Return true if one of the expressions is related to a Many property.
*/
@@ -433,15 +459,13 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
@Override
public void addSql(SpiExpressionRequest request) {
request.append(listAndStart);
for (int i = 0, size = list.size(); i < size; i++) {
SpiExpression expression = list.get(i);
if (i > 0) {
request.append(listAndJoin);
request.append(AND);
}
expression.addSql(request);
}
request.append(listAndEnd);
}
@Override
@@ -983,4 +1007,37 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return junction(Junction.Type.MUST_NOT);
}
@Override
public String nestedPath(BeanDescriptor<?> desc) {
// effectively handled by JunctionExpression
return null;
}
/**
* Set the nested path that all contained expressions share.
*/
public void setAllDocNested(String allDocNestedPath) {
this.allDocNestedPath = allDocNestedPath;
}
/**
* Replace the underlying expression list with one organised by nested path.
*/
public void setUnderlying(List<SpiExpression> groupedByNesting) {
this.list = groupedByNesting;
}
/**
* Prepare expressions for document store nested path handling.
*/
public void prepareDocNested(BeanDescriptor<T> beanDescriptor) {
PrepareDocNested.prepare(this, beanDescriptor);
}
public Object idEqualTo(String idName) {
if (list.size() == 1) {
return list.get(0).getIdEqualTo(idName);
}
return null;
}
}
@@ -147,4 +147,24 @@ public interface DocQueryContext {
* Return the expression path for the given property path.
*/
ExpressionPath getExpressionPath(String propName);
/**
* Start nested path expressions.
*/
void startNested(String nestedPath) throws IOException;
/**
* End nested path expressions.
*/
void endNested() throws IOException;
/**
* Start a not wrapping an expression.
*/
void startNot() throws IOException;
/**
* End a not wrapper.
*/
void endNot() throws IOException;
}
@@ -41,6 +41,12 @@ class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpress
throw new IllegalStateException("Not supported");
}
@Override
public Object getIdEqualTo(String idName) {
// always return null for this expression
return null;
}
@Override
public void prepareExpression(BeanQueryRequest<?> request) {
@@ -118,6 +124,11 @@ class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpress
return true;
}
@Override
public String nestedPath(BeanDescriptor<?> desc) {
return null;
}
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins) {
// Nothing to do for exists expression
@@ -25,6 +25,11 @@ class IdExpression extends NonPrepareExpression implements SpiExpression {
context.writeId(value);
}
@Override
public String nestedPath(BeanDescriptor<?> desc) {
return null;
}
/**
* Always returns false.
*/
@@ -22,6 +22,11 @@ public class IdInExpression extends NonPrepareExpression {
this.idList = idList;
}
@Override
public String nestedPath(BeanDescriptor<?> desc) {
return null;
}
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
}
@@ -16,15 +16,38 @@ class IsEmptyExpression extends AbstractExpression {
private final String propertyPath;
private String nestedPath;
IsEmptyExpression(String propertyName, boolean empty) {
super(propertyName);
this.empty = empty;
this.propertyPath = SplitName.split(propertyName)[0];
}
@Override
public String nestedPath(BeanDescriptor<?> desc) {
if (empty) {
// capture the nestedPath as we want to put wrap
// a NOT around the outer of the nested path exists
this.nestedPath = propertyNestedPath(propName, desc);
return null;
} else {
return super.nestedPath(desc);
}
}
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
context.writeExists(!empty, propName);
if (nestedPath == null) {
context.writeExists(!empty, propName);
} else {
// wrap NOT around the outside of nested path exists expression
context.startNot();
context.startNested(nestedPath);
context.writeExists(empty, propName);
context.endNested();
context.endNot();
}
}
public final String getPropName() {
@@ -39,7 +39,7 @@ import java.util.Set;
*/
class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, ExpressionList<T> {
private final DefaultExpressionList<T> exprList;
protected final DefaultExpressionList<T> exprList;
protected final Junction.Type type;
@@ -80,6 +80,12 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
context.endBoolGroupList();
}
@Override
public Object getIdEqualTo(String idName) {
// always null for this expression
return null;
}
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
@@ -790,4 +796,17 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
public ExpressionList<T> endNot() {
return endJunction();
}
@Override
public String nestedPath(BeanDescriptor<?> desc) {
PrepareDocNested.prepare(exprList, desc, type);
String nestedPath = exprList.allDocNestedPath;
if (nestedPath != null) {
// push the nestedPath up to parent
exprList.setAllDocNested(null);
return nestedPath;
}
return null;
}
}
@@ -45,9 +45,9 @@ abstract class LogicExpression implements SpiExpression {
}
}
protected final SpiExpression expOne;
protected SpiExpression expOne;
protected final SpiExpression expTwo;
protected SpiExpression expTwo;
private final String joinType;
@@ -67,6 +67,33 @@ abstract class LogicExpression implements SpiExpression {
context.endBool();
}
@Override
public String nestedPath(BeanDescriptor<?> desc) {
String pathOne = expOne.nestedPath(desc);
String pathTwo = expTwo.nestedPath(desc);
if (pathOne == null && pathTwo == null) {
return null;
}
if (pathOne != null && pathOne.equals(pathTwo)) {
return pathOne;
}
if (pathOne != null) {
expOne = new NestedPathWrapperExpression(pathOne, expOne);
}
if (pathTwo != null) {
expTwo = new NestedPathWrapperExpression(pathTwo, expTwo);
}
return null;
}
@Override
public Object getIdEqualTo(String idName) {
// always return null for this expression
return null;
}
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
expOne.containsMany(desc, manyWhereJoin);
@@ -0,0 +1,99 @@
package com.avaje.ebeaninternal.server.expression;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
import com.avaje.ebeaninternal.api.SpiExpressionValidation;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import java.io.IOException;
/**
* Wraps a single expression with nestedPath for document queries.
*/
class NestedPathWrapperExpression implements SpiExpression {
protected final String nestedPath;
protected final SpiExpression delegate;
NestedPathWrapperExpression(String nestedPath, SpiExpression delegate) {
this.nestedPath = nestedPath;
this.delegate = delegate;
}
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
context.startNested(nestedPath);
delegate.writeDocQuery(context);
context.endNested();
}
@Override
public Object getIdEqualTo(String idName) {
// always return null for this expression
return null;
}
@Override
public String nestedPath(BeanDescriptor<?> desc) {
return nestedPath;
}
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins) {
delegate.containsMany(desc, whereManyJoins);
}
@Override
public void prepareExpression(BeanQueryRequest<?> request) {
delegate.prepareExpression(request);
}
@Override
public void queryPlanHash(HashQueryPlanBuilder builder) {
delegate.queryPlanHash(builder);
}
@Override
public int queryBindHash() {
return delegate.queryBindHash();
}
@Override
public boolean isSameByPlan(SpiExpression other) {
if (other instanceof NestedPathWrapperExpression) {
NestedPathWrapperExpression that = (NestedPathWrapperExpression)other;
return nestedPath.equals(that.nestedPath)
&& delegate.isSameByPlan(that.delegate);
}
return false;
}
@Override
public boolean isSameByBind(SpiExpression other) {
return delegate.isSameByBind(other);
}
@Override
public void addSql(SpiExpressionRequest request) {
delegate.addSql(request);
}
@Override
public void addBindValues(SpiExpressionRequest request) {
delegate.addBindValues(request);
}
@Override
public void validate(SpiExpressionValidation validation) {
delegate.validate(validation);
}
@Override
public SpiExpression copyForPlanKey() {
return new NestedPathWrapperExpression(nestedPath, delegate.copyForPlanKey());
}
}
@@ -13,6 +13,12 @@ abstract class NonPrepareExpression implements SpiExpression {
// do nothing
}
@Override
public Object getIdEqualTo(String idName) {
// always null in this expression
return null;
}
@Override
public SpiExpression copyForPlanKey() {
return this;
@@ -26,6 +26,17 @@ class NoopExpression implements SpiExpression {
public void writeDocQuery(DocQueryContext context) throws IOException {
}
@Override
public Object getIdEqualTo(String idName) {
// always return null for this expression
return null;
}
@Override
public String nestedPath(BeanDescriptor<?> desc) {
return null;
}
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins) {
// nothing to do
@@ -29,11 +29,22 @@ final class NotExpression implements SpiExpression {
context.endBool();
}
@Override
public Object getIdEqualTo(String idName) {
// always return null for this expression
return null;
}
@Override
public SpiExpression copyForPlanKey() {
return new NotExpression(exp.copyForPlanKey());
}
@Override
public String nestedPath(BeanDescriptor<?> desc) {
return exp.nestedPath(desc);
}
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
exp.containsMany(desc, manyWhereJoin);
@@ -0,0 +1,163 @@
package com.avaje.ebeaninternal.server.expression;
import com.avaje.ebean.Junction;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Prepare nested path expressions for
*/
class PrepareDocNested {
/**
* Prepare the top level expressions for nested path handling.
*/
static void prepare(DefaultExpressionList<?> expressions, BeanDescriptor<?> beanDescriptor) {
new PrepareDocNested(expressions, beanDescriptor, null).process();
}
/**
* Prepare the Junction expressions for nested path handling.
*/
static void prepare(DefaultExpressionList<?> expressions, BeanDescriptor<?> beanDescriptor, Junction.Type type) {
new PrepareDocNested(expressions, beanDescriptor, type).process();
}
enum Mode {
NONE,
SINGLE,
MIXED
}
private final Junction.Type type;
private final DefaultExpressionList<?> original;
private final BeanDescriptor<?> beanDescriptor;
private final List<SpiExpression> origUnderlying;
private final int origSize;
private boolean hasNesting;
private boolean hasMixedNesting;
private String firstNestedPath;
PrepareDocNested(DefaultExpressionList<?> original, BeanDescriptor<?> beanDescriptor, Junction.Type type) {
this.type = type;
this.beanDescriptor = beanDescriptor;
this.original = original;
this.origUnderlying = original.getUnderlyingList();
this.origSize = origUnderlying.size();
}
void process() {
PrepareDocNested.Mode mode = determineMode();
if (mode == PrepareDocNested.Mode.SINGLE) {
original.setAllDocNested(firstNestedPath);
} else if (mode == PrepareDocNested.Mode.MIXED) {
original.setUnderlying(group());
}
}
/**
* Reorganise the flat list of expressions into a tree grouping expressions by nested path.
*
* Returns the new top level list of expressions.
*/
private List<SpiExpression> group() {
Map<String,Group> groups = new LinkedHashMap<String,Group>();
// organise expressions by nestedPath
for (int i = 0; i < origSize; i++) {
SpiExpression expr = origUnderlying.get(i);
String nestedPath = expr.nestedPath(beanDescriptor);
Group group = groups.get(nestedPath);
if (group == null) {
group = new Group(nestedPath);
groups.put(nestedPath, group);
}
group.list.add(expr);
}
List<SpiExpression> newList = new ArrayList<SpiExpression>();
Collection<Group> values = groups.values();
for (Group group : values) {
group.addTo(newList);
}
return newList;
}
/**
* Determined the nested path mode.
*/
private Mode determineMode() {
if (!hasNesting()) {
// no nested paths at all
return Mode.NONE;
}
if (!hasMixedNesting) {
// single nested path for all expressions
return Mode.SINGLE;
}
// mixed nested paths to underlying expression list needs re-organising by nested path
return Mode.MIXED;
}
/**
* Return true if the expressions have nested paths.
*/
private boolean hasNesting() {
for (int i = 0; i < origSize; i++) {
SpiExpression expr = origUnderlying.get(i);
String nestedPath = expr.nestedPath(beanDescriptor);
if (nestedPath == null) {
hasMixedNesting = true;
} if (nestedPath != null) {
hasNesting = true;
if (firstNestedPath == null) {
firstNestedPath = nestedPath;
} else if (hasMixedNesting || !firstNestedPath.equals(nestedPath)) {
hasMixedNesting = true;
return true;
}
}
}
return hasNesting;
}
/**
* List of SpiExpression grouped by nested path.
*/
class Group {
final String nestedPath;
final List<SpiExpression> list = new ArrayList<SpiExpression>();
Group(String nestedPath) {
this.nestedPath = nestedPath;
}
void addTo(List<SpiExpression> newList) {
if (nestedPath == null) {
newList.addAll(list);
} else {
newList.add(original.wrap(list, nestedPath, type));
}
}
}
}
@@ -25,6 +25,11 @@ class RawExpression extends NonPrepareExpression {
context.writeRaw(sql, values);
}
@Override
public String nestedPath(BeanDescriptor<?> desc) {
return null;
}
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
@@ -21,6 +21,14 @@ public class SimpleExpression extends AbstractExpression {
this.value = value;
}
@Override
public Object getIdEqualTo(String idName) {
if (type == Op.EQ && idName.equals(propName)) {
return value;
}
return null;
}
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
if (type == Op.BETWEEN) {
@@ -24,7 +24,7 @@ public class StringHelper {
String listDelimiter, String nameValueSeparator) {
HashMap<String, String> params = new HashMap<String, String>();
if ((allNameValuePairs == null) || (allNameValuePairs.length() == 0)) {
if ((allNameValuePairs == null) || (allNameValuePairs.isEmpty())) {
return params;
}
// trim off any leading listDelimiter...
@@ -56,7 +56,7 @@ public class StringHelper {
* Return true if the value is null or an empty string.
*/
public static boolean isNull(String value) {
return value == null || value.trim().length() == 0;
return value == null || value.trim().isEmpty();
}
/**
@@ -90,7 +90,7 @@ public class StringHelper {
// there is a key without a value?
String key = allNameValuePairs.substring(pos, delimPos);
key = key.trim();
if (key.length() > 0) {
if (!key.isEmpty()) {
map.put(key, null);
}
return getKeyValue(map, delimPos + 1, allNameValuePairs, listDelimiter,
@@ -33,7 +33,7 @@ public class Binder {
private final int asOfBindCount;
private final boolean bindAsOfWithFromClause;
private final boolean asOfStandardsBased;
private final DbExpressionHandler dbExpressionHandler;
@@ -42,12 +42,12 @@ public class Binder {
/**
* Set the PreparedStatement with which to bind variables to.
*/
public Binder(TypeManager typeManager, int asOfBindCount, boolean bindAsOfWithFromClause,
public Binder(TypeManager typeManager, int asOfBindCount, boolean asOfStandardsBased,
DbExpressionHandler dbExpressionHandler, DataTimeZone dataTimeZone) {
this.typeManager = typeManager;
this.asOfBindCount = asOfBindCount;
this.bindAsOfWithFromClause = bindAsOfWithFromClause;
this.asOfStandardsBased = asOfStandardsBased;
this.dbExpressionHandler = dbExpressionHandler;
this.dataTimeZone = dataTimeZone;
}
@@ -60,12 +60,10 @@ public class Binder {
}
/**
* Return true if the 'as of' predicates are in the from/join clause in which case the timestamp is
* bound early (before all the other predicates ala Oracle). Return false if the 'as of' predicates are
* appended to the end of the predicates and the timestamp is bound last (Postgres, MySql).
* Return true if the 'as of' history support is SQL2011 standards based.
*/
public boolean isBindAsOfWithFromClause() {
return bindAsOfWithFromClause;
public boolean isAsOfStandardsBased() {
return asOfStandardsBased;
}
/**
@@ -466,18 +466,20 @@ public class CQueryBuilder {
String inheritanceWhere = select.getInheritanceWhereSql();
boolean hasWhere = false;
if (inheritanceWhere.length() > 0) {
if (!inheritanceWhere.isEmpty()) {
sb.append(" where");
sb.append(inheritanceWhere);
hasWhere = true;
}
int asOfCount = query.getAsOfTableCount();
if (asOfCount > 0 && !historySupport.isStandardsBased()) {
hasWhere = appendWhere(hasWhere, sb);
sb.append(historySupport.getAsOfPredicate(request.getBaseTableAlias()));
}
if (request.isFindById() || query.getId() != null) {
if (hasWhere) {
sb.append(" and ");
} else {
sb.append(" where ");
}
appendWhere(hasWhere, sb);
BeanDescriptor<?> desc = request.getBeanDescriptor();
String idSql = desc.getIdBinderIdSql();
@@ -515,24 +517,6 @@ public class CQueryBuilder {
sb.append(dbFilterMany);
}
List<String> asOfTableAlias = query.getAsOfTableAlias();
if (asOfTableAlias != null && !historySupport.isBindAtFromClause()) {
// append the effective date predicates for each table alias
// that maps to a @History entity involved in this query
// Do this when history using separate tables/views (PG, MySql etc)
if (!hasWhere) {
sb.append(" where ");
} else {
sb.append("and ");
}
for (int i = 0; i < asOfTableAlias.size(); i++) {
if (i > 0) {
sb.append(" and ");
}
sb.append(historySupport.getAsOfPredicate(asOfTableAlias.get(i)));
}
}
if (!query.isIncludeSoftDeletes()) {
List<String> softDeletePredicates = query.getSoftDeletePredicates();
if (softDeletePredicates != null) {
@@ -565,6 +549,18 @@ public class CQueryBuilder {
}
/**
* Append where or and based on the hasWhere flag.
*/
private boolean appendWhere(boolean hasWhere, StringBuilder sb) {
if (hasWhere) {
sb.append(" and ");
} else {
sb.append(" where ");
}
return true;
}
/**
* Convert the dbOrderBy clause to be safe for adding to select. This is done when 'distinct' is
* used.
@@ -575,7 +571,7 @@ public class CQueryBuilder {
}
private boolean isEmpty(String s) {
return s == null || s.length() == 0;
return s == null || s.isEmpty();
}
}
@@ -134,7 +134,7 @@ public class CQueryBuilderRawSql {
}
private boolean isEmpty(String s) {
return s == null || s.length() == 0;
return s == null || s.isEmpty();
}
private String getOrderBy(CQueryPredicates predicates, RawSql.Sql sql) {
@@ -19,6 +19,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.SQLException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -204,15 +205,16 @@ public class CQueryEngine {
SpiQuery<T> query = request.getQuery();
if (query.isVersionsBetween() && !historySupport.isBindAtFromClause()) {
String sysPeriodLower = getSysPeriodLower(query);
if (query.isVersionsBetween() && !historySupport.isStandardsBased()) {
// just add as normal predicates using the lower bound
query.where().gt(getSysPeriodLower(query), query.getVersionStart());
query.where().lt(getSysPeriodLower(query), query.getVersionEnd());
query.where().gt(sysPeriodLower, query.getVersionStart());
query.where().lt(sysPeriodLower, query.getVersionEnd());
}
// order by id asc, lower sys period desc
query.orderBy().asc(request.getBeanDescriptor().getIdProperty().getName());
query.orderBy().desc(getSysPeriodLower(query));
query.orderBy().desc(sysPeriodLower);
CQuery<T> cquery = queryBuilder.buildQuery(request);
try {
@@ -222,6 +224,9 @@ public class CQueryEngine {
}
List<Version<T>> versions = cquery.readVersions();
// just order in memory rather than use NULLS LAST as that
// is not universally supported, not expect huge list here
Collections.sort(versions, OrderVersionDesc.INSTANCE);
deriveVersionDiffs(versions, request);
if (request.isLogSummary()) {
@@ -31,11 +31,10 @@ public class CQueryHistorySupport {
}
/**
* Return true if the bind of 'as of' timestamp occurs with the from clause
* rather than at the end.
* Return true if the underlying history support is standards based.
*/
public boolean isBindAtFromClause() {
return dbHistorySupport.isBindWithFromClause();
public boolean isStandardsBased() {
return dbHistorySupport.isStandardsBased();
}
/**
@@ -70,6 +70,8 @@ public class CQueryPlan {
protected final DataTimeZone dataTimeZone;
private final int asOfTableCount;
/**
* Key used to identify the query plan in audit logging.
*/
@@ -86,6 +88,7 @@ public class CQueryPlan {
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
this.planKey = request.getQueryPlanKey();
this.autoTuned = request.getQuery().isAutoTuned();
this.asOfTableCount = request.getQuery().getAsOfTableCount();
if (sqlRes != null) {
this.sql = sqlRes.getSql();
this.rowNumberIncluded = sqlRes.isIncludesRowNumberColumn();
@@ -111,6 +114,7 @@ public class CQueryPlan {
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
this.planKey = buildPlanKey(sql, rawSql, rowNumberIncluded, logWhereSql);
this.autoTuned = false;
this.asOfTableCount = 0;
this.sql = sql;
this.sqlTree = sqlTree;
this.rawSql = rawSql;
@@ -151,6 +155,10 @@ public class CQueryPlan {
return dataBind;
}
public int getAsOfTableCount() {
return asOfTableCount;
}
public boolean isAutoTuned() {
return autoTuned;
}
@@ -126,7 +126,7 @@ public class CQueryPredicates {
updateProperties.bind(binder, dataBind);
}
if (query.isVersionsBetween() && binder.isBindAsOfWithFromClause()) {
if (query.isVersionsBetween() && binder.isAsOfStandardsBased()) {
// sql2011 based versions between timestamp syntax
Timestamp start = query.getVersionStart();
Timestamp end = query.getVersionEnd();
@@ -136,13 +136,13 @@ public class CQueryPredicates {
dataBind.append(", ");
}
List<String> historyTableAlias = query.getAsOfTableAlias();
if (historyTableAlias != null && binder.isBindAsOfWithFromClause()) {
int asOfTableCount = request.getQueryPlan().getAsOfTableCount();
if (asOfTableCount > 0) {
// bind the asOf value for each table alias as part of the from/join clauses
// there is one effective date predicate per table alias
Timestamp asOf = query.getAsOf();
dataBind.append("asOf ").append(asOf);
for (int i = 0; i < historyTableAlias.size() * binder.getAsOfBindCount(); i++) {
for (int i = 0; i < asOfTableCount * binder.getAsOfBindCount(); i++) {
binder.bindObject(dataBind, asOf);
}
dataBind.append(", ");
@@ -167,16 +167,6 @@ public class CQueryPredicates {
filterMany.bind(dataBind);
}
if (historyTableAlias != null && !binder.isBindAsOfWithFromClause()) {
// bind the asOf value for each table alias after all the normal predicates
// there is one effective date predicate per table alias
Timestamp asOf = query.getAsOf();
dataBind.append(" asOf ").append(asOf);
for (int i = 0; i < historyTableAlias.size() * binder.getAsOfBindCount(); i++) {
binder.bindObject(dataBind, asOf);
}
}
if (having != null) {
having.bind(dataBind);
}
@@ -305,7 +295,7 @@ public class CQueryPredicates {
}
private boolean isEmpty(String s) {
return s == null || s.length() == 0;
return s == null || s.isEmpty();
}
private String parse(String expr, DeployParser deployParser) {
@@ -106,8 +106,8 @@ public class DefaultDbSqlContext implements DbSqlContext {
tableJoins.add(joinKey);
sb.append(" ");
sb.append(type);
sb.append(" ").append(type);
boolean addAsOfOnClause = false;
if (draftSupport != null) {
appendTable(table, draftSupport.getDraftTable(table));
@@ -117,32 +117,32 @@ public class DefaultDbSqlContext implements DbSqlContext {
} else {
// check if there is an associated history table and if so
// use the unionAll view - we expect an additional predicate to match
appendTable(table, historySupport.getAsOfView(table));
String asOfView = historySupport.getAsOfView(table);
appendTable(table, asOfView);
if (asOfView != null) {
addAsOfOnClause = !historySupport.isStandardsBased();
}
}
sb.append(a2);
sb.append(" on ");
for (int i = 0; i < cols.length; i++) {
TableJoinColumn pair = cols[i];
if (i > 0) {
sb.append(" and ");
}
sb.append(a2);
sb.append(".").append(pair.getForeignDbColumn());
sb.append(a2).append(".").append(pair.getForeignDbColumn());
sb.append(" = ");
sb.append(a1);
sb.append(".").append(pair.getLocalDbColumn());
sb.append(a1).append(".").append(pair.getLocalDbColumn());
}
// add on any inheritance where clause
if (inheritance != null && inheritance.length() > 0) {
sb.append(" and ");
sb.append(a2);
sb.append(".");
sb.append(inheritance);
if (inheritance != null && !inheritance.isEmpty()) {
sb.append(" and ").append(a2).append(".").append(inheritance);
}
if (addAsOfOnClause) {
sb.append(" and ").append(historySupport.getAsOfPredicate(a2));
}
sb.append(" ");
@@ -0,0 +1,29 @@
package com.avaje.ebeaninternal.server.query;
import com.avaje.ebean.Version;
import java.sql.Timestamp;
import java.util.Comparator;
/**
* Compare Version beans in descending order with nulls last.
*/
class OrderVersionDesc implements Comparator<Version<?>> {
static final OrderVersionDesc INSTANCE = new OrderVersionDesc();
@Override
public int compare(Version<?> o1, Version<?> o2) {
Timestamp v1 = o1.getStart();
if (v1 == null) {
return 1;
}
Timestamp v2 = o2.getStart();
if (v2 == null) {
return -1;
}
return v1.compareTo(v2) * -1;
}
}
@@ -1,9 +1,15 @@
package com.avaje.ebeaninternal.server.query;
/**
* Helper for dot notation property paths.
*/
public class SplitName {
private static final char PERIOD = '.';
/**
* Add the two name sections together in dot notation.
*/
public static String add(String prefix, String name) {
if (prefix != null) {
return prefix + "." + name;
@@ -38,10 +44,20 @@ public class SplitName {
}
}
/**
* Return the name split by last.
*/
public static String[] split(String name) {
return split(name, true);
}
/**
* Return the first part of the name.
*/
public static String begin(String name) {
return splitBegin(name)[0];
}
public static String[] splitBegin(String name) {
return split(name, false);
}
@@ -111,7 +111,7 @@ public class SqlTreeBuilder {
this.queryDetail = query.getDetail();
this.predicates = predicates;
this.alias = new SqlTreeAlias(request.getQuery().getAlias() == null ? request.getBeanDescriptor().getBaseTableAlias() : request.getQuery().getAlias());
this.alias = new SqlTreeAlias(request.getBaseTableAlias());
this.ctx = new DefaultDbSqlContext(alias, tableAliasPlaceHolder, columnAliasPrefix, !subQuery, historySupport, draftSupport);
}
@@ -417,7 +417,7 @@ public class SqlTreeBuilder {
// This makes sense for transient properties used to
// hold sum() count() type values (with SqlSelect)
for (String propName : queryProps.getSelectProperties()) {
if (propName.length() > 0) {
if (!propName.isEmpty()) {
addProperty(selectProps, desc, queryProps, propName);
}
}
@@ -81,7 +81,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
* Table alias set if this bean node includes a join to a intersection
* table and that table has history support.
*/
protected String intersectionAsOfTableAlias;
private boolean intersectionAsOfTableAlias;
/**
* Construct for Raw SQL.
@@ -501,11 +501,10 @@ public class SqlTreeNodeBean implements SqlTreeNode {
// if history on this bean type add it's alias
// for each alias we add an effect date predicate
if (desc.isHistorySupport()) {
query.addAsOfTableAlias(baseTableAlias);
query.incrementAsOfTableCount();
}
if (intersectionAsOfTableAlias != null) {
// adds the 'as of' predicate for this intersection table
query.addAsOfTableAlias(intersectionAsOfTableAlias);
if (intersectionAsOfTableAlias) {
query.incrementAsOfTableCount();
}
for (int i = 0; i < children.length; i++) {
children[i].addAsOfTableAlias(query);
@@ -531,7 +530,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
TableJoin manyToManyJoin = manyProp.getIntersectionTableJoin();
manyToManyJoin.addJoin(joinType, parentAlias, alias2, ctx);
if (!manyProp.isExcludedFromHistory()) {
intersectionAsOfTableAlias = alias2;
intersectionAsOfTableAlias = true;
}
return nodeBeanProp.addJoin(joinType, alias2, alias, ctx);
@@ -144,10 +144,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
private DefaultExpressionList<T> havingExpressions;
/**
* The list of table alias associated with @History entity beans.
*/
private List<String> asOfTableAlias;
private int asOfTableCount;
/**
* Set for flashback style 'as of' query.
@@ -236,6 +233,16 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return beanDescriptor;
}
@Override
public void checkIdEqualTo() {
if (id == null && whereExpressions != null) {
id = whereExpressions.idEqualTo(beanDescriptor.getIdName());
if (id != null) {
whereExpressions = null;
}
}
}
@Override
public boolean isAutoTunable() {
return beanDescriptor.isAutoTunable();
@@ -271,21 +278,14 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return softDeletePredicates;
}
/**
* This table alias is for a @History entity involved in the query and as
* such we need to add a 'as of predicate' to the query using this alias.
*/
@Override
public void addAsOfTableAlias(String tableAlias) {
if (asOfTableAlias == null) {
asOfTableAlias = new ArrayList<String>();
}
asOfTableAlias.add(tableAlias);
public void incrementAsOfTableCount() {
asOfTableCount++;
}
@Override
public List<String> getAsOfTableAlias() {
return asOfTableAlias;
public int getAsOfTableCount() {
return asOfTableCount;
}
@Override
@@ -484,6 +484,16 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
}
@Override
public void prepareDocNested() {
if (textExpressions != null) {
textExpressions.prepareDocNested(beanDescriptor);
}
if (whereExpressions != null) {
whereExpressions.prepareDocNested(beanDescriptor);
}
}
/**
* Setup to be a delete query.
*/
@@ -1151,7 +1161,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
@Override
public DefaultOrmQuery<T> order(String orderByClause) {
if (orderByClause == null || orderByClause.trim().length() == 0) {
if (orderByClause == null || orderByClause.trim().isEmpty()) {
this.orderBy = null;
} else {
this.orderBy = new OrderBy<T>(this, orderByClause);
@@ -134,7 +134,7 @@ public class OrmQueryDetailParser {
}
}
String whereClause = sb.toString().trim();
if (whereClause.length() > 0) {
if (!whereClause.isEmpty()) {
rawWhereClause = whereClause;
}
@@ -236,7 +236,7 @@ public class OrmQueryProperties implements Serializable {
@SuppressWarnings("unchecked")
public void configureBeanQuery(SpiQuery<?> query) {
if (trimmedProperties != null && trimmedProperties.length() > 0) {
if (trimmedProperties != null && !trimmedProperties.isEmpty()) {
query.select(trimmedProperties);
}
@@ -122,7 +122,7 @@ public class OrmQueryPropertiesParser {
String temp;
for (int i = 0; i < res.length; i++) {
temp = res[i].trim();
if (temp.length() > 0) {
if (!temp.isEmpty()) {
if (count > 0) {
sb.append(",");
}
@@ -72,6 +72,7 @@ public class OrmUpdateProperties {
@Override
public void bind(Binder binder, DataBind dataBind) throws SQLException {
binder.bindObject(dataBind, value);
dataBind.append(value).append(",");
}
}
@@ -105,6 +106,7 @@ public class OrmUpdateProperties {
public void bind(Binder binder, DataBind dataBind) throws SQLException {
for (Object val : bindValues) {
binder.bindObject(dataBind, val);
dataBind.append(val).append(",");
}
}
}
@@ -268,7 +268,7 @@ public class TCsvReader<T> implements CsvReader<T> {
strValue = strValue.trim();
if (strValue.length() == 0) {
if (strValue.isEmpty()) {
return;
}
@@ -35,7 +35,7 @@ public class ScalarTypeChar extends ScalarTypeBaseVarchar<Character> {
public Character read(DataReader dataReader) throws SQLException {
String string = dataReader.getString();
if (string == null || string.length() == 0) {
if (string == null || string.isEmpty()) {
return null;
} else {
return string.charAt(0);
@@ -86,7 +86,7 @@ public class BindParamsParser {
if (params.isSameBindHash()) {
String preparedSql = params.getPreparedSql();
if (preparedSql != null && preparedSql.length() > 0) {
if (preparedSql != null && !preparedSql.isEmpty()) {
// the sql has already been parsed and positionedParameters are set in order
return preparedSql;
}
@@ -33,7 +33,7 @@ public final class SortByClauseParser {
}
private Property parseSection(String section) {
if (section.length() == 0) {
if (section.isEmpty()) {
return null;
}
String[] words = section.split(" ");
@@ -321,7 +321,7 @@ public abstract class DocStoreBeanBaseAdapter<T> implements DocStoreBeanAdapter<
* Return the supplied value or default to the bean name lower case.
*/
protected String derive(BeanType<?> desc, String suppliedValue) {
return (suppliedValue != null && suppliedValue.length() > 0) ? suppliedValue : desc.getName().toLowerCase();
return (suppliedValue != null && !suppliedValue.isEmpty()) ? suppliedValue : desc.getName().toLowerCase();
}
@Override
@@ -30,6 +30,6 @@ public class PrimaryServerTest {
public void testLoadProperties() throws Exception {
Properties properties = PrimaryServer.getProperties();
assertTrue(properties.size() > 0);
assertTrue(!properties.isEmpty());
}
}
@@ -0,0 +1,32 @@
package com.avaje.ebean.config.dbplatform;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class MySqlHistorySupportTest {
private MySqlHistorySupport support = new MySqlHistorySupport();
@Test
public void getAsOfPredicate() {
String asOfPredicate = support.getAsOfPredicate("t0", "sys_period");
assertEquals(asOfPredicate, "(t0.sys_period_start <= ? and (t0.sys_period_end is null or t0.sys_period_end > ?))");
}
@Test
public void getLower() throws Exception {
String lower = support.getSysPeriodLower("t0", "sys_period");
assertEquals(lower, "t0.sys_period_start");
}
@Test
public void getUpper() throws Exception {
String upper = support.getSysPeriodUpper("t0", "sys_period");
assertEquals(upper, "t0.sys_period_end");
}
}
@@ -0,0 +1,38 @@
package com.avaje.ebean.config.dbplatform;
import org.junit.Test;
import static org.junit.Assert.*;
public class PostgresHistorySupportTest {
private PostgresHistorySupport support = new PostgresHistorySupport();
@Test
public void getBindCount() throws Exception {
assertEquals(support.getBindCount(), 1);
}
@Test
public void getAsOfPredicate() throws Exception {
String asOfPredicate = support.getAsOfPredicate("t0", "sys_period");
assertEquals(asOfPredicate, "t0.sys_period @> ?::timestamptz");
}
@Test
public void getSysPeriodLower() throws Exception {
String lower = support.getSysPeriodLower("t0", "sys_period");
assertEquals(lower, "lower(t0.sys_period)");
}
@Test
public void getSysPeriodUpper() throws Exception {
String upper = support.getSysPeriodUpper("t0", "sys_period");
assertEquals(upper, "upper(t0.sys_period)");
}
}
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.expression;
import com.avaje.ebean.Expr;
import com.avaje.ebean.Expression;
import com.avaje.tests.model.basic.Order;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -75,4 +76,54 @@ public class LogicExpressionTest extends BaseExpressionTest {
assertThat(and(eq("a", 10), eq("b", 10))
.isSameByBind(and(eq("a", 10), eq("c", 20)))).isFalse();
}
@Test
public void nestedPath_when_notNested() {
LogicExpression and = and(eq("orderDate", 10), eq("shipDate", 10));
and.nestedPath(getBeanDescriptor(Order.class));
assertThat(and.expOne).isInstanceOf(SimpleExpression.class);
assertThat(and.expTwo).isInstanceOf(SimpleExpression.class);
}
@Test
public void nestedPath_when_nestedSame() {
LogicExpression and = and(eq("details.orderQty", 10), eq("details.unitPrice", 10));
String path = and.nestedPath(getBeanDescriptor(Order.class));
assertThat(path).isEqualTo("details");
assertThat(and.expOne).isInstanceOf(SimpleExpression.class);
assertThat(and.expTwo).isInstanceOf(SimpleExpression.class);
}
@Test
public void nestedPath_when_nestedDifferent() {
LogicExpression and = and(eq("details.orderQty", 10), eq("shipments.shipTime", 10));
String path = and.nestedPath(getBeanDescriptor(Order.class));
assertThat(path).isNull();
assertThat(and.expOne).isInstanceOf(NestedPathWrapperExpression.class);
assertThat(((NestedPathWrapperExpression)and.expOne).nestedPath).isEqualTo("details");
assertThat(and.expTwo).isInstanceOf(NestedPathWrapperExpression.class);
assertThat(((NestedPathWrapperExpression)and.expTwo).nestedPath).isEqualTo("shipments");
}
@Test
public void nestedPath_when_oneNested() {
LogicExpression and = and(eq("details.orderQty", 10), eq("orderDate", 10));
String path = and.nestedPath(getBeanDescriptor(Order.class));
assertThat(path).isNull();
assertThat(and.expOne).isInstanceOf(NestedPathWrapperExpression.class);
assertThat(((NestedPathWrapperExpression)and.expOne).nestedPath).isEqualTo("details");
assertThat(and.expTwo).isInstanceOf(SimpleExpression.class);
}
}
@@ -0,0 +1,148 @@
package com.avaje.ebeaninternal.server.expression;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.tests.model.basic.Order;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class PrepareDocNestedTest extends BaseTestCase {
@Test
public void prepare() throws Exception {
ExpressionList<Order> where = Ebean.find(Order.class)
.where()
.gt("details.orderQty", 1)
.query().where();
DefaultExpressionList<?> exp = (DefaultExpressionList<?>)where;
PrepareDocNested.prepare(exp, getBeanDescriptor(Order.class));
List<SpiExpression> underlyingList = exp.getUnderlyingList();
assertEquals(underlyingList.size(), 1);
assertEquals(exp.allDocNestedPath, "details");
}
@Test
public void prepare_when_multipleOfSamePath() throws Exception {
ExpressionList<Order> where = Ebean.find(Order.class)
.where()
.gt("details.orderQty", 1)
.gt("details.unitPrice", 1)
.query().where();
DefaultExpressionList<?> exp = (DefaultExpressionList<?>)where;
PrepareDocNested.prepare(exp, getBeanDescriptor(Order.class));
List<SpiExpression> underlyingList = exp.getUnderlyingList();
assertEquals(underlyingList.size(), 2);
assertEquals(exp.allDocNestedPath, "details");
}
@Test
public void prepare_when_mixed() throws Exception {
ExpressionList<Order> where = Ebean.find(Order.class)
.where()
.gt("customer.id", 1)
.gt("details.orderQty", 1)
.gt("details.unitPrice", 1)
.query().where();
DefaultExpressionList<?> exp = (DefaultExpressionList<?>)where;
PrepareDocNested.prepare(exp, getBeanDescriptor(Order.class));
List<SpiExpression> underlyingList = exp.getUnderlyingList();
assertEquals(underlyingList.size(), 2);
assertNull(exp.allDocNestedPath);
DefaultExpressionList<?> second = (DefaultExpressionList<?>)underlyingList.get(1);
assertEquals(second.allDocNestedPath, "details");
}
@Test
public void prepare_when_nestedJunction() throws Exception {
ExpressionList<Order> where = Ebean.find(Order.class)
.where()
.not()
.gt("customer.id", 1)
.gt("details.orderQty", 1)
.gt("details.unitPrice", 1)
.query().where();
DefaultExpressionList<?> exp = (DefaultExpressionList<?>)where;
PrepareDocNested.prepare(exp, getBeanDescriptor(Order.class));
List<SpiExpression> underlyingList = exp.getUnderlyingList();
assertEquals(underlyingList.size(), 1);
assertNull(exp.allDocNestedPath);
JunctionExpression<?> junction = (JunctionExpression<?>)underlyingList.get(0);
List<SpiExpression> junctionUnderlying = junction.exprList.getUnderlyingList();
JunctionExpression<?> nestedNestedPath = (JunctionExpression)junctionUnderlying.get(1);
assertEquals(nestedNestedPath.exprList.allDocNestedPath, "details");
}
@Test
public void prepare_when_nestedMultiple() throws Exception {
ExpressionList<Order> where = Ebean.find(Order.class)
.where()
.isNotNull("shipments.shipTime")
.gt("details.orderQty", 1)
.gt("details.unitPrice", 1)
.query().where();
DefaultExpressionList<?> exp = (DefaultExpressionList<?>)where;
PrepareDocNested.prepare(exp, getBeanDescriptor(Order.class));
List<SpiExpression> underlyingList = exp.getUnderlyingList();
assertEquals(underlyingList.size(), 2);
assertNull(exp.allDocNestedPath);
DefaultExpressionList<?> shipExpr = (DefaultExpressionList<?>)underlyingList.get(0);
assertEquals(shipExpr.allDocNestedPath, "shipments");
DefaultExpressionList<?> detailsExpr = (DefaultExpressionList<?>)underlyingList.get(1);
assertEquals(detailsExpr.allDocNestedPath, "details");
}
@Test
public void prepare_when_manyMixed() throws Exception {
ExpressionList<Order> where = Ebean.find(Order.class)
.where()
.gt("customer.id", 1) // 0
.isNotNull("shipments.shipTime") // shipments 0
.isNotNull("status") // 1
.gt("details.orderQty", 1) // details 0
.isNotNull("orderDate") // 2
.gt("details.unitPrice", 1) // details 1
.query().where();
DefaultExpressionList<?> exp = (DefaultExpressionList<?>)where;
PrepareDocNested.prepare(exp, getBeanDescriptor(Order.class));
List<SpiExpression> underlyingList = exp.getUnderlyingList();
assertEquals(underlyingList.size(), 5);
assertNull(exp.allDocNestedPath);
DefaultExpressionList<?> shipExpr = (DefaultExpressionList<?>)underlyingList.get(3);
assertEquals(shipExpr.allDocNestedPath, "shipments");
DefaultExpressionList<?> detailsExpr = (DefaultExpressionList<?>)underlyingList.get(4);
assertEquals(detailsExpr.allDocNestedPath, "details");
}
}
@@ -0,0 +1,81 @@
package com.avaje.ebeaninternal.server.query;
import com.avaje.ebean.Version;
import org.junit.Test;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
public class OrderVersionDescTest {
private final long now = System.currentTimeMillis();
@Test
public void sort() {
Version<?> atNull = atNull();
Version<?> at100 = at(100);
Version<?> at200 = at(200);
Version<?> at300 = at(300);
List<Version<?>> versions = new ArrayList<Version<?>>();
versions.add(at200);
versions.add(atNull);
versions.add(at300);
versions.add(at100);
Collections.sort(versions, OrderVersionDesc.INSTANCE);
assertThat(versions.get(0)).isSameAs(at300);
assertThat(versions.get(1)).isSameAs(at200);
assertThat(versions.get(2)).isSameAs(at100);
assertThat(versions.get(3)).isSameAs(atNull);
}
@Test
public void compare_lt() {
assertEquals(OrderVersionDesc.INSTANCE.compare(at(0), at(1)), 1);
}
@Test
public void compare_gt() {
assertEquals(OrderVersionDesc.INSTANCE.compare(at(2), at(1)), -1);
}
@Test
public void compare_eq() {
assertEquals(OrderVersionDesc.INSTANCE.compare(at(1), at(1)), 0);
}
@Test
public void compare_nullFirst() {
assertEquals(OrderVersionDesc.INSTANCE.compare(atNull(), at(1)), 1);
}
@Test
public void compare_nullLast() {
assertEquals(OrderVersionDesc.INSTANCE.compare(at(0), atNull()), -1);
}
private Version<?> atNull() {
return new Version();
}
private Version at(long diff) {
Timestamp timestamp = new Timestamp(now + diff);
Version ver = new Version();
ver.setStart(timestamp);
return ver;
}
}
@@ -0,0 +1,82 @@
package com.avaje.ebeaninternal.server.query;
import org.junit.Test;
import static org.junit.Assert.*;
public class SplitNameTest {
@Test
public void add() throws Exception {
assertEquals(SplitName.add("a","b"), "a.b");
assertEquals(SplitName.add("a","b.c"), "a.b.c");
}
@Test
public void count() throws Exception {
assertEquals(SplitName.count("a"), 0);
assertEquals(SplitName.count("a.b"), 1);
assertEquals(SplitName.count("a.b.c"), 2);
assertEquals(SplitName.count("a.b.c.foo"), 3);
}
@Test
public void parent() throws Exception {
assertNull(SplitName.parent("a"));
assertEquals(SplitName.parent("a.b"), "a");
assertEquals(SplitName.parent("a.b.c"), "a.b");
assertNull(SplitName.parent(null));
}
@Test
public void split() throws Exception {
String[] split = SplitName.split("a.b.c");
assertEquals(split[0], "a.b");
assertEquals(split[1], "c");
}
@Test
public void begin_when_one() throws Exception {
assertEquals(SplitName.begin("a"), "a");
}
@Test
public void begin_when_both() throws Exception {
assertEquals(SplitName.begin("a.b"), "a");
}
@Test
public void begin_when_multi() throws Exception {
assertEquals(SplitName.begin("a.b.c"), "a");
}
@Test
public void splitBegin_when_both() throws Exception {
String[] split = SplitName.splitBegin("a.b");
assertEquals(split[0], "a");
assertEquals(split[1], "b");
}
@Test
public void splitBegin_when_bothPlus() throws Exception {
String[] split = SplitName.splitBegin("a.b.c");
assertEquals(split[0], "a");
assertEquals(split[1], "b.c");
}
@Test
public void splitBegin_when_one() throws Exception {
String[] split = SplitName.splitBegin("a");
assertEquals(split[0], "a");
assertNull(split[1]);
}
}
@@ -2,7 +2,6 @@ package com.avaje.ebeaninternal.server.querydefn;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.FetchConfig;
import com.avaje.tests.model.basic.Order;
import org.junit.Test;
@@ -10,6 +9,28 @@ import static org.assertj.core.api.Assertions.assertThat;
public class DefaultOrmQueryTest {
@Test
public void checkForId_when_eqId_then_translatedTo_setId() {
DefaultOrmQuery<Order> q1 = (DefaultOrmQuery<Order>)Ebean.find(Order.class).where().eq("id", 42).query();
assertThat(q1.getWhereExpressions()).isNotNull();
assertThat(q1.getId()).isNull();
q1.checkIdEqualTo();
assertThat(q1.getId()).isEqualTo(42);
assertThat(q1.getWhereExpressions()).isNull();
}
@Test
public void checkForId_when_idEq_ok() {
DefaultOrmQuery<Order> q1 = (DefaultOrmQuery<Order>)Ebean.find(Order.class).where().idEq(42).query();
assertThat(q1.getId()).isEqualTo(42);
q1.checkIdEqualTo();
assertThat(q1.getId()).isEqualTo(42);
}
@Test
public void when_addWhere_then_planChanges() {
@@ -41,6 +41,6 @@ public class TestFetchId extends BaseTestCase {
List<Object> idList = futureIds.get();
Assert.assertTrue("same instance", partial == idList);
Assert.assertTrue("sz > 0", ids.size() > 0);
Assert.assertTrue("sz > 0", !ids.isEmpty());
}
}
@@ -34,7 +34,7 @@ public class TestInheritRef extends BaseTestCase {
.setAutoTune(false)
.findList();
Assert.assertTrue(list.size() > 0);
Assert.assertTrue(!list.isEmpty());
Truck foundTruck = null;
int found = 0;
@@ -29,7 +29,7 @@ public class TestLazyLoadInCache extends BaseTestCase {
.orderBy().asc("id")
.findMap();
assertTrue(map.size() > 0);
assertTrue(!map.isEmpty());
Object id = map.keySet().iterator().next();
@@ -101,7 +101,7 @@ public class TestLimitQuery extends BaseTestCase {
List<Order> list = query.findList();
Assert.assertTrue("sz > 0", list.size() > 0);
Assert.assertTrue("sz > 0", !list.isEmpty());
String sql = query.getGeneratedSql();
boolean hasDetailsJoin = sql.contains("join o_order_detail");
@@ -76,7 +76,7 @@ public class TestM2MVanilla extends BaseTestCase {
Query<MUser> rolesQuery = Ebean.find(MUser.class).where().in("roles", roleList).query();
List<MUser> userInRolesList = rolesQuery.findList();
Assert.assertTrue(userInRolesList.size() > 0);
Assert.assertTrue(!userInRolesList.isEmpty());
List<MUser> list = Ebean.find(MUser.class)
.where().in("roles", roleList)
@@ -24,7 +24,7 @@ public class TestManyLazyLoad extends BaseTestCase {
awaitL2Cache();
List<Order> list = Ebean.find(Order.class).order().asc("id").findList();
assertTrue(list.size() + " > 0", list.size() > 0);
assertTrue(list.size() + " > 0", !list.isEmpty());
// just use the first one
Order order = list.get(0);
@@ -23,7 +23,7 @@ public class TestOrderByAnnotation extends BaseTestCase {
Customer customer = Ebean.find(Customer.class, custTest.getId());
List<Order> orders = customer.getOrders();
Assert.assertTrue(orders.size() > 0);
Assert.assertTrue(!orders.isEmpty());
Query<Order> q1 = Ebean.find(Order.class)
@@ -47,7 +47,7 @@ public class TestReadOnlyPropagation extends BaseTestCase {
Assert.assertTrue(!bc.isPopulated());
bc.size();
Assert.assertTrue(bc.size() > 0);
Assert.assertTrue(!bc.isEmpty());
Assert.assertTrue(bc.isReadOnly());
Assert.assertTrue(bc.isPopulated());
try {
@@ -47,7 +47,7 @@ public class TestSharedInstancePropagation extends BaseTestCase {
bc.size();
assertTrue(bc.isPopulated());
assertTrue(bc.size() > 0);
assertTrue(!bc.isEmpty());
OrderDetail detail = details.get(0);
assertTrue(Ebean.getBeanState(detail).isReadOnly());
@@ -23,7 +23,7 @@ public class TestWhereAnnotation extends BaseTestCase {
Customer customer = Ebean.find(Customer.class, custTest.getId());
List<Order> orders = customer.getOrders();
Assert.assertTrue(orders.size() > 0);
Assert.assertTrue(!orders.isEmpty());
Query<Customer> q1 = Ebean.find(Customer.class).setUseCache(false).fetch("orders").where()
.idEq(1).query();
@@ -48,7 +48,7 @@ public class TestBasicLazy extends BaseTestCase {
// some contacts
Customer c = Ebean.find(Customer.class).setId(1).findUnique();
Assert.assertNotNull(c.getContacts());
Assert.assertTrue("no contacts on test customer 1", c.getContacts().size() > 0);
Assert.assertTrue("no contacts on test customer 1", !c.getContacts().isEmpty());
// start transaction so we have a "long running" persistence context
Transaction tx = Ebean.beginTransaction();
@@ -56,7 +56,7 @@ public class TestBasicLazy extends BaseTestCase {
List<Order> order = Ebean.find(Order.class).where(Expr.eq("customer.id", 1)).findList();
Assert.assertNotNull(order);
Assert.assertTrue(order.size() > 0);
Assert.assertTrue(!order.isEmpty());
Customer customer = order.get(0).getCustomer();
Assert.assertNotNull(customer);
@@ -66,7 +66,7 @@ public class TestBasicLazy extends BaseTestCase {
List<Contact> contacts = customer.getContacts();
Assert.assertNotNull(contacts);
Assert.assertTrue("contacts not lazily fetched", contacts.size() > 0);
Assert.assertTrue("contacts not lazily fetched", !contacts.isEmpty());
} finally {
tx.commit();
}
@@ -196,7 +196,7 @@ public class TestBasicLazy extends BaseTestCase {
MyTestDataSourcePoolListener.SLEEP_AFTER_BORROW = 0;
}
if (exceptions.size() > 0) {
if (!exceptions.isEmpty()) {
System.err.println("Seen Exceptions:");
for (Throwable exception : exceptions) {
exception.printStackTrace();
@@ -35,7 +35,7 @@ public class TestLazyJoin2 extends BaseTestCase {
Order o0 = l0.get(0);
Customer c0 = o0.getCustomer();
List<Contact> contacts = c0.getContacts();
Assert.assertTrue(contacts.size() > 0);
Assert.assertTrue(!contacts.isEmpty());
// query 1) find order (status, shipDate)
// query 2) find orderDetail (quantity, price) join product (sku, name)
@@ -60,7 +60,7 @@ public class TestQueryJoin extends BaseTestCase {
System.out.println(billingAddress);
billingAddress.getLine1();
Assert.assertTrue(list.size() > 0);
Assert.assertTrue(!list.isEmpty());
}
}
@@ -68,7 +68,7 @@ public class TestSecondaryQueries extends BaseTestCase {
spiQuery.setLogSecondaryQuery(true);
List<Order> list = query.findList();
Assert.assertTrue(list.size() > 0);
Assert.assertTrue(!list.isEmpty());
for (Order order : list) {
order.getCustomer().getStatus();
}
+1 -1
View File
@@ -29,7 +29,7 @@ public class TestQueryCache extends BaseTestCase {
BeanCollection<Customer> bc = (BeanCollection<Customer>) list;
Assert.assertFalse(bc.isReadOnly());
Assert.assertFalse(bc.isEmpty());
Assert.assertTrue(list.size() > 0);
Assert.assertTrue(!list.isEmpty());
Assert.assertTrue(Ebean.getBeanState(list.get(0)).isReadOnly());
List<Customer> list2 = Ebean.find(Customer.class).setUseQueryCache(true).setReadOnly(true).where()
@@ -38,7 +38,7 @@ public class TestQueryCacheCountry extends BaseTestCase {
.findList();
assertEquals(1, queryCache.getStatistics(false).getSize());
assertTrue(countryList0.size() > 0);
assertTrue(!countryList0.isEmpty());
List<Country> countryList1 = Ebean.find(Country.class)
.setUseQueryCache(true)
@@ -70,6 +70,8 @@ public class TestHistoryInsert extends BaseTestCase {
assertThat(earlyVersion.getName()).isEqualTo("Jim");
assertThat(earlyVersion.getEmail()).isEqualTo("one@email.com");
Ebean.find(User.class).setId(user.getId()).asOf(afterInsert).findUnique();
history = fetchHistory(user);
assertThat(history).hasSize(3);

Some files were not shown because too many files have changed in this diff Show More