#882 - ENH: Support document store only use (ie. Use @DocStore without @Entity and persist, fetch only against ElasticSearch with no SQL DB)

This commit is contained in:
Rob Bygrave
2016-11-21 00:17:28 +13:00
parent d6b37bdf83
commit 182a47697e
32 changed files with 514 additions and 68 deletions
@@ -39,13 +39,13 @@ public final class DocStoreQueueEntry {
}
}
final Action type;
private final Action type;
final String queueId;
private final String queueId;
final String path;
private final String path;
final Object beanId;
private final Object beanId;
/**
* Construct for an INDEX or DELETE action.
@@ -132,6 +132,11 @@ public class ServerConfig {
*/
private DocStoreConfig docStoreConfig = new DocStoreConfig();
/**
* Set to true when the EbeanServer only uses Document store.
*/
private boolean docStoreOnly;
/**
* This is used to populate @WhoCreated, @WhoModified and
* support other audit features (who executed a query etc).
@@ -1210,6 +1215,20 @@ public class ServerConfig {
this.namingConvention = namingConvention;
}
/**
* Return true if this EbeanServer is a Document store only instance (has no JDBC DB).
*/
public boolean isDocStoreOnly() {
return docStoreOnly;
}
/**
* Set to true if this EbeanServer is Document store only instance (has no JDBC DB).
*/
public void setDocStoreOnly(boolean docStoreOnly) {
this.docStoreOnly = docStoreOnly;
}
/**
* Return the configuration for the ElasticSearch integration.
*/
@@ -2405,6 +2424,7 @@ public class ServerConfig {
dbTypeConfig.setGeometrySRID(srid);
}
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
explicitTransactionBeginMode = p.getBoolean("explicitTransactionBeginMode", explicitTransactionBeginMode);
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
@@ -73,6 +73,11 @@ public interface BeanType<T> {
*/
boolean isValidExpression(String property);
/**
* Return true if the type is document store only.
*/
boolean isDocStoreOnly();
/**
* Return the base table this bean type maps to.
*/
@@ -10,6 +10,7 @@ import com.avaje.ebeaninternal.server.core.PersistDeferredRelationship;
import com.avaje.ebeaninternal.server.core.PersistRequest;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.persist.BatchControl;
import com.avaje.ebeanservice.docstore.api.DocStoreTransaction;
import javax.persistence.PersistenceException;
import javax.persistence.RollbackException;
@@ -79,6 +80,11 @@ public class ScopedTransaction implements SpiTransaction {
}
}
@Override
public DocStoreTransaction getDocStoreTransaction() {
return transaction.getDocStoreTransaction();
}
@Override
public DocStoreMode getDocStoreMode() {
return transaction.getDocStoreMode();
@@ -9,6 +9,7 @@ import com.avaje.ebeaninternal.server.core.PersistDeferredRelationship;
import com.avaje.ebeaninternal.server.core.PersistRequest;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.persist.BatchControl;
import com.avaje.ebeanservice.docstore.api.DocStoreTransaction;
import java.sql.Connection;
@@ -265,4 +266,9 @@ public interface SpiTransaction extends Transaction {
* Send the change set to be prepared and then logged.
*/
void sendChangeLog(ChangeSet changeSet);
/**
* Return a document store transaction.
*/
DocStoreTransaction getDocStoreTransaction();
}
@@ -11,6 +11,7 @@ import com.avaje.ebean.config.PropertyMap;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.UnderscoreNamingConvention;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.H2Platform;
import com.avaje.ebean.dbmigration.DbOffline;
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
@@ -94,9 +95,14 @@ public class DefaultContainer implements SpiContainer {
BootupClasses bootupClasses = getBootupClasses(serverConfig);
setDataSource(serverConfig);
// check the autoCommit and Transaction Isolation
boolean online = checkDataSource(serverConfig);
boolean online = true;
if (serverConfig.isDocStoreOnly()) {
serverConfig.setDatabasePlatform(new H2Platform());
} else {
setDataSource(serverConfig);
// check the autoCommit and Transaction Isolation
online = checkDataSource(serverConfig);
}
// determine database platform (Oracle etc)
setDatabasePlatform(serverConfig);
@@ -284,7 +284,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
*/
public void executePlugins(boolean online) {
ddlGenerator.execute(online);
if (!serverConfig.isDocStoreOnly()) {
ddlGenerator.execute(online);
}
for (Plugin plugin : serverPlugins) {
plugin.online(online);
}
@@ -43,6 +43,7 @@ import com.avaje.ebeaninternal.server.readaudit.DefaultReadAuditPrepare;
import com.avaje.ebeaninternal.server.text.json.DJsonContext;
import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager;
import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager;
import com.avaje.ebeaninternal.server.transaction.DocStoreTransactionManager;
import com.avaje.ebeaninternal.server.transaction.ExplicitTransactionManager;
import com.avaje.ebeaninternal.server.transaction.ExternalTransactionScopeManager;
import com.avaje.ebeaninternal.server.transaction.JtaTransactionManager;
@@ -342,11 +343,12 @@ public class InternalConfiguration {
if (serverConfig.isExplicitTransactionBeginMode()) {
return new ExplicitTransactionManager(localL2, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager);
}
if (isAutoCommitMode()) {
return new AutoCommitTransactionManager(localL2, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager);
}
if (serverConfig.isDocStoreOnly()) {
return new DocStoreTransactionManager(localL2, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager);
}
return new TransactionManager(localL2, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager);
}
@@ -155,7 +155,9 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
* Prepare the query and calculate the query plan key.
*/
public void prepareQuery() {
if (beanDescriptor.isDocStoreOnly()) {
query.setUseDocStore(true);
}
adapterPreQuery();
this.secondaryQueries = query.convertJoins();
this.queryPlanKey = query.prepare(this);
@@ -138,6 +138,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
private long version;
private long now;
/**
* Flag set when request is added to JDBC batch registered as a "getter callback" to automatically flush batch.
*/
@@ -1096,4 +1098,41 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
// delete handled by the BeanController so return 0
return 0;
}
/**
* Persist to the document store now (via buffer, not post commit).
*/
public void docStorePersist() {
idValue = beanDescriptor.getId(entityBean);
switch (type) {
case UPDATE:
dirtyProperties = intercept.getDirtyProperties();
break;
}
// processing now so set IGNORE (unlike DB + DocStore processing with post-commit)
docStoreMode = DocStoreMode.IGNORE;
try {
docStoreUpdate(transaction.getDocStoreTransaction().obtain());
postExecute();
if (type == Type.UPDATE
&& beanDescriptor.isDocStoreEmbeddedInvalidation()
&& transaction.isPersistCascade()) {
// queue embedded/nested updates for later processing
beanDescriptor.docStoreUpdateEmbedded(this, transaction.getDocStoreTransaction().queue());
}
} catch (IOException e) {
throw new PersistenceException("Error persisting doc store bean", e);
}
}
/**
* Use a common 'now' value across both when created and when updated etc.
*/
public long now() {
if (now == 0) {
now = System.currentTimeMillis();
}
return now;
}
}
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.core.bootup;
import com.avaje.ebean.annotation.DocStore;
import com.avaje.ebean.config.CompoundType;
import com.avaje.ebean.config.IdGenerator;
import com.avaje.ebean.config.ScalarTypeConverter;
@@ -463,18 +464,14 @@ public class BootupClasses implements ClassFilter {
}
private boolean isEntity(Class<?> cls) {
Annotation ann = cls.getAnnotation(Entity.class);
if (ann != null) {
return true;
}
ann = cls.getAnnotation(Table.class);
return ann != null;
return has(cls, Entity.class) || has(cls, Table.class) || has(cls, DocStore.class);
}
private boolean isEmbeddable(Class<?> cls) {
return has(cls, Embeddable.class);
}
Annotation ann = cls.getAnnotation(Embeddable.class);
return ann != null;
private boolean has(Class<?> cls, Class<? extends Annotation> ann) {
return cls.getAnnotation(ann) != null;
}
}
@@ -85,7 +85,6 @@ import javax.persistence.PersistenceException;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
@@ -139,7 +138,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
public enum EntityType {
ORM, EMBEDDED, VIEW, SQL
ORM, EMBEDDED, VIEW, SQL, DOC
}
/**
@@ -397,7 +396,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
private final BeanDescriptorJsonHelp<T> jsonHelp;
private DocStoreBeanAdapter<T> docStoreAdapter;
private DocumentMapping docMapping;
private boolean docStoreEmbeddedInvalidation;
private final String defaultSelectClause;
@@ -602,6 +601,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
return ebeanServer;
}
/**
* Return true if this is a "Doc Store only" entity bean.
*/
public boolean isDocStoreOnly() {
return EntityType.DOC == entityType;
}
/**
* Return the type of this domain object.
*/
@@ -712,6 +718,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
}
/**
* Perform last initialisation for the descriptor.
*/
public void initLast() {
docStoreEmbeddedInvalidation = docStoreAdapter.hasEmbeddedInvalidation();
}
/**
* Initialise the document mapping.
*/
@@ -927,6 +940,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
return docStoreAdapter.isMapped();
}
/**
* Return true if this bean type has embedded doc store invalidation.
*/
public boolean isDocStoreEmbeddedInvalidation() {
return docStoreEmbeddedInvalidation;
}
/**
* Return the queueId used to uniquely identify this type when queuing an index updateAdd.
*/
@@ -558,10 +558,10 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
// create BeanManager for each non-embedded entity bean
for (BeanDescriptor<?> d : descMap.values()) {
d.initLast();
if (!d.isEmbedded()) {
BeanManager<?> m = beanManagerFactory.create(d);
beanManagerMap.put(d.getFullName(), m);
checkForValidEmbeddedId(d);
}
}
@@ -333,6 +333,10 @@ public class DeployBeanDescriptor<T> {
return EntityType.ORM.equals(et);
}
public boolean isDocStoreOnly() {
return EntityType.DOC.equals(entityType);
}
public EntityType getEntityType() {
if (entityType == null) {
entityType = EntityType.ORM;
@@ -95,12 +95,12 @@ public class DeployTableJoin {
/**
* Add a JoinColumn set.
*/
public void addJoinColumn(boolean order, Set<JoinColumn> jcSet, BeanTable beanTable) {
for (JoinColumn jc: jcSet) {
addJoinColumn(order, jc, beanTable);
public void addJoinColumn(boolean order, Set<JoinColumn> joinColumns, BeanTable beanTable) {
for (JoinColumn joinColumn : joinColumns) {
addJoinColumn(order, joinColumn, beanTable);
}
}
/**
* Return the join columns.
*/
@@ -1,14 +1,5 @@
package com.avaje.ebeaninternal.server.deploy.parse;
import java.util.Set;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.MapKey;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import com.avaje.ebean.annotation.HistoryExclude;
import com.avaje.ebean.annotation.PrivateOwned;
import com.avaje.ebean.annotation.Where;
@@ -25,17 +16,25 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.MapKey;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import java.util.Set;
/**
* Read the deployment annotation for Assoc Many beans.
*/
public class AnnotationAssocManys extends AnnotationParser {
class AnnotationAssocManys extends AnnotationParser {
private final BeanDescriptorManager factory;
/**
* Create with the DeployInfo.
*/
public AnnotationAssocManys(DeployBeanInfo<?> info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) {
AnnotationAssocManys(DeployBeanInfo<?> info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) {
super(info, javaxValidationAnnotations);
this.factory = factory;
}
@@ -197,6 +197,9 @@ public class AnnotationAssocOnes extends AnnotationParser {
private void readEmbedded(DeployBeanPropertyAssocOne<?> prop) {
if (descriptor.isDocStoreOnly() && prop.getDocStoreDoc() == null) {
prop.setDocStoreEmbedded("");
}
prop.setEmbedded();
prop.setDbInsertable(true);
prop.setDbUpdateable(true);
@@ -47,7 +47,7 @@ public abstract class AnnotationBase {
}
/**
* Return the annotation for the property.
* Return the annotation for the property.
* <p>
* Looks first at the field and then at the getter method.
* </p>
@@ -55,7 +55,7 @@ public abstract class AnnotationBase {
* If a <code>repeatable</code> annotation class is specified and the annotation is platform
* specific(see {@link #getPlatformMatchingAnnotation(Set, Class)}), then the platform specific
* annotation is returned. Otherwise the first annotation is retured. Note that you must no longer
* handle "java 1.6 repeatable containers" like {@link JoinColumn} / {@link JoinColumns} yourself.
* handle "java 1.6 repeatable containers" like {@link JoinColumn} / {@link JoinColumns} yourself.
* </p>
* <p>
*/
@@ -94,8 +94,7 @@ public abstract class AnnotationBase {
}
return ret;
}
/**
* Return the annotation for the property.
* <p>
@@ -170,10 +169,10 @@ public abstract class AnnotationBase {
}
/**
* Finds the first annotation of a type for this platform. (if annotation is platform specific, otherwise first
* Finds the first annotation of a type for this platform. (if annotation is platform specific, otherwise first
* found annotation is returned)
*/
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType,
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType,
Class<? extends DatabasePlatform> databasePlatform) {
if (annotationType == null) {
return null;
@@ -205,7 +204,7 @@ public abstract class AnnotationBase {
}
return null;
}
/**
* Find all {@link Annotation}s of {@code annotationType} on the supplied {@link AnnotatedElement}.
* <p>
@@ -232,7 +231,7 @@ public abstract class AnnotationBase {
Annotation[] anns = annotatedElement.getAnnotations();
for (Annotation ann : anns) {
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
if (ann.annotationType() == annotationType) {
ret.add((A) ann);
} else {
@@ -263,18 +262,18 @@ public abstract class AnnotationBase {
return null;
}
}
private static final ConcurrentMap<Annotation, Method> valueMethods = new ConcurrentHashMap<Annotation, Method>();
private static final Method nullMethod = getNullMethod();
/**
* Returns the <code>value()</code> method for a possible containerAnnotation.
* Method is retuned only, if its signature is <code>array of containingType</code>.
* Method is retuned only, if its signature is <code>array of containingType</code>.
*/
private static <A extends Annotation> Method getRepeatableValueMethod(
Annotation containerAnnotation, Class<A> containingType) {
Method method = valueMethods.get(containerAnnotation);
if (method == null) {
try {
@@ -295,11 +294,11 @@ public abstract class AnnotationBase {
}
return null;
}
/**
* Finds a suitable annotation from <code>Set<T> anns</code> for this platform.
* To distinguish between platforms, annotation type <code>T</code> must define
* a method withthis signature:
* To distinguish between platforms, annotation type <code>T</code> must define
* a method withthis signature:
* <p>
* <code>Class<? extends DatabasePlatform>[] platforms() default {};</code>
* </p>
@@ -345,4 +344,4 @@ public abstract class AnnotationBase {
return fallback;
}
}
}
@@ -108,8 +108,17 @@ public class AnnotationClass extends AnnotationParser {
private void read(Class<?> cls) {
// maybe doc store only so check for this before @Entity
DocStore docStore = AnnotationBase.findAnnotation(cls,DocStore.class);
if (docStore != null) {
descriptor.readDocStore(docStore);
descriptor.setEntityType(EntityType.DOC);
descriptor.setName(cls.getSimpleName());
}
Entity entity = AnnotationBase.findAnnotation(cls,Entity.class);
if (entity != null) {
descriptor.setEntityType(EntityType.ORM);
if (entity.name().equals("")) {
descriptor.setName(cls.getSimpleName());
} else {
@@ -170,11 +179,6 @@ public class AnnotationClass extends AnnotationParser {
descriptor.setDbComment(comment.value());
}
DocStore docStore = AnnotationBase.findAnnotation(cls,DocStore.class);
if (docStore != null) {
descriptor.readDocStore(docStore);
}
UpdateMode updateMode = AnnotationBase.findAnnotation(cls,UpdateMode.class);
if (updateMode != null) {
descriptor.setUpdateChangesOnly(updateMode.updateChangesOnly());
@@ -96,6 +96,13 @@ public class AnnotationFields extends AnnotationParser {
DocEmbedded docEmbedded = get(prop, DocEmbedded.class);
if (docEmbedded != null) {
prop.setDocStoreEmbedded(docEmbedded.doc());
if (descriptor.isDocStoreOnly()) {
if (get(prop, ManyToOne.class) == null) {
prop.setEmbedded();
prop.setDbInsertable(true);
prop.setDbUpdateable(true);
}
}
}
if (prop instanceof DeployBeanPropertyAssocOne<?>) {
@@ -16,18 +16,19 @@ public class DmlBeanPersisterFactory implements BeanPersisterFactory {
this.metaFactory = new MetaFactory(dbPlatform);
}
/**
* Create a DmlBeanPersister for the given bean type.
*/
public BeanPersister create(BeanDescriptor<?> desc) {
if (desc.isDocStoreOnly()) {
return new DocStoreBeanPersister(GeneratedProperties.of(desc));
}
UpdateMeta updMeta = metaFactory.createUpdate(desc);
DeleteMeta delMeta = metaFactory.createDelete(desc);
InsertMeta insMeta = metaFactory.createInsert(desc);
return new DmlBeanPersister(updMeta, insMeta, delMeta);
}
}
@@ -0,0 +1,37 @@
package com.avaje.ebeaninternal.server.persist.dml;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.persist.BeanPersister;
import javax.persistence.PersistenceException;
/**
* Document store based BeanPersister.
*/
class DocStoreBeanPersister implements BeanPersister {
private final GeneratedProperties generatedProperties;
DocStoreBeanPersister(GeneratedProperties generatedProperties) {
this.generatedProperties = generatedProperties;
}
@Override
public void insert(PersistRequestBean<?> request) throws PersistenceException {
//request.setIdValueForDocStore();
generatedProperties.preInsert(request.getEntityBean(), request.now());
request.docStorePersist();
}
@Override
public void update(PersistRequestBean<?> request) throws PersistenceException {
generatedProperties.preUpdate(request.getEntityBean(), request.now());
request.docStorePersist();
}
@Override
public int delete(PersistRequestBean<?> request) throws PersistenceException {
request.docStorePersist();
return 0;
}
}
@@ -0,0 +1,77 @@
package com.avaje.ebeaninternal.server.persist.dml;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import java.util.List;
/**
* Executes the generated property (like WhenCreated, WhoCreated etc) for doc store persisting.
*/
class GeneratedProperties {
/**
* Create the GeneratedProperties for the given bean type.
*/
static GeneratedProperties of(BeanDescriptor<?> desc) {
return new GeneratedPropertyCollector(desc).generatedProperties();
}
private final SetValue[] onInsert;
private final SetValue[] onUpdate;
GeneratedProperties(List<BeanProperty> inserts, List<BeanProperty> updates) {
this.onInsert = adapt(inserts);
this.onUpdate = adapt(updates);
}
private SetValue[] adapt(List<BeanProperty> inserts) {
SetValue[] setters = new SetValue[inserts.size()];
for (int i = 0; i < inserts.size(); i++) {
setters[i] = new SetValue(inserts.get(i));
}
return setters;
}
/**
* Set all the generated on insert values.
*/
public void preInsert(EntityBean bean, long now) {
for (SetValue setter : onInsert) {
setter.preInsert(bean, now);
}
}
/**
* Set all the generated on update values.
*/
public void preUpdate(EntityBean bean, long now) {
for (SetValue setter : onUpdate) {
setter.preUpdate(bean, now);
}
}
private static class SetValue {
private final BeanProperty property;
private final GeneratedProperty generatedProperty;
SetValue(BeanProperty property) {
this.property = property;
this.generatedProperty = property.getGeneratedProperty();
}
public void preInsert(EntityBean bean, long now) {
Object value = generatedProperty.getInsertValue(property, bean, now);
property.setValue(bean, value);
}
public void preUpdate(EntityBean bean, long now) {
Object value = generatedProperty.getUpdateValue(property, bean, now);
property.setValue(bean, value);
}
}
}
@@ -0,0 +1,40 @@
package com.avaje.ebeaninternal.server.persist.dml;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import java.util.ArrayList;
import java.util.List;
/**
* Collects the generated properties for inserts and updates for a given bean type.
*/
class GeneratedPropertyCollector {
private final List<BeanProperty> preInsert = new ArrayList<>();
private final List<BeanProperty> preUpdate = new ArrayList<>();
GeneratedPropertyCollector(BeanDescriptor<?> desc) {
for (BeanProperty beanProperty : desc.propertiesBaseScalar()) {
add(beanProperty);
}
}
GeneratedProperties generatedProperties() {
return new GeneratedProperties(preInsert, preUpdate);
}
void add(BeanProperty prop) {
GeneratedProperty gen = prop.getGeneratedProperty();
if (gen != null) {
if (gen.includeInInsert()) {
preInsert.add(prop);
}
if (gen.includeInUpdate()) {
preUpdate.add(prop);
}
}
}
}
@@ -0,0 +1,54 @@
package com.avaje.ebeaninternal.server.transaction;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Document store only transaction.
*/
public class DocStoreOnlyTransaction extends JdbcTransaction {
/**
* Create a new DocStore only Transaction.
*/
public DocStoreOnlyTransaction(String id, boolean explicit, TransactionManager manager) {
super(id, explicit, null, manager);
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public void setReadOnly(boolean readOnly) {
// do nothing
}
protected void deactivate() {
// do nothing
}
@Override
public Connection getInternalConnection() {
throw new RuntimeException("not supported on DocStoreTransaction");
}
@Override
public Connection getConnection() {
throw new RuntimeException("not supported on DocStoreTransaction");
}
@Override
protected void performRollback() throws SQLException {
// do nothing (could perhaps throw not supported exception)
}
@Override
protected void performCommit() throws SQLException {
if (docStoreTxn != null) {
manager.docStoreUpdateProcessor.commit(docStoreTxn);
}
}
}
@@ -0,0 +1,44 @@
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.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateProcessor;
import java.sql.Connection;
/**
* Transaction manager used for doc store only EbeanServer instance.
* <p>
* There is no underlying JDBC DataSource etc
*/
public class DocStoreTransactionManager extends TransactionManager {
private final String prefix = "";
/**
* Create the TransactionManager
*/
public DocStoreTransactionManager(boolean localL2Caching, ServerConfig config, ClusterManager clusterManager, BackgroundExecutor backgroundExecutor,
DocStoreUpdateProcessor docStoreUpdateProcessor, BeanDescriptorManager descMgr) {
super(localL2Caching, config, clusterManager, backgroundExecutor, docStoreUpdateProcessor, descMgr);
}
@Override
public SpiTransaction createTransaction(boolean explicit, int isolationLevel) {
long id = transactionCounter.incrementAndGet();
return createTransaction(explicit, null, id);
}
@Override
public SpiTransaction createQueryTransaction() {
return new DocStoreOnlyTransaction("", false, this);
}
@Override
protected SpiTransaction createTransaction(boolean explicit, Connection c, long id) {
return new DocStoreOnlyTransaction(prefix + id, explicit, this);
}
}
@@ -15,6 +15,7 @@ import com.avaje.ebeaninternal.server.core.PersistRequest;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.lib.util.Str;
import com.avaje.ebeaninternal.server.persist.BatchControl;
import com.avaje.ebeanservice.docstore.api.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -164,6 +165,8 @@ public class JdbcTransaction implements SpiTransaction {
*/
protected final boolean skipCacheAfterWrite;
protected DocStoreTransaction docStoreTxn;
/**
* Create a new JdbcTransaction.
*/
@@ -1079,6 +1082,15 @@ public class JdbcTransaction implements SpiTransaction {
getEvent().add(tableName, inserts, updates, deletes);
}
@Override
public DocStoreTransaction getDocStoreTransaction() {
if (docStoreTxn == null) {
queryOnly = false;
docStoreTxn = manager.createDocStoreTransaction(docStoreBatchSize);
}
return docStoreTxn;
}
@Override
public void putUserObject(String name, Object value) {
if (userObjects == null) {
@@ -13,6 +13,7 @@ import com.avaje.ebeaninternal.api.TransactionEventTable;
import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD;
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeanservice.docstore.api.DocStoreTransaction;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateProcessor;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdates;
import org.avaje.datasource.DataSourcePool;
@@ -144,6 +145,10 @@ public class TransactionManager {
return docStoreActive;
}
public DocStoreTransaction createDocStoreTransaction(int docStoreBatchSize) {
return docStoreUpdateProcessor.createTransaction(docStoreBatchSize);
}
public boolean isSkipCacheAfterWrite() {
return skipCacheAfterWrite;
}
@@ -433,4 +438,5 @@ public class TransactionManager {
beanDescriptorManager.processViewInvalidation(viewInvalidation);
}
}
}
@@ -0,0 +1,29 @@
package com.avaje.ebeanservice.docstore.api;
/**
* A document store transaction.
* <p>
* This might just be a buffer to batch persist requests to the document store and may not
* support transactional semantics (like rollback).
*/
public interface DocStoreTransaction {
/**
* Obtain a context to persist to (like a buffer).
*/
DocStoreUpdateContext obtain();
/**
* Add changes that should be queued to the DocStoreUpdates.
* <p>
* This mostly means nested/embedded updates that need to be processed after the source
* persist event has propagated.
* </p>
*/
DocStoreUpdates queue();
/**
* Flush all changes to the document store.
*/
void flush();
}
@@ -31,4 +31,15 @@ public interface DocStoreUpdateProcessor {
*/
void process(DocStoreUpdates docStoreUpdates, int bulkBatchSize);
/**
* Create a document store transaction hinting at the batch size.
* <p>
* The batch size can be set via {@link com.avaje.ebean.Transaction#setDocStoreBatchSize(int)}
*/
DocStoreTransaction createTransaction(int batchSize);
/**
* Perform commit/flush of the changes made via the document store transaction.
*/
void commit(DocStoreTransaction docStoreTransaction);
}
@@ -174,9 +174,12 @@ public abstract class DocStoreBeanBaseAdapter<T> implements DocStoreBeanAdapter<
String path = pathProp.getPath();
if (path != null) {
BeanDescriptor<?> targetDesc = desc.getBeanDescriptor(path);
String idName = targetDesc.getIdProperty().getName();
String fullPath = path + "." + idName;
targetDesc.docStoreAdapter().registerInvalidationPath(desc.getDocStoreQueueId(), fullPath, pathProp.getProperties());
BeanProperty idProperty = targetDesc.getIdProperty();
if (idProperty != null) {
// embedded beans don't have id property
String fullPath = path + "." + idProperty.getName();
targetDesc.docStoreAdapter().registerInvalidationPath(desc.getDocStoreQueueId(), fullPath, pathProp.getProperties());
}
}
}
registerPaths = true;
@@ -2,6 +2,7 @@ package com.avaje.ebeanservice.docstore.none;
import com.avaje.ebean.plugin.BeanType;
import com.avaje.ebeanservice.docstore.api.DocStoreQueryUpdate;
import com.avaje.ebeanservice.docstore.api.DocStoreTransaction;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateProcessor;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdates;
@@ -10,7 +11,7 @@ import java.io.IOException;
/**
* DocStoreUpdateProcessor that barfs if it is used.
*/
public class NoneDocStoreUpdateProcessor implements DocStoreUpdateProcessor {
class NoneDocStoreUpdateProcessor implements DocStoreUpdateProcessor {
@Override
public <T> DocStoreQueryUpdate<T> createQueryUpdate(BeanType<T> beanType, int bulkBatchSize) throws IOException {
@@ -21,4 +22,14 @@ public class NoneDocStoreUpdateProcessor implements DocStoreUpdateProcessor {
public void process(DocStoreUpdates docStoreUpdates, int bulkBatchSize) {
throw NoneDocStore.implementationNotInClassPath();
}
@Override
public DocStoreTransaction createTransaction(int batchSize) {
throw NoneDocStore.implementationNotInClassPath();
}
@Override
public void commit(DocStoreTransaction docStoreTransaction) {
throw NoneDocStore.implementationNotInClassPath();
}
}