mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
45
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec38137e5c | ||
|
|
bfcbd335c8 | ||
|
|
2cafdf709d | ||
|
|
ed2ea13deb | ||
|
|
8d3b4a0407 | ||
|
|
cb1fe297d6 | ||
|
|
6561c118ec | ||
|
|
eda7fe24c1 | ||
|
|
2353049c6f | ||
|
|
6c49ac0857 | ||
|
|
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 | ||
|
|
42b112c3d7 |
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>6.17.2</version>
|
||||
<version>7.1.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>avaje-ebeanorm</name>
|
||||
@@ -73,7 +73,7 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.5.3</version>
|
||||
<version>2.6.5</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>2.5.3</version>
|
||||
<version>2.6.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -138,8 +138,8 @@
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>2.1.0</version>
|
||||
<scope>test</scope>
|
||||
<version>2.4.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* Bean holding the details to update the document store.
|
||||
*/
|
||||
public final class DocStoreQueueEntry {
|
||||
|
||||
/**
|
||||
* Action to either update or delete a document from the index.
|
||||
*/
|
||||
public enum Action {
|
||||
|
||||
/**
|
||||
* Action is to update a document in the doc store.
|
||||
*/
|
||||
INDEX(1),
|
||||
|
||||
/**
|
||||
* Action is to delete a document from the doc store..
|
||||
*/
|
||||
DELETE(2),
|
||||
|
||||
/**
|
||||
* An update is required based on a change to a nested/embedded object at a given path.
|
||||
*/
|
||||
NESTED(3);
|
||||
|
||||
int value;
|
||||
|
||||
Action(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value associated with this action type.
|
||||
*/
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
final Action type;
|
||||
|
||||
final String queueId;
|
||||
|
||||
final String path;
|
||||
|
||||
final Object beanId;
|
||||
|
||||
/**
|
||||
* Construct for an INDEX or DELETE action.
|
||||
*/
|
||||
public DocStoreQueueEntry(Action type, String queueId, Object beanId) {
|
||||
this(type, queueId, null, beanId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for an NESTED/embedded path invalidation action.
|
||||
*/
|
||||
public DocStoreQueueEntry(Action type, String queueId, String path, Object beanId) {
|
||||
this.type = type;
|
||||
this.queueId = queueId;
|
||||
this.path = path;
|
||||
this.beanId = beanId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the event type.
|
||||
*/
|
||||
public Action getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associate queueId.
|
||||
*/
|
||||
public String getQueueId() {
|
||||
return queueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path if this is a nested update.
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean id (which matches the document id).
|
||||
*/
|
||||
public Object getBeanId() {
|
||||
return beanId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Document storage operations.
|
||||
*/
|
||||
public interface DocumentStore {
|
||||
|
||||
/**
|
||||
* Update the associated document store using the result of the query.
|
||||
* <p>
|
||||
* This will execute the query against the database creating a document for each
|
||||
* bean graph and sending this to the document store.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that the select and fetch paths of the query is set for you to match the
|
||||
* document structure needed based on <code>@DocStore</code> and <code>@DocStoreEmbedded</code>
|
||||
* so what this query requires is the predicates only.
|
||||
* </p>
|
||||
* <p>
|
||||
* This query will be executed using findEach so it is safe to use a query
|
||||
* that will fetch a lot of beans. The default bulkBatchSize is used.
|
||||
* </p>
|
||||
*
|
||||
* @param query The query used to update the associated document store.
|
||||
*/
|
||||
<T> void indexByQuery(Query<T> query);
|
||||
|
||||
/**
|
||||
* Update the associated document store index using the result of the query additionally specifying a
|
||||
* bulkBatchSize to use for sending the messages to ElasticSearch.
|
||||
*/
|
||||
<T> void indexByQuery(Query<T> query, int bulkBatchSize);
|
||||
|
||||
/**
|
||||
* Update the document store for all beans of this type.
|
||||
* <p>
|
||||
* This is the same as indexByQuery where the query has no predicates and so fetches all rows.
|
||||
*/
|
||||
void indexAll(Class<?> beanType);
|
||||
|
||||
/**
|
||||
* Return the bean by fetching it's content from the document store.
|
||||
* If the document is not found null is returned.
|
||||
*/
|
||||
@Nullable
|
||||
<T> T getById(Class<T> beanType, Object id);
|
||||
|
||||
/**
|
||||
* Execute the query against the document store returning the list.
|
||||
*/
|
||||
<T> List<T> findList(Query<T> query);
|
||||
|
||||
/**
|
||||
* Execute the query against the document store returning the paged list.
|
||||
* <p>
|
||||
* The query should have <code>firstRow</code> or <code>maxRows</code> set prior to calling this method.
|
||||
* </p>
|
||||
*/
|
||||
<T> PagedList<T> findPagedList(Query<T> query);
|
||||
|
||||
/**
|
||||
* Execute the query against the document store with the expectation of a large set of results
|
||||
* that are processed in a scrolling resultSet fashion.
|
||||
* <p>
|
||||
* For example, with the ElasticSearch doc store this uses SCROLL.
|
||||
* </p>
|
||||
*/
|
||||
<T> void findEach(Query<T> query, QueryEachConsumer<T> consumer);
|
||||
|
||||
/**
|
||||
* Process the queue entries sending updates to the document store or queuing them for later processing.
|
||||
*/
|
||||
long process(List<DocStoreQueueEntry> queueEntries) throws IOException;
|
||||
|
||||
/**
|
||||
* Drop the index from the document store (similar to DDL drop table).
|
||||
*/
|
||||
void dropIndex(String indexName);
|
||||
|
||||
/**
|
||||
* Create an index given a mapping file as a resource in the classPath (similar to DDL create table).
|
||||
*
|
||||
* @param indexName the name of the new index
|
||||
* @param alias the alias of the index
|
||||
* @param mappingResource the path of the mapping file as a resource in the classpath
|
||||
*/
|
||||
void createIndex(String indexName, String alias, String mappingResource);
|
||||
|
||||
/**
|
||||
* Copy the index to a new index.
|
||||
* <p>
|
||||
* This copy process does not use the database but instead will copy from the source index to a destination index.
|
||||
* </p>
|
||||
*
|
||||
* @param beanType The bean type of the source index
|
||||
* @param newIndex The name of the index to copy to
|
||||
*
|
||||
* @return the number of documents copied to the new index
|
||||
*/
|
||||
long copyIndex(Class<?> beanType, String newIndex);
|
||||
|
||||
/**
|
||||
* Copy entries from an index to a new index but limiting to documents that have been
|
||||
* modified since the sinceEpochMillis time.
|
||||
* <p>
|
||||
* To support this the document needs to have a <code>@WhenModified</code> property.
|
||||
* </p>
|
||||
*
|
||||
* @param beanType The bean type of the source index
|
||||
* @param newIndex The name of the index to copy to
|
||||
*
|
||||
* @return the number of documents copied to the new index
|
||||
*/
|
||||
long copyIndex(Class<?> beanType, String newIndex, long sinceEpochMillis);
|
||||
|
||||
}
|
||||
@@ -1886,11 +1886,16 @@ public interface EbeanServer {
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see com.avaje.ebean.text.PathProperties
|
||||
* @see Query#apply(com.avaje.ebean.text.PathProperties)
|
||||
* @see FetchPath
|
||||
* @see Query#apply(FetchPath)
|
||||
*/
|
||||
JsonContext json();
|
||||
|
||||
/**
|
||||
* Return the Document store.
|
||||
*/
|
||||
DocumentStore docStore();
|
||||
|
||||
/**
|
||||
* Publish a single bean given its type and id returning the resulting live bean.
|
||||
* <p>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.persistence.NonUniqueResultException;
|
||||
@@ -95,7 +94,7 @@ public interface ExpressionList<T> extends Serializable {
|
||||
/**
|
||||
* Apply the path properties to the query replacing the select and fetch clauses.
|
||||
*/
|
||||
Query<T> apply(PathProperties pathProperties);
|
||||
Query<T> apply(FetchPath fetchPath);
|
||||
|
||||
/**
|
||||
* Perform an 'As of' query using history tables to return the object graph
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Provides paths and properties for an object graph that can be used to control what parts of the object graph
|
||||
* is fetching (select and fetch clauses) and also can be used to control JSON marshalling (what parts of the object
|
||||
* graph are included in the JSON).
|
||||
*/
|
||||
public interface FetchPath {
|
||||
|
||||
/**
|
||||
* Return true if the path is included in this FetchPath.
|
||||
*/
|
||||
boolean hasPath(String path);
|
||||
|
||||
/**
|
||||
* Return the properties at the given path.
|
||||
*/
|
||||
Set<String> getProperties(String path);
|
||||
|
||||
/**
|
||||
* Apply the fetch path to the query.
|
||||
*/
|
||||
<T> void apply(Query<T> query);
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import com.avaje.ebean.util.ClassUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -588,10 +587,10 @@ public abstract class Model {
|
||||
/**
|
||||
* Creates a query applying the path properties to set the select and fetch clauses.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#apply(com.avaje.ebean.text.PathProperties)}
|
||||
* Equivalent to {@link Query#apply(FetchPath)}
|
||||
*/
|
||||
public Query<T> apply(PathProperties pathProperties) {
|
||||
return db().find(type).apply(pathProperties);
|
||||
public Query<T> apply(FetchPath fetchPath) {
|
||||
return db().find(type).apply(fetchPath);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
/**
|
||||
* Captures and wraps IOException's occurring during ElasticSearch processing etc.
|
||||
*/
|
||||
public class PersistenceIOException extends PersistenceException {
|
||||
|
||||
public PersistenceIOException(String msg, Exception cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
public PersistenceIOException(Exception cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.persistence.NonUniqueResultException;
|
||||
@@ -275,7 +274,7 @@ import java.util.Set;
|
||||
* @param <T>
|
||||
* the type of Entity bean this query will fetch.
|
||||
*/
|
||||
public interface Query<T> extends Serializable {
|
||||
public interface Query<T> {
|
||||
|
||||
/**
|
||||
* Return the RawSql that was set to use for this query.
|
||||
@@ -522,10 +521,10 @@ public interface Query<T> extends Serializable {
|
||||
/**
|
||||
* Apply the path properties replacing the select and fetch clauses.
|
||||
* <p>
|
||||
* This is typically used when the PathProperties is applied to both the query and the JSON output.
|
||||
* This is typically used when the FetchPath is applied to both the query and the JSON output.
|
||||
* </p>
|
||||
*/
|
||||
Query<T> apply(PathProperties pathProperties);
|
||||
Query<T> apply(FetchPath fetchPath);
|
||||
|
||||
/**
|
||||
* Execute the query returning the list of Id's.
|
||||
@@ -1286,6 +1285,14 @@ public interface Query<T> extends Serializable {
|
||||
*/
|
||||
Query<T> setUseQueryCache(boolean useQueryCache);
|
||||
|
||||
/**
|
||||
* Set to true if this query should execute against the doc store.
|
||||
* <p>
|
||||
* When setting this you may also consider disabling lazy loading.
|
||||
* </p>
|
||||
*/
|
||||
Query<T> setUseDocStore(boolean useDocStore);
|
||||
|
||||
/**
|
||||
* When set to true when you want the returned beans to be read only.
|
||||
*/
|
||||
@@ -1365,4 +1372,10 @@ public interface Query<T> extends Serializable {
|
||||
* </p>
|
||||
*/
|
||||
Set<String> validate();
|
||||
|
||||
/**
|
||||
* Return the query in JSON form for ElasticSearch doc store.
|
||||
*/
|
||||
String asElasticQuery();
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -648,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;
|
||||
}
|
||||
@@ -699,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.annotation.DocStoreEvent;
|
||||
import com.avaje.ebean.config.DocStoreConfig;
|
||||
import com.avaje.ebean.config.PersistBatch;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
@@ -81,6 +83,29 @@ public interface Transaction extends Closeable {
|
||||
*/
|
||||
boolean isActive();
|
||||
|
||||
/**
|
||||
* Set the behavior for document store updates on this transaction.
|
||||
* <p>
|
||||
* For example, set the mode to DocStoreEvent.IGNORE for this transaction and
|
||||
* then any changes via this transaction are not sent to the doc store. This
|
||||
* would be used when doing large bulk inserts into the database and we want
|
||||
* to control how that is sent to the document store.
|
||||
* </p>
|
||||
*/
|
||||
void setDocStoreUpdateMode(DocStoreEvent updateMode);
|
||||
|
||||
/**
|
||||
* Set the batch size to use for sending messages to the document store.
|
||||
* <p>
|
||||
* You might set this if you know the changes in this transaction result in especially large or
|
||||
* especially small payloads and want to adjust the batch size to match.
|
||||
* </p>
|
||||
* <p>
|
||||
* Setting this overrides the default of {@link DocStoreConfig#getBulkBatchSize()}
|
||||
* </p>
|
||||
*/
|
||||
void setDocStoreUpdateBatchSize(int batchSize);
|
||||
|
||||
/**
|
||||
* Explicitly turn off or on the cascading nature of save() and delete(). This
|
||||
* gives the developer exact control over what beans are saved and deleted
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Used to indicate that a particular string property should be treated as a 'code' and not analysed for text searching.
|
||||
* <p>
|
||||
* By default all Id properties and all Enum properties are treated as 'code' and not analysed.
|
||||
* </p>
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DocCode {
|
||||
|
||||
/**
|
||||
* Set to true to have the property additionally stored separately from _source.
|
||||
*/
|
||||
boolean store() default false;
|
||||
|
||||
/**
|
||||
* Set a boost value specific to this property.
|
||||
*/
|
||||
float boost() default 1;
|
||||
|
||||
/**
|
||||
* Set a value to use instead of null.
|
||||
*/
|
||||
String nullValue() default "";
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specify the entity type maps to a document store (like ElasticSearch).
|
||||
*/
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DocMapping {
|
||||
|
||||
/**
|
||||
* The property name the mapping applies to.
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Set this to true to indicate that this property should be un-analysed.
|
||||
*/
|
||||
boolean code() default false;
|
||||
|
||||
/**
|
||||
* Set this to true to get an additional un-analysed 'raw' field to use for sorting etc.
|
||||
*/
|
||||
boolean sortable() default false;
|
||||
|
||||
/**
|
||||
* Set to true to have the property additionally stored separately from _source.
|
||||
*/
|
||||
boolean store() default false;
|
||||
|
||||
/**
|
||||
* Set a boost value specific to this property.
|
||||
*/
|
||||
float boost() default 1;
|
||||
|
||||
/**
|
||||
* Set a value to use instead of null.
|
||||
*/
|
||||
String nullValue() default "";
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specify the entity type maps to a document store (like ElasticSearch).
|
||||
*/
|
||||
@Target({ ElementType.FIELD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DocProperty {
|
||||
|
||||
/**
|
||||
* Set this to true to indicate that this property should be un-analysed.
|
||||
*/
|
||||
boolean code() default false;
|
||||
|
||||
/**
|
||||
* Set this to true to get an additional un-analysed 'raw' field to use for sorting etc.
|
||||
*/
|
||||
boolean sortable() default false;
|
||||
|
||||
/**
|
||||
* Set to true to have the property additionally stored separately from _source.
|
||||
*/
|
||||
boolean store() default false;
|
||||
|
||||
/**
|
||||
* Set a boost value specific to this property.
|
||||
*/
|
||||
float boost() default 1;
|
||||
|
||||
/**
|
||||
* Set a value to use instead of null.
|
||||
*/
|
||||
String nullValue() default "";
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Used to indicate that a particular string property should support sorting. What this typically means is that
|
||||
* for ElasticSearch an additional 'raw' field is added that stores the un-analysed value. This un-analysed value
|
||||
* can be used for sorting etc and the original field used for text searching.
|
||||
* <p>
|
||||
* For example, customer name and product name are good candidates for marking with @DocSortable.
|
||||
* </p>
|
||||
*/
|
||||
@Target({ ElementType.FIELD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DocSortable {
|
||||
|
||||
/**
|
||||
* Set to true to have the property additionally stored separately from _source.
|
||||
*/
|
||||
boolean store() default false;
|
||||
|
||||
/**
|
||||
* Set a boost value specific to this property.
|
||||
*/
|
||||
float boost() default 1;
|
||||
|
||||
/**
|
||||
* Set a value to use instead of null.
|
||||
*/
|
||||
String nullValue() default "";
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specify the entity type maps to a document store (like ElasticSearch).
|
||||
*/
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DocStore {
|
||||
|
||||
/**
|
||||
* A unique Id used when queuing reindex events.
|
||||
*/
|
||||
String queueId() default "";
|
||||
|
||||
/**
|
||||
* The ElasticSearch index name. If left unspecified the short name of the bean type is used.
|
||||
*/
|
||||
String indexName() default "";
|
||||
|
||||
/**
|
||||
* The ElasticSearch index type. If left unspecified the short name of the bean type is used.
|
||||
*/
|
||||
String indexType() default "";
|
||||
|
||||
/**
|
||||
* The number of shards this index should use.
|
||||
*/
|
||||
int shards() default 0;
|
||||
|
||||
/**
|
||||
* The number of replicas this index should use.
|
||||
*/
|
||||
int replicas() default 0;
|
||||
|
||||
/**
|
||||
* Additional mapping that can be defined on the properties.
|
||||
*/
|
||||
DocMapping[] mapping() default {};
|
||||
|
||||
/**
|
||||
* Specify the behavior when bean Insert, Update, Delete events occur.
|
||||
*/
|
||||
DocStoreEvent persist() default DocStoreEvent.DEFAULT;
|
||||
|
||||
/**
|
||||
* Specify the behavior when bean Insert occurs.
|
||||
*/
|
||||
DocStoreEvent insert() default DocStoreEvent.DEFAULT;
|
||||
|
||||
/**
|
||||
* Specify the behavior when bean Update occurs.
|
||||
*/
|
||||
DocStoreEvent update() default DocStoreEvent.DEFAULT;
|
||||
|
||||
/**
|
||||
* Specify the behavior when bean Delete occurs.
|
||||
*/
|
||||
DocStoreEvent delete() default DocStoreEvent.DEFAULT;
|
||||
|
||||
/**
|
||||
* Specify to include only some properties in the doc store document.
|
||||
* <p>
|
||||
* If this is left as default then all scalar properties are included,
|
||||
* all @ManyToOne properties are included with just the nested id property
|
||||
* and no @OneToMany properties are included.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that typically DocStoreEmbedded is used on @ManyToOne and @OneToMany
|
||||
* properties to indicate what part of the nested document should be included.
|
||||
* </p>
|
||||
*
|
||||
* <h3>Example:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // only include the customer id and name
|
||||
* @DocStore(doc = "id,name")
|
||||
* @Entity @Table(name = "o_order")
|
||||
* public class Customer {
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
String doc() default "";
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specify the property is included in the parent document store index.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
*
|
||||
* @DocStore
|
||||
* @Entity @Table(name = "o_order")
|
||||
* public class Order {
|
||||
*
|
||||
* ...
|
||||
* // include some customer details including
|
||||
* // nested billingAddress
|
||||
* @DocStoreEmbedded(doc = "id,status,name,billingAddress(*,country(*)")
|
||||
* @ManyToOne
|
||||
* Customer customer;
|
||||
*
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@Target({ ElementType.FIELD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DocStoreEmbedded {
|
||||
|
||||
/**
|
||||
* The properties on the embedded bean to include in the index.
|
||||
*/
|
||||
String doc() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
/**
|
||||
* Defines the behavior options when a Insert, Update or Delete event occurs
|
||||
* on a bean with an associated ElasticSearch index.
|
||||
* <p>
|
||||
* For some indexes or some transactions it can be beneficial to queue the event
|
||||
* for later processing rather than look to update ElasticSearch at that time.
|
||||
* </p>
|
||||
*/
|
||||
public enum DocStoreEvent {
|
||||
|
||||
/**
|
||||
* Add the event to the queue for processing later (delaying the update to the document store).
|
||||
*/
|
||||
QUEUE,
|
||||
|
||||
/**
|
||||
* Update the document store when transaction succeeds.
|
||||
*/
|
||||
UPDATE,
|
||||
|
||||
/**
|
||||
* Ignore the event and not update the document store.
|
||||
* <p>
|
||||
* This can be used on a index or for a transaction where you want to have more
|
||||
* manual programmatic control over the updating of the document store. Say you want to
|
||||
* IGNORE on a particular transaction and instead manually queue a bulk update.
|
||||
* </p>
|
||||
*/
|
||||
IGNORE,
|
||||
|
||||
/**
|
||||
* The actual mode of QUEUE, UPDATE or IGNORE is set from the default configuration.
|
||||
*/
|
||||
DEFAULT
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -219,6 +219,18 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
this.fullyLoadedBean = fullyLoadedBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check each property to see if the bean is partially loaded.
|
||||
*/
|
||||
public boolean isPartial() {
|
||||
for (int i = 0; i < loadedProps.length; i++) {
|
||||
if (!loadedProps[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this bean has been directly modified (it has oldValues) or
|
||||
* if any embedded beans are either new or dirty (and hence need saving).
|
||||
@@ -469,7 +481,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
public void setLoadedProperty(int propertyIndex) {
|
||||
loadedProps[propertyIndex] = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the property is loaded.
|
||||
*/
|
||||
@@ -560,6 +572,23 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
return props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the array of flags indicating the dirty properties.
|
||||
*/
|
||||
public boolean[] getDirtyProperties() {
|
||||
int len = getPropertyLength();
|
||||
boolean[] dirties = new boolean[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (changedProps != null && changedProps[i]) {
|
||||
dirties[i] = true;
|
||||
} else if (embeddedDirty != null && embeddedDirty[i]) {
|
||||
// an embedded property has been changed - recurse
|
||||
dirties[i] = true;
|
||||
}
|
||||
}
|
||||
return dirties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of dirty properties.
|
||||
*/
|
||||
|
||||
@@ -10,12 +10,12 @@ package com.avaje.ebean.bean;
|
||||
public interface PersistenceContext {
|
||||
|
||||
/**
|
||||
* Put the entity bean into the PersistanceContext.
|
||||
* Put the entity bean into the PersistenceContext.
|
||||
*/
|
||||
void put(Object id, Object bean);
|
||||
|
||||
/**
|
||||
* Put the entity bean into the PersistanceContext if one is not already
|
||||
* Put the entity bean into the PersistenceContext if one is not already
|
||||
* present (for this id).
|
||||
* <p>
|
||||
* Returns an existing entity bean (if one is already there) and otherwise
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -90,6 +90,13 @@ public class ClassLoadConfig {
|
||||
return context.forName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the classLoader to use for service loading etc.
|
||||
*/
|
||||
public ClassLoader getClassLoader() {
|
||||
return context.getClassLoader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the preferred, caller and context class loaders.
|
||||
*/
|
||||
@@ -138,6 +145,9 @@ public class ClassLoadConfig {
|
||||
return Class.forName(name, true, classLoader);
|
||||
}
|
||||
|
||||
ClassLoader getClassLoader() {
|
||||
return preferredLoader != null ? preferredLoader : contextLoader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
import com.avaje.ebean.annotation.DocStoreEvent;
|
||||
|
||||
/**
|
||||
* Configuration for the Document store (ElasticSearch) integration.
|
||||
*/
|
||||
public class DocStoreConfig {
|
||||
|
||||
/**
|
||||
* True when the Document store integration is active/on.
|
||||
*/
|
||||
boolean active;
|
||||
|
||||
/**
|
||||
* When true the Document store should drop and re-create any document mapping (like DDL).
|
||||
*/
|
||||
boolean dropCreate;
|
||||
|
||||
/**
|
||||
* The URL of the Document store server. For example: http://localhost:9200.
|
||||
*/
|
||||
String url;
|
||||
|
||||
/**
|
||||
* The default mode used by indexes.
|
||||
*/
|
||||
DocStoreEvent persist = DocStoreEvent.UPDATE;
|
||||
|
||||
/**
|
||||
* The default batch size to use for the Bulk API calls.
|
||||
*/
|
||||
int bulkBatchSize = 1000;
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the Document store (ElasticSearch) integration is active.
|
||||
*/
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to make the Document store (ElasticSearch) integration active.
|
||||
*/
|
||||
public void setActive(boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the document store should recreate mappings.
|
||||
*/
|
||||
public boolean isDropCreate() {
|
||||
return dropCreate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if the document store should recreate mappings.
|
||||
*/
|
||||
public void setDropCreate(boolean dropCreate) {
|
||||
this.dropCreate = dropCreate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default behavior for when Insert, Update and Delete events occur on beans that have an associated
|
||||
* Document store.
|
||||
*/
|
||||
public DocStoreEvent getPersist() {
|
||||
return persist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default behavior for when Insert, Update and Delete events occur on beans that have an associated
|
||||
* Document store.
|
||||
* <ul>
|
||||
* <li>DocStoreEvent.UPDATE - build and send message to Bulk API</li>
|
||||
* <li>DocStoreEvent.QUEUE - add an entry with the index type and id only into a queue for later processing</li>
|
||||
* <li>DocStoreEvent.IGNORE - ignore. Most likely used when some scheduled batch job handles updating the index</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* You might choose to use QUEUE if that particular index data is updating very frequently or the cost of indexing
|
||||
* is expensive. Setting it to QUEUE can mean many changes can be batched together potentially coalescing multiple
|
||||
* updates for an index entry into a single update.
|
||||
* </p>
|
||||
* <p>
|
||||
* You might choose to use IGNORE when you have your own external process for updating the indexes. In this case
|
||||
* you don't want Ebean to do anything when the data changes.
|
||||
* </p>
|
||||
*/
|
||||
public void setPersist(DocStoreEvent persist) {
|
||||
this.persist = persist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the URL to the Document store.
|
||||
*/
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL to the Document store server.
|
||||
*
|
||||
* For a local ElasticSearch server this would be: http://localhost:9200
|
||||
*/
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the default batch size to use for calls to the Bulk API.
|
||||
*/
|
||||
public int getBulkBatchSize() {
|
||||
return bulkBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default batch size to use for calls to the Bulk API.
|
||||
* <p>
|
||||
* The batch size can be set on a transaction via {@link com.avaje.ebean.Transaction#setDocStoreUpdateBatchSize(int)}.
|
||||
* </p>
|
||||
*/
|
||||
public void setBulkBatchSize(int bulkBatchSize) {
|
||||
this.bulkBatchSize = bulkBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings specified in properties files.
|
||||
*/
|
||||
public void loadSettings(PropertiesWrapper properties) {
|
||||
|
||||
active = properties.getBoolean("docstore.active", active);
|
||||
url = properties.get("docstore.url", url);
|
||||
persist = properties.getEnum(DocStoreEvent.class, "docstore.persist", persist);
|
||||
bulkBatchSize = properties.getInt("docstore.bulkBatchSize", bulkBatchSize);
|
||||
}
|
||||
}
|
||||
@@ -25,8 +25,10 @@ import com.fasterxml.jackson.core.JsonFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* The configuration used for creating a EbeanServer.
|
||||
@@ -129,6 +131,11 @@ public class ServerConfig {
|
||||
*/
|
||||
private String classPathReaderClassName;
|
||||
|
||||
/**
|
||||
* Configuration for the ElasticSearch integration.
|
||||
*/
|
||||
private DocStoreConfig docStoreConfig = new DocStoreConfig();
|
||||
|
||||
/**
|
||||
* This is used to populate @WhoCreated, @WhoModified and
|
||||
* support other audit features (who executed a query etc).
|
||||
@@ -283,7 +290,7 @@ public class ServerConfig {
|
||||
private DbConstraintNaming constraintNaming = new DbConstraintNaming();
|
||||
|
||||
/**
|
||||
* Behaviour of update to include on the change properties.
|
||||
* Behaviour of update to include on the change properties.
|
||||
*/
|
||||
private boolean updateChangesOnly = true;
|
||||
|
||||
@@ -387,6 +394,8 @@ public class ServerConfig {
|
||||
*/
|
||||
private boolean expressionEqualsWithNullAsNoop;
|
||||
|
||||
private String jodaLocalTimeMode;
|
||||
|
||||
/**
|
||||
* Construct a Server Configuration for programmatically creating an EbeanServer.
|
||||
*/
|
||||
@@ -1151,6 +1160,20 @@ public class ServerConfig {
|
||||
this.namingConvention = namingConvention;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configuration for the ElasticSearch integration.
|
||||
*/
|
||||
public DocStoreConfig getDocStoreConfig() {
|
||||
return docStoreConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the configuration for the ElasticSearch integration.
|
||||
*/
|
||||
public void setDocStoreConfig(DocStoreConfig docStoreConfig) {
|
||||
this.docStoreConfig = docStoreConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the constraint naming convention used in DDL generation.
|
||||
*/
|
||||
@@ -1640,6 +1663,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.
|
||||
@@ -2118,6 +2155,23 @@ public class ServerConfig {
|
||||
this.classLoadConfig = classLoadConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the service loader using the classLoader defined in ClassLoadConfig.
|
||||
*/
|
||||
public <T> ServiceLoader<T> serviceLoad(Class<T> spiService) {
|
||||
|
||||
return ServiceLoader.load(spiService, classLoadConfig.getClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first service using the service loader (or null).
|
||||
*/
|
||||
public <T> T service(Class<T> spiService) {
|
||||
ServiceLoader<T> load = serviceLoad(spiService);
|
||||
Iterator<T> serviceInstances = load.iterator();
|
||||
return serviceInstances.hasNext() ? serviceInstances.next() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings from ebean.properties.
|
||||
*/
|
||||
@@ -2195,6 +2249,13 @@ public class ServerConfig {
|
||||
dataSourceConfig.loadSettings(p.withPrefix("datasource"));
|
||||
}
|
||||
|
||||
/**
|
||||
* This is broken out for the same reason as above - preserve existing behaviour but let it be overridden.
|
||||
*/
|
||||
protected void loadDocStoreSettings(PropertiesWrapper p) {
|
||||
docStoreConfig.loadSettings(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is broken out for the same reason as above - preserve existing behaviour but let it be overridden.
|
||||
*/
|
||||
@@ -2223,6 +2284,11 @@ public class ServerConfig {
|
||||
}
|
||||
loadDataSourceSettings(p);
|
||||
|
||||
if (docStoreConfig == null) {
|
||||
docStoreConfig = new DocStoreConfig();
|
||||
}
|
||||
docStoreConfig.loadSettings(p);
|
||||
|
||||
explicitTransactionBeginMode = p.getBoolean("explicitTransactionBeginMode", explicitTransactionBeginMode);
|
||||
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
|
||||
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
|
||||
@@ -2286,6 +2352,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);
|
||||
|
||||
@@ -16,8 +16,6 @@ public class OraclePlatform extends DatabasePlatform {
|
||||
this.name = "oracle";
|
||||
this.maxTableNameLength = 30;
|
||||
this.maxConstraintNameLength = 30;
|
||||
// OnQueryOnly.CLOSE as a performance optimisation on Oracle
|
||||
this.onQueryOnly = OnQueryOnly.CLOSE;
|
||||
this.dbEncrypt = new OracleDbEncrypt();
|
||||
this.sqlLimiter = new RownumSqlLimiter();
|
||||
this.platformDdl = new Oracle10Ddl(this.dbTypeMap, this.dbIdentity);
|
||||
|
||||
@@ -19,9 +19,6 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
public PostgresPlatform() {
|
||||
super();
|
||||
this.name = "postgres";
|
||||
|
||||
// OnQueryOnly.CLOSE as a performance optimisation on Postgres
|
||||
this.onQueryOnly = OnQueryOnly.CLOSE;
|
||||
this.likeClause = "like ? escape''";
|
||||
this.selectCountWithAlias = true;
|
||||
this.blobDbType = Types.LONGVARBINARY;
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
|
||||
|
||||
public PostgresHistoryDdl() {
|
||||
this.currentTimestamp = "statement_timestamp()";
|
||||
this.currentTimestamp = "current_timestamp";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+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
|
||||
|
||||
@@ -38,7 +38,7 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
|
||||
table.setComment(descriptor.getDbComment());
|
||||
if (descriptor.isHistorySupport()) {
|
||||
table.setWithHistory(true);
|
||||
BeanProperty whenCreated = descriptor.findWhenCreatedProperty();
|
||||
BeanProperty whenCreated = descriptor.getWhenCreatedProperty();
|
||||
if (whenCreated != null) {
|
||||
table.setWhenCreatedColumn(whenCreated.getDbColumn());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.avaje.ebean.plugin;
|
||||
|
||||
import com.avaje.ebean.FetchPath;
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Doc store functions for a specific entity bean type.
|
||||
*
|
||||
* @param <T> The type of entity bean
|
||||
*/
|
||||
public interface BeanDocType<T> {
|
||||
|
||||
/**
|
||||
* Return the doc store index type for this bean type.
|
||||
*/
|
||||
String getIndexType();
|
||||
|
||||
/**
|
||||
* Return the doc store index name for this bean type.
|
||||
*/
|
||||
String getIndexName();
|
||||
|
||||
/**
|
||||
* Apply the appropriate fetch path to the query such that the query returns beans matching
|
||||
* the document store structure with the expected embedded properties.
|
||||
*/
|
||||
void applyPath(Query<T> spiQuery);
|
||||
|
||||
/**
|
||||
* Return the FetchPath for the embedded document.
|
||||
*/
|
||||
FetchPath getEmbedded(String path);
|
||||
|
||||
/**
|
||||
* For embedded 'many' properties we need a FetchPath relative to the root which is used to
|
||||
* build and replace the embedded list.
|
||||
*/
|
||||
FetchPath getEmbeddedManyRoot(String path);
|
||||
|
||||
/**
|
||||
* Return a 'raw' property mapped for the given property.
|
||||
* If none exists the given property is returned.
|
||||
*/
|
||||
String rawProperty(String property);
|
||||
|
||||
/**
|
||||
* Store the bean in the doc store index.
|
||||
* <p>
|
||||
* This somewhat assumes the bean is fetched with appropriate path properties
|
||||
* to match the expected document structure.
|
||||
*/
|
||||
void index(Object idValue, T bean, DocStoreUpdateContext txn) throws IOException;
|
||||
|
||||
/**
|
||||
* Add a delete by Id to the doc store.
|
||||
*/
|
||||
void deleteById(Object idValue, DocStoreUpdateContext txn) throws IOException;
|
||||
|
||||
/**
|
||||
* Add a embedded document update to the doc store.
|
||||
*
|
||||
* @param idValue the Id value of the bean holding the embedded document
|
||||
* @param embeddedProperty the embedded property
|
||||
* @param embeddedRawContent the content of the embedded document in JSON form
|
||||
* @param txn the doc store transaction to add the update to
|
||||
*/
|
||||
void updateEmbedded(Object idValue, String embeddedProperty, String embeddedRawContent, DocStoreUpdateContext txn) throws IOException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package com.avaje.ebean.plugin;
|
||||
|
||||
import com.avaje.ebean.config.dbplatform.IdType;
|
||||
import com.avaje.ebean.event.BeanFindController;
|
||||
import com.avaje.ebean.event.BeanPersistController;
|
||||
import com.avaje.ebean.event.BeanPersistListener;
|
||||
import com.avaje.ebean.event.BeanQueryAdapter;
|
||||
import com.avaje.ebean.text.json.JsonReadOptions;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocumentMapping;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Information and methods on BeanDescriptors made available to plugins.
|
||||
*/
|
||||
public interface BeanType<T> {
|
||||
|
||||
/**
|
||||
* Return the full name of the bean type.
|
||||
*/
|
||||
String getFullName();
|
||||
|
||||
/**
|
||||
* Return the class type this BeanDescriptor describes.
|
||||
*/
|
||||
Class<T> getBeanType();
|
||||
|
||||
/**
|
||||
* Return the type bean for an OneToMany or ManyToOne or ManyToMany property.
|
||||
*/
|
||||
BeanType<?> getBeanTypeAtPath(String propertyName);
|
||||
|
||||
/**
|
||||
* Return all the properties for this bean type.
|
||||
*/
|
||||
Collection<? extends Property> allProperties();
|
||||
|
||||
/**
|
||||
* Return the Id property.
|
||||
*/
|
||||
Property getIdProperty();
|
||||
|
||||
/**
|
||||
* Return the when modified property if there is one defined.
|
||||
*/
|
||||
Property getWhenModifiedProperty();
|
||||
|
||||
/**
|
||||
* Return the when created property if there is one defined.
|
||||
*/
|
||||
Property getWhenCreatedProperty();
|
||||
|
||||
/**
|
||||
* Return the SpiProperty for a property to read values from a bean.
|
||||
*/
|
||||
Property getProperty(String propertyName);
|
||||
|
||||
/**
|
||||
* Return the SpiExpressionPath for a given property path.
|
||||
* <p>
|
||||
* This can return a property or nested property path.
|
||||
* </p>
|
||||
*/
|
||||
ExpressionPath getExpressionPath(String path);
|
||||
|
||||
/**
|
||||
* Return true if the property is a valid known property or path for the given bean type.
|
||||
*/
|
||||
boolean isValidExpression(String property);
|
||||
|
||||
/**
|
||||
* Return the base table this bean type maps to.
|
||||
*/
|
||||
String getBaseTable();
|
||||
|
||||
/**
|
||||
* Create a new instance of the bean.
|
||||
*/
|
||||
T createBean();
|
||||
|
||||
/**
|
||||
* Return the bean id. This is the same as getBeanId() but without the generic type.
|
||||
*/
|
||||
Object beanId(Object bean);
|
||||
|
||||
/**
|
||||
* Return the id value for the given bean.
|
||||
*/
|
||||
Object getBeanId(T bean);
|
||||
|
||||
/**
|
||||
* Set the id value to the bean.
|
||||
*/
|
||||
void setBeanId(T bean, Object idValue);
|
||||
|
||||
/**
|
||||
* Return the bean persist controller.
|
||||
*/
|
||||
BeanPersistController getPersistController();
|
||||
|
||||
/**
|
||||
* Return the bean persist listener.
|
||||
*/
|
||||
BeanPersistListener getPersistListener();
|
||||
|
||||
/**
|
||||
* Return the beanFinder. Usually null unless overriding the finder.
|
||||
*/
|
||||
BeanFindController getFindController();
|
||||
|
||||
/**
|
||||
* Return the BeanQueryAdapter or null if none is defined.
|
||||
*/
|
||||
BeanQueryAdapter getQueryAdapter();
|
||||
|
||||
/**
|
||||
* Return the identity generation type.
|
||||
*/
|
||||
IdType getIdType();
|
||||
|
||||
/**
|
||||
* Return the sequence name associated to this entity bean type (if there is one).
|
||||
*/
|
||||
String getSequenceName();
|
||||
|
||||
/**
|
||||
* Return true if this bean type has doc store backing.
|
||||
*/
|
||||
boolean isDocStoreMapped();
|
||||
|
||||
/**
|
||||
* Return the DocumentMapping for this bean type.
|
||||
* <p>
|
||||
* This is the document structure and mapping options for how this bean type is mapped
|
||||
* for the document store.
|
||||
* </p>
|
||||
*/
|
||||
DocumentMapping getDocMapping();
|
||||
|
||||
/**
|
||||
* Return the doc store queueId for this bean type.
|
||||
*/
|
||||
String getDocStoreQueueId();
|
||||
|
||||
/**
|
||||
* Return the doc store support for this bean type.\
|
||||
*/
|
||||
BeanDocType<T> docStore();
|
||||
|
||||
/**
|
||||
* Read the JSON content returning the bean.
|
||||
*/
|
||||
T jsonRead(JsonParser parser, JsonReadOptions readOptions, Object objectMapper) throws IOException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.avaje.ebean.plugin;
|
||||
|
||||
/**
|
||||
* A dot notation expression path.
|
||||
*/
|
||||
public interface ExpressionPath {
|
||||
|
||||
/**
|
||||
* Return true if there is a property on the path that is a many property.
|
||||
*/
|
||||
boolean containsMany();
|
||||
|
||||
/**
|
||||
* Set a value to the bean for this expression path.
|
||||
*
|
||||
* @param bean the bean to set the value on
|
||||
* @param value the value to set
|
||||
*/
|
||||
void set(Object bean, Object value);
|
||||
}
|
||||
+1
-1
@@ -3,7 +3,7 @@ package com.avaje.ebean.plugin;
|
||||
/**
|
||||
* A 'plugin' that wants to be configured on startup so it can use features of the EbeanServer itself.
|
||||
*/
|
||||
public interface SpiServerPlugin {
|
||||
public interface Plugin {
|
||||
|
||||
/**
|
||||
* Configure the plugin.
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.avaje.ebean.plugin;
|
||||
|
||||
/**
|
||||
* Property of a entity bean that can be read.
|
||||
*/
|
||||
public interface Property {
|
||||
|
||||
/**
|
||||
* Return the name of the property.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Return the value of the property on the given bean.
|
||||
*/
|
||||
Object getVal(Object bean);
|
||||
|
||||
/**
|
||||
* Return true if this is a OneToMany or ManyToMany property.
|
||||
*/
|
||||
boolean isMany();
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package com.avaje.ebean.plugin;
|
||||
|
||||
import com.avaje.ebean.config.dbplatform.IdType;
|
||||
import com.avaje.ebean.event.BeanFindController;
|
||||
import com.avaje.ebean.event.BeanPersistController;
|
||||
import com.avaje.ebean.event.BeanPersistListener;
|
||||
import com.avaje.ebean.event.BeanQueryAdapter;
|
||||
|
||||
/**
|
||||
* Information and methods on BeanDescriptors made available to plugins.
|
||||
*/
|
||||
public interface SpiBeanType<T> {
|
||||
|
||||
/**
|
||||
* Return the class type this BeanDescriptor describes.
|
||||
*/
|
||||
Class<T> getBeanType();
|
||||
|
||||
/**
|
||||
* Return true if the property is a valid known property or path for the given bean type.
|
||||
*/
|
||||
boolean isValidExpression(String property);
|
||||
|
||||
/**
|
||||
* Return the base table this bean type maps to.
|
||||
*/
|
||||
String getBaseTable();
|
||||
|
||||
/**
|
||||
* Return the id value for the given bean.
|
||||
*/
|
||||
Object getBeanId(T bean);
|
||||
|
||||
/**
|
||||
* Return the bean persist controller.
|
||||
*/
|
||||
BeanPersistController getPersistController();
|
||||
|
||||
/**
|
||||
* Return the bean persist listener.
|
||||
*/
|
||||
BeanPersistListener getPersistListener();
|
||||
|
||||
/**
|
||||
* Return the beanFinder. Usually null unless overriding the finder.
|
||||
*/
|
||||
BeanFindController getFindController();
|
||||
|
||||
/**
|
||||
* Return the BeanQueryAdapter or null if none is defined.
|
||||
*/
|
||||
BeanQueryAdapter getQueryAdapter();
|
||||
|
||||
/**
|
||||
* Return the identity generation type.
|
||||
*/
|
||||
IdType getIdType();
|
||||
|
||||
/**
|
||||
* Return the sequence name associated to this entity bean type (if there is one).
|
||||
*/
|
||||
String getSequenceName();
|
||||
|
||||
}
|
||||
@@ -24,16 +24,20 @@ public interface SpiServer extends EbeanServer {
|
||||
/**
|
||||
* Return all the bean types registered on this server instance.
|
||||
*/
|
||||
List<? extends SpiBeanType<?>> getBeanTypes();
|
||||
List<? extends BeanType<?>> getBeanTypes();
|
||||
|
||||
/**
|
||||
* Return the bean type for a given entity bean class.
|
||||
*/
|
||||
<T> SpiBeanType<T> getBeanType(Class<T> beanClass);
|
||||
<T> BeanType<T> getBeanType(Class<T> beanClass);
|
||||
|
||||
/**
|
||||
* Return the bean types mapped to the given base table.
|
||||
*/
|
||||
List<? extends SpiBeanType<?>> getBeanTypes(String baseTableName);
|
||||
List<? extends BeanType<?>> getBeanTypes(String baseTableName);
|
||||
|
||||
/**
|
||||
* Return the bean type for a given doc store queueId.
|
||||
*/
|
||||
BeanType<?> getBeanTypeForQueueId(String queueId);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package com.avaje.ebean.text;
|
||||
|
||||
import com.avaje.ebean.FetchPath;
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.avaje.ebean.Query;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This is a Tree like structure of paths and properties that can be used for
|
||||
@@ -20,7 +22,7 @@ import com.avaje.ebean.Query;
|
||||
* render (JAX-RS JSON / XML).
|
||||
* </p>
|
||||
*/
|
||||
public class PathProperties {
|
||||
public class PathProperties implements FetchPath {
|
||||
|
||||
private final Map<String, Props> pathMap;
|
||||
|
||||
@@ -44,39 +46,6 @@ public class PathProperties {
|
||||
this.pathMap.put(null, rootProps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for creating copy.
|
||||
*/
|
||||
private PathProperties(PathProperties orig) {
|
||||
this.rootProps = orig.rootProps.copy(this);
|
||||
this.pathMap = new LinkedHashMap<String, Props>(orig.pathMap.size());
|
||||
Set<Entry<String, Props>> entrySet = orig.pathMap.entrySet();
|
||||
for (Entry<String, Props> e : entrySet) {
|
||||
pathMap.put(e.getKey(), e.getValue().copy(this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of this instance so that it can be modified.
|
||||
* <p>
|
||||
* For example, you may want to create a copy to add extra properties to a
|
||||
* path so that they are fetching in a ORM query but perhaps not rendered by
|
||||
* default. That is, use a PathProperties for JSON or XML rendering, but
|
||||
* create a copy, add some extra properties and then use that copy to define
|
||||
* an ORM query.
|
||||
* </p>
|
||||
*/
|
||||
public PathProperties copy() {
|
||||
return new PathProperties(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are no paths defined.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return pathMap.isEmpty();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return pathMap.toString();
|
||||
}
|
||||
@@ -84,6 +53,7 @@ public class PathProperties {
|
||||
/**
|
||||
* Return true if the path is defined and has properties.
|
||||
*/
|
||||
@Override
|
||||
public boolean hasPath(String path) {
|
||||
Props props = pathMap.get(path);
|
||||
return props != null && !props.isEmpty();
|
||||
@@ -92,40 +62,38 @@ public class PathProperties {
|
||||
/**
|
||||
* Get the properties for a given path.
|
||||
*/
|
||||
public Set<String> get(String path) {
|
||||
@Override
|
||||
public Set<String> getProperties(String path) {
|
||||
Props props = pathMap.get(path);
|
||||
return props == null ? null : props.getProperties();
|
||||
}
|
||||
|
||||
public void addToPath(String path, String property) {
|
||||
getProps(path).getProperties().add(property);
|
||||
}
|
||||
|
||||
public void addNested(String prefix, PathProperties pathProps) {
|
||||
|
||||
for (Entry<String, Props> entry : pathProps.pathMap.entrySet()) {
|
||||
|
||||
String path = pathAdd(prefix, entry.getKey());
|
||||
String[] split = SplitName.split(path);
|
||||
getProps(split[0]).addProperty(split[1]);
|
||||
getProps(path).addProps(entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private String pathAdd(String prefix, String key) {
|
||||
return key == null ? prefix : prefix + "." + key;
|
||||
}
|
||||
|
||||
Props getProps(String path) {
|
||||
Props props = pathMap.get(path);
|
||||
if (props == null) {
|
||||
props = new Props(this, null, path);
|
||||
pathMap.put(path, props);
|
||||
}
|
||||
props.getProperties().add(property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the properties for a given path.
|
||||
*/
|
||||
public void put(String path, Set<String> properties) {
|
||||
pathMap.put(path, new Props(this, null, path, properties));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a path returning the properties set for that path.
|
||||
*/
|
||||
public Set<String> remove(String path) {
|
||||
Props props = pathMap.remove(path);
|
||||
return props == null ? null : props.getProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a shallow copy of the paths.
|
||||
*/
|
||||
public Set<String> getPaths() {
|
||||
return new LinkedHashSet<String>(pathMap.keySet());
|
||||
return props;
|
||||
}
|
||||
|
||||
public Collection<Props> getPathProps() {
|
||||
@@ -135,7 +103,7 @@ public class PathProperties {
|
||||
/**
|
||||
* Apply these path properties as fetch paths to the query.
|
||||
*/
|
||||
public void apply(Query<?> query) {
|
||||
public <T> void apply(Query<T> query) {
|
||||
|
||||
for (Entry<String, Props> entry : pathMap.entrySet()) {
|
||||
String path = entry.getKey();
|
||||
@@ -153,6 +121,40 @@ public class PathProperties {
|
||||
return rootProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the property (dot notation) is included in the PathProperties.
|
||||
*/
|
||||
public boolean includesProperty(String name) {
|
||||
|
||||
String[] split = SplitName.split(name);
|
||||
Props props = pathMap.get(split[0]);
|
||||
return (props != null && props.includes(split[1]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the property is included using a prefix.
|
||||
*/
|
||||
public boolean includesProperty(String prefix, String name) {
|
||||
return includesProperty(SplitName.add(prefix, name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the fetch path is included in the PathProperties.
|
||||
* <p>
|
||||
* The fetch path is a OneToMany or ManyToMany path in dot notation.
|
||||
* </p>
|
||||
*/
|
||||
public boolean includesPath(String path) {
|
||||
return pathMap.containsKey(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the path is included using a prefix.
|
||||
*/
|
||||
public boolean includesPath(String prefix, String name) {
|
||||
return includesPath(SplitName.add(prefix, name));
|
||||
}
|
||||
|
||||
public static class Props {
|
||||
|
||||
private final PathProperties owner;
|
||||
@@ -160,9 +162,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;
|
||||
@@ -173,13 +175,6 @@ public class PathProperties {
|
||||
this(owner, parentPath, path, new LinkedHashSet<String>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a shallow copy of this Props instance.
|
||||
*/
|
||||
public Props copy(PathProperties newOwner) {
|
||||
return new Props(newOwner, parentPath, path, new LinkedHashSet<String>(propSet));
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
@@ -195,7 +190,7 @@ public class PathProperties {
|
||||
/**
|
||||
* Return the properties for this property set.
|
||||
*/
|
||||
public Set<String> getProperties() {
|
||||
public LinkedHashSet<String> getProperties() {
|
||||
return propSet;
|
||||
}
|
||||
|
||||
@@ -228,15 +223,15 @@ public class PathProperties {
|
||||
/**
|
||||
* Add a child Property set.
|
||||
*/
|
||||
protected Props addChild(String subpath) {
|
||||
protected Props addChild(String subPath) {
|
||||
|
||||
subpath = subpath.trim();
|
||||
addProperty(subpath);
|
||||
subPath = subPath.trim();
|
||||
addProperty(subPath);
|
||||
|
||||
// build the subpath
|
||||
String p = path == null ? subpath : path + "." + subpath;
|
||||
Props nested = new Props(owner, path, p);
|
||||
owner.pathMap.put(p, nested);
|
||||
// build the subPath
|
||||
String fullPath = path == null ? subPath : path + "." + subPath;
|
||||
Props nested = new Props(owner, path, fullPath);
|
||||
owner.pathMap.put(fullPath, nested);
|
||||
return nested;
|
||||
}
|
||||
|
||||
@@ -246,6 +241,14 @@ public class PathProperties {
|
||||
protected void addProperty(String property) {
|
||||
propSet.add(property.trim());
|
||||
}
|
||||
|
||||
private void addProps(Props value) {
|
||||
propSet.addAll(value.propSet);
|
||||
}
|
||||
|
||||
private boolean includes(String prop) {
|
||||
return propSet.isEmpty() || propSet.contains(prop) || propSet.contains("*");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.avaje.ebean.text;
|
||||
/**
|
||||
* Parses Uri segments like :(id,name,shippingAddress(*),contacts(*)) so that
|
||||
* the response can be customised for performance.
|
||||
*
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
class PathPropertiesParser {
|
||||
@@ -50,12 +50,12 @@ class PathPropertiesParser {
|
||||
do {
|
||||
char c1 = chars[pos++];
|
||||
switch (c1) {
|
||||
case '(':
|
||||
return currentWord();
|
||||
default:
|
||||
if (pos == 1) {
|
||||
return "";
|
||||
}
|
||||
case '(':
|
||||
return currentWord();
|
||||
default:
|
||||
if (pos == 1) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
} while (pos < eof);
|
||||
throw new RuntimeException("Hit EOF while reading sectionTitle from " + startPos);
|
||||
@@ -72,28 +72,30 @@ class PathPropertiesParser {
|
||||
}
|
||||
|
||||
private void parseSection() {
|
||||
do {
|
||||
char c1 = chars[pos++];
|
||||
switch (c1) {
|
||||
case '(':
|
||||
addSubpath();
|
||||
break;
|
||||
case ',':
|
||||
addCurrentProperty();
|
||||
break;
|
||||
case ':':
|
||||
// start new section
|
||||
startPos = pos;
|
||||
return;
|
||||
case ')':
|
||||
// end of section
|
||||
addCurrentProperty();
|
||||
popSubpath();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
if (pos < eof) {
|
||||
do {
|
||||
char c1 = chars[pos++];
|
||||
switch (c1) {
|
||||
case '(':
|
||||
addSubpath();
|
||||
break;
|
||||
case ',':
|
||||
addCurrentProperty();
|
||||
break;
|
||||
case ':':
|
||||
// start new section
|
||||
startPos = pos;
|
||||
return;
|
||||
case ')':
|
||||
// end of section
|
||||
addCurrentProperty();
|
||||
popSubpath();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
} while (pos < eof);
|
||||
} while (pos < eof);
|
||||
}
|
||||
if (startPos < pos) {
|
||||
String currentWord = source.substring(startPos, pos);
|
||||
currentPathProps.addProperty(currentWord);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
|
||||
/**
|
||||
* Provides a JSON reader that can hold a persistence context and load context while reading JSON.
|
||||
* <p>
|
||||
* This provides a mechanism such that an object loaded from JSON can have unique instances
|
||||
* by using a persistence context and also support further lazy loading (via a load context).
|
||||
* </p>
|
||||
*/
|
||||
public interface JsonBeanReader<T> {
|
||||
|
||||
/**
|
||||
* Read the JSON returning a bean.
|
||||
*/
|
||||
T read();
|
||||
|
||||
/**
|
||||
* Create a new reader taking the context from the existing one but using a new JsonParser.
|
||||
*/
|
||||
JsonBeanReader<T> forJson(JsonParser moreJson);
|
||||
|
||||
/**
|
||||
* Add a bean explicitly to the persistence context.
|
||||
*/
|
||||
void persistenceContextPut(Object beanId, T currentBean);
|
||||
|
||||
/**
|
||||
* Return the persistence context if one is being used.
|
||||
*/
|
||||
PersistenceContext getPersistenceContext();
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import com.avaje.ebean.FetchPath;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
|
||||
@@ -56,6 +56,16 @@ public interface JsonContext {
|
||||
*/
|
||||
<T> T toBean(Class<T> cls, JsonParser parser, JsonReadOptions options) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Create and return a new bean reading for the bean type given the JSON options and source.
|
||||
* <p>
|
||||
* Note that JsonOption provides an option for setting a persistence context and also enabling
|
||||
* further lazy loading. Further lazy loading requires a persistence context so if that is set
|
||||
* on then a persistence context is created if there is not one set.
|
||||
* </p>
|
||||
*/
|
||||
<T> JsonBeanReader<T> createBeanReader(Class<T> cls, JsonParser parser, JsonReadOptions options) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Convert json string input into a list of beans of a specific type.
|
||||
*
|
||||
@@ -144,21 +154,21 @@ public interface JsonContext {
|
||||
void toJson(Object value, JsonGenerator generator) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Return the bean or collection as JSON string using PathProperties.
|
||||
* Return the bean or collection as JSON string using FetchPath.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
String toJson(Object value, PathProperties pathProperties) throws JsonIOException;
|
||||
String toJson(Object value, FetchPath fetchPath) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Write the bean or collection as json to the writer using the PathProperties.
|
||||
* Write the bean or collection as json to the writer using the FetchPath.
|
||||
*/
|
||||
void toJson(Object value, Writer writer, PathProperties pathProperties) throws JsonIOException;
|
||||
void toJson(Object value, Writer writer, FetchPath fetchPath) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Write the bean or collection to the JsonGenerator using the PathProperties.
|
||||
* Write the bean or collection to the JsonGenerator using the FetchPath.
|
||||
*/
|
||||
void toJson(Object value, JsonGenerator generator, PathProperties pathProperties) throws JsonIOException;
|
||||
void toJson(Object value, JsonGenerator generator, FetchPath fetchPath) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Deprecated in favour of using PathProperties by itself.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -17,6 +19,10 @@ public class JsonReadOptions {
|
||||
|
||||
protected Object objectMapper;
|
||||
|
||||
protected boolean enableLazyLoading;
|
||||
|
||||
protected PersistenceContext persistenceContext;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
@@ -46,6 +52,25 @@ public class JsonReadOptions {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if lazy loading is enabled after the objects are loaded.
|
||||
*/
|
||||
public boolean isEnableLazyLoading() {
|
||||
return enableLazyLoading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to enable lazy loading on partially populated beans.
|
||||
* <p>
|
||||
* If this is set to true a persistence context will be created if one has
|
||||
* not already been supplied.
|
||||
* </p>
|
||||
*/
|
||||
public JsonReadOptions setEnableLazyLoading(boolean enableLazyLoading) {
|
||||
this.enableLazyLoading = enableLazyLoading;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Jackson ObjectMapper to use (if not wanted to use the objectMapper set on the ServerConfig).
|
||||
*/
|
||||
@@ -56,7 +81,23 @@ public class JsonReadOptions {
|
||||
/**
|
||||
* Set the Jackson ObjectMapper to use (if not wanted to use the objectMapper set on the ServerConfig).
|
||||
*/
|
||||
public void setObjectMapper(Object objectMapper) {
|
||||
public JsonReadOptions setObjectMapper(Object objectMapper) {
|
||||
this.objectMapper = objectMapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the persistence context to use when building the object graph from the JSON.
|
||||
*/
|
||||
public JsonReadOptions setPersistenceContext(PersistenceContext persistenceContext) {
|
||||
this.persistenceContext = persistenceContext;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the persistence context to use when marshalling JSON.
|
||||
*/
|
||||
public PersistenceContext getPersistenceContext() {
|
||||
return persistenceContext;
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,13 @@ package com.avaje.ebean.text.json;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Writes any scalar type known to Ebean the Jackson generator.
|
||||
* Writes any scalar type known to Ebean to the underlying Jackson generator.
|
||||
*/
|
||||
public interface JsonScalar {
|
||||
|
||||
/**
|
||||
* Write the scalar type to JSON where the value can be any type known to Ebean
|
||||
* including Enums, Java8 time types, Joda types, URL, URI etc.
|
||||
*/
|
||||
void write(String name, Object value) throws IOException;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
import com.avaje.ebean.FetchPath;
|
||||
import com.avaje.ebean.config.JsonConfig;
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
|
||||
@@ -15,7 +16,7 @@ import java.util.Map;
|
||||
*/
|
||||
public class JsonWriteOptions {
|
||||
|
||||
protected PathProperties pathProperties;
|
||||
protected FetchPath pathProperties;
|
||||
|
||||
protected Object objectMapper;
|
||||
|
||||
@@ -38,7 +39,7 @@ public class JsonWriteOptions {
|
||||
/**
|
||||
* Construct JsonWriteOptions with the given pathProperties.
|
||||
*/
|
||||
public static JsonWriteOptions pathProperties(PathProperties pathProperties) {
|
||||
public static JsonWriteOptions pathProperties(FetchPath pathProperties) {
|
||||
JsonWriteOptions o = new JsonWriteOptions();
|
||||
o.setPathProperties(pathProperties);
|
||||
return o;
|
||||
@@ -47,14 +48,14 @@ public class JsonWriteOptions {
|
||||
/**
|
||||
* Set the Map of properties to include by path.
|
||||
*/
|
||||
public void setPathProperties(PathProperties pathProperties) {
|
||||
public void setPathProperties(FetchPath pathProperties) {
|
||||
this.pathProperties = pathProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the properties to include by path.
|
||||
*/
|
||||
public PathProperties getPathProperties() {
|
||||
public FetchPath getPathProperties() {
|
||||
return pathProperties;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -29,4 +29,5 @@ public interface LoadManyBuffer {
|
||||
|
||||
void configureQuery(SpiQuery<?> query);
|
||||
|
||||
boolean isUseDocStore();
|
||||
}
|
||||
@@ -144,7 +144,7 @@ public class LoadManyRequest extends LoadRequest {
|
||||
query.setLazyLoadForParents(many);
|
||||
|
||||
List<Object> idList = getParentIdList(batchSize);
|
||||
many.addWhereParentIdIn(query, idList);
|
||||
many.addWhereParentIdIn(query, idList, loadContext.isUseDocStore());
|
||||
|
||||
query.setPersistenceContext(loadContext.getPersistenceContext());
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import com.avaje.ebean.TransactionCallback;
|
||||
import com.avaje.ebean.annotation.DocStoreEvent;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.config.PersistBatch;
|
||||
import com.avaje.ebean.event.changelog.BeanChange;
|
||||
@@ -59,6 +60,26 @@ public class ScopedTransaction implements SpiTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocStoreEvent getDocStoreUpdateMode() {
|
||||
return transaction.getDocStoreUpdateMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDocStoreUpdateMode(DocStoreEvent indexUpdateMode) {
|
||||
transaction.setDocStoreUpdateMode(indexUpdateMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDocStoreBulkBatchSize() {
|
||||
return transaction.getDocStoreBulkBatchSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDocStoreUpdateBatchSize(int batchSize) {
|
||||
transaction.setDocStoreUpdateBatchSize(batchSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLogPrefix() {
|
||||
return transaction.getLogPrefix();
|
||||
|
||||
@@ -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;
|
||||
@@ -86,6 +83,11 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
*/
|
||||
BeanDescriptor<?> getBeanDescriptorById(String className);
|
||||
|
||||
/**
|
||||
* Return BeanDescriptor using it's unique doc store queueId.
|
||||
*/
|
||||
BeanDescriptor<?> getBeanDescriptorByQueueId(String queueId);
|
||||
|
||||
/**
|
||||
* Return BeanDescriptors mapped to this table.
|
||||
*/
|
||||
@@ -130,22 +132,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>
|
||||
|
||||
@@ -3,6 +3,9 @@ package com.avaje.ebeaninternal.api;
|
||||
import com.avaje.ebean.Expression;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.expression.ElasticExpressionContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
@@ -10,6 +13,11 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
*/
|
||||
public interface SpiExpression extends Expression {
|
||||
|
||||
/**
|
||||
* Write the expression as an elastic search expression.
|
||||
*/
|
||||
void writeElastic(ElasticExpressionContext context) throws IOException;
|
||||
|
||||
/**
|
||||
* Process "Many" properties populating ManyWhereJoins.
|
||||
* <p>
|
||||
@@ -17,32 +25,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 +73,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 +83,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.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import com.avaje.ebean.plugin.SpiBeanType;
|
||||
import com.avaje.ebean.plugin.BeanType;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
@@ -10,11 +10,11 @@ import java.util.Set;
|
||||
*/
|
||||
public class SpiExpressionValidation {
|
||||
|
||||
private final SpiBeanType<?> desc;
|
||||
private final BeanType<?> desc;
|
||||
|
||||
private final LinkedHashSet<String> unknown = new LinkedHashSet<String>();
|
||||
|
||||
public SpiExpressionValidation(SpiBeanType<?> desc) {
|
||||
public SpiExpressionValidation(BeanType<?> desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,16 +11,17 @@ import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebean.event.readaudit.ReadEvent;
|
||||
import com.avaje.ebean.plugin.SpiBeanType;
|
||||
import com.avaje.ebean.plugin.BeanType;
|
||||
import com.avaje.ebeaninternal.server.autotune.ProfilingListener;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
import com.avaje.ebeaninternal.server.expression.ElasticExpressionContext;
|
||||
import com.avaje.ebeaninternal.server.query.CancelableQuery;
|
||||
import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -129,6 +130,26 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the query as an elastic search query.
|
||||
*/
|
||||
void writeElastic(ElasticExpressionContext context) throws IOException;
|
||||
|
||||
/**
|
||||
* Return true if AutoTune should be attempted on this query.
|
||||
*/
|
||||
boolean isAutoTunable();
|
||||
|
||||
/**
|
||||
* Return the bean descriptor for this query.
|
||||
*/
|
||||
BeanDescriptor<T> getBeanDescriptor();
|
||||
|
||||
/**
|
||||
* Return true if this query should be executed against the doc store.
|
||||
*/
|
||||
boolean isUseDocStore();
|
||||
|
||||
/**
|
||||
* Return the PersistenceContextScope that this query should use.
|
||||
* <p>
|
||||
@@ -277,23 +298,13 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
/**
|
||||
* Return the lazy loading 'many' property.
|
||||
*/
|
||||
BeanPropertyAssocMany<?> getLazyLoadForParentsProperty();
|
||||
BeanPropertyAssocMany<?> getLazyLoadMany();
|
||||
|
||||
/**
|
||||
* Set the load mode (+lazy or +query) and the load description.
|
||||
*/
|
||||
void setLoadDescription(String loadMode, String loadDescription);
|
||||
|
||||
/**
|
||||
* Set the BeanDescriptor for the root type of this query.
|
||||
*/
|
||||
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 +330,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 +462,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.
|
||||
@@ -675,9 +656,9 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
boolean isDisableReadAudit();
|
||||
|
||||
/**
|
||||
* Return true if this is a query executing in the background.
|
||||
*/
|
||||
/**
|
||||
* Return true if this is a query executing in the background.
|
||||
*/
|
||||
boolean isFutureFetch();
|
||||
|
||||
/**
|
||||
@@ -714,6 +695,6 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
/**
|
||||
* Validate the query returning the set of properties with unknown paths.
|
||||
*/
|
||||
Set<String> validate(SpiBeanType<T> desc);
|
||||
Set<String> validate(BeanType<T> desc);
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import java.sql.Connection;
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.annotation.DocStoreEvent;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.event.changelog.BeanChange;
|
||||
import com.avaje.ebean.event.changelog.ChangeSet;
|
||||
@@ -20,7 +21,7 @@ import com.avaje.ebeaninternal.server.persist.BatchControl;
|
||||
public interface SpiTransaction extends Transaction {
|
||||
|
||||
/**
|
||||
* Return the string prefix with the transactin id and label used in logging.
|
||||
* Return the string prefix with the transaction id and label used in logging.
|
||||
*/
|
||||
String getLogPrefix();
|
||||
|
||||
@@ -102,6 +103,20 @@ public interface SpiTransaction extends Transaction {
|
||||
*/
|
||||
Boolean isUpdateAllLoadedProperties();
|
||||
|
||||
/**
|
||||
* Return the batchSize specifically set for this transaction or 0.
|
||||
* <p>
|
||||
* Returning 0 implies to use the system wide default batch size.
|
||||
* </p>
|
||||
*/
|
||||
DocStoreEvent getDocStoreUpdateMode();
|
||||
|
||||
/**
|
||||
* Return the batch size to us for ElasticSearch Bulk API calls
|
||||
* as a result of this transaction.
|
||||
*/
|
||||
int getDocStoreBulkBatchSize();
|
||||
|
||||
/**
|
||||
* Return the batchSize specifically set for this transaction or 0.
|
||||
* <p>
|
||||
@@ -157,12 +172,12 @@ public interface SpiTransaction extends Transaction {
|
||||
boolean isBatchThisRequest(PersistRequest.Type type);
|
||||
|
||||
/**
|
||||
* Return the queue used to batch up persist requests.
|
||||
* Return the BatchControl used to batch up persist requests.
|
||||
*/
|
||||
BatchControl getBatchControl();
|
||||
|
||||
/**
|
||||
* Set the queue used to batch up persist requests. There should only be one
|
||||
* Set the BatchControl used to batch up persist requests. There should only be one
|
||||
* PersistQueue set per transaction.
|
||||
*/
|
||||
void setBatchControl(BatchControl control);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreUpdates;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.transaction.DeleteByIdMap;
|
||||
@@ -120,4 +121,16 @@ public class TransactionEvent implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any relevant PersistRequestBean's to DocStoreUpdates for later processing.
|
||||
*/
|
||||
public void addDocStoreUpdates(DocStoreUpdates docStoreUpdates) {
|
||||
|
||||
List<PersistRequestBean<?>> persistRequestBeans = getPersistRequestBeans();
|
||||
if (persistRequestBeans != null) {
|
||||
for (int i=0; i< persistRequestBeans.size(); i++) {
|
||||
persistRequestBeans.get(i).addDocStoreUpdates(docStoreUpdates);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ import com.avaje.ebean.event.changelog.ChangeLogListener;
|
||||
import com.avaje.ebean.event.changelog.ChangeSet;
|
||||
import com.avaje.ebean.event.changelog.ChangeType;
|
||||
import com.avaje.ebean.plugin.SpiServer;
|
||||
import com.avaje.ebean.plugin.SpiServerPlugin;
|
||||
import com.avaje.ebean.plugin.Plugin;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -20,7 +20,7 @@ import java.util.Properties;
|
||||
* is fully contained with the transaction information.
|
||||
* </p>
|
||||
*/
|
||||
public class DefaultChangeLogListener implements ChangeLogListener, SpiServerPlugin {
|
||||
public class DefaultChangeLogListener implements ChangeLogListener, Plugin {
|
||||
|
||||
/**
|
||||
* The usual application specific logger.
|
||||
|
||||
@@ -36,16 +36,20 @@ public abstract class BeanRequest {
|
||||
* A transaction may have been passed in or active in the thread local. If
|
||||
* not then create one implicitly to handle the request.
|
||||
* </p>
|
||||
*
|
||||
* @return True if a transaction was set (from current or created).
|
||||
*/
|
||||
public void createImplicitTransIfRequired() {
|
||||
if (transaction == null) {
|
||||
transaction = ebeanServer.getCurrentServerTransaction();
|
||||
if (transaction == null || !transaction.isActive()) {
|
||||
// create an implicit transaction to execute this query
|
||||
transaction = ebeanServer.createServerTransaction(false, -1);
|
||||
createdTransaction = true;
|
||||
}
|
||||
}
|
||||
public boolean createImplicitTransIfRequired() {
|
||||
if (transaction != null) {
|
||||
return false;
|
||||
}
|
||||
transaction = ebeanServer.getCurrentServerTransaction();
|
||||
if (transaction == null || !transaction.isActive()) {
|
||||
// create an implicit transaction to execute this query
|
||||
transaction = ebeanServer.createServerTransaction(false, -1);
|
||||
createdTransaction = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,13 +15,12 @@ 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;
|
||||
import com.avaje.ebean.plugin.SpiBeanType;
|
||||
import com.avaje.ebean.plugin.BeanType;
|
||||
import com.avaje.ebean.plugin.SpiServer;
|
||||
import com.avaje.ebean.plugin.SpiServerPlugin;
|
||||
import com.avaje.ebean.plugin.Plugin;
|
||||
import com.avaje.ebean.text.csv.CsvReader;
|
||||
import com.avaje.ebean.text.json.JsonContext;
|
||||
import com.avaje.ebeaninternal.api.LoadBeanRequest;
|
||||
@@ -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;
|
||||
@@ -68,6 +66,7 @@ import com.avaje.ebeaninternal.server.transaction.TransactionManager;
|
||||
import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager;
|
||||
import com.avaje.ebeaninternal.util.ParamTypeHelper;
|
||||
import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreIntegration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -140,7 +139,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private final CQueryEngine cqueryEngine;
|
||||
|
||||
private final List<SpiServerPlugin> serverPlugins;
|
||||
private final List<Plugin> serverPlugins;
|
||||
|
||||
private final DdlGenerator ddlGenerator;
|
||||
|
||||
@@ -154,6 +153,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private final JsonContext jsonContext;
|
||||
|
||||
private final DocumentStore documentStore;
|
||||
|
||||
private final MetaInfoManager metaInfoManager;
|
||||
|
||||
/**
|
||||
@@ -221,8 +222,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
this.maxCallStack = serverConfig.getMaxCallStack();
|
||||
|
||||
this.rollbackOnChecked = serverConfig.isTransactionRollbackOnChecked();
|
||||
this.transactionManager = config.getTransactionManager();
|
||||
this.transactionScopeManager = config.getTransactionScopeManager();
|
||||
|
||||
this.persister = config.createPersister(this);
|
||||
this.queryEngine = config.createOrmQueryEngine();
|
||||
@@ -234,6 +233,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
this.beanLoader = new DefaultBeanLoader(this);
|
||||
this.jsonContext = config.createJsonContext(this);
|
||||
|
||||
DocStoreIntegration docStoreComponents = config.createDocStoreIntegration(this);
|
||||
this.transactionManager = config.createTransactionManager(docStoreComponents.updateProcessor());
|
||||
this.transactionScopeManager = config.createTransactionScopeManager(transactionManager);
|
||||
this.documentStore = docStoreComponents.documentStore();
|
||||
|
||||
this.serverPlugins = config.getPlugins();
|
||||
this.ddlGenerator = new DdlGenerator(this, serverConfig);
|
||||
|
||||
@@ -250,7 +255,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
autoTuneService.startup();
|
||||
|
||||
for (SpiServerPlugin plugin : serverPlugins) {
|
||||
for (Plugin plugin : serverPlugins) {
|
||||
plugin.configure(this);
|
||||
}
|
||||
}
|
||||
@@ -284,7 +289,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
for (SpiEbeanPlugin plugin : ebeanPlugins) {
|
||||
plugin.execute(online);
|
||||
}
|
||||
for (SpiServerPlugin plugin : serverPlugins) {
|
||||
for (Plugin plugin : serverPlugins) {
|
||||
plugin.online(online);
|
||||
}
|
||||
}
|
||||
@@ -410,7 +415,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private void shutdownPlugins() {
|
||||
|
||||
for (SpiServerPlugin plugin : serverPlugins) {
|
||||
for (Plugin plugin : serverPlugins) {
|
||||
try {
|
||||
plugin.shutdown();
|
||||
} catch (Throwable e) {
|
||||
@@ -463,19 +468,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);
|
||||
@@ -913,7 +909,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
public <T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery) throws PersistenceException {
|
||||
|
||||
BeanDescriptor<?> desc = getBeanDescriptor(beanType);
|
||||
BeanDescriptor<T> desc = getBeanDescriptor(beanType);
|
||||
if (desc == null) {
|
||||
throw new PersistenceException("Is " + beanType.getName() + " an Entity Bean? BeanDescriptor not found?");
|
||||
}
|
||||
@@ -923,7 +919,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
// this will parse the query
|
||||
return new DefaultOrmQuery<T>(beanType, this, expressionFactory, deployQuery);
|
||||
return new DefaultOrmQuery<T>(desc, this, expressionFactory, deployQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -958,10 +954,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
public <T> Query<T> createQuery(Class<T> beanType, String query) {
|
||||
BeanDescriptor<?> desc = getBeanDescriptor(beanType);
|
||||
BeanDescriptor<T> desc = getBeanDescriptor(beanType);
|
||||
if (desc == null) {
|
||||
String m = beanType.getName() + " is NOT an Entity Bean registered with this server?";
|
||||
throw new PersistenceException(m);
|
||||
throw new PersistenceException(beanType.getName() + " is NOT an Entity Bean registered with this server?");
|
||||
}
|
||||
switch (desc.getEntityType()) {
|
||||
case SQL:
|
||||
@@ -970,10 +965,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
// use the "default" SqlSelect
|
||||
DeployNamedQuery defaultSqlSelect = desc.getNamedQuery("default");
|
||||
return new DefaultOrmQuery<T>(beanType, this, expressionFactory, defaultSqlSelect);
|
||||
return new DefaultOrmQuery<T>(desc, this, expressionFactory, defaultSqlSelect);
|
||||
|
||||
default:
|
||||
return new DefaultOrmQuery<T>(beanType, this, expressionFactory, query);
|
||||
return new DefaultOrmQuery<T>(desc, this, expressionFactory, query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1048,69 +1043,31 @@ 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);
|
||||
|
||||
BeanDescriptor<T> desc = beanDescriptorManager.getBeanDescriptor(spiQuery.getBeanType());
|
||||
spiQuery.setBeanDescriptor(desc);
|
||||
|
||||
return createQueryRequest(desc, spiQuery, t);
|
||||
return createQueryRequest(spiQuery, t);
|
||||
}
|
||||
|
||||
public <T> SpiOrmQueryRequest<T> createQueryRequest(BeanDescriptor<T> desc, SpiQuery<T> query, Transaction t) {
|
||||
private <T> SpiOrmQueryRequest<T> createQueryRequest(SpiQuery<T> query, Transaction t) {
|
||||
|
||||
if (desc.isAutoTunable() && !query.isSqlSelect() && !autoTuneService.tuneQuery(query)) {
|
||||
if (query.isAutoTunable() && !autoTuneService.tuneQuery(query)) {
|
||||
// use deployment FetchType.LAZY/EAGER annotations
|
||||
// to define the 'default' select clause
|
||||
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, (SpiTransaction) t);
|
||||
request.prepareQuery();
|
||||
|
||||
return request;
|
||||
}
|
||||
@@ -1119,7 +1076,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
* Try to get the object out of the persistence context.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T findIdCheckPersistenceContextAndCache(Transaction transaction, BeanDescriptor<T> beanDescriptor, SpiQuery<T> query) {
|
||||
private <T> T findIdCheckPersistenceContextAndCache(Transaction transaction, SpiQuery<T> query) {
|
||||
|
||||
SpiTransaction t = (SpiTransaction) transaction;
|
||||
if (t == null) {
|
||||
@@ -1130,7 +1087,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
// first look in the transaction scoped persistence context
|
||||
context = t.getPersistenceContext();
|
||||
if (context != null) {
|
||||
WithOption o = context.getWithOption(beanDescriptor.getBeanType(), query.getId());
|
||||
WithOption o = context.getWithOption(query.getBeanType(), query.getId());
|
||||
if (o != null) {
|
||||
if (o.isDeleted()) {
|
||||
// Bean was previously deleted in the same transaction / persistence context
|
||||
@@ -1142,13 +1099,14 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
if (!beanDescriptor.calculateUseCache(query.isUseBeanCache())) {
|
||||
BeanDescriptor<T> desc = query.getBeanDescriptor();
|
||||
if (!desc.calculateUseCache(query.isUseBeanCache())) {
|
||||
// not using bean cache
|
||||
return null;
|
||||
}
|
||||
|
||||
// Hit the L2 bean cache
|
||||
return beanDescriptor.cacheBeanGet(query, context);
|
||||
return desc.cacheBeanGet(query, context);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1172,19 +1130,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
spiQuery.setType(Type.BEAN);
|
||||
|
||||
BeanDescriptor<T> desc = beanDescriptorManager.getBeanDescriptor(spiQuery.getBeanType());
|
||||
spiQuery.setBeanDescriptor(desc);
|
||||
|
||||
if (SpiQuery.Mode.NORMAL.equals(spiQuery.getMode()) && !spiQuery.isLoadBeanCache()) {
|
||||
// See if we can skip doing the fetch completely by getting the bean from the
|
||||
// persistence context or the bean cache
|
||||
T bean = findIdCheckPersistenceContextAndCache(t, desc, spiQuery);
|
||||
T bean = findIdCheckPersistenceContextAndCache(t, spiQuery);
|
||||
if (bean != null) {
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(desc, spiQuery, t);
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(spiQuery, t);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return (T) request.findId();
|
||||
@@ -1386,6 +1341,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
throw new PersistenceException("maxRows must be specified for findPagedList() query");
|
||||
}
|
||||
|
||||
if (spiQuery.isUseDocStore()) {
|
||||
return docStore().findPagedList(query);
|
||||
}
|
||||
|
||||
return new LimitOffsetPagedList<T>(this, spiQuery);
|
||||
}
|
||||
|
||||
@@ -1443,6 +1402,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
if (result != null) {
|
||||
return (List<T>) result;
|
||||
}
|
||||
if (request.isUseDocStore()) {
|
||||
return docStore().findList(query);
|
||||
}
|
||||
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
@@ -2046,22 +2008,32 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
/**
|
||||
* Return all the SPI BeanTypes.
|
||||
*/
|
||||
public List<? extends SpiBeanType<?>> getBeanTypes() {
|
||||
public List<? extends BeanType<?>> getBeanTypes() {
|
||||
return getBeanDescriptors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SPI bean types mapped to the given table.
|
||||
*/
|
||||
public List<? extends SpiBeanType<?>> getBeanTypes(String tableName) {
|
||||
public List<? extends BeanType<?>> getBeanTypes(String tableName) {
|
||||
return beanDescriptorManager.getBeanTypes(tableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanType<?> getBeanTypeForQueueId(String queueId) {
|
||||
return getBeanDescriptorByQueueId(queueId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanDescriptor<?> getBeanDescriptorByQueueId(String queueId) {
|
||||
return beanDescriptorManager.getBeanDescriptorByQueueId(queueId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SPI bean types for the given bean class.
|
||||
*/
|
||||
@Override
|
||||
public <T> SpiBeanType<T> getBeanType(Class<T> beanType) {
|
||||
public <T> BeanType<T> getBeanType(Class<T> beanType) {
|
||||
return getBeanDescriptor(beanType);
|
||||
}
|
||||
|
||||
@@ -2159,6 +2131,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return callStackFactory.createCallStack(finalTrace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentStore docStore() {
|
||||
return documentStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonContext json() {
|
||||
// immutable thread safe so return shared instance
|
||||
|
||||
@@ -11,7 +11,8 @@ import com.avaje.ebean.event.changelog.ChangeLogPrepare;
|
||||
import com.avaje.ebean.event.changelog.ChangeLogRegister;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditLogger;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import com.avaje.ebean.plugin.SpiServerPlugin;
|
||||
import com.avaje.ebean.plugin.Plugin;
|
||||
import com.avaje.ebean.plugin.SpiServer;
|
||||
import com.avaje.ebean.text.json.JsonContext;
|
||||
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
@@ -46,6 +47,10 @@ import com.avaje.ebeaninternal.server.transaction.TransactionManager;
|
||||
import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager;
|
||||
import com.avaje.ebeaninternal.server.type.DefaultTypeManager;
|
||||
import com.avaje.ebeaninternal.server.type.TypeManager;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreFactory;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreIntegration;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateProcessor;
|
||||
import com.avaje.ebeanservice.docstore.none.NoneDocStoreFactory;
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -82,10 +87,6 @@ public class InternalConfiguration {
|
||||
|
||||
private final BeanDescriptorManager beanDescriptorManager;
|
||||
|
||||
private final TransactionManager transactionManager;
|
||||
|
||||
private final TransactionScopeManager transactionScopeManager;
|
||||
|
||||
private final CQueryEngine cQueryEngine;
|
||||
|
||||
private final ClusterManager clusterManager;
|
||||
@@ -100,15 +101,18 @@ public class InternalConfiguration {
|
||||
|
||||
private final JsonFactory jsonFactory;
|
||||
|
||||
private final DocStoreFactory docStoreFactory;
|
||||
|
||||
/**
|
||||
* List of plugins (that ultimately the DefaultServer configures late in construction).
|
||||
*/
|
||||
private final List<SpiServerPlugin> plugins = new ArrayList<SpiServerPlugin>();
|
||||
private final List<Plugin> plugins = new ArrayList<Plugin>();
|
||||
|
||||
public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager,
|
||||
ServerCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor,
|
||||
ServerConfig serverConfig, BootupClasses bootupClasses) {
|
||||
|
||||
this.docStoreFactory = initDocStoreFactory(serverConfig.service(DocStoreFactory.class));
|
||||
this.jsonFactory = serverConfig.getJsonFactory();
|
||||
this.xmlConfig = xmlConfig;
|
||||
this.clusterManager = clusterManager;
|
||||
@@ -130,25 +134,21 @@ public class InternalConfiguration {
|
||||
Map<String, String> asOfTableMapping = beanDescriptorManager.deploy();
|
||||
Map<String, String> draftTableMap = beanDescriptorManager.getDraftTableMap();
|
||||
|
||||
this.transactionManager = createTransactionManager();
|
||||
|
||||
DatabasePlatform databasePlatform = serverConfig.getDatabasePlatform();
|
||||
|
||||
this.binder = getBinder(typeManager, databasePlatform);
|
||||
this.cQueryEngine = new CQueryEngine(databasePlatform, binder, asOfTableMapping, serverConfig.getAsOfSysPeriod(), draftTableMap);
|
||||
}
|
||||
|
||||
ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager();
|
||||
if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) {
|
||||
externalTransactionManager = new JtaTransactionManager();
|
||||
}
|
||||
if (externalTransactionManager != null) {
|
||||
externalTransactionManager.setTransactionManager(transactionManager);
|
||||
this.transactionScopeManager = new ExternalTransactionScopeManager(transactionManager, externalTransactionManager);
|
||||
logger.info("Using Transaction Manager [" + externalTransactionManager.getClass() + "]");
|
||||
} else {
|
||||
this.transactionScopeManager = new DefaultTransactionScopeManager(transactionManager);
|
||||
}
|
||||
private DocStoreFactory initDocStoreFactory(DocStoreFactory service) {
|
||||
return service == null ? new NoneDocStoreFactory() : service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the doc store factory.
|
||||
*/
|
||||
public DocStoreFactory getDocStoreFactory() {
|
||||
return docStoreFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,8 +156,8 @@ public class InternalConfiguration {
|
||||
* later on the DefaultServer for late call to configure().
|
||||
*/
|
||||
public <T> T plugin(T maybePlugin) {
|
||||
if (maybePlugin instanceof SpiServerPlugin) {
|
||||
plugins.add((SpiServerPlugin) maybePlugin);
|
||||
if (maybePlugin instanceof Plugin) {
|
||||
plugins.add((Plugin) maybePlugin);
|
||||
}
|
||||
return maybePlugin;
|
||||
}
|
||||
@@ -165,10 +165,10 @@ public class InternalConfiguration {
|
||||
/**
|
||||
* Return the list of plugins we collected during construction.
|
||||
*/
|
||||
public List<SpiServerPlugin> getPlugins() {
|
||||
public List<Plugin> getPlugins() {
|
||||
|
||||
// find additional plugins via ServiceLoader ...
|
||||
for (SpiServerPlugin plugin : ServiceLoader.load(SpiServerPlugin.class)) {
|
||||
for (Plugin plugin : ServiceLoader.load(Plugin.class)) {
|
||||
if (!plugins.contains(plugin)) {
|
||||
plugins.add(plugin);
|
||||
}
|
||||
@@ -244,34 +244,6 @@ public class InternalConfiguration {
|
||||
return new NotSupportedJsonExpression();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the TransactionManager taking into account autoCommit mode.
|
||||
*/
|
||||
private TransactionManager createTransactionManager() {
|
||||
|
||||
if (serverConfig.isExplicitTransactionBeginMode()) {
|
||||
return new ExplicitTransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
|
||||
}
|
||||
|
||||
if (isAutoCommitMode()) {
|
||||
return new AutoCommitTransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
|
||||
}
|
||||
|
||||
return new TransactionManager(clusterManager, backgroundExecutor, serverConfig, beanDescriptorManager, this.getBootupClasses());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if autoCommit mode is on.
|
||||
*/
|
||||
private boolean isAutoCommitMode() {
|
||||
if (serverConfig.isAutoCommitMode()) {
|
||||
// explicitly set
|
||||
return true;
|
||||
}
|
||||
DataSource dataSource = serverConfig.getDataSource();
|
||||
return dataSource instanceof DataSourcePool && ((DataSourcePool) dataSource).getAutoCommit();
|
||||
}
|
||||
|
||||
public JsonContext createJsonContext(SpiEbeanServer server) {
|
||||
|
||||
return new DJsonContext(server, jsonFactory, typeManager);
|
||||
@@ -317,10 +289,6 @@ public class InternalConfiguration {
|
||||
return expressionFactory;
|
||||
}
|
||||
|
||||
public TypeManager getTypeManager() {
|
||||
return typeManager;
|
||||
}
|
||||
|
||||
public Binder getBinder() {
|
||||
return binder;
|
||||
}
|
||||
@@ -345,14 +313,6 @@ public class InternalConfiguration {
|
||||
return deployUtil;
|
||||
}
|
||||
|
||||
public TransactionManager getTransactionManager() {
|
||||
return transactionManager;
|
||||
}
|
||||
|
||||
public TransactionScopeManager getTransactionScopeManager() {
|
||||
return transactionScopeManager;
|
||||
}
|
||||
|
||||
public CQueryEngine getCQueryEngine() {
|
||||
return cQueryEngine;
|
||||
}
|
||||
@@ -368,4 +328,57 @@ public class InternalConfiguration {
|
||||
public GeneratedPropertyFactory getGeneratedPropertyFactory() {
|
||||
return new GeneratedPropertyFactory(serverConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the DocStoreIntegration components for the given server.
|
||||
*/
|
||||
public DocStoreIntegration createDocStoreIntegration(SpiServer server) {
|
||||
return plugin(docStoreFactory.create(server));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the TransactionManager taking into account autoCommit mode.
|
||||
*/
|
||||
public TransactionManager createTransactionManager(DocStoreUpdateProcessor indexUpdateProcessor) {
|
||||
|
||||
if (serverConfig.isExplicitTransactionBeginMode()) {
|
||||
return new ExplicitTransactionManager(serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses());
|
||||
}
|
||||
|
||||
if (isAutoCommitMode()) {
|
||||
return new AutoCommitTransactionManager(serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses());
|
||||
}
|
||||
|
||||
return new TransactionManager(serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if autoCommit mode is on.
|
||||
*/
|
||||
private boolean isAutoCommitMode() {
|
||||
if (serverConfig.isAutoCommitMode()) {
|
||||
// explicitly set
|
||||
return true;
|
||||
}
|
||||
DataSource dataSource = serverConfig.getDataSource();
|
||||
return dataSource instanceof DataSourcePool && ((DataSourcePool) dataSource).getAutoCommit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the TransactionScopeManager taking into account JTA or external transaction manager.
|
||||
*/
|
||||
public TransactionScopeManager createTransactionScopeManager(TransactionManager transactionManager) {
|
||||
|
||||
ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager();
|
||||
if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) {
|
||||
externalTransactionManager = new JtaTransactionManager();
|
||||
}
|
||||
if (externalTransactionManager != null) {
|
||||
externalTransactionManager.setTransactionManager(transactionManager);
|
||||
logger.info("Using Transaction Manager [" + externalTransactionManager.getClass() + "]");
|
||||
return new ExternalTransactionScopeManager(transactionManager, externalTransactionManager);
|
||||
} else {
|
||||
return new DefaultTransactionScopeManager(transactionManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,16 +61,16 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
|
||||
private HashQuery cacheKey;
|
||||
|
||||
private HashQueryPlan queryPlanHash;
|
||||
private CQueryPlanKey queryPlanKey;
|
||||
|
||||
private SpiQuerySecondary secondaryQueries;
|
||||
|
||||
/**
|
||||
* Create the InternalQueryRequest.
|
||||
*/
|
||||
public OrmQueryRequest(SpiEbeanServer server, OrmQueryEngine queryEngine, SpiQuery<T> query, BeanDescriptor<T> desc, SpiTransaction t) {
|
||||
|
||||
public OrmQueryRequest(SpiEbeanServer server, OrmQueryEngine queryEngine, SpiQuery<T> query, SpiTransaction t) {
|
||||
super(server, t);
|
||||
|
||||
this.beanDescriptor = desc;
|
||||
this.beanDescriptor = query.getBeanDescriptor();
|
||||
this.rawSql = query.getRawSql();
|
||||
this.finder = beanDescriptor.getBeanFinder();
|
||||
this.queryEngine = queryEngine;
|
||||
@@ -125,11 +131,30 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
return loadContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUseDocStore() {
|
||||
return query.isUseDocStore();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 +215,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 +239,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 +329,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 +348,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 +375,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 +386,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 +421,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;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import com.avaje.ebean.ValuePair;
|
||||
import com.avaje.ebean.annotation.ConcurrencyMode;
|
||||
import com.avaje.ebean.annotation.DocStoreEvent;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebean.event.BeanPersistController;
|
||||
@@ -19,9 +20,13 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.persist.BatchControl;
|
||||
import com.avaje.ebeaninternal.server.persist.PersistExecute;
|
||||
import com.avaje.ebeaninternal.server.transaction.BeanPersistIdMap;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateContext;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreUpdates;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreUpdate;
|
||||
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -30,7 +35,7 @@ import java.util.Set;
|
||||
/**
|
||||
* PersistRequest for insert update or delete of a bean.
|
||||
*/
|
||||
public final class PersistRequestBean<T> extends PersistRequest implements BeanPersistRequest<T> {
|
||||
public final class PersistRequestBean<T> extends PersistRequest implements BeanPersistRequest<T>, DocStoreUpdate {
|
||||
|
||||
private final BeanManager<T> beanManager;
|
||||
|
||||
@@ -64,6 +69,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
|
||||
private final boolean publish;
|
||||
|
||||
private DocStoreEvent docStoreEvent;
|
||||
|
||||
private ConcurrencyMode concurrencyMode;
|
||||
|
||||
/**
|
||||
@@ -102,6 +109,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
*/
|
||||
private Set<String> updatedProperties;
|
||||
|
||||
/**
|
||||
* Flags indicating the dirty properties on the bean.
|
||||
*/
|
||||
private boolean[] dirtyProperties;
|
||||
|
||||
/**
|
||||
* Flag set when request is added to JDBC batch.
|
||||
*/
|
||||
@@ -135,7 +147,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
this.parentBean = parentBean;
|
||||
this.controller = beanDescriptor.getPersistController();
|
||||
this.type = type;
|
||||
|
||||
this.docStoreEvent = calcDocStoreEvent(transaction, type);
|
||||
if (saveRecurse) {
|
||||
this.persistCascade = t.isPersistCascade();
|
||||
}
|
||||
@@ -157,6 +169,17 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
this.dirty = intercept.isDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the document store event that should be used for this request.
|
||||
*
|
||||
* Used to check if the Transaction has set the mode to IGNORE when doing large batch inserts that we
|
||||
* don't want to send to the doc store.
|
||||
*/
|
||||
private DocStoreEvent calcDocStoreEvent(SpiTransaction txn, Type type) {
|
||||
DocStoreEvent docStoreEvent = (txn == null) ? null : txn.getDocStoreUpdateMode();
|
||||
return beanDescriptor.getDocStoreEvent(type, docStoreEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the draftDirty property should be set to true for this request.
|
||||
*/
|
||||
@@ -178,7 +201,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
* Init the transaction and also check for batch on cascade escalation.
|
||||
*/
|
||||
public void initTransIfRequiredWithBatchCascade() {
|
||||
createImplicitTransIfRequired();
|
||||
|
||||
if (createImplicitTransIfRequired()) {
|
||||
docStoreEvent = calcDocStoreEvent(transaction, type);
|
||||
}
|
||||
if (transaction.checkBatchEscalationOnCascade(this)) {
|
||||
// we escalated to use batch mode so flush when done
|
||||
// but if createdTransaction then commit will flush it
|
||||
@@ -239,6 +265,13 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
return intercept.getDirtyPropertyNames();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the dirty properties on this request.
|
||||
*/
|
||||
public boolean[] getDirtyProperties() {
|
||||
return dirtyProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if any of the given property names are dirty.
|
||||
*/
|
||||
@@ -247,6 +280,19 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
return intercept.hasDirtyProperty(propertyNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if any of the given properties are dirty.
|
||||
*/
|
||||
public boolean hasDirtyProperty(int[] propertyPositions) {
|
||||
|
||||
for (int i = 0; i < propertyPositions.length; i++) {
|
||||
if (dirtyProperties[propertyPositions[i]]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ValuePair> getUpdatedValues() {
|
||||
return intercept.getDirtyValues();
|
||||
@@ -254,7 +300,16 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
|
||||
public boolean isNotify() {
|
||||
this.notifyCache = beanDescriptor.isCacheNotify(publish);
|
||||
return notifyCache || isNotifyPersistListener();
|
||||
return notifyCache || isNotifyPersistListener() || isDocStoreNotify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this request should updateAdd an ElasticSearch index
|
||||
* by queuing an event or direct updateAdd (via Bulk API).
|
||||
*/
|
||||
private boolean isDocStoreNotify() {
|
||||
// Either queue or directly update the document store
|
||||
return docStoreEvent != DocStoreEvent.IGNORE;
|
||||
}
|
||||
|
||||
public boolean isNotifyPersistListener() {
|
||||
@@ -283,6 +338,45 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the persist request updating the document store.
|
||||
*/
|
||||
public void docStoreUpdate(DocStoreUpdateContext txn) throws IOException {
|
||||
|
||||
switch (type) {
|
||||
case INSERT:
|
||||
beanDescriptor.docStoreInsert(idValue, this, txn);
|
||||
break;
|
||||
case UPDATE:
|
||||
beanDescriptor.docStoreUpdate(idValue, this, txn);
|
||||
break;
|
||||
case DELETE:
|
||||
beanDescriptor.docStoreDeleteById(idValue, txn);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid type " + type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this event to the queue entries in IndexUpdates.
|
||||
*/
|
||||
public void addToQueue(DocStoreUpdates docStoreUpdates) {
|
||||
switch (type) {
|
||||
case INSERT:
|
||||
docStoreUpdates.queueIndex(beanDescriptor.getDocStoreQueueId(), idValue);
|
||||
break;
|
||||
case UPDATE:
|
||||
docStoreUpdates.queueIndex(beanDescriptor.getDocStoreQueueId(), idValue);
|
||||
break;
|
||||
case DELETE:
|
||||
docStoreUpdates.queueDelete(beanDescriptor.getDocStoreQueueId(), idValue);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid type " + type);
|
||||
}
|
||||
}
|
||||
|
||||
public void addToPersistMap(BeanPersistIdMap beanPersistMap) {
|
||||
|
||||
beanPersistMap.add(beanDescriptor, type, idValue);
|
||||
@@ -637,6 +731,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
controllerPost();
|
||||
}
|
||||
|
||||
if (type == Type.UPDATE && docStoreEvent == DocStoreEvent.UPDATE) {
|
||||
// get the dirty properties for update notification to the doc store
|
||||
dirtyProperties = intercept.getDirtyProperties();
|
||||
}
|
||||
// if bean persisted again then should result in an update
|
||||
intercept.setLoaded();
|
||||
if (isInsert()) {
|
||||
@@ -806,6 +904,29 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
return updatedManysOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* For requests that update document store add this event to either the list
|
||||
* of queue events or list of update events.
|
||||
*/
|
||||
public void addDocStoreUpdates(DocStoreUpdates docStoreUpdates) {
|
||||
|
||||
if (type == Type.UPDATE) {
|
||||
beanDescriptor.docStoreUpdateEmbedded(this, docStoreUpdates);
|
||||
}
|
||||
switch (docStoreEvent) {
|
||||
case UPDATE: {
|
||||
docStoreUpdates.addPersist(this);
|
||||
return;
|
||||
}
|
||||
case QUEUE: {
|
||||
if (type == Type.DELETE) {
|
||||
docStoreUpdates.queueDelete(beanDescriptor.getDocStoreQueueId(), idValue);
|
||||
} else {
|
||||
docStoreUpdates.queueIndex(beanDescriptor.getDocStoreQueueId(), idValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if all loaded properties should be used for an update.
|
||||
@@ -874,4 +995,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
return Type.SOFT_DELETE == type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the Version property on the bean.
|
||||
*/
|
||||
public void setVersionValue(Object versionValue) {
|
||||
beanDescriptor.getVersionProperty().setValueIntercept(entityBean, versionValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -116,4 +116,9 @@ public interface SpiOrmQueryRequest<T> {
|
||||
* Mark the underlying transaction as not being query only.
|
||||
*/
|
||||
void markNotQueryOnly();
|
||||
|
||||
/**
|
||||
* Return true if this query is expected to use the doc store.
|
||||
*/
|
||||
boolean isUseDocStore();
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.avaje.ebean.SqlUpdate;
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.ValuePair;
|
||||
import com.avaje.ebean.annotation.ConcurrencyMode;
|
||||
import com.avaje.ebean.annotation.DocStoreEvent;
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
@@ -28,8 +29,13 @@ import com.avaje.ebean.event.readaudit.ReadAuditPrepare;
|
||||
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.ebean.plugin.BeanDocType;
|
||||
import com.avaje.ebean.plugin.BeanType;
|
||||
import com.avaje.ebean.plugin.ExpressionPath;
|
||||
import com.avaje.ebean.plugin.Property;
|
||||
import com.avaje.ebean.text.json.JsonReadOptions;
|
||||
import com.avaje.ebeaninternal.api.CQueryPlanKey;
|
||||
import com.avaje.ebeaninternal.api.LoadContext;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
@@ -60,8 +66,13 @@ import com.avaje.ebeaninternal.server.text.json.ReadJson;
|
||||
import com.avaje.ebeaninternal.server.text.json.WriteJson;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.util.SortByClause;
|
||||
import com.avaje.ebeaninternal.util.SortByClause.Property;
|
||||
import com.avaje.ebeaninternal.util.SortByClauseParser;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreBeanAdapter;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateContext;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreUpdates;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocMappingBuilder;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocumentMapping;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -73,21 +84,21 @@ 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;
|
||||
|
||||
/**
|
||||
* Describes Beans including their deployment information.
|
||||
*/
|
||||
public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanDescriptor.class);
|
||||
|
||||
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>();
|
||||
|
||||
@@ -240,7 +251,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
private final BeanProperty versionProperty;
|
||||
|
||||
private final int versionPropertyIndex;
|
||||
|
||||
|
||||
private final BeanProperty whenModifiedProperty;
|
||||
|
||||
private final BeanProperty whenCreatedProperty;
|
||||
|
||||
/**
|
||||
* Properties that are initialised in the constructor need to be 'unloaded' to support partial object queries.
|
||||
*/
|
||||
@@ -342,12 +357,17 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
|
||||
private final boolean cacheSharableBeans;
|
||||
|
||||
private final String docStoreQueueId;
|
||||
|
||||
private DocumentMapping docMapping;
|
||||
|
||||
private final BeanDescriptorDraftHelp<T> draftHelp;
|
||||
private final BeanDescriptorCacheHelp<T> cacheHelp;
|
||||
private final BeanDescriptorJsonHelp<T> jsonHelp;
|
||||
|
||||
private final DocStoreBeanAdapter<T> docStoreAdapter;
|
||||
|
||||
private final String defaultSelectClause;
|
||||
private final Set<String> defaultSelectClauseSet;
|
||||
private final LinkedHashSet<String> defaultSelectClauseSet;
|
||||
|
||||
private SpiEbeanServer ebeanServer;
|
||||
|
||||
@@ -440,12 +460,15 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
this.derivedTableJoins = listHelper.getTableJoin();
|
||||
|
||||
boolean noRelationships = propertiesOne.length + propertiesMany.length == 0;
|
||||
|
||||
|
||||
this.cacheSharableBeans = noRelationships && deploy.getCacheOptions().isReadOnly();
|
||||
this.cacheHelp = new BeanDescriptorCacheHelp<T>(this, owner.getCacheManager(), deploy.getCacheOptions(), cacheSharableBeans, propertiesOneImported);
|
||||
this.jsonHelp = new BeanDescriptorJsonHelp<T>(this);
|
||||
this.draftHelp = new BeanDescriptorDraftHelp<T>(this);
|
||||
|
||||
|
||||
this.docStoreAdapter = owner.createDocStoreBeanAdapter(this, deploy);
|
||||
this.docStoreQueueId = docStoreAdapter.getQueueId();
|
||||
|
||||
// Check if there are no cascade save associated beans ( subject to change
|
||||
// in initialiseOther()). Note that if we are in an inheritance hierarchy
|
||||
// then we also need to check every BeanDescriptors in the InheritInfo as
|
||||
@@ -459,6 +482,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
|
||||
// object used to handle Id values
|
||||
this.idBinder = owner.createIdBinder(idProperty);
|
||||
this.whenModifiedProperty = findWhenModifiedProperty();
|
||||
this.whenCreatedProperty = findWhenCreatedProperty();
|
||||
|
||||
// derive the index position of the Id and Version properties
|
||||
if (Modifier.isAbstract(beanType.getModifiers())) {
|
||||
@@ -659,6 +684,17 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
namedUpdate.initialise(parser);
|
||||
}
|
||||
}
|
||||
docStoreAdapter.registerPaths();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the document mapping.
|
||||
*/
|
||||
public void initialiseDocMapping() {
|
||||
for (int i = 0; i < propertiesMany.length; i++) {
|
||||
propertiesMany[i].initialisePostTarget();
|
||||
}
|
||||
docMapping = docStoreAdapter.createDocMapping();
|
||||
}
|
||||
|
||||
public void initInheritInfo() {
|
||||
@@ -856,7 +892,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;
|
||||
}
|
||||
|
||||
@@ -868,6 +904,84 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
return inheritInfo == null || inheritInfo.isRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this type maps to a root type of a doc store document (not embedded or ignored).
|
||||
*/
|
||||
@Override
|
||||
public boolean isDocStoreMapped() {
|
||||
return docStoreAdapter.isMapped();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the queueId used to uniquely identify this type when queuing an index updateAdd.
|
||||
*/
|
||||
@Override
|
||||
public String getDocStoreQueueId() {
|
||||
return docStoreQueueId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentMapping getDocMapping() {
|
||||
return docMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the doc store helper for this bean type.
|
||||
*/
|
||||
@Override
|
||||
public BeanDocType docStore() {
|
||||
return docStoreAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return doc store adapter for internal use for processing persist requests.
|
||||
*/
|
||||
public DocStoreBeanAdapter<T> docStoreAdapter() {
|
||||
return docStoreAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the Document mapping recursively with the given prefix relative to the root of the document.
|
||||
*/
|
||||
public void docStoreMapping(DocMappingBuilder mapping, String prefix) {
|
||||
|
||||
if (prefix != null && idProperty != null) {
|
||||
// id property not included in the
|
||||
idProperty.docStoreMapping(mapping, prefix);
|
||||
}
|
||||
|
||||
for (BeanProperty prop: propertiesNonTransient) {
|
||||
prop.docStoreMapping(mapping, prefix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of DocStoreEvent that should occur for this type of persist request
|
||||
* given the transactions requested mode.
|
||||
*/
|
||||
public DocStoreEvent getDocStoreEvent(PersistRequest.Type persistType, DocStoreEvent txnMode) {
|
||||
return docStoreAdapter.getEvent(persistType, txnMode);
|
||||
}
|
||||
|
||||
public void docStoreInsert(Object idValue, PersistRequestBean<T> persistRequest, DocStoreUpdateContext bulkUpdate) throws IOException {
|
||||
docStoreAdapter.insert(idValue, persistRequest, bulkUpdate);
|
||||
}
|
||||
|
||||
public void docStoreUpdate(Object idValue, PersistRequestBean<T> persistRequest, DocStoreUpdateContext bulkUpdate) throws IOException {
|
||||
docStoreAdapter.update(idValue, persistRequest, bulkUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this update invalidates an embedded part of a doc store document.
|
||||
*/
|
||||
public void docStoreUpdateEmbedded(PersistRequestBean<T> request, DocStoreUpdates docStoreUpdates) {
|
||||
docStoreAdapter.updateEmbedded(request, docStoreUpdates);
|
||||
}
|
||||
|
||||
public void docStoreDeleteById(Object idValue, DocStoreUpdateContext txn) throws IOException {
|
||||
docStoreAdapter.deleteById(idValue, txn);
|
||||
}
|
||||
|
||||
public T publish(T draftBean, T liveBean) {
|
||||
return draftHelp.publish(draftBean, liveBean);
|
||||
}
|
||||
@@ -1163,11 +1277,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);
|
||||
}
|
||||
|
||||
@@ -1219,10 +1333,24 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
return deleteRecurseSkippable && !isBeanCaching();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the 'when modified' property if there is one defined.
|
||||
*/
|
||||
public BeanProperty getWhenModifiedProperty() {
|
||||
return whenModifiedProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the 'when created' property if there is one defined.
|
||||
*/
|
||||
public BeanProperty getWhenCreatedProperty() {
|
||||
return whenCreatedProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a property annotated with @WhenCreated or @CreatedTimestamp.
|
||||
*/
|
||||
public BeanProperty findWhenCreatedProperty() {
|
||||
private BeanProperty findWhenCreatedProperty() {
|
||||
|
||||
for (int i = 0; i < propertiesBaseScalar.length; i++) {
|
||||
if (propertiesBaseScalar[i].isGeneratedWhenCreated()) {
|
||||
@@ -1235,7 +1363,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
/**
|
||||
* Find a property annotated with @WhenModified or @UpdatedTimestamp.
|
||||
*/
|
||||
public BeanProperty findWhenModifiedProperty() {
|
||||
private BeanProperty findWhenModifiedProperty() {
|
||||
|
||||
for (int i = 0; i < propertiesBaseScalar.length; i++) {
|
||||
if (propertiesBaseScalar[i].isGeneratedWhenModified()) {
|
||||
@@ -1252,7 +1380,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];
|
||||
}
|
||||
}
|
||||
@@ -1328,6 +1456,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
return namedUpdates.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T createBean() {
|
||||
return (T)createEntityBean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new EntityBean.
|
||||
*/
|
||||
@@ -1401,6 +1534,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
return targetDesc.getBeanPropertyFromPath(split[1]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanType<?> getBeanTypeAtPath(String path) {
|
||||
return getBeanDescriptor(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the BeanDescriptor for a given path of Associated One or Many beans.
|
||||
*/
|
||||
@@ -1473,6 +1611,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
/**
|
||||
* Return the class type this BeanDescriptor describes.
|
||||
*/
|
||||
@Override
|
||||
public Class<T> getBeanType() {
|
||||
return beanType;
|
||||
}
|
||||
@@ -1484,6 +1623,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
* instead.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
@@ -1512,6 +1652,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
return (idProperty == null) ? null : idProperty.getValue(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object beanId(Object bean) {
|
||||
return getId((EntityBean) bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBeanId(T bean) {
|
||||
return getId((EntityBean) bean);
|
||||
@@ -1552,6 +1697,14 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
return idBinder.convertId(idValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bean id value converting if necessary.
|
||||
*/
|
||||
@Override
|
||||
public void setBeanId(T bean, Object idValue) {
|
||||
idBinder.convertSetId(idValue, (EntityBean) bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert and set the id value.
|
||||
* <p>
|
||||
@@ -1563,6 +1716,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
return idBinder.convertSetId(idValue, bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property getProperty(String propName) {
|
||||
return getBeanProperty(propName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a BeanProperty by its name.
|
||||
*/
|
||||
@@ -1585,6 +1743,27 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all the assoc many properties on this bean that are not populated with the load context.
|
||||
* <p>
|
||||
* This provides further lazy loading via the load context.
|
||||
* </p>
|
||||
*/
|
||||
public void lazyLoadRegister(String prefix, EntityBeanIntercept ebi, EntityBean bean, LoadContext loadContext) {
|
||||
|
||||
// load the List/Set/Map proxy objects (deferred fetching of lists)
|
||||
BeanPropertyAssocMany<?>[] manys = propertiesMany();
|
||||
for (int i = 0; i < manys.length; i++) {
|
||||
if (!ebi.isLoadedProperty(manys[i].getPropertyIndex())) {
|
||||
BeanCollection<?> ref = manys[i].createReferenceIfNull(bean);
|
||||
if (ref != null && !ref.isRegisteredWithLoadContext()) {
|
||||
String path = SplitName.add(prefix, manys[i].getName());
|
||||
loadContext.register(path, ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the lazy loading property is a Many in which case just
|
||||
* define a Reference for the collection and not invoke a query.
|
||||
@@ -1626,16 +1805,16 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
// create a compound comparator based on the list of properties
|
||||
ElComparator<T>[] comparators = new ElComparator[sortBy.size()];
|
||||
|
||||
List<Property> sortProps = sortBy.getProperties();
|
||||
List<SortByClause.Property> sortProps = sortBy.getProperties();
|
||||
for (int i = 0; i < sortProps.size(); i++) {
|
||||
Property sortProperty = sortProps.get(i);
|
||||
SortByClause.Property sortProperty = sortProps.get(i);
|
||||
comparators[i] = createPropertyComparator(sortProperty);
|
||||
}
|
||||
|
||||
return new ElComparatorCompound<T>(comparators);
|
||||
}
|
||||
|
||||
private ElComparator<T> createPropertyComparator(Property sortProp) {
|
||||
private ElComparator<T> createPropertyComparator(SortByClause.Property sortProp) {
|
||||
|
||||
ElPropertyValue elGetValue = getElGetValue(sortProp.getName());
|
||||
|
||||
@@ -1670,6 +1849,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
return elGetValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionPath getExpressionPath(String path) {
|
||||
return getElGetValue(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to ElPropertyValue but also uses foreign key shortcuts.
|
||||
* <p>
|
||||
@@ -2130,6 +2314,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
return derivedTableJoins;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Property> allProperties() {
|
||||
return propertiesAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a collection of all BeanProperty. This includes transient properties.
|
||||
*/
|
||||
@@ -2168,6 +2357,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanProperty getIdProperty() {
|
||||
return idProperty;
|
||||
}
|
||||
@@ -2442,6 +2632,14 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
return propertiesLocal;
|
||||
}
|
||||
|
||||
public void jsonWriteDirty(WriteJson writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
|
||||
jsonHelp.jsonWriteDirty(writeJson, bean, dirtyProps);
|
||||
}
|
||||
|
||||
protected void jsonWriteDirtyProperties(WriteJson writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
|
||||
jsonHelp.jsonWriteDirtyProperties(writeJson, bean, dirtyProps);
|
||||
}
|
||||
|
||||
public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException {
|
||||
jsonHelp.jsonWrite(writeJson, bean, null);
|
||||
}
|
||||
@@ -2461,4 +2659,10 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
protected T jsonReadObject(ReadJson jsonRead, String path) throws IOException {
|
||||
return jsonHelp.jsonReadObject(jsonRead, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T jsonRead(JsonParser parser, JsonReadOptions readOptions, Object objectMapper) throws IOException {
|
||||
ReadJson jsonRead = new ReadJson(this, parser, readOptions, objectMapper);
|
||||
return jsonRead(jsonRead, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,14 @@ import java.util.Map;
|
||||
public class BeanDescriptorJsonHelp<T> {
|
||||
|
||||
private final BeanDescriptor<T> desc;
|
||||
|
||||
|
||||
private final InheritInfo inheritInfo;
|
||||
|
||||
|
||||
public BeanDescriptorJsonHelp(BeanDescriptor<T> desc) {
|
||||
this.desc = desc;
|
||||
this.inheritInfo = desc.inheritInfo;
|
||||
}
|
||||
|
||||
|
||||
public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) throws IOException {
|
||||
|
||||
writeJson.writeStartObject(key);
|
||||
@@ -46,12 +46,36 @@ public class BeanDescriptorJsonHelp<T> {
|
||||
|
||||
protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) throws IOException {
|
||||
|
||||
|
||||
WriteBean writeBean = writeJson.createWriteBean(desc, bean);
|
||||
writeBean.write(writeJson);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void jsonWriteDirty(WriteJson writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
|
||||
|
||||
if (inheritInfo == null) {
|
||||
jsonWriteDirtyProperties(writeJson, bean, dirtyProps);
|
||||
|
||||
} else {
|
||||
InheritInfo localInheritInfo = inheritInfo.readType(bean.getClass());
|
||||
BeanDescriptor<?> localDescriptor = localInheritInfo.getBeanDescriptor();
|
||||
localDescriptor.jsonWriteDirtyProperties(writeJson, bean, dirtyProps);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void jsonWriteDirtyProperties(WriteJson writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
|
||||
|
||||
writeJson.writeStartObject(null);
|
||||
// render the dirty properties
|
||||
BeanProperty[] props = desc.propertiesNonTransient();
|
||||
for (int j = 0; j < props.length; j++) {
|
||||
if (dirtyProps[props[j].getPropertyIndex()]) {
|
||||
props[j].jsonWrite(writeJson, bean);
|
||||
}
|
||||
}
|
||||
writeJson.writeEndObject();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T jsonRead(ReadJson jsonRead, String path) throws IOException {
|
||||
|
||||
@@ -66,14 +90,14 @@ public class BeanDescriptorJsonHelp<T> {
|
||||
return null;
|
||||
}
|
||||
if (JsonToken.START_OBJECT != token) {
|
||||
throw new JsonParseException("Unexpected token "+token+" - expecting start_object", parser.getCurrentLocation());
|
||||
throw new JsonParseException("Unexpected token " + token + " - expecting start_object", parser.getCurrentLocation());
|
||||
}
|
||||
}
|
||||
|
||||
if (desc.inheritInfo == null) {
|
||||
return jsonReadObject(jsonRead, path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// check for the discriminator value to determine the correct sub type
|
||||
String discColumn = inheritInfo.getRoot().getDiscriminatorColumn();
|
||||
|
||||
@@ -81,8 +105,8 @@ public class BeanDescriptorJsonHelp<T> {
|
||||
String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?";
|
||||
throw new JsonParseException(msg, parser.getCurrentLocation());
|
||||
}
|
||||
|
||||
String propName = parser.getCurrentName();
|
||||
|
||||
String propName = parser.getCurrentName();
|
||||
if (!propName.equalsIgnoreCase(discColumn)) {
|
||||
// just try to assume this is the correct bean type in the inheritance
|
||||
BeanProperty property = desc.getBeanProperty(propName);
|
||||
@@ -91,24 +115,24 @@ public class BeanDescriptorJsonHelp<T> {
|
||||
property.jsonRead(jsonRead, bean);
|
||||
return jsonReadProperties(jsonRead, bean, path);
|
||||
}
|
||||
String msg = "Error reading inheritance discriminator, expected property ["+discColumn+"] but got [" + propName + "] ?";
|
||||
String msg = "Error reading inheritance discriminator, expected property [" + discColumn + "] but got [" + propName + "] ?";
|
||||
throw new JsonParseException(msg, parser.getCurrentLocation());
|
||||
}
|
||||
|
||||
String discValue = parser.nextTextValue();
|
||||
|
||||
|
||||
String discValue = parser.nextTextValue();
|
||||
|
||||
// determine the sub type for this particular json object
|
||||
InheritInfo localInheritInfo = inheritInfo.readType(discValue);
|
||||
BeanDescriptor<?> localDescriptor = localInheritInfo.getBeanDescriptor();
|
||||
return (T) localDescriptor.jsonReadObject(jsonRead, path);
|
||||
}
|
||||
|
||||
|
||||
protected T jsonReadObject(ReadJson readJson, String path) throws IOException {
|
||||
|
||||
EntityBean bean = desc.createEntityBean();
|
||||
return jsonReadProperties(readJson, bean, path);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected T jsonReadProperties(ReadJson readJson, EntityBean bean, String path) throws IOException {
|
||||
|
||||
@@ -117,7 +141,7 @@ public class BeanDescriptorJsonHelp<T> {
|
||||
}
|
||||
|
||||
// unmapped properties, send to JsonReadBeanVisitor later
|
||||
Map<String,Object> unmappedProperties = null;
|
||||
Map<String, Object> unmappedProperties = null;
|
||||
|
||||
do {
|
||||
JsonParser parser = readJson.getParser();
|
||||
@@ -141,16 +165,22 @@ public class BeanDescriptorJsonHelp<T> {
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected token " + event + " - expecting key or end_object at: " + parser.getCurrentLocation());
|
||||
}
|
||||
|
||||
|
||||
} while (true);
|
||||
|
||||
// visit JsonReadBeanVisitor (if registered for this path)
|
||||
readJson.beanVisitor(bean, unmappedProperties);
|
||||
|
||||
Object contextBean = null;
|
||||
Object id = desc.beanId(bean);
|
||||
if (id != null) {
|
||||
// check if the bean has already been loaded
|
||||
contextBean = readJson.persistenceContextPutIfAbsent(id, bean, desc);
|
||||
}
|
||||
if (contextBean == null) {
|
||||
readJson.beanVisitor(bean, unmappedProperties);
|
||||
}
|
||||
if (path != null) {
|
||||
readJson.popPath();
|
||||
}
|
||||
return (T)bean;
|
||||
return contextBean == null ? (T) bean : (T) contextBean;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import com.avaje.ebean.event.changelog.ChangeLogFilter;
|
||||
import com.avaje.ebean.event.changelog.ChangeLogListener;
|
||||
import com.avaje.ebean.event.changelog.ChangeLogPrepare;
|
||||
import com.avaje.ebean.event.changelog.ChangeLogRegister;
|
||||
import com.avaje.ebean.plugin.SpiBeanType;
|
||||
import com.avaje.ebean.plugin.BeanType;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.TransactionEventTable;
|
||||
import com.avaje.ebeaninternal.server.core.BootupClasses;
|
||||
@@ -52,6 +52,8 @@ import com.avaje.ebeaninternal.server.properties.BeanPropertiesReader;
|
||||
import com.avaje.ebeaninternal.server.properties.BeanPropertyInfo;
|
||||
import com.avaje.ebeaninternal.server.properties.BeanPropertyInfoFactory;
|
||||
import com.avaje.ebeaninternal.server.properties.EnhanceBeanPropertyInfoFactory;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreBeanAdapter;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -111,12 +113,16 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
private final BeanManagerFactory beanManagerFactory;
|
||||
|
||||
private final ServerConfig serverConfig;
|
||||
|
||||
private final ChangeLogListener changeLogListener;
|
||||
|
||||
private final ChangeLogRegister changeLogRegister;
|
||||
|
||||
private final ChangeLogPrepare changeLogPrepare;
|
||||
|
||||
private final DocStoreFactory docStoreFactory;
|
||||
|
||||
private int enhancedClassCount;
|
||||
|
||||
private final boolean updateChangesOnly;
|
||||
@@ -125,14 +131,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
private final String serverName;
|
||||
|
||||
private final ServerConfig serverConfig;
|
||||
|
||||
private Map<Class<?>, DeployBeanInfo<?>> deplyInfoMap = new HashMap<Class<?>, DeployBeanInfo<?>>();
|
||||
|
||||
private final Map<Class<?>, BeanTable> beanTableMap = new HashMap<Class<?>, BeanTable>();
|
||||
|
||||
private final Map<String, BeanDescriptor<?>> descMap = new HashMap<String, BeanDescriptor<?>>();
|
||||
|
||||
private final Map<String, BeanDescriptor<?>> descQueueMap = new HashMap<String, BeanDescriptor<?>>();
|
||||
|
||||
private final Map<String, BeanManager<?>> beanManagerMap = new HashMap<String, BeanManager<?>>();
|
||||
|
||||
private final Map<String, List<BeanDescriptor<?>>> tableToDescMap = new HashMap<String, List<BeanDescriptor<?>>>();
|
||||
@@ -183,6 +189,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
this.serverConfig = config.getServerConfig();
|
||||
this.serverName = InternString.intern(serverConfig.getName());
|
||||
this.cacheManager = config.getCacheManager();
|
||||
this.docStoreFactory = config.getDocStoreFactory();
|
||||
this.xmlConfig = config.getXmlConfig();
|
||||
this.dbSequenceBatchSize = serverConfig.getDatabaseSequenceBatchSize();
|
||||
this.backgroundExecutor = config.getBackgroundExecutor();
|
||||
@@ -246,6 +253,15 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
return serverConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DocStoreBeanAdapter<T> createDocStoreBeanAdapter(BeanDescriptor descriptor, DeployBeanDescriptor<T> deploy) {
|
||||
return docStoreFactory.createAdapter(descriptor, deploy);
|
||||
}
|
||||
|
||||
public BeanDescriptor<?> getBeanDescriptorByQueueId(String queueId) {
|
||||
return descQueueMap.get(queueId);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> BeanDescriptor<T> getBeanDescriptor(Class<T> entityType) {
|
||||
return (BeanDescriptor<T>) descMap.get(entityType.getName());
|
||||
@@ -359,7 +375,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
/**
|
||||
* Return the BeanDescriptors mapped to the table.
|
||||
*/
|
||||
public List<? extends SpiBeanType<?>> getBeanTypes(String tableName) {
|
||||
public List<? extends BeanType<?>> getBeanTypes(String tableName) {
|
||||
return tableToDescMap.get(tableName.toLowerCase());
|
||||
}
|
||||
|
||||
@@ -433,6 +449,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
d.initialiseOther(asOfTableMap, asOfViewSuffix, draftTableMap);
|
||||
}
|
||||
|
||||
// PASS 4:
|
||||
// now initialise document mapping which needs target descriptors
|
||||
for (BeanDescriptor<?> d : descMap.values()) {
|
||||
d.initialiseDocMapping();
|
||||
}
|
||||
|
||||
// create BeanManager for each non-embedded entity bean
|
||||
for (BeanDescriptor<?> d : descMap.values()) {
|
||||
if (!d.isEmbedded()) {
|
||||
@@ -518,6 +540,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
private void registerBeanDescriptor(BeanDescriptor<?> desc) {
|
||||
descMap.put(desc.getBeanType().getName(), desc);
|
||||
if (desc.isDocStoreMapped()) {
|
||||
descQueueMap.put(desc.getDocStoreQueueId(), desc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1050,7 +1075,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
*/
|
||||
private <T> DeployBeanInfo<T> createDeployBeanInfo(Class<T> beanClass) {
|
||||
|
||||
DeployBeanDescriptor<T> desc = new DeployBeanDescriptor<T>(beanClass);
|
||||
DeployBeanDescriptor<T> desc = new DeployBeanDescriptor<T>(beanClass, serverConfig);
|
||||
|
||||
desc.setUpdateChangesOnly(updateChangesOnly);
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.config.EncryptKey;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreBeanAdapter;
|
||||
|
||||
/**
|
||||
* Provides a method to find a BeanDescriptor.
|
||||
@@ -38,6 +40,13 @@ public interface BeanDescriptorMap {
|
||||
*/
|
||||
EncryptKey getEncryptKey(String tableName, String columnName);
|
||||
|
||||
/**
|
||||
* Create a IdBinder for this bean property.
|
||||
*/
|
||||
IdBinder createIdBinder(BeanProperty id);
|
||||
|
||||
/**
|
||||
* Create a doc store specific adapter for this bean type.
|
||||
*/
|
||||
<T> DocStoreBeanAdapter<T> createDocStoreBeanAdapter(BeanDescriptor descriptor, DeployBeanDescriptor<T> deploy);
|
||||
}
|
||||
|
||||
@@ -166,6 +166,11 @@ public final class BeanFkeyProperty implements ElPropertyValue {
|
||||
throw new RuntimeException("ElPropertyDeploy only - not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Object bean, Object value) {
|
||||
throw new RuntimeException("ElPropertyDeploy only - not implemented");
|
||||
}
|
||||
|
||||
public void elSetValue(EntityBean bean, Object value, boolean populate) {
|
||||
throw new RuntimeException("ElPropertyDeploy only - not implemented");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,10 @@ import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.config.EncryptKey;
|
||||
import com.avaje.ebean.config.dbplatform.DbEncryptFunction;
|
||||
import com.avaje.ebean.config.dbplatform.DbType;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarTypeEnum;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocMappingBuilder;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyMapping;
|
||||
import com.avaje.ebean.plugin.Property;
|
||||
import com.avaje.ebean.text.StringParser;
|
||||
import com.avaje.ebeaninternal.server.core.InternString;
|
||||
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
|
||||
@@ -24,6 +28,9 @@ import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarTypeBoolean;
|
||||
import com.avaje.ebeaninternal.util.ValueUtil;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyOptions;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
import com.avaje.ebeanservice.docstore.api.support.DocStructure;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -42,7 +49,7 @@ import java.util.Map;
|
||||
* Description of a property of a bean. Includes its deployment information such
|
||||
* as database column mapping information.
|
||||
*/
|
||||
public class BeanProperty implements ElPropertyValue {
|
||||
public class BeanProperty implements ElPropertyValue, Property {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanProperty.class);
|
||||
|
||||
@@ -196,6 +203,8 @@ public class BeanProperty implements ElPropertyValue {
|
||||
@SuppressWarnings("rawtypes")
|
||||
final ScalarType scalarType;
|
||||
|
||||
final DocPropertyOptions docOptions;
|
||||
|
||||
/**
|
||||
* The length or precision for DB column.
|
||||
*/
|
||||
@@ -313,6 +322,7 @@ public class BeanProperty implements ElPropertyValue {
|
||||
this.lob = isLobType(dbType);
|
||||
this.propertyType = deploy.getPropertyType();
|
||||
this.field = deploy.getField();
|
||||
this.docOptions = deploy.getDocPropertyOptions();
|
||||
|
||||
this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(), false, null);
|
||||
this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(), dbEncrypted, dbColumn);
|
||||
@@ -414,6 +424,7 @@ public class BeanProperty implements ElPropertyValue {
|
||||
this.lob = isLobType(dbType);
|
||||
this.propertyType = source.getPropertyType();
|
||||
this.field = source.getField();
|
||||
this.docOptions = source.docOptions;
|
||||
|
||||
this.elPlaceHolder = override.replace(source.elPlaceHolder, source.dbColumn);
|
||||
this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn);
|
||||
@@ -557,6 +568,11 @@ public class BeanProperty implements ElPropertyValue {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMany() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isAssignableFrom(Class<?> type) {
|
||||
return owningType.isAssignableFrom(type);
|
||||
}
|
||||
@@ -670,6 +686,15 @@ public class BeanProperty implements ElPropertyValue {
|
||||
bean._ebean_getIntercept().setChangedProperty(propertyIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the changed value without invoking interception (lazy loading etc).
|
||||
* Typically used to set generated values on update.
|
||||
*/
|
||||
public void setValueChanged(EntityBean bean, Object value) {
|
||||
setValue(bean, value);
|
||||
bean._ebean_getIntercept().setChangedProperty(propertyIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the property without interception or
|
||||
* PropertyChangeSupport.
|
||||
@@ -716,6 +741,10 @@ public class BeanProperty implements ElPropertyValue {
|
||||
throw new RuntimeException("Expected to be called only on BeanPropertyCompoundScalar");
|
||||
}
|
||||
|
||||
public Object getVal(Object bean) {
|
||||
return getValue((EntityBean)bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the property method.
|
||||
*/
|
||||
@@ -746,6 +775,14 @@ public class BeanProperty implements ElPropertyValue {
|
||||
return convertToLogicalType(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Object bean, Object value) {
|
||||
|
||||
// convert for Enums etc
|
||||
Object logicalVal = convertToLogicalType(value);
|
||||
elSetValue((EntityBean) bean, logicalVal, true);
|
||||
}
|
||||
|
||||
public void elSetValue(EntityBean bean, Object value, boolean populate) {
|
||||
if (bean != null) {
|
||||
// Not using setValueIntercept at this stage
|
||||
@@ -1187,6 +1224,14 @@ public class BeanProperty implements ElPropertyValue {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append this property to the document store based on includeByDefault setting.
|
||||
*/
|
||||
public void docStoreInclude(boolean includeByDefault, DocStructure docStructure) {
|
||||
if (includeByDefault) {
|
||||
docStructure.addProperty(name);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings(value = "unchecked")
|
||||
public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException {
|
||||
@@ -1263,4 +1308,28 @@ public class BeanProperty implements ElPropertyValue {
|
||||
map.put(propName, new ValuePair(newVal, oldVal));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the document mapping if this property is included for this index.
|
||||
*/
|
||||
public void docStoreMapping(DocMappingBuilder mapping, String prefix) {
|
||||
|
||||
if (mapping.includesProperty(prefix, name)) {
|
||||
|
||||
DocPropertyType type = scalarType.getDocType();
|
||||
DocPropertyOptions options = docOptions.copy();
|
||||
if (DocPropertyType.STRING == type && isDocCode()) {
|
||||
options.setCode(true);
|
||||
}
|
||||
|
||||
mapping.add(new DocPropertyMapping(name, type, options));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this should be treated as a 'code' (effectively not analysed).
|
||||
*/
|
||||
private boolean isDocCode() {
|
||||
return id || scalarType instanceof ScalarTypeEnum;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,12 @@ import java.util.ArrayList;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocMappingBuilder;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyMapping;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
import com.avaje.ebeanservice.docstore.api.support.DocStructure;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -34,7 +40,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
IdBinder targetIdBinder;
|
||||
|
||||
InheritInfo targetInheritInfo;
|
||||
|
||||
|
||||
String targetIdProperty;
|
||||
|
||||
/**
|
||||
@@ -59,6 +65,8 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
|
||||
final String mappedBy;
|
||||
|
||||
final String docStoreDoc;
|
||||
|
||||
final String extraWhere;
|
||||
|
||||
boolean saveRecurseSkippable;
|
||||
@@ -71,7 +79,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
this.extraWhere = InternString.intern(deploy.getExtraWhere());
|
||||
this.beanTable = deploy.getBeanTable();
|
||||
this.mappedBy = InternString.intern(deploy.getMappedBy());
|
||||
|
||||
this.docStoreDoc = deploy.getDocStoreDoc();
|
||||
this.tableJoin = new TableJoin(deploy.getTableJoin());
|
||||
|
||||
this.targetType = deploy.getTargetType();
|
||||
@@ -215,6 +223,63 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
return extraWhere;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the elastic search doc for this embedded property.
|
||||
*/
|
||||
public String getDocStoreDoc() {
|
||||
return docStoreDoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if and how the associated bean is included in the doc store document.
|
||||
*/
|
||||
@Override
|
||||
public void docStoreInclude(boolean includeByDefault, DocStructure docStructure) {
|
||||
|
||||
String embeddedDoc = getDocStoreDoc();
|
||||
if (embeddedDoc == null) {
|
||||
// not annotated so use include by default
|
||||
// which is *ToOne included and *ToMany excluded
|
||||
if (includeByDefault) {
|
||||
docStoreIncludeByDefault(docStructure.doc());
|
||||
}
|
||||
} else {
|
||||
// explicitly annotated to be included
|
||||
if (embeddedDoc.isEmpty()) {
|
||||
embeddedDoc = "*";
|
||||
}
|
||||
// add in a nested way
|
||||
PathProperties embDoc = PathProperties.parse(embeddedDoc);
|
||||
docStructure.addNested(name, embDoc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Include the property in the document store by default.
|
||||
*/
|
||||
protected void docStoreIncludeByDefault(PathProperties pathProps) {
|
||||
pathProps.addToPath(null, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void docStoreMapping(DocMappingBuilder mapping, String prefix) {
|
||||
|
||||
if (mapping.includesPath(prefix, name)) {
|
||||
String fullName = SplitName.add(prefix, name);
|
||||
|
||||
DocPropertyType type = isMany() ? DocPropertyType.LIST : DocPropertyType.OBJECT;
|
||||
DocPropertyMapping nested = new DocPropertyMapping(name, type);
|
||||
|
||||
mapping.push(nested);
|
||||
targetDescriptor.docStoreMapping(mapping, fullName);
|
||||
mapping.pop();
|
||||
|
||||
if (!nested.getChildren().isEmpty()) {
|
||||
mapping.add(nested);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this association is updateable.
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebean.*;
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.Expression;
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebean.SqlUpdate;
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.BeanCollection.ModifyListenMode;
|
||||
import com.avaje.ebean.bean.BeanCollectionAdd;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
|
||||
@@ -91,6 +96,8 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
*/
|
||||
protected BeanPropertyAssocOne<?> childMasterProperty;
|
||||
|
||||
private String childMasterIdProperty;
|
||||
|
||||
private boolean embeddedExportedProperties;
|
||||
|
||||
private BeanCollectionHelp<T> help;
|
||||
@@ -179,16 +186,31 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise after the target bean descriptors have been all set.
|
||||
*/
|
||||
public void initialisePostTarget() {
|
||||
if (childMasterProperty != null) {
|
||||
BeanProperty masterId = childMasterProperty.getTargetDescriptor().getIdProperty();
|
||||
childMasterIdProperty = childMasterProperty.getName() + "." + masterId.getName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void docStoreIncludeByDefault(PathProperties pathProps) {
|
||||
// by default not including "Many" properties in document store
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -282,10 +304,29 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
return server.findIds(q, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the loaded current bean to its associated parent.
|
||||
*/
|
||||
public void lazyLoadMany(EntityBean current) {
|
||||
EntityBean parentBean = childMasterProperty.getValueAsEntityBean(current);
|
||||
if (parentBean != null) {
|
||||
addBeanToCollectionWithCreate(parentBean, current, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void addWhereParentIdIn(SpiQuery<?> query, List<Object> parentIds, boolean useDocStore) {
|
||||
if (useDocStore) {
|
||||
// assumes the ManyToOne property is included
|
||||
query.where().in(childMasterIdProperty, parentIds);
|
||||
} else {
|
||||
addWhereParentIdIn(query, parentIds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a where clause to the query for a given list of parent Id's.
|
||||
*/
|
||||
public void addWhereParentIdIn(SpiQuery<?> query, List<Object> parentIds) {
|
||||
private void addWhereParentIdIn(SpiQuery<?> query, List<Object> parentIds) {
|
||||
|
||||
String tableAlias = manyToMany ? "int_." : "t0.";
|
||||
if (manyToMany) {
|
||||
@@ -416,7 +457,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
public void add(BeanCollection<?> collection, EntityBean bean) {
|
||||
help.add(collection, bean);
|
||||
help.add(collection, bean, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -450,6 +491,10 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
return targetDescriptor.getIdBinder().getAssocIdInExpr(prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMany() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssocId() {
|
||||
@@ -904,6 +949,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
jsonHelp.jsonRead(readJson, parentBean);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void publishMany(EntityBean draft, EntityBean live) {
|
||||
|
||||
// collections will not be null due to enhancement
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebean.annotation.DocCode;
|
||||
import com.avaje.ebean.annotation.DocProperty;
|
||||
import com.avaje.ebean.annotation.DocSortable;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyOptions;
|
||||
|
||||
/**
|
||||
* The options for document property collected when reading deployment mapping.
|
||||
*/
|
||||
public class DeployDocPropertyOptions {
|
||||
|
||||
private Boolean code;
|
||||
|
||||
private Boolean sortable;
|
||||
|
||||
private Boolean store;
|
||||
|
||||
private Float boost;
|
||||
|
||||
private String nullValue;
|
||||
|
||||
/**
|
||||
* Read the DocProperty deployment options.
|
||||
*/
|
||||
public void setDocProperty(DocProperty doc) {
|
||||
code = checkDefault(doc.code());
|
||||
sortable = checkDefault(doc.sortable());
|
||||
store = checkDefault(doc.store());
|
||||
boost = checkDefault(doc.boost());
|
||||
nullValue = checkDefault(doc.nullValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the DocSortable deployment options.
|
||||
*/
|
||||
public void setDocSortable(DocSortable doc) {
|
||||
sortable = Boolean.TRUE;
|
||||
store = checkDefault(doc.store());
|
||||
boost = checkDefault(doc.boost());
|
||||
nullValue = checkDefault(doc.nullValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the DocCode deployment options.
|
||||
*/
|
||||
public void setDocCode(DocCode doc) {
|
||||
code = Boolean.TRUE;
|
||||
store = checkDefault(doc.store());
|
||||
boost = checkDefault(doc.boost());
|
||||
nullValue = checkDefault(doc.nullValue());
|
||||
}
|
||||
|
||||
private String checkDefault(String s) {
|
||||
return "".equals(s) ? null : s;
|
||||
}
|
||||
|
||||
private Float checkDefault(float boost) {
|
||||
return (boost == 1) ? null : boost;
|
||||
}
|
||||
|
||||
private Boolean checkDefault(boolean store) {
|
||||
return (store) ? Boolean.TRUE : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DocPropertyOptions with the collected options.
|
||||
*/
|
||||
public DocPropertyOptions create() {
|
||||
return new DocPropertyOptions(code, sortable, store, boost, nullValue);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+106
-4
@@ -1,6 +1,9 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import com.avaje.ebean.annotation.ConcurrencyMode;
|
||||
import com.avaje.ebean.annotation.DocStore;
|
||||
import com.avaje.ebean.annotation.DocStoreEvent;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.TableName;
|
||||
import com.avaje.ebean.config.dbplatform.IdGenerator;
|
||||
import com.avaje.ebean.config.dbplatform.IdType;
|
||||
@@ -10,6 +13,7 @@ import com.avaje.ebean.event.BeanPersistListener;
|
||||
import com.avaje.ebean.event.BeanPostLoad;
|
||||
import com.avaje.ebean.event.BeanQueryAdapter;
|
||||
import com.avaje.ebean.event.changelog.ChangeLogFilter;
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import com.avaje.ebeaninternal.server.core.CacheOptions;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
|
||||
import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistController;
|
||||
@@ -33,7 +37,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.
|
||||
@@ -54,6 +57,8 @@ public class DeployBeanDescriptor<T> {
|
||||
|
||||
private static final String I_SCALAOBJECT = "scala.ScalaObject";
|
||||
|
||||
private final ServerConfig serverConfig;
|
||||
|
||||
/**
|
||||
* Map of BeanProperty Linked so as to preserve order.
|
||||
*/
|
||||
@@ -167,11 +172,31 @@ public class DeployBeanDescriptor<T> {
|
||||
|
||||
private String dbComment;
|
||||
|
||||
/**
|
||||
* One of NONE, INDEX or EMBEDDED.
|
||||
*/
|
||||
private boolean docStoreMapped;
|
||||
|
||||
private DocStore docStore;
|
||||
|
||||
private PathProperties docStorePathProperties;
|
||||
|
||||
private String docStoreQueueId;
|
||||
|
||||
private String docStoreIndexName;
|
||||
|
||||
private String docStoreIndexType;
|
||||
|
||||
private DocStoreEvent docStorePersist;
|
||||
private DocStoreEvent docStoreInsert;
|
||||
private DocStoreEvent docStoreUpdate;
|
||||
private DocStoreEvent docStoreDelete;
|
||||
|
||||
/**
|
||||
* Construct the BeanDescriptor.
|
||||
*/
|
||||
public DeployBeanDescriptor(Class<T> beanType) {
|
||||
public DeployBeanDescriptor(Class<T> beanType, ServerConfig serverConfig) {
|
||||
this.serverConfig = serverConfig;
|
||||
this.beanType = beanType;
|
||||
}
|
||||
|
||||
@@ -235,6 +260,26 @@ public class DeployBeanDescriptor<T> {
|
||||
return draftableElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the top level doc store deployment information.
|
||||
*/
|
||||
public void readDocStore(DocStore docStore) {
|
||||
|
||||
this.docStore = docStore;
|
||||
docStoreMapped = true;
|
||||
docStoreQueueId = docStore.queueId();
|
||||
docStoreIndexName = docStore.indexName();
|
||||
docStoreIndexType = docStore.indexType();
|
||||
docStorePersist = docStore.persist();
|
||||
docStoreInsert = docStore.insert();
|
||||
docStoreUpdate = docStore.update();
|
||||
docStoreDelete = docStore.delete();
|
||||
String doc = docStore.doc();
|
||||
if (doc != null && doc.length() > 0) {
|
||||
docStorePathProperties = PathProperties.parse(doc);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isScalaObject() {
|
||||
Class<?>[] interfaces = beanType.getInterfaces();
|
||||
for (int i = 0; i < interfaces.length; i++) {
|
||||
@@ -763,7 +808,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 +825,7 @@ public class DeployBeanDescriptor<T> {
|
||||
set.add(temp);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableSet(set);
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -932,4 +977,61 @@ public class DeployBeanDescriptor<T> {
|
||||
checkInheritance(parent);
|
||||
}
|
||||
}
|
||||
|
||||
public PathProperties getDocStorePathProperties() {
|
||||
return docStorePathProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this type is mapped for a doc store.
|
||||
*/
|
||||
public boolean isDocStoreMapped() {
|
||||
return docStoreMapped;
|
||||
}
|
||||
|
||||
public String getDocStoreQueueId() {
|
||||
return docStoreQueueId;
|
||||
}
|
||||
|
||||
public String getDocStoreIndexName() {
|
||||
return docStoreIndexName;
|
||||
}
|
||||
|
||||
public String getDocStoreIndexType() {
|
||||
return docStoreIndexType;
|
||||
}
|
||||
|
||||
public DocStore getDocStore() {
|
||||
return docStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DocStore index behavior for bean inserts.
|
||||
*/
|
||||
public DocStoreEvent getDocStoreInsertEvent() {
|
||||
return getDocStoreIndexEvent(docStoreInsert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DocStore index behavior for bean updates.
|
||||
*/
|
||||
public DocStoreEvent getDocStoreUpdateEvent() {
|
||||
return getDocStoreIndexEvent(docStoreUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DocStore index behavior for bean deletes.
|
||||
*/
|
||||
public DocStoreEvent getDocStoreDeleteEvent() {
|
||||
return getDocStoreIndexEvent(docStoreDelete);
|
||||
}
|
||||
|
||||
private DocStoreEvent getDocStoreIndexEvent(DocStoreEvent mostSpecific) {
|
||||
if (!docStoreMapped) {
|
||||
return DocStoreEvent.IGNORE;
|
||||
}
|
||||
if (mostSpecific != DocStoreEvent.DEFAULT) return mostSpecific;
|
||||
if (docStorePersist != DocStoreEvent.DEFAULT) return docStorePersist;
|
||||
return serverConfig.getDocStoreConfig().getPersist();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import com.avaje.ebean.annotation.CreatedTimestamp;
|
||||
import com.avaje.ebean.annotation.DocCode;
|
||||
import com.avaje.ebean.annotation.DocProperty;
|
||||
import com.avaje.ebean.annotation.DocSortable;
|
||||
import com.avaje.ebean.annotation.SoftDelete;
|
||||
import com.avaje.ebean.annotation.UpdatedTimestamp;
|
||||
import com.avaje.ebean.annotation.WhenCreated;
|
||||
@@ -12,6 +15,7 @@ import com.avaje.ebean.config.dbplatform.DbEncrypt;
|
||||
import com.avaje.ebean.config.dbplatform.DbEncryptFunction;
|
||||
import com.avaje.ebeaninternal.server.core.InternString;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.DeployDocPropertyOptions;
|
||||
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
import com.avaje.ebeaninternal.server.properties.BeanPropertyGetter;
|
||||
@@ -19,6 +23,7 @@ import com.avaje.ebeaninternal.server.properties.BeanPropertySetter;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarTypeEnum;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarTypeWrapper;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyOptions;
|
||||
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.FetchType;
|
||||
@@ -159,6 +164,8 @@ public class DeployBeanProperty {
|
||||
*/
|
||||
private int dbType;
|
||||
|
||||
private DeployDocPropertyOptions docMapping = new DeployDocPropertyOptions();
|
||||
|
||||
/**
|
||||
* The method used to read the property.
|
||||
*/
|
||||
@@ -908,4 +915,21 @@ public class DeployBeanProperty {
|
||||
public String getDbComment() {
|
||||
return dbComment;
|
||||
}
|
||||
|
||||
public void setDocProperty(DocProperty docProperty) {
|
||||
docMapping.setDocProperty(docProperty);
|
||||
}
|
||||
|
||||
public void setDocSortable(DocSortable docSortable) {
|
||||
docMapping.setDocSortable(docSortable);
|
||||
}
|
||||
|
||||
public void setDocCode(DocCode docCode) {
|
||||
docMapping.setDocCode(docCode);
|
||||
}
|
||||
|
||||
public DocPropertyOptions getDocPropertyOptions() {
|
||||
return docMapping.create();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+16
-1
@@ -1,5 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import com.avaje.ebean.annotation.DocStoreEmbedded;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanTable;
|
||||
|
||||
@@ -37,7 +38,9 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
* From the deployment mappedBy attribute.
|
||||
*/
|
||||
String mappedBy;
|
||||
|
||||
|
||||
String docStoreDoc;
|
||||
|
||||
/**
|
||||
* Construct the property.
|
||||
*/
|
||||
@@ -126,4 +129,16 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
this.mappedBy = mappedBy;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set DocStoreEmbedded deployment information.
|
||||
*/
|
||||
public void setDocStoreEmbedded(DocStoreEmbedded embedded) {
|
||||
docStoreDoc = embedded.doc();
|
||||
}
|
||||
|
||||
public String getDocStoreDoc() {
|
||||
return docStoreDoc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import javax.persistence.UniqueConstraint;
|
||||
|
||||
import com.avaje.ebean.annotation.CacheStrategy;
|
||||
import com.avaje.ebean.annotation.CacheTuning;
|
||||
import com.avaje.ebean.annotation.DocStore;
|
||||
import com.avaje.ebean.annotation.DbComment;
|
||||
import com.avaje.ebean.annotation.Draftable;
|
||||
import com.avaje.ebean.annotation.DraftableElement;
|
||||
@@ -166,6 +167,11 @@ public class AnnotationClass extends AnnotationParser {
|
||||
descriptor.setDbComment(comment.value());
|
||||
}
|
||||
|
||||
DocStore docStore = cls.getAnnotation(DocStore.class);
|
||||
if (docStore != null) {
|
||||
descriptor.readDocStore(docStore);
|
||||
}
|
||||
|
||||
UpdateMode updateMode = cls.getAnnotation(UpdateMode.class);
|
||||
if (updateMode != null) {
|
||||
descriptor.setUpdateChangesOnly(updateMode.updateChangesOnly());
|
||||
|
||||
@@ -64,7 +64,7 @@ public class AnnotationFields extends AnnotationParser {
|
||||
|
||||
for (DeployBeanProperty prop : descriptor.propertiesAll()) {
|
||||
if (prop instanceof DeployBeanPropertyAssoc<?>) {
|
||||
readAssocOne(prop);
|
||||
readAssocOne((DeployBeanPropertyAssoc<?>)prop);
|
||||
} else {
|
||||
readField(prop);
|
||||
}
|
||||
@@ -74,7 +74,7 @@ public class AnnotationFields extends AnnotationParser {
|
||||
/**
|
||||
* Read the Id marker annotations on EmbeddedId properties.
|
||||
*/
|
||||
private void readAssocOne(DeployBeanProperty prop) {
|
||||
private void readAssocOne(DeployBeanPropertyAssoc<?> prop) {
|
||||
|
||||
readJsonAnnotations(prop);
|
||||
|
||||
@@ -91,6 +91,11 @@ public class AnnotationFields extends AnnotationParser {
|
||||
prop.setEmbedded();
|
||||
}
|
||||
|
||||
DocStoreEmbedded docStoreEmbedded = get(prop, DocStoreEmbedded.class);
|
||||
if (docStoreEmbedded != null) {
|
||||
prop.setDocStoreEmbedded(docStoreEmbedded);
|
||||
}
|
||||
|
||||
if (prop instanceof DeployBeanPropertyAssocOne<?>) {
|
||||
if (prop.isId() && !prop.isEmbedded()) {
|
||||
prop.setEmbedded();
|
||||
@@ -179,6 +184,19 @@ public class AnnotationFields extends AnnotationParser {
|
||||
}
|
||||
}
|
||||
|
||||
DocProperty docProperty = get(prop, DocProperty.class);
|
||||
if (docProperty != null) {
|
||||
prop.setDocProperty(docProperty);
|
||||
}
|
||||
DocSortable docSortable = get(prop, DocSortable.class);
|
||||
if (docSortable != null) {
|
||||
prop.setDocSortable(docSortable);
|
||||
}
|
||||
DocCode docCode = get(prop, DocCode.class);
|
||||
if (docCode != null) {
|
||||
prop.setDocCode(docCode);
|
||||
}
|
||||
|
||||
if (get(prop, DbHstore.class) != null) {
|
||||
util.setDbHstore(prop);
|
||||
}
|
||||
|
||||
@@ -249,6 +249,11 @@ public class ElPropertyChain implements ElPropertyValue {
|
||||
return chain[last].elGetValue(prevBean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Object bean, Object value) {
|
||||
elSetValue((EntityBean)bean, value, true);
|
||||
}
|
||||
|
||||
public void elSetValue(EntityBean bean, Object value, boolean populate) {
|
||||
|
||||
EntityBean prevBean = bean;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.el;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.plugin.ExpressionPath;
|
||||
import com.avaje.ebean.text.StringParser;
|
||||
|
||||
/**
|
||||
@@ -9,7 +10,7 @@ import com.avaje.ebean.text.StringParser;
|
||||
* This can be used for local sorting and filtering.
|
||||
* </p>
|
||||
*/
|
||||
public interface ElPropertyValue extends ElPropertyDeploy {
|
||||
public interface ElPropertyValue extends ElPropertyDeploy, ExpressionPath {
|
||||
|
||||
/**
|
||||
* Return the Id values for the given bean value.
|
||||
|
||||
@@ -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,12 @@ 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.io.IOException;
|
||||
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 +27,23 @@ class AllEqualsExpression implements SpiExpression {
|
||||
return propName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
|
||||
context.writeBoolMustStart();
|
||||
for (Map.Entry<String, Object> entry : propMap.entrySet()) {
|
||||
Object value = entry.getValue();
|
||||
String propName = entry.getKey();
|
||||
if (value == null) {
|
||||
context.writeExists(false, propName);
|
||||
} else {
|
||||
context.writeTerm(propName, value);
|
||||
}
|
||||
}
|
||||
context.writeBoolEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
if (propMap != null) {
|
||||
for (String propertyName : propMap.keySet()) {
|
||||
@@ -44,6 +62,7 @@ class AllEqualsExpression implements SpiExpression {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
if (propMap.isEmpty()) {
|
||||
@@ -57,6 +76,7 @@ class AllEqualsExpression implements SpiExpression {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
if (propMap.isEmpty()) {
|
||||
@@ -92,7 +112,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 +121,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 +135,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,9 +1,10 @@
|
||||
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 java.io.IOException;
|
||||
|
||||
class BetweenExpression extends AbstractExpression {
|
||||
|
||||
@@ -21,28 +22,50 @@ class BetweenExpression extends AbstractExpression {
|
||||
this.valueHigh = valHigh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
context.writeRange(propName, Op.GT_EQ, valueLow, Op.LT_EQ, valueHigh);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
+35
-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;
|
||||
@@ -9,10 +8,12 @@ import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 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 +33,16 @@ class BetweenPropertyExpression implements SpiExpression {
|
||||
return propName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
|
||||
context.writeBoolMustStart();
|
||||
context.writeSimple(Op.LT_EQ, lowProperty, value);
|
||||
context.writeSimple(Op.GT_EQ, highProperty, value);
|
||||
context.writeBoolEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
|
||||
ElPropertyDeploy elProp = desc.getElPropertyDeploy(name(lowProperty));
|
||||
@@ -51,25 +62,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);
|
||||
}
|
||||
}
|
||||
|
||||
+41
-10
@@ -1,10 +1,12 @@
|
||||
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;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class CaseInsensitiveEqualExpression extends AbstractExpression {
|
||||
|
||||
private static final long serialVersionUID = -6406036750998971064L;
|
||||
@@ -16,6 +18,22 @@ class CaseInsensitiveEqualExpression extends AbstractExpression {
|
||||
this.value = value.toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
|
||||
String[] values = value.split(" ");
|
||||
if (values.length == 1) {
|
||||
context.writeMatch(propName, value);
|
||||
} else {
|
||||
context.writeBoolStart(true);
|
||||
for (String val : values) {
|
||||
context.writeMatch(propName, val);
|
||||
}
|
||||
context.writeBoolEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
ElPropertyValue prop = getElProp(request);
|
||||
@@ -28,29 +46,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);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user