diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java index 20a5a208f..2d4803603 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java @@ -14,7 +14,7 @@ import java.util.List; */ public class MCompoundForeignKey { - private final String name; + private String name; private final String referenceTable; private final List columns = new ArrayList(); private final List referenceColumns = new ArrayList(); @@ -47,6 +47,28 @@ public class MCompoundForeignKey { return fk; } + /** + * Add a counter to the foreign key and index names to avoid duplication. + */ + public void addNameSuffix(int counter) { + this.name = name + "_" + counter; + this.indexName = indexName + "_" + counter; + } + + /** + * Return the foreign key name. + */ + public String getName() { + return name; + } + + /** + * Return the index name. + */ + public String getIndexName() { + return indexName; + } + /** * Return the columns making up the foreign key in order. */ diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java index 1fc50e448..e85937955 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java @@ -14,6 +14,7 @@ import com.avaje.ebean.dbmigration.migration.UniqueConstraint; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -482,4 +483,32 @@ public class MTable { return (value == 0) ? null : BigInteger.valueOf(value); } + /** + * Check if there are duplicate foreign keys. + *

+ * This can occur when an ManyToMany relates back to itself. + *

+ */ + public void checkDuplicateForeignKeys() { + + if (hasDuplicateForeignKeys()) { + int counter = 1; + for (MCompoundForeignKey fk : compoundKeys) { + fk.addNameSuffix(counter++); + } + } + } + + /** + * Return true if the foreign key names are not unique. + */ + private boolean hasDuplicateForeignKeys() { + Set fkNames = new HashSet(); + for (MCompoundForeignKey fk : compoundKeys) { + if (!fkNames.add(fk.getName())) { + return true; + } + } + return false; + } } diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java index ab9604e39..a1ed1a2da 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java @@ -47,6 +47,8 @@ public class ModelBuildIntersectionTable { BeanDescriptor targetDesc = manyProp.getTargetDescriptor(); buildFkConstraints(targetDesc, tableJoin.columns(), false); + + intersectionTable.checkDuplicateForeignKeys(); } diff --git a/src/test/java/com/avaje/tests/m2m/TestM2MSelfRelationship.java b/src/test/java/com/avaje/tests/m2m/TestM2MSelfRelationship.java new file mode 100644 index 000000000..05931d957 --- /dev/null +++ b/src/test/java/com/avaje/tests/m2m/TestM2MSelfRelationship.java @@ -0,0 +1,29 @@ +package com.avaje.tests.m2m; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.tests.model.m2m.MnyTopic; +import org.junit.Test; + +/** + * Added to test DDL generation for ManyToMany related back to itself. + */ +public class TestM2MSelfRelationship extends BaseTestCase { + + @Test + public void test() { + + Ebean.getDefaultServer(); + + // Create 2 roles r0 and r1 + MnyTopic r0 = new MnyTopic("r0"); + MnyTopic r1 = new MnyTopic("r1"); + + // Save r1 and r2 + Ebean.save(r0); + Ebean.save(r1); + + r0.getSubTopics().add(r1); + Ebean.save(r0); + } +} diff --git a/src/test/java/com/avaje/tests/model/m2m/MnyTopic.java b/src/test/java/com/avaje/tests/model/m2m/MnyTopic.java new file mode 100644 index 000000000..d4772c099 --- /dev/null +++ b/src/test/java/com/avaje/tests/model/m2m/MnyTopic.java @@ -0,0 +1,67 @@ +package com.avaje.tests.model.m2m; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.Version; +import java.util.List; + +@Entity +public class MnyTopic { + + @Id + Long id; + + String name; + + @Version + Long version; + + @ManyToMany + @JoinTable(name = "subtopics", + joinColumns = @JoinColumn(name = "topic", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "subtopic", referencedColumnName = "id")) + List subTopics; + + public MnyTopic() { + + } + + public MnyTopic(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 Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } + + public List getSubTopics() { + return subTopics; + } + + public void setSubTopics(List subTopics) { + this.subTopics = subTopics; + } +}