diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MCompoundForeignKey.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MCompoundForeignKey.java index bafe16a51..7a1686b73 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MCompoundForeignKey.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MCompoundForeignKey.java @@ -1,5 +1,6 @@ package io.ebeaninternal.dbmigration.model; +import io.ebean.annotation.ConstraintMode; import io.ebeaninternal.dbmigration.ddlgeneration.platform.DdlHelp; import io.ebeaninternal.dbmigration.migration.AlterForeignKey; import io.ebeaninternal.dbmigration.migration.ForeignKey; @@ -22,6 +23,8 @@ public class MCompoundForeignKey { private final List columns = new ArrayList<>(); private final List referenceColumns = new ArrayList<>(); private String indexName; + private ConstraintMode fkeyOnDelete; + private ConstraintMode fkeyOnUpdate; public MCompoundForeignKey(String name, String referenceTable, String indexName) { this.name = name; @@ -47,9 +50,15 @@ public class MCompoundForeignKey { fk.setColumnNames(toColumnNames(columns)); fk.setRefColumnNames(toColumnNames(referenceColumns)); fk.setRefTableName(referenceTable); + fk.setOnDelete(fkeyModeOf(fkeyOnDelete)); + fk.setOnUpdate(fkeyModeOf(fkeyOnUpdate)); return fk; } - + + private String fkeyModeOf(ConstraintMode mode) { + return (mode == null) ? null : mode.name(); + } + /** * Create and return an AlterForeignKey migration element. */ @@ -63,7 +72,7 @@ public class MCompoundForeignKey { fk.setTableName(tableName); return fk; } - + /** * Create and return an AlterForeignKey migration element. */ @@ -117,7 +126,6 @@ public class MCompoundForeignKey { * Return as an array of string column names. */ private String toColumnNames(List columns) { - StringBuilder sb = new StringBuilder(40); for (int i = 0; i < columns.size(); i++) { if (i > 0) { @@ -139,7 +147,7 @@ public class MCompoundForeignKey { return true; if (!(obj instanceof MCompoundForeignKey)) return false; - + MCompoundForeignKey other = (MCompoundForeignKey) obj; return Objects.equals(columns, other.columns) && Objects.equals(indexName, other.indexName) @@ -148,4 +156,8 @@ public class MCompoundForeignKey { && Objects.equals(referenceTable, other.referenceTable); } + public void setForeignKeyModes(ConstraintMode onDelete, ConstraintMode onUpdate) { + this.fkeyOnDelete = onDelete; + this.fkeyOnUpdate = onUpdate; + } } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/build/ModelBuildPropertyVisitor.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/build/ModelBuildPropertyVisitor.java index 4f62384c6..51c948185 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/build/ModelBuildPropertyVisitor.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/build/ModelBuildPropertyVisitor.java @@ -91,7 +91,6 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor { @Override public void visitEnd() { - // set the primary key name table.setPkName(primaryKeyName()); @@ -169,6 +168,8 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor { @Override public void visitOneImported(BeanPropertyAssocOne p) { + PropertyForeignKey foreignKey = p.foreignKey(); + boolean addForeignKey = foreignKey == null || !foreignKey.isNoConstraint(); TableJoinColumn[] columns = p.tableJoin().columns(); if (columns.length == 0) { @@ -178,17 +179,19 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor { List modelColumns = new ArrayList<>(columns.length); MCompoundForeignKey compoundKey = null; - if (columns.length > 1) { + if (addForeignKey && columns.length > 1) { // compound foreign key String refTable = p.targetDescriptor().baseTable(); String fkName = foreignKeyConstraintName(p.name()); String fkIndex = foreignKeyIndexName(p.name()); compoundKey = new MCompoundForeignKey(fkName, refTable, fkIndex); + if (foreignKey != null) { + compoundKey.setForeignKeyModes(foreignKey.getOnDelete(), foreignKey.getOnUpdate()); + } table.addForeignKey(compoundKey); } for (TableJoinColumn column : columns) { - String dbCol = column.getLocalDbColumn(); BeanProperty importedProperty = p.findMatchImport(dbCol); if (importedProperty == null) { @@ -200,26 +203,27 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor { MColumn col = table.addColumn(dbCol, columnDefn, !p.isNullable()); col.setDbMigrationInfos(p.dbMigrationInfos()); col.setDefaultValue(p.dbColumnDefault()); - if (columns.length == 1) { - if (p.hasForeignKeyConstraint() && !importedProperty.descriptor().suppressForeignKey()) { - // single references column (put it on the column) - String refTable = importedProperty.descriptor().baseTable(); - if (refTable == null) { - // odd case where an EmbeddedId only has 1 property - refTable = p.targetDescriptor().baseTable(); - } - col.setReferences(refTable + "." + refColumn); - col.setForeignKeyName(foreignKeyConstraintName(col.getName())); - if (p.hasForeignKeyIndex()) { - col.setForeignKeyIndex(foreignKeyIndexName(col.getName())); - } - PropertyForeignKey foreignKey = p.foreignKey(); - if (foreignKey != null) { - col.setForeignKeyModes(foreignKey.getOnDelete(), foreignKey.getOnUpdate()); + if (addForeignKey) { + if (columns.length > 1) { + compoundKey.addColumnPair(dbCol, refColumn); + } else { + if (p.hasForeignKeyConstraint() && !importedProperty.descriptor().suppressForeignKey()) { + // single references column (put it on the column) + String refTable = importedProperty.descriptor().baseTable(); + if (refTable == null) { + // odd case where an EmbeddedId only has 1 property + refTable = p.targetDescriptor().baseTable(); + } + col.setReferences(refTable + "." + refColumn); + col.setForeignKeyName(foreignKeyConstraintName(col.getName())); + if (p.hasForeignKeyIndex()) { + col.setForeignKeyIndex(foreignKeyIndexName(col.getName())); + } + if (foreignKey != null) { + col.setForeignKeyModes(foreignKey.getOnDelete(), foreignKey.getOnUpdate()); + } } } - } else { - compoundKey.addColumnPair(dbCol, refColumn); } modelColumns.add(col); } diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/model/build/ModelBuildBeanVisitorTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/model/build/ModelBuildBeanVisitorTest.java index 53a8a6038..4991ded74 100644 --- a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/model/build/ModelBuildBeanVisitorTest.java +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/model/build/ModelBuildBeanVisitorTest.java @@ -1,8 +1,6 @@ package io.ebeaninternal.dbmigration.model.build; -import io.ebean.DatabaseBuilder; -import io.localtest.BaseTestCase; import io.ebean.DB; import io.ebean.DatabaseFactory; import io.ebean.config.DatabaseConfig; @@ -11,11 +9,13 @@ import io.ebean.platform.h2.H2Platform; import io.ebean.platform.sqlserver.SqlServer17Platform; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.dbmigration.ddlgeneration.platform.DefaultConstraintMaxLength; +import io.ebeaninternal.dbmigration.migration.ForeignKey; import io.ebeaninternal.dbmigration.model.MColumn; import io.ebeaninternal.dbmigration.model.MCompoundForeignKey; import io.ebeaninternal.dbmigration.model.MTable; import io.ebeaninternal.dbmigration.model.ModelContainer; import io.ebeaninternal.dbmigration.model.visitor.VisitAllUsing; +import io.localtest.BaseTestCase; import org.junit.jupiter.api.Test; import org.tests.model.basic.CKeyAssoc; import org.tests.model.basic.CKeyDetail; @@ -44,6 +44,7 @@ public class ModelBuildBeanVisitorTest extends BaseTestCase { new VisitAllUsing(addTable, defaultServer).visitAllBeans(); assert_compound_pk(model); + assert_compound_fk(model); assert_discriminatorColumn_explicit(model); assert_discriminatorColumn_implied(model); @@ -73,7 +74,7 @@ public class ModelBuildBeanVisitorTest extends BaseTestCase { config.setDbOffline(true); config.setDatabasePlatform(new SqlServer17Platform()); - final SpiEbeanServer database = (SpiEbeanServer)DatabaseFactory.create(config); + final SpiEbeanServer database = (SpiEbeanServer) DatabaseFactory.create(config); try { ModelBuildContext ctx = new ModelBuildContext(model, config.getDatabasePlatform(), config.getConstraintNaming(), true); @@ -105,6 +106,21 @@ public class ModelBuildBeanVisitorTest extends BaseTestCase { assertThat(item.primaryKeyColumns()).hasSize(2); } + private void assert_compound_fk(ModelContainer model) { + MTable item = model.getTable("item"); + assertThat(item).isNotNull(); + List compoundKeys = item.getCompoundKeys(); + assertThat(compoundKeys).hasSize(1); + + MCompoundForeignKey compoundForeignKey = compoundKeys.get(0); + ForeignKey foreignKey = compoundForeignKey.createForeignKey(); + assertThat(foreignKey.getName()).isEqualTo("fk_item_etype"); + assertThat(foreignKey.getColumnNames()).isEqualTo("customer,type"); + assertThat(foreignKey.getRefTableName()).isEqualTo("\"type\""); + assertThat(foreignKey.getOnDelete()).isEqualTo("SET_NULL"); + assertThat(foreignKey.getOnUpdate()).isEqualTo("SET_DEFAULT"); + } + private void assert_discriminatorColumn_explicit(ModelContainer model) { MTable configuration = model.getTable("configuration"); diff --git a/ebean-ddl-generator/src/test/java/org/tests/model/compositekeys/Item.java b/ebean-ddl-generator/src/test/java/org/tests/model/compositekeys/Item.java index f0e7c2c19..b4a4fd592 100644 --- a/ebean-ddl-generator/src/test/java/org/tests/model/compositekeys/Item.java +++ b/ebean-ddl-generator/src/test/java/org/tests/model/compositekeys/Item.java @@ -1,5 +1,7 @@ package org.tests.model.compositekeys; +import io.ebean.annotation.ConstraintMode; +import io.ebean.annotation.DbForeignKey; import jakarta.persistence.AttributeOverride; import jakarta.persistence.Column; import jakarta.persistence.Embedded; @@ -32,11 +34,13 @@ public class Item { @Version private Long version; + @DbForeignKey(onDelete = ConstraintMode.SET_NULL, onUpdate = ConstraintMode.SET_DEFAULT) @ManyToOne @JoinColumn(name = "customer", referencedColumnName = "customer", insertable = false, updatable = false) @JoinColumn(name = "type", referencedColumnName = "type", insertable = false, updatable = false) private Type eType; + @DbForeignKey(noConstraint = true) @ManyToOne @JoinColumn(name = "customer", referencedColumnName = "customer", insertable = false, updatable = false) @JoinColumn(name = "region", referencedColumnName = "type", insertable = false, updatable = false)