From 5e8afbe50d0b867bfe0a1833da48e7ae4268d531 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Mon, 23 Nov 2015 23:11:15 +1300 Subject: [PATCH] #461 - ENH: Add @Draftable support - @DraftDirty --- .../server/core/PersistRequestBean.java | 126 +++++++++--------- .../server/deploy/BeanDescriptor.java | 23 ++++ .../deploy/meta/DeployBeanPropertyLists.java | 8 ++ .../server/persist/DefaultPersister.java | 60 ++++++--- .../server/query/CQueryEngine.java | 6 + .../server/query/CQueryFetchIds.java | 1 - .../server/persist/BatchedBeanHolderTest.java | 4 +- 7 files changed, 146 insertions(+), 82 deletions(-) diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java index d66a85425..0c431d1a0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java @@ -61,6 +61,8 @@ public final class PersistRequestBean extends PersistRequest implements BeanP private final boolean dirty; + private final boolean publish; + private ConcurrencyMode concurrencyMode; /** @@ -119,10 +121,8 @@ public final class PersistRequestBean extends PersistRequest implements BeanP */ private boolean requestUpdateAllLoadedProps; - private boolean publish; - public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager mgr, SpiTransaction t, - PersistExecute persistExecute, PersistRequest.Type type, boolean saveRecurse) { + PersistExecute persistExecute, PersistRequest.Type type, boolean saveRecurse, boolean publish) { super(server, t, persistExecute); this.entityBean = (EntityBean) bean; @@ -134,7 +134,7 @@ public final class PersistRequestBean extends PersistRequest implements BeanP this.parentBean = parentBean; this.controller = beanDescriptor.getPersistController(); this.type = type; - + if (saveRecurse) { this.persistCascade = t.isPersistCascade(); } @@ -149,6 +149,10 @@ public final class PersistRequestBean extends PersistRequest implements BeanP beanDescriptor.checkMutableProperties(intercept); } this.concurrencyMode = beanDescriptor.getConcurrencyMode(intercept); + this.publish = publish; + if (!publish && beanDescriptor.isDraftable()) { + beanDescriptor.setDraftDirty(entityBean, true); + } this.dirty = intercept.isDirty(); } @@ -245,17 +249,17 @@ public final class PersistRequestBean extends PersistRequest implements BeanP public void notifyCache() { if (notifyCache) { switch (type) { - case INSERT: - beanDescriptor.cacheHandleInsert(this); - break; - case UPDATE: - beanDescriptor.cacheHandleUpdate(idValue, this); - break; - case DELETE: - // Bean deleted from cache early via postDelete() - break; - default: - throw new IllegalStateException("Invalid type " + type); + case INSERT: + beanDescriptor.cacheHandleInsert(this); + break; + case UPDATE: + beanDescriptor.cacheHandleUpdate(idValue, this); + break; + case DELETE: + // Bean deleted from cache early via postDelete() + break; + default: + throw new IllegalStateException("Invalid type " + type); } } } @@ -449,24 +453,24 @@ public final class PersistRequestBean extends PersistRequest implements BeanP @Override public int executeNow() { switch (type) { - case INSERT: - persistExecute.executeInsertBean(this); - return -1; + case INSERT: + persistExecute.executeInsertBean(this); + return -1; - case UPDATE: - if (beanPersistListener != null) { - // store the updated properties for sending later - updatedProperties = getUpdatedProperties(); - } - persistExecute.executeUpdateBean(this); - return -1; + case UPDATE: + if (beanPersistListener != null) { + // store the updated properties for sending later + updatedProperties = getUpdatedProperties(); + } + persistExecute.executeUpdateBean(this); + return -1; - case DELETE: - persistExecute.executeDeleteBean(this); - return -1; + case DELETE: + persistExecute.executeDeleteBean(this); + return -1; - default: - throw new RuntimeException("Invalid type " + type); + default: + throw new RuntimeException("Invalid type " + type); } } @@ -515,8 +519,12 @@ public final class PersistRequestBean extends PersistRequest implements BeanP throw new OptimisticLockException(m, null, bean); } switch (type) { - case DELETE: postDelete(); break; - case UPDATE: postUpdate(); break; + case DELETE: + postDelete(); + break; + case UPDATE: + postUpdate(); + break; default: // do nothing } } @@ -573,35 +581,36 @@ public final class PersistRequestBean extends PersistRequest implements BeanP private void controllerPost() { switch (type) { - case INSERT: - controller.postInsert(this); - break; - case UPDATE: - controller.postUpdate(this); - break; - case DELETE: - controller.postDelete(this); - break; - default: - break; + case INSERT: + controller.postInsert(this); + break; + case UPDATE: + controller.postUpdate(this); + break; + case DELETE: + controller.postDelete(this); + break; + default: + break; } } private void logSummary() { + String draft = (beanDescriptor.isDraftable() && !publish) ? " draft[true]" : ""; String name = beanDescriptor.getName(); switch (type) { - case INSERT: - transaction.logSummary("Inserted [" + name + "] [" + idValue + "]"); - break; - case UPDATE: - transaction.logSummary("Updated [" + name + "] [" + idValue + "]"); - break; - case DELETE: - transaction.logSummary("Deleted [" + name + "] [" + idValue + "]"); - break; - default: - break; + case INSERT: + transaction.logSummary("Inserted [" + name + "] [" + idValue + "]" + draft); + break; + case UPDATE: + transaction.logSummary("Updated [" + name + "] [" + idValue + "]" + draft); + break; + case DELETE: + transaction.logSummary("Deleted [" + name + "] [" + idValue + "]"); + break; + default: + break; } } @@ -722,7 +731,7 @@ public final class PersistRequestBean extends PersistRequest implements BeanP /** * Determine if all loaded properties should be used for an update. *

