#118 - ENH: Add support for for Soft Deletes (Logical Deletion) ...

This commit is contained in:
Robin Bygrave
2015-12-02 23:46:26 +13:00
parent f39a684c8e
commit aea7a7fd65
17 changed files with 392 additions and 20 deletions
+18
View File
@@ -704,6 +704,10 @@ public final class Ebean {
* you automatically.
* </p>
* <p>
* If the bean is configured with <code>@SoftDelete</code> then this will perform a soft
* delete rather than a hard/permanent delete.
* </p>
* <p>
* If the Bean does not have a version property (or loaded version property) and
* the bean does not exist then this returns false indicating that nothing was
* deleted. Note that, if JDBC batch mode is used then this always returns true.
@@ -713,6 +717,13 @@ public final class Ebean {
return serverMgr.getDefaultServer().delete(bean);
}
/**
* Delete the bean in permanent fashion (will not use soft delete).
*/
public static boolean deletePermanent(Object bean) throws OptimisticLockException {
return serverMgr.getDefaultServer().deletePermanent(bean);
}
/**
* Delete the bean given its type and id.
*/
@@ -734,6 +745,13 @@ public final class Ebean {
return serverMgr.getDefaultServer().deleteAll(beans);
}
/**
* Delete permanent all the beans in the Collection (will not use soft delete).
*/
public static int deleteAllPermanent(Collection<?> beans) throws OptimisticLockException {
return serverMgr.getDefaultServer().deleteAllPermanent(beans);
}
/**
* Refresh the values of a bean.
* <p>
@@ -1273,6 +1273,26 @@ public interface EbeanServer {
*/
boolean delete(Object bean, Transaction transaction) throws OptimisticLockException;
/**
* Delete a bean permanently without soft delete.
*/
boolean deletePermanent(Object bean) throws OptimisticLockException;
/**
* Delete a bean permanently without soft delete using an explicit transaction.
*/
boolean deletePermanent(Object bean, Transaction transaction) throws OptimisticLockException;
/**
* Delete all the beans in the collection permanently without soft delete.
*/
int deleteAllPermanent(Collection<?> beans) throws OptimisticLockException;
/**
* Delete all the beans in the collection permanently without soft delete using an explicit transaction.
*/
int deleteAllPermanent(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
/**
* Delete the bean given its type and id.
*/
@@ -1883,9 +1883,28 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
/**
* Delete the bean with the explicit transaction.
*/
public boolean delete(Object bean, Transaction t) {
return persister.delete(checkEntityBean(bean), t);
public boolean delete(Object bean, Transaction t) throws OptimisticLockException {
return persister.delete(checkEntityBean(bean), t, false);
}
@Override
public boolean deletePermanent(Object bean) throws OptimisticLockException {
return deletePermanent(bean, null);
}
@Override
public boolean deletePermanent(Object bean, Transaction t) throws OptimisticLockException {
return persister.delete(checkEntityBean(bean), t, true);
}
@Override
public int deleteAllPermanent(Collection<?> beans) {
return deleteAllInternal(beans.iterator(), null, true);
}
@Override
public int deleteAllPermanent(Collection<?> beans, Transaction t) {
return deleteAllInternal(beans.iterator(), t, true);
}
/**
@@ -1893,7 +1912,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
*/
@Override
public int deleteAll(Collection<?> beans) {
return deleteAllInternal(beans.iterator(), null);
return deleteAllInternal(beans.iterator(), null, false);
}
/**
@@ -1901,13 +1920,13 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
*/
@Override
public int deleteAll(Collection<?> beans, Transaction t) {
return deleteAllInternal(beans.iterator(), t);
return deleteAllInternal(beans.iterator(), t, false);
}
/**
* Delete all the beans in the iterator with an explicit transaction.
*/
private int deleteAllInternal(Iterator<?> it, Transaction t) {
private int deleteAllInternal(Iterator<?> it, Transaction t, boolean permanent) {
TransWrapper wrap = initTransIfRequired(t);
@@ -1917,7 +1936,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
int deleteCount = 0;
while (it.hasNext()) {
EntityBean bean = checkEntityBean(it.next());
persister.delete(bean, trans);
persister.delete(bean, trans, permanent);
deleteCount++;
}
@@ -12,7 +12,7 @@ import com.avaje.ebeaninternal.server.persist.PersistExecute;
public abstract class PersistRequest extends BeanRequest implements BatchPostExecute {
public enum Type {
INSERT, UPDATE, DELETE, SOFT_DELETE, UPDATESQL, CALLABLESQL
INSERT, UPDATE, DELETE, SOFT_DELETE, DELETE_PERMANENT, UPDATESQL, CALLABLESQL
}
protected boolean persistCascade;
@@ -256,6 +256,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
beanDescriptor.cacheHandleUpdate(idValue, this);
break;
case DELETE:
case SOFT_DELETE:
// Bean deleted from cache early via postDelete()
break;
default:
@@ -546,6 +547,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
switch (type) {
case DELETE:
case SOFT_DELETE:
postDelete();
break;
case UPDATE:
@@ -614,6 +616,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
controller.postUpdate(this);
break;
case DELETE:
case SOFT_DELETE:
controller.postDelete(this);
break;
default:
@@ -635,6 +638,9 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
case DELETE:
transaction.logSummary("Deleted [" + name + "] [" + idValue + "]" + draft);
break;
case SOFT_DELETE:
transaction.logSummary("SoftDelete [" + name + "] [" + idValue + "]" + draft);
break;
default:
break;
}
@@ -66,7 +66,7 @@ public interface Persister {
/**
* Delete the bean.
*/
boolean delete(EntityBean entityBean, Transaction t);
boolean delete(EntityBean entityBean, Transaction t, boolean permanent);
/**
* Delete multiple beans given a collection of Id values.
@@ -683,6 +683,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
case INSERT:
return changeLogFilter.includeInsert(request) ? insertBeanChange(request): null;
case UPDATE:
case SOFT_DELETE:
return changeLogFilter.includeUpdate(request) ? updateBeanChange(request): null;
case DELETE:
return changeLogFilter.includeDelete(request) ? deleteBeanChange(request) :null;
@@ -138,6 +138,7 @@ public class BatchedBeanHolder {
return inserts.size();
case UPDATE:
case SOFT_DELETE:
if (updates == null) {
updates = new ArrayList<PersistRequest>();
}
@@ -336,7 +336,7 @@ public final class DefaultPersister implements Persister {
*/
private void deleteRecurse(EntityBean detailBean, Transaction t, boolean softDelete) {
Type deleteType = softDelete ? Type.SOFT_DELETE : Type.DELETE;
Type deleteType = softDelete ? Type.SOFT_DELETE : Type.DELETE_PERMANENT;
deleteRequest(createRequest(detailBean, t, deleteType));
}
@@ -498,15 +498,16 @@ public final class DefaultPersister implements Persister {
* Delete the bean with the explicit transaction.
* Return false if the delete is executed without OCC and 0 rows were deleted.
*/
public boolean delete(EntityBean bean, Transaction t) {
public boolean delete(EntityBean bean, Transaction t, boolean permanent) {
PersistRequestBean<EntityBean> request = createRequest(bean, t, Type.DELETE);
Type deleteType = permanent ? Type.DELETE_PERMANENT : Type.DELETE;
PersistRequestBean<EntityBean> request = createRequest(bean, t, deleteType);
boolean deleted = deleteRequest(request);
if (request.isDraftable()) {
if (request.isDraftable() && request.getType() == Type.DELETE) {
// we have just deleting a draft bean so now we need to delete the
// associated 'live' bean. This is effectively an 'automatic publish'.
deleteRequest(createPublishRequest(request.createReference(), t, Type.DELETE, true));
deleteRequest(createPublishRequest(request.createReference(), t, Type.DELETE_PERMANENT, true));
}
return deleted;
}
@@ -541,8 +542,8 @@ public final class DefaultPersister implements Persister {
private void deleteList(List<?> beanList, Transaction t, boolean softDelete) {
for (int i = 0; i < beanList.size(); i++) {
// deleteRecurse((EntityBean) beanList.get(i), t, softDelete);
delete((EntityBean) beanList.get(i), t);
deleteRecurse((EntityBean) beanList.get(i), t, softDelete);
//delete((EntityBean) beanList.get(i), t);
}
}
@@ -590,7 +591,7 @@ public final class DefaultPersister implements Persister {
// We actually need to execute a query to get the foreign key values
// as they are required for the delete cascade. Query back just the
// Id and the appropriate foreign key values
Query<?> q = deleteRequiresQuery(descriptor, propImportDelete);
Query<?> q = deleteRequiresQuery(descriptor, propImportDelete, softDelete);
if (idList != null) {
q.where().idIn(idList);
if (t.isLogSummary()) {
@@ -701,7 +702,7 @@ public final class DefaultPersister implements Persister {
* We need to create and execute a query to get the foreign key values as
* the delete cascades to them (foreign keys).
*/
private Query<?> deleteRequiresQuery(BeanDescriptor<?> desc, BeanPropertyAssocOne<?>[] propImportDelete) {
private Query<?> deleteRequiresQuery(BeanDescriptor<?> desc, BeanPropertyAssocOne<?>[] propImportDelete, boolean softDelete) {
Query<?> q = server.createQuery(desc.getBeanType());
StringBuilder sb = new StringBuilder(30);
@@ -710,6 +711,10 @@ public final class DefaultPersister implements Persister {
}
q.setAutoTune(false);
q.select(sb.toString());
if (!softDelete) {
// hard delete so we want this query to include logically deleted rows (if any)
q.includeSoftDeletes();
}
return q;
}
@@ -1492,7 +1497,10 @@ public final class DefaultPersister implements Persister {
private <T> PersistRequestBean<T> createRequest(T bean, Transaction t, Object parentBean, BeanManager<?> mgr,
PersistRequest.Type type, boolean saveRecurse, boolean publish) {
if (type == Type.DELETE && mgr.getBeanDescriptor().isSoftDelete()) {
if (type == Type.DELETE_PERMANENT) {
type = Type.DELETE;
} else if (type == Type.DELETE && mgr.getBeanDescriptor().isSoftDelete()) {
// automatically convert to soft delete for types that support it
type = Type.SOFT_DELETE;
}
@@ -183,6 +183,7 @@ public class BeanPersistIds implements Serializable {
addUpdateId(id);
break;
case DELETE:
case SOFT_DELETE:
addDeleteId(id);
break;