Compare commits

..
Author SHA1 Message Date
rob bygrave 7c1c144af5 [maven-release-plugin] prepare release ebean-11.22.4 2018-08-31 21:56:19 +12:00
rob bygrave 7809858634 #1478 - MySql DDL, invalid comment included resulting in PersistenceException: Failed to run script 2018-08-31 21:54:48 +12:00
rob bygrave bd3cb3c547 [maven-release-plugin] prepare for next development iteration 2018-08-27 21:44:54 +12:00
rob bygrave db25cb7bfb [maven-release-plugin] prepare release ebean-11.22.3 2018-08-27 21:44:38 +12:00
rob bygrave 55a9ba7170 #1475 - @OneToOne child with @SoftDelete that is deleted is still loaded by parent 2018-08-27 21:35:00 +12:00
rob bygrave 2df2224286 No effective change - add a new test for OneToOne with fetchType LAZY 2018-08-27 20:00:44 +12:00
rob bygrave e1e8092144 [maven-release-plugin] prepare for next development iteration 2018-08-21 21:15:23 +12:00
rob bygrave 4d4886d808 [maven-release-plugin] prepare release ebean-11.22.2 2018-08-21 21:15:07 +12:00
rob bygrave d11bb2c4e5 No effective change - add a new test for OneToMany orphanRemoval=true 2018-08-21 21:08:17 +12:00
rob bygrave 663b1bb4aa #1474 - IllegalStateException: Table [some_table] does not exists in model? ... when applying drop table migration and table is no longer in the model 2018-08-21 20:45:31 +12:00
SnōwballandRob Bygrave f3d26e32ec Possible fix error with multiple uses of enum (#1473)
When an enum is exists in the type cache and isn't String, @DbArray
fails. This includes a test that should fail everywhere (order of
reading fields isn't guaranteed afaik and the column needs to be read
before the @DbArray for the test to fail).
2018-08-21 20:26:08 +12:00
rob bygrave 5f83584315 [maven-release-plugin] prepare for next development iteration 2018-08-16 00:05:31 +12:00
25 changed files with 509 additions and 84 deletions
+2 -2
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.22.1</version>
<version>11.22.4</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -22,7 +22,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-11.22.1</tag>
<tag>ebean-11.22.4</tag>
</scm>
<profiles>
@@ -184,13 +184,12 @@ public class ModelContainer {
/**
* Unset the withHistory flag on the associated base table.
*/
private void applyChange(DropHistoryTable change) {
protected void applyChange(DropHistoryTable change) {
MTable table = tables.get(change.getBaseTable());
if (table == null) {
throw new IllegalStateException("Table [" + change.getBaseTable() + "] does not exist in model?");
if (table != null) {
table.setWithHistory(false);
}
table.setWithHistory(false);
}
private void applyChange(AddUniqueConstraint change) {
@@ -241,19 +240,14 @@ public class ModelContainer {
if (tables.containsKey(tableName)) {
throw new IllegalStateException("Table [" + tableName + "] already exists in model?");
}
MTable table = new MTable(createTable);
tables.put(tableName, table);
tables.put(tableName, new MTable(createTable));
}
/**
* Apply a DropTable change to the model.
*/
protected void applyChange(DropTable dropTable) {
String tableName = dropTable.getName();
if (!tables.containsKey(tableName)) {
throw new IllegalStateException("Table [" + tableName + "] does not exists in model?");
}
tables.remove(tableName);
tables.remove(dropTable.getName());
}
/**
@@ -264,22 +258,16 @@ public class ModelContainer {
if (indexes.containsKey(indexName)) {
throw new IllegalStateException("Index [" + indexName + "] already exists in model?");
}
MIndex index = new MIndex(createIndex);
indexes.put(createIndex.getIndexName(), index);
indexes.put(createIndex.getIndexName(), new MIndex(createIndex));
}
/**
* Apply a DropTable change to the model.
*/
protected void applyChange(DropIndex dropIndex) {
String name = dropIndex.getIndexName();
if (!indexes.containsKey(name)) {
throw new IllegalStateException("Index [" + name + "] does not exist in model?");
}
indexes.remove(name);
indexes.remove(dropIndex.getIndexName());
}
/**
* Apply a AddColumn change to the model.
*/
@@ -7,8 +7,14 @@ import io.ebeaninternal.server.query.SqlJoinType;
*/
class AssocOneHelpRefExported extends AssocOneHelp {
public AssocOneHelpRefExported(BeanPropertyAssocOne<?> property) {
private final boolean softDelete;
private final String softDeletePredicate;
AssocOneHelpRefExported(BeanPropertyAssocOne<?> property) {
super(property);
this.softDelete = property.targetDescriptor.isSoftDelete();
this.softDeletePredicate = (softDelete) ? property.targetDescriptor.getSoftDeletePredicate("") : null;
}
/**
@@ -28,6 +34,10 @@ class AssocOneHelpRefExported extends AssocOneHelp {
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
String relativePrefix = ctx.getRelativePrefix(property.getName());
property.tableJoin.addJoin(joinType, relativePrefix, ctx);
if (softDelete && !ctx.isIncludeSoftDelete()) {
property.tableJoin.addJoin(joinType, relativePrefix, ctx, softDeletePredicate);
} else {
property.tableJoin.addJoin(joinType, relativePrefix, ctx);
}
}
}
@@ -12,8 +12,6 @@ public interface DbSqlContext {
*/
void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2, String inheritance);
void pushSecondaryTableAlias(String alias);
/**
* Push the current table alias onto the stack.
*/
@@ -91,11 +89,6 @@ public interface DbSqlContext {
*/
String getContent();
/**
* Return the current join node.
*/
String peekJoin();
/**
* Push a join node onto the stack.
*/
@@ -125,6 +118,11 @@ public interface DbSqlContext {
*/
void appendHistorySysPeriod();
/**
* Return true if the query includes soft deleted rows.
*/
boolean isIncludeSoftDelete();
/**
* Return true if the query is a 'asDraft' query.
*/
@@ -139,4 +137,5 @@ public interface DbSqlContext {
* Append 'for update' lock hints on FROM clause (sql server only).
*/
void appendFromForUpdate();
}
@@ -126,6 +126,16 @@ public final class TableJoin {
return type;
}
public SqlJoinType addJoin(SqlJoinType joinType, String prefix, DbSqlContext ctx, String predicate) {
String[] names = SplitName.split(prefix);
String a1 = ctx.getTableAlias(names[0]);
String a2 = ctx.getTableAlias(prefix);
SqlJoinType returnJoinType = addJoin(joinType, a1, a2, ctx);
ctx.append("and ").append(a2).append(predicate);
return returnJoinType;
}
public SqlJoinType addJoin(SqlJoinType joinType, String prefix, DbSqlContext ctx) {
String[] names = SplitName.split(prefix);
@@ -70,6 +70,11 @@ class DefaultDbSqlContext implements DbSqlContext {
this.fromForUpdate = fromForUpdate;
}
@Override
public boolean isIncludeSoftDelete() {
return alias.isIncludeSoftDelete();
}
@Override
public void appendFromForUpdate() {
if (fromForUpdate != null) {
@@ -99,11 +104,6 @@ class DefaultDbSqlContext implements DbSqlContext {
return encryptedProps.toArray(new BeanProperty[encryptedProps.size()]);
}
@Override
public String peekJoin() {
return joinStack.peek();
}
@Override
public void popJoin() {
joinStack.pop();
@@ -206,11 +206,6 @@ class DefaultDbSqlContext implements DbSqlContext {
return alias.getTableAliasManyWhere(prefix);
}
@Override
public void pushSecondaryTableAlias(String alias) {
tableAliasStack.push(alias);
}
@Override
public String getRelativePrefix(String propName) {
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.query;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.SpiQuery;
import java.util.HashMap;
import java.util.HashSet;
@@ -17,6 +18,8 @@ class SqlTreeAlias {
private static final Pattern TABLE_ALIAS_REPLACE = Pattern.compile("${}", Pattern.LITERAL);
private final SpiQuery.TemporalMode temporalMode;
private int counter;
private int manyWhereCounter;
@@ -33,8 +36,9 @@ class SqlTreeAlias {
private final String rootTableAlias;
SqlTreeAlias(String rootTableAlias) {
SqlTreeAlias(String rootTableAlias, SpiQuery.TemporalMode temporalMode) {
this.rootTableAlias = rootTableAlias;
this.temporalMode = temporalMode;
}
/**
@@ -211,4 +215,8 @@ class SqlTreeAlias {
boolean isIncludeJoins() {
return !aliasMap.isEmpty() || !manyWhereAliasMap.isEmpty();
}
boolean isIncludeSoftDelete() {
return temporalMode == SpiQuery.TemporalMode.SOFT_DELETED;
}
}
@@ -110,7 +110,7 @@ public final class SqlTreeBuilder {
this.queryDetail = query.getDetail();
this.predicates = predicates;
this.alias = new SqlTreeAlias(request.getBaseTableAlias());
this.alias = new SqlTreeAlias(request.getBaseTableAlias(), temporalMode);
this.distinctOnPlatform = builder.isPlatformDistinctOn();
String fromForUpdate = builder.fromForUpdate(query);
@@ -351,7 +351,7 @@ public final class DefaultTypeManager implements TypeManager {
if (type.equals(List.class)) {
if (arrayTypeListFactory != null) {
if (isEnumType(valueType)) {
return arrayTypeListFactory.typeForEnum(createEnumScalarType(asEnumClass(valueType), EnumType.STRING));
return arrayTypeListFactory.typeForEnum(createEnumScalarType(asEnumClass(valueType), null));
}
return arrayTypeListFactory.typeFor(valueType);
}
@@ -360,7 +360,7 @@ public final class DefaultTypeManager implements TypeManager {
} else if (type.equals(Set.class)) {
if (arrayTypeSetFactory != null) {
if (isEnumType(valueType)) {
return arrayTypeSetFactory.typeForEnum(createEnumScalarType(asEnumClass(valueType), EnumType.STRING));
return arrayTypeSetFactory.typeForEnum(createEnumScalarType(asEnumClass(valueType), null));
}
return arrayTypeSetFactory.typeFor(valueType);
}
@@ -13,16 +13,16 @@ if not exists (select name from sys.types where name = 'ebean_uniqueidentifier_
if not exists (select name from sys.types where name = 'ebean_nvarchar_tvp') create type ebean_nvarchar_tvp as table (c1 nvarchar(max));
delimiter $$
-----------------------------------------------------------
--
-- PROCEDURE: usp_ebean_drop_indices TABLE, COLUMN
-- deletes all indices referring to TABLE.COLUMN
-----------------------------------------------------------
--
CREATE OR ALTER PROCEDURE usp_ebean_drop_indices @tableName nvarchar(255), @columnName nvarchar(255)
AS SET NOCOUNT ON
declare @sql nvarchar(1000)
declare @indexName nvarchar(255)
BEGIN
DECLARE index_cursor CURSOR FOR SELECT i.name from sys.indexes i
DECLARE index_cursor CURSOR FOR SELECT i.name from sys.indexes i
join sys.index_columns ic on ic.object_id = i.object_id and ic.index_id = i.index_id
join sys.columns c on c.object_id = ic.object_id and c.column_id = ic.column_id
where i.object_id = OBJECT_ID(@tableName) AND c.name = @columnName;
@@ -41,10 +41,10 @@ END
$$
delimiter $$
--------------------------------------------------------------------
--
-- PROCEDURE: usp_ebean_drop_default_constraint TABLE, COLUMN
-- deletes the default constraint, which has a random name
--------------------------------------------------------------------
--
CREATE OR ALTER PROCEDURE usp_ebean_drop_default_constraint @tableName nvarchar(255), @columnName nvarchar(255)
AS SET NOCOUNT ON
declare @tmp nvarchar(1000)
@@ -58,16 +58,16 @@ END
$$
delimiter $$
--------------------------------------------------------------------
--
-- PROCEDURE: usp_ebean_drop_constraints TABLE, COLUMN
-- deletes constraints and foreign keys refering to TABLE.COLUMN
--------------------------------------------------------------------
--
CREATE OR ALTER PROCEDURE usp_ebean_drop_constraints @tableName nvarchar(255), @columnName nvarchar(255)
AS SET NOCOUNT ON
declare @sql nvarchar(1000)
declare @constraintName nvarchar(255)
BEGIN
DECLARE name_cursor CURSOR FOR
DECLARE name_cursor CURSOR FOR
SELECT cc.name from sys.check_constraints cc
join sys.columns c on c.object_id = cc.parent_object_id and c.column_id = cc.parent_column_id
where parent_object_id = OBJECT_ID(@tableName) AND c.name = @columnName
@@ -92,10 +92,10 @@ END
$$
delimiter $$
-------------------------------------------------------------------------------------
--
-- PROCEDURE: usp_ebean_drop_column TABLE, COLUMN
-- deletes the column annd ensures that all indices and constraints are dropped first
-------------------------------------------------------------------------------------
--
CREATE OR ALTER PROCEDURE usp_ebean_drop_column @tableName nvarchar(255), @columnName nvarchar(255)
AS SET NOCOUNT ON
declare @sql nvarchar(1000)
@@ -114,21 +114,21 @@ $$
DROP PROCEDURE IF EXISTS usp_ebean_drop_foreign_keys;
delimiter $$
------------------------------------------------------------------------------
--
-- PROCEDURE: usp_ebean_drop_foreign_keys TABLE, COLUMN
-- deletes all constraints and foreign keys referring to TABLE.COLUMN
------------------------------------------------------------------------------
--
CREATE PROCEDURE usp_ebean_drop_foreign_keys(IN p_table_name VARCHAR(255), IN p_column_name VARCHAR(255))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE c_fk_name CHAR(255);
DECLARE curs CURSOR FOR SELECT CONSTRAINT_NAME from information_schema.KEY_COLUMN_USAGE
DECLARE curs CURSOR FOR SELECT CONSTRAINT_NAME from information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = DATABASE() and TABLE_NAME = p_table_name and COLUMN_NAME = p_column_name
AND REFERENCED_TABLE_NAME IS NOT NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN curs;
read_loop: LOOP
FETCH curs INTO c_fk_name;
IF done THEN
@@ -138,7 +138,7 @@ BEGIN
PREPARE stmt FROM @sql;
EXECUTE stmt;
END LOOP;
CLOSE curs;
END
$$
@@ -146,10 +146,10 @@ $$
DROP PROCEDURE IF EXISTS usp_ebean_drop_column;
delimiter $$
-------------------------------------------------------------------------------------
--
-- PROCEDURE: usp_ebean_drop_column TABLE, COLUMN
-- deletes the column and ensures that all indices and constraints are dropped first
-------------------------------------------------------------------------------------
--
CREATE PROCEDURE usp_ebean_drop_column(IN p_table_name VARCHAR(255), IN p_column_name VARCHAR(255))
BEGIN
CALL usp_ebean_drop_foreign_keys(p_table_name, p_column_name);
@@ -1,6 +1,9 @@
package io.ebeaninternal.dbmigration.model;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.migration.DropIndex;
import io.ebeaninternal.dbmigration.migration.DropTable;
import io.ebeaninternal.dbmigration.migration.Migration;
import io.ebeaninternal.dbmigration.migrationreader.MigrationXmlReader;
import org.junit.Test;
@@ -79,6 +82,31 @@ public class ModelContainerTest {
assertThat(container.getPendingDrops()).isEmpty();
}
@Test
public void apply_dropTable_when_notInModel_then_ok() {
ModelContainer container = new ModelContainer();
container.apply(mig("5.0__dropTable.model.xml"), ver("5.0"));
assertThat(container.getTables()).isEmpty();
}
@Test
public void apply_drop_when_notInModel_then_ok() {
ModelContainer container = new ModelContainer();
DropTable dropTable = new DropTable();
dropTable.setName("DoesNotExist");
container.applyChange(dropTable);
DropIndex dropIndex = new DropIndex();
dropIndex.setIndexName("DoesNotExist");
container.applyChange(dropIndex);
DropHistoryTable dropHistoryTable = new DropHistoryTable();
dropHistoryTable.setBaseTable("DoesNotExist");
container.applyChange(dropHistoryTable);
}
private ModelContainer container_2_1() {
ModelContainer container = new ModelContainer();
container.apply(mig("2.0.model.xml"), ver("2.0"));
@@ -72,7 +72,7 @@ public class TestQueryCacheTableDependency extends BaseTestCase {
.findCount();
assertThat(custs).isEqualTo(1);
Ebean.createSqlUpdate("update O_ADDRESS set line_2=? where line_2=?")
Ebean.createSqlUpdate("update o_address set line_2=? where line_2=?")
.setNextParameter("St Lucky3")
.setNextParameter("St Lucky2")
.execute();
@@ -18,6 +18,8 @@ public class EArrayBean {
ONE, TWO, THREE
}
IntEnum foo;
@Id
Long id;
@@ -50,6 +52,14 @@ public class EArrayBean {
@Version
Long version;
public IntEnum getFoo() {
return foo;
}
public void setFoo(final IntEnum foo) {
this.foo = foo;
}
public Long getId() {
return id;
}
@@ -8,14 +8,14 @@ import java.util.Objects;
@Embeddable
public class CkeUserKey {
@Basic(optional = false)
@Column(name = "cod_cpny")
private int codCompany;
@Basic(optional = false)
@Column(name = "username")
private String username;
@Basic(optional = false)
@Column(name = "cod_cpny")
private int codCompany;
public CkeUserKey(int codCompany, String username) {
this.codCompany = codCompany;
this.username = username;
@@ -2,6 +2,7 @@ package org.tests.model.onetoone;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
@@ -13,7 +14,7 @@ public class OtoBMaster {
String name;
@OneToOne(cascade = CascadeType.ALL, mappedBy = "master")
@OneToOne(cascade = CascadeType.ALL, mappedBy = "master", fetch = FetchType.LAZY)
OtoBChild child;
public Long getId() {
@@ -0,0 +1,73 @@
package org.tests.model.onetoone;
import io.ebean.Finder;
import io.ebean.annotation.SoftDelete;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Version;
@Entity
public class OtoSdChild {
public static Finder<Long, OtoSdChild> find = new Finder<>(OtoSdChild.class);
@Id
long id;
String child;
@SoftDelete
boolean deleted;
@OneToOne
OtoSdMaster master;
@Version
long version;
public OtoSdChild(String child) {
this.child = child;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getChild() {
return child;
}
public void setChild(String child) {
this.child = child;
}
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
public OtoSdMaster getMaster() {
return master;
}
public void setMaster(OtoSdMaster master) {
this.master = master;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
@@ -0,0 +1,55 @@
package org.tests.model.onetoone;
import io.ebean.Finder;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Version;
@Entity
public class OtoSdMaster {
public static Finder<Long, OtoSdMaster> find = new Finder<>(OtoSdMaster.class);
@Id
long id;
String name;
@OneToOne(cascade = CascadeType.ALL, mappedBy = "master")//, fetch = FetchType.LAZY)
OtoSdChild child;
@Version
long version;
public OtoSdMaster(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 OtoSdChild getChild() {
return child;
}
public void setChild(OtoSdChild child) {
this.child = child;
}
}
@@ -3,12 +3,53 @@ package org.tests.model.onetoone;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.EbeanServer;
import io.ebean.Query;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class TestOneToOneImportedPkNative extends BaseTestCase {
@Test
public void findWithLazyOneToOne() {
OtoBChild child = new OtoBChild();
child.setChild("c1");
OtoBMaster master = new OtoBMaster();
master.setName("m2");
master.setChild(child);
Ebean.save(master);
Query<OtoBMaster> query = Ebean.find(OtoBMaster.class)
//.select("name")
.where().idEq(master.getId())
.query();
OtoBMaster one = query.findOne();
String sql = sqlOf(query);
assertThat(sql).contains("select t0.id, t0.name from oto_bmaster t0 where t0.id ");
assertThat(sql).doesNotContain("left join oto_bchild");
assertThat(one).isNotNull();
LoggedSqlCollector.start();
OtoBChild child1 = one.getChild();
assertThat(child1).isNotNull();
assertThat(child1.getChild()).isEqualTo("c1");
List<String> lazyLoadSql = LoggedSqlCollector.stop();
assertThat(lazyLoadSql).hasSize(2);
assertThat(lazyLoadSql.get(0)).contains("select t0.id, t0.name, t1.master_id from oto_bmaster t0 left join oto_bchild t1");
assertThat(lazyLoadSql.get(1)).contains("select t0.master_id, t0.child, t0.master_id from oto_bchild t0 where t0.master_id");
}
@Test
public void native_with_o2oAndImportedPrimaryKey() {
@@ -0,0 +1,47 @@
package org.tests.model.onetoone;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class TestOneToOneSoftDeleteChild extends BaseTestCase {
@Test
public void deleteChild() {
OtoSdChild child = new OtoSdChild("c1");
OtoSdMaster master = new OtoSdMaster("m1");
master.setChild(child);
Ebean.save(master);
verifyBeforeDelete(master, child);
Ebean.delete(child);
verifyAfterDelete(master, child);
}
private void verifyBeforeDelete(OtoSdMaster parent, OtoSdChild child) {
assertThat(OtoSdMaster.find.byId(parent.getId()).getChild().getId())
.isEqualTo(child.getId());
assertThat(
OtoSdChild.find.byId(child.getId()).getMaster().getId())
.isEqualTo(parent.getId());
}
private void verifyAfterDelete(OtoSdMaster parent, OtoSdChild child) {
// After delete, finding child by id should return null
assertThat(OtoSdChild.find.byId(child.getId()))
.isNull();
// After delete, getting linked child from parent should return null
assertThat(OtoSdMaster.find.byId(parent.getId()).getChild())
.isNull();
}
}
@@ -0,0 +1,58 @@
package org.tests.model.orphanremoval;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Version;
@Entity
public class OrpDetail {
@Id
String id;
String detail;
@ManyToOne
OrpMaster master;
@Version
long version;
public OrpDetail(String id, String detail) {
this.id = id;
this.detail = detail;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public OrpMaster getMaster() {
return master;
}
public void setMaster(OrpMaster master) {
this.master = master;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
@@ -0,0 +1,60 @@
package org.tests.model.orphanremoval;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Version;
import java.util.List;
@Entity
public class OrpMaster {
@Id
String id;
String name;
@OneToMany(orphanRemoval = true, cascade = CascadeType.ALL)
List<OrpDetail> details;
@Version
long version;
public OrpMaster(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<OrpDetail> getDetails() {
return details;
}
public void setDetails(List<OrpDetail> details) {
this.details = details;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
@@ -0,0 +1,36 @@
package org.tests.model.orphanremoval;
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 TestOrphanRemoveO2M extends BaseTestCase {
@Test
public void clear_expect_deletes() {
OrpMaster master = new OrpMaster("m","master");
master.getDetails().add(new OrpDetail("d1", "d1"));
master.getDetails().add(new OrpDetail("d2", "d2"));
Ebean.save(master);
master.getDetails().clear();
LoggedSqlCollector.start();
Ebean.save(master);
List<String> sql = LoggedSqlCollector.stop();
assertThat(Ebean.find(OrpDetail.class, "d1")).isNull();
assertThat(Ebean.find(OrpDetail.class, "d2")).isNull();
assertThat(sql).hasSize(2);
assertThat(sql.get(0)).contains("delete from orp_detail where id=?");
}
}
@@ -2,21 +2,21 @@
DROP PROCEDURE IF EXISTS usp_ebean_drop_foreign_keys;
delimiter $$
------------------------------------------------------------------------------
--
-- PROCEDURE: usp_ebean_drop_foreign_keys TABLE, COLUMN
-- deletes all constraints and foreign keys referring to TABLE.COLUMN
------------------------------------------------------------------------------
--
CREATE PROCEDURE usp_ebean_drop_foreign_keys(IN p_table_name VARCHAR(255), IN p_column_name VARCHAR(255))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE c_fk_name CHAR(255);
DECLARE curs CURSOR FOR SELECT CONSTRAINT_NAME from information_schema.KEY_COLUMN_USAGE
DECLARE curs CURSOR FOR SELECT CONSTRAINT_NAME from information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = DATABASE() and TABLE_NAME = p_table_name and COLUMN_NAME = p_column_name
AND REFERENCED_TABLE_NAME IS NOT NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN curs;
read_loop: LOOP
FETCH curs INTO c_fk_name;
IF done THEN
@@ -26,7 +26,7 @@ BEGIN
PREPARE stmt FROM @sql;
EXECUTE stmt;
END LOOP;
CLOSE curs;
END
$$
@@ -34,10 +34,10 @@ $$
DROP PROCEDURE IF EXISTS usp_ebean_drop_column;
delimiter $$
-------------------------------------------------------------------------------------
--
-- PROCEDURE: usp_ebean_drop_column TABLE, COLUMN
-- deletes the column and ensures that all indices and constraints are dropped first
-------------------------------------------------------------------------------------
--
CREATE PROCEDURE usp_ebean_drop_column(IN p_table_name VARCHAR(255), IN p_column_name VARCHAR(255))
BEGIN
CALL usp_ebean_drop_foreign_keys(p_table_name, p_column_name);
@@ -10,16 +10,16 @@ if not exists (select name from sys.types where name = 'ebean_uniqueidentifier_
if not exists (select name from sys.types where name = 'ebean_nvarchar_tvp') create type ebean_nvarchar_tvp as table (c1 nvarchar(max));
delimiter $$
-----------------------------------------------------------
--
-- PROCEDURE: usp_ebean_drop_indices TABLE, COLUMN
-- deletes all indices referring to TABLE.COLUMN
-----------------------------------------------------------
--
CREATE OR ALTER PROCEDURE usp_ebean_drop_indices @tableName nvarchar(255), @columnName nvarchar(255)
AS SET NOCOUNT ON
declare @sql nvarchar(1000)
declare @indexName nvarchar(255)
BEGIN
DECLARE index_cursor CURSOR FOR SELECT i.name from sys.indexes i
DECLARE index_cursor CURSOR FOR SELECT i.name from sys.indexes i
join sys.index_columns ic on ic.object_id = i.object_id and ic.index_id = i.index_id
join sys.columns c on c.object_id = ic.object_id and c.column_id = ic.column_id
where i.object_id = OBJECT_ID(@tableName) AND c.name = @columnName;
@@ -38,10 +38,10 @@ END
$$
delimiter $$
--------------------------------------------------------------------
--
-- PROCEDURE: usp_ebean_drop_default_constraint TABLE, COLUMN
-- deletes the default constraint, which has a random name
--------------------------------------------------------------------
--
CREATE OR ALTER PROCEDURE usp_ebean_drop_default_constraint @tableName nvarchar(255), @columnName nvarchar(255)
AS SET NOCOUNT ON
declare @tmp nvarchar(1000)
@@ -55,16 +55,16 @@ END
$$
delimiter $$
--------------------------------------------------------------------
--
-- PROCEDURE: usp_ebean_drop_constraints TABLE, COLUMN
-- deletes constraints and foreign keys refering to TABLE.COLUMN
--------------------------------------------------------------------
--
CREATE OR ALTER PROCEDURE usp_ebean_drop_constraints @tableName nvarchar(255), @columnName nvarchar(255)
AS SET NOCOUNT ON
declare @sql nvarchar(1000)
declare @constraintName nvarchar(255)
BEGIN
DECLARE name_cursor CURSOR FOR
DECLARE name_cursor CURSOR FOR
SELECT cc.name from sys.check_constraints cc
join sys.columns c on c.object_id = cc.parent_object_id and c.column_id = cc.parent_column_id
where parent_object_id = OBJECT_ID(@tableName) AND c.name = @columnName
@@ -89,10 +89,10 @@ END
$$
delimiter $$
-------------------------------------------------------------------------------------
--
-- PROCEDURE: usp_ebean_drop_column TABLE, COLUMN
-- deletes the column annd ensures that all indices and constraints are dropped first
-------------------------------------------------------------------------------------
--
CREATE OR ALTER PROCEDURE usp_ebean_drop_column @tableName nvarchar(255), @columnName nvarchar(255)
AS SET NOCOUNT ON
declare @sql nvarchar(1000)
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
<changeSet type="apply">
<dropTable name="some_table"/>
</changeSet>
</migration>