diff --git a/src/main/java/com/avaje/ebean/annotation/SoftDelete.java b/src/main/java/com/avaje/ebean/annotation/SoftDelete.java
index 56fde390e..3cc7a1f35 100644
--- a/src/main/java/com/avaje/ebean/annotation/SoftDelete.java
+++ b/src/main/java/com/avaje/ebean/annotation/SoftDelete.java
@@ -9,19 +9,17 @@ import java.lang.annotation.Target;
* Used to indicate a property on an entity bean used to control 'soft delete'
* (also known as 'logical delete').
*
- * The property should be of type boolean, int or short.
+ * The property should be of type boolean.
*
+ * {@code
+ *
+ * @SoftDelete
+ * boolean deleted;
+ *
+ * }
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SoftDelete {
- /**
- * Specify the bind value that matches 'deleted' state.
- *
- * If not specified then for boolean this is true and
- * for int and short this value is 1.
- *
- */
- String value() default "";
}
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 f7691b92d..0f2e02d52 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java
@@ -22,6 +22,7 @@ import com.avaje.ebeaninternal.server.text.json.ReadJson;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.type.ScalarType;
+import com.avaje.ebeaninternal.server.type.ScalarTypeBoolean;
import com.avaje.ebeaninternal.util.ValueUtil;
import com.fasterxml.jackson.core.JsonToken;
import org.slf4j.Logger;
@@ -231,7 +232,9 @@ public class BeanProperty implements ElPropertyValue {
final boolean softDelete;
- final Object softDeleteValue;
+ final String softDeleteDbSet;
+
+ final String softDeleteDbPredicate;
final boolean indexed;
@@ -262,9 +265,6 @@ public class BeanProperty implements ElPropertyValue {
this.draftDirty = deploy.isDraftDirty();
this.draftOnly = deploy.isDraftOnly();
this.draftReset = deploy.isDraftReset();
- this.softDelete = deploy.isSoftDelete();
- this.softDeleteValue = deploy.getSoftDeleteValue();
-
this.secondaryTable = deploy.isSecondaryTable();
if (secondaryTable) {
this.secondaryTableJoin = new TableJoin(deploy.getSecondaryTableJoin());
@@ -308,6 +308,16 @@ public class BeanProperty implements ElPropertyValue {
this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(), false, null);
this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(), dbEncrypted, dbColumn);
+ this.softDelete = deploy.isSoftDelete();
+ if (softDelete) {
+ ScalarTypeBoolean.BooleanBase boolType = (ScalarTypeBoolean.BooleanBase)scalarType;
+ this.softDeleteDbSet = dbColumn+"="+boolType.getDbTrueLiteral();
+ this.softDeleteDbPredicate = dbColumn+"="+boolType.getDbFalseLiteral();
+ } else {
+ this.softDeleteDbSet = null;
+ this.softDeleteDbPredicate = null;
+ }
+
this.jsonSerialize = deploy.isJsonSerialize();
this.jsonDeserialize = deploy.isJsonDeserialize();
}
@@ -352,7 +362,8 @@ public class BeanProperty implements ElPropertyValue {
this.draftOnly = source.draftOnly;
this.draftReset = source.draftReset;
this.softDelete = source.softDelete;
- this.softDeleteValue = source.softDeleteValue;
+ this.softDeleteDbSet = source.softDeleteDbSet;
+ this.softDeleteDbPredicate = source.softDeleteDbPredicate;
this.fetchEager = source.fetchEager;
this.unidirectionalShadow = source.unidirectionalShadow;
this.discriminator = source.discriminator;
@@ -624,22 +635,26 @@ public class BeanProperty implements ElPropertyValue {
}
}
+ /**
+ * Return the DB literal expression to set the deleted state to true.
+ */
public String getSoftDeleteDbSet() {
- return dbColumn +"=true";
+ return softDeleteDbSet;
}
/**
* Return the DB literal predicate used to filter out soft deleted rows from a query.
*/
public String getSoftDeleteDbPredicate(String tableAlias) {
- return tableAlias+"."+dbColumn+"=false";
+ return tableAlias+"."+softDeleteDbPredicate;
}
/**
* Set the soft delete property value on the bean without invoking lazy loading.
*/
public void setSoftDeleteValue(EntityBean bean) {
- setValue(bean, softDeleteValue);
+ // assumes boolean deleted true being set which is ok limitation for now
+ setValue(bean, true);
bean._ebean_getIntercept().setChangedProperty(propertyIndex);
}
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 b4ab4b93d..30858aac0 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
@@ -1,6 +1,7 @@
package com.avaje.ebeaninternal.server.deploy.meta;
import com.avaje.ebean.annotation.CreatedTimestamp;
+import com.avaje.ebean.annotation.SoftDelete;
import com.avaje.ebean.annotation.UpdatedTimestamp;
import com.avaje.ebean.annotation.WhenCreated;
import com.avaje.ebean.annotation.WhenModified;
@@ -190,7 +191,6 @@ public class DeployBeanProperty {
private boolean draftReset;
private boolean softDelete;
- private String softDeleteValue = "";
public DeployBeanProperty(DeployBeanDescriptor> desc, Class> propertyType, ScalarType> scalarType, ScalarTypeConverter, ?> typeConverter) {
this.desc = desc;
@@ -224,6 +224,8 @@ public class DeployBeanProperty {
return AUDITCOLUMN_ORDER;
} else if (field.getAnnotation(Version.class) != null) {
return VERSIONCOLUMN_ORDER;
+ } else if (field.getAnnotation(SoftDelete.class) != null) {
+ return VERSIONCOLUMN_ORDER;
}
return 0;
}
@@ -876,42 +878,12 @@ public class DeployBeanProperty {
return draftReset;
}
- public void setSoftDelete(String softDeleteValue) {
+ public void setSoftDelete() {
this.softDelete = true;
- this.softDeleteValue = softDeleteValue;
}
public boolean isSoftDelete() {
return softDelete;
}
- public Object getSoftDeleteValue() {
- return !softDelete ? null : "".equals(softDeleteValue) ? defaultSoftDeleteValue() : parseSoftDeleteValue();
- }
-
- private Object parseSoftDeleteValue() {
- if (Boolean.class.equals(propertyType) || boolean.class.equals(propertyType)) {
- return Boolean.parseBoolean(softDeleteValue);
- }
- if (Integer.class.equals(propertyType) || int.class.equals(propertyType)) {
- return Integer.parseInt(softDeleteValue);
- }
- if (Short.class.equals(propertyType) || short.class.equals(propertyType)) {
- return Short.parseShort(softDeleteValue);
- }
- throw new IllegalStateException("@SoftDelete on ["+getFullBeanName()+"] mapped to unsupported type propertyType["+propertyType+"]");
- }
-
- private Object defaultSoftDeleteValue() {
- if (Boolean.class.equals(propertyType) || boolean.class.equals(propertyType)) {
- return Boolean.TRUE;
- }
- if (Integer.class.equals(propertyType) || int.class.equals(propertyType)) {
- return Integer.valueOf(1);
- }
- if (Short.class.equals(propertyType) || short.class.equals(propertyType)) {
- return Short.valueOf("1");
- }
- throw new IllegalStateException("@SoftDelete on ["+getFullBeanName()+"] mapped to unsupported type propertyType["+propertyType+"]");
- }
}
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 851fdd46f..fde8d0041 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
@@ -160,7 +160,7 @@ public class AnnotationFields extends AnnotationParser {
}
SoftDelete softDelete = get(prop, SoftDelete.class);
if (softDelete != null) {
- prop.setSoftDelete(softDelete.value());
+ prop.setSoftDelete();
}
DbJson dbJson = get(prop, DbJson.class);
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java
index f591337c7..254fd6d78 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java
@@ -45,27 +45,12 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
protected void configureQuery(SpiQuery> query, String lazyLoadProperty) {
- // propagate the readOnly state
- if (parent.isReadOnly() != null) {
- query.setReadOnly(parent.isReadOnly());
- }
- // propagate the asOf and lazy loading mode
- query.setDisableLazyLoading(parent.isDisableLazyLoading());
- query.asOf(parent.getAsOf());
+ parent.propagateQueryState(query);
query.setParentNode(objectGraphNode);
query.setLazyLoadProperty(lazyLoadProperty);
- if (parent.isAsDraft()) {
- query.asDraft();
- }
- if (parent.isDisableReadAudit()) {
- query.setDisableReadAuditing();
- }
if (queryProps != null) {
queryProps.configureBeanQuery(query);
}
- if (parent.isUseAutoTune()) {
- query.setAutoTune(true);
- }
}
protected void register(EntityBeanIntercept ebi) {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java
index 99b563d14..db7f1c366 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java
@@ -43,6 +43,7 @@ public class DLoadContext implements LoadContext {
private final int defaultBatchSize;
private final boolean disableLazyLoading;
private final boolean disableReadAudit;
+ private final boolean includeSoftDeletes;
/**
* The path relative to the root of the object graph.
@@ -67,6 +68,7 @@ public class DLoadContext implements LoadContext {
SpiQuery> query = request.getQuery();
this.asOf = query.getAsOf();
this.asDraft = query.isAsDraft();
+ this.includeSoftDeletes = query.isIncludeSoftDeletes();
this.readOnly = query.isReadOnly();
this.disableReadAudit = query.isDisableReadAudit();
this.disableLazyLoading = query.isDisableLazyLoading();
@@ -201,10 +203,6 @@ public class DLoadContext implements LoadContext {
return new ObjectGraphNode(origin, path);
}
- public boolean isUseAutoTune() {
- return useProfiling;
- }
-
protected String getFullPath(String path) {
if (relativePath == null) {
return path;
@@ -225,34 +223,6 @@ public class DLoadContext implements LoadContext {
return readOnly;
}
- /**
- * Return the 'as of' timestamp that should propagate to secondary queries.
- */
- protected Timestamp getAsOf() {
- return asOf;
- }
-
- /**
- * Return true if the root query is a 'asDraft' query that should propagate to secondary queries.
- */
- protected boolean isAsDraft() {
- return asDraft;
- }
-
- /**
- * Return true if disable read auditing should propagate to secondary queries.
- */
- protected boolean isDisableReadAudit() {
- return disableReadAudit;
- }
-
- /**
- * Return true if disable lazy loading should propagate to secondary queries.
- */
- protected boolean isDisableLazyLoading() {
- return disableLazyLoading;
- }
-
public PersistenceContext getPersistenceContext() {
return persistenceContext;
}
@@ -335,4 +305,26 @@ public class DLoadContext implements LoadContext {
return desc.getBeanPropertyFromPath(path);
}
+ /**
+ * Propagate the original query settings (draft, asOf etc) to the secondary queries.
+ */
+ public void propagateQueryState(SpiQuery> query) {
+ if (readOnly != null) {
+ query.setReadOnly(readOnly);
+ }
+ query.setDisableLazyLoading(disableLazyLoading);
+ query.asOf(asOf);
+ if (asDraft) {
+ query.asDraft();
+ }
+ if (includeSoftDeletes) {
+ query.includeSoftDeletes();
+ }
+ if (disableReadAudit) {
+ query.setDisableReadAuditing();
+ }
+ if (useProfiling) {
+ query.setAutoTune(true);
+ }
+ }
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java
index 6e5f8a763..6ed015ce0 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java
@@ -56,27 +56,11 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
public void configureQuery(SpiQuery> query) {
- // propagate the readOnly state
- if (parent.isReadOnly() != null) {
- query.setReadOnly(parent.isReadOnly());
- }
-
- // propagate the asOf and lazy loading mode
- query.setDisableLazyLoading(parent.isDisableLazyLoading());
- query.asOf(parent.getAsOf());
+ parent.propagateQueryState(query);
query.setParentNode(objectGraphNode);
- if (parent.isAsDraft()) {
- query.asDraft();
- }
- if (parent.isDisableReadAudit()) {
- query.setDisableReadAuditing();
- }
if (queryProps != null) {
queryProps.configureBeanQuery(query);
}
- if (parent.isUseAutoTune()) {
- query.setAutoTune(true);
- }
}
public BeanPropertyAssocMany> getBeanProperty() {
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 ef6b4b61d..711147526 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java
@@ -1245,9 +1245,10 @@ public final class DefaultPersister implements Persister {
BeanPropertyAssocMany>[] manys = desc.propertiesManyDelete();
for (int i = 0; i < manys.length; i++) {
if (manys[i].isManyToMany()) {
- // delete associated rows from intersection table
- deleteAssocManyIntersection(parentBean, manys[i], t, request.isPublish());
-
+ if (!softDelete) {
+ // delete associated rows from intersection table (but not during soft delete)
+ deleteAssocManyIntersection(parentBean, manys[i], t, request.isPublish());
+ }
} else {
if (ModifyListenMode.REMOVALS.equals(manys[i].getModifyListenMode())) {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java
index 94d85293e..154d4a331 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java
@@ -29,6 +29,16 @@ public class ScalarTypeBoolean {
super(true, Types.BOOLEAN);
}
+ @Override
+ public String getDbFalseLiteral() {
+ return "false";
+ }
+
+ @Override
+ public String getDbTrueLiteral() {
+ return "true";
+ }
+
public Boolean toBeanType(Object value) {
return BasicTypeConverter.toBoolean(value);
}
@@ -68,6 +78,16 @@ public class ScalarTypeBoolean {
super(true, Types.BIT);
}
+ @Override
+ public String getDbFalseLiteral() {
+ return "0";
+ }
+
+ @Override
+ public String getDbTrueLiteral() {
+ return "1";
+ }
+
public Boolean toBeanType(Object value) {
return BasicTypeConverter.toBoolean(value);
}
@@ -106,6 +126,16 @@ public class ScalarTypeBoolean {
this.falseValue = falseValue;
}
+ @Override
+ public String getDbFalseLiteral() {
+ return falseValue.toString();
+ }
+
+ @Override
+ public String getDbTrueLiteral() {
+ return trueValue.toString();
+ }
+
@Override
public int getLength() {
return 1;
@@ -179,6 +209,16 @@ public class ScalarTypeBoolean {
this.falseValue = falseValue;
}
+ @Override
+ public String getDbFalseLiteral() {
+ return "'"+falseValue+"'";
+ }
+
+ @Override
+ public String getDbTrueLiteral() {
+ return "'"+trueValue+"'";
+ }
+
@Override
public int getLength() {
// typically this will return 1
@@ -245,6 +285,16 @@ public class ScalarTypeBoolean {
super(Boolean.class, jdbcNative, jdbcType);
}
+ /**
+ * Return the DB literal value for false.
+ */
+ public abstract String getDbFalseLiteral();
+
+ /**
+ * Return the DB literal value for true.
+ */
+ public abstract String getDbTrueLiteral();
+
public String formatValue(Boolean t) {
return t.toString();
}
diff --git a/src/test/java/com/avaje/tests/model/softdelete/EBasicNoSDChild.java b/src/test/java/com/avaje/tests/model/softdelete/EBasicNoSDChild.java
new file mode 100644
index 000000000..5b5c51f6a
--- /dev/null
+++ b/src/test/java/com/avaje/tests/model/softdelete/EBasicNoSDChild.java
@@ -0,0 +1,72 @@
+package com.avaje.tests.model.softdelete;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+import javax.persistence.Version;
+
+@Entity
+public class EBasicNoSDChild {
+
+ @Id
+ Long id;
+
+ @Version
+ Long version;
+
+ @ManyToOne(optional = false)
+ EBasicSoftDelete owner;
+
+ String childName;
+
+ long amount;
+
+ public EBasicNoSDChild(EBasicSoftDelete owner, String childName, long amount) {
+ this.owner = owner;
+ this.childName = childName;
+ this.amount = amount;
+ }
+
+ public EBasicNoSDChild() {
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Long getVersion() {
+ return version;
+ }
+
+ public void setVersion(Long version) {
+ this.version = version;
+ }
+
+ public EBasicSoftDelete getOwner() {
+ return owner;
+ }
+
+ public void setOwner(EBasicSoftDelete owner) {
+ this.owner = owner;
+ }
+
+ public String getChildName() {
+ return childName;
+ }
+
+ public void setChildName(String childName) {
+ this.childName = childName;
+ }
+
+ public long getAmount() {
+ return amount;
+ }
+
+ public void setAmount(long amount) {
+ this.amount = amount;
+ }
+}
diff --git a/src/test/java/com/avaje/tests/model/softdelete/EBasicSDChild.java b/src/test/java/com/avaje/tests/model/softdelete/EBasicSDChild.java
new file mode 100644
index 000000000..ceee82f97
--- /dev/null
+++ b/src/test/java/com/avaje/tests/model/softdelete/EBasicSDChild.java
@@ -0,0 +1,48 @@
+package com.avaje.tests.model.softdelete;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+
+@Entity
+public class EBasicSDChild extends BaseSoftDelete {
+
+ @ManyToOne(optional = false)
+ EBasicSoftDelete owner;
+
+ String childName;
+
+ long amount;
+
+ public EBasicSDChild(EBasicSoftDelete owner, String childName, long amount) {
+ this.owner = owner;
+ this.childName = childName;
+ this.amount = amount;
+ }
+
+ public EBasicSDChild() {
+ }
+
+ public EBasicSoftDelete getOwner() {
+ return owner;
+ }
+
+ public void setOwner(EBasicSoftDelete owner) {
+ this.owner = owner;
+ }
+
+ public String getChildName() {
+ return childName;
+ }
+
+ public void setChildName(String childName) {
+ this.childName = childName;
+ }
+
+ public long getAmount() {
+ return amount;
+ }
+
+ public void setAmount(long amount) {
+ this.amount = amount;
+ }
+}
diff --git a/src/test/java/com/avaje/tests/model/softdelete/EBasicSoftDelete.java b/src/test/java/com/avaje/tests/model/softdelete/EBasicSoftDelete.java
index 9058410ee..640d4296a 100644
--- a/src/test/java/com/avaje/tests/model/softdelete/EBasicSoftDelete.java
+++ b/src/test/java/com/avaje/tests/model/softdelete/EBasicSoftDelete.java
@@ -1,6 +1,9 @@
package com.avaje.tests.model.softdelete;
+import javax.persistence.CascadeType;
import javax.persistence.Entity;
+import javax.persistence.OneToMany;
+import java.util.List;
@Entity
public class EBasicSoftDelete extends BaseSoftDelete {
@@ -9,6 +12,13 @@ public class EBasicSoftDelete extends BaseSoftDelete {
String description;
+ @OneToMany(mappedBy = "owner", cascade = CascadeType.ALL)
+ List children;
+
+ @OneToMany(mappedBy = "owner", cascade = CascadeType.ALL)
+ List nosdChildren;
+
+
public String getName() {
return name;
}
@@ -24,4 +34,28 @@ public class EBasicSoftDelete extends BaseSoftDelete {
public void setDescription(String description) {
this.description = description;
}
+
+ public List getChildren() {
+ return children;
+ }
+
+ public void setChildren(List children) {
+ this.children = children;
+ }
+
+ public void addChild(String childName, long amount) {
+ getChildren().add(new EBasicSDChild(this, childName, amount));
+ }
+
+ public List getNosdChildren() {
+ return nosdChildren;
+ }
+
+ public void setNosdChildren(List nosdChildren) {
+ this.nosdChildren = nosdChildren;
+ }
+
+ public void addNoSoftDeleteChild(String childName, long amount) {
+ getNosdChildren().add(new EBasicNoSDChild(this, childName, amount));
+ }
}
diff --git a/src/test/java/com/avaje/tests/model/softdelete/ESoftDelRole.java b/src/test/java/com/avaje/tests/model/softdelete/ESoftDelRole.java
new file mode 100644
index 000000000..44f29ca4f
--- /dev/null
+++ b/src/test/java/com/avaje/tests/model/softdelete/ESoftDelRole.java
@@ -0,0 +1,35 @@
+package com.avaje.tests.model.softdelete;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.ManyToMany;
+import java.util.List;
+
+@Entity
+public class ESoftDelRole extends BaseSoftDelete {
+
+ String roleName;
+
+ @ManyToMany(cascade = CascadeType.ALL)
+ List users;
+
+ public ESoftDelRole(String roleName) {
+ this.roleName = roleName;
+ }
+
+ public String getRoleName() {
+ return roleName;
+ }
+
+ public void setRoleName(String roleName) {
+ this.roleName = roleName;
+ }
+
+ public List getUsers() {
+ return users;
+ }
+
+ public void setUsers(List users) {
+ this.users = users;
+ }
+}
diff --git a/src/test/java/com/avaje/tests/model/softdelete/ESoftDelUser.java b/src/test/java/com/avaje/tests/model/softdelete/ESoftDelUser.java
new file mode 100644
index 000000000..e1b41101d
--- /dev/null
+++ b/src/test/java/com/avaje/tests/model/softdelete/ESoftDelUser.java
@@ -0,0 +1,38 @@
+package com.avaje.tests.model.softdelete;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.ManyToMany;
+import java.util.List;
+
+@Entity
+public class ESoftDelUser extends BaseSoftDelete {
+
+ String userName;
+
+ @ManyToMany(cascade = CascadeType.ALL)
+ List roles;
+
+ public ESoftDelUser(String userName) {
+ this.userName = userName;
+ }
+
+ public ESoftDelUser() {
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public List getRoles() {
+ return roles;
+ }
+
+ public void setRoles(List roles) {
+ this.roles = roles;
+ }
+}
diff --git a/src/test/java/com/avaje/tests/softdelete/TestSoftDeleteBasic.java b/src/test/java/com/avaje/tests/softdelete/TestSoftDeleteBasic.java
index efade738c..4954ac54d 100644
--- a/src/test/java/com/avaje/tests/softdelete/TestSoftDeleteBasic.java
+++ b/src/test/java/com/avaje/tests/softdelete/TestSoftDeleteBasic.java
@@ -2,11 +2,15 @@ package com.avaje.tests.softdelete;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
+import com.avaje.ebean.Query;
import com.avaje.ebean.SqlQuery;
import com.avaje.ebean.SqlRow;
import com.avaje.tests.model.softdelete.EBasicSoftDelete;
+import org.avaje.ebeantest.LoggedSqlCollector;
import org.junit.Test;
+import java.util.List;
+
import static org.assertj.core.api.Assertions.assertThat;
public class TestSoftDeleteBasic extends BaseTestCase {
@@ -40,4 +44,84 @@ public class TestSoftDeleteBasic extends BaseTestCase {
assertThat(findInclude).isNotNull();
}
+
+ @Test
+ public void testCascadeSaveDelete() {
+
+ EBasicSoftDelete bean = new EBasicSoftDelete();
+ bean.setName("cascadeOne");
+ bean.addChild("child1", 10);
+ bean.addChild("child2", 20);
+ bean.addChild("child3", 30);
+ bean.addNoSoftDeleteChild("nsd1", 101);
+ bean.addNoSoftDeleteChild("nsd2", 102);
+
+ Ebean.save(bean);
+
+ LoggedSqlCollector.start();
+
+ Ebean.delete(bean);
+
+ List loggedSql = LoggedSqlCollector.stop();
+
+ // The children without SoftDelete are left as is (so no third statement)
+ assertThat(loggedSql).hasSize(2);
+
+ // first statement is a single bulk update of the children with SoftDelete
+ assertThat(loggedSql.get(0)).contains("update ebasic_sdchild set deleted=");
+ assertThat(loggedSql.get(0)).contains("where owner_id = ?");
+
+ // second statement is the top level bean
+ assertThat(loggedSql.get(1)).contains("update ebasic_soft_delete set version=?, deleted=? where id=? and version=?");
+
+ }
+
+
+ @Test
+ public void testFetch() {
+
+ EBasicSoftDelete bean = new EBasicSoftDelete();
+ bean.setName("cascadeOne");
+ bean.addChild("child1", 10);
+ bean.addChild("child2", 20);
+ bean.addChild("child3", 30);
+ bean.addNoSoftDeleteChild("nsd1", 101);
+ bean.addNoSoftDeleteChild("nsd2", 102);
+
+ Ebean.save(bean);
+
+ Ebean.delete(bean.getChildren().get(1));
+
+ LoggedSqlCollector.start();
+
+ Query query1 =
+ Ebean.find(EBasicSoftDelete.class)
+ .fetch("children")
+ .where().eq("id", bean.getId())
+ .query();
+
+ List fetch1 = query1.findList();
+ String generatedSql = query1.getGeneratedSql();
+
+ // first statement is a single bulk update of the children with SoftDelete
+ assertThat(generatedSql).contains("t0.deleted=");
+ assertThat(generatedSql).contains("t1.deleted=");
+ assertThat(fetch1.get(0).getChildren()).hasSize(2);
+
+ assertThat(fetch1.get(0).getNosdChildren()).hasSize(2);
+
+
+ // fetch again using lazy loading
+ EBasicSoftDelete fetchWithLazy = Ebean.find(EBasicSoftDelete.class, bean.getId());
+ assertThat(fetchWithLazy.getChildren()).hasSize(2);
+
+ // fetch includeSoftDeletes using lazy loading
+ EBasicSoftDelete fetchAllWithLazy =
+ Ebean.find(EBasicSoftDelete.class)
+ .setId(bean.getId())
+ .includeSoftDeletes()
+ .findUnique();
+
+ assertThat(fetchAllWithLazy.getChildren()).hasSize(3);
+ }
}
diff --git a/src/test/java/com/avaje/tests/softdelete/TestSoftDeleteManyToMany.java b/src/test/java/com/avaje/tests/softdelete/TestSoftDeleteManyToMany.java
new file mode 100644
index 000000000..b6852d5ce
--- /dev/null
+++ b/src/test/java/com/avaje/tests/softdelete/TestSoftDeleteManyToMany.java
@@ -0,0 +1,43 @@
+package com.avaje.tests.softdelete;
+
+import com.avaje.ebean.BaseTestCase;
+import com.avaje.ebean.Ebean;
+import com.avaje.tests.model.softdelete.ESoftDelRole;
+import com.avaje.tests.model.softdelete.ESoftDelUser;
+import org.avaje.ebeantest.LoggedSqlCollector;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class TestSoftDeleteManyToMany extends BaseTestCase {
+
+ @Test
+ public void test() {
+
+ ESoftDelRole role1 = new ESoftDelRole("role1");
+ ESoftDelRole role2 = new ESoftDelRole("role2");
+
+ Ebean.save(role1);
+ Ebean.save(role2);
+
+ ESoftDelUser user1 = new ESoftDelUser("user1");
+ user1.getRoles().add(role1);
+ user1.getRoles().add(role2);
+
+ Ebean.save(user1);
+
+
+ LoggedSqlCollector.start();
+ Ebean.delete(user1);
+
+ List loggedSql = LoggedSqlCollector.stop();
+
+ // No Delete from the relationship table
+ assertThat(loggedSql).hasSize(1);
+ assertThat(loggedSql.get(0)).contains("update esoft_del_user set version=?, deleted=? where id=? and version=?;");
+
+ }
+
+}