#1369 - BeanListener and/or BeanPersistAdapter not fired on delete of children when cascaded

This commit is contained in:
rob bygrave
2018-05-06 20:21:10 +12:00
parent 49d9d88fbe
commit 33031d1c69
6 changed files with 211 additions and 6 deletions
@@ -1723,7 +1723,15 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* associated L2 bean caching.
*/
public boolean isDeleteByStatement() {
return deleteRecurseSkippable && !isBeanCaching();
return persistListener == null
&& persistController == null
&& deleteRecurseSkippable && !isBeanCaching();
}
public boolean isDeleteByBulk() {
return persistListener == null
&& persistController == null
&& propertiesManyToMany.length == 0;
}
/**
@@ -588,10 +588,18 @@ public final class DefaultPersister implements Persister {
}
}
private void deleteList(List<?> beanList, Transaction t, boolean softDelete) {
private void deleteList(List<?> beanList, SpiTransaction t, boolean softDelete, boolean children) {
if (children) {
t.depth(-1);
t.checkBatchEscalationOnCollection();
}
for (Object aBeanList : beanList) {
deleteRecurse((EntityBean) aBeanList, t, softDelete);
}
if (children) {
t.flushBatchOnCollection();
t.depth(+1);
}
}
/**
@@ -675,7 +683,7 @@ public final class DefaultPersister implements Persister {
t.logSummary("-- DeleteById of " + descriptor.getName() + " ids[" + idList + "] requires fetch of foreign key values");
}
List<?> beanList = server.findList(q, t);
deleteList(beanList, t, softDelete);
deleteList(beanList, t, softDelete, false);
return beanList.size();
} else {
@@ -1012,7 +1020,7 @@ public final class DefaultPersister implements Persister {
// cascade delete the beans in the collection
BeanDescriptor<?> targetDesc = many.getTargetDescriptor();
if (!softDelete || targetDesc.isSoftDelete()) {
if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isBeanCaching()) {
if (targetDesc.isDeleteByStatement()) {
// Just delete all the children with one statement
IntersectionRow intRow = many.buildManyDeleteChildren(parentBean, excludeDetailIds);
SqlUpdate sqlDelete = intRow.createDelete(server, softDelete);
@@ -1037,13 +1045,13 @@ public final class DefaultPersister implements Persister {
*/
private void deleteChildrenById(SpiTransaction t, BeanDescriptor<?> targetDesc, List<Object> childIds, boolean softDelete) {
if (targetDesc.propertiesManyToMany().length > 0) {
if (!targetDesc.isDeleteByBulk()) {
// convert into a list of reference objects and perform delete by object
List<Object> refList = new ArrayList<>(childIds.size());
for (Object id : childIds) {
refList.add(targetDesc.createReference(id, null));
}
deleteList(refList, t, softDelete);
deleteList(refList, t, softDelete, true);
} else {
// perform delete by statement if possible
@@ -0,0 +1,51 @@
package org.tests.delete;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@Entity
public class DcDetail {
@Id
long id;
@ManyToOne
DcMaster master;
String description;
long version;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public DcMaster getMaster() {
return master;
}
public void setMaster(DcMaster master) {
this.master = master;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
@@ -0,0 +1,28 @@
package org.tests.delete;
import io.ebean.event.AbstractBeanPersistListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class DcListener extends AbstractBeanPersistListener {
static List<Object> deleted = Collections.synchronizedList(new ArrayList<>());
@Override
public boolean isRegisterFor(Class<?> cls) {
return cls.equals(DcDetail.class);
}
@Override
public void deleted(Object bean) {
deleted.add(bean);
}
static List<Object> deletedBeans() {
List<Object> copy = new ArrayList<>(deleted);
deleted.clear();
return copy;
}
}
@@ -0,0 +1,59 @@
package org.tests.delete;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Version;
import java.util.List;
@Entity
public class DcMaster {
@Id
long id;
String name;
@OneToMany(mappedBy = "master", cascade = CascadeType.ALL)
List<DcDetail> details;
@Version
long version;
public DcMaster(String name) {
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<DcDetail> getDetails() {
return details;
}
public void setDetails(List<DcDetail> details) {
this.details = details;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
@@ -0,0 +1,51 @@
package org.tests.delete;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class TestDeleteCascadeWithListener extends BaseTestCase {
@Test
public void test() {
DcMaster m0 = new DcMaster("m0");
m0.getDetails().add(new DcDetail());
m0.getDetails().add(new DcDetail());
m0.getDetails().add(new DcDetail());
Ebean.save(m0);
DcMaster found = Ebean.find(DcMaster.class, m0.getId());
LoggedSqlCollector.start();
Ebean.delete(found);
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(5);
assertThat(sql.get(0)).contains("select t0.id from dc_detail t0 where master_id=?");
assertThat(sql.get(1)).contains("delete from dc_detail where id=?");
assertThat(sql.get(3)).contains("delete from dc_detail where id=?");
assertThat(sql.get(4)).contains("delete from dc_master where id=? and version=?");
awaitListenerPropagation();
List<Object> beans = DcListener.deletedBeans();
assertThat(beans).hasSize(3);
}
private void awaitListenerPropagation() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
}
}