mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
62
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76033385dc | ||
|
|
b61cd30e29 | ||
|
|
18edaf877b | ||
|
|
f055a0d0cb | ||
|
|
a22574b0c9 | ||
|
|
3130f751f5 | ||
|
|
c83606c1da | ||
|
|
439858b259 | ||
|
|
3d3948aa8b | ||
|
|
27045baf44 | ||
|
|
5c6d6608ef | ||
|
|
2fcaab44c6 | ||
|
|
69bf1758bc | ||
|
|
af554410fd | ||
|
|
ae7827c483 | ||
|
|
4df2d4020e | ||
|
|
1aeffd31e0 | ||
|
|
5c59066ff8 | ||
|
|
a2bf8a0e46 | ||
|
|
3ef77ea410 | ||
|
|
fe2c2e5331 | ||
|
|
3d91a4f927 | ||
|
|
165d4aca92 | ||
|
|
076f8a03b9 | ||
|
|
ca944eb756 | ||
|
|
769e4bb488 | ||
|
|
0d456b2ad6 | ||
|
|
39e913cbe2 | ||
|
|
ae38672e02 | ||
|
|
8a8b36ec1e | ||
|
|
62aca1e77b | ||
|
|
f0b80068b6 | ||
|
|
5721fe3807 | ||
|
|
afffb88a29 | ||
|
|
4c3a125192 | ||
|
|
f8804f8d18 | ||
|
|
4848c24a0a | ||
|
|
4c3d1d9edd | ||
|
|
42b112c3d7 | ||
|
|
59996da07a | ||
|
|
32d4b83a86 | ||
|
|
ca824c5e0d | ||
|
|
b2f7664316 | ||
|
|
062f7a281f | ||
|
|
019ce4e614 | ||
|
|
6b490ed8b1 | ||
|
|
1bee6fcae5 | ||
|
|
097552b8d3 | ||
|
|
9fbfca5f73 | ||
|
|
9e70ba915f | ||
|
|
a1a5dcd035 | ||
|
|
d2b048b0b4 | ||
|
|
4272c17689 | ||
|
|
11723e3a32 | ||
|
|
237cdcdd3d | ||
|
|
e96bee74ae | ||
|
|
c8b57c3302 | ||
|
|
f65ea96b89 | ||
|
|
a1bd0b469a | ||
|
|
fd5563e3da | ||
|
|
f9156ede55 | ||
|
|
10d2d591db |
+2
-1
@@ -1,5 +1,6 @@
|
||||
*.autofetch
|
||||
*.sql
|
||||
*create-all.sql
|
||||
*drop-all.sql
|
||||
*.orig
|
||||
.classpath
|
||||
.project
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>6.16.3</version>
|
||||
<version>6.18.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>avaje-ebeanorm</name>
|
||||
|
||||
@@ -71,7 +71,7 @@ class DRawSqlParser {
|
||||
|
||||
preFrom = trimSelectKeyword(preFrom);
|
||||
|
||||
return new Sql(sql.hashCode(), preFrom, preWhere, whereExprAnd, preHaving, havingExprAnd, orderByPrefix, orderBySql, (distinctPos > -1));
|
||||
return new Sql(sql, preFrom, preWhere, whereExprAnd, preHaving, havingExprAnd, orderByPrefix, orderBySql, (distinctPos > -1));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -249,4 +249,22 @@ public class FetchConfig implements Serializable {
|
||||
return queryAll;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
FetchConfig that = (FetchConfig) o;
|
||||
if (lazyBatchSize != that.lazyBatchSize) return false;
|
||||
if (queryBatchSize != that.queryBatchSize) return false;
|
||||
return queryAll == that.queryAll;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = lazyBatchSize;
|
||||
result = 92821 * result + queryBatchSize;
|
||||
result = 92821 * result + (queryAll ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +206,14 @@ public final class RawSql implements Serializable {
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key;
|
||||
*/
|
||||
public Key getKey() {
|
||||
boolean parsed = sql != null && sql.parsed;
|
||||
String unParsedSql = (sql == null) ? "" : sql.unparsedSql;
|
||||
return new Key(parsed, unParsedSql, columnMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resultSet if this is a ResultSet based RawSql.
|
||||
@@ -221,16 +229,6 @@ public final class RawSql implements Serializable {
|
||||
return columnMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hash for this query.
|
||||
*/
|
||||
public int queryHash() {
|
||||
if (resultSet != null) {
|
||||
return 31 * columnMapping.queryHash();
|
||||
}
|
||||
return 31 * sql.queryHash() + columnMapping.queryHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the sql part of the query. For parsed RawSql the sql is broken
|
||||
* up so that Ebean can insert extra WHERE and HAVING expressions into the
|
||||
@@ -260,13 +258,10 @@ public final class RawSql implements Serializable {
|
||||
|
||||
private final boolean distinct;
|
||||
|
||||
private final int queryHashCode;
|
||||
|
||||
/**
|
||||
* Construct for unparsed SQL.
|
||||
*/
|
||||
protected Sql(String unparsedSql) {
|
||||
this.queryHashCode = unparsedSql.hashCode();
|
||||
this.parsed = false;
|
||||
this.unparsedSql = unparsedSql;
|
||||
this.preFrom = null;
|
||||
@@ -282,12 +277,11 @@ public final class RawSql implements Serializable {
|
||||
/**
|
||||
* Construct for parsed SQL.
|
||||
*/
|
||||
protected Sql(int queryHashCode, String preFrom, String preWhere, boolean andWhereExpr,
|
||||
protected Sql(String unparsedSql, String preFrom, String preWhere, boolean andWhereExpr,
|
||||
String preHaving, boolean andHavingExpr, String orderByPrefix, String orderBy, boolean distinct) {
|
||||
|
||||
this.queryHashCode = queryHashCode;
|
||||
this.unparsedSql = unparsedSql;
|
||||
this.parsed = true;
|
||||
this.unparsedSql = null;
|
||||
this.preFrom = preFrom;
|
||||
this.preHaving = preHaving;
|
||||
this.preWhere = preWhere;
|
||||
@@ -298,13 +292,6 @@ public final class RawSql implements Serializable {
|
||||
this.distinct = distinct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash for this query.
|
||||
*/
|
||||
public int queryHash() {
|
||||
return queryHashCode;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if (!parsed) {
|
||||
return "unparsed[" + unparsedSql + "]";
|
||||
@@ -406,13 +393,10 @@ public final class RawSql implements Serializable {
|
||||
|
||||
private final boolean immutable;
|
||||
|
||||
private final int queryHashCode;
|
||||
|
||||
/**
|
||||
* Construct from parsed sql where the columns have been identified.
|
||||
*/
|
||||
protected ColumnMapping(List<Column> columns) {
|
||||
this.queryHashCode = 0;
|
||||
this.immutable = false;
|
||||
this.parsed = true;
|
||||
this.propertyMap = null;
|
||||
@@ -428,7 +412,6 @@ public final class RawSql implements Serializable {
|
||||
* Construct for unparsed sql.
|
||||
*/
|
||||
protected ColumnMapping() {
|
||||
this.queryHashCode = 0;
|
||||
this.immutable = false;
|
||||
this.parsed = false;
|
||||
this.propertyMap = null;
|
||||
@@ -443,17 +426,13 @@ public final class RawSql implements Serializable {
|
||||
this.immutable = false;
|
||||
this.parsed = false;
|
||||
this.propertyMap = null;
|
||||
//this.propertyColumnMap = null;
|
||||
this.dbColumnMap = new LinkedHashMap<String, Column>();
|
||||
|
||||
int hc = 31;
|
||||
|
||||
int pos = 0;
|
||||
for (String prop : propertyNames) {
|
||||
hc = 31 * hc + prop.hashCode();
|
||||
dbColumnMap.put(prop, new Column(pos++, prop, null, prop));
|
||||
}
|
||||
propertyColumnMap = dbColumnMap;
|
||||
this.queryHashCode = hc;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -464,20 +443,28 @@ public final class RawSql implements Serializable {
|
||||
this.parsed = parsed;
|
||||
this.dbColumnMap = dbColumnMap;
|
||||
|
||||
int hc = ColumnMapping.class.getName().hashCode();
|
||||
|
||||
HashMap<String, Column> pcMap = new HashMap<String, Column>();
|
||||
HashMap<String, String> pMap = new HashMap<String, String>();
|
||||
|
||||
for (Column c : dbColumnMap.values()) {
|
||||
pMap.put(c.getPropertyName(), c.getDbColumn());
|
||||
pcMap.put(c.getPropertyName(), c);
|
||||
hc = 31 * hc + ((c.getPropertyName() == null) ? 0 : c.getPropertyName().hashCode());
|
||||
hc = 31 * hc + ((c.getDbColumn() == null) ? 0 : c.getDbColumn().hashCode());
|
||||
}
|
||||
this.propertyMap = Collections.unmodifiableMap(pMap);
|
||||
this.propertyColumnMap = Collections.unmodifiableMap(pcMap);
|
||||
this.queryHashCode = hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ColumnMapping that = (ColumnMapping) o;
|
||||
return dbColumnMap.equals(that.dbColumnMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return dbColumnMap.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -521,16 +508,6 @@ public final class RawSql implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query hash for this column mapping.
|
||||
*/
|
||||
public int queryHash() {
|
||||
if (queryHashCode == 0) {
|
||||
throw new RuntimeException("Bug: queryHashCode == 0");
|
||||
}
|
||||
return queryHashCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the Columns where supplied by parsing the sql select
|
||||
* clause.
|
||||
@@ -585,6 +562,9 @@ public final class RawSql implements Serializable {
|
||||
* <p>
|
||||
* For example modify all mappings with table alias "c" to have the path prefix "customer".
|
||||
* </p>
|
||||
* <p>
|
||||
* For the "Root type" you don't need to specify a tableAliasMapping.
|
||||
* </p>
|
||||
*/
|
||||
public void tableAliasMapping(String tableAlias, String path) {
|
||||
|
||||
@@ -645,6 +625,27 @@ public final class RawSql implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Column that = (Column) o;
|
||||
if (indexPos != that.indexPos) return false;
|
||||
if (!dbColumn.equals(that.dbColumn)) return false;
|
||||
if (dbAlias != null ? !dbAlias.equals(that.dbAlias) : that.dbAlias != null) return false;
|
||||
return propertyName != null ? propertyName.equals(that.propertyName) : that.propertyName == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = indexPos;
|
||||
result = 31 * result + dbColumn.hashCode();
|
||||
result = 31 * result + (dbAlias != null ? dbAlias.hashCode() : 0);
|
||||
result = 31 * result + (propertyName != null ? propertyName.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return dbColumn + "->" + propertyName;
|
||||
}
|
||||
@@ -696,4 +697,39 @@ public final class RawSql implements Serializable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A key for the RawSql object using for the query plan.
|
||||
*/
|
||||
public static final class Key {
|
||||
|
||||
private final boolean parsed;
|
||||
private final ColumnMapping columnMapping;
|
||||
private final String unParsedSql;
|
||||
|
||||
Key(boolean parsed, String unParsedSql, ColumnMapping columnMapping) {
|
||||
this.parsed = parsed;
|
||||
this.unParsedSql = unParsedSql;
|
||||
this.columnMapping = columnMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Key that = (Key) o;
|
||||
return parsed == that.parsed
|
||||
&& columnMapping.equals(that.columnMapping)
|
||||
&& unParsedSql.equals(that.unParsedSql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (parsed ? 1 : 0);
|
||||
result = 31 * result + columnMapping.hashCode();
|
||||
result = 31 * result + unParsedSql.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +106,9 @@ public class RawSqlBuilder {
|
||||
* <p>
|
||||
* For example modify all mappings with table alias "c" to have the path prefix "customer".
|
||||
* </p>
|
||||
* <p>
|
||||
* For the "Root type" you don't need to specify a tableAliasMapping.
|
||||
* </p>
|
||||
*/
|
||||
public RawSqlBuilder tableAliasMapping(String tableAlias, String path) {
|
||||
columnMapping.tableAliasMapping(tableAlias, path);
|
||||
|
||||
@@ -125,6 +125,11 @@ public interface BeanCollection<E> extends Serializable {
|
||||
*/
|
||||
void internalAdd(Object bean);
|
||||
|
||||
/**
|
||||
* Add the bean with a check to see if it is already contained.
|
||||
*/
|
||||
void internalAddWithCheck(Object bean);
|
||||
|
||||
/**
|
||||
* Return the number of elements in the List Set or Map.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package com.avaje.ebean.common;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollectionAdd;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -8,17 +12,13 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollectionAdd;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
|
||||
/**
|
||||
* List capable of lazy loading.
|
||||
*/
|
||||
public final class BeanList<E> extends AbstractBeanCollection<E> implements List<E>, BeanCollectionAdd {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
/**
|
||||
* The underlying List implementation.
|
||||
*/
|
||||
@@ -74,6 +74,13 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void internalAddWithCheck(Object bean) {
|
||||
if (list == null || !list.contains(bean)) {
|
||||
internalAdd(bean);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkEmptyLazyLoad() {
|
||||
if (list == null) {
|
||||
list = new ArrayList<E>();
|
||||
@@ -99,11 +106,11 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
private void initAsUntouched() {
|
||||
init(false);
|
||||
}
|
||||
|
||||
|
||||
private void init() {
|
||||
init(true);
|
||||
}
|
||||
|
||||
|
||||
private void init(boolean setTouched) {
|
||||
synchronized (this) {
|
||||
if (list == null) {
|
||||
@@ -134,7 +141,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
public Collection<E> getActualDetails() {
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<?> getActualEntries() {
|
||||
return list;
|
||||
|
||||
@@ -66,7 +66,18 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
map.put((K) key, (E) bean);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void internalPutWithCheck(Object key, Object bean) {
|
||||
if (map == null || !map.containsKey(key)) {
|
||||
internalPut(key, bean);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void internalAddWithCheck(Object bean) {
|
||||
throw new RuntimeException("Not allowed for map");
|
||||
}
|
||||
|
||||
public void internalAdd(Object bean) {
|
||||
throw new RuntimeException("Not allowed for map");
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package com.avaje.ebean.common;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollectionAdd;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollectionAdd;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
|
||||
/**
|
||||
* Set capable of lazy loading.
|
||||
*/
|
||||
@@ -57,6 +57,13 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
set.add((E) bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void internalAddWithCheck(Object bean) {
|
||||
if (set == null || !set.contains(bean)) {
|
||||
internalAdd(bean);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void internalAdd(Object bean) {
|
||||
if (set == null) {
|
||||
@@ -107,11 +114,11 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
private void initAsUntouched() {
|
||||
init(false);
|
||||
}
|
||||
|
||||
|
||||
private void init() {
|
||||
init(true);
|
||||
}
|
||||
|
||||
|
||||
private void init(boolean setTouched) {
|
||||
synchronized (this) {
|
||||
if (set == null) {
|
||||
|
||||
@@ -23,11 +23,6 @@ public class DbMigrationConfig {
|
||||
*/
|
||||
protected boolean generate;
|
||||
|
||||
/**
|
||||
* Set to true to suppress the output of the rollback script.
|
||||
*/
|
||||
protected boolean suppressRollback;
|
||||
|
||||
/**
|
||||
* The migration version name (typically FlywayDb compatible).
|
||||
* <p>
|
||||
@@ -61,35 +56,17 @@ public class DbMigrationConfig {
|
||||
*/
|
||||
protected String modelPath = "model";
|
||||
|
||||
/**
|
||||
* Subdirectory the drop ddl scripts go into.
|
||||
*/
|
||||
protected String dropPath = "drop";
|
||||
|
||||
/**
|
||||
* Subdirectory the rollback ddl scripts go into.
|
||||
*/
|
||||
protected String rollbackPath = "rollback";
|
||||
|
||||
/**
|
||||
* Apply script suffix.
|
||||
*/
|
||||
protected String applySuffix = ".sql";
|
||||
|
||||
/**
|
||||
* Default drop script suffix to ddl so that it isn't picked up by FlywayDb.
|
||||
*/
|
||||
protected String dropSuffix = ".drop.ddl";
|
||||
|
||||
/**
|
||||
* Default rollback script suffix to ddl so that it isn't picked up by FlywayDb.
|
||||
*/
|
||||
protected String rollbackSuffix = ".rollback.ddl";
|
||||
|
||||
protected String modelSuffix = ".model.xml";
|
||||
|
||||
protected boolean includeGeneratedFileComment;
|
||||
|
||||
/**
|
||||
* The version of a pending drop that should be generated as the next migration.
|
||||
*/
|
||||
protected String generatePendingDrop;
|
||||
|
||||
/**
|
||||
* Return the DB platform to generate migration DDL for.
|
||||
*
|
||||
@@ -140,34 +117,6 @@ public class DbMigrationConfig {
|
||||
this.modelPath = modelPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the relative path for the drop ddl scripts (defaults to drop).
|
||||
*/
|
||||
public String getDropPath() {
|
||||
return dropPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the relative path for the drop ddl scripts (defaults to drop).
|
||||
*/
|
||||
public void setDropPath(String dropPath) {
|
||||
this.dropPath = dropPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the relative path for the rollback ddl scripts (defaults to rollback).
|
||||
*/
|
||||
public String getRollbackPath() {
|
||||
return rollbackPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the relative path for the rollback ddl scripts (defaults to rollback).
|
||||
*/
|
||||
public void setRollbackPath(String rollbackPath) {
|
||||
this.rollbackPath = rollbackPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the model suffix (defaults to model.xml)
|
||||
*/
|
||||
@@ -182,20 +131,6 @@ public class DbMigrationConfig {
|
||||
this.modelSuffix = modelSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the rollback script should not be output.
|
||||
*/
|
||||
public boolean isSuppressRollback() {
|
||||
return suppressRollback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to suppress the output of the rollback script.
|
||||
*/
|
||||
public void setSuppressRollback(boolean suppressRollback) {
|
||||
this.suppressRollback = suppressRollback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the apply script suffix (defaults to sql).
|
||||
*/
|
||||
@@ -210,34 +145,6 @@ public class DbMigrationConfig {
|
||||
this.applySuffix = applySuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the drop script suffix (defaults to ddl so that it isn't picked up by FlywayDb).
|
||||
*/
|
||||
public String getDropSuffix() {
|
||||
return dropSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the drop script suffix (defaults to ddl so that it isn't picked up by FlywayDb).
|
||||
*/
|
||||
public void setDropSuffix(String dropSuffix) {
|
||||
this.dropSuffix = dropSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the rollback script suffix (defaults to ddl so that it isn't picked up by FlywayDb).
|
||||
*/
|
||||
public String getRollbackSuffix() {
|
||||
return rollbackSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rollback script suffix (defaults to ddl so that it isn't picked up by FlywayDb).
|
||||
*/
|
||||
public void setRollbackSuffix(String rollbackSuffix) {
|
||||
this.rollbackSuffix = rollbackSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the generated file comment should be included.
|
||||
*/
|
||||
@@ -252,6 +159,20 @@ public class DbMigrationConfig {
|
||||
this.includeGeneratedFileComment = includeGeneratedFileComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the migration version (or "next") to generate pending drops for.
|
||||
*/
|
||||
public String getGeneratePendingDrop() {
|
||||
return generatePendingDrop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the migration version (or "next") to generate pending drops for.
|
||||
*/
|
||||
public void setGeneratePendingDrop(String generatePendingDrop) {
|
||||
this.generatePendingDrop = generatePendingDrop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the migration version.
|
||||
* <p>
|
||||
@@ -275,8 +196,6 @@ public class DbMigrationConfig {
|
||||
* into a single directory.
|
||||
*/
|
||||
public void singleDirectory() {
|
||||
this.dropPath = "";
|
||||
this.rollbackPath = "";
|
||||
this.modelPath = "";
|
||||
}
|
||||
|
||||
@@ -290,17 +209,13 @@ public class DbMigrationConfig {
|
||||
singleDirectory();
|
||||
} else {
|
||||
modelPath = properties.get("migration.modelPath", modelPath);
|
||||
rollbackPath = properties.get("migration.rollbackPath", rollbackPath);
|
||||
dropPath = properties.get("migration.dropPath", dropPath);
|
||||
}
|
||||
applySuffix = properties.get("migration.applySuffix", applySuffix);
|
||||
dropSuffix = properties.get("migration.dropSuffix", dropSuffix);
|
||||
rollbackSuffix = properties.get("migration.rollbackSuffix", rollbackSuffix);
|
||||
modelSuffix = properties.get("migration.modelSuffix", modelSuffix);
|
||||
includeGeneratedFileComment = properties.getBoolean("migration.includeGeneratedFileComment", includeGeneratedFileComment);
|
||||
generatePendingDrop = properties.get("migration.generatePendingDrop", generatePendingDrop);
|
||||
|
||||
platform = properties.getEnum(DbPlatformName.class, "migration.platform", platform);
|
||||
suppressRollback = properties.getBoolean("migration.suppressRollback", suppressRollback);
|
||||
|
||||
generate = properties.getBoolean("migration.generate", generate);
|
||||
version = properties.get("migration.version", version);
|
||||
@@ -324,7 +239,6 @@ public class DbMigrationConfig {
|
||||
return generate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called by EbeanServer on start.
|
||||
*
|
||||
|
||||
@@ -387,6 +387,8 @@ public class ServerConfig {
|
||||
*/
|
||||
private boolean expressionEqualsWithNullAsNoop;
|
||||
|
||||
private String jodaLocalTimeMode;
|
||||
|
||||
/**
|
||||
* Construct a Server Configuration for programmatically creating an EbeanServer.
|
||||
*/
|
||||
@@ -1640,6 +1642,20 @@ public class ServerConfig {
|
||||
this.disableClasspathSearch = disableClasspathSearch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mode to use for Joda LocalTime support 'normal' or 'utc'.
|
||||
*/
|
||||
public String getJodaLocalTimeMode() {
|
||||
return jodaLocalTimeMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mode to use for Joda LocalTime support 'normal' or 'utc'.
|
||||
*/
|
||||
public void setJodaLocalTimeMode(String jodaLocalTimeMode) {
|
||||
this.jodaLocalTimeMode = jodaLocalTimeMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Programmatically add classes (typically entities) that this server should
|
||||
* use.
|
||||
@@ -2286,6 +2302,7 @@ public class ServerConfig {
|
||||
dbUuid = DbUuid.BINARY;
|
||||
}
|
||||
localTimeWithNanos = p.getBoolean("localTimeWithNanos", localTimeWithNanos);
|
||||
jodaLocalTimeMode = p.get("jodaLocalTimeMode", jodaLocalTimeMode);
|
||||
|
||||
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", lazyLoadBatchSize);
|
||||
queryBatchSize = p.getInt("queryBatchSize", queryBatchSize);
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlWriter;
|
||||
import com.avaje.ebean.dbmigration.model.CurrentModel;
|
||||
import com.avaje.ebean.dbmigration.model.MConfiguration;
|
||||
import com.avaje.ebean.dbmigration.model.MigrationModel;
|
||||
import com.avaje.ebean.dbmigration.model.MigrationVersion;
|
||||
import com.avaje.ebean.dbmigration.model.ModelContainer;
|
||||
import com.avaje.ebean.dbmigration.model.ModelDiff;
|
||||
import com.avaje.ebean.dbmigration.model.PlatformDdlWriter;
|
||||
@@ -201,46 +202,15 @@ public class DbMigration {
|
||||
if (!online) {
|
||||
DbOffline.setRunningMigration();
|
||||
}
|
||||
|
||||
setDefaults();
|
||||
|
||||
try {
|
||||
Request request = createRequest();
|
||||
|
||||
File migrationDir = getMigrationDirectory();
|
||||
File modelDir = getModelDirectory(migrationDir);
|
||||
|
||||
MigrationModel migrationModel = new MigrationModel(modelDir, migrationConfig.getModelSuffix());
|
||||
ModelContainer migrated = migrationModel.read();
|
||||
|
||||
CurrentModel currentModel = new CurrentModel(server, constraintNaming);
|
||||
ModelContainer current = currentModel.read();
|
||||
|
||||
ModelDiff diff = new ModelDiff(migrated);
|
||||
diff.compareTo(current);
|
||||
|
||||
if (diff.isEmpty()) {
|
||||
logger.info("no changes detected - no migration written");
|
||||
return;
|
||||
}
|
||||
|
||||
// there were actually changes to write
|
||||
Migration dbMigration = diff.getMigration();
|
||||
|
||||
String fullVersion = getFullVersion(migrationModel);
|
||||
|
||||
logger.info("generating migration:{}", fullVersion);
|
||||
if (!writeMigrationXml(dbMigration, modelDir, fullVersion)) {
|
||||
logger.warn("migration already exists, not generating DDL");
|
||||
|
||||
String pendingVersion = generatePendingDrop();
|
||||
if (pendingVersion != null) {
|
||||
generatePendingDrop(request, pendingVersion);
|
||||
} else {
|
||||
if (databasePlatform != null) {
|
||||
// writer needs the current model to provide table/column details for
|
||||
// history ddl generation (triggers, history tables etc)
|
||||
DdlWrite write = new DdlWrite(new MConfiguration(), currentModel.read());
|
||||
PlatformDdlWriter writer = createDdlWriter(databasePlatform, "");
|
||||
writer.processMigration(dbMigration, write, migrationDir , fullVersion);
|
||||
}
|
||||
writeExtraPlatformDdl(fullVersion, currentModel, dbMigration, migrationDir);
|
||||
generateDiff(request);
|
||||
}
|
||||
|
||||
} finally {
|
||||
@@ -251,9 +221,128 @@ public class DbMigration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full version for the migration being generated.
|
||||
* Generate the diff migration.
|
||||
*/
|
||||
private String getFullVersion(MigrationModel migrationModel) {
|
||||
private void generateDiff(Request request) throws IOException {
|
||||
|
||||
List<String> pendingDrops = request.getPendingDrops();
|
||||
if (!pendingDrops.isEmpty()) {
|
||||
logger.info("Pending un-applied drops in versions {}", pendingDrops);
|
||||
}
|
||||
|
||||
Migration migration = request.createDiffMigration();
|
||||
if (migration == null) {
|
||||
logger.info("no changes detected - no migration written");
|
||||
} else {
|
||||
// there were actually changes to write
|
||||
generateMigration(request, migration, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the migration based on the pendingDrops from a prior version.
|
||||
*/
|
||||
private void generatePendingDrop(Request request, String pendingVersion) throws IOException {
|
||||
|
||||
Migration migration = request.migrationForPendingDrop(pendingVersion);
|
||||
|
||||
generateMigration(request, migration, pendingVersion);
|
||||
|
||||
List<String> pendingDrops = request.getPendingDrops();
|
||||
if (!pendingDrops.isEmpty()) {
|
||||
logger.info("... remaining pending un-applied drops in versions {}", pendingDrops);
|
||||
}
|
||||
}
|
||||
|
||||
private Request createRequest() {
|
||||
return new Request();
|
||||
}
|
||||
|
||||
private class Request {
|
||||
|
||||
final File migrationDir;
|
||||
final File modelDir;
|
||||
final MigrationModel migrationModel;
|
||||
final CurrentModel currentModel;
|
||||
final ModelContainer migrated;
|
||||
final ModelContainer current;
|
||||
|
||||
private Request() {
|
||||
this.migrationDir = getMigrationDirectory();
|
||||
this.modelDir = getModelDirectory(migrationDir);
|
||||
this.migrationModel = new MigrationModel(modelDir, migrationConfig.getModelSuffix());
|
||||
this.migrated = migrationModel.read();
|
||||
this.currentModel = new CurrentModel(server, constraintNaming);
|
||||
this.current = currentModel.read();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the migration for the pending drops for a given version.
|
||||
*/
|
||||
public Migration migrationForPendingDrop(String pendingVersion) {
|
||||
|
||||
Migration migration = migrated.migrationForPendingDrop(pendingVersion);
|
||||
|
||||
// register any remaining pending drops
|
||||
migrated.registerPendingHistoryDropColumns(current);
|
||||
return migration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of versions that have pending un-applied drops.
|
||||
*/
|
||||
public List<String> getPendingDrops() {
|
||||
return migrated.getPendingDrops();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return the diff of the current model to the migration model.
|
||||
*/
|
||||
public Migration createDiffMigration() {
|
||||
ModelDiff diff = new ModelDiff(migrated);
|
||||
diff.compareTo(current);
|
||||
return diff.isEmpty() ? null : diff.getMigration();
|
||||
}
|
||||
}
|
||||
|
||||
private void generateMigration(Request request, Migration dbMigration, String dropsFor) throws IOException {
|
||||
|
||||
String fullVersion = getFullVersion(request.migrationModel, dropsFor);
|
||||
|
||||
logger.info("generating migration:{}", fullVersion);
|
||||
if (!writeMigrationXml(dbMigration, request.modelDir, fullVersion)) {
|
||||
logger.warn("migration already exists, not generating DDL");
|
||||
|
||||
} else {
|
||||
if (databasePlatform != null) {
|
||||
// writer needs the current model to provide table/column details for
|
||||
// history ddl generation (triggers, history tables etc)
|
||||
DdlWrite write = new DdlWrite(new MConfiguration(), request.current);
|
||||
PlatformDdlWriter writer = createDdlWriter(databasePlatform, "");
|
||||
writer.processMigration(dbMigration, write, request.migrationDir , fullVersion);
|
||||
}
|
||||
writeExtraPlatformDdl(fullVersion, request.currentModel, dbMigration, request.migrationDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the next pending drop changeSet should be generated as the next migration.
|
||||
*/
|
||||
private String generatePendingDrop() {
|
||||
|
||||
String nextDrop = System.getProperty("ddl.migration.pendingDropsFor");
|
||||
if (nextDrop != null) {
|
||||
return nextDrop;
|
||||
}
|
||||
return migrationConfig.getGeneratePendingDrop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full version for the migration being generated.
|
||||
*
|
||||
* The full version can contain a comment suffix after a "__" double underscore.
|
||||
*/
|
||||
private String getFullVersion(MigrationModel migrationModel, String dropsFor) {
|
||||
|
||||
String version = migrationConfig.getVersion();
|
||||
if (version == null) {
|
||||
@@ -261,10 +350,14 @@ public class DbMigration {
|
||||
}
|
||||
|
||||
String fullVersion = version;
|
||||
if (migrationConfig.getName() != null) {
|
||||
fullVersion += "__" + toUnderScore(migrationConfig.getName());
|
||||
|
||||
String name = migrationConfig.getName();
|
||||
if (name != null) {
|
||||
fullVersion += "__" + toUnderScore(name);
|
||||
} else if (dropsFor != null) {
|
||||
fullVersion += "__" + toUnderScore("dropsFor_" + MigrationVersion.trim(dropsFor));
|
||||
|
||||
} else if (version.equals(initialVersion)) {
|
||||
fullVersion += "__initial";
|
||||
}
|
||||
return fullVersion;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.avaje.ebean.dbmigration;
|
||||
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.dbmigration.model.CurrentModel;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanPlugin;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
@@ -30,8 +29,8 @@ public class DdlGenerator {
|
||||
private final boolean createOnly;
|
||||
|
||||
private CurrentModel currentModel;
|
||||
private String dropContent;
|
||||
private String createContent;
|
||||
private String dropAllContent;
|
||||
private String createAllContent;
|
||||
|
||||
public DdlGenerator(SpiEbeanServer server, ServerConfig serverConfig) {
|
||||
this.server = server;
|
||||
@@ -84,18 +83,18 @@ public class DdlGenerator {
|
||||
|
||||
protected void runDropSql() throws IOException {
|
||||
if (!createOnly) {
|
||||
if (dropContent == null) {
|
||||
dropContent = readFile(getDropFileName());
|
||||
if (dropAllContent == null) {
|
||||
dropAllContent = readFile(getDropFileName());
|
||||
}
|
||||
runScript(true, dropContent, getDropFileName());
|
||||
runScript(true, dropAllContent, getDropFileName());
|
||||
}
|
||||
}
|
||||
|
||||
protected void runCreateSql() throws IOException {
|
||||
if (createContent == null) {
|
||||
createContent = readFile(getCreateFileName());
|
||||
if (createAllContent == null) {
|
||||
createAllContent = readFile(getCreateFileName());
|
||||
}
|
||||
runScript(false, createContent, getCreateFileName());
|
||||
runScript(false, createAllContent, getCreateFileName());
|
||||
}
|
||||
|
||||
protected void runInitSql() throws IOException {
|
||||
@@ -132,8 +131,7 @@ public class DdlGenerator {
|
||||
protected void writeDrop(String dropFile) {
|
||||
|
||||
try {
|
||||
String c = generateDropDdl();
|
||||
writeFile(dropFile, c);
|
||||
writeFile(dropFile, generateDropAllDdl());
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException("Error generating Drop DDL", e);
|
||||
}
|
||||
@@ -142,28 +140,27 @@ public class DdlGenerator {
|
||||
protected void writeCreate(String createFile) {
|
||||
|
||||
try {
|
||||
String c = generateCreateDdl();
|
||||
writeFile(createFile, c);
|
||||
writeFile(createFile, generateCreateAllDdl());
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException("Error generating Create DDL", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected String generateDropDdl() {
|
||||
protected String generateDropAllDdl() {
|
||||
|
||||
try {
|
||||
dropContent = currentModel().getDropDdl();
|
||||
return dropContent;
|
||||
dropAllContent = currentModel().getDropAllDdl();
|
||||
return dropAllContent;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected String generateCreateDdl() {
|
||||
protected String generateCreateAllDdl() {
|
||||
|
||||
try {
|
||||
createContent = currentModel().getCreateDdl();
|
||||
return createContent;
|
||||
createAllContent = currentModel().getCreateDdl();
|
||||
return createAllContent;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -12,32 +12,17 @@ public class DdlWrite {
|
||||
|
||||
private final ModelContainer currentModel;
|
||||
|
||||
private final DdlBuffer applyDropDependencies;
|
||||
|
||||
private final DdlBuffer apply;
|
||||
|
||||
private final DdlBuffer applyForeignKeys;
|
||||
|
||||
private final DdlBuffer applyHistory;
|
||||
|
||||
private final DdlBuffer rollbackForeignKeys;
|
||||
private final DdlBuffer dropAllForeignKeys;
|
||||
|
||||
private final DdlBuffer rollback;
|
||||
|
||||
/**
|
||||
* For DDL that drops tables and columns etc.
|
||||
*
|
||||
* This DDL typically can not run automatically in production as there is most commonly
|
||||
* existing servers running the application using these tables and columns. Typically
|
||||
* these drop statements may be executed AFTER all the servers in the application have
|
||||
* migrated onto new code.
|
||||
*/
|
||||
private final DdlBuffer drop;
|
||||
|
||||
/**
|
||||
* For use when History is turned off for a base table or history is no longer
|
||||
* desired on specific columns. This DDL should typically execute manually after review
|
||||
* by DBA's.
|
||||
*/
|
||||
private final DdlBuffer dropHistory;
|
||||
private final DdlBuffer dropAll;
|
||||
|
||||
/**
|
||||
* Create without any configuration or current model (no history support).
|
||||
@@ -51,13 +36,12 @@ public class DdlWrite {
|
||||
*/
|
||||
public DdlWrite(MConfiguration configuration, ModelContainer currentModel) {
|
||||
this.currentModel = currentModel;
|
||||
this.applyDropDependencies = new BaseDdlBuffer(configuration);
|
||||
this.apply = new BaseDdlBuffer(configuration);
|
||||
this.applyForeignKeys = new BaseDdlBuffer(configuration);
|
||||
this.applyHistory = new BaseDdlBuffer(configuration);
|
||||
this.rollbackForeignKeys = new BaseDdlBuffer(configuration);
|
||||
this.rollback = new BaseDdlBuffer(configuration);
|
||||
this.drop = new BaseDdlBuffer(configuration);
|
||||
this.dropHistory = new BaseDdlBuffer(configuration);
|
||||
this.dropAllForeignKeys = new BaseDdlBuffer(configuration);
|
||||
this.dropAll = new BaseDdlBuffer(configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,22 +61,8 @@ public class DdlWrite {
|
||||
public boolean isApplyEmpty() {
|
||||
return apply.getBuffer().isEmpty()
|
||||
&& applyForeignKeys.getBuffer().isEmpty()
|
||||
&& applyHistory.getBuffer().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the apply rollback buffers are all empty.
|
||||
*/
|
||||
public boolean isApplyRollbackEmpty() {
|
||||
return rollback.getBuffer().isEmpty()
|
||||
&& rollbackForeignKeys.getBuffer().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true the drop buffers are empty.
|
||||
*/
|
||||
public boolean isDropEmpty() {
|
||||
return drop.getBuffer().isEmpty() && dropHistory.getBuffer().isEmpty();
|
||||
&& applyHistory.getBuffer().isEmpty()
|
||||
&& applyDropDependencies.getBuffer().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,6 +72,13 @@ public class DdlWrite {
|
||||
return apply;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the buffer that executes early to drop dependencies like views etc.
|
||||
*/
|
||||
public DdlBuffer applyDropDependencies() {
|
||||
return applyDropDependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the buffer that APPLY DDL is written to for foreign keys and their associated indexes.
|
||||
* <p>
|
||||
@@ -120,41 +97,17 @@ public class DdlWrite {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the buffer that ROLLBACK DDL is written to for foreign keys and associated indexes.
|
||||
* Return the buffer used for the 'drop all DDL' for dropping foreign keys and associated indexes.
|
||||
*/
|
||||
public DdlBuffer rollbackForeignKeys() {
|
||||
return rollbackForeignKeys;
|
||||
public DdlBuffer dropAllForeignKeys() {
|
||||
return dropAllForeignKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the buffer that ROLLBACK DDL is written to which is considered safe to run when
|
||||
* apply changes fail to execute. This will reverse the apply changes typically dropping
|
||||
* newly created tables, foreign keys etc.
|
||||
* <p>
|
||||
* When apply changes are made against DB's that support transactional DDL you could argue
|
||||
* that these rollback statements are not necessary.
|
||||
* <p>
|
||||
* Note that statements added to this rollback buffer are executed after foreign key rollback
|
||||
* has been executed.
|
||||
* Return the buffer used for the 'drop all DDL' to drop tables, views and history triggers etc.
|
||||
*/
|
||||
public DdlBuffer rollback() {
|
||||
return rollback;
|
||||
public DdlBuffer dropAll() {
|
||||
return dropAll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the buffer that destructive changes are written to. This is typically drop table and
|
||||
* drop column.
|
||||
*/
|
||||
public DdlBuffer drop() {
|
||||
return drop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the buffer that is used when history is no longer required on a table or specific columns.
|
||||
*/
|
||||
public DdlBuffer dropHistory() {
|
||||
return dropHistory;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+13
-42
@@ -140,7 +140,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
// add drop table to the rollback buffer - do this before
|
||||
// we drop the related sequence (if sequences are used)
|
||||
dropTable(writer.rollback(), tableName);
|
||||
dropTable(writer.dropAll(), tableName);
|
||||
|
||||
if (useSequence) {
|
||||
String pkCol = pk.get(0).getName();
|
||||
@@ -149,7 +149,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
// add blank line for a bit of whitespace between tables
|
||||
apply.end();
|
||||
writer.rollback().end();
|
||||
writer.dropAll().end();
|
||||
|
||||
writeAddForeignKeys(writer, createTable);
|
||||
|
||||
@@ -204,7 +204,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
.append(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames))
|
||||
.endOfStatement();
|
||||
|
||||
write.rollbackForeignKeys()
|
||||
write.dropAllForeignKeys()
|
||||
.append(platformDdl.dropIndex(uqName, tableName))
|
||||
.endOfStatement();
|
||||
}
|
||||
@@ -225,7 +225,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
String createSeq = platformDdl.createSequence(seqName, initial, allocate);
|
||||
if (createSeq != null) {
|
||||
writer.apply().append(createSeq).newLine();
|
||||
writer.rollback().append(platformDdl.dropSequence(seqName)).endOfStatement();
|
||||
writer.dropAll().append(platformDdl.dropSequence(seqName)).endOfStatement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,15 +295,15 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
fkeyBuffer.end();
|
||||
|
||||
write.rollbackForeignKeys()
|
||||
write.dropAllForeignKeys()
|
||||
.append(platformDdl.alterTableDropForeignKey(tableName, fkName)).endOfStatement();
|
||||
|
||||
if (indexName != null) {
|
||||
write.rollbackForeignKeys()
|
||||
write.dropAllForeignKeys()
|
||||
.append(platformDdl.dropIndex(indexName, tableName)).endOfStatement();
|
||||
}
|
||||
|
||||
write.rollbackForeignKeys().end();
|
||||
write.dropAllForeignKeys().end();
|
||||
|
||||
}
|
||||
|
||||
@@ -471,7 +471,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
.append(platformDdl.createIndex(createIndex.getIndexName(), createIndex.getTableName(), cols))
|
||||
.endOfStatement();
|
||||
|
||||
writer.rollback()
|
||||
writer.dropAll()
|
||||
.append(platformDdl.dropIndex(createIndex.getIndexName(), createIndex.getTableName()))
|
||||
.endOfStatement();
|
||||
}
|
||||
@@ -520,7 +520,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
List<Column> columns = addColumn.getColumn();
|
||||
for (Column column : columns) {
|
||||
alterTableAddColumn(writer.apply(), tableName, column, false);
|
||||
alterTableDropColumn(writer.rollback(), tableName, column.getName());
|
||||
}
|
||||
|
||||
if (isTrue(addColumn.isWithHistory())) {
|
||||
@@ -529,13 +528,11 @@ public class BaseTableDdl implements TableDdl {
|
||||
for (Column column : columns) {
|
||||
regenerateHistoryTriggers(tableName, HistoryTableUpdate.Change.ADD, column.getName());
|
||||
alterTableAddColumn(writer.apply(), historyTable, column, true);
|
||||
alterTableDropColumn(writer.rollback(), historyTable, column.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// add a bit of whitespace
|
||||
writer.apply().end();
|
||||
writer.rollback().end();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -544,7 +541,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
@Override
|
||||
public void generate(DdlWrite writer, DropTable dropTable) throws IOException {
|
||||
|
||||
dropTable(writer.drop(), dropTable.getName());
|
||||
dropTable(writer.apply(), dropTable.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -555,14 +552,14 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
String tableName = dropColumn.getTableName();
|
||||
|
||||
alterTableDropColumn(writer.drop(), tableName, dropColumn.getColumnName());
|
||||
alterTableDropColumn(writer.apply(), tableName, dropColumn.getColumnName());
|
||||
if (isTrue(dropColumn.isWithHistory())) {
|
||||
// also drop from the history table
|
||||
regenerateHistoryTriggers(tableName, HistoryTableUpdate.Change.DROP, dropColumn.getColumnName());
|
||||
alterTableDropColumn(writer.drop(), historyTable(tableName), dropColumn.getColumnName());
|
||||
alterTableDropColumn(writer.apply(), historyTable(tableName), dropColumn.getColumnName());
|
||||
}
|
||||
|
||||
writer.drop().end();
|
||||
writer.apply().end();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -642,21 +639,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
|
||||
// reverse and generate the rollback statement
|
||||
String currentType = alter.getCurrentType();
|
||||
String type = alter.getType();
|
||||
Boolean currentNotnull = alter.isCurrentNotnull();
|
||||
Boolean notnull = alter.isNotnull();
|
||||
|
||||
alter.setCurrentType(type);
|
||||
alter.setType(currentType);
|
||||
alter.setNotnull(currentNotnull);
|
||||
alter.setCurrentNotnull(notnull);
|
||||
|
||||
// write the rollback
|
||||
ddl = platformDdl.alterColumnBaseAttributes(alter);
|
||||
writer.rollback().append(ddl).endOfStatement();
|
||||
|
||||
if (isTrue(alter.isWithHistory()) && alter.getType() != null) {
|
||||
// mysql and sql server column type change allowing nulls in the history table column
|
||||
AlterColumn alterHistoryColumn = new AlterColumn();
|
||||
@@ -667,11 +649,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
// write the apply to history table
|
||||
writer.apply().append(histColumnDdl).endOfStatement();
|
||||
|
||||
// write the rollback from history table
|
||||
alterHistoryColumn.setType(currentType);
|
||||
histColumnDdl = platformDdl.alterColumnBaseAttributes(alterHistoryColumn);
|
||||
writer.rollback().append(histColumnDdl).endOfStatement();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -689,8 +666,6 @@ public class BaseTableDdl implements TableDdl {
|
||||
String ddl = platformDdl.alterColumnNotnull(alter.getTableName(), alter.getColumnName(), alter.isNotnull());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
ddl = platformDdl.alterColumnNotnull(alter.getTableName(), alter.getColumnName(), alter.isCurrentNotnull());
|
||||
writer.rollback().append(ddl).endOfStatement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -699,14 +674,10 @@ public class BaseTableDdl implements TableDdl {
|
||||
String ddl = platformDdl.alterColumnType(alter.getTableName(), alter.getColumnName(), alter.getType());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
ddl = platformDdl.alterColumnType(alter.getTableName(), alter.getColumnName(), alter.getCurrentType());
|
||||
writer.rollback().append(ddl).endOfStatement();
|
||||
if (isTrue(alter.isWithHistory())) {
|
||||
// apply same type change to matching column in the history table
|
||||
ddl = platformDdl.alterColumnType(historyTable(alter.getTableName()), alter.getColumnName(), alter.getType());
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
ddl = platformDdl.alterColumnType(historyTable(alter.getTableName()), alter.getColumnName(), alter.getCurrentType());
|
||||
writer.rollback().append(ddl).endOfStatement();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -762,7 +733,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
.append(platformDdl.alterTableAddUniqueConstraint(alter.getTableName(), uqName, cols))
|
||||
.endOfStatement();
|
||||
|
||||
writer.rollbackForeignKeys()
|
||||
writer.dropAllForeignKeys()
|
||||
.append(platformDdl.dropIndex(uqName, alter.getTableName()))
|
||||
.endOfStatement();
|
||||
}
|
||||
|
||||
+75
-30
@@ -10,7 +10,6 @@ import com.avaje.ebean.dbmigration.model.MColumn;
|
||||
import com.avaje.ebean.dbmigration.model.MTable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -45,21 +44,43 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
this.historySuffix = serverConfig.getHistoryTableSuffix();
|
||||
this.constraintNaming = serverConfig.getConstraintNaming();
|
||||
|
||||
this.sysPeriodStart = sysPeriod+"_start";
|
||||
this.sysPeriodEnd = sysPeriod+"_end";
|
||||
this.sysPeriodStart = sysPeriod + "_start";
|
||||
this.sysPeriodEnd = sysPeriod + "_end";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void regenerateHistoryTriggers(DdlWrite writer, HistoryTableUpdate update) throws IOException {
|
||||
public void updateTriggers(DdlWrite writer, HistoryTableUpdate update) throws IOException {
|
||||
|
||||
MTable table = writer.getTable(update.getBaseTable());
|
||||
if (table == null) {
|
||||
throw new IllegalStateException("MTable "+update.getBaseTable()+" not found in writer? (required for history DDL)");
|
||||
throw new IllegalStateException("MTable " + update.getBaseTable() + " not found in writer? (required for history DDL)");
|
||||
}
|
||||
regenerateHistoryTriggers(writer, table, update);
|
||||
updateTriggers(writer, table, update);
|
||||
}
|
||||
|
||||
protected abstract void regenerateHistoryTriggers(DdlWrite writer, MTable table, HistoryTableUpdate update) throws IOException;
|
||||
/**
|
||||
* Replace the existing triggers/stored procedures/views for history table support given the included columns.
|
||||
*/
|
||||
protected abstract void updateHistoryTriggers(DbTriggerUpdate triggerUpdate) throws IOException;
|
||||
|
||||
/**
|
||||
* Process the HistoryTableUpdate which can result in changes to the apply, rollback
|
||||
* and drop scripts.
|
||||
*/
|
||||
protected void updateTriggers(DdlWrite writer, MTable table, HistoryTableUpdate update) throws IOException {
|
||||
|
||||
writer.applyHistory().append("-- changes: ").append(update.description()).newLine();
|
||||
|
||||
updateHistoryTriggers(createDbTriggerUpdate(writer, table));
|
||||
}
|
||||
|
||||
protected DbTriggerUpdate createDbTriggerUpdate(DdlWrite writer, MTable table) {
|
||||
|
||||
List<String> columns = columnNamesForApply(table);
|
||||
String baseTableName = table.getName();
|
||||
String historyTableName = historyTableName(baseTableName);
|
||||
return new DbTriggerUpdate(baseTableName, historyTableName, writer, columns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException {
|
||||
@@ -67,18 +88,17 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
String baseTable = dropHistoryTable.getBaseTable();
|
||||
|
||||
// drop in appropriate order
|
||||
dropTriggers(writer.dropHistory(), baseTable);
|
||||
dropHistoryTableEtc(writer.dropHistory(), baseTable);
|
||||
dropTriggers(writer.applyDropDependencies(), baseTable);
|
||||
dropHistoryTableEtc(writer.applyDropDependencies(), baseTable);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException {
|
||||
|
||||
String baseTable = addHistoryTable.getBaseTable();
|
||||
MTable table = writer.getTable(baseTable);
|
||||
if (table == null) {
|
||||
throw new IllegalStateException("MTable "+baseTable+" not found in writer? (required for history DDL)");
|
||||
throw new IllegalStateException("MTable " + baseTable + " not found in writer? (required for history DDL)");
|
||||
}
|
||||
|
||||
createWithHistory(writer, table);
|
||||
@@ -90,12 +110,11 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
String baseTable = table.getName();
|
||||
String whenCreatedColumn = table.getWhenCreatedColumn();
|
||||
|
||||
// rollback changes in appropriate order
|
||||
dropTriggers(writer.rollback(), baseTable);
|
||||
dropHistoryTableEtc(writer.rollback(), baseTable);
|
||||
dropTriggers(writer.dropAll(), baseTable);
|
||||
dropHistoryTableEtc(writer.dropAll(), baseTable);
|
||||
|
||||
addHistoryTable(writer, table, whenCreatedColumn);
|
||||
addStoredFunction(writer, table, null);
|
||||
createStoredFunction(writer, table);
|
||||
createTriggers(writer, table);
|
||||
}
|
||||
|
||||
@@ -103,7 +122,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
|
||||
protected abstract void dropTriggers(DdlBuffer buffer, String baseTable) throws IOException;
|
||||
|
||||
protected void addStoredFunction(DdlWrite writer, MTable table, HistoryTableUpdate update) throws IOException {
|
||||
protected void createStoredFunction(DdlWrite writer, MTable table) throws IOException {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@@ -158,7 +177,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
|
||||
apply.append("create table ").append(table.getName()).append(historySuffix).append("(").newLine();
|
||||
|
||||
Collection<MColumn> cols = table.getColumns().values();
|
||||
Collection<MColumn> cols = table.allColumns();
|
||||
for (MColumn column : cols) {
|
||||
if (!column.isDraftOnly()) {
|
||||
writeColumnDefinition(apply, column.getName(), column.getType());
|
||||
@@ -191,6 +210,26 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
.endOfStatement().end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or replace the with_history view with explicit columns.
|
||||
*/
|
||||
protected void createWithHistoryView(DbTriggerUpdate update) throws IOException {
|
||||
|
||||
DdlBuffer apply = update.historyBuffer();
|
||||
apply.append("create or replace view ").append(update.getBaseTable()).append(viewSuffix).append(" as select ");
|
||||
appendColumnNames(apply, update.getColumns(), "");
|
||||
appendSysPeriodColumns(apply, ", ");
|
||||
apply.append(" from ").append(update.getBaseTable()).append(" union all select ");
|
||||
appendColumnNames(apply, update.getColumns(), "");
|
||||
appendSysPeriodColumns(apply, ", ");
|
||||
apply.append(" from ").append(update.getHistoryTable()).endOfStatement().end();
|
||||
}
|
||||
|
||||
protected void appendSysPeriodColumns(DdlBuffer apply, String prefix) throws IOException {
|
||||
appendColumnName(apply, prefix, sysPeriodStart);
|
||||
appendColumnName(apply, prefix, sysPeriodEnd);
|
||||
}
|
||||
|
||||
protected void dropHistoryTableEtc(DdlBuffer buffer, String baseTableName) throws IOException {
|
||||
|
||||
buffer.append("drop view ").append(baseTableName).append(viewSuffix).endOfStatement();
|
||||
@@ -203,8 +242,6 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
buffer.append("alter table ").append(baseTableName).append(" drop column ").append(sysPeriodEnd).endOfStatement();
|
||||
}
|
||||
|
||||
//protected abstract void addFunction(DdlBuffer apply, String procedureName, String historyTable, List<String> includedColumns) throws IOException;
|
||||
|
||||
protected void appendInsertIntoHistory(DdlBuffer buffer, String historyTable, List<String> columns) throws IOException {
|
||||
|
||||
buffer.append(" insert into ").append(historyTable).append(" (").append(sysPeriodStart).append(",").append(sysPeriodEnd).append(",");
|
||||
@@ -216,7 +253,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
|
||||
protected void appendColumnNames(DdlBuffer buffer, List<String> columns, String columnPrefix) throws IOException {
|
||||
|
||||
for (int i=0; i< columns.size(); i++) {
|
||||
for (int i = 0; i < columns.size(); i++) {
|
||||
if (i > 0) {
|
||||
buffer.append(", ");
|
||||
}
|
||||
@@ -226,18 +263,26 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of included columns in order.
|
||||
* Append a single column to the buffer if it is not null.
|
||||
*/
|
||||
protected List<String> includedColumnNames(MTable table) throws IOException {
|
||||
protected void appendColumnName(DdlBuffer buffer, String prefix, String columnName) throws IOException {
|
||||
|
||||
Collection<MColumn> columns = table.getColumns().values();
|
||||
List<String> includedColumns = new ArrayList<String>(columns.size());
|
||||
|
||||
for (MColumn column : columns) {
|
||||
if (column.isIncludeInHistory()) {
|
||||
includedColumns.add(column.getName());
|
||||
}
|
||||
if (columnName != null) {
|
||||
buffer.append(prefix).append(columnName);
|
||||
}
|
||||
return includedColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the column names included in history for the apply script.
|
||||
* <p>
|
||||
* Note that dropped columns are actually still included at this point as they are going
|
||||
* to be removed from the history handling when the drop script runs that also deletes
|
||||
* the column.
|
||||
* </p>
|
||||
*/
|
||||
protected List<String> columnNamesForApply(MTable table) {
|
||||
|
||||
return table.allHistoryColumns(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DB trigger update when a change occurs on a table with history.
|
||||
*/
|
||||
public class DbTriggerUpdate {
|
||||
|
||||
private final String baseTableName;
|
||||
|
||||
private final String historyTableName;
|
||||
|
||||
private final DdlWrite writer;
|
||||
|
||||
private final List<String> columns;
|
||||
|
||||
public DbTriggerUpdate(String baseTableName, String historyTableName, DdlWrite writer, List<String> columns) {
|
||||
this.baseTableName = baseTableName;
|
||||
this.historyTableName = historyTableName;
|
||||
this.writer = writer;
|
||||
this.columns = columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the appropriate buffer for the current mode.
|
||||
*/
|
||||
public DdlBuffer historyBuffer() {
|
||||
return writer.applyHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the appropriate drop dependency buffer for the current mode.
|
||||
*/
|
||||
public DdlBuffer dropDependencyBuffer() {
|
||||
return writer.applyDropDependencies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base table name.
|
||||
*/
|
||||
public String getBaseTable() {
|
||||
return baseTableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the history table name.
|
||||
*/
|
||||
public String getHistoryTable() {
|
||||
return historyTableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the included columns.
|
||||
*/
|
||||
public List<String> getColumns() {
|
||||
return columns;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
|
||||
import com.avaje.ebean.dbmigration.model.MTable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* H2 history support using DB triggers to maintain a history table.
|
||||
@@ -28,40 +27,20 @@ public class H2HistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
protected void createTriggers(DdlWrite writer, MTable table) throws IOException {
|
||||
|
||||
String baseTableName = table.getName();
|
||||
String historyTableName = historyTableName(baseTableName);
|
||||
List<String> includedColumns = includedColumnNames(table);
|
||||
|
||||
DdlBuffer apply = writer.applyHistory();
|
||||
|
||||
addCreateTrigger(apply, updateTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
addCreateTrigger(apply, updateTriggerName(baseTableName), baseTableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void regenerateHistoryTriggers(DdlWrite writer, MTable table, HistoryTableUpdate update) throws IOException {
|
||||
protected void updateHistoryTriggers(DbTriggerUpdate update) throws IOException {
|
||||
|
||||
String baseTableName = table.getName();
|
||||
String historyTableName = historyTableName(baseTableName);
|
||||
List<String> includedColumns = includedColumnNames(table);
|
||||
|
||||
DdlBuffer apply = writer.applyHistory();
|
||||
|
||||
apply.append("-- Regenerated ").newLine();
|
||||
apply.append("-- changes: ").append(update.description()).newLine();
|
||||
|
||||
dropTriggers(apply, baseTableName);
|
||||
addCreateTrigger(apply, updateTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
|
||||
// put a reverted version into the rollback buffer
|
||||
update.toRevertedColumns(includedColumns);
|
||||
|
||||
DdlBuffer rollback = writer.rollback();
|
||||
rollback.append("-- Revert regenerated ").newLine();
|
||||
rollback.append("-- revert changes: ").append(update.description()).newLine();
|
||||
dropTriggers(rollback, baseTableName);
|
||||
addCreateTrigger(rollback, updateTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
DdlBuffer buffer = update.historyBuffer();
|
||||
dropTriggers(buffer, update.getBaseTable());
|
||||
addCreateTrigger(buffer, updateTriggerName(update.getBaseTable()), update.getBaseTable());
|
||||
}
|
||||
|
||||
private void addCreateTrigger(DdlBuffer apply, String triggerName, String baseTable, String historyTable, List<String> includedColumns) throws IOException {
|
||||
private void addCreateTrigger(DdlBuffer apply, String triggerName, String baseTable) throws IOException {
|
||||
|
||||
// Note that this does not take into account the historyTable name (excepts _history suffix) and
|
||||
// does not take into account excluded columns (all columns included in history)
|
||||
|
||||
+11
-43
@@ -10,7 +10,6 @@ import java.util.List;
|
||||
*/
|
||||
public class HistoryTableUpdate {
|
||||
|
||||
|
||||
/**
|
||||
* Column change type.
|
||||
*/
|
||||
@@ -21,35 +20,25 @@ public class HistoryTableUpdate {
|
||||
EXCLUDE
|
||||
}
|
||||
|
||||
public static class Column {
|
||||
private static class Column {
|
||||
|
||||
final Change change;
|
||||
|
||||
final String column;
|
||||
|
||||
public final Change change;
|
||||
public final String column;
|
||||
public Column(Change change, String column) {
|
||||
this.change = change;
|
||||
this.column = column;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return description();
|
||||
}
|
||||
|
||||
public String description() {
|
||||
return change.name().toLowerCase()+" "+column;
|
||||
}
|
||||
|
||||
public void apply(List<String> includedColumns) {
|
||||
switch (change) {
|
||||
case ADD:
|
||||
case INCLUDE: {
|
||||
includedColumns.remove(column);
|
||||
break;
|
||||
}
|
||||
case DROP:
|
||||
case EXCLUDE: {
|
||||
includedColumns.add(column);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected change "+change);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final String baseTable;
|
||||
@@ -65,27 +54,12 @@ public class HistoryTableUpdate {
|
||||
|
||||
/**
|
||||
* Return a description of the changes that cause the history trigger/function
|
||||
* to be regenerated (added, dropped, included or excluded columns).
|
||||
* to be regenerated (added, included, excluded and dropped columns).
|
||||
*/
|
||||
public String description() {
|
||||
StringBuilder sb = new StringBuilder(90);
|
||||
for (int i = 0; i < columnChanges.size(); i++) {
|
||||
if (i > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(columnChanges.get(i).description());
|
||||
}
|
||||
return sb.toString();
|
||||
return columnChanges.toString();
|
||||
}
|
||||
|
||||
public void toRevertedColumns(List<String> includedColumns) {
|
||||
|
||||
for (Column columnChange : columnChanges) {
|
||||
columnChange.apply(includedColumns);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a comment for column added, dropped, included or excluded.
|
||||
*/
|
||||
@@ -100,10 +74,4 @@ public class HistoryTableUpdate {
|
||||
return baseTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the comments.
|
||||
*/
|
||||
public List<Column> getColumnChanges() {
|
||||
return columnChanges;
|
||||
}
|
||||
}
|
||||
|
||||
+19
-41
@@ -5,7 +5,6 @@ import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
|
||||
import com.avaje.ebean.dbmigration.model.MTable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* MySql history support using DB triggers to maintain a history table.
|
||||
@@ -26,67 +25,46 @@ public class MySqlHistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
@Override
|
||||
protected void createTriggers(DdlWrite writer, MTable table) throws IOException {
|
||||
|
||||
String baseTableName = table.getName();
|
||||
String historyTableName = historyTableName(baseTableName);
|
||||
List<String> includedColumns = includedColumnNames(table);
|
||||
DbTriggerUpdate update = createDbTriggerUpdate(writer, table);
|
||||
|
||||
DdlBuffer apply = writer.applyHistory();
|
||||
|
||||
addBeforeUpdate(apply, updateTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
addBeforeDelete(apply, deleteTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
addBeforeUpdate(updateTriggerName(update.getBaseTable()), update);
|
||||
addBeforeDelete(deleteTriggerName(update.getBaseTable()), update);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void regenerateHistoryTriggers(DdlWrite writer, MTable table, HistoryTableUpdate update) throws IOException {
|
||||
protected void updateHistoryTriggers(DbTriggerUpdate update) throws IOException {
|
||||
|
||||
String baseTableName = table.getName();
|
||||
String historyTableName = historyTableName(baseTableName);
|
||||
List<String> includedColumns = includedColumnNames(table);
|
||||
DdlBuffer buffer = update.historyBuffer();
|
||||
String baseTable = update.getBaseTable();
|
||||
|
||||
DdlBuffer apply = writer.applyHistory();
|
||||
|
||||
apply.append("-- Regenerated ").newLine();
|
||||
apply.append("-- changes: ").append(update.description()).newLine();
|
||||
// lock the base table while we drop and recreate the triggers
|
||||
apply.append("lock tables ").append(baseTableName).append(" write").endOfStatement();
|
||||
dropTriggers(apply, baseTableName);
|
||||
addBeforeUpdate(apply, updateTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
addBeforeDelete(apply, deleteTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
apply.append("unlock tables").endOfStatement();
|
||||
|
||||
// put a reverted version into the rollback buffer
|
||||
update.toRevertedColumns(includedColumns);
|
||||
|
||||
DdlBuffer rollback = writer.rollback();
|
||||
rollback.append("-- Revert regenerated ").newLine();
|
||||
rollback.append("-- revert changes: ").append(update.description()).newLine();
|
||||
// lock the base table while we drop and recreate the triggers
|
||||
rollback.append("lock tables ").append(baseTableName).append(" write").endOfStatement();
|
||||
dropTriggers(rollback, baseTableName);
|
||||
addBeforeUpdate(rollback, updateTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
addBeforeDelete(rollback, deleteTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
rollback.append("unlock tables").endOfStatement();
|
||||
buffer.append("lock tables ").append(baseTable).append(" write").endOfStatement();
|
||||
dropTriggers(buffer, baseTable);
|
||||
addBeforeUpdate(updateTriggerName(baseTable), update);
|
||||
addBeforeDelete(deleteTriggerName(baseTable), update);
|
||||
buffer.append("unlock tables").endOfStatement();
|
||||
}
|
||||
|
||||
private void addBeforeUpdate(DdlBuffer apply, String triggerName, String baseTable, String historyTable, List<String> includedColumns) throws IOException {
|
||||
private void addBeforeUpdate(String triggerName, DbTriggerUpdate update) throws IOException {
|
||||
|
||||
DdlBuffer apply = update.historyBuffer();
|
||||
apply
|
||||
.append("delimiter $$").newLine()
|
||||
.append("create trigger ").append(triggerName).append(" before update on ").append(baseTable)
|
||||
.append("create trigger ").append(triggerName).append(" before update on ").append(update.getBaseTable())
|
||||
.append(" for each row begin").newLine();
|
||||
appendInsertIntoHistory(apply, historyTable, includedColumns);
|
||||
appendInsertIntoHistory(apply, update.getHistoryTable(), update.getColumns());
|
||||
apply
|
||||
.append(" set NEW.").append(sysPeriod).append("_start = now(6)").endOfStatement()
|
||||
.append("end$$").newLine();
|
||||
}
|
||||
|
||||
private void addBeforeDelete(DdlBuffer apply, String triggerName, String baseTable, String historyTable, List<String> includedColumns) throws IOException {
|
||||
private void addBeforeDelete(String triggerName, DbTriggerUpdate update) throws IOException {
|
||||
|
||||
DdlBuffer apply = update.historyBuffer();
|
||||
apply
|
||||
.append("delimiter $$").newLine()
|
||||
.append("create trigger ").append(triggerName).append(" before delete on ").append(baseTable)
|
||||
.append("create trigger ").append(triggerName).append(" before delete on ").append(update.getBaseTable())
|
||||
.append(" for each row begin").newLine();
|
||||
appendInsertIntoHistory(apply, historyTable, includedColumns);
|
||||
appendInsertIntoHistory(apply, update.getHistoryTable(), update.getColumns());
|
||||
apply.append("end$$").newLine();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ public class NoHistorySupportDdl implements PlatformHistoryDdl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void regenerateHistoryTriggers(DdlWrite write, HistoryTableUpdate update) {
|
||||
public void updateTriggers(DdlWrite write, HistoryTableUpdate update) {
|
||||
// does nothing
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ public class PlatformDdl {
|
||||
* Regenerate the history triggers (or function) due to a column being added/dropped/excluded or included.
|
||||
*/
|
||||
public void regenerateHistoryTriggers(DdlWrite write, HistoryTableUpdate update) throws IOException {
|
||||
historyDdl.regenerateHistoryTriggers(write, update);
|
||||
historyDdl.updateTriggers(write, update);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -36,5 +36,5 @@ public interface PlatformHistoryDdl {
|
||||
/**
|
||||
* Regenerate the history triggers/stored function due to column added/dropped/included or excluded.
|
||||
*/
|
||||
void regenerateHistoryTriggers(DdlWrite write, HistoryTableUpdate baseTable) throws IOException;
|
||||
void updateTriggers(DdlWrite write, HistoryTableUpdate baseTable) throws IOException;
|
||||
}
|
||||
|
||||
+26
-25
@@ -45,6 +45,11 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendSysPeriodColumns(DdlBuffer apply, String prefix) throws IOException {
|
||||
appendColumnName(apply, prefix, sysPeriod);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dropSysPeriodColumns(DdlBuffer buffer, String baseTableName) throws IOException {
|
||||
buffer.append("alter table ").append(baseTableName).append(" drop column ").append(sysPeriod).endOfStatement();
|
||||
@@ -72,7 +77,7 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
buffer.end();
|
||||
}
|
||||
|
||||
protected void addFunction(DdlBuffer apply, String procedureName, String historyTable, List<String> includedColumns) throws IOException {
|
||||
protected void createOrReplaceFunction(DdlBuffer apply, String procedureName, String historyTable, List<String> includedColumns) throws IOException {
|
||||
apply
|
||||
.append("create or replace function ").append(procedureName).append("() returns trigger as $$").newLine()
|
||||
.append("begin").newLine();
|
||||
@@ -96,40 +101,36 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void regenerateHistoryTriggers(DdlWrite writer, MTable table, HistoryTableUpdate update) throws IOException {
|
||||
|
||||
// just replace the stored function with 'create or replace'
|
||||
addStoredFunction(writer, table, update);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addStoredFunction(DdlWrite writer, MTable table, HistoryTableUpdate update) throws IOException {
|
||||
protected void createStoredFunction(DdlWrite writer, MTable table) throws IOException {
|
||||
|
||||
String procedureName = procedureName(table.getName());
|
||||
String historyTable = historyTableName(table.getName());
|
||||
|
||||
List<String> includedColumns = includedColumnNames(table);
|
||||
List<String> columnNames = columnNamesForApply(table);
|
||||
createOrReplaceFunction(writer.applyHistory(), procedureName, historyTable, columnNames);
|
||||
}
|
||||
|
||||
DdlBuffer apply = writer.applyHistory();
|
||||
@Override
|
||||
protected void updateHistoryTriggers(DbTriggerUpdate update) throws IOException {
|
||||
|
||||
if (update != null) {
|
||||
apply.append("-- Regenerated ").append(procedureName).newLine();
|
||||
apply.append("-- changes: ").append(update.description()).newLine();
|
||||
}
|
||||
String procedureName = procedureName(update.getBaseTable());
|
||||
|
||||
addFunction(apply, procedureName, historyTable, includedColumns);
|
||||
recreateHistoryView(update);
|
||||
createOrReplaceFunction(update.historyBuffer(), procedureName, update.getHistoryTable(), update.getColumns());
|
||||
}
|
||||
|
||||
/**
|
||||
* For postgres we need to drop and recreate the view. Well, we could add columns to the end of the view
|
||||
* but otherwise we need to drop and create it.
|
||||
*/
|
||||
private void recreateHistoryView(DbTriggerUpdate update) throws IOException {
|
||||
|
||||
if (update != null) {
|
||||
// put a reverted version into the rollback buffer
|
||||
update.toRevertedColumns(includedColumns);
|
||||
DdlBuffer buffer = update.dropDependencyBuffer();
|
||||
// we need to drop the view early/first before any changes to the tables etc
|
||||
buffer.append("drop view if exists ").append(update.getBaseTable()).append(viewSuffix).endOfStatement();
|
||||
|
||||
DdlBuffer rollback = writer.rollback();
|
||||
rollback.append("-- Revert regenerated ").append(procedureName).newLine();
|
||||
rollback.append("-- revert changes: ").append(update.description()).newLine();
|
||||
|
||||
addFunction(rollback, procedureName, historyTable, includedColumns);
|
||||
}
|
||||
// recreate the view with specific columns specified (the columns generally are not dropped until later)
|
||||
createWithHistoryView(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+10
-9
@@ -5,7 +5,7 @@ import com.avaje.ebean.config.dbplatform.DbTypeMap;
|
||||
|
||||
/**
|
||||
* Converts a logical column definition into platform specific one.
|
||||
*
|
||||
* <p>
|
||||
* This translates standard sql types into platform specific ones.
|
||||
*/
|
||||
public class PlatformTypeConverter {
|
||||
@@ -44,20 +44,21 @@ public class PlatformTypeConverter {
|
||||
return columnDefinition;
|
||||
}
|
||||
|
||||
String type = columnDefinition.substring(0,open);
|
||||
String suffix = close + 1 < columnDefinition.length() ? columnDefinition.substring(close + 1) : "";
|
||||
String type = columnDefinition.substring(0, open);
|
||||
try {
|
||||
DbType dbType = platformTypes.lookup(type);
|
||||
int comma = columnDefinition.indexOf(',',open);
|
||||
int comma = columnDefinition.indexOf(',', open);
|
||||
if (comma > -1) {
|
||||
// scale and precision - decimal(10,4)
|
||||
int scale = Integer.parseInt(columnDefinition.substring(open+1, comma));
|
||||
int precision = Integer.parseInt(columnDefinition.substring(comma+1, close));
|
||||
return dbType.renderType(scale,precision);
|
||||
int scale = Integer.parseInt(columnDefinition.substring(open + 1, comma));
|
||||
int precision = Integer.parseInt(columnDefinition.substring(comma + 1, close));
|
||||
return dbType.renderType(scale, precision) + suffix;
|
||||
|
||||
} else {
|
||||
// scale - varchar(10)
|
||||
int scale = Integer.parseInt(columnDefinition.substring(open+1, close));
|
||||
return dbType.renderType(scale,0);
|
||||
int scale = Integer.parseInt(columnDefinition.substring(open + 1, close));
|
||||
return dbType.renderType(scale, 0) + suffix;
|
||||
}
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
@@ -73,7 +74,7 @@ public class PlatformTypeConverter {
|
||||
|
||||
try {
|
||||
DbType dbType = platformTypes.lookup(columnDefinition);
|
||||
return dbType.renderType(0,0);
|
||||
return dbType.renderType(0, 0);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
// assume already platform specific, leave as is
|
||||
|
||||
@@ -27,6 +27,8 @@ import javax.xml.bind.annotation.XmlType;
|
||||
* </choice>
|
||||
* </sequence>
|
||||
* <attribute name="type" use="required" type="{http://ebean-orm.github.io/xml/ns/dbmigration}changeSetType" />
|
||||
* <attribute name="dropsFor" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="suppressDropsForever" type="{http://www.w3.org/2001/XMLSchema}boolean" />
|
||||
* <attribute name="generated" type="{http://www.w3.org/2001/XMLSchema}boolean" />
|
||||
* <attribute name="author" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="comment" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
@@ -62,6 +64,10 @@ public class ChangeSet {
|
||||
protected List<Object> changeSetChildren;
|
||||
@XmlAttribute(name = "type", required = true)
|
||||
protected ChangeSetType type;
|
||||
@XmlAttribute(name = "dropsFor")
|
||||
protected String dropsFor;
|
||||
@XmlAttribute(name = "suppressDropsForever")
|
||||
protected Boolean suppressDropsForever;
|
||||
@XmlAttribute(name = "generated")
|
||||
protected Boolean generated;
|
||||
@XmlAttribute(name = "author")
|
||||
@@ -134,6 +140,54 @@ public class ChangeSet {
|
||||
this.type = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the dropsFor property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public String getDropsFor() {
|
||||
return dropsFor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the dropsFor property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public void setDropsFor(String value) {
|
||||
this.dropsFor = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the suppressDropsForever property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link Boolean }
|
||||
*
|
||||
*/
|
||||
public Boolean isSuppressDropsForever() {
|
||||
return suppressDropsForever;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the suppressDropsForever property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link Boolean }
|
||||
*
|
||||
*/
|
||||
public void setSuppressDropsForever(Boolean value) {
|
||||
this.suppressDropsForever = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the generated property.
|
||||
*
|
||||
|
||||
@@ -15,7 +15,7 @@ import javax.xml.bind.annotation.XmlType;
|
||||
* <simpleType name="changeSetType">
|
||||
* <restriction base="{http://www.w3.org/2001/XMLSchema}string">
|
||||
* <enumeration value="apply"/>
|
||||
* <enumeration value="drop"/>
|
||||
* <enumeration value="pendingDrops"/>
|
||||
* <enumeration value="baseline"/>
|
||||
* </restriction>
|
||||
* </simpleType>
|
||||
@@ -28,8 +28,8 @@ public enum ChangeSetType {
|
||||
|
||||
@XmlEnumValue("apply")
|
||||
APPLY("apply"),
|
||||
@XmlEnumValue("drop")
|
||||
DROP("drop"),
|
||||
@XmlEnumValue("pendingDrops")
|
||||
PENDING_DROPS("pendingDrops"),
|
||||
@XmlEnumValue("baseline")
|
||||
BASELINE("baseline");
|
||||
private final String value;
|
||||
|
||||
@@ -30,6 +30,7 @@ import javax.xml.bind.annotation.XmlType;
|
||||
* <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}tablespaceAttributes"/>
|
||||
* <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="withHistory" type="{http://www.w3.org/2001/XMLSchema}boolean" />
|
||||
* <attribute name="draft" type="{http://www.w3.org/2001/XMLSchema}boolean" />
|
||||
* <attribute name="identityType" type="{http://ebean-orm.github.io/xml/ns/dbmigration}identityType" />
|
||||
* <attribute name="sequenceName" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="sequenceInitial" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" />
|
||||
@@ -59,6 +60,8 @@ public class CreateTable {
|
||||
protected String name;
|
||||
@XmlAttribute(name = "withHistory")
|
||||
protected Boolean withHistory;
|
||||
@XmlAttribute(name = "draft")
|
||||
protected Boolean draft;
|
||||
@XmlAttribute(name = "identityType")
|
||||
protected IdentityType identityType;
|
||||
@XmlAttribute(name = "sequenceName")
|
||||
@@ -213,6 +216,30 @@ public class CreateTable {
|
||||
this.withHistory = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the draft property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link Boolean }
|
||||
*
|
||||
*/
|
||||
public Boolean isDraft() {
|
||||
return draft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the draft property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link Boolean }
|
||||
*
|
||||
*/
|
||||
public void setDraft(Boolean value) {
|
||||
this.draft = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the identityType property.
|
||||
*
|
||||
|
||||
@@ -117,13 +117,13 @@ public class CurrentModel {
|
||||
/**
|
||||
* Return the 'Drop' DDL.
|
||||
*/
|
||||
public String getDropDdl() throws IOException {
|
||||
public String getDropAllDdl() throws IOException {
|
||||
|
||||
createDdl();
|
||||
|
||||
StringBuilder ddl = new StringBuilder(2000);
|
||||
ddl.append(write.rollbackForeignKeys().getBuffer());
|
||||
ddl.append(write.rollback().getBuffer());
|
||||
ddl.append(write.dropAllForeignKeys().getBuffer());
|
||||
ddl.append(write.dropAll().getBuffer());
|
||||
|
||||
return ddl.toString();
|
||||
}
|
||||
@@ -159,12 +159,7 @@ public class CurrentModel {
|
||||
ModelDiff diff = new ModelDiff();
|
||||
diff.compareTo(model);
|
||||
|
||||
List<Object> applyChanges = diff.getApplyChanges();
|
||||
|
||||
// put the changes into a ChangeSet
|
||||
ChangeSet applyChangeSet = new ChangeSet();
|
||||
applyChangeSet.getChangeSetChildren().addAll(applyChanges);
|
||||
return applyChangeSet;
|
||||
return diff.getApplyChangeSet();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -283,7 +283,6 @@ public class MColumn {
|
||||
/**
|
||||
* Compare the column meta data and return true if there is a change that means
|
||||
* the history table column needs
|
||||
|
||||
*/
|
||||
public void compare(ModelDiff modelDiff, MTable table, MColumn newColumn) {
|
||||
|
||||
|
||||
@@ -10,13 +10,14 @@ import com.avaje.ebean.dbmigration.migration.DropHistoryTable;
|
||||
import com.avaje.ebean.dbmigration.migration.DropTable;
|
||||
import com.avaje.ebean.dbmigration.migration.IdentityType;
|
||||
import com.avaje.ebean.dbmigration.migration.UniqueConstraint;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -38,6 +39,8 @@ import java.util.Set;
|
||||
*/
|
||||
public class MTable {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MTable.class);
|
||||
|
||||
/**
|
||||
* Table name.
|
||||
*/
|
||||
@@ -118,6 +121,8 @@ public class MTable {
|
||||
*/
|
||||
private AddColumn addColumn;
|
||||
|
||||
private List<String> droppedColumns = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* Create a copy of this table structure as a 'draft' table.
|
||||
*
|
||||
@@ -126,14 +131,14 @@ public class MTable {
|
||||
*/
|
||||
public MTable createDraftTable() {
|
||||
|
||||
draftTable = new MTable(name+"_draft");
|
||||
draftTable = new MTable(name + "_draft");
|
||||
draftTable.draft = true;
|
||||
draftTable.whenCreatedColumn = whenCreatedColumn;
|
||||
// compoundKeys
|
||||
// compoundUniqueConstraints
|
||||
draftTable.identityType = identityType;
|
||||
|
||||
for (MColumn col: columns.values()) {
|
||||
for (MColumn col : allColumns()) {
|
||||
draftTable.addColumn(col.copyForDraft());
|
||||
}
|
||||
|
||||
@@ -150,6 +155,7 @@ public class MTable {
|
||||
this.tablespace = createTable.getTablespace();
|
||||
this.indexTablespace = createTable.getIndexTablespace();
|
||||
this.withHistory = Boolean.TRUE.equals(createTable.isWithHistory());
|
||||
this.draft = Boolean.TRUE.equals(createTable.isDraft());
|
||||
this.sequenceName = createTable.getSequenceName();
|
||||
this.sequenceInitial = toInt(createTable.getSequenceInitial());
|
||||
this.sequenceAllocate = toInt(createTable.getSequenceAllocate());
|
||||
@@ -194,8 +200,11 @@ public class MTable {
|
||||
if (withHistory) {
|
||||
createTable.setWithHistory(Boolean.TRUE);
|
||||
}
|
||||
if (draft) {
|
||||
createTable.setDraft(Boolean.TRUE);
|
||||
}
|
||||
|
||||
for (MColumn column : this.columns.values()) {
|
||||
for (MColumn column : allColumns()) {
|
||||
// filter out draftOnly columns from the base table
|
||||
if (draft || !column.isDraftOnly()) {
|
||||
createTable.getColumn().add(column.createColumn());
|
||||
@@ -244,26 +253,33 @@ public class MTable {
|
||||
|
||||
addColumn = null;
|
||||
|
||||
Set<String> mappedColumns = new LinkedHashSet<String>();
|
||||
Map<String, MColumn> newColumnMap = newTable.getColumns();
|
||||
|
||||
Collection<MColumn> newColumns = newTable.getColumns().values();
|
||||
for (MColumn newColumn : newColumns) {
|
||||
MColumn localColumn = columns.get(newColumn.getName());
|
||||
// compare newColumns to existing columns (look for new and diff columns)
|
||||
for (MColumn newColumn : newColumnMap.values()) {
|
||||
MColumn localColumn = getColumn(newColumn.getName());
|
||||
if (localColumn == null) {
|
||||
diffNewColumn(newColumn);
|
||||
// can ignore if draftOnly column and non-draft table
|
||||
if (!newColumn.isDraftOnly() || draft) {
|
||||
diffNewColumn(newColumn);
|
||||
}
|
||||
} else {
|
||||
// note that if there are alter column changes in here then
|
||||
// the table withHistory is taken into account
|
||||
localColumn.compare(modelDiff, this, newColumn);
|
||||
mappedColumns.add(newColumn.getName());
|
||||
}
|
||||
}
|
||||
|
||||
Collection<MColumn> existingColumns = columns.values();
|
||||
for (MColumn existingColumn : existingColumns) {
|
||||
if (!mappedColumns.contains(existingColumn.getName())) {
|
||||
diffDropColumn(modelDiff, existingColumn);
|
||||
// compare existing columns (look for dropped columns)
|
||||
int columnPosition = 0;
|
||||
for (MColumn existingColumn : allColumns()) {
|
||||
MColumn newColumn = newColumnMap.get(existingColumn.getName());
|
||||
if (newColumn == null) {
|
||||
diffDropColumn(modelDiff, existingColumn, columnPosition, newTable);
|
||||
} else if (newColumn.isDraftOnly() && !draft) {
|
||||
// effectively a drop column (draft only column on a non-draft table)
|
||||
logger.trace("... drop column {} from table {} as now draftOnly", newColumn.getName(), name);
|
||||
diffDropColumn(modelDiff, existingColumn, columnPosition, newTable);
|
||||
}
|
||||
columnPosition++;
|
||||
}
|
||||
|
||||
if (addColumn != null) {
|
||||
@@ -287,7 +303,7 @@ public class MTable {
|
||||
public void apply(AlterColumn alterColumn) {
|
||||
checkTableName(alterColumn.getTableName());
|
||||
String columnName = alterColumn.getColumnName();
|
||||
MColumn existingColumn = columns.get(columnName);
|
||||
MColumn existingColumn = getColumn(columnName);
|
||||
if (existingColumn == null) {
|
||||
throw new IllegalStateException("Column [" + columnName + "] does not exist for AlterColumn change?");
|
||||
}
|
||||
@@ -299,7 +315,10 @@ public class MTable {
|
||||
*/
|
||||
public void apply(DropColumn dropColumn) {
|
||||
checkTableName(dropColumn.getTableName());
|
||||
columns.remove(dropColumn.getColumnName());
|
||||
MColumn removed = columns.remove(dropColumn.getColumnName());
|
||||
if (removed == null) {
|
||||
throw new IllegalStateException("Column [" + dropColumn.getColumnName() + "] does not exist for DropColumn change on table [" + dropColumn.getTableName() + "]?");
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@@ -313,10 +332,6 @@ public class MTable {
|
||||
return draft;
|
||||
}
|
||||
|
||||
public String getPkName() {
|
||||
return pkName;
|
||||
}
|
||||
|
||||
public void setPkName(String pkName) {
|
||||
this.pkName = pkName;
|
||||
}
|
||||
@@ -341,11 +356,43 @@ public class MTable {
|
||||
return withHistory;
|
||||
}
|
||||
|
||||
public void setWithHistory(boolean withHistory) {
|
||||
public MTable setWithHistory(boolean withHistory) {
|
||||
this.withHistory = withHistory;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, MColumn> getColumns() {
|
||||
public List<String> allHistoryColumns(boolean includeDropped) {
|
||||
|
||||
List<String> columnNames = new ArrayList<String>(columns.size());
|
||||
for (MColumn column : columns.values()) {
|
||||
if (column.isIncludeInHistory()) {
|
||||
columnNames.add(column.getName());
|
||||
}
|
||||
}
|
||||
if (includeDropped && !droppedColumns.isEmpty()) {
|
||||
for (String droppedColumn : droppedColumns) {
|
||||
columnNames.add(droppedColumn);
|
||||
}
|
||||
}
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the columns (excluding columns marked as dropped).
|
||||
*/
|
||||
public Collection<MColumn> allColumns() {
|
||||
|
||||
return columns.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the column by name.
|
||||
*/
|
||||
public MColumn getColumn(String name) {
|
||||
return columns.get(name);
|
||||
}
|
||||
|
||||
private Map<String, MColumn> getColumns() {
|
||||
return columns;
|
||||
}
|
||||
|
||||
@@ -357,26 +404,14 @@ public class MTable {
|
||||
return compoundKeys;
|
||||
}
|
||||
|
||||
public String getSequenceName() {
|
||||
return sequenceName;
|
||||
}
|
||||
|
||||
public void setSequenceName(String sequenceName) {
|
||||
this.sequenceName = sequenceName;
|
||||
}
|
||||
|
||||
public int getSequenceInitial() {
|
||||
return sequenceInitial;
|
||||
}
|
||||
|
||||
public void setSequenceInitial(int sequenceInitial) {
|
||||
this.sequenceInitial = sequenceInitial;
|
||||
}
|
||||
|
||||
public int getSequenceAllocate() {
|
||||
return sequenceAllocate;
|
||||
}
|
||||
|
||||
public void setSequenceAllocate(int sequenceAllocate) {
|
||||
this.sequenceAllocate = sequenceAllocate;
|
||||
}
|
||||
@@ -399,22 +434,12 @@ public class MTable {
|
||||
this.identityType = identityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identity type to use for this table.
|
||||
* <p>
|
||||
* If set then this overrides the platform default so for UUID generated values
|
||||
* or DB's supporting both sequences and autoincrement.
|
||||
*/
|
||||
public IdentityType getIdentityType() {
|
||||
return identityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of columns that make the primary key.
|
||||
*/
|
||||
public List<MColumn> primaryKeyColumns() {
|
||||
List<MColumn> pk = new ArrayList<MColumn>(3);
|
||||
for (MColumn column : columns.values()) {
|
||||
for (MColumn column : allColumns()) {
|
||||
if (column.isPrimaryKey()) {
|
||||
pk.add(column);
|
||||
}
|
||||
@@ -473,7 +498,7 @@ public class MTable {
|
||||
*/
|
||||
public MColumn addColumn(String dbCol, String columnDefn, boolean notnull) {
|
||||
|
||||
MColumn existingColumn = columns.get(dbCol);
|
||||
MColumn existingColumn = getColumn(dbCol);
|
||||
if (existingColumn != null) {
|
||||
if (notnull) {
|
||||
existingColumn.setNotnull(true);
|
||||
@@ -507,12 +532,12 @@ public class MTable {
|
||||
/**
|
||||
* Add a 'drop column' to the diff.
|
||||
*/
|
||||
private void diffDropColumn(ModelDiff modelDiff, MColumn existingColumn) {
|
||||
private void diffDropColumn(ModelDiff modelDiff, MColumn existingColumn, int columnPosition, MTable newTable) {
|
||||
|
||||
DropColumn dropColumn = new DropColumn();
|
||||
dropColumn.setTableName(name);
|
||||
dropColumn.setColumnName(existingColumn.getName());
|
||||
if (withHistory) {
|
||||
if (withHistory && !existingColumn.isHistoryExclude()) {
|
||||
// These dropColumns should occur on the history
|
||||
// table as well as the base table
|
||||
dropColumn.setWithHistory(Boolean.TRUE);
|
||||
@@ -521,6 +546,16 @@ public class MTable {
|
||||
modelDiff.addDropColumn(dropColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a pending un-applied drop column.
|
||||
* <p>
|
||||
* This means this column still needs to be included in history views/triggers etc even
|
||||
* though it is not part of the current model.
|
||||
*/
|
||||
public void registerPendingDropColumn(String columnName) {
|
||||
droppedColumns.add(columnName);
|
||||
}
|
||||
|
||||
private int toInt(BigInteger value) {
|
||||
return (value == null) ? 0 : value.intValue();
|
||||
}
|
||||
@@ -532,7 +567,7 @@ public class MTable {
|
||||
/**
|
||||
* Check if there are duplicate foreign keys.
|
||||
* <p>
|
||||
* This can occur when an ManyToMany relates back to itself.
|
||||
* This can occur when an ManyToMany relates back to itself.
|
||||
* </p>
|
||||
*/
|
||||
public void checkDuplicateForeignKeys() {
|
||||
@@ -563,7 +598,7 @@ public class MTable {
|
||||
*/
|
||||
public void adjustReferences(ModelContainer modelContainer) {
|
||||
|
||||
Collection<MColumn> cols = columns.values();
|
||||
Collection<MColumn> cols = allColumns();
|
||||
for (MColumn col : cols) {
|
||||
String references = col.getReferences();
|
||||
if (references != null) {
|
||||
@@ -583,7 +618,7 @@ public class MTable {
|
||||
*/
|
||||
private String extractBaseTable(String references) {
|
||||
int lastDot = references.lastIndexOf('.');
|
||||
return references.substring(0,lastDot);
|
||||
return references.substring(0, lastDot);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -592,7 +627,7 @@ public class MTable {
|
||||
*/
|
||||
private String deriveReferences(String references, String draftTableName) {
|
||||
int lastDot = references.lastIndexOf('.');
|
||||
return draftTableName+"."+references.substring(lastDot+1);
|
||||
return draftTableName + "." + references.substring(lastDot + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public class MigrationModel {
|
||||
List<MigrationResource> resources = new ArrayList<MigrationResource>();
|
||||
|
||||
for (File xmlFile: xmlFiles) {
|
||||
resources.add(new MigrationResource(xmlFile));
|
||||
resources.add(new MigrationResource(xmlFile, createVersion(xmlFile)));
|
||||
}
|
||||
|
||||
// sort into version order before applying
|
||||
@@ -60,7 +60,7 @@ public class MigrationModel {
|
||||
|
||||
for (MigrationResource migrationResource: resources) {
|
||||
logger.debug("read {}", migrationResource);
|
||||
model.apply(migrationResource.read());
|
||||
model.apply(migrationResource.read(), migrationResource.getVersion());
|
||||
}
|
||||
|
||||
// remember the last version
|
||||
@@ -69,6 +69,12 @@ public class MigrationModel {
|
||||
}
|
||||
}
|
||||
|
||||
private MigrationVersion createVersion(File xmlFile) {
|
||||
String fileName = xmlFile.getName();
|
||||
String versionName = fileName.substring(0, fileName.length() - modelSuffix.length());
|
||||
return MigrationVersion.parse(versionName);
|
||||
}
|
||||
|
||||
public String getNextVersion(String initialVersion) {
|
||||
|
||||
return lastVersion == null ? initialVersion : lastVersion.nextVersion();
|
||||
|
||||
@@ -17,9 +17,9 @@ public class MigrationResource implements Comparable<MigrationResource> {
|
||||
/**
|
||||
* Construct with a migration xml file.
|
||||
*/
|
||||
public MigrationResource(File migrationFile) {
|
||||
public MigrationResource(File migrationFile, MigrationVersion version) {
|
||||
this.migrationFile = migrationFile;
|
||||
this.version = MigrationVersion.parse(migrationFile.getName());
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebean.dbmigration.model;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* The version of a migration used so that migrations are processed in order.
|
||||
*/
|
||||
@@ -15,23 +17,74 @@ public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
*/
|
||||
private final int[] ordering;
|
||||
|
||||
private MigrationVersion(String raw, int[] ordering) {
|
||||
private final boolean[] underscores;
|
||||
|
||||
private final String comment;
|
||||
|
||||
private MigrationVersion(String raw, int[] ordering, boolean[] underscores, String comment) {
|
||||
this.raw = raw;
|
||||
this.ordering = ordering;
|
||||
this.underscores = underscores;
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the version comment.
|
||||
*/
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the version in raw form.
|
||||
*/
|
||||
public String getRaw() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the trimmed version excluding version comment and un-parsable string.
|
||||
*/
|
||||
public String asString() {
|
||||
return formattedVersion(false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the trimmed version with any underscores replaced with '.'
|
||||
*/
|
||||
public String normalised() {
|
||||
return formattedVersion(true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next version based on this version.
|
||||
*/
|
||||
public String nextVersion() {
|
||||
return formattedVersion(false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version part of the string.
|
||||
*
|
||||
* Normalised means always use '.' delimiters (no underscores).
|
||||
* NextVersion means bump/increase the last version number by 1.
|
||||
*/
|
||||
private String formattedVersion(boolean normalised, boolean nextVersion) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < ordering.length; i++) {
|
||||
if (i < ordering.length -1 ) {
|
||||
sb.append(ordering[i]).append(".");
|
||||
if (i < ordering.length - 1) {
|
||||
sb.append(ordering[i]);
|
||||
if (normalised) {
|
||||
sb.append('.');
|
||||
} else {
|
||||
sb.append(underscores[i] ? '_' : '.');
|
||||
}
|
||||
} else {
|
||||
sb.append(ordering[i]+1);
|
||||
sb.append((nextVersion) ? ordering[i] + 1 : ordering[i]);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
@@ -54,33 +107,54 @@ public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the raw version string and just return the leading version number;
|
||||
*/
|
||||
public static String trim(String raw) {
|
||||
return parse(raw).asString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the raw version string into a MigrationVersion.
|
||||
*/
|
||||
public static MigrationVersion parse(String raw) {
|
||||
|
||||
String value = raw.replace("__",".");
|
||||
value = value.replace('_','.');
|
||||
String comment = "";
|
||||
String value = raw;
|
||||
int commentStart = raw.indexOf("__");
|
||||
if (commentStart > -1) {
|
||||
// trim off the trailing comment
|
||||
comment = raw.substring(commentStart + 2);
|
||||
value = value.substring(0, commentStart);
|
||||
}
|
||||
|
||||
value = value.replace('_', '.');
|
||||
|
||||
String[] sections = value.split("\\.");
|
||||
|
||||
boolean[] underscores = new boolean[sections.length];
|
||||
int[] ordering = new int[sections.length];
|
||||
|
||||
int delimiterPos = 0;
|
||||
int stopIndex = 0;
|
||||
for (int i = 0; i < sections.length; i++) {
|
||||
try {
|
||||
ordering[i] = Integer.parseInt(sections[i]);
|
||||
stopIndex++;
|
||||
|
||||
delimiterPos += sections[i].length();
|
||||
underscores[i] = (delimiterPos < raw.length() - 1 && raw.charAt(delimiterPos) == '_');
|
||||
delimiterPos++;
|
||||
} catch (NumberFormatException e) {
|
||||
// stop parsing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int[] actualOrder = new int[stopIndex];
|
||||
System.arraycopy(ordering, 0, actualOrder, 0, stopIndex);
|
||||
int[] actualOrder = Arrays.copyOf(ordering, stopIndex);
|
||||
boolean[] actualUnderscores = Arrays.copyOf(underscores, stopIndex);
|
||||
|
||||
return new MigrationVersion(raw, actualOrder);
|
||||
return new MigrationVersion(raw, actualOrder, actualUnderscores, comment);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.avaje.ebean.dbmigration.migration.AddColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.AddHistoryTable;
|
||||
import com.avaje.ebean.dbmigration.migration.AlterColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.ChangeSet;
|
||||
import com.avaje.ebean.dbmigration.migration.ChangeSetType;
|
||||
import com.avaje.ebean.dbmigration.migration.CreateIndex;
|
||||
import com.avaje.ebean.dbmigration.migration.CreateTable;
|
||||
import com.avaje.ebean.dbmigration.migration.DropColumn;
|
||||
@@ -12,7 +13,6 @@ import com.avaje.ebean.dbmigration.migration.DropIndex;
|
||||
import com.avaje.ebean.dbmigration.migration.DropTable;
|
||||
import com.avaje.ebean.dbmigration.migration.Migration;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -28,23 +28,23 @@ public class ModelContainer {
|
||||
/**
|
||||
* All the tables in the model.
|
||||
*/
|
||||
private Map<String, MTable> tables = new LinkedHashMap<String, MTable>();
|
||||
private final Map<String, MTable> tables = new LinkedHashMap<String, MTable>();
|
||||
|
||||
/**
|
||||
* All the non unique non foreign key indexes.
|
||||
*/
|
||||
private Map<String, MIndex> indexes = new LinkedHashMap<String, MIndex>();
|
||||
private final Map<String, MIndex> indexes = new LinkedHashMap<String, MIndex>();
|
||||
|
||||
private final PendingDrops pendingDrops = new PendingDrops();
|
||||
|
||||
public ModelContainer() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the FK references on all the draft tables.
|
||||
*/
|
||||
public void adjustDraftReferences() {
|
||||
Collection<MTable> tables = this.tables.values();
|
||||
for (MTable table : tables) {
|
||||
for (MTable table : this.tables.values()) {
|
||||
if (table.isDraft()) {
|
||||
table.adjustReferences(this);
|
||||
}
|
||||
@@ -82,14 +82,31 @@ public class ModelContainer {
|
||||
/**
|
||||
* Apply a migration with associated changeSets to the model.
|
||||
*/
|
||||
public void apply(Migration migration) {
|
||||
public void apply(Migration migration, MigrationVersion version) {
|
||||
|
||||
List<ChangeSet> changeSets = migration.getChangeSet();
|
||||
for (ChangeSet changeSet : changeSets) {
|
||||
applyChangeSet(changeSet);
|
||||
boolean pending = changeSet.getType() == ChangeSetType.PENDING_DROPS;
|
||||
if (pending) {
|
||||
// un-applied drop columns etc
|
||||
pendingDrops.add(version, changeSet);
|
||||
|
||||
} else if (isDropsFor(changeSet)) {
|
||||
pendingDrops.appliedDropsFor(changeSet);
|
||||
}
|
||||
if (!isDropsFor(changeSet)) {
|
||||
applyChangeSet(changeSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the changeSet contains drops for a previous PENDING_DROPS changeSet.
|
||||
*/
|
||||
private boolean isDropsFor(ChangeSet changeSet) {
|
||||
return changeSet.getDropsFor() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a changeSet to the model.
|
||||
*/
|
||||
@@ -245,4 +262,53 @@ public class ModelContainer {
|
||||
|
||||
indexes.put(indexName, new MIndex(indexName, tableName, columnNames));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of versions containing un-applied pending drops.
|
||||
*/
|
||||
public List<String> getPendingDrops() {
|
||||
return pendingDrops.pendingDrops();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the migration for the pending drops for a given version.
|
||||
*/
|
||||
public Migration migrationForPendingDrop(String pendingVersion) {
|
||||
return pendingDrops.migrationForVersion(pendingVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the drop columns on history tables that have not been applied yet.
|
||||
*/
|
||||
public void registerPendingHistoryDropColumns(ModelContainer newModel) {
|
||||
pendingDrops.registerPendingHistoryDropColumns(newModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any pending drop columns on history tables. These columns are now not in the current
|
||||
* logical model but we still need to include them in the history views and triggers until they
|
||||
* are actually dropped.
|
||||
*/
|
||||
public void registerPendingHistoryDropColumns(ChangeSet changeSet) {
|
||||
for (Object change : changeSet.getChangeSetChildren()) {
|
||||
if (change instanceof DropColumn) {
|
||||
DropColumn dropColumn = (DropColumn) change;
|
||||
if (Boolean.TRUE.equals(dropColumn.isWithHistory())) {
|
||||
registerPendingDropColumn(dropColumn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a drop column on a history tables that has not been applied yet.
|
||||
*/
|
||||
private void registerPendingDropColumn(DropColumn dropColumn) {
|
||||
|
||||
MTable table = getTable(dropColumn.getTableName());
|
||||
if (table == null) {
|
||||
throw new IllegalArgumentException("Table ["+dropColumn.getTableName()+"] not found?");
|
||||
}
|
||||
table.registerPendingDropColumn(dropColumn.getColumnName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,16 +66,14 @@ public class ModelDiff {
|
||||
public Migration getMigration() {
|
||||
|
||||
Migration migration = new Migration();
|
||||
ChangeSet applyChangeSet = getApplyChangeSet();
|
||||
if (!applyChangeSet.getChangeSetChildren().isEmpty()) {
|
||||
if (!applyChanges.isEmpty()) {
|
||||
// add a non empty apply changeSet
|
||||
migration.getChangeSet().add(applyChangeSet);
|
||||
migration.getChangeSet().add(getApplyChangeSet());
|
||||
}
|
||||
|
||||
ChangeSet dropChangeSet = getDropChangeSet();
|
||||
if (!dropChangeSet.getChangeSetChildren().isEmpty()) {
|
||||
if (!dropChanges.isEmpty()) {
|
||||
// add a non empty drop changeSet
|
||||
migration.getChangeSet().add(dropChangeSet);
|
||||
migration.getChangeSet().add(getDropChangeSet());
|
||||
}
|
||||
return migration;
|
||||
}
|
||||
@@ -83,14 +81,14 @@ public class ModelDiff {
|
||||
/**
|
||||
* Return the list of 'apply' changes.
|
||||
*/
|
||||
public List<Object> getApplyChanges() {
|
||||
List<Object> getApplyChanges() {
|
||||
return applyChanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of 'drop' changes.
|
||||
*/
|
||||
public List<Object> getDropChanges() {
|
||||
List<Object> getDropChanges() {
|
||||
return dropChanges;
|
||||
}
|
||||
|
||||
@@ -108,10 +106,10 @@ public class ModelDiff {
|
||||
/**
|
||||
* Return the 'drop' changeSet.
|
||||
*/
|
||||
public ChangeSet getDropChangeSet() {
|
||||
ChangeSet getDropChangeSet() {
|
||||
// put the changes into a ChangeSet
|
||||
ChangeSet createChangeSet = new ChangeSet();
|
||||
createChangeSet.setType(ChangeSetType.DROP);
|
||||
createChangeSet.setType(ChangeSetType.PENDING_DROPS);
|
||||
createChangeSet.getChangeSetChildren().addAll(dropChanges);
|
||||
return createChangeSet;
|
||||
}
|
||||
@@ -156,6 +154,12 @@ public class ModelDiff {
|
||||
}
|
||||
}
|
||||
|
||||
// register un-applied ones from the previous migrations
|
||||
baseModel.registerPendingHistoryDropColumns(newModel);
|
||||
if (!dropChanges.isEmpty()) {
|
||||
// register new ones created just now as part of this diff
|
||||
newModel.registerPendingHistoryDropColumns(getDropChangeSet());
|
||||
}
|
||||
}
|
||||
|
||||
protected void addDropTable(MTable existingTable) {
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
package com.avaje.ebean.dbmigration.model;
|
||||
|
||||
import com.avaje.ebean.dbmigration.migration.ChangeSet;
|
||||
import com.avaje.ebean.dbmigration.migration.ChangeSetType;
|
||||
import com.avaje.ebean.dbmigration.migration.DropColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.DropTable;
|
||||
import com.avaje.ebean.dbmigration.migration.Migration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The migrations with pending un-applied drops.
|
||||
*/
|
||||
public class PendingDrops {
|
||||
|
||||
private final LinkedHashMap<String, Entry> map = new LinkedHashMap<String, Entry>();
|
||||
|
||||
/**
|
||||
* Add a 'pending drops' changeSet for the given version.
|
||||
*/
|
||||
public void add(MigrationVersion version, ChangeSet changeSet) {
|
||||
|
||||
Entry entry = map.get(version.normalised());
|
||||
if (entry == null) {
|
||||
entry = new Entry(version);
|
||||
map.put(version.normalised(), entry);
|
||||
}
|
||||
entry.add(changeSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of versions with pending drops.
|
||||
*/
|
||||
public List<String> pendingDrops() {
|
||||
|
||||
List<String> versions = new ArrayList<String>();
|
||||
for (Entry value : map.values()) {
|
||||
if (value.hasPendingDrops()) {
|
||||
versions.add(value.version.asString());
|
||||
}
|
||||
}
|
||||
return versions;
|
||||
}
|
||||
|
||||
/**
|
||||
* All the pending drops for this migration version have been applied so we need
|
||||
* to remove the (unsuppressed) pending drops for this version.
|
||||
*/
|
||||
public boolean appliedDropsFor(ChangeSet changeSet) {
|
||||
|
||||
MigrationVersion version = MigrationVersion.parse(changeSet.getDropsFor());
|
||||
|
||||
Entry entry = map.get(version.normalised());
|
||||
if (entry.removeDrops(changeSet)) {
|
||||
// it had no suppressForever changeSets so remove completely
|
||||
map.remove(version.normalised());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the migration for the pending drops from a version.
|
||||
* <p>
|
||||
* The value of version can be "next" to find the first un-applied pending drops.
|
||||
* </p>
|
||||
*/
|
||||
public Migration migrationForVersion(String pendingVersion) {
|
||||
|
||||
Entry entry = getEntry(pendingVersion);
|
||||
|
||||
Migration migration = new Migration();
|
||||
Iterator<ChangeSet> it = entry.list.iterator();
|
||||
while (it.hasNext()) {
|
||||
ChangeSet changeSet = it.next();
|
||||
if (!isSuppressForever(changeSet)) {
|
||||
it.remove();
|
||||
changeSet.setType(ChangeSetType.APPLY);
|
||||
changeSet.setDropsFor(entry.version.asString());
|
||||
migration.getChangeSet().add(changeSet);
|
||||
}
|
||||
}
|
||||
|
||||
if (migration.getChangeSet().isEmpty()) {
|
||||
throw new IllegalArgumentException("The remaining pendingDrops changeSets in migration ["+pendingVersion+"] are suppressDropsForever=true and can't be applied");
|
||||
}
|
||||
|
||||
if (!entry.containsSuppressForever()) {
|
||||
// we can remove it completely as it has no suppressForever changes
|
||||
map.remove(entry.version.normalised());
|
||||
}
|
||||
|
||||
return migration;
|
||||
}
|
||||
|
||||
private Entry getEntry(String pendingVersion) {
|
||||
|
||||
if ("next".equalsIgnoreCase(pendingVersion)) {
|
||||
Iterator<Entry> it = map.values().iterator();
|
||||
if (it.hasNext()) {
|
||||
return it.next();
|
||||
}
|
||||
} else {
|
||||
Entry remove = map.get(MigrationVersion.parse(pendingVersion).normalised());
|
||||
if (remove != null) {
|
||||
return remove;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No 'pendingDrops' changeSets for migration version [" + pendingVersion + "] found");
|
||||
}
|
||||
|
||||
/**
|
||||
* Register pending drop columns on history tables to the new model.
|
||||
*/
|
||||
public void registerPendingHistoryDropColumns(ModelContainer newModel) {
|
||||
|
||||
for (Entry entry : map.values()) {
|
||||
for (ChangeSet changeSet : entry.list) {
|
||||
newModel.registerPendingHistoryDropColumns(changeSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is an Entry for the given version.
|
||||
*/
|
||||
boolean testContainsEntryFor(MigrationVersion version) {
|
||||
return map.containsKey(version.normalised());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Entry for the given version.
|
||||
*/
|
||||
Entry testGetEntryFor(MigrationVersion version) {
|
||||
return map.get(version.normalised());
|
||||
}
|
||||
|
||||
static class Entry {
|
||||
|
||||
final MigrationVersion version;
|
||||
|
||||
final List<ChangeSet> list = new ArrayList<ChangeSet>();
|
||||
|
||||
Entry(MigrationVersion version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
void add(ChangeSet changeSet) {
|
||||
list.add(changeSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this contains suppressForever changeSets.
|
||||
*/
|
||||
boolean containsSuppressForever() {
|
||||
for (ChangeSet changeSet : list) {
|
||||
if (isSuppressForever(changeSet)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this contains drops that can be applied / migrated.
|
||||
*/
|
||||
boolean hasPendingDrops() {
|
||||
for (ChangeSet changeSet : list) {
|
||||
if (!isSuppressForever(changeSet)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the drops that are not suppressForever and return true if that
|
||||
* removed all the changeSets (and there are no suppressForever ones).
|
||||
*/
|
||||
boolean removeDrops(ChangeSet appliedDrops) {
|
||||
|
||||
Iterator<ChangeSet> iterator = list.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
ChangeSet next = iterator.next();
|
||||
if (!isSuppressForever(next)) {
|
||||
removeMatchingChanges(next, appliedDrops);
|
||||
if (next.getChangeSetChildren().isEmpty()) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the applied drops from the pending ones matching by table name and column name.
|
||||
*/
|
||||
private void removeMatchingChanges(ChangeSet pendingDrops, ChangeSet appliedDrops) {
|
||||
|
||||
List<Object> pending = pendingDrops.getChangeSetChildren();
|
||||
Iterator<Object> iterator = pending.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Object pendingDrop = iterator.next();
|
||||
if (pendingDrop instanceof DropColumn && dropColumnIn((DropColumn)pendingDrop, appliedDrops)) {
|
||||
iterator.remove();
|
||||
|
||||
} else if (pendingDrop instanceof DropTable && dropTableIn((DropTable)pendingDrop, appliedDrops)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the pendingDrop is contained in the appliedDrops.
|
||||
*/
|
||||
private boolean dropTableIn(DropTable pendingDrop, ChangeSet appliedDrops) {
|
||||
for (Object o : appliedDrops.getChangeSetChildren()) {
|
||||
if (o instanceof DropTable && sameTable(pendingDrop, (DropTable)o)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the pendingDrop is contained in the appliedDrops.
|
||||
*/
|
||||
private boolean dropColumnIn(DropColumn pendingDrop, ChangeSet appliedDrops) {
|
||||
for (Object o : appliedDrops.getChangeSetChildren()) {
|
||||
if (o instanceof DropColumn && sameColumn(pendingDrop, (DropColumn) o)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the DropTable match by table name.
|
||||
*/
|
||||
private boolean sameTable(DropTable pendingDrop, DropTable o) {
|
||||
return pendingDrop.getName().equals(o.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the DropColumns match by table and column name.
|
||||
*/
|
||||
private boolean sameColumn(DropColumn pending, DropColumn o) {
|
||||
return pending.getColumnName().equals(o.getColumnName())
|
||||
&& pending.getTableName().equals(o.getTableName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static boolean isSuppressForever(ChangeSet next) {
|
||||
return Boolean.TRUE.equals(next.isSuppressDropsForever());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,9 +3,11 @@ package com.avaje.ebean.dbmigration.model;
|
||||
import com.avaje.ebean.config.DbMigrationConfig;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
|
||||
import com.avaje.ebean.dbmigration.migration.ChangeSet;
|
||||
import com.avaje.ebean.dbmigration.migration.ChangeSetType;
|
||||
import com.avaje.ebean.dbmigration.migration.Migration;
|
||||
|
||||
import java.io.File;
|
||||
@@ -43,7 +45,7 @@ public class PlatformDdlWriter {
|
||||
|
||||
List<ChangeSet> changeSets = dbMigration.getChangeSet();
|
||||
for (ChangeSet changeSet : changeSets) {
|
||||
if (!changeSet.getChangeSetChildren().isEmpty()) {
|
||||
if (isApply(changeSet)) {
|
||||
handler.generate(write, changeSet);
|
||||
}
|
||||
}
|
||||
@@ -52,6 +54,13 @@ public class PlatformDdlWriter {
|
||||
writePlatformDdl(write, writePath, fullVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the changeSet is APPLY and not empty.
|
||||
*/
|
||||
private boolean isApply(ChangeSet changeSet) {
|
||||
return changeSet.getType() == ChangeSetType.APPLY && !changeSet.getChangeSetChildren().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the ddl files.
|
||||
*/
|
||||
@@ -65,26 +74,6 @@ public class PlatformDdlWriter {
|
||||
} finally {
|
||||
applyWriter.close();
|
||||
}
|
||||
|
||||
if (!config.isSuppressRollback() && !write.isApplyRollbackEmpty()) {
|
||||
FileWriter applyRollbackWriter = createWriter(resourcePath, fullVersion, config.getRollbackPath(), config.getRollbackSuffix());
|
||||
try {
|
||||
writeApplyRollbackDdl(applyRollbackWriter, write);
|
||||
applyRollbackWriter.flush();
|
||||
} finally {
|
||||
applyRollbackWriter.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!write.isDropEmpty()) {
|
||||
FileWriter dropWriter = createWriter(resourcePath, fullVersion, config.getDropPath(), config.getDropSuffix());
|
||||
try {
|
||||
writeDropDdl(dropWriter, write);
|
||||
dropWriter.flush();
|
||||
} finally {
|
||||
dropWriter.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,29 +105,19 @@ public class PlatformDdlWriter {
|
||||
protected void writeApplyDdl(Writer writer, DdlWrite write) throws IOException {
|
||||
|
||||
// merge the apply buffers in the appropriate order
|
||||
prependDropDependencies(writer, write.applyDropDependencies());
|
||||
writer.append("-- apply changes\n");
|
||||
writer.append(write.apply().getBuffer());
|
||||
writer.append(write.applyForeignKeys().getBuffer());
|
||||
writer.append(write.applyHistory().getBuffer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the 'Rollback' DDL buffers to the writer.
|
||||
*/
|
||||
protected void writeApplyRollbackDdl(Writer writer, DdlWrite write) throws IOException {
|
||||
|
||||
// merge the rollback buffers in the appropriate order
|
||||
writer.append(write.rollbackForeignKeys().getBuffer());
|
||||
writer.append(write.rollback().getBuffer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the 'Drop' DDL buffers to the writer.
|
||||
*/
|
||||
protected void writeDropDdl(Writer writer, DdlWrite write) throws IOException {
|
||||
|
||||
// merge the rollback buffers in the appropriate order
|
||||
writer.append(write.dropHistory().getBuffer());
|
||||
writer.append(write.drop().getBuffer());
|
||||
private void prependDropDependencies(Writer writer, DdlBuffer buffer) throws IOException {
|
||||
if (!buffer.isEmpty()) {
|
||||
writer.append("-- drop dependencies\n");
|
||||
writer.append(buffer.getBuffer());
|
||||
writer.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -158,7 +158,7 @@ public class ModelBuildContext {
|
||||
int fkCount = 0;
|
||||
int ixCount = 0;
|
||||
int uqCount = 0;
|
||||
Collection<MColumn> cols = draftTable.getColumns().values();
|
||||
Collection<MColumn> cols = draftTable.allColumns();
|
||||
for (MColumn col: cols) {
|
||||
if (col.getForeignKeyName() != null) {
|
||||
// Note that we adjust the 'references' table later in a second pass
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
table.setPkName(determinePrimaryKeyName());
|
||||
|
||||
// check if indexes on foreign keys should be suppressed
|
||||
for (MColumn column : table.getColumns().values()) {
|
||||
for (MColumn column : table.allColumns()) {
|
||||
if (hasValue(column.getForeignKeyIndex())) {
|
||||
if (indexSet.contains(column.getName())) {
|
||||
// suppress index on foreign key as there is already
|
||||
|
||||
@@ -92,7 +92,7 @@ public class PathProperties {
|
||||
/**
|
||||
* Get the properties for a given path.
|
||||
*/
|
||||
public Set<String> get(String path) {
|
||||
public LinkedHashSet<String> get(String path) {
|
||||
Props props = pathMap.get(path);
|
||||
return props == null ? null : props.getProperties();
|
||||
}
|
||||
@@ -109,7 +109,7 @@ public class PathProperties {
|
||||
/**
|
||||
* Set the properties for a given path.
|
||||
*/
|
||||
public void put(String path, Set<String> properties) {
|
||||
public void put(String path, LinkedHashSet<String> properties) {
|
||||
pathMap.put(path, new Props(this, null, path, properties));
|
||||
}
|
||||
|
||||
@@ -160,9 +160,9 @@ public class PathProperties {
|
||||
private final String parentPath;
|
||||
private final String path;
|
||||
|
||||
private final Set<String> propSet;
|
||||
private final LinkedHashSet<String> propSet;
|
||||
|
||||
private Props(PathProperties owner, String parentPath, String path, Set<String> propSet) {
|
||||
private Props(PathProperties owner, String parentPath, String path, LinkedHashSet<String> propSet) {
|
||||
this.owner = owner;
|
||||
this.path = path;
|
||||
this.parentPath = parentPath;
|
||||
@@ -195,7 +195,7 @@ public class PathProperties {
|
||||
/**
|
||||
* Return the properties for this property set.
|
||||
*/
|
||||
public Set<String> getProperties() {
|
||||
public LinkedHashSet<String> getProperties() {
|
||||
return propSet;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* Key used for caching query plans for ORM and RawSql queries.
|
||||
*/
|
||||
public interface CQueryPlanKey {
|
||||
|
||||
/**
|
||||
* Used by read audit such that we can log read audit entries without the full sql
|
||||
* (which would make the read audit logs verbose).
|
||||
*/
|
||||
String getPartialKey();
|
||||
|
||||
}
|
||||
@@ -5,32 +5,18 @@ package com.avaje.ebeaninternal.api;
|
||||
*/
|
||||
public class HashQuery {
|
||||
|
||||
private final HashQueryPlan planHash;
|
||||
private final CQueryPlanKey planHash;
|
||||
|
||||
private final int bindHash;
|
||||
|
||||
/**
|
||||
* Create the HashQuery.
|
||||
*/
|
||||
public HashQuery(HashQueryPlan planHash, int bindHash) {
|
||||
public HashQuery(CQueryPlanKey planHash, int bindHash) {
|
||||
this.planHash = planHash;
|
||||
this.bindHash = bindHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query plan hash.
|
||||
*/
|
||||
public HashQueryPlan getPlanHash() {
|
||||
return planHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bind values hash.
|
||||
*/
|
||||
public int getBindHash() {
|
||||
return bindHash;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 31 * planHash.hashCode();
|
||||
hc = 31 * hc + bindHash;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Used to build HashQueryPlan instances.
|
||||
*/
|
||||
@@ -9,21 +11,19 @@ public class HashQueryPlanBuilder {
|
||||
|
||||
private int bindCount;
|
||||
|
||||
private String rawSql;
|
||||
|
||||
public HashQueryPlanBuilder() {
|
||||
this.planHash = 31;
|
||||
this.planHash = 92821;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return planHash+":"+bindCount+(rawSql != null ? ":r" : "");
|
||||
return planHash+":"+bindCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(Class<?> cls) {
|
||||
planHash = planHash * 31 + cls.getName().hashCode();
|
||||
planHash = planHash * 92821 + cls.getName().hashCode();
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,22 @@ public class HashQueryPlanBuilder {
|
||||
* Add an object to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(Object object) {
|
||||
planHash = planHash * 31 + (object == null ? 0 : object.hashCode());
|
||||
planHash = planHash * 92821 + (object == null ? 0 : object.hashCode());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the set with order being important.
|
||||
*/
|
||||
public HashQueryPlanBuilder addOrdered(Set<?> set) {
|
||||
if (set == null) {
|
||||
add(false);
|
||||
} else {
|
||||
add(true);
|
||||
for (Object o : set) {
|
||||
add(o);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -39,7 +54,7 @@ public class HashQueryPlanBuilder {
|
||||
* Add an integer to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(int hashValue) {
|
||||
planHash = planHash * 31 + (hashValue);
|
||||
planHash = planHash * 92821 + (hashValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -47,7 +62,7 @@ public class HashQueryPlanBuilder {
|
||||
* Add a boolean to the hash calculation.
|
||||
*/
|
||||
public HashQueryPlanBuilder add(boolean booleanValue) {
|
||||
planHash = planHash * 31 + (booleanValue ? 31 : 0);
|
||||
planHash = planHash * 92821 + (booleanValue ? 92821 : 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -58,19 +73,24 @@ public class HashQueryPlanBuilder {
|
||||
bindCount += extraBindCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add raw sql to the hash.
|
||||
*/
|
||||
public void addRawSql(String rawSql) {
|
||||
this.rawSql = rawSql;
|
||||
public void bindIfNotNull(Object someValue) {
|
||||
if (someValue != null) {
|
||||
bindCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and return the calculated HashQueryPlan.
|
||||
*/
|
||||
public HashQueryPlan build() {
|
||||
return new HashQueryPlan(rawSql, planHash, bindCount);
|
||||
public String build() {
|
||||
return planHash+"_"+bindCount;
|
||||
}
|
||||
|
||||
|
||||
public int getPlanHash() {
|
||||
return planHash;
|
||||
}
|
||||
|
||||
public int getBindCount() {
|
||||
return bindCount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Controls the loading of reference objects for a query instance.
|
||||
@@ -22,17 +25,7 @@ public interface LoadContext {
|
||||
*/
|
||||
void executeSecondaryQueries(OrmQueryRequest<?> parentRequest);
|
||||
|
||||
/**
|
||||
* Register any secondary queries (+query or +lazy) with their
|
||||
* appropriate LoadBeanContext or LoadManyContext.
|
||||
* <p>
|
||||
* This is so the LoadBeanContext or LoadManyContext use the
|
||||
* defined query for +query and +lazy execution.
|
||||
* </p>
|
||||
*/
|
||||
void registerSecondaryQueries(SpiQuery<?> query);
|
||||
|
||||
/**
|
||||
/**
|
||||
* Return the node for a given path which is used by AutoTune profiling.
|
||||
*/
|
||||
ObjectGraphNode getObjectGraphNode(String path);
|
||||
|
||||
@@ -13,11 +13,8 @@ import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditLogger;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest;
|
||||
import com.avaje.ebean.dbmigration.DdlGenerator;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.query.CQuery;
|
||||
import com.avaje.ebeaninternal.server.query.CQueryEngine;
|
||||
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
|
||||
|
||||
import java.util.List;
|
||||
@@ -130,22 +127,11 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
*/
|
||||
void remoteTransactionEvent(RemoteTransactionEvent event);
|
||||
|
||||
/**
|
||||
* Create a query request object.
|
||||
*/
|
||||
<T> SpiOrmQueryRequest<T> createQueryRequest(BeanDescriptor<T> desc, SpiQuery<T> q,
|
||||
Transaction t);
|
||||
|
||||
/**
|
||||
* Compile a query.
|
||||
*/
|
||||
<T> CQuery<T> compileQuery(Query<T> query, Transaction t);
|
||||
|
||||
/**
|
||||
* Return the queryEngine for this server.
|
||||
*/
|
||||
CQueryEngine getQueryEngine();
|
||||
|
||||
/**
|
||||
* Execute the findId's query but without copying the query.
|
||||
* <p>
|
||||
|
||||
@@ -17,32 +17,36 @@ public interface SpiExpression extends Expression {
|
||||
* </p>
|
||||
*/
|
||||
void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins);
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Prepare the expression. For example, compile sub-query expressions etc.
|
||||
*/
|
||||
void prepareExpression(BeanQueryRequest<?> request);
|
||||
|
||||
/**
|
||||
* Calculate a hash value used to identify a query for AutoTune tuning.
|
||||
* <p>
|
||||
* That is, if the hash changes then the query will be considered different
|
||||
* from an AutoTune perspective and get different tuning.
|
||||
* </p>
|
||||
*/
|
||||
void queryAutoTuneHash(HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Calculate a hash value for the expression.
|
||||
* This includes the expression type and property but should exclude
|
||||
* the bind values.
|
||||
* <p>
|
||||
* This is used where queries are the same except for the bind values, in which
|
||||
* case the query execution plan can be reused.
|
||||
* </p>
|
||||
*/
|
||||
void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder);
|
||||
|
||||
void queryPlanHash(HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Return the hash value for the values that will be bound.
|
||||
*/
|
||||
int queryBindHash();
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the expression is the same without taking into account bind values.
|
||||
*/
|
||||
boolean isSameByPlan(SpiExpression other);
|
||||
|
||||
/**
|
||||
* Return true if the expression is the same with respect to bind values.
|
||||
*/
|
||||
boolean isSameByBind(SpiExpression other);
|
||||
|
||||
/**
|
||||
* Add some sql to the query.
|
||||
* <p>
|
||||
@@ -61,7 +65,7 @@ public interface SpiExpression extends Expression {
|
||||
/**
|
||||
* Add the parameter values to be set against query. For each ? place holder
|
||||
* there should be a corresponding value that is added to the bindList.
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
* the associated request.
|
||||
*/
|
||||
@@ -71,4 +75,10 @@ public interface SpiExpression extends Expression {
|
||||
* Validate all the properties/paths associated with this expression.
|
||||
*/
|
||||
void validate(SpiExpressionValidation validation);
|
||||
|
||||
/**
|
||||
* Return a copy of the expression for use in the query plan key.
|
||||
*/
|
||||
SpiExpression copyForPlanKey();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.ExpressionFactory;
|
||||
import com.avaje.ebean.ExpressionList;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Internal extension of ExpressionList.
|
||||
*/
|
||||
public interface SpiExpressionList<T> extends ExpressionList<T> {
|
||||
public interface SpiExpressionList<T> extends ExpressionList<T>, SpiExpression {
|
||||
|
||||
/**
|
||||
* Return the underlying list of expressions.
|
||||
@@ -23,52 +19,9 @@ public interface SpiExpressionList<T> extends ExpressionList<T> {
|
||||
*/
|
||||
SpiExpressionList<?> trimPath(int prefixTrim);
|
||||
|
||||
/**
|
||||
* Restore the ExpressionFactory after deserialisation.
|
||||
*/
|
||||
void setExpressionFactory(ExpressionFactory expr);
|
||||
|
||||
/**
|
||||
* Process "Many" properties populating ManyWhereJoins.
|
||||
* <p>
|
||||
* Predicates on Many properties require an extra independent join clause.
|
||||
* </p>
|
||||
*/
|
||||
void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoins);
|
||||
|
||||
/**
|
||||
* Return true if this list is empty.
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Concatenate the expression sql into a String.
|
||||
* <p>
|
||||
* The list of expressions are evaluated in order building a sql statement
|
||||
* with bind parameters.
|
||||
* </p>
|
||||
*/
|
||||
String buildSql(SpiExpressionRequest request);
|
||||
|
||||
/**
|
||||
* Combine the expression bind values into a list.
|
||||
* <p>
|
||||
* Expressions are evaluated in order and all the resulting bind values are
|
||||
* returned as a List.
|
||||
* </p>
|
||||
*
|
||||
* @return the list of all the bind values in order.
|
||||
*/
|
||||
ArrayList<Object> buildBindValues(SpiExpressionRequest request);
|
||||
|
||||
/**
|
||||
* Calculate a hash based on the expressions but excluding the actual bind
|
||||
* values.
|
||||
*/
|
||||
void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Validate all the properties/paths used in this expression list.
|
||||
*/
|
||||
void validate(SpiExpressionValidation validation);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.avaje.ebeaninternal.server.core.JsonExpressionHandler;
|
||||
import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Request object used for gathering expression sql and bind values.
|
||||
*/
|
||||
@@ -54,7 +54,7 @@ public interface SpiExpressionRequest {
|
||||
/**
|
||||
* Return the ordered list of bind values for all expressions in this request.
|
||||
*/
|
||||
ArrayList<Object> getBindValues();
|
||||
List<Object> getBindValues();
|
||||
|
||||
/**
|
||||
* Increments the parameter index and returns that value.
|
||||
|
||||
@@ -289,11 +289,6 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
void setBeanDescriptor(BeanDescriptor<?> desc);
|
||||
|
||||
/**
|
||||
* Initialise/determine the joins required to support 'many' where clause predicates.
|
||||
*/
|
||||
boolean initManyWhereJoins();
|
||||
|
||||
/**
|
||||
* Return the joins required to support predicates on the many properties.
|
||||
*/
|
||||
@@ -319,31 +314,15 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
void setFilterMany(String prop, ExpressionList<?> filterMany);
|
||||
|
||||
/**
|
||||
* Remove the query joins from query detail.
|
||||
* <p>
|
||||
* These are registered with the Load Context.
|
||||
* </p>
|
||||
*/
|
||||
List<OrmQueryProperties> removeQueryJoins();
|
||||
|
||||
/**
|
||||
* Remove the lazy joins from query detail.
|
||||
* <p>
|
||||
* These are registered with the Load Context.
|
||||
* </p>
|
||||
*/
|
||||
List<OrmQueryProperties> removeLazyJoins();
|
||||
|
||||
/**
|
||||
* Set the path of the many when +query/+lazy loading query is executed.
|
||||
*/
|
||||
void setLazyLoadManyPath(String lazyLoadManyPath);
|
||||
|
||||
/**
|
||||
* Convert any many joins fetch joins to query joins.
|
||||
* Convert joins as necessary to query joins etc.
|
||||
*/
|
||||
void convertManyFetchJoinsToQueryJoins(boolean allowOne, int queryBatch);
|
||||
SpiQuerySecondary convertJoins();
|
||||
|
||||
/**
|
||||
* Return the TransactionContext.
|
||||
@@ -467,27 +446,13 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Calculate a hash used by AutoTune to identify when a query has changed
|
||||
* (and hence potentially needs a new tuned query plan to be developed).
|
||||
* Prepare the query which prepares sub-query expressions and calculates
|
||||
* and returns the query plan key.
|
||||
* <p>
|
||||
* Excludes bind values and occurs prior to AutoTune potentially
|
||||
* tuning/modifying the query.
|
||||
* The query plan excludes actual bind values (as they don't effect the query plan).
|
||||
* </p>
|
||||
*/
|
||||
HashQueryPlan queryAutoTuneHash(HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Identifies queries that are the same bar the bind variables.
|
||||
* <p>
|
||||
* This is used AFTER AutoTune has potentially tuned the query. This is
|
||||
* used to identify and reused query plans (the final SQL string and
|
||||
* associated SqlTree object).
|
||||
* </p>
|
||||
* <p>
|
||||
* Excludes the actual bind values (as they don't effect the query plan).
|
||||
* </p>
|
||||
*/
|
||||
HashQueryPlan queryPlanHash(BeanQueryRequest<?> request);
|
||||
CQueryPlanKey prepare(BeanQueryRequest<?> request);
|
||||
|
||||
/**
|
||||
* Calculate a hash based on the bind values used in the query.
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The secondary query paths for 'query joins' and 'lazy loading'.
|
||||
*/
|
||||
public interface SpiQuerySecondary {
|
||||
|
||||
/**
|
||||
* Return a list of path/properties that are query join loaded.
|
||||
*/
|
||||
List<OrmQueryProperties> getQueryJoins();
|
||||
|
||||
/**
|
||||
* Return the list of path/properties that are lazy loaded.
|
||||
*/
|
||||
List<OrmQueryProperties> getLazyJoins();
|
||||
}
|
||||
@@ -110,7 +110,7 @@ public class ProfileOrigin {
|
||||
Collection<Props> pathProperties = pathProps.getPathProps();
|
||||
for (Props props : pathProperties) {
|
||||
if (!props.isEmpty()) {
|
||||
detail.addFetch(props.getPath(), props.getPropertiesAsString(), null);
|
||||
detail.fetch(props.getPath(), props.getPropertiesAsString(), null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.dbmigration.DdlGenerator;
|
||||
import com.avaje.ebean.event.BeanPersistController;
|
||||
import com.avaje.ebean.event.BeanQueryAdapter;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditLogger;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import com.avaje.ebean.meta.MetaInfoManager;
|
||||
@@ -32,7 +31,6 @@ import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanPlugin;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery.Type;
|
||||
import com.avaje.ebeaninternal.api.SpiSqlQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
@@ -463,19 +461,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return cqueryEngine.buildQuery(orm);
|
||||
}
|
||||
|
||||
public CQueryEngine getQueryEngine() {
|
||||
return cqueryEngine;
|
||||
}
|
||||
|
||||
public ServerCacheManager getServerCacheManager() {
|
||||
return serverCacheManager;
|
||||
}
|
||||
|
||||
public void refreshMany(Object parentBean, String propertyName, Transaction t) {
|
||||
|
||||
beanLoader.refreshMany(checkEntityBean(parentBean), propertyName, t);
|
||||
}
|
||||
|
||||
public void refreshMany(Object parentBean, String propertyName) {
|
||||
|
||||
beanLoader.refreshMany(checkEntityBean(parentBean), propertyName);
|
||||
@@ -1048,7 +1037,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return findId(query, t);
|
||||
}
|
||||
|
||||
private <T> SpiOrmQueryRequest<T> createQueryRequest(Type type, Query<T> query, Transaction t) {
|
||||
<T> SpiOrmQueryRequest<T> createQueryRequest(Type type, Query<T> query, Transaction t) {
|
||||
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
spiQuery.setType(type);
|
||||
@@ -1059,7 +1048,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return createQueryRequest(desc, spiQuery, t);
|
||||
}
|
||||
|
||||
public <T> SpiOrmQueryRequest<T> createQueryRequest(BeanDescriptor<T> desc, SpiQuery<T> query, Transaction t) {
|
||||
private <T> SpiOrmQueryRequest<T> createQueryRequest(BeanDescriptor<T> desc, SpiQuery<T> query, Transaction t) {
|
||||
|
||||
if (desc.isAutoTunable() && !query.isSqlSelect() && !autoTuneService.tuneQuery(query)) {
|
||||
// use deployment FetchType.LAZY/EAGER annotations
|
||||
@@ -1067,50 +1056,15 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
query.setDefaultSelectClause();
|
||||
}
|
||||
|
||||
if (query.selectAllForLazyLoadProperty()) {
|
||||
// we need to select all properties to ensure the lazy load property
|
||||
// was included (was not included by default or via autoTune).
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Using selectAllForLazyLoadProperty");
|
||||
}
|
||||
}
|
||||
query.selectAllForLazyLoadProperty();
|
||||
|
||||
// if determine cost and no origin for AutoTune
|
||||
if (query.getParentNode() == null) {
|
||||
query.setOrigin(createCallStack());
|
||||
}
|
||||
|
||||
// determine extra joins required to support where clause
|
||||
// predicates on *ToMany properties
|
||||
if (query.initManyWhereJoins()) {
|
||||
// we need a sql distinct now
|
||||
query.setSqlDistinct(true);
|
||||
}
|
||||
|
||||
boolean allowOneManyFetch = true;
|
||||
if (Mode.LAZYLOAD_MANY.equals(query.getMode())) {
|
||||
allowOneManyFetch = false;
|
||||
|
||||
} else if (query.hasMaxRowsOrFirstRow() && !query.isRawSql() && !query.isSqlSelect()) {
|
||||
// convert ALL fetch joins to Many's to be query joins
|
||||
// so that limit offset type SQL clauses work
|
||||
allowOneManyFetch = false;
|
||||
}
|
||||
|
||||
query.convertManyFetchJoinsToQueryJoins(allowOneManyFetch, queryBatchSize);
|
||||
|
||||
SpiTransaction serverTrans = (SpiTransaction) t;
|
||||
OrmQueryRequest<T> request = new OrmQueryRequest<T>(this, queryEngine, query, desc, serverTrans);
|
||||
|
||||
BeanQueryAdapter queryAdapter = desc.getQueryAdapter();
|
||||
if (queryAdapter != null) {
|
||||
// adaption of the query probably based on the
|
||||
// current user
|
||||
queryAdapter.preQuery(request);
|
||||
}
|
||||
|
||||
// the query hash after any tuning
|
||||
request.calculateQueryPlanHash();
|
||||
OrmQueryRequest<T> request = new OrmQueryRequest<T>(this, queryEngine, query, desc, (SpiTransaction) t);
|
||||
request.prepareQuery();
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.*;
|
||||
import com.avaje.ebean.PersistenceContextScope;
|
||||
import com.avaje.ebean.QueryEachConsumer;
|
||||
import com.avaje.ebean.QueryEachWhileConsumer;
|
||||
import com.avaje.ebean.QueryIterator;
|
||||
import com.avaje.ebean.RawSql;
|
||||
import com.avaje.ebean.Version;
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.event.BeanFindController;
|
||||
import com.avaje.ebean.event.BeanQueryAdapter;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.BeanIdList;
|
||||
import com.avaje.ebeaninternal.api.CQueryPlanKey;
|
||||
import com.avaje.ebeaninternal.api.HashQuery;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlan;
|
||||
import com.avaje.ebeaninternal.api.LoadContext;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery.Type;
|
||||
import com.avaje.ebeaninternal.api.SpiQuerySecondary;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
@@ -32,6 +31,13 @@ import com.avaje.ebeaninternal.server.query.CQueryPlan;
|
||||
import com.avaje.ebeaninternal.server.query.CancelableQuery;
|
||||
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Wraps the objects involved in executing a Query.
|
||||
*/
|
||||
@@ -55,7 +61,9 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
|
||||
private HashQuery cacheKey;
|
||||
|
||||
private HashQueryPlan queryPlanHash;
|
||||
private CQueryPlanKey queryPlanKey;
|
||||
|
||||
private SpiQuerySecondary secondaryQueries;
|
||||
|
||||
/**
|
||||
* Create the InternalQueryRequest.
|
||||
@@ -126,10 +134,24 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the query plan hash AFTER any potential AutoTune tuning.
|
||||
* Run BeanQueryAdapter preQuery() if needed.
|
||||
*/
|
||||
public void calculateQueryPlanHash() {
|
||||
this.queryPlanHash = query.queryPlanHash(this);
|
||||
private void adapterPreQuery() {
|
||||
BeanQueryAdapter queryAdapter = beanDescriptor.getQueryAdapter();
|
||||
if (queryAdapter != null) {
|
||||
queryAdapter.preQuery(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the query and calculate the query plan key.
|
||||
*/
|
||||
public void prepareQuery() {
|
||||
|
||||
adapterPreQuery();
|
||||
|
||||
this.secondaryQueries = query.convertJoins();
|
||||
this.queryPlanKey = query.prepare(this);
|
||||
}
|
||||
|
||||
public boolean isRawSql() {
|
||||
@@ -190,8 +212,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
}
|
||||
// initialise the persistenceContext and loadContext
|
||||
this.persistenceContext = getPersistenceContext(query, transaction);
|
||||
this.loadContext = new DLoadContext(this);
|
||||
this.loadContext.registerSecondaryQueries(query);
|
||||
this.loadContext = new DLoadContext(this, secondaryQueries);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,7 +236,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
|
||||
// determine the scope (from the query and then server)
|
||||
PersistenceContextScope scope = ebeanServer.getPersistenceContextScope(query);
|
||||
return (scope == PersistenceContextScope.QUERY) ? new DefaultPersistenceContext() : t.getPersistenceContext();
|
||||
return (scope == PersistenceContextScope.QUERY) ? new DefaultPersistenceContext() : t.getPersistenceContext();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -305,7 +326,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Set<?> findSet() {
|
||||
return (Set<T>)queryEngine.findMany(this);
|
||||
return (Set<T>) queryEngine.findMany(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -324,10 +345,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
return (Map<?, ?>) queryEngine.findMany(this);
|
||||
}
|
||||
|
||||
public SpiQuery.Type getQueryType() {
|
||||
return query.getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a bean specific finder if one has been set.
|
||||
*/
|
||||
@@ -355,7 +372,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
* query plan for this query exists.
|
||||
*/
|
||||
public CQueryPlan getQueryPlan() {
|
||||
return beanDescriptor.getQueryPlan(queryPlanHash);
|
||||
return beanDescriptor.getQueryPlan(queryPlanKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -366,15 +383,15 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
* with just the bind variables changing.
|
||||
* </p>
|
||||
*/
|
||||
public HashQueryPlan getQueryPlanHash() {
|
||||
return queryPlanHash;
|
||||
public CQueryPlanKey getQueryPlanKey() {
|
||||
return queryPlanKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put the QueryPlan into the cache.
|
||||
*/
|
||||
public void putQueryPlan(CQueryPlan queryPlan) {
|
||||
beanDescriptor.putQueryPlan(queryPlanHash, queryPlan);
|
||||
beanDescriptor.putQueryPlan(queryPlanKey, queryPlan);
|
||||
}
|
||||
|
||||
public boolean isUseBeanCache() {
|
||||
@@ -401,7 +418,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
for (T bean : actualDetails) {
|
||||
ids.add(beanDescriptor.getIdForJson(bean));
|
||||
}
|
||||
beanDescriptor.readAuditMany(queryPlanHash.getPartialKey(), "l2-query-cache", ids);
|
||||
beanDescriptor.readAuditMany(queryPlanKey.getPartialKey(), "l2-query-cache", ids);
|
||||
}
|
||||
|
||||
return cached;
|
||||
|
||||
@@ -42,7 +42,7 @@ public interface BeanCollectionHelp<T> {
|
||||
/**
|
||||
* Add a bean to the List Set or Map.
|
||||
*/
|
||||
void add(BeanCollection<?> collection, EntityBean bean);
|
||||
void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck);
|
||||
|
||||
/**
|
||||
* Create a lazy loading proxy for a List Set or Map.
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.avaje.ebean.event.readaudit.ReadEvent;
|
||||
import com.avaje.ebean.meta.MetaBeanInfo;
|
||||
import com.avaje.ebean.meta.MetaQueryPlanStatistic;
|
||||
import com.avaje.ebean.plugin.SpiBeanType;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlan;
|
||||
import com.avaje.ebeaninternal.api.CQueryPlanKey;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
@@ -73,9 +73,9 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
@@ -87,7 +87,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
|
||||
private final ConcurrentHashMap<Integer, SpiUpdatePlan> updatePlanCache = new ConcurrentHashMap<Integer, SpiUpdatePlan>();
|
||||
|
||||
private final ConcurrentHashMap<HashQueryPlan, CQueryPlan> queryPlanCache = new ConcurrentHashMap<HashQueryPlan, CQueryPlan>();
|
||||
private final ConcurrentHashMap<CQueryPlanKey, CQueryPlan> queryPlanCache = new ConcurrentHashMap<CQueryPlanKey, CQueryPlan>();
|
||||
|
||||
private final ConcurrentHashMap<String, ElPropertyValue> elCache = new ConcurrentHashMap<String, ElPropertyValue>();
|
||||
|
||||
@@ -347,7 +347,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
private final BeanDescriptorJsonHelp<T> jsonHelp;
|
||||
|
||||
private final String defaultSelectClause;
|
||||
private final Set<String> defaultSelectClauseSet;
|
||||
private final LinkedHashSet<String> defaultSelectClauseSet;
|
||||
|
||||
private SpiEbeanServer ebeanServer;
|
||||
|
||||
@@ -856,7 +856,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
/**
|
||||
* Return the default select clause already parsed into an ordered Set.
|
||||
*/
|
||||
public Set<String> getDefaultSelectClauseSet() {
|
||||
public LinkedHashSet<String> getDefaultSelectClauseSet() {
|
||||
return defaultSelectClauseSet;
|
||||
}
|
||||
|
||||
@@ -1163,11 +1163,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public CQueryPlan getQueryPlan(HashQueryPlan key) {
|
||||
public CQueryPlan getQueryPlan(CQueryPlanKey key) {
|
||||
return queryPlanCache.get(key);
|
||||
}
|
||||
|
||||
public void putQueryPlan(HashQueryPlan key, CQueryPlan plan) {
|
||||
public void putQueryPlan(CQueryPlanKey key, CQueryPlan plan) {
|
||||
queryPlanCache.put(key, plan);
|
||||
}
|
||||
|
||||
@@ -1252,7 +1252,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
|
||||
OrmQueryDetail detail = query.getDetail();
|
||||
for (int i = 0; i < propertiesMany.length; i++) {
|
||||
if (detail.includes(propertiesMany[i].getName())) {
|
||||
if (detail.includesPath(propertiesMany[i].getName())) {
|
||||
return propertiesMany[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,12 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
|
||||
* Internal add bypassing any modify listening.
|
||||
*/
|
||||
@Override
|
||||
public void add(BeanCollection<?> collection, EntityBean bean) {
|
||||
collection.internalAdd(bean);
|
||||
public void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck) {
|
||||
if (withCheck) {
|
||||
collection.internalAddWithCheck(bean);
|
||||
} else {
|
||||
collection.internalAdd(bean);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -108,13 +108,14 @@ public final class BeanMapHelp<T> implements BeanCollectionHelp<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(BeanCollection<?> collection, EntityBean bean) {
|
||||
public void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck) {
|
||||
|
||||
if (bean == null) {
|
||||
((BeanMap<?, ?>) collection).internalPutNull();
|
||||
} else {
|
||||
Object keyValue = beanProperty.getValueIntercept(bean);
|
||||
((BeanMap<?, ?>) collection).internalPut(keyValue, bean);
|
||||
BeanMap<?, ?> map = ((BeanMap<?, ?>) collection);
|
||||
map.internalPutWithCheck(keyValue, bean);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -182,13 +182,13 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
/**
|
||||
* Add the bean to the appropriate collection on the parent bean.
|
||||
*/
|
||||
public void addBeanToCollectionWithCreate(EntityBean parentBean, EntityBean detailBean) {
|
||||
public void addBeanToCollectionWithCreate(EntityBean parentBean, EntityBean detailBean, boolean withCheck) {
|
||||
BeanCollection<?> bc = (BeanCollection<?>) super.getValue(parentBean);
|
||||
if (bc == null) {
|
||||
bc = help.createEmpty(parentBean);
|
||||
setValue(parentBean, bc);
|
||||
}
|
||||
help.add(bc, detailBean);
|
||||
help.add(bc, detailBean, withCheck);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -416,7 +416,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
public void add(BeanCollection<?> collection, EntityBean bean) {
|
||||
help.add(collection, bean);
|
||||
help.add(collection, bean, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,8 +61,12 @@ public final class BeanSetHelp<T> implements BeanCollectionHelp<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public void add(BeanCollection<?> collection, EntityBean bean) {
|
||||
collection.internalAdd(bean);
|
||||
public void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck) {
|
||||
if (withCheck) {
|
||||
collection.internalAddWithCheck(bean);
|
||||
} else {
|
||||
collection.internalAdd(bean);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
@@ -9,7 +10,7 @@ import com.avaje.ebean.SqlUpdate;
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import com.avaje.ebeaninternal.server.expression.IdInExpression;
|
||||
import com.avaje.ebeaninternal.util.DefaultExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.expression.DefaultExpressionRequest;
|
||||
|
||||
public class IntersectionRow {
|
||||
|
||||
@@ -98,7 +99,7 @@ public class IntersectionRow {
|
||||
sb.append(er.getSql());
|
||||
sb.append(" ) ");
|
||||
|
||||
ArrayList<Object> bindValues = er.getBindValues();
|
||||
List<Object> bindValues = er.getBindValues();
|
||||
for (int i = 0; i < bindValues.size(); i++) {
|
||||
bindParams.setParameter(++count, bindValues.get(i));
|
||||
}
|
||||
|
||||
@@ -63,6 +63,32 @@ public final class TableJoin {
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return queryHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
TableJoin that = (TableJoin) o;
|
||||
|
||||
if (!table.equals(that.table)) return false;
|
||||
if (type != that.type) return false;
|
||||
if (columns.length != that.columns.length) return false;
|
||||
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
if (!columns[i].equals(that.columns[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return a hash value for adding to a query plan.
|
||||
*/
|
||||
|
||||
@@ -8,76 +8,97 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
|
||||
*/
|
||||
public class TableJoinColumn {
|
||||
|
||||
/**
|
||||
* The local database column name.
|
||||
*/
|
||||
private final String localDbColumn;
|
||||
/**
|
||||
* The local database column name.
|
||||
*/
|
||||
private final String localDbColumn;
|
||||
|
||||
/**
|
||||
* The foreign database column name.
|
||||
*/
|
||||
private final String foreignDbColumn;
|
||||
/**
|
||||
* The foreign database column name.
|
||||
*/
|
||||
private final String foreignDbColumn;
|
||||
|
||||
private final boolean insertable;
|
||||
|
||||
private final boolean updateable;
|
||||
private final boolean insertable;
|
||||
|
||||
/**
|
||||
* Hash for including in a query plan
|
||||
*/
|
||||
private final int queryHash;
|
||||
private final boolean updateable;
|
||||
|
||||
/**
|
||||
* Create the pair.
|
||||
*/
|
||||
public TableJoinColumn(DeployTableJoinColumn deploy) {
|
||||
this.localDbColumn = InternString.intern(deploy.getLocalDbColumn());
|
||||
this.foreignDbColumn = InternString.intern(deploy.getForeignDbColumn());
|
||||
this.insertable = deploy.isInsertable();
|
||||
this.updateable = deploy.isUpdateable();
|
||||
this.queryHash = hashOf(localDbColumn) * 31 + hashOf(foreignDbColumn);
|
||||
}
|
||||
/**
|
||||
* Hash for including in a query plan
|
||||
*/
|
||||
private final int queryHash;
|
||||
|
||||
private int hashOf(String value) {
|
||||
return (value == null) ? 0 : value.hashCode();
|
||||
}
|
||||
/**
|
||||
* Create the pair.
|
||||
*/
|
||||
public TableJoinColumn(DeployTableJoinColumn deploy) {
|
||||
this.localDbColumn = InternString.intern(deploy.getLocalDbColumn());
|
||||
this.foreignDbColumn = InternString.intern(deploy.getForeignDbColumn());
|
||||
this.insertable = deploy.isInsertable();
|
||||
this.updateable = deploy.isUpdateable();
|
||||
this.queryHash = hash();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return localDbColumn+" = "+foreignDbColumn;
|
||||
}
|
||||
int hash() {
|
||||
int result = localDbColumn != null ? localDbColumn.hashCode() : 0;
|
||||
result = 31 * result + (foreignDbColumn != null ? foreignDbColumn.hashCode() : 0);
|
||||
result = 31 * result + (insertable ? 1 : 0);
|
||||
result = 31 * result + (updateable ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash for including in a query plan.
|
||||
*/
|
||||
public int queryHash() {
|
||||
return queryHash;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return queryHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the foreign database column name.
|
||||
*/
|
||||
public String getForeignDbColumn() {
|
||||
return foreignDbColumn;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
/**
|
||||
* Return the local database column name.
|
||||
*/
|
||||
public String getLocalDbColumn() {
|
||||
return localDbColumn;
|
||||
}
|
||||
TableJoinColumn that = (TableJoinColumn) o;
|
||||
if (insertable != that.insertable) return false;
|
||||
if (updateable != that.updateable) return false;
|
||||
if (!localDbColumn.equals(that.localDbColumn)) return false;
|
||||
return foreignDbColumn.equals(that.foreignDbColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this column should be insertable.
|
||||
*/
|
||||
public boolean isInsertable() {
|
||||
return insertable;
|
||||
}
|
||||
public String toString() {
|
||||
return localDbColumn + " = " + foreignDbColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this column should be updateable.
|
||||
*/
|
||||
public boolean isUpdateable() {
|
||||
return updateable;
|
||||
}
|
||||
/**
|
||||
* Return a hash for including in a query plan.
|
||||
*/
|
||||
public int queryHash() {
|
||||
return queryHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the foreign database column name.
|
||||
*/
|
||||
public String getForeignDbColumn() {
|
||||
return foreignDbColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the local database column name.
|
||||
*/
|
||||
public String getLocalDbColumn() {
|
||||
return localDbColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this column should be insertable.
|
||||
*/
|
||||
public boolean isInsertable() {
|
||||
return insertable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this column should be updateable.
|
||||
*/
|
||||
public boolean isUpdateable() {
|
||||
return updateable;
|
||||
}
|
||||
}
|
||||
|
||||
+45
-46
@@ -1,13 +1,12 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.sql.Types;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
/**
|
||||
* Creates "Counter" GeneratedProperty for various types of number.
|
||||
* <p>
|
||||
@@ -16,49 +15,49 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
*/
|
||||
public class CounterFactory {
|
||||
|
||||
final GeneratedCounterInteger integerCounter = new GeneratedCounterInteger();
|
||||
final GeneratedCounterInteger integerCounter = new GeneratedCounterInteger();
|
||||
|
||||
final GeneratedCounterLong longCounter = new GeneratedCounterLong();
|
||||
final GeneratedCounterLong longCounter = new GeneratedCounterLong();
|
||||
|
||||
public void setCounter(DeployBeanProperty property) {
|
||||
public void setCounter(DeployBeanProperty property) {
|
||||
|
||||
property.setGeneratedProperty(createCounter(property));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the GeneratedProperty based on the property type.
|
||||
*/
|
||||
private GeneratedProperty createCounter(DeployBeanProperty property) {
|
||||
|
||||
Class<?> propType = property.getPropertyType();
|
||||
if (propType.equals(Integer.class) || propType.equals(int.class)) {
|
||||
return integerCounter;
|
||||
}
|
||||
if (propType.equals(Long.class) || propType.equals(long.class)) {
|
||||
return longCounter;
|
||||
}
|
||||
|
||||
int type = getType(propType);
|
||||
return new GeneratedCounter(type);
|
||||
}
|
||||
|
||||
private int getType(Class<?> propType){
|
||||
if (propType.equals(Short.class) || propType.equals(short.class)){
|
||||
return Types.TINYINT;
|
||||
}
|
||||
if (propType.equals(BigDecimal.class)){
|
||||
return Types.DECIMAL;
|
||||
}
|
||||
if (propType.equals(Double.class) || propType.equals(double.class)){
|
||||
return Types.DOUBLE;
|
||||
}
|
||||
if (propType.equals(Float.class) || propType.equals(float.class)){
|
||||
return Types.REAL;
|
||||
}
|
||||
if (propType.equals(BigInteger.class)){
|
||||
return Types.BIGINT;
|
||||
}
|
||||
String msg = "Can not support Counter for type "+propType.getName();
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
property.setGeneratedProperty(createCounter(property));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the GeneratedProperty based on the property type.
|
||||
*/
|
||||
private GeneratedProperty createCounter(DeployBeanProperty property) {
|
||||
|
||||
Class<?> propType = property.getPropertyType();
|
||||
if (propType.equals(Integer.class) || propType.equals(int.class)) {
|
||||
return integerCounter;
|
||||
}
|
||||
if (propType.equals(Long.class) || propType.equals(long.class)) {
|
||||
return longCounter;
|
||||
}
|
||||
|
||||
int type = getType(propType);
|
||||
return new GeneratedCounter(type);
|
||||
}
|
||||
|
||||
private int getType(Class<?> propType) {
|
||||
if (propType.equals(Short.class) || propType.equals(short.class)) {
|
||||
return Types.TINYINT;
|
||||
}
|
||||
if (propType.equals(BigDecimal.class)) {
|
||||
return Types.DECIMAL;
|
||||
}
|
||||
if (propType.equals(Double.class) || propType.equals(double.class)) {
|
||||
return Types.DOUBLE;
|
||||
}
|
||||
if (propType.equals(Float.class) || propType.equals(float.class)) {
|
||||
return Types.REAL;
|
||||
}
|
||||
if (propType.equals(BigInteger.class)) {
|
||||
return Types.BIGINT;
|
||||
}
|
||||
String msg = "Can not support Counter for type " + propType.getName();
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
+39
-37
@@ -9,49 +9,51 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
*/
|
||||
public class GeneratedCounter implements GeneratedProperty {
|
||||
|
||||
final int numberType;
|
||||
final int numberType;
|
||||
|
||||
public GeneratedCounter(int numberType) {
|
||||
this.numberType = numberType;
|
||||
}
|
||||
public GeneratedCounter(int numberType) {
|
||||
this.numberType = numberType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Always returns a 1.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return BasicTypeConverter.convert(1, numberType);
|
||||
}
|
||||
/**
|
||||
* Always returns a 1.
|
||||
*/
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return BasicTypeConverter.convert(1, numberType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the current value by one.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
Number currVal = (Number) prop.getValue(bean);
|
||||
Integer nextVal = currVal.intValue() + 1;
|
||||
return BasicTypeConverter.convert(nextVal, numberType);
|
||||
}
|
||||
/**
|
||||
* Increments the current value by one.
|
||||
*/
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
Number currVal = (Number) prop.getValue(bean);
|
||||
Integer nextVal = currVal.intValue() + 1;
|
||||
return BasicTypeConverter.convert(nextVal, numberType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every update.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Include this in every update.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every insert setting initial counter value to 1.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Include this in every insert setting initial counter value to 1.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+37
-35
@@ -8,46 +8,48 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
*/
|
||||
public class GeneratedCounterInteger implements GeneratedProperty {
|
||||
|
||||
public GeneratedCounterInteger() {
|
||||
public GeneratedCounterInteger() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Always returns a 1.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return 1;
|
||||
}
|
||||
/**
|
||||
* Always returns a 1.
|
||||
*/
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the current value by one.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
Integer i = (Integer) prop.getValue(bean);
|
||||
return i + 1;
|
||||
}
|
||||
/**
|
||||
* Increments the current value by one.
|
||||
*/
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
Integer i = (Integer) prop.getValue(bean);
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every update.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Include this in every update.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every insert setting initial counter value to 1.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Include this in every insert setting initial counter value to 1.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+37
-35
@@ -8,46 +8,48 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
*/
|
||||
public class GeneratedCounterLong implements GeneratedProperty {
|
||||
|
||||
public GeneratedCounterLong() {
|
||||
public GeneratedCounterLong() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Always returns a 1.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return (long) 1;
|
||||
}
|
||||
/**
|
||||
* Always returns a 1.
|
||||
*/
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return (long) 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the current value by one.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
Long i = (Long) prop.getValue(bean);
|
||||
return i + 1;
|
||||
}
|
||||
/**
|
||||
* Increments the current value by one.
|
||||
*/
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
Long i = (Long) prop.getValue(bean);
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every update.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Include this in every update.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every insert setting initial counter value to 1.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Include this in every insert setting initial counter value to 1.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+36
-34
@@ -1,50 +1,52 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Used to generate a (java.util.Date) timestamp when a bean is inserted.
|
||||
*/
|
||||
public class GeneratedInsertDate implements GeneratedProperty {
|
||||
|
||||
/**
|
||||
* Return the current time as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return new Date(System.currentTimeMillis());
|
||||
}
|
||||
/**
|
||||
* Return the current time as a Timestamp.
|
||||
*/
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new Date(now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Just returns the beans original insert timestamp value.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return prop.getValue(bean);
|
||||
}
|
||||
/**
|
||||
* Just returns the beans original insert timestamp value.
|
||||
*/
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return prop.getValue(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return false.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Return false.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Return true.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-11
@@ -3,10 +3,6 @@ package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
/**
|
||||
* Support java.time types as GeneratedProperty.
|
||||
*/
|
||||
@@ -35,7 +31,7 @@ public class GeneratedInsertJavaTime {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return prop.getValue(bean);
|
||||
}
|
||||
}
|
||||
@@ -46,8 +42,8 @@ public class GeneratedInsertJavaTime {
|
||||
public static class LocalDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return LocalDateTime.now();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toLocalDateTime(now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,8 +53,8 @@ public class GeneratedInsertJavaTime {
|
||||
public static class OffsetDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return OffsetDateTime.now();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toOffsetDateTime(now);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -69,8 +65,8 @@ public class GeneratedInsertJavaTime {
|
||||
public static class ZonedDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return ZonedDateTime.now();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toZonedDateTime(now);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-7
@@ -2,9 +2,8 @@ package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import org.joda.time.LocalDateTime;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Support joda time types as GeneratedProperty.
|
||||
@@ -34,7 +33,7 @@ public class GeneratedInsertJodaTime {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return prop.getValue(bean);
|
||||
}
|
||||
}
|
||||
@@ -45,8 +44,8 @@ public class GeneratedInsertJodaTime {
|
||||
public static class LocalDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return new LocalDateTime();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new LocalDateTime(now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,8 +55,8 @@ public class GeneratedInsertJodaTime {
|
||||
public static class DateTimeDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return new DateTime();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new DateTime(now);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+34
-32
@@ -8,41 +8,43 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
*/
|
||||
public class GeneratedInsertLong implements GeneratedProperty {
|
||||
|
||||
/**
|
||||
* Return the current time as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
/**
|
||||
* Return the current time as a Timestamp.
|
||||
*/
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return now;
|
||||
}
|
||||
|
||||
/**
|
||||
* Just returns the beans original insert timestamp value.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return prop.getValue(bean);
|
||||
}
|
||||
/**
|
||||
* Just returns the beans original insert timestamp value.
|
||||
*/
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return prop.getValue(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return false.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Return false.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Return true.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+36
-34
@@ -1,50 +1,52 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/**
|
||||
* Used to generate a timestamp when a bean is inserted.
|
||||
*/
|
||||
public class GeneratedInsertTimestamp implements GeneratedProperty, GeneratedWhenCreated {
|
||||
|
||||
/**
|
||||
* Return the current time as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return new Timestamp(System.currentTimeMillis());
|
||||
}
|
||||
/**
|
||||
* Return the current time as a Timestamp.
|
||||
*/
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new Timestamp(now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Just returns the beans original insert timestamp value.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return prop.getValue(bean);
|
||||
}
|
||||
/**
|
||||
* Just returns the beans original insert timestamp value.
|
||||
*/
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return prop.getValue(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return false.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Return false.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Return true.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+30
-30
@@ -9,39 +9,39 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
*/
|
||||
public interface GeneratedProperty {
|
||||
|
||||
/**
|
||||
* Get the generated insert value for a specific property of a bean.
|
||||
*/
|
||||
Object getInsertValue(BeanProperty prop, EntityBean bean);
|
||||
/**
|
||||
* Get the generated insert value for a specific property of a bean.
|
||||
*/
|
||||
Object getInsertValue(BeanProperty prop, EntityBean bean, long now);
|
||||
|
||||
/**
|
||||
* Get the generated update value for a specific property of a bean.
|
||||
*/
|
||||
Object getUpdateValue(BeanProperty prop, EntityBean bean);
|
||||
/**
|
||||
* Get the generated update value for a specific property of a bean.
|
||||
*/
|
||||
Object getUpdateValue(BeanProperty prop, EntityBean bean, long now);
|
||||
|
||||
/**
|
||||
* Return true if this should always be includes in an update statement.
|
||||
* <p>
|
||||
* Used to include GeneratedUpdateTimestamp in dynamic table updates.
|
||||
* </p>
|
||||
*/
|
||||
boolean includeInUpdate();
|
||||
|
||||
/**
|
||||
* Return true if the property should be included in an update even if
|
||||
* it is not loaded (ie. Last Updated Timestamp).
|
||||
*/
|
||||
boolean includeInAllUpdates();
|
||||
/**
|
||||
* Return true if this should always be includes in an update statement.
|
||||
* <p>
|
||||
* Used to include GeneratedUpdateTimestamp in dynamic table updates.
|
||||
* </p>
|
||||
*/
|
||||
boolean includeInUpdate();
|
||||
|
||||
/**
|
||||
* Return true if this should be included in insert statements.
|
||||
*/
|
||||
boolean includeInInsert();
|
||||
/**
|
||||
* Return true if the property should be included in an update even if
|
||||
* it is not loaded (ie. Last Updated Timestamp).
|
||||
*/
|
||||
boolean includeInAllUpdates();
|
||||
|
||||
/**
|
||||
* Return true if the GeneratedProperty implies the DDL to create the DB
|
||||
* column should have a not null constraint.
|
||||
*/
|
||||
boolean isDDLNotNullable();
|
||||
/**
|
||||
* Return true if this should be included in insert statements.
|
||||
*/
|
||||
boolean includeInInsert();
|
||||
|
||||
/**
|
||||
* Return true if the GeneratedProperty implies the DDL to create the DB
|
||||
* column should have a not null constraint.
|
||||
*/
|
||||
boolean isDDLNotNullable();
|
||||
|
||||
}
|
||||
|
||||
+38
-38
@@ -1,19 +1,19 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.avaje.ebean.config.ClassLoadConfig;
|
||||
import com.avaje.ebean.config.CurrentUserProvider;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* Default implementation of GeneratedPropertyFactory.
|
||||
*/
|
||||
public class GeneratedPropertyFactory {
|
||||
|
||||
private final CounterFactory counterFactory = new CounterFactory();
|
||||
private final CounterFactory counterFactory = new CounterFactory();
|
||||
|
||||
private final InsertTimestampFactory insertFactory;
|
||||
|
||||
@@ -34,7 +34,7 @@ public class GeneratedPropertyFactory {
|
||||
this.updateFactory = new UpdateTimestampFactory(classLoadConfig);
|
||||
|
||||
CurrentUserProvider currentUserProvider = serverConfig.getCurrentUserProvider();
|
||||
if (currentUserProvider != null) {
|
||||
if (currentUserProvider != null) {
|
||||
generatedWhoCreated = new GeneratedWhoCreated(currentUserProvider);
|
||||
generatedWhoModified = new GeneratedWhoModified(currentUserProvider);
|
||||
} else {
|
||||
@@ -42,47 +42,47 @@ public class GeneratedPropertyFactory {
|
||||
generatedWhoModified = null;
|
||||
}
|
||||
|
||||
numberTypes.add(Integer.class.getName());
|
||||
numberTypes.add(int.class.getName());
|
||||
numberTypes.add(Long.class.getName());
|
||||
numberTypes.add(long.class.getName());
|
||||
numberTypes.add(Short.class.getName());
|
||||
numberTypes.add(short.class.getName());
|
||||
numberTypes.add(Double.class.getName());
|
||||
numberTypes.add(double.class.getName());
|
||||
numberTypes.add(BigDecimal.class.getName());
|
||||
}
|
||||
numberTypes.add(Integer.class.getName());
|
||||
numberTypes.add(int.class.getName());
|
||||
numberTypes.add(Long.class.getName());
|
||||
numberTypes.add(long.class.getName());
|
||||
numberTypes.add(Short.class.getName());
|
||||
numberTypes.add(short.class.getName());
|
||||
numberTypes.add(Double.class.getName());
|
||||
numberTypes.add(double.class.getName());
|
||||
numberTypes.add(BigDecimal.class.getName());
|
||||
}
|
||||
|
||||
public ClassLoadConfig getClassLoadConfig() {
|
||||
return classLoadConfig;
|
||||
}
|
||||
|
||||
private boolean isNumberType(String typeClassName) {
|
||||
return numberTypes.contains(typeClassName);
|
||||
}
|
||||
|
||||
public void setVersion(DeployBeanProperty property) {
|
||||
if (isNumberType(property.getPropertyType().getName())) {
|
||||
setCounter(property);
|
||||
} else {
|
||||
setUpdateTimestamp(property);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCounter(DeployBeanProperty property) {
|
||||
|
||||
counterFactory.setCounter(property);
|
||||
}
|
||||
return numberTypes.contains(typeClassName);
|
||||
}
|
||||
|
||||
public void setInsertTimestamp(DeployBeanProperty property) {
|
||||
|
||||
insertFactory.setInsertTimestamp(property);
|
||||
}
|
||||
public void setVersion(DeployBeanProperty property) {
|
||||
if (isNumberType(property.getPropertyType().getName())) {
|
||||
setCounter(property);
|
||||
} else {
|
||||
setUpdateTimestamp(property);
|
||||
}
|
||||
}
|
||||
|
||||
public void setUpdateTimestamp(DeployBeanProperty property) {
|
||||
|
||||
updateFactory.setUpdateTimestamp(property);
|
||||
}
|
||||
public void setCounter(DeployBeanProperty property) {
|
||||
|
||||
counterFactory.setCounter(property);
|
||||
}
|
||||
|
||||
public void setInsertTimestamp(DeployBeanProperty property) {
|
||||
|
||||
insertFactory.setInsertTimestamp(property);
|
||||
}
|
||||
|
||||
public void setUpdateTimestamp(DeployBeanProperty property) {
|
||||
|
||||
updateFactory.setUpdateTimestamp(property);
|
||||
}
|
||||
|
||||
public void setWhoCreated(DeployBeanProperty property) {
|
||||
if (generatedWhoCreated == null) {
|
||||
|
||||
+34
-34
@@ -1,51 +1,51 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Generate a (java.util.Date) Timestamp whenever the bean is inserted or
|
||||
* updated.
|
||||
*/
|
||||
public class GeneratedUpdateDate implements GeneratedProperty {
|
||||
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return new Date(System.currentTimeMillis());
|
||||
}
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new Date(now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return new Date(System.currentTimeMillis());
|
||||
}
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new Date(now);
|
||||
}
|
||||
|
||||
/**
|
||||
* For dynamic table updates make sure this is included.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* For dynamic table updates make sure this is included.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every insert.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Include this in every insert.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-16
@@ -3,10 +3,6 @@ package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
/**
|
||||
* Support java.time DateTime types as GeneratedProperty.
|
||||
*/
|
||||
@@ -41,13 +37,13 @@ public class GeneratedUpdateJavaTime {
|
||||
public static class LocalDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return LocalDateTime.now();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toLocalDateTime(now);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return LocalDateTime.now();
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toLocalDateTime(now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,13 +53,13 @@ public class GeneratedUpdateJavaTime {
|
||||
public static class OffsetDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return OffsetDateTime.now();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toOffsetDateTime(now);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return OffsetDateTime.now();
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toOffsetDateTime(now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,13 +69,13 @@ public class GeneratedUpdateJavaTime {
|
||||
public static class ZonedDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return ZonedDateTime.now();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toZonedDateTime(now);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return ZonedDateTime.now();
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toZonedDateTime(now);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-10
@@ -2,9 +2,8 @@ package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import org.joda.time.LocalDateTime;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Support java.time DateTime types as GeneratedProperty.
|
||||
@@ -40,13 +39,13 @@ public class GeneratedUpdateJodaTime {
|
||||
public static class LocalDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return new LocalDateTime();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new LocalDateTime(now);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return new LocalDateTime();
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new LocalDateTime(now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,13 +55,13 @@ public class GeneratedUpdateJodaTime {
|
||||
public static class DateTimeDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return new DateTime();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new DateTime(now);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return new DateTime();
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new DateTime(now);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+32
-32
@@ -8,41 +8,41 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
*/
|
||||
public class GeneratedUpdateLong implements GeneratedProperty {
|
||||
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return now;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return now;
|
||||
}
|
||||
|
||||
/**
|
||||
* For dynamic table updates make sure this is included.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* For dynamic table updates make sure this is included.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every insert.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Include this in every insert.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+34
-34
@@ -1,50 +1,50 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/**
|
||||
* Generate a Timestamp whenever the bean is inserted or updated.
|
||||
*/
|
||||
public class GeneratedUpdateTimestamp implements GeneratedProperty, GeneratedWhenModified {
|
||||
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return new Timestamp(System.currentTimeMillis());
|
||||
}
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new Timestamp(now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return new Timestamp(System.currentTimeMillis());
|
||||
}
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new Timestamp(now);
|
||||
}
|
||||
|
||||
/**
|
||||
* For dynamic table updates make sure this is included.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* For dynamic table updates make sure this is included.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every insert.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Include this in every insert.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -16,12 +16,12 @@ public class GeneratedWhoCreated implements GeneratedProperty {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return currentUserProvider.currentUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -16,12 +16,12 @@ public class GeneratedWhoModified implements GeneratedProperty {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return currentUserProvider.currentUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return currentUserProvider.currentUser();
|
||||
}
|
||||
|
||||
|
||||
+17
-18
@@ -1,5 +1,9 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import com.avaje.ebean.config.ClassLoadConfig;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
@@ -7,17 +11,12 @@ import java.time.ZonedDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.config.ClassLoadConfig;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
/**
|
||||
* Helper for creating Insert timestamp GeneratedProperty objects.
|
||||
*/
|
||||
public class InsertTimestampFactory {
|
||||
|
||||
final GeneratedInsertLong longTime = new GeneratedInsertLong();
|
||||
final GeneratedInsertLong longTime = new GeneratedInsertLong();
|
||||
|
||||
final Map<Class<?>, GeneratedProperty> map = new HashMap<Class<?>, GeneratedProperty>();
|
||||
|
||||
@@ -41,21 +40,21 @@ public class InsertTimestampFactory {
|
||||
|
||||
public void setInsertTimestamp(DeployBeanProperty property) {
|
||||
|
||||
property.setGeneratedProperty(createInsertTimestamp(property));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the insert GeneratedProperty depending on the property type.
|
||||
*/
|
||||
public GeneratedProperty createInsertTimestamp(DeployBeanProperty property) {
|
||||
|
||||
Class<?> propType = property.getPropertyType();
|
||||
property.setGeneratedProperty(createInsertTimestamp(property));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the insert GeneratedProperty depending on the property type.
|
||||
*/
|
||||
public GeneratedProperty createInsertTimestamp(DeployBeanProperty property) {
|
||||
|
||||
Class<?> propType = property.getPropertyType();
|
||||
GeneratedProperty generatedProperty = map.get(propType);
|
||||
if (generatedProperty != null) {
|
||||
return generatedProperty;
|
||||
}
|
||||
|
||||
throw new PersistenceException("Generated Insert Timestamp not supported on "+propType.getName());
|
||||
}
|
||||
|
||||
throw new PersistenceException("Generated Insert Timestamp not supported on " + propType.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
/**
|
||||
* Helper methods for Java time conversion.
|
||||
*/
|
||||
public class JavaTimeUtils {
|
||||
|
||||
/**
|
||||
* Return the system millis time as a LocalDateTime.
|
||||
*/
|
||||
public static Object toLocalDateTime(long systemMillis) {
|
||||
return new Timestamp(systemMillis).toLocalDateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the system millis time as a OffsetDateTime.
|
||||
*/
|
||||
public static Object toOffsetDateTime(long systemMillis) {
|
||||
return OffsetDateTime.ofInstant(Instant.ofEpochMilli(systemMillis), ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the system millis time as a ZonedDateTime.
|
||||
*/
|
||||
public static Object toZonedDateTime(long systemMillis) {
|
||||
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(systemMillis), ZoneId.systemDefault());
|
||||
}
|
||||
}
|
||||
+16
-17
@@ -1,5 +1,9 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import com.avaje.ebean.config.ClassLoadConfig;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
@@ -7,17 +11,12 @@ import java.time.ZonedDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.config.ClassLoadConfig;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
/**
|
||||
* Helper for creating Update timestamp GeneratedProperty objects.
|
||||
*/
|
||||
public class UpdateTimestampFactory {
|
||||
|
||||
final GeneratedUpdateLong longTime = new GeneratedUpdateLong();
|
||||
final GeneratedUpdateLong longTime = new GeneratedUpdateLong();
|
||||
|
||||
final Map<Class<?>, GeneratedProperty> map = new HashMap<Class<?>, GeneratedProperty>();
|
||||
|
||||
@@ -38,15 +37,15 @@ public class UpdateTimestampFactory {
|
||||
}
|
||||
}
|
||||
|
||||
public void setUpdateTimestamp(DeployBeanProperty property) {
|
||||
public void setUpdateTimestamp(DeployBeanProperty property) {
|
||||
|
||||
property.setGeneratedProperty(createUpdateTimestamp(property));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the update GeneratedProperty depending on the property type.
|
||||
*/
|
||||
protected GeneratedProperty createUpdateTimestamp(DeployBeanProperty property) {
|
||||
property.setGeneratedProperty(createUpdateTimestamp(property));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the update GeneratedProperty depending on the property type.
|
||||
*/
|
||||
protected GeneratedProperty createUpdateTimestamp(DeployBeanProperty property) {
|
||||
|
||||
Class<?> propType = property.getPropertyType();
|
||||
GeneratedProperty generatedProperty = map.get(propType);
|
||||
@@ -54,7 +53,7 @@ public class UpdateTimestampFactory {
|
||||
return generatedProperty;
|
||||
}
|
||||
|
||||
throw new PersistenceException("Generated update Timestamp not supported on "+propType.getName());
|
||||
}
|
||||
|
||||
throw new PersistenceException("Generated update Timestamp not supported on " + propType.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
|
||||
<TITLE>Counter, Insert Timestamp, Update Timestamp support</TITLE>
|
||||
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
|
||||
<TITLE>Counter, Insert Timestamp, Update Timestamp support</TITLE>
|
||||
</HEAD>
|
||||
<Body BGCOLOR="#ffffff">
|
||||
Counter, Insert Timestamp, Update Timestamp support
|
||||
|
||||
@@ -33,7 +33,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Describes Beans including their deployment information.
|
||||
@@ -763,7 +762,7 @@ public class DeployBeanDescriptor<T> {
|
||||
/**
|
||||
* Parse the include separating by comma or semicolon.
|
||||
*/
|
||||
public Set<String> parseDefaultSelectClause(String rawList) {
|
||||
public LinkedHashSet<String> parseDefaultSelectClause(String rawList) {
|
||||
|
||||
if (rawList == null) {
|
||||
return null;
|
||||
@@ -780,7 +779,7 @@ public class DeployBeanDescriptor<T> {
|
||||
set.add(temp);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableSet(set);
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
@@ -21,19 +22,20 @@ public abstract class AbstractExpression implements SpiExpression {
|
||||
this.propName = propName;
|
||||
}
|
||||
|
||||
public String getPropertyName() {
|
||||
return propName;
|
||||
@Override
|
||||
public SpiExpression copyForPlanKey() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
|
||||
String propertyName = getPropertyName();
|
||||
if (propertyName != null) {
|
||||
ElPropertyDeploy elProp = desc.getElPropertyDeploy(propertyName);
|
||||
if (propName != null) {
|
||||
ElPropertyDeploy elProp = desc.getElPropertyDeploy(propName);
|
||||
if (elProp != null) {
|
||||
if (elProp.containsFormulaWithJoin()) {
|
||||
// for findRowCount query select clause
|
||||
manyWhereJoin.addFormulaWithJoin(propertyName);
|
||||
manyWhereJoin.addFormulaWithJoin(propName);
|
||||
}
|
||||
if (elProp.containsMany()) {
|
||||
// for findRowCount we join to a many property
|
||||
@@ -44,13 +46,17 @@ public abstract class AbstractExpression implements SpiExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(SpiExpressionValidation validation) {
|
||||
validation.validate(getPropertyName());
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
protected ElPropertyValue getElProp(SpiExpressionRequest request) {
|
||||
@Override
|
||||
public void validate(SpiExpressionValidation validation) {
|
||||
validation.validate(propName);
|
||||
}
|
||||
|
||||
String propertyName = getPropertyName();
|
||||
return request.getBeanDescriptor().getElGetValue(propertyName);
|
||||
protected final ElPropertyValue getElProp(SpiExpressionRequest request) {
|
||||
|
||||
return request.getBeanDescriptor().getElGetValue(propName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
@@ -12,7 +8,11 @@ import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
|
||||
class AllEqualsExpression implements SpiExpression {
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
class AllEqualsExpression extends NonPrepareExpression {
|
||||
|
||||
private static final long serialVersionUID = -8691773558205937025L;
|
||||
|
||||
@@ -26,6 +26,7 @@ class AllEqualsExpression implements SpiExpression {
|
||||
return propName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
if (propMap != null) {
|
||||
for (String propertyName : propMap.keySet()) {
|
||||
@@ -44,6 +45,7 @@ class AllEqualsExpression implements SpiExpression {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
if (propMap.isEmpty()) {
|
||||
@@ -57,6 +59,7 @@ class AllEqualsExpression implements SpiExpression {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
if (propMap.isEmpty()) {
|
||||
@@ -92,7 +95,8 @@ class AllEqualsExpression implements SpiExpression {
|
||||
* The null check is required due to the "is null" sql being generated.
|
||||
* </p>
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
|
||||
builder.add(AllEqualsExpression.class);
|
||||
|
||||
@@ -100,14 +104,11 @@ class AllEqualsExpression implements SpiExpression {
|
||||
Object value = entry.getValue();
|
||||
String propName = entry.getKey();
|
||||
builder.add(propName).add(value == null ? 0 : 1);
|
||||
builder.bind(value == null ? 0 : 1);
|
||||
builder.bindIfNotNull(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
|
||||
int hc = 31;
|
||||
@@ -117,4 +118,48 @@ class AllEqualsExpression implements SpiExpression {
|
||||
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof AllEqualsExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AllEqualsExpression that = (AllEqualsExpression) other;
|
||||
return isSameByValue(that, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
if (!(other instanceof AllEqualsExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AllEqualsExpression that = (AllEqualsExpression) other;
|
||||
return isSameByValue(that, true);
|
||||
}
|
||||
|
||||
private boolean isSameByValue(AllEqualsExpression that, boolean byValue) {
|
||||
|
||||
if (propMap.size() != that.propMap.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Iterator<Entry<String, Object>> thisIt = propMap.entrySet().iterator();
|
||||
Iterator<Entry<String, Object>> thatIt = that.propMap.entrySet().iterator();
|
||||
|
||||
while (thisIt.hasNext() && thatIt.hasNext()) {
|
||||
Entry<String, Object> thisNext = thisIt.next();
|
||||
Entry<String, Object> thatNext = thatIt.next();
|
||||
|
||||
if (!thisNext.getKey().equals(thatNext.getKey())) {
|
||||
return false;
|
||||
}
|
||||
if (!Same.sameBy(byValue, thisNext.getValue(), thatNext.getValue())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
|
||||
|
||||
class BetweenExpression extends AbstractExpression {
|
||||
|
||||
private static final long serialVersionUID = 2078918165221454910L;
|
||||
@@ -21,28 +20,45 @@ class BetweenExpression extends AbstractExpression {
|
||||
this.valueHigh = valHigh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
request.addBindValue(valueLow);
|
||||
request.addBindValue(valueHigh);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
request.append(getPropertyName()).append(BETWEEN).append(" ? and ? ");
|
||||
request.append(propName).append(BETWEEN).append(" ? and ? ");
|
||||
}
|
||||
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(BetweenExpression.class).add(propName);
|
||||
builder.bind(2);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
int hc = valueLow.hashCode();
|
||||
hc = hc * 31 + valueHigh.hashCode();
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof BetweenExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BetweenExpression that = (BetweenExpression) other;
|
||||
return this.propName.equals(that.propName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
BetweenExpression that = (BetweenExpression) other;
|
||||
return valueLow.equals(that.valueLow)
|
||||
&& valueHigh.equals(that.valueHigh);
|
||||
}
|
||||
}
|
||||
|
||||
+24
-7
@@ -1,6 +1,5 @@
|
||||
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;
|
||||
@@ -12,7 +11,7 @@ import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
/**
|
||||
* Between expression where a value is between two properties.
|
||||
*/
|
||||
class BetweenPropertyExpression implements SpiExpression {
|
||||
class BetweenPropertyExpression extends NonPrepareExpression {
|
||||
|
||||
private static final long serialVersionUID = 2078918165221454910L;
|
||||
|
||||
@@ -32,6 +31,7 @@ class BetweenPropertyExpression implements SpiExpression {
|
||||
return propName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
|
||||
ElPropertyDeploy elProp = desc.getElPropertyDeploy(name(lowProperty));
|
||||
@@ -51,25 +51,42 @@ class BetweenPropertyExpression implements SpiExpression {
|
||||
validation.validate(highProperty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
request.addBindValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
request.append(" ? ").append(BETWEEN).append(name(lowProperty)).append(" and ").append(name(highProperty));
|
||||
}
|
||||
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(BetweenPropertyExpression.class).add(lowProperty).add(highProperty);
|
||||
builder.bind(1);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof BetweenPropertyExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BetweenPropertyExpression that = (BetweenPropertyExpression) other;
|
||||
return lowProperty.equals(that.lowProperty)
|
||||
&& highProperty.equals(that.highProperty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
BetweenPropertyExpression that = (BetweenPropertyExpression) other;
|
||||
return value.equals(that.value);
|
||||
}
|
||||
}
|
||||
|
||||
+24
-10
@@ -1,7 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
@@ -16,6 +16,7 @@ class CaseInsensitiveEqualExpression extends AbstractExpression {
|
||||
this.value = value.toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
@@ -28,29 +29,42 @@ class CaseInsensitiveEqualExpression extends AbstractExpression {
|
||||
request.addBindValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
String propertyName = getPropertyName();
|
||||
String pname = propertyName;
|
||||
|
||||
String pname = propName;
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
if (prop != null && prop.isDbEncrypted()) {
|
||||
pname = prop.getBeanProperty().getDecryptProperty(propertyName);
|
||||
pname = prop.getBeanProperty().getDecryptProperty(propName);
|
||||
}
|
||||
|
||||
request.append("lower(").append(pname).append(") =? ");
|
||||
}
|
||||
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(CaseInsensitiveEqualExpression.class).add(propName);
|
||||
builder.bind(1);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof CaseInsensitiveEqualExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CaseInsensitiveEqualExpression that = (CaseInsensitiveEqualExpression) other;
|
||||
return this.propName.equals(that.propName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
CaseInsensitiveEqualExpression that = (CaseInsensitiveEqualExpression) other;
|
||||
return value.equals(that.value);
|
||||
}
|
||||
}
|
||||
|
||||
+62
-15
@@ -86,6 +86,20 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
this.likeType = likeType;
|
||||
}
|
||||
|
||||
DefaultExampleExpression(ArrayList<SpiExpression> source) {
|
||||
this.entity = null;
|
||||
this.list = new ArrayList<SpiExpression>(source.size());
|
||||
for (SpiExpression expression : source) {
|
||||
list.add(expression.copyForPlanKey());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiExpression copyForPlanKey() {
|
||||
return new DefaultExampleExpression(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins) {
|
||||
if (list != null) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
@@ -94,31 +108,37 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExampleExpression includeZeros() {
|
||||
includeZeros = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExampleExpression caseInsensitive() {
|
||||
caseInsensitive = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExampleExpression useStartsWith() {
|
||||
likeType = LikeType.STARTS_WITH;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExampleExpression useContains() {
|
||||
likeType = LikeType.CONTAINS;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExampleExpression useEndsWith() {
|
||||
likeType = LikeType.ENDS_WITH;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExampleExpression useEqualTo() {
|
||||
likeType = LikeType.EQUAL_TO;
|
||||
return this;
|
||||
@@ -134,6 +154,7 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
/**
|
||||
* Adds bind values to the request.
|
||||
*/
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
@@ -145,6 +166,7 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
/**
|
||||
* Generates and adds the sql to the request.
|
||||
*/
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
@@ -162,34 +184,27 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash for AutoTune query identification.
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
// we have not yet built the list of expressions
|
||||
// so just based on the class name
|
||||
builder.add(DefaultExampleExpression.class);
|
||||
@Override
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
list = buildExpressions(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash for query plan identification.
|
||||
* Return a hash for AutoTune query identification.
|
||||
*/
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
|
||||
// this is always called once, and always called before
|
||||
// addSql() and addBindValues() methods
|
||||
list = buildExpressions(request);
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
|
||||
builder.add(DefaultExampleExpression.class);
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
list.get(i).queryPlanHash(request, builder);
|
||||
list.get(i).queryPlanHash(builder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash for the actual bind values used.
|
||||
*/
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
int hc = DefaultExampleExpression.class.getName().hashCode();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
@@ -199,6 +214,38 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof DefaultExampleExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DefaultExampleExpression that = (DefaultExampleExpression) other;
|
||||
if (this.list.size() != that.list.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (!list.get(i).isSameByPlan(that.list.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
DefaultExampleExpression that = (DefaultExampleExpression) other;
|
||||
if (this.list.size() != that.list.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (!list.get(i).isSameByBind(that.list.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the List of expressions.
|
||||
*/
|
||||
|
||||
+55
-32
@@ -1,4 +1,4 @@
|
||||
package com.avaje.ebeaninternal.util;
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
@@ -56,6 +56,10 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
this.listAndJoin = " and ";
|
||||
}
|
||||
|
||||
private DefaultExpressionList() {
|
||||
this(null, null, null, new ArrayList<SpiExpression>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiExpressionList<?> trimPath(int prefixTrim) {
|
||||
throw new RuntimeException("Only allowed on FilterExpressionList");
|
||||
@@ -65,17 +69,6 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ExpressionFactory.
|
||||
* <p>
|
||||
* After deserialisation so that it can be further modified.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public void setExpressionFactory(ExpressionFactory expr) {
|
||||
this.expr = expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of the expression list.
|
||||
* <p>
|
||||
@@ -88,6 +81,14 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return copy;
|
||||
}
|
||||
|
||||
public DefaultExpressionList<T> copyForPlanKey() {
|
||||
DefaultExpressionList<T> copy = new DefaultExpressionList<T>();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
copy.list.add(list.get(i).copyForPlanKey());
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if one of the expressions is related to a Many property.
|
||||
*/
|
||||
@@ -325,7 +326,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String buildSql(SpiExpressionRequest request) {
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
request.append(listAndStart);
|
||||
for (int i = 0, size = list.size(); i < size; i++) {
|
||||
@@ -336,28 +337,19 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
expression.addSql(request);
|
||||
}
|
||||
request.append(listAndEnd);
|
||||
return request.getSql();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<Object> buildBindValues(SpiExpressionRequest request) {
|
||||
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
for (int i = 0, size = list.size(); i < size; i++) {
|
||||
SpiExpression expression = list.get(i);
|
||||
expression.addBindValues(request);
|
||||
list.get(i).addBindValues(request);
|
||||
}
|
||||
return request.getBindValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a hash based on the expressions but excluding the actual bind
|
||||
* values.
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(DefaultExpressionList.class);
|
||||
@Override
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
for (int i = 0, size = list.size(); i < size; i++) {
|
||||
SpiExpression expression = list.get(i);
|
||||
expression.queryAutoTuneHash(builder);
|
||||
list.get(i).prepareExpression(request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,26 +358,57 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
* values.
|
||||
*/
|
||||
@Override
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(DefaultExpressionList.class);
|
||||
for (int i = 0, size = list.size(); i < size; i++) {
|
||||
SpiExpression expression = list.get(i);
|
||||
expression.queryPlanHash(request, builder);
|
||||
list.get(i).queryPlanHash(builder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a hash based on the expressions.
|
||||
*/
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
int hash = DefaultExpressionList.class.getName().hashCode();
|
||||
for (int i = 0, size = list.size(); i < size; i++) {
|
||||
SpiExpression expression = list.get(i);
|
||||
hash = hash * 31 + expression.queryBindHash();
|
||||
hash = hash * 31 + list.get(i).queryBindHash();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof DefaultExpressionList)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DefaultExpressionList<?> that = (DefaultExpressionList<?>)other;
|
||||
if (list.size() != that.list.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0, size = list.size(); i < size; i++) {
|
||||
if (!list.get(i).isSameByPlan(that.list.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
DefaultExpressionList<?> that = (DefaultExpressionList<?>)other;
|
||||
if (list.size() != that.list.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0, size = list.size(); i < size; i++) {
|
||||
if (!list.get(i).isSameByBind(that.list.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Path exists - for the given path in a JSON document.
|
||||
*/
|
||||
+17
-5
@@ -1,7 +1,8 @@
|
||||
package com.avaje.ebeaninternal.util;
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionList;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
@@ -20,7 +21,7 @@ public class DefaultExpressionRequest implements SpiExpressionRequest {
|
||||
|
||||
private final StringBuilder sql = new StringBuilder();
|
||||
|
||||
private final ArrayList<Object> bindValues = new ArrayList<Object>();
|
||||
private final List<Object> bindValues = new ArrayList<Object>();
|
||||
|
||||
private final DeployParser deployParser;
|
||||
|
||||
@@ -39,7 +40,7 @@ public class DefaultExpressionRequest implements SpiExpressionRequest {
|
||||
this.binder = binder;
|
||||
this.expressionList = expressionList;
|
||||
// immediately build the list of bind values (callback style)
|
||||
expressionList.buildBindValues(this);
|
||||
expressionList.addBindValues(this);
|
||||
}
|
||||
|
||||
public DefaultExpressionRequest(BeanDescriptor<?> beanDescriptor) {
|
||||
@@ -54,7 +55,8 @@ public class DefaultExpressionRequest implements SpiExpressionRequest {
|
||||
* Build sql for the underlying expression list.
|
||||
*/
|
||||
public String buildSql() {
|
||||
return expressionList.buildSql(this);
|
||||
expressionList.addSql(this);
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,10 +72,12 @@ public class DefaultExpressionRequest implements SpiExpressionRequest {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonExpressionHandler getJsonHandler() {
|
||||
return binder.getJsonExpressionHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parseDeploy(String logicalProp) {
|
||||
|
||||
String s = deployParser.getDeployWord(logicalProp);
|
||||
@@ -93,14 +97,17 @@ public class DefaultExpressionRequest implements SpiExpressionRequest {
|
||||
/**
|
||||
* Increments the parameter index and returns that value.
|
||||
*/
|
||||
@Override
|
||||
public int nextParameter() {
|
||||
return ++paramIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanDescriptor<?> getBeanDescriptor() {
|
||||
return beanDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiOrmQueryRequest<?> getQueryRequest() {
|
||||
return queryRequest;
|
||||
}
|
||||
@@ -108,16 +115,19 @@ public class DefaultExpressionRequest implements SpiExpressionRequest {
|
||||
/**
|
||||
* Append text the underlying sql expression.
|
||||
*/
|
||||
@Override
|
||||
public SpiExpressionRequest append(String sqlExpression) {
|
||||
sql.append(sqlExpression);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindEncryptKey(Object bindValue) {
|
||||
bindValues.add(bindValue);
|
||||
bindLog("****");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValue(Object bindValue) {
|
||||
bindValues.add(bindValue);
|
||||
bindLog(bindValue);
|
||||
@@ -136,11 +146,13 @@ public class DefaultExpressionRequest implements SpiExpressionRequest {
|
||||
return bindLog == null ? "" : bindLog.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSql() {
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
public ArrayList<Object> getBindValues() {
|
||||
@Override
|
||||
public List<Object> getBindValues() {
|
||||
return bindValues;
|
||||
}
|
||||
|
||||
@@ -17,71 +17,103 @@ public class ExistsExpression implements SpiExpression {
|
||||
|
||||
private static final long serialVersionUID = 666990277309851644L;
|
||||
|
||||
private final boolean not;
|
||||
protected final boolean not;
|
||||
|
||||
private final SpiQuery<?> subQuery;
|
||||
protected final SpiQuery<?> subQuery;
|
||||
|
||||
private transient CQuery<?> compiledSubQuery;
|
||||
protected List<Object> bindParams;
|
||||
|
||||
protected String sql;
|
||||
|
||||
public ExistsExpression(SpiQuery<?> subQuery, boolean not) {
|
||||
this.subQuery = subQuery;
|
||||
this.not = not;
|
||||
}
|
||||
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(ExistsExpression.class).add(not);
|
||||
|
||||
subQuery.queryAutoTuneHash(builder);
|
||||
ExistsExpression(boolean not, String sql , List<Object> bindParams) {
|
||||
this.not = not;
|
||||
this.sql = sql;
|
||||
this.bindParams = bindParams;
|
||||
this.subQuery = null;
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
|
||||
// queryPlanHash executes prior to addSql() or addBindValues()
|
||||
// ... so compiledQuery will exist
|
||||
compiledSubQuery = compileSubQuery(request);
|
||||
CQuery<?> subQuery = compileSubQuery(request);
|
||||
this.bindParams = subQuery.getPredicates().getWhereExprBindValues();
|
||||
this.sql = subQuery.getGeneratedSql().replace('\n', ' ');
|
||||
}
|
||||
|
||||
queryAutoTuneHash(builder);
|
||||
@Override
|
||||
public SpiExpression copyForPlanKey() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile/build the sub query.
|
||||
*/
|
||||
private CQuery<?> compileSubQuery(BeanQueryRequest<?> queryRequest) {
|
||||
|
||||
protected CQuery<?> compileSubQuery(BeanQueryRequest<?> queryRequest) {
|
||||
SpiEbeanServer ebeanServer = (SpiEbeanServer) queryRequest.getEbeanServer();
|
||||
return ebeanServer.compileQuery(subQuery, queryRequest.getTransaction());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(ExistsExpression.class).add(not);
|
||||
builder.add(sql).add(bindParams.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return subQuery.queryBindHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
String subSelect = compiledSubQuery.getGeneratedSql();
|
||||
subSelect = subSelect.replace('\n', ' ');
|
||||
|
||||
if (not) {
|
||||
request.append(" not");
|
||||
}
|
||||
request.append(" exists (");
|
||||
request.append(subSelect);
|
||||
request.append(sql);
|
||||
request.append(") ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
List<Object> bindParams = compiledSubQuery.getPredicates().getWhereExprBindValues();
|
||||
|
||||
if (bindParams == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < bindParams.size(); i++) {
|
||||
request.addBindValue(bindParams.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof ExistsExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ExistsExpression that = (ExistsExpression) other;
|
||||
return this.sql.equals(that.sql)
|
||||
&& this.not == that.not
|
||||
&& this.bindParams.size() == that.bindParams.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
ExistsExpression that = (ExistsExpression) other;
|
||||
if (this.bindParams.size() != that.bindParams.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < bindParams.size(); i++) {
|
||||
if (!bindParams.get(i).equals(that.bindParams.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins) {
|
||||
// Nothing to do for exists expression
|
||||
|
||||
+1
-2
@@ -1,8 +1,7 @@
|
||||
package com.avaje.ebeaninternal.util;
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.*;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionList;
|
||||
import com.avaje.ebeaninternal.server.expression.FilterExprPath;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.List;
|
||||
@@ -1,18 +1,16 @@
|
||||
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 com.avaje.ebeaninternal.util.DefaultExpressionRequest;
|
||||
|
||||
/**
|
||||
* Slightly redundant as Query.setId() ultimately also does the same job.
|
||||
*/
|
||||
class IdExpression implements SpiExpression {
|
||||
class IdExpression extends NonPrepareExpression implements SpiExpression {
|
||||
|
||||
private static final long serialVersionUID = -3065936341718489842L;
|
||||
|
||||
@@ -25,6 +23,7 @@ class IdExpression implements SpiExpression {
|
||||
/**
|
||||
* Always returns false.
|
||||
*/
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
|
||||
}
|
||||
@@ -34,6 +33,7 @@ class IdExpression implements SpiExpression {
|
||||
// always valid
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
// 'flatten' EmbeddedId and multiple Id cases
|
||||
@@ -45,6 +45,7 @@ class IdExpression implements SpiExpression {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
DefaultExpressionRequest r = (DefaultExpressionRequest) request;
|
||||
@@ -56,17 +57,25 @@ class IdExpression implements SpiExpression {
|
||||
/**
|
||||
* No properties so this is just a unique static number.
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(IdExpression.class);
|
||||
builder.bind(1);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
return other instanceof IdExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
IdExpression that = (IdExpression) other;
|
||||
return value.equals(that.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
@@ -10,12 +7,13 @@ import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
|
||||
import com.avaje.ebeaninternal.util.DefaultExpressionRequest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Slightly redundant as Query.setId() ultimately also does the same job.
|
||||
*/
|
||||
public class IdInExpression implements SpiExpression {
|
||||
public class IdInExpression extends NonPrepareExpression {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -25,6 +23,7 @@ public class IdInExpression implements SpiExpression {
|
||||
this.idList = idList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
}
|
||||
|
||||
@@ -33,6 +32,7 @@ public class IdInExpression implements SpiExpression {
|
||||
// always valid
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
// Bind the Id values including EmbeddedId and multiple Id
|
||||
@@ -60,6 +60,7 @@ public class IdInExpression implements SpiExpression {
|
||||
request.append(inClause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
DefaultExpressionRequest r = (DefaultExpressionRequest) request;
|
||||
@@ -74,17 +75,38 @@ public class IdInExpression implements SpiExpression {
|
||||
/**
|
||||
* Incorporates the number of Id values to bind.
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(IdInExpression.class).add(idList.size());
|
||||
builder.bind(idList.size());
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return idList.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof IdInExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IdInExpression that = (IdInExpression) other;
|
||||
return this.idList.size() == that.idList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
IdInExpression that = (IdInExpression) other;
|
||||
if (this.idList.size() != that.idList.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < idList.size(); i++) {
|
||||
if (!idList.get(i).equals(that.idList.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
@@ -28,6 +28,7 @@ class InExpression extends AbstractExpression {
|
||||
this.not = not;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
@@ -51,6 +52,7 @@ class InExpression extends AbstractExpression {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
if (values.length == 0) {
|
||||
@@ -59,20 +61,18 @@ class InExpression extends AbstractExpression {
|
||||
return;
|
||||
}
|
||||
|
||||
String propertyName = getPropertyName();
|
||||
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
if (prop != null && !prop.isAssocId()) {
|
||||
prop = null;
|
||||
}
|
||||
|
||||
if (prop != null) {
|
||||
request.append(prop.getAssocIdInExpr(propertyName));
|
||||
request.append(prop.getAssocIdInExpr(propName));
|
||||
String inClause = prop.getAssocIdInValueExpr(values.length);
|
||||
request.append(inClause);
|
||||
|
||||
} else {
|
||||
request.append(propertyName);
|
||||
request.append(propName);
|
||||
if (not) {
|
||||
request.append(" not");
|
||||
}
|
||||
@@ -88,15 +88,13 @@ class InExpression extends AbstractExpression {
|
||||
/**
|
||||
* Based on the number of values in the in clause.
|
||||
*/
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(InExpression.class).add(propName).add(values.length).add(not);
|
||||
builder.bind(values.length);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
int hc = 31;
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
@@ -105,4 +103,29 @@ class InExpression extends AbstractExpression {
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof InExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
InExpression that = (InExpression) other;
|
||||
return propName.equals(that.propName)
|
||||
&& not == that.not
|
||||
&& values.length == that.values.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
InExpression that = (InExpression) other;
|
||||
if (this.values.length != that.values.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (!values[i].equals(that.values[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user