#323 - DDL Generation support for foreign key cascade options

This commit is contained in:
Rob Bygrave
2018-02-19 22:24:37 +13:00
parent b8aa822a30
commit 40634412f5
31 changed files with 868 additions and 133 deletions
@@ -0,0 +1,54 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.annotation.ConstraintMode;
import io.ebean.config.dbplatform.oracle.OraclePlatform;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class Oracle10DdlTest {
Oracle10Ddl create() {
return new Oracle10Ddl(new OraclePlatform());
}
@Test
public void appendForeignKeyOnDelete_expectEmtpy_when_nullRestrictSetDefault() {
Oracle10Ddl oracle = create();
StringBuilder sb = new StringBuilder();
oracle.appendForeignKeyOnDelete(sb, oracle.withDefault(null));
assertThat(sb.toString()).isEqualTo("");
sb = new StringBuilder();
oracle.appendForeignKeyOnDelete(sb, ConstraintMode.RESTRICT);
assertThat(sb.toString()).isEqualTo("");
sb = new StringBuilder();
oracle.appendForeignKeyOnDelete(sb, ConstraintMode.SET_DEFAULT);
assertThat(sb.toString()).isEqualTo("");
}
@Test
public void appendForeignKeyOnDelete_setNull() {
Oracle10Ddl oracle = create();
StringBuilder sb = new StringBuilder();
oracle.appendForeignKeyOnDelete(sb, ConstraintMode.SET_NULL);
assertThat(sb.toString()).isEqualTo(" on delete set null");
}
@Test
public void appendForeignKeyOnDelete_cascade() {
Oracle10Ddl oracle = create();
StringBuilder sb = new StringBuilder();
oracle.appendForeignKeyOnDelete(sb, ConstraintMode.CASCADE);
assertThat(sb.toString()).isEqualTo(" on delete cascade");
}
}
@@ -27,7 +27,12 @@ public class TestOrderedList extends BaseTestCase {
List<String> sql = LoggedSqlCollector.current();
assertThat(sql.size()).isGreaterThan(1);
assertThat(sql.get(0)).contains("insert into om_ordered_master");
assertThat(sql.get(1)).contains("insert into om_ordered_detail (name, version, sort_order, master_id) values (?,?,?,?)");
boolean hasId = sql.get(1).contains(" (id, name");
if (hasId) {
assertThat(sql.get(1)).contains("insert into om_ordered_detail (id, name, version, sort_order, master_id) values (?,?,?,?,?)");
} else {
assertThat(sql.get(1)).contains("insert into om_ordered_detail (name, version, sort_order, master_id) values (?,?,?,?)");
}
// update without any changes
Ebean.save(master);
@@ -0,0 +1,50 @@
package org.tests.ddl;
import io.ebean.annotation.ConstraintMode;
import io.ebean.annotation.DbForeignKey;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@Entity
public class DfkCascade {
@Id
long id;
String name;
@ManyToOne
@DbForeignKey(onDelete = ConstraintMode.CASCADE)
DfkCascadeOne one;
public DfkCascade(String name, DfkCascadeOne one) {
this.name = name;
this.one = one;
}
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 DfkCascadeOne getOne() {
return one;
}
public void setOne(DfkCascadeOne one) {
this.one = one;
}
}
@@ -0,0 +1,47 @@
package org.tests.ddl;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.util.List;
@Entity
public class DfkCascadeOne {
@Id
long id;
String name;
@OneToMany(mappedBy = "one", cascade = CascadeType.ALL)
List<DfkCascade> details;
public DfkCascadeOne(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<DfkCascade> getDetails() {
return details;
}
public void setDetails(List<DfkCascade> details) {
this.details = details;
}
}
+49
View File
@@ -0,0 +1,49 @@
package org.tests.ddl;
import io.ebean.annotation.DbForeignKey;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@Entity
public class DfkNone {
@Id
long id;
String name;
@ManyToOne
@DbForeignKey(noConstraint = true)
DfkOne one;
public DfkNone(String name, DfkOne one) {
this.name = name;
this.one = one;
}
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 DfkOne getOne() {
return one;
}
public void setOne(DfkOne one) {
this.one = one;
}
}
+33
View File
@@ -0,0 +1,33 @@
package org.tests.ddl;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class DfkOne {
@Id
long id;
String name;
public DfkOne(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;
}
}
@@ -0,0 +1,50 @@
package org.tests.ddl;
import io.ebean.annotation.ConstraintMode;
import io.ebean.annotation.DbForeignKey;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@Entity
public class DfkSetNull {
@Id
long id;
String name;
@ManyToOne
@DbForeignKey(onDelete = ConstraintMode.SET_NULL)
DfkOne one;
public DfkSetNull(String name, DfkOne one) {
this.name = name;
this.one = one;
}
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 DfkOne getOne() {
return one;
}
public void setOne(DfkOne one) {
this.one = one;
}
}
@@ -0,0 +1,77 @@
package org.tests.ddl;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class TestForeignKeyModes extends BaseTestCase {
@Test
public void none() {
DfkOne one = new DfkOne("one");
Ebean.save(one);
DfkNone none = new DfkNone("none", one);
Ebean.save(none);
// fails unless there is no Foreign key ...
Ebean.delete(one);
DfkNone found = Ebean.find(DfkNone.class, none.getId());
assertThat(found).isNotNull();
// we still reference one ... even though it does not exist anymore
assertThat(found.getOne()).isNotNull();
}
@Test
public void setNullOnDelete() {
DfkOne one = new DfkOne("one2");
Ebean.save(one);
DfkSetNull other = new DfkSetNull("none", one);
Ebean.save(other);
// success with ... fkey value set to null
Ebean.delete(one);
DfkSetNull found = Ebean.find(DfkSetNull.class, other.getId());
assertThat(found).isNotNull();
assertThat(found.getOne()).isNull();
}
@Test
public void onDeleteCascade() {
DfkCascadeOne one = new DfkCascadeOne("one3");
DfkCascade other = new DfkCascade("cascade1", one);
one.getDetails().add(other);
one.getDetails().add(new DfkCascade("cascade2", one));
one.getDetails().add(new DfkCascade("cascade3", one));
Ebean.save(one);
LoggedSqlCollector.start();
Ebean.delete(one);
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains("delete from dfk_cascade_one where id=?");
DfkCascade found = Ebean.find(DfkCascade.class, other.getId());
assertThat(found).isNull();
}
}
@@ -35,7 +35,7 @@ public class TestOneToOnePrimaryKeyJoin extends BaseTestCase {
OtoPrime found = query.findOne();
assertThat(found).isNotNull();
assertThat(sqlOf(query)).contains("select t0.pid, t0.name, t0.version, t0.pid from oto_prime t0 where t0.pid = ?")
assertThat(sqlOf(query, 10)).contains("select t0.pid, t0.name, t0.version, t0.pid from oto_prime t0 where t0.pid = ?")
.as("we don't join to oto_prime_extra");
assertThat(found.getName()).isEqualTo("p" + desc);
@@ -47,7 +47,7 @@ public class TestOneToOnePrimaryKeyJoin extends BaseTestCase {
OtoPrime oneWith = queryWithFetch.findOne();
assertThat(oneWith).isNotNull();
assertThat(sqlOf(queryWithFetch)).contains("select t0.pid, t0.name, t0.version, t1.eid, t1.extra, t1.version from oto_prime t0 join oto_prime_extra t1 on t1.eid = t0.pid where t0.pid = ?")
assertThat(sqlOf(queryWithFetch, 10)).contains("select t0.pid, t0.name, t0.version, t1.eid, t1.extra, t1.version from oto_prime t0 join oto_prime_extra t1 on t1.eid = t0.pid where t0.pid = ?")
.as("we join to oto_prime_extra");
@@ -35,7 +35,7 @@ public class TestOneToOnePrimaryKeyJoinBidi extends BaseTestCase {
OtoUBPrime found = query.findOne();
assertThat(found).isNotNull();
assertThat(sqlOf(query)).contains("select t0.pid, t0.name, t0.version, t0.pid from oto_ubprime t0 where t0.pid = ?")
assertThat(sqlOf(query, 10)).contains("select t0.pid, t0.name, t0.version, t0.pid from oto_ubprime t0 where t0.pid = ?")
.as("we don't join to oto_ubprime_extra");
assertThat(found.getName()).isEqualTo("u" + desc);
@@ -47,7 +47,7 @@ public class TestOneToOnePrimaryKeyJoinBidi extends BaseTestCase {
OtoUBPrime oneWith = queryWithFetch.findOne();
assertThat(oneWith).isNotNull();
assertThat(sqlOf(queryWithFetch)).contains("select t0.pid, t0.name, t0.version, t1.eid, t1.extra, t1.version, t1.eid from oto_ubprime t0 left join oto_ubprime_extra t1 on t1.eid = t0.pid where t0.pid = ?")
assertThat(sqlOf(queryWithFetch, 10)).contains("select t0.pid, t0.name, t0.version, t1.eid, t1.extra, t1.version, t1.eid from oto_ubprime t0 left join oto_ubprime_extra t1 on t1.eid = t0.pid where t0.pid = ?")
.as("we join to oto_prime_extra");
@@ -35,7 +35,7 @@ public class TestOneToOnePrimaryKeyJoinOptional extends BaseTestCase {
OtoUPrime found = query.findOne();
assertThat(found).isNotNull();
assertThat(sqlOf(query)).contains("select t0.pid, t0.name, t0.version, t0.pid from oto_uprime t0 where t0.pid = ?")
assertThat(sqlOf(query, 4)).contains("select t0.pid, t0.name, t0.version, t0.pid from oto_uprime t0 where t0.pid = ?")
.as("we don't join to oto_uprime_extra");
assertThat(found.getName()).isEqualTo("u" + desc);
@@ -47,7 +47,7 @@ public class TestOneToOnePrimaryKeyJoinOptional extends BaseTestCase {
OtoUPrime oneWith = queryWithFetch.findOne();
assertThat(oneWith).isNotNull();
assertThat(sqlOf(queryWithFetch)).contains("select t0.pid, t0.name, t0.version, t1.eid, t1.extra, t1.version from oto_uprime t0 left join oto_uprime_extra t1 on t1.eid = t0.pid where t0.pid = ?")
assertThat(sqlOf(queryWithFetch, 6)).contains("select t0.pid, t0.name, t0.version, t1.eid, t1.extra, t1.version from oto_uprime t0 left join oto_uprime_extra t1 on t1.eid = t0.pid where t0.pid = ?")
.as("we join to oto_prime_extra");
@@ -7,7 +7,6 @@ import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.Size;
import java.time.Instant;
@Entity
@@ -21,7 +20,7 @@ public class EPersonOnline {
@Size(max=127)
String email;
boolean online;
boolean onlineStatus;
@WhenModified
Instant whenUpdated;
@@ -42,12 +41,12 @@ public class EPersonOnline {
this.email = email;
}
public boolean isOnline() {
return online;
public boolean isOnlineStatus() {
return onlineStatus;
}
public void setOnline(boolean online) {
this.online = online;
public void setOnlineStatus(boolean onlineStatus) {
this.onlineStatus = onlineStatus;
}
public Instant getWhenUpdated() {
@@ -15,7 +15,7 @@ public class TestSqlUpdateUpsert extends BaseTestCase {
@Test
public void h2Merge() {
String sql = "merge into e_person_online (email, online, when_updated) key(email) values (?, ?, now())";
String sql = "merge into e_person_online (email, online_status, when_updated) key(email) values (?, ?, now())";
String email = "baz@one.com";
@@ -28,9 +28,9 @@ public class TestSqlUpdateUpsert extends BaseTestCase {
EPersonOnline found = Ebean.find(EPersonOnline.class, key);
assertThat(found).isNotNull();
assertThat(found.getEmail()).isEqualTo(email);
assertThat(found.isOnline()).isTrue();
assertThat(found.isOnlineStatus()).isTrue();
String sqlNamed = "merge into e_person_online (email, online, when_updated) key(email) values (:email, :online, now())";
String sqlNamed = "merge into e_person_online (email, online_status, when_updated) key(email) values (:email, :online, now())";
SqlUpdate sqlUpdate2 = Ebean.createSqlUpdate(sqlNamed)
.setGetGeneratedKeys(true)
@@ -45,7 +45,7 @@ public class TestSqlUpdateUpsert extends BaseTestCase {
assertThat(found2).isNotNull();
assertThat(found2.getId()).isEqualTo(key);
assertThat(found2.getEmail()).isEqualTo(email);
assertThat(found2.isOnline()).isFalse();
assertThat(found2.isOnlineStatus()).isFalse();
assertThat(found2.getWhenUpdated()).isGreaterThan(found.getWhenUpdated());
}
@@ -53,7 +53,7 @@ public class TestSqlUpdateUpsert extends BaseTestCase {
@Test
public void postgresUpsert() {
String sql = "insert into e_person_online (email, online, when_updated) values (?, ?, now()) on conflict (email) do update set when_updated=now(), online = ?";
String sql = "insert into e_person_online (email, online_status, when_updated) values (?, ?, now()) on conflict (email) do update set when_updated=now(), online_status = ?";
String email = "foo@one.com";
@@ -67,10 +67,10 @@ public class TestSqlUpdateUpsert extends BaseTestCase {
EPersonOnline found = Ebean.find(EPersonOnline.class, key);
assertThat(found).isNotNull();
assertThat(found.getEmail()).isEqualTo("foo@one.com");
assertThat(found.isOnline()).isTrue();
assertThat(found.isOnlineStatus()).isTrue();
String sqlNamed = "insert into e_person_online (email, online, when_updated) values (:email, :online, now()) on conflict (email) do update set when_updated=now(), online = :online";
String sqlNamed = "insert into e_person_online (email, online_status, when_updated) values (:email, :online, now()) on conflict (email) do update set when_updated=now(), online_status = :online";
SqlUpdate sqlUpdate2 = Ebean.createSqlUpdate(sqlNamed)
.setGetGeneratedKeys(true)
.setParameter("email", email)
@@ -82,7 +82,7 @@ public class TestSqlUpdateUpsert extends BaseTestCase {
assertThat(found2).isNotNull();
assertThat(found2.getId()).isEqualTo(key);
assertThat(found2.getEmail()).isEqualTo("foo@one.com");
assertThat(found2.isOnline()).isFalse();
assertThat(found2.isOnlineStatus()).isFalse();
assertThat(found2.getWhenUpdated()).isGreaterThan(found.getWhenUpdated());
}
@@ -93,7 +93,7 @@ public class TestSqlUpdateUpsert extends BaseTestCase {
String email = "bar@one.com";
String sql = "insert into e_person_online (email, online, when_updated) values (?, ?, current_time) on duplicate key update when_updated=current_time, online = ?";
String sql = "insert into e_person_online (email, online_status, when_updated) values (?, ?, current_time) on duplicate key update when_updated=current_time, online_status = ?";
SqlUpdate sqlUpdate = Ebean.createSqlUpdate(sql)
.setGetGeneratedKeys(true)
.setParameter(1, email)
@@ -106,10 +106,10 @@ public class TestSqlUpdateUpsert extends BaseTestCase {
EPersonOnline found = Ebean.find(EPersonOnline.class, key);
assertThat(found).isNotNull();
assertThat(found.getEmail()).isEqualTo("bar@one.com");
assertThat(found.isOnline()).isTrue();
assertThat(found.isOnlineStatus()).isTrue();
String sqlNamed = "insert into e_person_online (email, online, when_updated) values (:email, :online, current_time) on duplicate key update when_updated=current_time, online = :online";
String sqlNamed = "insert into e_person_online (email, online_status, when_updated) values (:email, :online, current_time) on duplicate key update when_updated=current_time, online_status = :online";
SqlUpdate sqlUpdate2 = Ebean.createSqlUpdate(sqlNamed)
.setGetGeneratedKeys(true)
.setParameter("email", email)
@@ -122,7 +122,7 @@ public class TestSqlUpdateUpsert extends BaseTestCase {
assertThat(found2).isNotNull();
assertThat(found2.getId()).isEqualTo(key);
assertThat(found2.getEmail()).isEqualTo("bar@one.com");
assertThat(found2.isOnline()).isFalse();
assertThat(found2.isOnlineStatus()).isFalse();
}
}