ebean-orm / ebean-orm/ebean

Inheritance cascade of fields with bean inheritance

Open
#1,846 2 comments 0 reactions 1 assignee View on GitHub

@rbygrave is already working on this.

Since Oct 18, 2019.

bug
Dominant language
Java
Stars
1.5k
Forks
267
Avg merge
3d 20h
Merged PRs (30d)
5

Description

Deletes aren't cascaded to embedded beans referenced using @ElementCollection

Steps to reproduce

The (abstract) data model is:

  • A bean (RootBean) that is inherited by SimpleBean and ComplexBean
  • ComplexBean embeds instances of ElementBean
  • RootBeans are referenced through @OneToMany from ReferencingBean
@Entity
class ReferencingBean {
    @Id
    @GeneratedValue
    public UUID id;

    @OneToMany(cascade = CascadeType.ALL)
    private List<RootBean> rootBeans;

    public ReferencingBean(List<RootBean> rootBeans) { ... }
    public List<RootBean> getRootBeans() { ... }
    public void setRootBeans(List<RootBean> rootBeans) { ... }
}

@Entity
@Inheritance
abstract class RootBean {
    @Id
    @GeneratedValue
    public UUID id;
}

@Entity
class SimpleBean extends RootBean {
    private String value;

    public SimpleBean(String value) { ... }
    public String getValue() { ... }
    public void setValue(String value) { ... }
}

@Entity
class ComplexBean extends RootBean {
    @ElementCollection
    private List<ElementBean> elements;

    public ComplexBean(List<ElementBean> elements) { ... }
    public List<ElementBean> getElements() { ... }
    public void setElements(List<ElementBean> elements) { ... }
}

@Embeddable
class ElementBean {
    @EmbeddedId
    @GeneratedValue
    private Long id;

    @NotNull
    private String value;

    public ElementBean(String value) { ... }
    public String getValue() { ... }
}

Instantiating the data structure described above and then deleting a ReferencingBean fails as illustrated by the test below:

RootBean bean1 = new ComplexBean(asList(new ElementBean("element-1"), new ElementBean("element-2")));
RootBean bean2 = new SimpleBean("simple-1");
ReferencingBean referencingBean = new ReferencingBean(asList(bean1,bean2));

database.save(referencingBean);
database.delete(referencingBean);

assertNull(database.find(RootBean.class, database.getBeanId(bean1)));
assertNull(database.find(RootBean.class, database.getBeanId(bean2)));

with the following output:


2019-10-17 09:37:28,307  INFO [main] io.ebean.config.properties.LoadContext:83 - loaded properties from []
2019-10-17 09:37:28,325  INFO [main] io.ebean.EbeanVersion:31 - ebean version: 11.45.1
2019-10-17 09:37:28,629  INFO [main] io.ebean.datasource.pool.ConnectionPool:297 - DataSourcePool [db] autoCommit[false] transIsolation[READ_COMMITTED] min[2] max[200]
2019-10-17 09:37:28,822  INFO [main] io.ebeaninternal.server.core.DefaultContainer:208 - DatabasePlatform name:db platform:h2
2019-10-17 09:37:29,466  INFO [main] io.ebean.migration.ddl.DdlRunner:65 - Executing db-drop-all.sql - 35 statements
2019-10-17 09:37:29,475  INFO [main] io.ebean.migration.ddl.DdlRunner:65 - Executing db-create-all.sql - 35 statements
2019-10-17 09:37:29,538 DEBUG [main] io.ebeaninternal.server.logger.DSpiLogger:26 - txn[] insert into referencing_bean (id) values (?); -- bind(3bb12ba3-a997-4466-bb4e-f87ab996d43b)
2019-10-17 09:37:29,547 DEBUG [main] io.ebeaninternal.server.logger.DSpiLogger:26 - txn[] insert into root_bean (id, referencing_bean_id, dtype) values (?,?,?)
2019-10-17 09:37:29,547 DEBUG [main] io.ebeaninternal.server.logger.DSpiLogger:26 - txn[]  -- bind(f31c05f1-ab35-45d9-9206-a28ae91eeb7f,3bb12ba3-a997-4466-bb4e-f87ab996d43b,ComplexBean)
2019-10-17 09:37:29,555 DEBUG [main] io.ebeaninternal.server.logger.DSpiLogger:26 - txn[] insert into root_bean_elements (root_bean_id,id,value) values (?,?,?)
2019-10-17 09:37:29,556 DEBUG [main] io.ebeaninternal.server.logger.DSpiLogger:26 - txn[]  -- bind(f31c05f1-ab35-45d9-9206-a28ae91eeb7f, null, element-1)
2019-10-17 09:37:29,556 DEBUG [main] io.ebeaninternal.server.logger.DSpiLogger:26 - txn[]  -- bind(f31c05f1-ab35-45d9-9206-a28ae91eeb7f, null, element-2)
2019-10-17 09:37:29,559 DEBUG [main] io.ebeaninternal.server.logger.DSpiLogger:26 - txn[] insert into root_bean (id, referencing_bean_id, dtype, value) values (?,?,?,?)
2019-10-17 09:37:29,559 DEBUG [main] io.ebeaninternal.server.logger.DSpiLogger:26 - txn[]  -- bind(53372fc0-8928-40f1-a1f0-15c66d5d3aaa,3bb12ba3-a997-4466-bb4e-f87ab996d43b,SimpleBean,simple-1)
2019-10-17 09:37:29,591 DEBUG [main] io.ebeaninternal.server.logger.DSpiLogger:26 - txn[] select t0.id from referencing_bean t0 where t0.id = ?  ; --bind(3bb12ba3-a997-4466-bb4e-f87ab996d43b, )
2019-10-17 09:37:29,595 DEBUG [main] io.ebeaninternal.server.logger.DSpiLogger:26 - txn[] select t0.dtype, t0.id, t0.value from root_bean t0 where t0.id = ?  ; --bind(f31c05f1-ab35-45d9-9206-a28ae91eeb7f, )
2019-10-17 09:37:29,596 DEBUG [main] io.ebeaninternal.server.logger.DSpiLogger:26 - txn[] select t0.dtype, t0.id, t0.value from root_bean t0 where t0.id = ?  ; --bind(53372fc0-8928-40f1-a1f0-15c66d5d3aaa, )
2019-10-17 09:37:29,603 DEBUG [main] io.ebeaninternal.server.logger.DSpiLogger:26 - txn[] select t0.id from root_bean t0 where referencing_bean_id=? ; --bind(3bb12ba3-a997-4466-bb4e-f87ab996d43b)



