#1397 - @DbForeignKey(noConstraint = true) is ignored on @ManyToMany ... foreign constraints still added

This commit is contained in:
rob bygrave
2018-05-31 10:55:58 +12:00
parent 0aa9208dcc
commit ddc00fedb6
8 changed files with 124 additions and 25 deletions
@@ -5,6 +5,7 @@ import io.ebeaninternal.dbmigration.model.MTable;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.PropertyForeignKey;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.TableJoinColumn;
@@ -48,10 +49,12 @@ class ModelBuildIntersectionTable {
private void buildFkConstraints() {
ctx.fkeyBuilder(intersectionTable)
.addForeignKey(manyProp.getBeanDescriptor(), intersectionTableJoin, true)
.addForeignKey(manyProp.getTargetDescriptor(), tableJoin, false);
PropertyForeignKey foreignKey = manyProp.getForeignKey();
if (foreignKey == null || !foreignKey.isNoConstraint()) {
ctx.fkeyBuilder(intersectionTable)
.addForeignKey(manyProp.getBeanDescriptor(), intersectionTableJoin, true)
.addForeignKey(manyProp.getTargetDescriptor(), tableJoin, false);
}
intersectionTable.checkDuplicateForeignKeys();
}
@@ -65,6 +65,8 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
*/
final TableJoin tableJoin;
final PropertyForeignKey foreignKey;
/**
* The type of the joined bean.
*/
@@ -90,6 +92,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
*/
public BeanPropertyAssoc(BeanDescriptor<?> descriptor, DeployBeanPropertyAssoc<T> deploy) {
super(descriptor, deploy);
this.foreignKey = deploy.getForeignKey();
this.extraWhere = InternString.intern(deploy.getExtraWhere());
this.beanTable = deploy.getBeanTable();
this.mappedBy = InternString.intern(deploy.getMappedBy());
@@ -128,6 +131,13 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
return fetchPreference;
}
/**
* Return the extra configuration for the foreign key.
*/
public PropertyForeignKey getForeignKey() {
return foreignKey;
}
/**
* Create a ElPropertyValue for a *ToOne or *ToMany.
*/
@@ -44,8 +44,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
private final boolean primaryKeyExport;
private final PropertyForeignKey foreignKey;
private AssocOneHelp localHelp;
protected final BeanProperty[] embeddedProps;
@@ -73,8 +71,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
DeployBeanPropertyAssocOne<T> deploy) {
super(descriptor, deploy);
foreignKey = deploy.getForeignKey();
primaryKeyExport = deploy.isPrimaryKeyExport();
oneToOne = deploy.isOneToOne();
oneToOneExported = deploy.isOneToOneExported();
@@ -323,10 +319,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
}
}
public PropertyForeignKey getForeignKey() {
return foreignKey;
}
public boolean hasForeignKey() {
return foreignKey == null || !foreignKey.isNoConstraint();
}
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.deploy.meta;
import io.ebeaninternal.server.deploy.BeanCascadeInfo;
import io.ebeaninternal.server.deploy.BeanTable;
import io.ebeaninternal.server.deploy.PropertyForeignKey;
/**
* Abstract base for properties mapped to an associated bean, list, set or map.
@@ -16,7 +17,7 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
/**
* Persist settings.
*/
protected final BeanCascadeInfo cascadeInfo = new BeanCascadeInfo();
final BeanCascadeInfo cascadeInfo = new BeanCascadeInfo();
/**
* The join table information.
@@ -42,6 +43,8 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
private int fetchPreference = 1000;
private PropertyForeignKey foreignKey;
/**
* Construct the property.
*/
@@ -117,6 +120,13 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
return cascadeInfo;
}
public void setForeignKey(PropertyForeignKey foreignKey) {
this.foreignKey = foreignKey;
}
public PropertyForeignKey getForeignKey() {
return foreignKey;
}
/**
* Return the mappedBy deployment attribute.
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.deploy.meta;
import io.ebeaninternal.server.deploy.PropertyForeignKey;
import io.ebeaninternal.server.query.SqlJoinType;
import javax.persistence.CascadeType;
@@ -22,8 +21,6 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
private String columnPrefix;
private PropertyForeignKey foreignKey;
private boolean orphanRemoval;
/**
@@ -148,14 +145,6 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
}
}
public void setForeignKey(PropertyForeignKey foreignKey) {
this.foreignKey = foreignKey;
}
public PropertyForeignKey getForeignKey() {
return foreignKey;
}
public void setOrphanRemoval(boolean orphanRemoval) {
this.orphanRemoval = orphanRemoval;
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.annotation.DbForeignKey;
import io.ebean.annotation.FetchPreference;
import io.ebean.annotation.HistoryExclude;
import io.ebean.annotation.PrivateOwned;
@@ -12,6 +13,7 @@ import io.ebean.util.StringHelper;
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanTable;
import io.ebeaninternal.server.deploy.PropertyForeignKey;
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
@@ -95,6 +97,12 @@ class AnnotationAssocManys extends AnnotationParser {
readElementCollection(prop, elementCollection);
}
// for ManyToMany typically to disable foreign keys from intersection table
DbForeignKey dbForeignKey = get(prop, DbForeignKey.class);
if (dbForeignKey != null){
prop.setForeignKey(new PropertyForeignKey(dbForeignKey));
}
if (get(prop, HistoryExclude.class) != null) {
prop.setExcludedFromHistory();
}
@@ -0,0 +1,54 @@
package org.tests.ddl;
import io.ebean.annotation.DbForeignKey;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import java.util.ArrayList;
import java.util.List;
@Entity
public class DfkNoneViaMtoM {
@Id
long id;
String name;
/**
* Have no Foreign key constraints on the intersection table.
*/
@ManyToMany(cascade = CascadeType.ALL)
@DbForeignKey(noConstraint = true)
List<DfkOne> ones = new ArrayList<>();
public DfkNoneViaMtoM(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<DfkOne> getOnes() {
return ones;
}
public void setOnes(List<DfkOne> ones) {
this.ones = ones;
}
}
@@ -48,6 +48,40 @@ public class TestForeignKeyModes extends BaseTestCase {
assertThat(found.getOne()).isNotNull();
}
@Test
public void noneViaManyToMany() {
DfkOne one = Ebean.getReference(DfkOne.class, 999L);
DfkNoneViaMtoM none = new DfkNoneViaMtoM("none2");
none.getOnes().add(one);
// Would normally fail as DfkOne id:999 is not actually in Database
// and with the foreign key constraint on the intersection table the
// insert into the intersection table would fail
Ebean.save(none);
DfkNoneViaMtoM found = Ebean.find(DfkNoneViaMtoM.class)
.setId(none.getId())
.fetch("ones", "id")
.findOne();
assertThat(found).isNotNull();
// but we can't actually fetch it via ORM M2M ... as it joins across
// to DfkOne and that doesn't exist
assertThat(found.getOnes()).isNotNull();
assertThat(found.getOnes()).hasSize(0);
LoggedSqlCollector.start();
Ebean.delete(none);
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(2);
assertThat(sql.get(0)).contains("delete from dfk_none_via_mto_m_dfk_one where dfk_none_via_mto_m_id = ?");
assertThat(sql.get(1)).contains("delete from dfk_none_via_mto_m where id=?");
}
@Test
public void setNullOnDelete() {
@@ -63,7 +97,6 @@ public class TestForeignKeyModes extends BaseTestCase {
DfkSetNull found = Ebean.find(DfkSetNull.class, other.getId());
assertThat(found).isNotNull();
assertThat(found.getOne()).isNull();
}
@Test