From 4f51a3b130d5077f717c303f3ac4eb4899468762 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Wed, 25 Nov 2015 11:06:14 +1300 Subject: [PATCH] #463 - ENH: Add @DraftReset support - property that is set to null on the draft been when it is published (@Draftable feature) --- .../avaje/ebean/annotation/DraftReset.java | 19 +++++++ .../server/deploy/BeanDescriptor.java | 7 +++ .../deploy/BeanDescriptorDraftHelp.java | 51 +++++++++++++++++- .../server/deploy/BeanProperty.java | 11 ++++ .../deploy/meta/DeployBeanProperty.java | 9 ++++ .../server/deploy/parse/AnnotationFields.java | 4 +- .../server/persist/DefaultPersister.java | 53 ++++++++++++------- 7 files changed, 132 insertions(+), 22 deletions(-) create mode 100644 src/main/java/com/avaje/ebean/annotation/DraftReset.java diff --git a/src/main/java/com/avaje/ebean/annotation/DraftReset.java b/src/main/java/com/avaje/ebean/annotation/DraftReset.java new file mode 100644 index 000000000..6063cf081 --- /dev/null +++ b/src/main/java/com/avaje/ebean/annotation/DraftReset.java @@ -0,0 +1,19 @@ +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; + +/** + * Marks a property on a @Draftable bean that is set to null on the 'draft bean' on publish. + *

+ * This is expected to be put on properties that get 'reset' or 'cleared' after a publish. + * These properties might represent a publish comment or publish timestamp. + *

