diff --git a/ebean-api/src/main/java/io/ebean/event/BeanFindController.java b/ebean-api/src/main/java/io/ebean/event/BeanFindController.java index c81a89e60..5190dd063 100644 --- a/ebean-api/src/main/java/io/ebean/event/BeanFindController.java +++ b/ebean-api/src/main/java/io/ebean/event/BeanFindController.java @@ -50,4 +50,17 @@ public interface BeanFindController { */ BeanCollection findMany(BeanQueryRequest request); + /** + * Allows post-processing of the find result. + */ + default BeanCollection postProcessMany(BeanQueryRequest request, BeanCollection result) { + return result; + } + + /** + * Allows post-processing of the find result. + */ + default T postProcess(BeanQueryRequest request, T result) { + return result; + }; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultOrmQueryEngine.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultOrmQueryEngine.java index adbeee6d5..86076b379 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultOrmQueryEngine.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultOrmQueryEngine.java @@ -131,6 +131,10 @@ public final class DefaultOrmQueryEngine implements OrmQueryEngine { result = queryEngine.findMany(request); } + if (finder != null) { + result = finder.postProcessMany(request, result); + } + SpiQuery query = request.query(); if (result != null && request.isBeanCachePutMany()) { @@ -171,6 +175,10 @@ public final class DefaultOrmQueryEngine implements OrmQueryEngine { result = queryEngine.find(request); } + if (finder != null) { + result = finder.postProcess(request, result); + } + if (result != null && request.isBeanCachePut()) { request.descriptor().cacheBeanPut((EntityBean) result); } diff --git a/ebean-test/src/test/java/io/ebean/event/BeanFindControllerTest.java b/ebean-test/src/test/java/io/ebean/event/BeanFindControllerTest.java index 4d8b5ee88..94be03769 100644 --- a/ebean-test/src/test/java/io/ebean/event/BeanFindControllerTest.java +++ b/ebean-test/src/test/java/io/ebean/event/BeanFindControllerTest.java @@ -10,10 +10,18 @@ import org.junit.jupiter.api.Test; import org.tests.example.ModUuidGenerator; import org.tests.model.basic.EBasic; import org.tests.model.basic.ECustomId; +import org.tests.model.controller.FindControllerMain; +import org.tests.model.controller.SoftRefA; +import org.tests.model.controller.SoftRefB; +import org.tests.model.controller.TestBeanFindController; import java.util.List; -import static org.junit.jupiter.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; public class BeanFindControllerTest extends BaseTestCase { @@ -116,4 +124,91 @@ public class BeanFindControllerTest extends BaseTestCase { b.setName("47"); return b; } + + @Test + public void testPostProcess() { + + Database db = prepareSoftRefs(); + + final FindControllerMain controllerDbA = db.find(FindControllerMain.class, 1); + assertThat(controllerDbA).isNotNull(); + assertThat(controllerDbA.getTarget()) + .isNotNull() + .isInstanceOf(SoftRefA.class) + .hasFieldOrPropertyWithValue("title", "softRefA"); + + final FindControllerMain controllerDbB = db.find(FindControllerMain.class, 2); + assertThat(controllerDbB).isNotNull(); + assertThat(controllerDbB.getTarget()) + .isNotNull() + .isInstanceOf(SoftRefB.class) + .hasFieldOrPropertyWithValue("title", "softRefB"); + + } + + @Test + public void testPostProcessFindMany() { + Database db = prepareSoftRefs(); + + final List controllers = db.find(FindControllerMain.class).orderById(true).findList(); + + assertThat(controllers).hasSize(2); + + final FindControllerMain controllerDbA = controllers.get(0); + assertThat(controllerDbA.getId()).isEqualTo(1); + assertThat(controllerDbA.getTarget()) + .isNotNull() + .isInstanceOf(SoftRefA.class) + .hasFieldOrPropertyWithValue("title", "softRefA"); + + final FindControllerMain controllerDbB = controllers.get(1); + assertThat(controllerDbB.getId()).isEqualTo(2); + assertThat(controllerDbB.getTarget()) + .isNotNull() + .isInstanceOf(SoftRefB.class) + .hasFieldOrPropertyWithValue("title", "softRefB"); + + } + + private Database prepareSoftRefs() { + DatabaseConfig config = new DatabaseConfig(); + + config.setName("h2otherfind"); + config.loadFromProperties(); + config.setDdlGenerate(true); + config.setDdlRun(true); + config.setDdlExtra(false); + + config.setRegister(false); + config.setDefaultServer(false); + config.add(new ModUuidGenerator()); + config.getClasses().add(FindControllerMain.class); + config.getClasses().add(SoftRefA.class); + config.getClasses().add(SoftRefB.class); + + config.getFindControllers().add(new TestBeanFindController()); + + Database db = DatabaseFactory.create(config); + + final SoftRefA softRefA = new SoftRefA(); + softRefA.setTitle("softRefA"); + db.save(softRefA); + + final SoftRefB softRefB = new SoftRefB(); + softRefB.setTitle("softRefB"); + db.save(softRefB); + + final FindControllerMain main1 = new FindControllerMain(); + main1.setTargetId(softRefA.getId()); + main1.setTargetTableName("soft_ref_a"); + db.save(main1); + + final FindControllerMain main2 = new FindControllerMain(); + main2.setTargetId(softRefB.getId()); + main2.setTargetTableName("soft_ref_b"); + db.save(main2); + + return db; + } + } diff --git a/ebean-test/src/test/java/org/tests/model/controller/FindControllerMain.java b/ebean-test/src/test/java/org/tests/model/controller/FindControllerMain.java new file mode 100644 index 000000000..bcc21445f --- /dev/null +++ b/ebean-test/src/test/java/org/tests/model/controller/FindControllerMain.java @@ -0,0 +1,51 @@ +package org.tests.model.controller; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Transient; + +@Entity +public class FindControllerMain { + + @Id + private Integer id; + + private Integer targetId; + + private String targetTableName; + + @Transient + private Object target; + + public Integer getId() { + return id; + } + + public void setId(final Integer id) { + this.id = id; + } + + public Integer getTargetId() { + return targetId; + } + + public void setTargetId(final Integer targetId) { + this.targetId = targetId; + } + + public String getTargetTableName() { + return targetTableName; + } + + public void setTargetTableName(final String targetTableName) { + this.targetTableName = targetTableName; + } + + public Object getTarget() { + return target; + } + + public void setTarget(final Object target) { + this.target = target; + } +} diff --git a/ebean-test/src/test/java/org/tests/model/controller/SoftRefA.java b/ebean-test/src/test/java/org/tests/model/controller/SoftRefA.java new file mode 100644 index 000000000..41a2bd10e --- /dev/null +++ b/ebean-test/src/test/java/org/tests/model/controller/SoftRefA.java @@ -0,0 +1,29 @@ +package org.tests.model.controller; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class SoftRefA { + + @Id + private Integer id; + + private String title; + + public Integer getId() { + return id; + } + + public void setId(final Integer id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(final String title) { + this.title = title; + } +} diff --git a/ebean-test/src/test/java/org/tests/model/controller/SoftRefB.java b/ebean-test/src/test/java/org/tests/model/controller/SoftRefB.java new file mode 100644 index 000000000..c7086096e --- /dev/null +++ b/ebean-test/src/test/java/org/tests/model/controller/SoftRefB.java @@ -0,0 +1,29 @@ +package org.tests.model.controller; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class SoftRefB { + + @Id + private Integer id; + + private String title; + + public Integer getId() { + return id; + } + + public void setId(final Integer id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(final String title) { + this.title = title; + } +} diff --git a/ebean-test/src/test/java/org/tests/model/controller/TestBeanFindController.java b/ebean-test/src/test/java/org/tests/model/controller/TestBeanFindController.java new file mode 100644 index 000000000..27cbae2a2 --- /dev/null +++ b/ebean-test/src/test/java/org/tests/model/controller/TestBeanFindController.java @@ -0,0 +1,89 @@ +package org.tests.model.controller; + +import io.ebean.bean.BeanCollection; +import io.ebean.event.BeanFindController; +import io.ebean.event.BeanQueryRequest; +import io.ebean.plugin.BeanType; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TestBeanFindController implements BeanFindController { + + @Override + public boolean isRegisterFor(final Class cls) { + return cls.isAssignableFrom(FindControllerMain.class); + } + + @Override + public boolean isInterceptFind(final BeanQueryRequest request) { + return false; + } + + @Override + public T find(final BeanQueryRequest request) { + return null; + } + + @Override + public boolean isInterceptFindMany(final BeanQueryRequest request) { + return false; + } + + @Override + public BeanCollection findMany(final BeanQueryRequest request) { + return null; + } + + @Override + public BeanCollection postProcessMany(final BeanQueryRequest request, final BeanCollection result) { + Map, List> elementsMap = new HashMap<>(); + Map, Map> controllerLookup = new HashMap<>(); + + for (Object entry : result.getActualEntries()) { + FindControllerMain findControllerMain = (FindControllerMain) entry; + Class beanType = beanTypeFor(findControllerMain.getTargetTableName(), request); + + if (beanType != null) { + elementsMap.computeIfAbsent(beanType, key -> new ArrayList<>()).add(findControllerMain.getTargetId()); + controllerLookup.computeIfAbsent(beanType, key -> new HashMap<>()).put(findControllerMain.getTargetId(), findControllerMain); + } + } + + elementsMap.forEach((beanType, ids) -> { + final Map idLookup = controllerLookup.get(beanType); + request.getEbeanServer().find(beanType).where() + .idIn(ids).setMapKey("id") + .findMap().forEach((id, bean) -> idLookup.get((Integer) id).setTarget(bean)); + }); + + return result; + } + + @Override + public T postProcess(final BeanQueryRequest request, final T result) { + FindControllerMain findControllerMain = (FindControllerMain) result; + + Class beanType = beanTypeFor(findControllerMain.getTargetTableName(), request); + + if (beanType != null) { + findControllerMain.setTarget(request.getEbeanServer().find(beanType, findControllerMain.getTargetId())); + } + + return result; + } + + private Class beanTypeFor(String tableName, BeanQueryRequest request) { + List> types = request.getEbeanServer() + .getPluginApi().getBeanTypes(tableName); + for (BeanType type : types) { + if (type.isInheritanceRoot()) { + return type.getBeanType(); + } + } + return null; + } + +}