- * Takes into account transaction setting and JDBC batch. + * Takes into account transaction setting and JDBC batch. *

*/ public boolean determineUpdateAllLoadedProperties() { @@ -739,13 +748,6 @@ public final class PersistRequestBean extends PersistRequest implements BeanP return requestUpdateAllLoadedProps; } - /** - * This is a persist request for a 'publish' action. - */ - public void setPublish() { - publish = true; - } - /** * Return true if this request is a 'publish' action. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java index e040a7566..657989df1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -158,6 +158,8 @@ public class BeanDescriptor implements MetaBeanInfo, SpiBeanType { private final boolean draftableElement; + private final BeanProperty draftDirty; + /** * Map of BeanProperty Linked so as to preserve order. */ @@ -393,6 +395,7 @@ public class BeanDescriptor implements MetaBeanInfo, SpiBeanType { this.idProperty = listHelper.getId(); this.versionProperty = listHelper.getVersionProperty(); + this.draftDirty = listHelper.getDraftDirty(); this.propMap = listHelper.getPropertyMap(); this.propertiesTransient = listHelper.getTransients(); this.propertiesNonTransient = listHelper.getNonTransients(); @@ -833,6 +836,13 @@ public class BeanDescriptor implements MetaBeanInfo, SpiBeanType { return draftHelp.publish(draftBean, liveBean); } + /** + * Return the draft dirty boolean property or null if there is not one assigned to this bean type. + */ + public BeanProperty getDraftDirty() { + return draftDirty; + } + /** * Set the bean caching on or off. */ @@ -1918,6 +1928,19 @@ public class BeanDescriptor implements MetaBeanInfo, SpiBeanType { return draftableElement; } + /** + * If there is a @DraftDirty property set it's value on the bean. + */ + public void setDraftDirty(EntityBean entityBean, boolean value) { + if (draftDirty != null) { + // check to see if the dirty property has already + // been set and if so do not set the value + if (!entityBean._ebean_getIntercept().isChangedProperty(draftDirty.getPropertyIndex())) { + draftDirty.setValueIntercept(entityBean, value); + } + } + } + /** * Return true if this entity bean has history support. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java index 0e0c6af68..d8bcaad5c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java @@ -28,6 +28,8 @@ public class DeployBeanPropertyLists { private BeanProperty versionProperty; + private BeanProperty draftDirty; + private final BeanDescriptor desc; private final LinkedHashMap propertyMap; @@ -175,6 +177,8 @@ public class DeployBeanPropertyLists { logger.warn("Multiple @Version properties - property " + prop.getFullBeanName() + " not treated as a version property"); } + } else if (prop.isDraftDirty()) { + draftDirty = prop; } if (prop instanceof BeanPropertyCompound) { baseCompound.add((BeanPropertyCompound) prop); @@ -285,6 +289,10 @@ public class DeployBeanPropertyLists { return getMany2Many(); } + public BeanProperty getDraftDirty() { + return draftDirty; + } + /** * Mode used to determine which BeanPropertyAssoc to include. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java index 00afeace9..b3722e1f3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java @@ -36,6 +36,7 @@ import org.slf4j.LoggerFactory; import javax.persistence.PersistenceException; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -132,14 +133,14 @@ public final class DefaultPersister implements Persister { query.asDraft(); Class beanType = query.getBeanType(); - List draftBeans = server.findList(query, transaction); BeanDescriptor desc = server.getBeanDescriptor(beanType); - if (draftBeans.isEmpty()) { - throw new IllegalArgumentException("No draft beans found to publish"); - } + List draftBeans = server.findList(query, transaction); + PUB.debug("publish [{}] count[{}]", desc.getName(), draftBeans.size()); - PUB.debug("publish [{}] count:{}", desc.getName(), draftBeans.size()); + if (draftBeans.isEmpty()) { + return Collections.emptyList(); + } // get the list of Id's List idList = new ArrayList(); @@ -156,6 +157,11 @@ public final class DefaultPersister implements Persister { BeanManager mgr = beanDescriptorManager.getBeanManager(beanType); + // collect a list of draft beans that have had their + // dirty status set back to false by the publish + List draftUpdates = new ArrayList(); + BeanProperty draftDirty = desc.getDraftDirty(); + for (T draftBean: draftBeans) { Object draftID = desc.getBeanId(draftBean); T existingLiveBean = liveBeans.get(draftID); @@ -164,23 +170,45 @@ public final class DefaultPersister implements Persister { livePublish.add(liveBean); Type persistType = (existingLiveBean == null) ? Type.INSERT : Type.UPDATE; - PersistRequestBean request = createRequest(liveBean, transaction, null, mgr, persistType, true); - request.setPublish(); - - PUB.trace("publish [{}] id[{}]", desc.getName(), draftID); + PUB.trace("publish bean [{}] id[{}] type[{}]", desc.getName(), draftID, persistType); + PersistRequestBean request = createRequest(liveBean, transaction, null, mgr, persistType, true, true); if (persistType == Type.INSERT) { insert(request); } else { update(request); } - PUB.debug("publish complete for type:{}", desc.getName()); + unsetDraftDirtyProperty(draftUpdates, draftDirty, draftBean); } + if (!draftUpdates.isEmpty()) { + // update the dirty status on the drafts that have been published + PUB.debug("publish - update dirty status on [{}] drafts", draftUpdates.size()); + for (T draftUpdate : draftUpdates) { + update(createRequest(draftUpdate, transaction, null, mgr, Type.UPDATE, false, false)); + } + } + + PUB.debug("publish - complete for [{}]", desc.getName()); + return livePublish; } + /** + * Set the draft dirty state to false and add to the draftUpdates list. + */ + private void unsetDraftDirtyProperty(List draftUpdates, BeanProperty draftDirty, T draftBean) { + + if (draftDirty != null) { + EntityBean draftEntityBean = (EntityBean)draftBean; + draftDirty.setValueIntercept(draftEntityBean, false); + if (draftEntityBean._ebean_getIntercept().isDirty()) { + draftUpdates.add(draftBean); + } + } + } + /** * Recursively delete the bean. This calls back to the EbeanServer. */ @@ -258,9 +286,6 @@ public final class DefaultPersister implements Persister { // determine insert or update taking into account stateless updates PersistRequestBean request = createRequestRecurse(bean, t, parentBean, insertMode, publish); - if (publish) { - request.setPublish(); - } if (request.isReference()) { // its a reference... @@ -1261,7 +1286,7 @@ public final class DefaultPersister implements Persister { if (mgr == null) { throw new PersistenceException(errNotRegistered(bean.getClass())); } - return createRequest(bean, t, null, mgr, type, false); + return createRequest(bean, t, null, mgr, type, false, false); } /** @@ -1284,7 +1309,7 @@ public final class DefaultPersister implements Persister { // determine Insert or Update based on bean state and insert flag type = desc.isInsertMode(entityBean._ebean_getIntercept(), insertMode) ? Type.INSERT : Type.UPDATE; } - return createRequest(bean, t, parentBean, mgr, type, true); + return createRequest(bean, t, parentBean, mgr, type, true, publish); } /** @@ -1292,9 +1317,10 @@ public final class DefaultPersister implements Persister { * perform an insert, update or delete. */ @SuppressWarnings({"unchecked", "rawtypes"}) - private PersistRequestBean createRequest(T bean, Transaction t, Object parentBean, BeanManager mgr, PersistRequest.Type type, boolean saveRecurse) { + private PersistRequestBean createRequest(T bean, Transaction t, Object parentBean, BeanManager mgr, + PersistRequest.Type type, boolean saveRecurse, boolean publish) { - return new PersistRequestBean(server, bean, parentBean, mgr, (SpiTransaction) t, persistExecute, type, saveRecurse); + return new PersistRequestBean(server, bean, parentBean, mgr, (SpiTransaction) t, persistExecute, type, saveRecurse, publish); } private String errNotRegistered(Class beanClass) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java index f3ef190f4..d11d8fb1e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java @@ -415,6 +415,9 @@ public class CQueryEngine { if (query.isAutoTuned()) { msg.append("tuned[true] "); } + if (query.isAsDraft()) { + msg.append(" draft[true] "); + } if (originKey != null) { msg.append("origin[").append(originKey).append("] "); } @@ -458,6 +461,9 @@ public class CQueryEngine { if (query.isAutoTuned()) { msg.append("tuned[true] "); } + if (query.isAsDraft()) { + msg.append(" draft[true] "); + } if (originKey != null) { msg.append("origin[").append(originKey).append("] "); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java index b48d0aa0c..a0e0a9dbb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java @@ -88,7 +88,6 @@ public class CQueryFetchIds { * Return a summary description of this query. */ public String getSummary() { - //noinspection StringBufferReplaceableByString StringBuilder sb = new StringBuilder(80); sb.append("FindIds exeMicros[").append(executionTimeMicros) .append("] rows[").append(rowCount) diff --git a/src/test/java/com/avaje/ebeaninternal/server/persist/BatchedBeanHolderTest.java b/src/test/java/com/avaje/ebeaninternal/server/persist/BatchedBeanHolderTest.java index 1ffe29a67..c1452e1b8 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/persist/BatchedBeanHolderTest.java +++ b/src/test/java/com/avaje/ebeaninternal/server/persist/BatchedBeanHolderTest.java @@ -26,13 +26,13 @@ public class BatchedBeanHolderTest { BatchedBeanHolder holder = new BatchedBeanHolder(null, beanDescriptor, 1); Customer customer = new Customer(); - PersistRequestBean req1 = new PersistRequestBean(server, customer, null, beanManager, null, null, PersistRequest.Type.INSERT, false); + PersistRequestBean req1 = new PersistRequestBean(server, customer, null, beanManager, null, null, PersistRequest.Type.INSERT, false, false); int size = holder.append(req1); assertEquals(1, size); - PersistRequestBean req2 = new PersistRequestBean(server, customer, null, beanManager, null, null, PersistRequest.Type.INSERT, false); + PersistRequestBean req2 = new PersistRequestBean(server, customer, null, beanManager, null, null, PersistRequest.Type.INSERT, false, false); size = holder.append(req2); assertEquals(0, size);