mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
DbMigration supports altering UniqueConstraints and foreign keys (#1279)
* NEW: Ebean reads version from pop.properties and prints it at start up * DDL generation and migration can append a header and platformMigration is prepared to append epilog & prolog to scripts * FIX: Set identity only, if it is not the platform default type * Updated reference scripts * ADD basic support for foreign key migration * DbMigration detects alter foreign keys * tests
This commit is contained in:
committed by
Rob Bygrave
parent
a64a244745
commit
da7bb834e7
@@ -8,11 +8,12 @@ import io.ebean.Transaction;
|
||||
import io.ebean.annotation.IgnorePlatform;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.migration.ddl.DdlRunner;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.Helper;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
@@ -23,14 +24,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
public class DbMigrationTest extends BaseTestCase {
|
||||
|
||||
private int runScript(boolean expectErrors, String scriptName) throws IOException {
|
||||
try (InputStream stream = getClass().getResourceAsStream("/dbmigration/migrationtest/" + server().getPluginApi().getDatabasePlatform().getName()+"/" + scriptName);
|
||||
java.util.Scanner s = new java.util.Scanner(stream)) {
|
||||
s.useDelimiter("\\A");
|
||||
if (s.hasNext()) {
|
||||
return runScript(expectErrors, s.next(), scriptName);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
String ddl = Helper.asText(this, "/dbmigration/migrationtest/" + server().getPluginApi().getDatabasePlatform().getName()+"/" + scriptName);
|
||||
return runScript(expectErrors, ddl, scriptName);
|
||||
}
|
||||
|
||||
private int runScript(boolean expectErrors, String content, String scriptName) {
|
||||
@@ -58,35 +53,44 @@ public class DbMigrationTest extends BaseTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@IgnorePlatform({Platform.ORACLE, Platform.SQLSERVER})
|
||||
@IgnorePlatform(Platform.ORACLE)
|
||||
@Test
|
||||
public void testRunMigration() throws IOException {
|
||||
// first clean up previously created objects
|
||||
runScript(true, "drop table migtest_e_ref;\n","test");
|
||||
runScript(true, "drop table migtest_e_basic;\n"
|
||||
+ "drop table migtest_e_history;\n"
|
||||
+ "drop table migtest_e_ref;\n"
|
||||
+ "drop table migtest_e_ref cascade;\n"
|
||||
+ "drop table migtest_e_user;\n"
|
||||
+ "drop table migtest_e_history;\n"
|
||||
+ "drop table migtest_e_history cascade;\n" // pg
|
||||
+ "drop table migtest_e_history_history cascade;\n" // pg
|
||||
+ "drop sequence migtest_e_basic_seq;\n"
|
||||
+ "drop sequence migtest_e_history_seq;\n"
|
||||
+ "drop sequence migtest_e_ref_seq;\n"
|
||||
+ "drop sequence migtest_e_user;\n"
|
||||
+ "drop sequence migtest_e_history;\n"
|
||||
, "cleanup");
|
||||
cleanup("migtest_ckey_assoc",
|
||||
"migtest_ckey_detail",
|
||||
"migtest_ckey_parent",
|
||||
"migtest_e_basic",
|
||||
"migtest_e_history",
|
||||
"migtest_e_history2",
|
||||
"migtest_e_ref",
|
||||
"migtest_e_softdelete",
|
||||
"migtest_e_user",
|
||||
"migtest_mtm_c",
|
||||
"migtest_mtm_m",
|
||||
"migtest_mtm_c_migtest_mtm_m",
|
||||
"migtest_mtm_m_migtest_mtm_c",
|
||||
"migtest_oto_child",
|
||||
"migtest_oto_master");
|
||||
|
||||
|
||||
runScript(false, "1.0__initial.sql");
|
||||
|
||||
SqlUpdate update = server().createSqlUpdate("insert into migtest_e_basic (id, old_boolean, user_id) values (1, :false, 1), (2, :true, 1)");
|
||||
update.setParameter("false", false);
|
||||
update.setParameter("true", true);
|
||||
if (isOracle()) {
|
||||
SqlUpdate update = server().createSqlUpdate("insert into migtest_e_basic (id, old_boolean, user_id) values (1, :false, 1)");
|
||||
update.setParameter("false", false);
|
||||
assertThat(server().execute(update)).isEqualTo(1);
|
||||
|
||||
assertThat(server().execute(update)).isEqualTo(2);
|
||||
update = server().createSqlUpdate("insert into migtest_e_basic (id, old_boolean, user_id) values (2, :true, 1)");
|
||||
update.setParameter("true", true);
|
||||
assertThat(server().execute(update)).isEqualTo(1);
|
||||
} else {
|
||||
SqlUpdate update = server().createSqlUpdate("insert into migtest_e_basic (id, old_boolean, user_id) values (1, :false, 1), (2, :true, 1)");
|
||||
update.setParameter("false", false);
|
||||
update.setParameter("true", true);
|
||||
|
||||
assertThat(server().execute(update)).isEqualTo(2);
|
||||
}
|
||||
|
||||
// Run migration
|
||||
runScript(false, "1.1.sql");
|
||||
@@ -115,10 +119,19 @@ public class DbMigrationTest extends BaseTestCase {
|
||||
assertThat(row.getTimestamp("some_date")).isEqualTo(new Timestamp(100, 0, 1, 0, 0, 0, 0)); // = 2000-01-01T00:00:00
|
||||
|
||||
// Run migration & drops
|
||||
if (isMySql()) {
|
||||
return; // TODO: mysql cannot drop table (need stored procedure for drop column)
|
||||
}
|
||||
runScript(false, "1.2__dropsFor_1.1.sql");
|
||||
|
||||
|
||||
select = server().createSqlQuery("select * from migtest_e_basic order by id");
|
||||
// Oracle caches the statement and does not detect schema change. It fails with
|
||||
// an ORA-01007
|
||||
if (isOracle()) {
|
||||
select = server().createSqlQuery("select * from migtest_e_basic order by id,id");
|
||||
} else {
|
||||
select = server().createSqlQuery("select * from migtest_e_basic order by id");
|
||||
}
|
||||
result = select.findList();
|
||||
assertThat(result).hasSize(2);
|
||||
row = result.get(0);
|
||||
@@ -135,4 +148,21 @@ public class DbMigrationTest extends BaseTestCase {
|
||||
assertThat(row.keySet()).contains("old_boolean", "old_boolean2");
|
||||
}
|
||||
|
||||
private void cleanup(String ... tables) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String table : tables) {
|
||||
// simple and stupid try to execute all commands on all dialects.
|
||||
sb.append("alter table ").append(table).append(" set ( system_versioning = OFF );\n");
|
||||
sb.append("drop table ").append(table).append(";\n");
|
||||
sb.append("drop table ").append(table).append(" cascade;\n");
|
||||
sb.append("drop table ").append(table).append("_history;\n");
|
||||
sb.append("drop table ").append(table).append("_history cascade;\n");
|
||||
sb.append("drop view ").append(table).append("_with_history;\n");
|
||||
sb.append("drop sequence ").append(table).append("_seq;\n");
|
||||
}
|
||||
|
||||
runScript(true, sb.toString(), "cleanup");
|
||||
runScript(true, sb.toString(), "cleanup");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package misc.migration.v1_0;
|
||||
|
||||
import io.ebean.annotation.ConstraintMode;
|
||||
import io.ebean.annotation.DbForeignKey;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_cascade")
|
||||
public class DfkCascade {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
@ManyToOne
|
||||
@DbForeignKey(onDelete = ConstraintMode.CASCADE)
|
||||
DfkCascadeOne one;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package misc.migration.v1_0;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_cascade_one")
|
||||
public class DfkCascadeOne {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
@OneToMany(mappedBy = "one", cascade = CascadeType.ALL)
|
||||
List<DfkCascade> details;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package misc.migration.v1_0;
|
||||
|
||||
import io.ebean.annotation.DbForeignKey;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_none")
|
||||
public class DfkNone {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
@ManyToOne
|
||||
@DbForeignKey(noConstraint = true)
|
||||
DfkOne one;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package misc.migration.v1_0;
|
||||
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_none_via_join")
|
||||
public class DfkNoneViaJoin {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "one_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||
DfkOne one;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package misc.migration.v1_0;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_one")
|
||||
public class DfkOne {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package misc.migration.v1_0;
|
||||
|
||||
import io.ebean.annotation.ConstraintMode;
|
||||
import io.ebean.annotation.DbForeignKey;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_set_null")
|
||||
public class DfkSetNull {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
@ManyToOne
|
||||
@DbForeignKey(onDelete = ConstraintMode.SET_NULL)
|
||||
DfkOne one;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package misc.migration.v1_1;
|
||||
|
||||
import io.ebean.annotation.ConstraintMode;
|
||||
import io.ebean.annotation.DbForeignKey;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_cascade")
|
||||
public class DfkCascade {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
@ManyToOne
|
||||
@DbForeignKey(onDelete = ConstraintMode.RESTRICT)
|
||||
DfkCascadeOne one;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package misc.migration.v1_1;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_cascade_one")
|
||||
public class DfkCascadeOne {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
@OneToMany(mappedBy = "one", cascade = CascadeType.ALL)
|
||||
List<DfkCascade> details;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package misc.migration.v1_1;
|
||||
|
||||
import io.ebean.annotation.DbForeignKey;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_none")
|
||||
public class DfkNone {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
@ManyToOne
|
||||
@DbForeignKey(noConstraint = false)
|
||||
DfkOne one;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package misc.migration.v1_1;
|
||||
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_none_via_join")
|
||||
public class DfkNoneViaJoin {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "one_id", foreignKey = @ForeignKey(ConstraintMode.CONSTRAINT))
|
||||
DfkOne one;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package misc.migration.v1_1;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_one")
|
||||
public class DfkOne {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package misc.migration.v1_1;
|
||||
|
||||
import io.ebean.annotation.ConstraintMode;
|
||||
import io.ebean.annotation.DbForeignKey;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_set_null")
|
||||
public class DfkSetNull {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
@ManyToOne
|
||||
@DbForeignKey(onDelete = ConstraintMode.RESTRICT)
|
||||
DfkOne one;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package misc.migration.v1_2;
|
||||
|
||||
import io.ebean.annotation.ConstraintMode;
|
||||
import io.ebean.annotation.DbForeignKey;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_cascade")
|
||||
public class DfkCascade {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
@ManyToOne
|
||||
@DbForeignKey(onDelete = ConstraintMode.CASCADE)
|
||||
DfkCascadeOne one;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package misc.migration.v1_2;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_cascade_one")
|
||||
public class DfkCascadeOne {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
@OneToMany(mappedBy = "one", cascade = CascadeType.ALL)
|
||||
List<DfkCascade> details;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package misc.migration.v1_2;
|
||||
|
||||
import io.ebean.annotation.DbForeignKey;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_none")
|
||||
public class DfkNone {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
@ManyToOne
|
||||
@DbForeignKey(noConstraint = true)
|
||||
DfkOne one;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package misc.migration.v1_2;
|
||||
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_none_via_join")
|
||||
public class DfkNoneViaJoin {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "one_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||
DfkOne one;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package misc.migration.v1_2;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_one")
|
||||
public class DfkOne {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package misc.migration.v1_2;
|
||||
|
||||
import io.ebean.annotation.ConstraintMode;
|
||||
import io.ebean.annotation.DbForeignKey;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "migtest_fk_set_null")
|
||||
public class DfkSetNull {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
@ManyToOne
|
||||
@DbForeignKey(onDelete = ConstraintMode.SET_NULL)
|
||||
DfkOne one;
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user