Testcase demonstrating stateless update resulting in a delete

This commit is contained in:
Jan Klička
2022-06-29 16:09:51 +02:00
parent fa11dac2b2
commit bb8f3831b8
4 changed files with 199 additions and 0 deletions
@@ -0,0 +1,78 @@
package org.tests.o2m;
import io.ebean.DB;
import io.ebean.test.LoggedSql;
import io.ebean.xtest.BaseTestCase;
import org.junit.jupiter.api.Test;
import org.tests.o2m.dm.GoodsEntity;
import org.tests.o2m.dm.WorkflowEntity;
import org.tests.o2m.dm.WorkflowOperationEntity;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class TestOneToManyStatelessUpdateResultsInSoftDelete extends BaseTestCase {
@Test
public void testStatelessUpdate() {
LoggedSql.start();
var goods = new GoodsEntity();
var workflow = new WorkflowEntity();
var operation1 = new WorkflowOperationEntity();
goods.setWorkflowEntity(workflow);
workflow.setOperations(List.of(operation1));
DB.save(goods);
var dbGoods = DB.find(GoodsEntity.class).findOne();
// sanity check asserts
assertThat(dbGoods.getId()).isEqualTo(1L);
assertThat(dbGoods.getWorkflowEntity().getId()).isEqualTo(1L);
assertThat(dbGoods.getWorkflowEntity().getOperations()).hasSize(1);
assertThat(dbGoods.getWorkflowEntity().getOperations().get(0).getId()).isEqualTo(1L);
List<String> createSql = LoggedSql.stop();
LoggedSql.start();
// statelessly add another operation to the workflow and save goods
var goodsStateless = new GoodsEntity();
goodsStateless.setId(1L);
var workflowStateless = new WorkflowEntity();
workflowStateless.setId(1L);
var operation1Stateless = new WorkflowOperationEntity();
operation1Stateless.setId(1L);
var operation2 = new WorkflowOperationEntity();
goodsStateless.setWorkflowEntity(workflowStateless);
workflowStateless.setOperations(List.of(operation1Stateless, operation2));
/*
- this update generates following statements
1 txn[] delete from workflow_operation_entity where workflow_id=?
2 txn[] -- bind(1)
3 txn[] update workflow_entity set when_modified=? where id=?
4 txn[] -- bind(2022-06-29 15:43:55.573,1)
5 txn[] update workflow_operation_entity set deleted=true where workflow_id = ? and not ( id in (?) )
6 txn[] -- bind(1, Array[1]={1})
7 txn[] insert into workflow_operation_entity (name, version, when_created, when_modified, deleted, workflow_id) values (?,?,?,?,?,?)
8 txn[] -- bind(null,1,2022-06-29 15:43:55.584,2022-06-29 15:43:55.584,false,1)
9 txn[] update goods_entity set when_modified=?, workflow_entity_id=? where id=?; -- bind(2022-06-29 15:43:55.573,1,1)
- number 1 is wrong
- no delete should be issued
- even if it was issued, it should have been soft delete
- the DB.update will throw exception if there is one-to-many relation on workflow_operation_entity
- it would still be referenced from other table
*/
DB.update(goodsStateless);
var updateSql = LoggedSql.stop();
updateSql.forEach(System.out::println);
var dbGoodsAfterUpdate = DB.find(GoodsEntity.class).findOne();
assertThat(dbGoodsAfterUpdate.getWorkflowEntity().getOperations()).hasSize(2);
assertThat(dbGoodsAfterUpdate.getWorkflowEntity().getOperations().get(0).getId()).isEqualTo(1L);
assertThat(dbGoodsAfterUpdate.getWorkflowEntity().getOperations().get(1).getId()).isEqualTo(2L);
updateSql.forEach(sql -> assertThat(sql).doesNotContain("delete from workflow_entity"));
}
}
@@ -0,0 +1,32 @@
package org.tests.o2m.dm;
import org.tests.model.draftable.BaseDomain;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
@Entity
public class GoodsEntity extends BaseDomain {
private String name;
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
private WorkflowEntity workflowEntity;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public WorkflowEntity getWorkflowEntity() {
return workflowEntity;
}
public void setWorkflowEntity(WorkflowEntity workflowEntity) {
this.workflowEntity = workflowEntity;
}
}
@@ -0,0 +1,44 @@
package org.tests.o2m.dm;
import io.ebean.annotation.SoftDelete;
import org.tests.model.draftable.BaseDomain;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class WorkflowEntity extends BaseDomain {
private String revision;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "workflow_id")
private List<WorkflowOperationEntity> operations = new ArrayList<>();
@SoftDelete
private boolean deleted;
public String getRevision() {
return revision;
}
public void setRevision(String revision) {
this.revision = revision;
}
public List<WorkflowOperationEntity> getOperations() {
return operations;
}
public void setOperations(List<WorkflowOperationEntity> operations) {
this.operations = operations;
}
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
}
@@ -0,0 +1,45 @@
package org.tests.o2m.dm;
import io.ebean.annotation.SoftDelete;
import org.tests.model.draftable.BaseDomain;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class WorkflowOperationEntity extends BaseDomain {
private String name;
@ManyToOne
@JoinColumn(name = "workflow_id")
private WorkflowEntity workflowEntity;
@SoftDelete
private boolean deleted;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public WorkflowEntity getWorkflowEntity() {
return workflowEntity;
}
public void setWorkflowEntity(WorkflowEntity workflowEntity) {
this.workflowEntity = workflowEntity;
}
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
}