mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Fix for #149 - OneToMany with nested ManyToMany deletion bug
This commit is contained in:
@@ -413,14 +413,14 @@ public final class DefaultPersister implements Persister {
|
||||
if (t.isPersistCascade()) {
|
||||
// OneToOne exported side with delete cascade
|
||||
BeanPropertyAssocOne<?>[] expOnes = descriptor.propertiesOneExportedDelete();
|
||||
for (int i = 0; i < expOnes.length; i++) {
|
||||
for (int i = 0; i < expOnes.length; i++) {
|
||||
BeanDescriptor<?> targetDesc = expOnes[i].getTargetDescriptor();
|
||||
if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isBeanCaching()) {
|
||||
SqlUpdate sqlDelete = expOnes[i].deleteByParentId(id, idList);
|
||||
executeSqlUpdate(sqlDelete, t);
|
||||
} else {
|
||||
List<Object> childIds = expOnes[i].findIdsByParentId(id, idList, t);
|
||||
delete(targetDesc, null, childIds, t);
|
||||
deleteChildrenById(t, targetDesc, childIds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1057,12 +1057,33 @@ public final class DefaultPersister implements Persister {
|
||||
Object parentId = desc.getId(parentBean);
|
||||
List<Object> idsByParentId = many.findIdsByParentId(parentId, null, t, excludeDetailIds);
|
||||
if (!idsByParentId.isEmpty()) {
|
||||
delete(targetDesc, null, idsByParentId, t);
|
||||
deleteChildrenById(t, targetDesc, idsByParentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cascade delete child entities by Id.
|
||||
* <p>
|
||||
* Will use delete by object if the child entity has manyToMany relationships.
|
||||
*/
|
||||
private void deleteChildrenById(SpiTransaction t, BeanDescriptor<?> targetDesc, List<Object> childIds) {
|
||||
|
||||
if (targetDesc.propertiesManyToMany().length > 0) {
|
||||
// convert into a list of reference objects and perform delete by object
|
||||
List<Object> refList = new ArrayList<Object>(childIds.size());
|
||||
for (Object id : childIds) {
|
||||
refList.add(targetDesc.createReference(null, id));
|
||||
}
|
||||
deleteList(refList, t);
|
||||
|
||||
} else {
|
||||
// perform delete by statement if possible
|
||||
delete(targetDesc, null, childIds, t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save any associated one beans.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.avaje.tests.m2m;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.tests.model.m2m.Permission;
|
||||
import com.avaje.tests.model.m2m.Role;
|
||||
import com.avaje.tests.model.m2m.Tenant;
|
||||
|
||||
public class TestM2MDeleteObjectWithCascadeToM2m extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
Ebean.createUpdate(Permission.class, "delete from Permission").execute();
|
||||
Ebean.createUpdate(Tenant.class, "delete from Tenant").execute();
|
||||
Ebean.createUpdate(Role.class, "delete from Role").execute();
|
||||
|
||||
Tenant tenant1 = new Tenant();
|
||||
tenant1.setName("Tenant");
|
||||
|
||||
Ebean.save(tenant1);
|
||||
|
||||
Permission p1 = new Permission();
|
||||
Permission p2 = new Permission();
|
||||
|
||||
p1.setName("p1");
|
||||
p2.setName("p2");
|
||||
|
||||
Ebean.save(p1);
|
||||
Ebean.save(p2);
|
||||
|
||||
Role role1 = new Role();
|
||||
role1.setName("RoleOne");
|
||||
role1.setTenant(tenant1);
|
||||
|
||||
Set<Permission> permissions = new HashSet<Permission>();
|
||||
List<Permission> permsList = Ebean.find(Permission.class).findList();
|
||||
permissions.addAll(permsList);
|
||||
|
||||
role1.setPermissions(permissions);
|
||||
|
||||
Ebean.save(role1);
|
||||
|
||||
|
||||
List<Tenant> tenantList = Ebean.find(Tenant.class).fetch("roles").findList();
|
||||
List<Role> roleList = Ebean.find(Role.class).fetch("permissions").findList();
|
||||
List<Permission> permissionList = Ebean.find(Permission.class).fetch("roles").findList();
|
||||
|
||||
Assert.assertEquals(1, tenantList.size());
|
||||
Assert.assertEquals(2, permissionList.size());
|
||||
Assert.assertEquals(1, roleList.size());
|
||||
|
||||
Ebean.delete(tenant1);
|
||||
|
||||
List<Tenant> tenantList2 = Ebean.find(Tenant.class).fetch("roles").findList();
|
||||
List<Role> roleList2 = Ebean.find(Role.class).fetch("permissions").findList();
|
||||
List<Permission> permissionList2 = Ebean.find(Permission.class).fetch("roles").findList();
|
||||
|
||||
Assert.assertEquals(0, tenantList2.size());
|
||||
Assert.assertEquals(0, roleList2.size());
|
||||
Assert.assertEquals(2, permissionList2.size());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.avaje.tests.m2m;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.tests.model.m2m.Permission;
|
||||
import com.avaje.tests.model.m2m.Role;
|
||||
import com.avaje.tests.model.m2m.Tenant;
|
||||
|
||||
public class TestM2mDeleteObject extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
Ebean.createUpdate(Permission.class, "delete from Permission").execute();
|
||||
Ebean.createUpdate(Tenant.class, "delete from Tenant").execute();
|
||||
Ebean.createUpdate(Role.class, "delete from Role").execute();
|
||||
|
||||
Tenant t = new Tenant();
|
||||
t.setName("tenant");
|
||||
|
||||
Ebean.save(t);
|
||||
|
||||
Permission p1 = new Permission();
|
||||
Permission p2 = new Permission();
|
||||
|
||||
p1.setName("p1");
|
||||
p2.setName("p2");
|
||||
|
||||
Ebean.save(p1);
|
||||
|
||||
Ebean.save(p2);
|
||||
|
||||
Role role1 = new Role();
|
||||
role1.setName("role");
|
||||
role1.setTenant(t);
|
||||
|
||||
Set<Permission> permissions = new HashSet<Permission>();
|
||||
List<Permission> permsList = Ebean.find(Permission.class).findList();
|
||||
permissions.addAll(permsList);
|
||||
|
||||
role1.setPermissions(permissions);
|
||||
|
||||
Ebean.save(role1);
|
||||
|
||||
List<Tenant> tenantList = Ebean.find(Tenant.class).fetch("roles").findList();
|
||||
List<Role> roleList = Ebean.find(Role.class).fetch("permissions").findList();
|
||||
List<Permission> permissionList = Ebean.find(Permission.class).fetch("roles").findList();
|
||||
|
||||
Assert.assertEquals(1, tenantList.size());
|
||||
Assert.assertEquals(2, permissionList.size());
|
||||
Assert.assertEquals(1, roleList.size());
|
||||
|
||||
Ebean.delete(role1);
|
||||
|
||||
List<Tenant> tenantList2 = Ebean.find(Tenant.class).fetch("roles").findList();
|
||||
List<Role> roleList2 = Ebean.find(Role.class).fetch("permissions").findList();
|
||||
List<Permission> permissionList2 = Ebean.find(Permission.class).fetch("roles").findList();
|
||||
|
||||
Assert.assertEquals(0, roleList2.size());
|
||||
Assert.assertEquals(1, tenantList2.size());
|
||||
Assert.assertEquals(2, permissionList2.size());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.avaje.tests.model.m2m;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import com.avaje.ebean.annotation.CacheStrategy;
|
||||
|
||||
/**
|
||||
* The Class Permission.
|
||||
*/
|
||||
@Entity
|
||||
@CacheStrategy(readOnly = true)
|
||||
@Table(name = "mt_permission")
|
||||
public class Permission {
|
||||
|
||||
@Id
|
||||
private UUID id;
|
||||
|
||||
@Column
|
||||
private String name;
|
||||
|
||||
@ManyToMany(mappedBy = "permissions")
|
||||
private Set<Role> roles;
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<Role> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(Set<Role> roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "name:" + name + "id:" + id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.avaje.tests.model.m2m;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Version;
|
||||
|
||||
/**
|
||||
* The Class Role.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "mt_role")
|
||||
public class Role {
|
||||
|
||||
@Id
|
||||
private UUID id;
|
||||
|
||||
@Column(length = 50)
|
||||
private String name;
|
||||
|
||||
@ManyToMany(cascade = CascadeType.REMOVE)
|
||||
private Set<Permission> permissions;
|
||||
|
||||
@ManyToOne
|
||||
private Tenant tenant;
|
||||
|
||||
@Version
|
||||
private Long version;
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<Permission> getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public void setPermissions(Set<Permission> permissions) {
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Tenant getTenant() {
|
||||
return tenant;
|
||||
}
|
||||
|
||||
public void setTenant(Tenant tenant) {
|
||||
this.tenant = tenant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "name:" + name + " id:" + id + " tenant:" + tenant;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.avaje.tests.model.m2m;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Version;
|
||||
|
||||
/**
|
||||
* The Class Tenant.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "mt_tenant")
|
||||
public class Tenant {
|
||||
|
||||
@Id
|
||||
private UUID id;
|
||||
|
||||
@Column
|
||||
private String name;
|
||||
|
||||
@OneToMany(mappedBy = "tenant", cascade = CascadeType.REMOVE)
|
||||
private Set<Role> roles;
|
||||
|
||||
@Version
|
||||
private Long version;
|
||||
|
||||
public Tenant() {
|
||||
}
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Set<Role> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(Set<Role> roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "name:" + name + " id:" + id;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user