From 1a6a426683a36a3740acbe8d9ef939cb1e489522 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Fri, 17 Dec 2021 10:43:27 +1300 Subject: [PATCH] #2458 - Add BeanPersistController.isCascade() to identify cascade vs "top level" persist requests --- .../io/ebean/event/BeanPersistRequest.java | 6 ++ .../server/core/PersistRequestBean.java | 5 ++ .../basic/event/TestPreInsertValidation.java | 38 +++++++++++- .../org/tests/model/basic/TWithPreInsert.java | 26 ++++++++- .../model/basic/TWithPreInsertChild.java | 58 +++++++++++++++++++ .../model/basic/TWithPreInsertCommon.java | 14 +++++ .../event/TWithPreInsertPersistAdapter.java | 25 ++++---- 7 files changed, 154 insertions(+), 18 deletions(-) create mode 100644 ebean-test/src/test/java/org/tests/model/basic/TWithPreInsertChild.java create mode 100644 ebean-test/src/test/java/org/tests/model/basic/TWithPreInsertCommon.java diff --git a/ebean-api/src/main/java/io/ebean/event/BeanPersistRequest.java b/ebean-api/src/main/java/io/ebean/event/BeanPersistRequest.java index 57930a107..2f3198b65 100644 --- a/ebean-api/src/main/java/io/ebean/event/BeanPersistRequest.java +++ b/ebean-api/src/main/java/io/ebean/event/BeanPersistRequest.java @@ -43,6 +43,12 @@ public interface BeanPersistRequest { return transaction(); } + /** + * Return true if this request is due to cascading persist. + * False implies this is a "top level" request. + */ + boolean isCascade(); + /** * For an update or delete of a partially populated bean this is the set of * loaded properties and otherwise returns null. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java index dc14a15ae..2fbf362ee 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java @@ -213,6 +213,11 @@ public final class PersistRequestBean extends PersistRequest implements BeanP return beanDescriptor.docStoreMode(type, txnMode); } + @Override + public boolean isCascade() { + return Flags.isRecurse(flags); + } + /** * Return true if the draftDirty property should be set to true for this request. */ diff --git a/ebean-test/src/test/java/org/tests/basic/event/TestPreInsertValidation.java b/ebean-test/src/test/java/org/tests/basic/event/TestPreInsertValidation.java index 54f26782e..c67e63c4a 100644 --- a/ebean-test/src/test/java/org/tests/basic/event/TestPreInsertValidation.java +++ b/ebean-test/src/test/java/org/tests/basic/event/TestPreInsertValidation.java @@ -5,13 +5,15 @@ import io.ebean.BaseTestCase; import io.ebean.DB; import org.junit.jupiter.api.Test; import org.tests.model.basic.TWithPreInsert; +import org.tests.model.basic.TWithPreInsertChild; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertNotNull; -public class TestPreInsertValidation extends BaseTestCase { +class TestPreInsertValidation extends BaseTestCase { @Test - public void test() { + void test() { TWithPreInsert e = new TWithPreInsert(); e.setTitle("Mister"); @@ -22,15 +24,45 @@ public class TestPreInsertValidation extends BaseTestCase { // the save worked and name set in preInsert assertNotNull(e.getId()); assertNotNull(e.getName()); + assertThat(e.requestCascadeState()).isEqualTo(2); TWithPreInsert e1 = DB.find(TWithPreInsert.class, e.getId()); e1.setTitle("Missus"); DB.save(e1); + + assertThat(e1.requestCascadeState()).isEqualTo(12); } @Test - public void testStatelessUpdate() { + void test_cascade() { + + TWithPreInsert e = new TWithPreInsert(); + e.setTitle("ParentCascading"); + e.children().add(new TWithPreInsertChild("Child0")); + // the perInsert should populate the + // name with should not be null + DB.save(e); + + // the save worked and name set in preInsert + assertNotNull(e.getId()); + assertNotNull(e.getName()); + assertThat(e.requestCascadeState()).isEqualTo(2); + assertThat(e.children().get(0).requestCascadeState()).isEqualTo(1); + + TWithPreInsert e1 = DB.find(TWithPreInsert.class, e.getId()); + assert e1 != null; + + e1.setTitle("ParentCascading-changed"); + e1.children().get(0).setName("Child0-changed"); + DB.save(e1); + + assertThat(e1.requestCascadeState()).isEqualTo(12); + assertThat(e1.children().get(0).requestCascadeState()).isEqualTo(11); + } + + @Test + void testStatelessUpdate() { TWithPreInsert e = new TWithPreInsert(); e.setName("BeanForUpdateTest"); diff --git a/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsert.java b/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsert.java index f93378240..21398bc7a 100644 --- a/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsert.java +++ b/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsert.java @@ -2,10 +2,15 @@ package org.tests.model.basic; import javax.persistence.Entity; import javax.persistence.Id; +import javax.persistence.OneToMany; import javax.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; + +import static javax.persistence.CascadeType.PERSIST; @Entity -public class TWithPreInsert { +public class TWithPreInsert implements TWithPreInsertCommon { @Id private Integer id; @@ -15,6 +20,14 @@ public class TWithPreInsert { private String title; + @OneToMany(cascade = PERSIST) + private List children = new ArrayList<>(); + + /** + * For testing what happened in persist controller. + */ + transient int requestCascadeState; + public Integer getId() { return id; } @@ -39,4 +52,15 @@ public class TWithPreInsert { this.title = title; } + public List children() { + return children; + } + + public int requestCascadeState() { + return requestCascadeState; + } + + public void requestCascadeState(int requestCascadeState) { + this.requestCascadeState = requestCascadeState; + } } diff --git a/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsertChild.java b/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsertChild.java new file mode 100644 index 000000000..39a1d8dff --- /dev/null +++ b/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsertChild.java @@ -0,0 +1,58 @@ +package org.tests.model.basic; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.validation.constraints.NotNull; + +@Entity +public class TWithPreInsertChild implements TWithPreInsertCommon { + + @Id + private Integer id; + + @NotNull + private String name; + + private String title; + + /** + * For testing what happened in persist controller. + */ + transient int requestCascadeState; + + public TWithPreInsertChild(String name) { + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public int requestCascadeState() { + return requestCascadeState; + } + + public void requestCascadeState(int requestCascadeState) { + this.requestCascadeState = requestCascadeState; + } +} diff --git a/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsertCommon.java b/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsertCommon.java new file mode 100644 index 000000000..dc61e28ba --- /dev/null +++ b/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsertCommon.java @@ -0,0 +1,14 @@ +package org.tests.model.basic; + +public interface TWithPreInsertCommon { + + String getName(); + + void setName(String name); + + String getTitle(); + + void setTitle(String title); + + void requestCascadeState(int requestCascadeState); +} diff --git a/ebean-test/src/test/java/org/tests/model/basic/event/TWithPreInsertPersistAdapter.java b/ebean-test/src/test/java/org/tests/model/basic/event/TWithPreInsertPersistAdapter.java index 41b6fe141..03143d385 100644 --- a/ebean-test/src/test/java/org/tests/model/basic/event/TWithPreInsertPersistAdapter.java +++ b/ebean-test/src/test/java/org/tests/model/basic/event/TWithPreInsertPersistAdapter.java @@ -2,36 +2,33 @@ package org.tests.model.basic.event; import io.ebean.event.BeanPersistAdapter; import io.ebean.event.BeanPersistRequest; -import org.tests.model.basic.TWithPreInsert; +import org.tests.model.basic.TWithPreInsertCommon; public class TWithPreInsertPersistAdapter extends BeanPersistAdapter { @Override public boolean isRegisterFor(Class cls) { - return TWithPreInsert.class.equals(cls); + return TWithPreInsertCommon.class.isAssignableFrom(cls); } @Override public boolean preInsert(BeanPersistRequest request) { - - TWithPreInsert e = (TWithPreInsert) request.bean(); - if (e.getName() == null) { - e.setName("set on preInsert"); + TWithPreInsertCommon bean = (TWithPreInsertCommon) request.bean(); + if (bean.getName() == null) { + bean.setName("set on preInsert"); } + bean.requestCascadeState(request.isCascade() ? 1 : 2); return true; } @Override public boolean preUpdate(BeanPersistRequest request) { - - TWithPreInsert b = (TWithPreInsert) request.bean(); - System.out.println("preUpdate - title is: " + b.getTitle()); - if (b.getTitle() == null) { - b.setTitle("set on preUpdate"); + TWithPreInsertCommon bean = (TWithPreInsertCommon) request.bean(); + System.out.println("preUpdate - title is: " + bean.getTitle()); + if (bean.getTitle() == null) { + bean.setTitle("set on preUpdate"); } - // request.getEbeanServer().refresh(b); - // System.out.println("title is Mister:"+b.getTitle()); - + bean.requestCascadeState(request.isCascade() ? 11 : 12); return super.preUpdate(request); }