io.ebean.DataIntegrityException: Referential integrity constraint violation: "FK_ROOT_BEAN_ELEMENTS_ROOT_BEAN_ID: PUBLIC.ROOT_BEAN_ELEMENTS FOREIGN KEY(ROOT_BEAN_ID) REFERENCES PUBLIC.ROOT_BEAN(ID) ('f31c05f1-ab35-45d9-9206-a28ae91eeb7f')"; SQL statement:
delete from root_bean where id  in (?,?) [23503-193]

	at io.ebean.config.dbplatform.SqlCodeTranslator.translate(SqlCodeTranslator.java:49)
	at io.ebean.config.dbplatform.DatabasePlatform.translate(DatabasePlatform.java:227)
	at io.ebeaninternal.server.transaction.TransactionManager.translate(TransactionManager.java:243)
	at io.ebeaninternal.server.transaction.JdbcTransaction.translate(JdbcTransaction.java:689)
	at io.ebeaninternal.server.core.PersistRequest.translateSqlException(PersistRequest.java:105)
	at io.ebeaninternal.server.persist.ExeUpdateSql.execute(ExeUpdateSql.java:59)
	at io.ebeaninternal.server.persist.DefaultPersistExecute.executeSqlUpdate(DefaultPersistExecute.java:95)
	at io.ebeaninternal.server.core.PersistRequestUpdateSql.executeNow(PersistRequestUpdateSql.java:85)
	at io.ebeaninternal.server.core.PersistRequest.executeStatement(PersistRequest.java:126)
	at io.ebeaninternal.server.core.PersistRequestUpdateSql.executeOrQueue(PersistRequestUpdateSql.java:95)
	at io.ebeaninternal.server.persist.DefaultPersister.executeOrQueue(DefaultPersister.java:121)
	at io.ebeaninternal.server.persist.DefaultPersister.executeSqlUpdate(DefaultPersister.java:166)
	at io.ebeaninternal.server.persist.DefaultPersister.delete(DefaultPersister.java:827)
	at io.ebeaninternal.server.persist.DefaultPersister.deleteChildrenById(DefaultPersister.java:1123)
	at io.ebeaninternal.server.persist.DefaultPersister.deleteManyDetails(DefaultPersister.java:1099)
	at io.ebeaninternal.server.persist.DefaultPersister.deleteAssocMany(DefaultPersister.java:1065)
	at io.ebeaninternal.server.persist.DefaultPersister.delete(DefaultPersister.java:891)
	at io.ebeaninternal.server.persist.DefaultPersister.deleteRequest(DefaultPersister.java:617)
	at io.ebeaninternal.server.persist.DefaultPersister.deleteRequest(DefaultPersister.java:597)
	at io.ebeaninternal.server.persist.DefaultPersister.delete(DefaultPersister.java:589)
	at io.ebeaninternal.server.core.DefaultServer.delete(DefaultServer.java:1978)
	at io.ebeaninternal.server.core.DefaultServer.delete(DefaultServer.java:1969)
	at ....InheritanceDeleteCascadeTest.test2(InheritanceDeleteCascadeTest.java:49)
	...
Caused by: org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "FK_ROOT_BEAN_ELEMENTS_ROOT_BEAN_ID: PUBLIC.ROOT_BEAN_ELEMENTS FOREIGN KEY(ROOT_BEAN_ID) REFERENCES PUBLIC.ROOT_BEAN(ID) ('f31c05f1-ab35-45d9-9206-a28ae91eeb7f')"; SQL statement:
delete from root_bean where id  in (?,?) [23503-193]
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
	at org.h2.message.DbException.get(DbException.java:179)
	at org.h2.message.DbException.get(DbException.java:155)
	at org.h2.constraint.ConstraintReferential.checkRow(ConstraintReferential.java:426)
	at org.h2.constraint.ConstraintReferential.checkRowRefTable(ConstraintReferential.java:443)
	at org.h2.constraint.ConstraintReferential.checkRow(ConstraintReferential.java:318)
	at org.h2.table.Table.fireConstraints(Table.java:967)
	at org.h2.table.Table.fireAfterRow(Table.java:985)
	at org.h2.command.dml.Delete.update(Delete.java:101)
	at org.h2.command.CommandContainer.update(CommandContainer.java:98)
	at org.h2.command.Command.executeUpdate(Command.java:258)
	at org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:160)
	at org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:146)
	at io.ebean.datasource.pool.ExtendedPreparedStatement.executeUpdate(ExtendedPreparedStatement.java:148)
	at io.ebeaninternal.server.persist.ExeUpdateSql.execute(ExeUpdateSql.java:50)
	... 40 more

By coincidence I discovered a workaround: adding a @PreDelete hook in the RootBean:

@Entity
@Inheritance
public abstract class RootBean {

    ...

    // added as workaround for the issue
    @PreRemove
    public void preRemove() {
    }

}

I hope this workaround gives a clue for a solution.

Let me know if I can do anything to help.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.