+ */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface DraftReset { + +} 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 5076cc767..c54ee3868 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -837,6 +837,13 @@ public class BeanDescriptor implements MetaBeanInfo, SpiBeanType { return draftHelp.publish(draftBean, liveBean); } + /** + * Reset properties on the draft bean based on @DraftDirty and @DraftReset. + */ + public boolean draftReset(T draftBean) { + return draftHelp.draftReset(draftBean); + } + /** * Return the draft dirty boolean property or null if there is not one assigned to this bean type. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorDraftHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorDraftHelp.java index 118c8fdf4..b4c2996c9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorDraftHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorDraftHelp.java @@ -3,6 +3,9 @@ package com.avaje.ebeaninternal.server.deploy; import com.avaje.ebean.Query; import com.avaje.ebean.bean.EntityBean; +import java.util.ArrayList; +import java.util.List; + /** * Helper for BeanDescriptor that manages draft entity beans. * @@ -12,16 +15,60 @@ public final class BeanDescriptorDraftHelp { private final BeanDescriptor desc; + private final BeanProperty draftDirty; + + private final BeanProperty[] resetProperties; + public BeanDescriptorDraftHelp(BeanDescriptor desc) { this.desc = desc; + this.draftDirty = desc.getDraftDirty(); + this.resetProperties = resetProperties(); } /** - * + * Return the properties that are reset on draft beans after publish. */ - public void initialise() { + private BeanProperty[] resetProperties() { + + List list = new ArrayList(); + + BeanProperty[] props = desc.propertiesNonMany(); + for (BeanProperty prop : props) { + if (prop.isDraftReset()) { + list.add(prop); + } + } + + return list.toArray(new BeanProperty[list.size()]); } + /** + * Set the value of all the 'reset properties' to null on the draft bean. + */ + public boolean draftReset(T draftBean) { + + EntityBean draftEntityBean = (EntityBean)draftBean; + + if (draftDirty != null) { + // set @DraftDirty property to false + draftDirty.setValueIntercept(draftEntityBean, false); + } + + // set to null on all @DraftReset properties + for (BeanProperty resetProperty : resetProperties) { + resetProperty.setValueIntercept(draftEntityBean, null); + } + + // return true if the bean is dirty (and should be persisted) + return draftEntityBean._ebean_getIntercept().isDirty(); + } + + /** + * Transfer the values from the draftBean to the liveBean. + *

+ * This will recursive transfer values to all @DraftableElement properties. + *

+ */ public T publish(T draftBean, T liveBean) { if (liveBean == null) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java index dbec4d4bf..d97f11480 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java @@ -227,6 +227,8 @@ public class BeanProperty implements ElPropertyValue { final boolean draftDirty; + final boolean draftReset; + final boolean indexed; final String indexName; @@ -255,6 +257,7 @@ public class BeanProperty implements ElPropertyValue { this.excludedFromHistory = deploy.isExcludedFromHistory(); this.draftDirty = deploy.isDraftDirty(); this.draftOnly = deploy.isDraftOnly(); + this.draftReset = deploy.isDraftReset(); this.secondaryTable = deploy.isSecondaryTable(); if (secondaryTable) { @@ -341,6 +344,7 @@ public class BeanProperty implements ElPropertyValue { this.excludedFromHistory = source.excludedFromHistory; this.draftDirty = source.draftDirty; this.draftOnly = source.draftOnly; + this.draftReset = source.draftReset; this.fetchEager = source.fetchEager; this.unidirectionalShadow = source.unidirectionalShadow; this.discriminator = source.discriminator; @@ -1045,6 +1049,13 @@ public class BeanProperty implements ElPropertyValue { return draftDirty; } + /** + * Return true if this property is reset/cleared on publish (on the draft bean). + */ + public boolean isDraftReset() { + return draftReset; + } + /** * Return true if this property should be included in an Insert. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java index c87c35246..7b856ac10 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java @@ -185,6 +185,7 @@ public class DeployBeanProperty { private boolean draftOnly; private boolean draftDirty; + private boolean draftReset; public DeployBeanProperty(DeployBeanDescriptor desc, Class propertyType, ScalarType scalarType, ScalarTypeConverter typeConverter) { this.desc = desc; @@ -854,4 +855,12 @@ public class DeployBeanProperty { public boolean isDraftDirty() { return draftDirty; } + + public void setDraftReset() { + this.draftReset = true; + } + + public boolean isDraftReset() { + return draftReset; + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java index 85683bdb6..95b7cb3d8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java @@ -152,10 +152,12 @@ public class AnnotationFields extends AnnotationParser { if (get(prop, DraftOnly.class) != null) { prop.setDraftOnly(); } - if (get(prop, DraftDirty.class) != null) { prop.setDraftDirty(); } + if (get(prop, DraftReset.class) != null) { + prop.setDraftReset(); + } DbJson dbJson = get(prop, DbJson.class); if (dbJson != null) { 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 884395cac..7cf75ff6e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java @@ -159,10 +159,7 @@ 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(); + DraftHandler draftHandler = new DraftHandler(desc); for (T draftBean: draftBeans) { Object draftID = desc.getBeanId(draftBean); @@ -181,34 +178,52 @@ public final class DefaultPersister implements Persister { update(request); } - unsetDraftDirtyProperty(draftUpdates, draftDirty, draftBean); + draftHandler.resetDraft(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)); - } - } + draftHandler.updateDrafts(transaction, mgr); PUB.debug("publish - complete for [{}]", desc.getName()); - return livePublish; } /** - * Set the draft dirty state to false and add to the draftUpdates list. + * Helper to handle draft beans (properties reset etc). */ - private void unsetDraftDirtyProperty(List draftUpdates, BeanProperty draftDirty, T draftBean) { + class DraftHandler { - if (draftDirty != null) { - EntityBean draftEntityBean = (EntityBean)draftBean; - draftDirty.setValueIntercept(draftEntityBean, false); - if (draftEntityBean._ebean_getIntercept().isDirty()) { + final BeanDescriptor desc; + final BeanProperty draftDirty; + final List draftUpdates = new ArrayList(); + + DraftHandler(BeanDescriptor desc) { + this.desc = desc; + this.draftDirty = desc.getDraftDirty(); + } + + /** + * Set the draft dirty state to false and reset any dirtyReset properties. + */ + void resetDraft(T draftBean) { + if (desc.draftReset(draftBean)) { + // draft bean is dirty so collect it for persisting later draftUpdates.add(draftBean); } } + + /** + * Save all the draft beans (with various properties reset etc). + */ + void updateDrafts(Transaction transaction, BeanManager mgr) { + 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)); + } + } + } + } /**