#461 - ENH: Add @Draftable support - @DraftDirty

This commit is contained in:
Robin Bygrave
2015-11-23 23:11:15 +13:00
parent db371cbc6a
commit 5e8afbe50d
7 changed files with 146 additions and 82 deletions
@@ -61,6 +61,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
private final boolean dirty;
private final boolean publish;
private ConcurrencyMode concurrencyMode;
/**
@@ -119,10 +121,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
*/
private boolean requestUpdateAllLoadedProps;
private boolean publish;
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<T> extends PersistRequest implements BeanP
/**
* Determine if all loaded properties should be used for an update.
* <p>
* Takes into account transaction setting and JDBC batch.
* Takes into account transaction setting and JDBC batch.
* </p>
*/
public boolean determineUpdateAllLoadedProperties() {
@@ -739,13 +748,6 @@ public final class PersistRequestBean<T> 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.
*/
@@ -158,6 +158,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
private final boolean draftableElement;
private final BeanProperty draftDirty;
/**
* Map of BeanProperty Linked so as to preserve order.
*/
@@ -393,6 +395,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
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<T> implements MetaBeanInfo, SpiBeanType<T> {
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<T> implements MetaBeanInfo, SpiBeanType<T> {
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.
*/
@@ -28,6 +28,8 @@ public class DeployBeanPropertyLists {
private BeanProperty versionProperty;
private BeanProperty draftDirty;
private final BeanDescriptor<?> desc;
private final LinkedHashMap<String, BeanProperty> 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.
*/
@@ -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<T> beanType = query.getBeanType();
List<T> draftBeans = server.findList(query, transaction);
BeanDescriptor<T> desc = server.getBeanDescriptor(beanType);
if (draftBeans.isEmpty()) {
throw new IllegalArgumentException("No draft beans found to publish");
}
List<T> 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<Object> idList = new ArrayList<Object>();
@@ -156,6 +157,11 @@ public final class DefaultPersister implements Persister {
BeanManager<T> mgr = beanDescriptorManager.getBeanManager(beanType);
// collect a list of draft beans that have had their
// dirty status set back to false by the publish
List<T> draftUpdates = new ArrayList<T>();
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<T> 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<T> 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 <T> void unsetDraftDirtyProperty(List<T> 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 <T> PersistRequestBean<T> createRequest(T bean, Transaction t, Object parentBean, BeanManager<?> mgr, PersistRequest.Type type, boolean saveRecurse) {
private <T> PersistRequestBean<T> 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) {
@@ -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("] ");
}
@@ -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)