#1447 - Delete query ... should honour cascading delete if defined

See also PR #1445 with the failing test case
This commit is contained in:
rob bygrave
2018-07-05 22:37:54 +12:00
parent 68649d0e98
commit 588aaf9361
9 changed files with 174 additions and 86 deletions
@@ -1384,7 +1384,18 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
try {
request.initTransIfRequired();
request.markNotQueryOnly();
return request.delete();
if (request.isDeleteByStatement()) {
return request.delete();
} else {
// escalate to fetch the ids of the beans to delete due
// to cascading deletes or l2 caching etc
List<Object> ids = request.findIds();
if (ids.isEmpty()) {
return 0;
} else {
return persister.deleteByIds(request.getBeanDescriptor(), ids, request.getTransaction(), false);
}
}
} finally {
request.endTransIfRequired();
}
@@ -116,6 +116,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
}
}
@Override
public boolean isDeleteByStatement() {
return beanDescriptor.isDeleteByStatement();
}
@Override
public boolean isMultiValueIdSupported() {
return beanDescriptor.isMultiValueIdSupported();
@@ -63,6 +63,11 @@ public interface Persister {
*/
int deleteMany(Class<?> beanType, Collection<?> ids, Transaction transaction, boolean permanent);
/**
* Delete multiple beans when escalated from a delete query.
*/
int deleteByIds(BeanDescriptor<?> descriptor, List<Object> idList, Transaction transaction, boolean permanent);
/**
* Execute the Update.
*/
@@ -171,4 +171,9 @@ public interface SpiOrmQueryRequest<T> extends BeanQueryRequest<T>, DocQueryRequ
* Set profile location for "find all" if not set.
*/
void profileLocationAll();
/**
* Return true if delete by statement is allowed for this type given cascade rules etc.
*/
boolean isDeleteByStatement();
}
@@ -680,6 +680,12 @@ public final class DefaultPersister implements Persister {
return delete(descriptor, id, null, transaction, deleteMode);
}
@Override
public int deleteByIds(BeanDescriptor<?> descriptor, List<Object> idList, Transaction transaction, boolean permanent) {
DeleteMode deleteMode = (permanent || !descriptor.isSoftDelete()) ? DeleteMode.HARD : DeleteMode.SOFT;
return delete(descriptor, null, idList, transaction, deleteMode);
}
/**
* Delete by Id or a List of Id's.
*/
@@ -739,18 +745,20 @@ public final class DefaultPersister implements Persister {
// OneToMany's with delete cascade
BeanPropertyAssocMany<?>[] manys = descriptor.propertiesManyDelete();
for (BeanPropertyAssocMany<?> many : manys) {
BeanDescriptor<?> targetDesc = many.getTargetDescriptor();
// only cascade soft deletes when supported by target
if (deleteMode.isHard() || targetDesc.isSoftDelete()) {
if (deleteMode.isHard() && targetDesc.isDeleteByStatement()) {
// we can just delete children with a single statement
SqlUpdate sqlDelete = many.deleteByParentId(id, idList);
executeSqlUpdate(sqlDelete, t);
} else {
// we need to fetch the Id's to delete (recurse or notify L2 cache)
List<Object> childIds = many.findIdsByParentId(id, idList, t, null);
if (!childIds.isEmpty()) {
delete(targetDesc, null, childIds, t, deleteMode);
if (!many.isManyToMany()) {
BeanDescriptor<?> targetDesc = many.getTargetDescriptor();
// only cascade soft deletes when supported by target
if (deleteMode.isHard() || targetDesc.isSoftDelete()) {
if (deleteMode.isHard() && targetDesc.isDeleteByStatement()) {
// we can just delete children with a single statement
SqlUpdate sqlDelete = many.deleteByParentId(id, idList);
executeSqlUpdate(sqlDelete, t);
} else {
// we need to fetch the Id's to delete (recurse or notify L2 cache)
List<Object> childIds = many.findIdsByParentId(id, idList, t, null);
if (!childIds.isEmpty()) {
delete(targetDesc, null, childIds, t, deleteMode);
}
}
}
}