mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#304 - ElasticSearch integration part 1 / doc store integration
This commit is contained in:
@@ -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);
|
||||
|
||||
}
|
||||
@@ -1891,6 +1891,11 @@ public interface EbeanServer {
|
||||
*/
|
||||
JsonContext json();
|
||||
|
||||
/**
|
||||
* Return the Document store.
|
||||
*/
|
||||
DocumentStore docStore();
|
||||
|
||||
/**
|
||||
* Publish a single bean given its type and id returning the resulting live bean.
|
||||
* <p>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -274,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.
|
||||
@@ -1285,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.
|
||||
*/
|
||||
@@ -1364,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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1153,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.
|
||||
*/
|
||||
@@ -2134,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.
|
||||
*/
|
||||
@@ -2211,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.
|
||||
*/
|
||||
@@ -2239,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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -5,32 +5,96 @@ 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.
|
||||
*/
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@@ -61,4 +125,33 @@ public interface BeanType<T> {
|
||||
*/
|
||||
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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -36,4 +36,8 @@ public interface SpiServer extends EbeanServer {
|
||||
*/
|
||||
List<? extends BeanType<?>> getBeanTypes(String baseTableName);
|
||||
|
||||
/**
|
||||
* Return the bean type for a given doc store queueId.
|
||||
*/
|
||||
BeanType<?> getBeanTypeForQueueId(String queueId);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -83,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.
|
||||
*/
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -16,10 +16,12 @@ 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 java.io.IOException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -128,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>
|
||||
@@ -276,18 +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);
|
||||
|
||||
/**
|
||||
* Return the joins required to support predicates on the many properties.
|
||||
*/
|
||||
@@ -639,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();
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -66,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;
|
||||
|
||||
@@ -152,6 +153,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private final JsonContext jsonContext;
|
||||
|
||||
private final DocumentStore documentStore;
|
||||
|
||||
private final MetaInfoManager metaInfoManager;
|
||||
|
||||
/**
|
||||
@@ -219,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();
|
||||
@@ -232,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);
|
||||
|
||||
@@ -902,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?");
|
||||
}
|
||||
@@ -912,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
|
||||
@@ -947,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:
|
||||
@@ -959,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1042,15 +1048,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
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);
|
||||
}
|
||||
|
||||
private <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();
|
||||
@@ -1063,7 +1066,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
query.setOrigin(createCallStack());
|
||||
}
|
||||
|
||||
OrmQueryRequest<T> request = new OrmQueryRequest<T>(this, queryEngine, query, desc, (SpiTransaction) t);
|
||||
OrmQueryRequest<T> request = new OrmQueryRequest<T>(this, queryEngine, query, (SpiTransaction) t);
|
||||
request.prepareQuery();
|
||||
|
||||
return request;
|
||||
@@ -1073,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) {
|
||||
@@ -1084,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
|
||||
@@ -1096,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1126,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();
|
||||
@@ -1340,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);
|
||||
}
|
||||
|
||||
@@ -1397,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();
|
||||
@@ -2011,6 +2019,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
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.
|
||||
*/
|
||||
@@ -2113,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
|
||||
|
||||
@@ -12,6 +12,7 @@ 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.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,6 +101,8 @@ public class InternalConfiguration {
|
||||
|
||||
private final JsonFactory jsonFactory;
|
||||
|
||||
private final DocStoreFactory docStoreFactory;
|
||||
|
||||
/**
|
||||
* List of plugins (that ultimately the DefaultServer configures late in construction).
|
||||
*/
|
||||
@@ -109,6 +112,7 @@ public class InternalConfiguration {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,11 +68,9 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
/**
|
||||
* 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;
|
||||
@@ -133,6 +131,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
return loadContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUseDocStore() {
|
||||
return query.isUseDocStore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run BeanQueryAdapter preQuery() if needed.
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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.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;
|
||||
|
||||
@@ -240,7 +251,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<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,10 +357,15 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<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 LinkedHashSet<String> defaultSelectClauseSet;
|
||||
|
||||
@@ -440,12 +460,15 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<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, BeanType<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, BeanType<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() {
|
||||
@@ -868,6 +904,84 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<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);
|
||||
}
|
||||
@@ -1219,10 +1333,24 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<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, BeanType<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()) {
|
||||
@@ -1328,6 +1456,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<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, BeanType<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, BeanType<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, BeanType<T> {
|
||||
* instead.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
@@ -1512,6 +1652,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<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, BeanType<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, BeanType<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, BeanType<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, BeanType<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, BeanType<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, BeanType<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, BeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanProperty getIdProperty() {
|
||||
return idProperty;
|
||||
}
|
||||
@@ -2442,6 +2632,14 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<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, BeanType<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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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,6 +186,21 @@ 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.
|
||||
*/
|
||||
@@ -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) {
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+104
-1
@@ -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;
|
||||
@@ -53,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.
|
||||
*/
|
||||
@@ -166,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;
|
||||
}
|
||||
|
||||
@@ -234,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++) {
|
||||
@@ -931,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.
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -26,6 +27,22 @@ class AllEqualsExpression extends NonPrepareExpression {
|
||||
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) {
|
||||
|
||||
@@ -4,6 +4,8 @@ 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 {
|
||||
|
||||
private static final long serialVersionUID = 2078918165221454910L;
|
||||
@@ -20,6 +22,11 @@ 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);
|
||||
|
||||
+11
@@ -8,6 +8,8 @@ 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.
|
||||
*/
|
||||
@@ -31,6 +33,15 @@ class BetweenPropertyExpression extends NonPrepareExpression {
|
||||
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) {
|
||||
|
||||
|
||||
+17
@@ -5,6 +5,8 @@ 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,21 @@ 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) {
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.avaje.ebean.ExampleExpression;
|
||||
@@ -94,6 +95,17 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
if (!list.isEmpty()) {
|
||||
context.writeBoolMustStart();
|
||||
for (SpiExpression expr : list) {
|
||||
expr.writeElastic(context);
|
||||
}
|
||||
context.writeBoolEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiExpression copyForPlanKey() {
|
||||
return new DefaultExampleExpression(list);
|
||||
|
||||
+2
-2
@@ -306,7 +306,7 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
*/
|
||||
@Override
|
||||
public Expression exists(Query<?> subQuery) {
|
||||
return new ExistsExpression((SpiQuery<?>) subQuery, false);
|
||||
return new ExistsQueryExpression((SpiQuery<?>) subQuery, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -314,7 +314,7 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
*/
|
||||
@Override
|
||||
public Expression notExists(Query<?> subQuery) {
|
||||
return new ExistsExpression((SpiQuery<?>) subQuery, true);
|
||||
return new ExistsQueryExpression((SpiQuery<?>) subQuery, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+39
-11
@@ -10,6 +10,7 @@ import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -59,6 +60,33 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
this(null, null, null, new ArrayList<SpiExpression>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
writeElastic(context, null);
|
||||
}
|
||||
|
||||
public void writeElastic(ElasticExpressionContext context, SpiExpression idEquals) throws IOException {
|
||||
|
||||
int size = list.size();
|
||||
if (size == 1 && idEquals == null) {
|
||||
// only 1 expression - skip bool must
|
||||
list.get(0).writeElastic(context);
|
||||
} else if (size == 0 && idEquals != null) {
|
||||
// only idEquals - skip bool must
|
||||
idEquals.writeElastic(context);
|
||||
} else {
|
||||
// bool must wrap all the children
|
||||
context.writeBoolMustStart();
|
||||
if (idEquals != null) {
|
||||
idEquals.writeElastic(context);
|
||||
}
|
||||
for (int i = 0; i < size; i++) {
|
||||
list.get(i).writeElastic(context);
|
||||
}
|
||||
context.writeBoolEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiExpressionList<?> trimPath(int prefixTrim) {
|
||||
throw new RuntimeException("Only allowed on FilterExpressionList");
|
||||
@@ -382,7 +410,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return false;
|
||||
}
|
||||
|
||||
DefaultExpressionList<?> that = (DefaultExpressionList<?>)other;
|
||||
DefaultExpressionList<?> that = (DefaultExpressionList<?>) other;
|
||||
if (list.size() != that.list.size()) {
|
||||
return false;
|
||||
}
|
||||
@@ -396,7 +424,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
DefaultExpressionList<?> that = (DefaultExpressionList<?>)other;
|
||||
DefaultExpressionList<?> that = (DefaultExpressionList<?>) other;
|
||||
if (list.size() != that.list.size()) {
|
||||
return false;
|
||||
}
|
||||
@@ -421,16 +449,16 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
* Path does not exist - for the given path in a JSON document.
|
||||
*/
|
||||
@Override
|
||||
public ExpressionList<T> jsonNotExists(String propertyName, String path){
|
||||
public ExpressionList<T> jsonNotExists(String propertyName, String path) {
|
||||
add(expr.jsonNotExists(propertyName, path));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Equal to expression for the value at the given path in the JSON document.
|
||||
*/
|
||||
@Override
|
||||
public ExpressionList<T> jsonEqualTo(String propertyName, String path, Object value){
|
||||
public ExpressionList<T> jsonEqualTo(String propertyName, String path, Object value) {
|
||||
add(expr.jsonEqualTo(propertyName, path, value));
|
||||
return this;
|
||||
}
|
||||
@@ -439,7 +467,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
* Not Equal to - for the given path in a JSON document.
|
||||
*/
|
||||
@Override
|
||||
public ExpressionList<T> jsonNotEqualTo(String propertyName, String path, Object val){
|
||||
public ExpressionList<T> jsonNotEqualTo(String propertyName, String path, Object val) {
|
||||
add(expr.jsonNotEqualTo(propertyName, path, val));
|
||||
return this;
|
||||
}
|
||||
@@ -448,7 +476,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
* Greater than - for the given path in a JSON document.
|
||||
*/
|
||||
@Override
|
||||
public ExpressionList<T> jsonGreaterThan(String propertyName, String path, Object val){
|
||||
public ExpressionList<T> jsonGreaterThan(String propertyName, String path, Object val) {
|
||||
add(expr.jsonGreaterThan(propertyName, path, val));
|
||||
return this;
|
||||
}
|
||||
@@ -457,7 +485,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
* Greater than or equal to - for the given path in a JSON document.
|
||||
*/
|
||||
@Override
|
||||
public ExpressionList<T> jsonGreaterOrEqual(String propertyName, String path, Object val){
|
||||
public ExpressionList<T> jsonGreaterOrEqual(String propertyName, String path, Object val) {
|
||||
add(expr.jsonGreaterOrEqual(propertyName, path, val));
|
||||
return this;
|
||||
}
|
||||
@@ -466,7 +494,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
* Less than - for the given path in a JSON document.
|
||||
*/
|
||||
@Override
|
||||
public ExpressionList<T> jsonLessThan(String propertyName, String path, Object val){
|
||||
public ExpressionList<T> jsonLessThan(String propertyName, String path, Object val) {
|
||||
add(expr.jsonLessThan(propertyName, path, val));
|
||||
return this;
|
||||
}
|
||||
@@ -475,7 +503,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
* Less than or equal to - for the given path in a JSON document.
|
||||
*/
|
||||
@Override
|
||||
public ExpressionList<T> jsonLessOrEqualTo(String propertyName, String path, Object val){
|
||||
public ExpressionList<T> jsonLessOrEqualTo(String propertyName, String path, Object val) {
|
||||
add(expr.jsonLessOrEqualTo(propertyName, path, val));
|
||||
return this;
|
||||
}
|
||||
@@ -484,7 +512,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
* Between - for the given path in a JSON document.
|
||||
*/
|
||||
@Override
|
||||
public ExpressionList<T> jsonBetween(String propertyName, String path, Object lowerValue, Object upperValue){
|
||||
public ExpressionList<T> jsonBetween(String propertyName, String path, Object lowerValue, Object upperValue) {
|
||||
add(expr.jsonBetween(propertyName, path, lowerValue, upperValue));
|
||||
return this;
|
||||
}
|
||||
|
||||
+303
@@ -0,0 +1,303 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.OrderBy;
|
||||
import com.avaje.ebean.plugin.BeanType;
|
||||
import com.avaje.ebean.plugin.ExpressionPath;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Context for writing elastic search expressions.
|
||||
*/
|
||||
public class ElasticExpressionContext {
|
||||
|
||||
public static final String MUST = "must";
|
||||
public static final String SHOULD = "should";
|
||||
public static final String MUST_NOT = "must_not";
|
||||
public static final String BOOL = "bool";
|
||||
public static final String TERM = "term";
|
||||
public static final String RANGE = "range";
|
||||
public static final String TERMS = "terms";
|
||||
public static final String IDS = "ids";
|
||||
public static final String VALUES = "values";
|
||||
public static final String PREFIX = "prefix";
|
||||
public static final String MATCH = "match";
|
||||
public static final String WILDCARD = "wildcard";
|
||||
public static final String EXISTS = "exists";
|
||||
public static final String FIELD = "field";
|
||||
|
||||
private final JsonGenerator json;
|
||||
|
||||
private final BeanType<?> desc;
|
||||
|
||||
private String currentNestedPath;
|
||||
|
||||
public ElasticExpressionContext(JsonGenerator json, BeanType<?> desc) {
|
||||
this.json = json;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JsonGenerator.
|
||||
*/
|
||||
public JsonGenerator json() {
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the JsonGenerator buffer.
|
||||
*/
|
||||
public void flush() throws IOException {
|
||||
endNested();
|
||||
json.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the path contains a many.
|
||||
*/
|
||||
public boolean containsMany(String path) {
|
||||
ExpressionPath elPath = desc.getExpressionPath(path);
|
||||
return elPath == null || elPath.containsMany();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an associated 'raw' property given the property name.
|
||||
*/
|
||||
private String rawProperty(String propertyName) {
|
||||
return desc.docStore().rawProperty(propertyName);
|
||||
}
|
||||
|
||||
public void writeBoolStart(boolean conjunction) throws IOException {
|
||||
writeBoolStart((conjunction) ? MUST : SHOULD);
|
||||
}
|
||||
|
||||
public void writeBoolMustStart() throws IOException {
|
||||
writeBoolStart(MUST);
|
||||
}
|
||||
|
||||
public void writeBoolMustNotStart() throws IOException {
|
||||
writeBoolStart(MUST_NOT);
|
||||
}
|
||||
|
||||
private void writeBoolStart(String type) throws IOException {
|
||||
endNested();
|
||||
json.writeStartObject();
|
||||
json.writeObjectFieldStart(BOOL);
|
||||
json.writeArrayFieldStart(type);
|
||||
}
|
||||
|
||||
public void writeBoolEnd() throws IOException {
|
||||
json.writeEndArray();
|
||||
json.writeEndObject();
|
||||
json.writeEndObject();
|
||||
}
|
||||
|
||||
public void writeTerm(String propertyName, Object value) throws IOException {
|
||||
|
||||
writeRawType(TERM, rawProperty(propertyName), value);
|
||||
}
|
||||
|
||||
public void writeRange(String propertyName, String rangeType, Object value) throws IOException {
|
||||
|
||||
prepareNestedPath(propertyName);
|
||||
json.writeStartObject();
|
||||
json.writeObjectFieldStart(RANGE);
|
||||
json.writeObjectFieldStart(rawProperty(propertyName));
|
||||
json.writeFieldName(rangeType);
|
||||
json.writeObject(value);
|
||||
json.writeEndObject();
|
||||
json.writeEndObject();
|
||||
json.writeEndObject();
|
||||
}
|
||||
|
||||
public void writeRange(String propertyName, Op lowOp, Object valueLow, Op highOp, Object valueHigh) throws IOException {
|
||||
|
||||
prepareNestedPath(propertyName);
|
||||
json.writeStartObject();
|
||||
json.writeObjectFieldStart(RANGE);
|
||||
json.writeObjectFieldStart(rawProperty(propertyName));
|
||||
json.writeFieldName(lowOp.docExp());
|
||||
json.writeObject(valueLow);
|
||||
json.writeFieldName(highOp.docExp());
|
||||
json.writeObject(valueHigh);
|
||||
json.writeEndObject();
|
||||
json.writeEndObject();
|
||||
json.writeEndObject();
|
||||
}
|
||||
|
||||
public void writeTerms(String propertyName, Object[] values) throws IOException {
|
||||
|
||||
prepareNestedPath(propertyName);
|
||||
json.writeStartObject();
|
||||
json.writeObjectFieldStart(TERMS);
|
||||
json.writeArrayFieldStart(rawProperty(propertyName));
|
||||
for (Object value : values) {
|
||||
json.writeObject(value);
|
||||
}
|
||||
json.writeEndArray();
|
||||
json.writeEndObject();
|
||||
json.writeEndObject();
|
||||
}
|
||||
|
||||
|
||||
public void writeIds(List<?> idList) throws IOException {
|
||||
|
||||
endNested();
|
||||
json.writeStartObject();
|
||||
json.writeObjectFieldStart(IDS);
|
||||
json.writeArrayFieldStart(VALUES);
|
||||
for (Object id : idList) {
|
||||
json.writeObject(id);
|
||||
}
|
||||
json.writeEndArray();
|
||||
json.writeEndObject();
|
||||
json.writeEndObject();
|
||||
}
|
||||
|
||||
public void writeId(Object value) throws IOException {
|
||||
|
||||
List<Object> ids = new ArrayList<Object>(1);
|
||||
ids.add(value);
|
||||
writeIds(ids);
|
||||
}
|
||||
|
||||
public void writeSuffix(String propertyName, String value) {
|
||||
throw new IllegalArgumentException("Not implemented yet. Could search for a mapped 'reversed' property and do prefix query");
|
||||
}
|
||||
|
||||
public void writePrefix(String propertyName, String value) throws IOException {
|
||||
// use analysed field
|
||||
prepareNestedPath(propertyName);
|
||||
writeRawType(PREFIX, propertyName, value);
|
||||
}
|
||||
|
||||
public void writeMatch(String propertyName, String value) throws IOException {
|
||||
// use analysed field
|
||||
prepareNestedPath(propertyName);
|
||||
writeRawType(MATCH, propertyName, value);
|
||||
}
|
||||
|
||||
public void writeWildcard(String propertyName, String value) throws IOException {
|
||||
prepareNestedPath(propertyName);
|
||||
writeRawType(WILDCARD, propertyName, value);
|
||||
}
|
||||
|
||||
public void writeRaw(String jsonExpression) throws IOException {
|
||||
json.writeRaw(jsonExpression);
|
||||
}
|
||||
|
||||
public void writeExists(boolean notNull, String propertyName) throws IOException {
|
||||
|
||||
prepareNestedPath(propertyName);
|
||||
if (!notNull) {
|
||||
writeBoolMustNotStart();
|
||||
}
|
||||
writeExists(propertyName);
|
||||
if (!notNull) {
|
||||
writeBoolEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeExists(String propertyName) throws IOException {
|
||||
writeRawType(EXISTS, FIELD, propertyName);
|
||||
}
|
||||
|
||||
private void writeRawType(String type, String propertyName, Object value) throws IOException {
|
||||
|
||||
prepareNestedPath(propertyName);
|
||||
json.writeStartObject();
|
||||
json.writeObjectFieldStart(type);
|
||||
json.writeFieldName(propertyName);
|
||||
json.writeObject(value);
|
||||
json.writeEndObject();
|
||||
json.writeEndObject();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void writeSimple(Op type, String propertyName, Object value) throws IOException {
|
||||
|
||||
prepareNestedPath(propertyName);
|
||||
switch (type) {
|
||||
case EQ:
|
||||
writeTerm(propertyName, value);
|
||||
break;
|
||||
case NOT_EQ:
|
||||
writeBoolMustNotStart();
|
||||
writeTerm(propertyName, value);
|
||||
writeBoolEnd();
|
||||
break;
|
||||
case EXISTS:
|
||||
writeExists(true, propertyName);
|
||||
break;
|
||||
case NOT_EXISTS:
|
||||
writeExists(false, propertyName);
|
||||
break;
|
||||
case BETWEEN:
|
||||
throw new IllegalStateException("BETWEEN Not expected in SimpleExpression?");
|
||||
|
||||
default:
|
||||
writeRange(propertyName, type.docExp(), value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the query sort.
|
||||
*/
|
||||
public <T> void writeOrderBy(OrderBy<T> orderBy) throws IOException {
|
||||
|
||||
if (orderBy != null && !orderBy.isEmpty()) {
|
||||
json.writeArrayFieldStart("sort");
|
||||
for (OrderBy.Property property : orderBy.getProperties()) {
|
||||
json.writeStartObject();
|
||||
json.writeObjectFieldStart(rawProperty(property.getProperty()));
|
||||
json.writeStringField("order", property.isAscending() ? "asc" : "desc");
|
||||
json.writeEndObject();
|
||||
json.writeEndObject();
|
||||
}
|
||||
json.writeEndArray();
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareNestedPath(String propName) throws IOException {
|
||||
ExpressionPath exprPath = desc.getExpressionPath(propName);
|
||||
if (exprPath != null && exprPath.containsMany()) {
|
||||
String[] manyPath = SplitName.splitBegin(propName);
|
||||
startNested(manyPath[0]);
|
||||
} else {
|
||||
endNested();
|
||||
}
|
||||
}
|
||||
|
||||
private void startNested(String nestedPath) throws IOException {
|
||||
|
||||
if (currentNestedPath != null) {
|
||||
if (currentNestedPath.equals(nestedPath)) {
|
||||
// just add to currentNestedPath
|
||||
return;
|
||||
} else {
|
||||
endNested();
|
||||
}
|
||||
}
|
||||
currentNestedPath = nestedPath;
|
||||
|
||||
json.writeStartObject();
|
||||
json.writeObjectFieldStart("nested");
|
||||
json.writeStringField("path", nestedPath);
|
||||
json.writeFieldName("filter");
|
||||
}
|
||||
|
||||
private void endNested() throws IOException {
|
||||
if (currentNestedPath != null) {
|
||||
currentNestedPath = null;
|
||||
//json.writeEndObject();
|
||||
json.writeEndObject();
|
||||
json.writeEndObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-7
@@ -1,5 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
@@ -13,7 +14,7 @@ import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.query.CQuery;
|
||||
|
||||
public class ExistsExpression implements SpiExpression {
|
||||
class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpression {
|
||||
|
||||
private static final long serialVersionUID = 666990277309851644L;
|
||||
|
||||
@@ -25,18 +26,23 @@ public class ExistsExpression implements SpiExpression {
|
||||
|
||||
protected String sql;
|
||||
|
||||
public ExistsExpression(SpiQuery<?> subQuery, boolean not) {
|
||||
public ExistsQueryExpression(SpiQuery<?> subQuery, boolean not) {
|
||||
this.subQuery = subQuery;
|
||||
this.not = not;
|
||||
}
|
||||
|
||||
ExistsExpression(boolean not, String sql , List<Object> bindParams) {
|
||||
ExistsQueryExpression(boolean not, String sql , List<Object> bindParams) {
|
||||
this.not = not;
|
||||
this.sql = sql;
|
||||
this.bindParams = bindParams;
|
||||
this.subQuery = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
throw new IllegalStateException("Not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
|
||||
@@ -60,7 +66,7 @@ public class ExistsExpression implements SpiExpression {
|
||||
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(ExistsExpression.class).add(not);
|
||||
builder.add(ExistsQueryExpression.class).add(not);
|
||||
builder.add(sql).add(bindParams.size());
|
||||
}
|
||||
|
||||
@@ -90,11 +96,11 @@ public class ExistsExpression implements SpiExpression {
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof ExistsExpression)) {
|
||||
if (!(other instanceof ExistsQueryExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ExistsExpression that = (ExistsExpression) other;
|
||||
ExistsQueryExpression that = (ExistsQueryExpression) other;
|
||||
return this.sql.equals(that.sql)
|
||||
&& this.not == that.not
|
||||
&& this.bindParams.size() == that.bindParams.size();
|
||||
@@ -102,7 +108,7 @@ public class ExistsExpression implements SpiExpression {
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
ExistsExpression that = (ExistsExpression) other;
|
||||
ExistsQueryExpression that = (ExistsQueryExpression) other;
|
||||
if (this.bindParams.size() != that.bindParams.size()) {
|
||||
return false;
|
||||
}
|
||||
@@ -7,6 +7,8 @@ import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Slightly redundant as Query.setId() ultimately also does the same job.
|
||||
*/
|
||||
@@ -20,6 +22,11 @@ class IdExpression extends NonPrepareExpression implements SpiExpression {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
context.writeId(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Always returns false.
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -27,6 +28,11 @@ public class IdInExpression extends NonPrepareExpression {
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
context.writeIds(idList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(SpiExpressionValidation validation) {
|
||||
// always valid
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
class InExpression extends AbstractExpression {
|
||||
@@ -28,6 +29,11 @@ class InExpression extends AbstractExpression {
|
||||
this.not = not;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
context.writeTerms(propName, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
|
||||
@@ -8,12 +8,13 @@ import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.query.CQuery;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* In expression using a sub query.
|
||||
*/
|
||||
class InQueryExpression extends AbstractExpression {
|
||||
class InQueryExpression extends AbstractExpression implements UnsupportedDocStoreExpression {
|
||||
|
||||
private static final long serialVersionUID = 666990277309851644L;
|
||||
|
||||
@@ -39,6 +40,11 @@ class InQueryExpression extends AbstractExpression {
|
||||
this.bindParams = bindParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
throw new IllegalStateException("Not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Generally speaking tests the value at a given path in the JSON document.
|
||||
* <p>
|
||||
@@ -57,6 +59,17 @@ class JsonPathExpression extends AbstractExpression {
|
||||
this.upperValue = upperValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
|
||||
String fullName = propName + "." + path;
|
||||
if (operator == Op.BETWEEN) {
|
||||
context.writeRange(fullName, Op.GT_EQ, value, Op.LT_EQ, upperValue);
|
||||
} else {
|
||||
context.writeSimple(operator, fullName, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(JsonPathExpression.class).add(propName).add(path).add(operator);
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -98,6 +99,17 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
this.exprList = exprList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
|
||||
context.writeBoolStart(!disjunction);
|
||||
List<SpiExpression> list = exprList.internalList();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
list.get(i).writeElastic(context);
|
||||
}
|
||||
context.writeBoolEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
|
||||
@@ -126,8 +138,7 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
|
||||
@Override
|
||||
public Junction<T> add(Expression item) {
|
||||
SpiExpression i = (SpiExpression) item;
|
||||
exprList.add(i);
|
||||
exprList.add(item);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -143,8 +154,7 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
List<SpiExpression> list = exprList.internalList();
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
SpiExpression item = list.get(i);
|
||||
item.addBindValues(request);
|
||||
list.get(i).addBindValues(request);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class LikeExpression extends AbstractExpression {
|
||||
|
||||
private static final long serialVersionUID = -5398151809111172380L;
|
||||
@@ -23,6 +25,36 @@ class LikeExpression extends AbstractExpression {
|
||||
this.val = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
|
||||
String paramVal = (caseInsensitive) ? val.toLowerCase() : val;
|
||||
switch (type) {
|
||||
case RAW:
|
||||
context.writeWildcard(propName, paramVal);
|
||||
break;
|
||||
|
||||
case STARTS_WITH:
|
||||
context.writePrefix(propName, paramVal);
|
||||
break;
|
||||
|
||||
case ENDS_WITH:
|
||||
context.writeSuffix(propName, paramVal);
|
||||
break;
|
||||
|
||||
case CONTAINS:
|
||||
context.writeMatch(propName, paramVal);
|
||||
break;
|
||||
|
||||
case EQUAL_TO:
|
||||
context.writeTerm(propName, paramVal);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new RuntimeException("LikeType " + type + " missed?");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A logical And or Or for joining two expressions.
|
||||
*/
|
||||
@@ -60,6 +62,16 @@ abstract class LogicExpression implements SpiExpression {
|
||||
this.expTwo = (SpiExpression) expTwo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
|
||||
boolean conjunction = joinType.equals(AND);
|
||||
context.writeBoolStart(conjunction);
|
||||
expOne.writeElastic(context);
|
||||
expTwo.writeElastic(context);
|
||||
context.writeBoolEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
expOne.containsMany(desc, manyWhereJoin);
|
||||
|
||||
@@ -8,6 +8,8 @@ import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Effectively an expression that has no effect.
|
||||
*/
|
||||
@@ -20,6 +22,10 @@ class NoopExpression implements SpiExpression {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins) {
|
||||
// nothing to do
|
||||
|
||||
@@ -9,6 +9,8 @@ import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
final class NotExpression implements SpiExpression {
|
||||
|
||||
private static final long serialVersionUID = 5648926732402355781L;
|
||||
@@ -22,6 +24,13 @@ final class NotExpression implements SpiExpression {
|
||||
this.exp = (SpiExpression) exp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
context.writeBoolMustNotStart();
|
||||
exp.writeElastic(context);
|
||||
context.writeBoolEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiExpression copyForPlanKey() {
|
||||
return new NotExpression(exp.copyForPlanKey());
|
||||
|
||||
@@ -5,6 +5,8 @@ import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* Slightly redundant as Query.setId() ultimately also does the same job.
|
||||
@@ -20,6 +22,11 @@ class NullExpression extends AbstractExpression {
|
||||
this.notNull = notNull;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
context.writeExists(notNull, propName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
|
||||
|
||||
@@ -8,54 +8,56 @@ public enum Op {
|
||||
/**
|
||||
* Exists (JSON).
|
||||
*/
|
||||
EXISTS(" is not null "),
|
||||
EXISTS(" is not null ", ""),
|
||||
|
||||
/**
|
||||
* Not Exists (JSON).
|
||||
*/
|
||||
NOT_EXISTS(" is null "),
|
||||
NOT_EXISTS(" is null ", ""),
|
||||
|
||||
/**
|
||||
* Between (JSON).
|
||||
*/
|
||||
BETWEEN(" between ? and ? "),
|
||||
BETWEEN(" between ? and ? ", ""),
|
||||
|
||||
/**
|
||||
* Equal to
|
||||
*/
|
||||
EQ(" = ? "),
|
||||
EQ(" = ? ", ""),
|
||||
|
||||
/**
|
||||
* Not equal to.
|
||||
*/
|
||||
NOT_EQ(" <> ? "),
|
||||
NOT_EQ(" <> ? ", ""),
|
||||
|
||||
/**
|
||||
*
|
||||
* Less than.
|
||||
*/
|
||||
|
||||
LT(" < ? "),
|
||||
LT(" < ? ", "lt"),
|
||||
|
||||
/**
|
||||
* Less than or equal to.
|
||||
*/
|
||||
LT_EQ(" <= ? "),
|
||||
LT_EQ(" <= ? ", "lte"),
|
||||
|
||||
/**
|
||||
* Greater than.
|
||||
*/
|
||||
GT(" > ? "),
|
||||
GT(" > ? ", "gt"),
|
||||
|
||||
/**
|
||||
* Greater than or equal to.
|
||||
*/
|
||||
GT_EQ(" >= ? ");
|
||||
GT_EQ(" >= ? ", "gte");
|
||||
|
||||
final String exp;
|
||||
|
||||
Op(String exp) {
|
||||
final String docExp;
|
||||
|
||||
Op(String exp, String docExp) {
|
||||
this.exp = exp;
|
||||
this.docExp = docExp;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,4 +66,11 @@ public enum Op {
|
||||
public String bind() {
|
||||
return exp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the doc store expression.
|
||||
*/
|
||||
public String docExp() {
|
||||
return docExp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class RawExpression extends NonPrepareExpression {
|
||||
|
||||
private static final long serialVersionUID = 7973903141340334606L;
|
||||
@@ -20,6 +22,11 @@ class RawExpression extends NonPrepareExpression {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
context.writeRaw(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SimpleExpression extends AbstractExpression {
|
||||
|
||||
private static final long serialVersionUID = -382881395755603790L;
|
||||
@@ -20,6 +22,16 @@ public class SimpleExpression extends AbstractExpression {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
|
||||
if (type == Op.BETWEEN) {
|
||||
throw new IllegalStateException("BETWEEN Not expected in SimpleExpression?");
|
||||
}
|
||||
|
||||
context.writeSimple(type, propName, value);
|
||||
}
|
||||
|
||||
public final String getPropName() {
|
||||
return propName;
|
||||
}
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
/**
|
||||
* Marked interface for expressions unsupported in doc store.
|
||||
*/
|
||||
public interface UnsupportedDocStoreExpression {
|
||||
}
|
||||
@@ -45,7 +45,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
|
||||
|
||||
protected void configureQuery(SpiQuery<?> query, String lazyLoadProperty) {
|
||||
|
||||
parent.propagateQueryState(query);
|
||||
parent.propagateQueryState(query, desc.isDocStoreMapped());
|
||||
query.setParentNode(objectGraphNode);
|
||||
query.setLazyLoadProperty(lazyLoadProperty);
|
||||
if (queryProps != null) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.loadcontext;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.CallStack;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
@@ -45,6 +46,7 @@ public class DLoadContext implements LoadContext {
|
||||
private final boolean disableLazyLoading;
|
||||
private final boolean disableReadAudit;
|
||||
private final boolean includeSoftDeletes;
|
||||
protected final boolean useDocStore;
|
||||
|
||||
/**
|
||||
* The path relative to the root of the object graph.
|
||||
@@ -59,6 +61,34 @@ public class DLoadContext implements LoadContext {
|
||||
|
||||
private List<OrmQueryProperties> secQuery;
|
||||
|
||||
/**
|
||||
* Construct for use with JSON marshalling (doc store).
|
||||
*/
|
||||
public DLoadContext(BeanDescriptor<?> rootDescriptor, PersistenceContext persistenceContext) {
|
||||
|
||||
this.useDocStore = true;
|
||||
this.rootDescriptor = rootDescriptor;
|
||||
this.ebeanServer = rootDescriptor.getEbeanServer();
|
||||
this.persistenceContext = persistenceContext;
|
||||
this.origin = initOrigin();
|
||||
this.defaultBatchSize = 100;
|
||||
this.excludeBeanCache = false;
|
||||
this.asDraft = false;
|
||||
this.asOf = null;
|
||||
this.readOnly = false;
|
||||
this.disableLazyLoading = false;
|
||||
this.disableReadAudit = false;
|
||||
this.includeSoftDeletes = false;
|
||||
this.relativePath = null;
|
||||
this.useProfiling = false;
|
||||
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
|
||||
}
|
||||
|
||||
private ObjectGraphOrigin initOrigin() {
|
||||
CallStack callStack = ebeanServer.createCallStack();
|
||||
return new ObjectGraphOrigin(0, callStack, rootDescriptor.getFullName());
|
||||
}
|
||||
|
||||
public DLoadContext(OrmQueryRequest<?> request, SpiQuerySecondary secondaryQueries) {
|
||||
|
||||
this.persistenceContext = request.getPersistenceContext();
|
||||
@@ -67,6 +97,7 @@ public class DLoadContext implements LoadContext {
|
||||
this.rootDescriptor = request.getBeanDescriptor();
|
||||
|
||||
SpiQuery<?> query = request.getQuery();
|
||||
this.useDocStore = query.isUseDocStore();
|
||||
this.asOf = query.getAsOf();
|
||||
this.asDraft = query.isAsDraft();
|
||||
this.includeSoftDeletes = query.isIncludeSoftDeletes();
|
||||
@@ -300,7 +331,10 @@ public class DLoadContext implements LoadContext {
|
||||
/**
|
||||
* Propagate the original query settings (draft, asOf etc) to the secondary queries.
|
||||
*/
|
||||
public void propagateQueryState(SpiQuery<?> query) {
|
||||
public void propagateQueryState(SpiQuery<?> query, boolean docStoreMapped) {
|
||||
if (useDocStore && docStoreMapped) {
|
||||
query.setUseDocStore(true);
|
||||
}
|
||||
if (readOnly != null) {
|
||||
query.setReadOnly(readOnly);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
|
||||
|
||||
public void configureQuery(SpiQuery<?> query) {
|
||||
|
||||
parent.propagateQueryState(query);
|
||||
parent.propagateQueryState(query, desc.isDocStoreMapped());
|
||||
query.setParentNode(objectGraphNode);
|
||||
if (queryProps != null) {
|
||||
queryProps.configureBeanQuery(query);
|
||||
@@ -129,6 +129,11 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
|
||||
this.list = new ArrayList<BeanCollection<?>>(batchSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUseDocStore() {
|
||||
return context.parent.useDocStore;
|
||||
}
|
||||
|
||||
public int getBatchSize() {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.avaje.ebeaninternal.server.persist.dml;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
@@ -17,7 +16,6 @@ import javax.persistence.OptimisticLockException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Base class for Handler implementations.
|
||||
@@ -48,7 +46,10 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
|
||||
|
||||
protected String sql;
|
||||
|
||||
protected ArrayList<UpdateGenValue> updateGenValues;
|
||||
/**
|
||||
* The generated value for the @Version property. Must be set after where clause is bound.
|
||||
*/
|
||||
protected Object versionValue;
|
||||
|
||||
protected DmlHandler(PersistRequestBean<?> persistRequest, boolean emptyStringToNull) {
|
||||
this.now = System.currentTimeMillis();
|
||||
@@ -224,11 +225,8 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public void registerUpdateGenValue(BeanProperty prop, EntityBean bean, Object value) {
|
||||
if (updateGenValues == null) {
|
||||
updateGenValues = new ArrayList<UpdateGenValue>();
|
||||
}
|
||||
updateGenValues.add(new UpdateGenValue(prop, bean, value));
|
||||
public void registerGeneratedVersion(Object versionValue) {
|
||||
this.versionValue = versionValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,11 +234,8 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
|
||||
* clause has been bound.
|
||||
*/
|
||||
public void setUpdateGenValues() {
|
||||
if (updateGenValues != null) {
|
||||
for (int i = 0; i < updateGenValues.size(); i++) {
|
||||
UpdateGenValue updGenVal = updateGenValues.get(i);
|
||||
updGenVal.setValue();
|
||||
}
|
||||
if (versionValue != null) {
|
||||
persistRequest.setVersionValue(versionValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,30 +277,4 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
|
||||
return stmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hold the values from GeneratedValue that need to be set to the bean
|
||||
* property after the where clause has been built.
|
||||
*/
|
||||
private static final class UpdateGenValue {
|
||||
|
||||
private final BeanProperty property;
|
||||
|
||||
private final EntityBean bean;
|
||||
|
||||
private final Object value;
|
||||
|
||||
private UpdateGenValue(BeanProperty property, EntityBean bean, Object value) {
|
||||
this.property = property;
|
||||
this.bean = bean;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value to the bean property.
|
||||
*/
|
||||
private void setValue() {
|
||||
// support PropertyChangeSupport
|
||||
property.setValueIntercept(bean, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -42,13 +42,14 @@ public class BindablePropertyUpdateGenerated extends BindableProperty {
|
||||
// generated value should be the correct type
|
||||
request.bind(value, prop);
|
||||
|
||||
// only register the update value if it was included
|
||||
// in the bean in the first place
|
||||
if (request.getPersistRequest().isLoadedProperty(prop)) {
|
||||
//if (request.isIncluded(prop)) {
|
||||
// need to set the generated value to the bean later
|
||||
// after the where clause has been generated
|
||||
request.registerUpdateGenValue(prop, bean, value);
|
||||
if (prop.isVersion()) {
|
||||
if (request.getPersistRequest().isLoadedProperty(prop)) {
|
||||
// set to the bean after the where clause has been generated
|
||||
request.registerGeneratedVersion(value);
|
||||
}
|
||||
} else {
|
||||
// @WhenModified set without invoking interception
|
||||
prop.setValueChanged(bean, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,5 +61,4 @@ public class BindablePropertyUpdateGenerated extends BindableProperty {
|
||||
request.appendColumn(prop.getDbColumn());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public interface BindableRequest {
|
||||
* Register the value from a update GeneratedValue. This can only be set to
|
||||
* the bean property after the where clause has bean built.
|
||||
*/
|
||||
void registerUpdateGenValue(BeanProperty prop, EntityBean bean, Object value);
|
||||
void registerGeneratedVersion(Object value);
|
||||
|
||||
/**
|
||||
* Return the original PersistRequest.
|
||||
|
||||
@@ -194,7 +194,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
|
||||
this.queryPlan = queryPlan;
|
||||
this.query = request.getQuery();
|
||||
this.queryMode = query.getMode();
|
||||
this.lazyLoadManyProperty = query.getLazyLoadForParentsProperty();
|
||||
this.lazyLoadManyProperty = query.getLazyLoadMany();
|
||||
|
||||
this.readOnly = request.isReadOnly();
|
||||
|
||||
|
||||
@@ -259,7 +259,7 @@ public class SqlTreeBuilder {
|
||||
buildExtraJoins(desc, myList);
|
||||
|
||||
// Optional many property for lazy loading query
|
||||
BeanPropertyAssocMany<?> lazyLoadMany = (query == null) ? null : query.getLazyLoadForParentsProperty();
|
||||
BeanPropertyAssocMany<?> lazyLoadMany = (query == null) ? null : query.getLazyLoadMany();
|
||||
boolean withId = !rawNoId && !subQuery && (query == null || !query.isDistinct());
|
||||
return new SqlTreeNodeRoot(desc, props, myList, withId, includeJoin, lazyLoadMany, SpiQuery.TemporalMode.of(query), disableLazyLoad);
|
||||
|
||||
|
||||
@@ -232,7 +232,6 @@ public class SqlTreeNodeBean implements SqlTreeNode {
|
||||
|
||||
PersistenceContext persistenceContext = (!readId || temporalVersions) ? null : ctx.getPersistenceContext();
|
||||
|
||||
boolean newBean = false;
|
||||
if (readId) {
|
||||
Object id = localIdBinder.readSet(ctx, localBean);
|
||||
if (id == null) {
|
||||
@@ -243,7 +242,6 @@ public class SqlTreeNodeBean implements SqlTreeNode {
|
||||
contextBean = (EntityBean) persistenceContext.putIfAbsent(id, localBean);
|
||||
if (contextBean == null) {
|
||||
// bean just added to the persistenceContext
|
||||
newBean = true;
|
||||
contextBean = localBean;
|
||||
} else {
|
||||
// bean already exists in persistenceContext
|
||||
|
||||
@@ -10,6 +10,8 @@ import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebean.event.readaudit.ReadEvent;
|
||||
import com.avaje.ebean.plugin.BeanType;
|
||||
import com.avaje.ebean.FetchPath;
|
||||
import com.avaje.ebean.text.json.JsonContext;
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.api.CQueryPlanKey;
|
||||
import com.avaje.ebeaninternal.api.HashQuery;
|
||||
@@ -25,11 +27,15 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.DRawSqlSelect;
|
||||
import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
import com.avaje.ebeaninternal.server.expression.DefaultExpressionList;
|
||||
import com.avaje.ebeaninternal.server.expression.ElasticExpressionContext;
|
||||
import com.avaje.ebeaninternal.server.expression.SimpleExpression;
|
||||
import com.avaje.ebeaninternal.server.query.CancelableQuery;
|
||||
import com.avaje.ebeaninternal.server.expression.DefaultExpressionList;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
@@ -42,29 +48,27 @@ import java.util.Set;
|
||||
*/
|
||||
public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
private static final long serialVersionUID = 6838006264714672460L;
|
||||
|
||||
private final Class<T> beanType;
|
||||
|
||||
private transient final EbeanServer server;
|
||||
private final BeanDescriptor<T> beanDescriptor;
|
||||
|
||||
private transient BeanCollectionTouched beanCollectionTouched;
|
||||
private final EbeanServer server;
|
||||
|
||||
private transient final ExpressionFactory expressionFactory;
|
||||
private BeanCollectionTouched beanCollectionTouched;
|
||||
|
||||
private final ExpressionFactory expressionFactory;
|
||||
|
||||
/**
|
||||
* For lazy loading of ManyToMany we need to add a join to the intersection table. This is that
|
||||
* join to the intersection table.
|
||||
*/
|
||||
private transient TableJoin includeTableJoin;
|
||||
private TableJoin includeTableJoin;
|
||||
|
||||
private transient ProfilingListener profilingListener;
|
||||
|
||||
private transient BeanDescriptor<?> beanDescriptor;
|
||||
private ProfilingListener profilingListener;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
private transient CancelableQuery cancelableQuery;
|
||||
private CancelableQuery cancelableQuery;
|
||||
|
||||
/**
|
||||
* The name of the query.
|
||||
@@ -235,14 +239,17 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
*/
|
||||
private CQueryPlanKey queryPlanKey;
|
||||
|
||||
private transient PersistenceContext persistenceContext;
|
||||
private PersistenceContext persistenceContext;
|
||||
|
||||
private ManyWhereJoins manyWhereJoins;
|
||||
|
||||
private RawSql rawSql;
|
||||
|
||||
public DefaultOrmQuery(Class<T> beanType, EbeanServer server, ExpressionFactory expressionFactory, String query) {
|
||||
this.beanType = beanType;
|
||||
private boolean useDocStore;
|
||||
|
||||
public DefaultOrmQuery(BeanDescriptor<T> desc, EbeanServer server, ExpressionFactory expressionFactory, String query) {
|
||||
this.beanDescriptor = desc;
|
||||
this.beanType = desc.getBeanType();
|
||||
this.server = server;
|
||||
this.expressionFactory = expressionFactory;
|
||||
this.detail = new OrmQueryDetail();
|
||||
@@ -255,10 +262,11 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
/**
|
||||
* Additional supply a query which is parsed.
|
||||
*/
|
||||
public DefaultOrmQuery(Class<T> beanType, EbeanServer server, ExpressionFactory expressionFactory,
|
||||
public DefaultOrmQuery(BeanDescriptor<T> desc, EbeanServer server, ExpressionFactory expressionFactory,
|
||||
DeployNamedQuery namedQuery) throws PersistenceException {
|
||||
|
||||
this.beanType = beanType;
|
||||
this.beanDescriptor = desc;
|
||||
this.beanType = desc.getBeanType();
|
||||
this.server = server;
|
||||
this.expressionFactory = expressionFactory;
|
||||
this.detail = new OrmQueryDetail();
|
||||
@@ -282,6 +290,88 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public String asElasticQuery() {
|
||||
|
||||
StringWriter sw = new StringWriter(200);
|
||||
JsonContext json = server.json();
|
||||
|
||||
JsonGenerator generator = json.createGenerator(sw);
|
||||
|
||||
BeanType<T> beanType = server.getPluginApi().getBeanType(this.beanType);
|
||||
ElasticExpressionContext context = new ElasticExpressionContext(generator, beanType);
|
||||
|
||||
try {
|
||||
writeElastic(context);
|
||||
context.flush();
|
||||
return sw.toString();
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
|
||||
JsonGenerator json = context.json();
|
||||
json.writeStartObject();
|
||||
if (firstRow > 0) {
|
||||
json.writeNumberField("from", firstRow);
|
||||
}
|
||||
if (maxRows > 0) {
|
||||
json.writeNumberField("size", maxRows);
|
||||
}
|
||||
|
||||
detail.writeElastic(context);
|
||||
context.writeOrderBy(orderBy);
|
||||
|
||||
json.writeFieldName("query");
|
||||
json.writeStartObject();
|
||||
|
||||
SpiExpression idEquals = null;
|
||||
if (id != null) {
|
||||
idEquals = (SpiExpression)expressionFactory.idEq(id);
|
||||
}
|
||||
|
||||
boolean hasWhere = (whereExpressions != null && !whereExpressions.isEmpty());
|
||||
if (idEquals != null || hasWhere) {
|
||||
json.writeFieldName("filtered");
|
||||
json.writeStartObject();
|
||||
json.writeFieldName("filter");
|
||||
if (hasWhere) {
|
||||
whereExpressions.writeElastic(context, idEquals);
|
||||
} else {
|
||||
idEquals.writeElastic(context);
|
||||
}
|
||||
json.writeEndObject();
|
||||
} else {
|
||||
json.writeObjectFieldStart("match_all");
|
||||
json.writeEndObject();
|
||||
}
|
||||
json.writeEndObject();
|
||||
json.writeEndObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanDescriptor<T> getBeanDescriptor() {
|
||||
return beanDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoTunable() {
|
||||
return beanDescriptor.isAutoTunable() && !isSqlSelect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> setUseDocStore(boolean useDocStore) {
|
||||
this.useDocStore = useDocStore;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUseDocStore() {
|
||||
return useDocStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> apply(FetchPath fetchPath) {
|
||||
fetchPath.apply(this);
|
||||
@@ -337,13 +427,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the BeanDescriptor for the root type of this query.
|
||||
*/
|
||||
public void setBeanDescriptor(BeanDescriptor<?> beanDescriptor) {
|
||||
this.beanDescriptor = beanDescriptor;
|
||||
}
|
||||
|
||||
public RawSql getRawSql() {
|
||||
return rawSql;
|
||||
}
|
||||
@@ -559,7 +642,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
public DefaultOrmQuery<T> copy(EbeanServer server) {
|
||||
|
||||
DefaultOrmQuery<T> copy = new DefaultOrmQuery<T>(beanType, server, expressionFactory, (String) null);
|
||||
DefaultOrmQuery<T> copy = new DefaultOrmQuery<T>(beanDescriptor, server, expressionFactory, (String) null);
|
||||
copy.name = name;
|
||||
copy.includeTableJoin = includeTableJoin;
|
||||
copy.profilingListener = profilingListener;
|
||||
@@ -669,7 +752,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanPropertyAssocMany<?> getLazyLoadForParentsProperty() {
|
||||
public BeanPropertyAssocMany<?> getLazyLoadMany() {
|
||||
return lazyLoadForParentsProperty;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,12 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
import com.avaje.ebeaninternal.server.expression.ElasticExpressionContext;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -498,4 +501,67 @@ public class OrmQueryDetail implements Serializable {
|
||||
public Set<String> getFetchPaths() {
|
||||
return fetchPaths.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the Elastic search source include and fields if necessary.
|
||||
* <p>
|
||||
* Fetch all property is put into includes.
|
||||
* Fetch on 'many' path is put into includes.
|
||||
* Fetch on 'one' paths and root path are put into fields.
|
||||
* </p>
|
||||
*/
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
|
||||
Set<String> includes = new LinkedHashSet<String>();
|
||||
Set<String> fields = new LinkedHashSet<String>();
|
||||
|
||||
for (Map.Entry<String, OrmQueryProperties> entry : fetchPaths.entrySet()) {
|
||||
|
||||
String path = entry.getKey();
|
||||
OrmQueryProperties value = entry.getValue();
|
||||
if (value.allProperties()) {
|
||||
includes.add(path + ".*");
|
||||
} else if (context.containsMany(path)) {
|
||||
for (String propName : value.getIncluded()) {
|
||||
includes.add(path + "." + propName);
|
||||
}
|
||||
} else {
|
||||
for (String propName : value.getIncluded()) {
|
||||
fields.add(path + "." + propName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSelectClause()) {
|
||||
Set<String> included = baseProps.getIncluded();
|
||||
if (included != null) {
|
||||
for (String propName : included) {
|
||||
fields.add(propName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!includes.isEmpty()) {
|
||||
JsonGenerator json = context.json();
|
||||
json.writeFieldName("_source");
|
||||
json.writeStartObject();
|
||||
json.writeFieldName("include");
|
||||
json.writeStartArray();
|
||||
for (String propName : includes) {
|
||||
json.writeString(propName);
|
||||
}
|
||||
json.writeEndArray();
|
||||
json.writeEndObject();
|
||||
}
|
||||
|
||||
if (!fields.isEmpty()) {
|
||||
JsonGenerator json = context.json();
|
||||
json.writeFieldName("fields");
|
||||
json.writeStartArray();
|
||||
for (String propName : fields) {
|
||||
json.writeString(propName);
|
||||
}
|
||||
json.writeEndArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.avaje.ebeaninternal.server.text.json;
|
||||
|
||||
import com.avaje.ebean.PersistenceIOException;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.text.json.JsonBeanReader;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A 'context' for reading entity beans from JSON.
|
||||
* <p>
|
||||
* This is used such that a load context and persistence context can be used to span multiple marshalling requests.
|
||||
* </p>
|
||||
*/
|
||||
public class DJsonBeanReader<T> implements JsonBeanReader<T> {
|
||||
|
||||
private final BeanDescriptor<T> desc;
|
||||
|
||||
private final ReadJson readJson;
|
||||
|
||||
public DJsonBeanReader(BeanDescriptor<T> desc, ReadJson readJson) {
|
||||
this.desc = desc;
|
||||
this.readJson = readJson;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void persistenceContextPut(Object beanId, T currentBean) {
|
||||
readJson.persistenceContextPut(beanId, currentBean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistenceContext getPersistenceContext() {
|
||||
return readJson.getPersistenceContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T read() {
|
||||
try {
|
||||
return desc.jsonRead(readJson, null);
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonBeanReader<T> forJson(JsonParser moreJson) {
|
||||
return new DJsonBeanReader(desc, readJson.forJson(moreJson));
|
||||
}
|
||||
}
|
||||
@@ -106,15 +106,23 @@ public class DJsonContext implements JsonContext {
|
||||
|
||||
public <T> T toBean(Class<T> cls, JsonParser parser, JsonReadOptions options) throws JsonIOException {
|
||||
|
||||
ReadJson readJson = new ReadJson(parser, options, determineObjectMapper(options));
|
||||
BeanDescriptor<T> desc = getDescriptor(cls);
|
||||
ReadJson readJson = new ReadJson(desc, parser, options, determineObjectMapper(options));
|
||||
try {
|
||||
BeanDescriptor<T> d = getDescriptor(cls);
|
||||
return d.jsonRead(readJson, null);
|
||||
return desc.jsonRead(readJson, null);
|
||||
} catch (IOException e) {
|
||||
throw new JsonIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DJsonBeanReader createBeanReader(Class<T> cls, JsonParser parser, JsonReadOptions options) throws JsonIOException {
|
||||
|
||||
BeanDescriptor<T> desc = getDescriptor(cls);
|
||||
ReadJson readJson = new ReadJson(desc, parser, options, determineObjectMapper(options));
|
||||
return new DJsonBeanReader<T>(desc, readJson);
|
||||
}
|
||||
|
||||
public <T> List<T> toList(Class<T> cls, String json) throws JsonIOException {
|
||||
return toList(cls, new StringReader(json));
|
||||
}
|
||||
@@ -138,9 +146,9 @@ public class DJsonContext implements JsonContext {
|
||||
|
||||
public <T> List<T> toList(Class<T> cls, JsonParser src, JsonReadOptions options) throws JsonIOException {
|
||||
|
||||
ReadJson readJson = new ReadJson(src, options, determineObjectMapper(options));
|
||||
BeanDescriptor<T> desc = getDescriptor(cls);
|
||||
ReadJson readJson = new ReadJson(desc, src, options, determineObjectMapper(options));
|
||||
try {
|
||||
BeanDescriptor<T> d = getDescriptor(cls);
|
||||
|
||||
List<T> list = new ArrayList<T>();
|
||||
|
||||
@@ -153,7 +161,7 @@ public class DJsonContext implements JsonContext {
|
||||
}
|
||||
|
||||
do {
|
||||
T bean = d.jsonRead(readJson, null);
|
||||
T bean = desc.jsonRead(readJson, null);
|
||||
if (bean == null) {
|
||||
break;
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
package com.avaje.ebeaninternal.server.text.json;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.text.json.JsonReadBeanVisitor;
|
||||
import com.avaje.ebean.text.json.JsonReadOptions;
|
||||
import com.avaje.ebeaninternal.api.LoadContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.loadcontext.DLoadContext;
|
||||
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@@ -14,34 +21,120 @@ import java.util.Map;
|
||||
*/
|
||||
public class ReadJson {
|
||||
|
||||
private final BeanDescriptor<?> rootDesc;
|
||||
|
||||
/**
|
||||
* Jackson parser.
|
||||
*/
|
||||
final JsonParser parser;
|
||||
private final JsonParser parser;
|
||||
|
||||
/**
|
||||
* Stack of the path - used to find the appropriate JsonReadBeanVisitor.
|
||||
*/
|
||||
final PathStack pathStack;
|
||||
private final PathStack pathStack;
|
||||
|
||||
/**
|
||||
* Map of the JsonReadBeanVisitor keyed by path.
|
||||
*/
|
||||
final Map<String, JsonReadBeanVisitor<?>> visitorMap;
|
||||
private final Map<String, JsonReadBeanVisitor<?>> visitorMap;
|
||||
|
||||
final Object objectMapper;
|
||||
private final Object objectMapper;
|
||||
|
||||
private final PersistenceContext persistenceContext;
|
||||
|
||||
private final LoadContext loadContext;
|
||||
|
||||
/**
|
||||
* Construct with parser and readOptions.
|
||||
*/
|
||||
public ReadJson(JsonParser parser, JsonReadOptions readOptions, Object objectMapper) {
|
||||
public ReadJson(BeanDescriptor<?> desc, JsonParser parser, JsonReadOptions readOptions, Object objectMapper) {
|
||||
|
||||
this.rootDesc = desc;
|
||||
this.parser = parser;
|
||||
this.objectMapper = objectMapper;
|
||||
this.persistenceContext = initPersistenceContext(readOptions);
|
||||
this.loadContext = initLoadContext(desc, readOptions);
|
||||
|
||||
// only create visitorMap, pathStack if needed ...
|
||||
this.visitorMap = (readOptions == null) ? null : readOptions.getVisitorMap();
|
||||
this.pathStack = (visitorMap == null) ? null : new PathStack();
|
||||
this.pathStack = (visitorMap == null && loadContext == null) ? null : new PathStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct when transferring load context, persistence context, object mapper etc to a new ReadJson instance.
|
||||
*/
|
||||
private ReadJson(JsonParser moreJson, ReadJson source) {
|
||||
this.parser = moreJson;
|
||||
this.rootDesc = source.rootDesc;
|
||||
this.pathStack = source.pathStack;
|
||||
this.visitorMap = source.visitorMap;
|
||||
this.objectMapper = source.objectMapper;
|
||||
this.persistenceContext = source.persistenceContext;
|
||||
this.loadContext = source.loadContext;
|
||||
}
|
||||
|
||||
private LoadContext initLoadContext(BeanDescriptor<?> desc, JsonReadOptions readOptions) {
|
||||
if (readOptions != null && readOptions.isEnableLazyLoading()) {
|
||||
return new DLoadContext(desc, persistenceContext);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private PersistenceContext initPersistenceContext(JsonReadOptions readOptions) {
|
||||
if (readOptions != null && readOptions.getPersistenceContext() != null) {
|
||||
return readOptions.getPersistenceContext();
|
||||
}
|
||||
return new DefaultPersistenceContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the persistence context being used if any.
|
||||
*/
|
||||
public PersistenceContext getPersistenceContext() {
|
||||
return persistenceContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance of ReadJson using the existing context but with a new JsonParser.
|
||||
*/
|
||||
public ReadJson forJson(JsonParser moreJson) {
|
||||
return new ReadJson(moreJson, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the bean to the persistence context.
|
||||
*/
|
||||
public <T> void persistenceContextPut(Object beanId, T currentBean) {
|
||||
|
||||
persistenceContextPutIfAbsent(beanId, (EntityBean)currentBean, rootDesc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put the bean into the persistence context. If there is already a matching bean in the
|
||||
* persistence context then return that instance else return null.
|
||||
*/
|
||||
public Object persistenceContextPutIfAbsent(Object id, EntityBean bean, BeanDescriptor<?> beanDesc) {
|
||||
|
||||
if (persistenceContext == null) {
|
||||
// no persistenceContext means no lazy loading either
|
||||
return null;
|
||||
}
|
||||
|
||||
Object contextBean = persistenceContext.putIfAbsent(id, bean);
|
||||
if (contextBean == null) {
|
||||
if (loadContext != null) {
|
||||
EntityBeanIntercept ebi = bean._ebean_getIntercept();
|
||||
if (ebi.isPartial()) {
|
||||
// register for further lazy loading
|
||||
String path = pathStack.peekWithNull();
|
||||
loadContext.register(path, ebi);
|
||||
beanDesc.lazyLoadRegister(path, ebi, bean, loadContext);
|
||||
}
|
||||
ebi.setLoaded();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return contextBean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,4 +203,5 @@ public class ReadJson {
|
||||
public Object readValueUsingObjectMapper(Class<?> propertyType) throws IOException {
|
||||
return getObjectMapper().readValue(parser, propertyType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-4
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.transaction;
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateProcessor;
|
||||
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
|
||||
import com.avaje.ebeaninternal.server.core.BootupClasses;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
@@ -16,10 +17,10 @@ import java.sql.Connection;
|
||||
*/
|
||||
public class AutoCommitTransactionManager extends TransactionManager {
|
||||
|
||||
public AutoCommitTransactionManager(ClusterManager clusterManager, BackgroundExecutor backgroundExecutor,
|
||||
ServerConfig config, BeanDescriptorManager descMgr, BootupClasses bootupClasses) {
|
||||
|
||||
super(clusterManager, backgroundExecutor, config, descMgr, bootupClasses);
|
||||
public AutoCommitTransactionManager(ServerConfig serverConfig, ClusterManager clusterManager, BackgroundExecutor backgroundExecutor,
|
||||
DocStoreUpdateProcessor indexUpdateProcessor, BeanDescriptorManager descMgr, BootupClasses bootupClasses) {
|
||||
|
||||
super(serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, descMgr, bootupClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-3
@@ -23,7 +23,7 @@ import java.util.Set;
|
||||
* <p>
|
||||
* Duplicate beans are ones having the same type and unique id value. These are
|
||||
* considered duplicates and replaced by the bean instance that was already
|
||||
* loaded into the PersistanceContext.
|
||||
* loaded into the PersistenceContext.
|
||||
* </p>
|
||||
*/
|
||||
public final class DefaultPersistenceContext implements PersistenceContext {
|
||||
@@ -36,13 +36,13 @@ public final class DefaultPersistenceContext implements PersistenceContext {
|
||||
private final Monitor monitor = new Monitor();
|
||||
|
||||
/**
|
||||
* Create a new PersistanceContext.
|
||||
* Create a new PersistenceContext.
|
||||
*/
|
||||
public DefaultPersistenceContext() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an object into the PersistanceContext.
|
||||
* Set an object into the PersistenceContext.
|
||||
*/
|
||||
public void put(Object id, Object bean) {
|
||||
synchronized (monitor) {
|
||||
|
||||
@@ -9,6 +9,10 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.avaje.ebean.annotation.DocStoreEvent;
|
||||
import com.avaje.ebeanservice.docstore.api.support.DocStoreDeleteEvent;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreUpdates;
|
||||
|
||||
/**
|
||||
* Beans deleted by Id used for updating L2 Cache.
|
||||
*/
|
||||
@@ -72,5 +76,28 @@ public final class DeleteByIdMap {
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add the deletes to the DocStoreUpdates.
|
||||
*/
|
||||
public void addDocStoreUpdates(DocStoreUpdates docStoreUpdates, DocStoreEvent txnIndexMode) {
|
||||
for (BeanPersistIds deleteIds : beanMap.values()) {
|
||||
BeanDescriptor<?> desc = deleteIds.getBeanDescriptor();
|
||||
DocStoreEvent docStoreEvent = desc.getDocStoreEvent(PersistRequest.Type.DELETE, txnIndexMode);
|
||||
if (DocStoreEvent.IGNORE != docStoreEvent) {
|
||||
// Add to queue or bulk update entries
|
||||
boolean queue = (DocStoreEvent.QUEUE == docStoreEvent);
|
||||
String queueId = desc.getDocStoreQueueId();
|
||||
List<Serializable> idValues = deleteIds.getDeleteIds();
|
||||
if (idValues != null) {
|
||||
for (int i = 0; i < idValues.size(); i++) {
|
||||
if (queue) {
|
||||
docStoreUpdates.queueDelete(queueId, idValues.get(i));
|
||||
} else {
|
||||
docStoreUpdates.addDelete(new DocStoreDeleteEvent(desc, idValues.get(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -4,6 +4,7 @@ import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateProcessor;
|
||||
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
|
||||
import com.avaje.ebeaninternal.server.core.BootupClasses;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
@@ -16,10 +17,10 @@ import java.sql.Connection;
|
||||
*/
|
||||
public class ExplicitTransactionManager extends TransactionManager {
|
||||
|
||||
public ExplicitTransactionManager(ClusterManager clusterManager, BackgroundExecutor backgroundExecutor,
|
||||
ServerConfig config, BeanDescriptorManager descMgr, BootupClasses bootupClasses) {
|
||||
public ExplicitTransactionManager(ServerConfig serverConfig, ClusterManager clusterManager, BackgroundExecutor backgroundExecutor,
|
||||
DocStoreUpdateProcessor indexUpdateProcessor, BeanDescriptorManager descMgr, BootupClasses bootupClasses) {
|
||||
|
||||
super(clusterManager, backgroundExecutor, config, descMgr, bootupClasses);
|
||||
super(serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, descMgr, bootupClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user