mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Merge pull request #3154 from ebean-orm/feature/3134-idClass-delete
#3134 - String cannot be cast to class io.ebean.bean.EntityBean" when deleting entities that use cascade = [CascadeType.ALL] with composite keys
This commit is contained in:
@@ -212,7 +212,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
private final EntityBean prototypeEntityBean;
|
||||
|
||||
private final IdBinder idBinder;
|
||||
private final String idSelect;
|
||||
private String idBinderInLHSSql;
|
||||
private String idBinderIdSql;
|
||||
private String deleteByIdSql;
|
||||
@@ -355,23 +354,11 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
propertiesIndex[i] = propMap.get(ebi.property(i));
|
||||
}
|
||||
}
|
||||
idSelect = initIdSelect();
|
||||
}
|
||||
|
||||
String initIdSelect() {
|
||||
if (idProperty != null && !idProperty.name().equals("_idClass")) {
|
||||
return idProperty.name();
|
||||
} else if (entityType == EntityType.EMBEDDED) {
|
||||
return null;
|
||||
} else {
|
||||
StringJoiner sj = new StringJoiner(",");
|
||||
for (BeanProperty prop : propertiesNonMany) {
|
||||
if (prop.isImportedPrimaryKey()) {
|
||||
sj.add(prop.name());
|
||||
}
|
||||
}
|
||||
return sj.toString().intern();
|
||||
}
|
||||
public String idSelect() {
|
||||
if (idBinder == null) throw new UnsupportedOperationException();
|
||||
return idBinder.idSelect();
|
||||
}
|
||||
|
||||
public boolean isJacksonCorePresent() {
|
||||
@@ -2450,9 +2437,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
|
||||
BeanProperty _findBeanProperty(String propName) {
|
||||
BeanProperty prop = propMap.get(propName);
|
||||
if (prop == null && inheritInfo != null) {
|
||||
// search in sub types...
|
||||
return inheritInfo.findSubTypeProperty(propName);
|
||||
if (prop == null) {
|
||||
if ("_idClass".equals(propName)) {
|
||||
return idProperty;
|
||||
} else if (inheritInfo != null) {
|
||||
// search in sub types...
|
||||
return inheritInfo.findSubTypeProperty(propName);
|
||||
}
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
@@ -3057,10 +3048,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
return idProperty;
|
||||
}
|
||||
|
||||
public String idSelect() {
|
||||
return idSelect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this bean should be inserted rather than updated.
|
||||
*
|
||||
|
||||
@@ -23,11 +23,6 @@ abstract class BeanDescriptorElement<T> extends BeanDescriptor<T> {
|
||||
this.elementHelp = elementHelp;
|
||||
}
|
||||
|
||||
@Override
|
||||
String initIdSelect() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String shortName(String name) {
|
||||
int pos = name.lastIndexOf('.');
|
||||
if (pos > 1) {
|
||||
|
||||
@@ -26,6 +26,8 @@ public interface IdBinder {
|
||||
*/
|
||||
void initialise();
|
||||
|
||||
String idSelect();
|
||||
|
||||
/**
|
||||
* Return true if this is a compound key and must use expanded and or form.
|
||||
*/
|
||||
|
||||
@@ -45,6 +45,11 @@ public final class IdBinderEmbedded implements IdBinder {
|
||||
this.idInValueSql = idInExpandedForm ? idInExpanded() : idInCompressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String idSelect() {
|
||||
return embIdProperty.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIdInExpandedForm() {
|
||||
return idInExpandedForm;
|
||||
|
||||
@@ -2,11 +2,11 @@ package io.ebeaninternal.server.deploy.id;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.server.bind.DataBind;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.bind.DataBind;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
@@ -27,6 +27,11 @@ final class IdBinderEmpty implements IdBinder {
|
||||
public void initialise() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String idSelect() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIdInExpandedForm() {
|
||||
return false;
|
||||
|
||||
@@ -45,6 +45,11 @@ public final class IdBinderSimple implements IdBinder {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public String idSelect() {
|
||||
return idProperty.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIdInExpandedForm() {
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.tests.cache.embeddedid;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@IdClass(ConceptId.class)
|
||||
@SuppressWarnings("unused")
|
||||
public class Concept {
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
@Id
|
||||
private String networkId;
|
||||
|
||||
private String label;
|
||||
|
||||
@OneToMany(mappedBy = "from", cascade = {CascadeType.ALL})
|
||||
private List<Connection> outgoingConnections;
|
||||
|
||||
@OneToMany(mappedBy = "to", cascade = {CascadeType.ALL})
|
||||
private List<Connection> incomingConnections;
|
||||
|
||||
public Concept(String networkId, String id, String label) {
|
||||
this.networkId = networkId;
|
||||
this.id = id;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String networkId() {
|
||||
return networkId;
|
||||
}
|
||||
|
||||
public void setNetworkId(String networkId) {
|
||||
this.networkId = networkId;
|
||||
}
|
||||
|
||||
public String label() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.tests.cache.embeddedid;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
|
||||
@Embeddable
|
||||
public class ConceptId {
|
||||
private final String networkId;
|
||||
private final String id;
|
||||
|
||||
public ConceptId(String networkId, String id) {
|
||||
this.networkId = networkId;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ConceptId conceptId = (ConceptId) o;
|
||||
if (!id.equals(conceptId.id)) return false;
|
||||
return networkId.equals(conceptId.networkId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = id.hashCode();
|
||||
result = 31 * result + networkId.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package org.tests.cache.embeddedid;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Entity
|
||||
@IdClass(ConceptId.class)
|
||||
@SuppressWarnings("unused")
|
||||
public class Connection {
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
@Id
|
||||
private String networkId;
|
||||
|
||||
private String label;
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumns({
|
||||
@JoinColumn(name = "from_conc", referencedColumnName = "id", nullable = false),
|
||||
@JoinColumn(
|
||||
name = "network_id", referencedColumnName = "network_id",
|
||||
nullable = false, insertable = false, updatable = false
|
||||
)
|
||||
})
|
||||
private Concept from;
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumns({
|
||||
@JoinColumn(name = "to_conc", referencedColumnName = "id", nullable = false),
|
||||
@JoinColumn(
|
||||
name = "network_id", referencedColumnName = "network_id",
|
||||
nullable = false, insertable = false, updatable = false
|
||||
)
|
||||
})
|
||||
private Concept to;
|
||||
|
||||
public Connection(
|
||||
String networkId, String id, String label,
|
||||
Concept from, Concept to
|
||||
) {
|
||||
this.networkId = networkId;
|
||||
this.id = id;
|
||||
this.label = label;
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getNetworkId() {
|
||||
return networkId;
|
||||
}
|
||||
|
||||
public void setNetworkId(String networkId) {
|
||||
this.networkId = networkId;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public Concept from() {
|
||||
return from;
|
||||
}
|
||||
|
||||
public void setFrom(Concept from) {
|
||||
this.from = from;
|
||||
}
|
||||
|
||||
public Concept to() {
|
||||
return to;
|
||||
}
|
||||
|
||||
public void setTo(Concept to) {
|
||||
this.to = to;
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package org.tests.cache.embeddedid;
|
||||
|
||||
import io.ebean.DB;
|
||||
import io.ebean.test.LoggedSql;
|
||||
import io.ebean.xtest.BaseTestCase;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class TestCompositeForeignKey extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
void createConnectionWithCompositeForeignKey() {
|
||||
String networkId = "test-network";
|
||||
Concept concept1 = new Concept(networkId, "concept1", "concept 1");
|
||||
Concept concept2 = new Concept(networkId, "concept2", "concept 2");
|
||||
|
||||
DB.saveAll(concept1, concept2);
|
||||
|
||||
String connectionId = "test-connection";
|
||||
|
||||
Connection connection = new Connection(
|
||||
networkId, connectionId, "test", concept1, concept2
|
||||
);
|
||||
DB.save(connection);
|
||||
|
||||
Connection reloaded = DB.find(Connection.class, new ConceptId(networkId, connectionId));
|
||||
Objects.requireNonNull(reloaded);
|
||||
|
||||
LoggedSql.start();
|
||||
// this breaks if we uncomment the OneToMany relationships with cascade = ALL
|
||||
DB.createQuery(Concept.class).delete();
|
||||
|
||||
List<String> sql = LoggedSql.stop();
|
||||
assertThat(sql).hasSize(4);
|
||||
assertThat(sql.get(0)).contains("select t0.network_id, t0.id from concept t0");
|
||||
if (isSqlServer() || isDb2() || isNuoDb()) {
|
||||
assertThat(sql.get(1)).contains("delete from connection where ");
|
||||
assertThat(sql.get(2)).contains("delete from connection where ");
|
||||
assertThat(sql.get(3)).contains("delete from concept where ");
|
||||
} else {
|
||||
assertThat(sql.get(1)).contains("delete from connection where (network_id,from_conc) in");
|
||||
assertThat(sql.get(2)).contains("delete from connection where (network_id,to_conc) in");
|
||||
assertThat(sql.get(3)).contains("delete from concept where (network_id,id) in");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user