From 50824f1d7f5cbe2da2bb2f0b4f12fc9171788ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20P=C3=B6hler?= Date: Fri, 22 Oct 2021 15:04:46 +0200 Subject: [PATCH] NEW: CustomDeployParser --- .../java/io/ebean/config/DatabaseConfig.java | 13 ++++ .../io/ebean/plugin/CustomDeployParser.java | 15 +++++ .../plugin/DeployBeanDescriptorMeta.java | 37 +++++++++++ .../plugin/DeployBeanPropertyAssocMeta.java | 23 +++++++ .../ebean/plugin/DeployBeanPropertyMeta.java | 38 ++++++++++++ .../server/core/DefaultContainer.java | 1 + .../server/core/bootup/BootupClasses.java | 16 +++++ .../server/deploy/BeanDescriptorManager.java | 3 + .../deploy/CustomDeployParserManager.java | 23 +++++++ .../deploy/meta/DeployBeanDescriptor.java | 19 +++++- .../deploy/meta/DeployBeanProperty.java | 24 +++++++- .../deploy/meta/DeployBeanPropertyAssoc.java | 9 ++- .../tevent/CustomFormulaAnnotationParser.java | 61 +++++++++++++++++++ .../org/tests/model/tevent/TEventOne.java | 9 +++ .../aggregation/TestAggregationCount.java | 2 +- 15 files changed, 287 insertions(+), 6 deletions(-) create mode 100644 ebean-api/src/main/java/io/ebean/plugin/CustomDeployParser.java create mode 100644 ebean-api/src/main/java/io/ebean/plugin/DeployBeanDescriptorMeta.java create mode 100644 ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyAssocMeta.java create mode 100644 ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyMeta.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/deploy/CustomDeployParserManager.java create mode 100644 ebean-test/src/test/java/org/tests/model/tevent/CustomFormulaAnnotationParser.java diff --git a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java index 3e0085a12..36a4212d5 100644 --- a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java +++ b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java @@ -16,6 +16,7 @@ import io.ebean.event.changelog.ChangeLogPrepare; import io.ebean.event.changelog.ChangeLogRegister; import io.ebean.event.readaudit.ReadAuditLogger; import io.ebean.event.readaudit.ReadAuditPrepare; +import io.ebean.plugin.CustomDeployParser; import io.ebean.util.StringHelper; import javax.persistence.EnumType; @@ -389,6 +390,7 @@ public class DatabaseConfig { private List queryAdapters = new ArrayList<>(); private final List bulkTableEventListeners = new ArrayList<>(); private final List configStartupListeners = new ArrayList<>(); + private final List customDeployParsers = new ArrayList<>(); /** * By default inserts are included in the change log. @@ -2674,6 +2676,17 @@ public class DatabaseConfig { return configStartupListeners; } + /** + * Add a CustomDeployParser. + */ + public void addCustomDeployParser(CustomDeployParser customDeployParser) { + customDeployParsers.add(customDeployParser); + } + + public List getCustomDeployParsers() { + return customDeployParsers; + } + /** * Register all the BeanPersistListener instances. *

diff --git a/ebean-api/src/main/java/io/ebean/plugin/CustomDeployParser.java b/ebean-api/src/main/java/io/ebean/plugin/CustomDeployParser.java new file mode 100644 index 000000000..4c854f2f1 --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/plugin/CustomDeployParser.java @@ -0,0 +1,15 @@ +package io.ebean.plugin; + +import io.ebean.config.dbplatform.DatabasePlatform; + +/** + * Fired after all beans are parsed. You may implement own parsers to handle custom annotations. + * (See test case for example) + * + * @author Roland Praml, FOCONIS AG + */ +@FunctionalInterface +public interface CustomDeployParser { + + void parse(DeployBeanDescriptorMeta descriptor, DatabasePlatform databasePlatform); +} diff --git a/ebean-api/src/main/java/io/ebean/plugin/DeployBeanDescriptorMeta.java b/ebean-api/src/main/java/io/ebean/plugin/DeployBeanDescriptorMeta.java new file mode 100644 index 000000000..a5afba570 --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/plugin/DeployBeanDescriptorMeta.java @@ -0,0 +1,37 @@ +package io.ebean.plugin; + +import java.util.Collection; +import java.util.List; + +/** + * General deployment information. This is used in {@link CustomDeployParser}. + * + * @author Roland Praml, FOCONIS AG + */ +public interface DeployBeanDescriptorMeta { + + /** + * Return a collection of all BeanProperty deployment information. + */ + public Collection propertiesAll(); + + /** + * Get a BeanProperty by its name. + */ + public DeployBeanPropertyMeta getBeanProperty(String secondaryBeanName); + + /** + * Return the DeployBeanDescriptorMeta for the given bean class. + */ + public DeployBeanDescriptorMeta getDeployBeanDescriptorMeta(Class propertyType); + + /** + * Returns the discriminator column, if any. + * @return + */ + public String getDiscriminatorColumn(); + + public String getBaseTable(); + + DeployBeanPropertyMeta idProperty(); +} diff --git a/ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyAssocMeta.java b/ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyAssocMeta.java new file mode 100644 index 000000000..d7d1d637a --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyAssocMeta.java @@ -0,0 +1,23 @@ +package io.ebean.plugin; + +public interface DeployBeanPropertyAssocMeta extends DeployBeanPropertyMeta { + + /** + * Return the mappedBy deployment attribute. + *

+ * This is the name of the property in the 'detail' bean that maps back to + * this 'master' bean. + *

+ */ + String getMappedBy(); + + /** + * Return the base table for this association. + *

+ * This has the table name which is used to determine the relationship for + * this association. + *

+ */ + String getBaseTable(); + +} diff --git a/ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyMeta.java b/ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyMeta.java new file mode 100644 index 000000000..b581181d2 --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyMeta.java @@ -0,0 +1,38 @@ +package io.ebean.plugin; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public interface DeployBeanPropertyMeta { + + /** + * Return the name of the property. + */ + String getName(); + + /** + * The database column name this is mapped to. + */ + String getDbColumn(); + + /** + * Return the bean Field associated with this property. + */ + Field getField(); + + /** + * The property is based on a formula. + */ + void setSqlFormula(String sqlSelect, String sqlJoin); + + /** + * Return the bean type. + */ + Class getOwningType(); + + /** + * Return the property type. + */ + Class getPropertyType(); + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java index 4009c9d49..afff3e2a0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java @@ -168,6 +168,7 @@ public final class DefaultContainer implements SpiContainer { bootup.addPersistListeners(config.getPersistListeners()); bootup.addQueryAdapters(config.getQueryAdapters()); bootup.addServerConfigStartup(config.getServerConfigStartupListeners()); + bootup.addCustomDeployParser(config.getCustomDeployParsers()); bootup.addChangeLogInstances(config); bootup.runServerConfigStartup(config); return bootup; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java index 491739c13..ce34e56d2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/bootup/BootupClasses.java @@ -11,6 +11,7 @@ import io.ebean.event.changelog.ChangeLogPrepare; import io.ebean.event.changelog.ChangeLogRegister; import io.ebean.event.readaudit.ReadAuditLogger; import io.ebean.event.readaudit.ReadAuditPrepare; +import io.ebean.plugin.CustomDeployParser; import io.ebean.util.AnnotationUtil; import io.ebeaninternal.api.CoreLog; import org.slf4j.Logger; @@ -51,6 +52,7 @@ public class BootupClasses implements Predicate> { private final List> beanPersistListenerCandidates = new ArrayList<>(); private final List> beanQueryAdapterCandidates = new ArrayList<>(); private final List> serverConfigStartupCandidates = new ArrayList<>(); + private final List> customDeployParserCandidates = new ArrayList<>(); private final List idGeneratorInstances = new ArrayList<>(); private final List beanPersistControllerInstances = new ArrayList<>(); @@ -60,6 +62,7 @@ public class BootupClasses implements Predicate> { private final List beanPersistListenerInstances = new ArrayList<>(); private final List beanQueryAdapterInstances = new ArrayList<>(); private final List serverConfigStartupInstances = new ArrayList<>(); + private final List customDeployParserInstances = new ArrayList<>(); // single objects private Class changeLogPrepareClass; @@ -169,6 +172,10 @@ public class BootupClasses implements Predicate> { add(startupInstances, serverConfigStartupInstances, serverConfigStartupCandidates); } + public void addCustomDeployParser(List customDeployParser) { + add(customDeployParser, customDeployParserInstances, customDeployParserCandidates); + } + public void addChangeLogInstances(DatabaseConfig config) { readAuditPrepare = config.getReadAuditPrepare(); readAuditLogger = config.getReadAuditLogger(); @@ -285,6 +292,10 @@ public class BootupClasses implements Predicate> { return createAdd(beanQueryAdapterInstances, beanQueryAdapterCandidates); } + public List getCustomDeployParsers() { + return createAdd(customDeployParserInstances, customDeployParserCandidates); + } + /** * Return the list of Embeddable classes. */ @@ -404,6 +415,11 @@ public class BootupClasses implements Predicate> { interesting = true; } + if (CustomDeployParser.class.isAssignableFrom(cls)) { + customDeployParserCandidates.add((Class) cls); + interesting = true; + } + // single instances, last assigned wins if (ChangeLogListener.class.isAssignableFrom(cls)) { changeLogListenerClass = (Class) cls; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java index 3c949b6e1..6a5c66888 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -74,6 +74,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy private final BeanFinderManager beanFinderManager; private final PersistListenerManager persistListenerManager; private final BeanQueryAdapterManager beanQueryAdapterManager; + private final CustomDeployParserManager customDeployParserManager; private final NamingConvention namingConvention; private final DeployCreateProperties createProperties; private final BeanManagerFactory beanManagerFactory; @@ -154,6 +155,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy this.persistListenerManager = new PersistListenerManager(bootupClasses); this.beanQueryAdapterManager = new BeanQueryAdapterManager(bootupClasses); this.beanFinderManager = new BeanFinderManager(bootupClasses); + this.customDeployParserManager = new CustomDeployParserManager(bootupClasses); this.transientProperties = new TransientProperties(); this.changeLogPrepare = config.changeLogPrepare(bootupClasses.getChangeLogPrepare()); this.changeLogListener = config.changeLogListener(bootupClasses.getChangeLogListener()); @@ -301,6 +303,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy readEntityBeanTable(); readEntityDeploymentAssociations(); readInheritedIdGenerators(); + deployInfoMap.values().forEach(customDeployParserManager::parse); // creates the BeanDescriptors readEntityRelationships(); List> list = new ArrayList<>(descMap.values()); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/CustomDeployParserManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/CustomDeployParserManager.java new file mode 100644 index 000000000..77fd807c4 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/CustomDeployParserManager.java @@ -0,0 +1,23 @@ +package io.ebeaninternal.server.deploy; + +import java.util.List; + +import io.ebean.plugin.CustomDeployParser; +import io.ebeaninternal.server.core.bootup.BootupClasses; +import io.ebeaninternal.server.deploy.parse.DeployBeanInfo; + +public class CustomDeployParserManager { + + private final List parsers; + + public CustomDeployParserManager(BootupClasses bootupClasses) { + parsers = bootupClasses.getCustomDeployParsers(); + } + + public void parse(DeployBeanInfo value) { + for (CustomDeployParser parser : parsers) { + parser.parse(value.getDescriptor(), value.getUtil().getDbPlatform()); + } + } + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java index f6282db2d..2b5783aaa 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java @@ -16,6 +16,7 @@ import io.ebean.event.BeanPostConstructListener; import io.ebean.event.BeanPostLoad; import io.ebean.event.BeanQueryAdapter; import io.ebean.event.changelog.ChangeLogFilter; +import io.ebean.plugin.DeployBeanDescriptorMeta; import io.ebean.text.PathProperties; import io.ebean.util.AnnotationUtil; import io.ebean.util.SplitName; @@ -55,7 +56,7 @@ import java.util.Set; /** * Describes Beans including their deployment information. */ -public class DeployBeanDescriptor { +public class DeployBeanDescriptor implements DeployBeanDescriptorMeta { private static final Map EMPTY_NAMED_QUERY = new HashMap<>(); @@ -199,7 +200,7 @@ public class DeployBeanDescriptor { /** * Return the DeployBeanInfo for the given bean class. */ - DeployBeanInfo getDeploy(Class cls) { + public DeployBeanInfo getDeploy(Class cls) { return manager.deploy(cls); } @@ -619,6 +620,7 @@ public class DeployBeanDescriptor { * Return the base table. Only properties mapped to the base table are by * default persisted. */ + @Override public String getBaseTable() { return baseTable; } @@ -696,6 +698,7 @@ public class DeployBeanDescriptor { /** * Get a BeanProperty by its name. */ + @Override public DeployBeanProperty getBeanProperty(String propName) { return propMap.get(propName); } @@ -815,6 +818,7 @@ public class DeployBeanDescriptor { /** * Return a collection of all BeanProperty deployment information. */ + @Override public Collection propertiesAll() { return propMap.values(); } @@ -884,6 +888,7 @@ public class DeployBeanDescriptor { /** * Return the BeanProperty that is the Id. */ + @Override public DeployBeanProperty idProperty() { if (idProperty != null) { return idProperty; @@ -1180,4 +1185,14 @@ public class DeployBeanDescriptor { } return base; } + + @Override + public String getDiscriminatorColumn() { + return inheritInfo == null ? null : inheritInfo.getDiscriminatorColumn(); + } + + @Override + public DeployBeanDescriptorMeta getDeployBeanDescriptorMeta(Class propertyType) { + return getDeploy(propertyType).getDescriptor(); + } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java index 33d1bf266..36c9710ac 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java @@ -1,11 +1,25 @@ package io.ebeaninternal.server.deploy.meta; -import io.ebean.annotation.*; +import io.ebean.annotation.CreatedTimestamp; +import io.ebean.annotation.DocCode; +import io.ebean.annotation.DocProperty; +import io.ebean.annotation.DocSortable; +import io.ebean.annotation.Formula; +import io.ebean.annotation.MutationDetection; +import io.ebean.annotation.Platform; +import io.ebean.annotation.SoftDelete; +import io.ebean.annotation.UpdatedTimestamp; +import io.ebean.annotation.WhenCreated; +import io.ebean.annotation.WhenModified; +import io.ebean.annotation.Where; +import io.ebean.annotation.WhoCreated; +import io.ebean.annotation.WhoModified; import io.ebean.config.ScalarTypeConverter; import io.ebean.config.dbplatform.DbDefaultValue; import io.ebean.config.dbplatform.DbEncrypt; import io.ebean.config.dbplatform.DbEncryptFunction; import io.ebean.core.type.ScalarType; +import io.ebean.plugin.DeployBeanPropertyMeta; import io.ebean.util.AnnotationUtil; import io.ebeaninternal.server.core.InternString; import io.ebeaninternal.server.deploy.BeanProperty; @@ -35,7 +49,7 @@ import java.util.Set; * Description of a property of a bean. Includes its deployment information such * as database column mapping information. */ -public class DeployBeanProperty { +public class DeployBeanProperty implements DeployBeanPropertyMeta { private static final int ID_ORDER = 1000000; private static final int UNIDIRECTIONAL_ORDER = 100000; @@ -406,6 +420,7 @@ public class DeployBeanProperty { this.owningType = owningType; } + @Override public Class getOwningType() { return owningType; } @@ -434,6 +449,7 @@ public class DeployBeanProperty { /** * Return the name of the property. */ + @Override public String getName() { return name; } @@ -448,6 +464,7 @@ public class DeployBeanProperty { /** * Return the bean Field associated with this property. */ + @Override public Field getField() { return field; } @@ -551,6 +568,7 @@ public class DeployBeanProperty { /** * The property is based on a formula. */ + @Override public void setSqlFormula(String formulaSelect, String formulaJoin) { this.sqlFormulaSelect = formulaSelect; this.sqlFormulaJoin = formulaJoin.isEmpty() ? null : formulaJoin; @@ -654,6 +672,7 @@ public class DeployBeanProperty { /** * The database column name this is mapped to. */ + @Override public String getDbColumn() { if (sqlFormulaSelect != null) { return sqlFormulaSelect; @@ -847,6 +866,7 @@ public class DeployBeanProperty { /** * Return the property type. */ + @Override public Class getPropertyType() { return propertyType; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java index 3461be3bd..123972cb0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.deploy.meta; +import io.ebean.plugin.DeployBeanPropertyAssocMeta; import io.ebeaninternal.server.deploy.BeanCascadeInfo; import io.ebeaninternal.server.deploy.BeanTable; import io.ebeaninternal.server.deploy.PropertyForeignKey; @@ -7,7 +8,7 @@ import io.ebeaninternal.server.deploy.PropertyForeignKey; /** * Abstract base for properties mapped to an associated bean, list, set or map. */ -public abstract class DeployBeanPropertyAssoc extends DeployBeanProperty { +public abstract class DeployBeanPropertyAssoc extends DeployBeanProperty implements DeployBeanPropertyAssocMeta { /** * The type of the joined bean. @@ -129,6 +130,7 @@ public abstract class DeployBeanPropertyAssoc extends DeployBeanProperty { * this 'master' bean. *

*/ + @Override public String getMappedBy() { return mappedBy; } @@ -173,4 +175,9 @@ public abstract class DeployBeanPropertyAssoc extends DeployBeanProperty { public void setTargetType(Class targetType) { this.targetType = (Class)targetType; } + + @Override + public String getBaseTable() { + return getBeanTable().getBaseTable(); + } } diff --git a/ebean-test/src/test/java/org/tests/model/tevent/CustomFormulaAnnotationParser.java b/ebean-test/src/test/java/org/tests/model/tevent/CustomFormulaAnnotationParser.java new file mode 100644 index 000000000..3f392aed0 --- /dev/null +++ b/ebean-test/src/test/java/org/tests/model/tevent/CustomFormulaAnnotationParser.java @@ -0,0 +1,61 @@ +package org.tests.model.tevent; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import io.ebean.annotation.Formula; +import io.ebean.config.dbplatform.DatabasePlatform; +import io.ebean.plugin.CustomDeployParser; +import io.ebean.plugin.DeployBeanDescriptorMeta; +import io.ebean.plugin.DeployBeanPropertyMeta; +import io.ebean.util.AnnotationUtil; +import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; + +/** + * Custom Annotation parser which parses @Count annotation + * + * @author Roland Praml, FOCONIS AG + */ +public class CustomFormulaAnnotationParser implements CustomDeployParser { + + private int counter; + + + @Target(FIELD) + @Retention(RUNTIME) + @Formula(select="TODO", join = "TODO") // meta-formula + public @interface Count { + String value(); + } + + + + @Override + public void parse(final DeployBeanDescriptorMeta descriptor, final DatabasePlatform databasePlatform) { + for (DeployBeanPropertyMeta prop : descriptor.propertiesAll()) { + readField(descriptor, prop); + } + } + + private void readField(DeployBeanDescriptorMeta descriptor, DeployBeanPropertyMeta prop) { + Count countAnnot = AnnotationUtil.get(prop.getField(), Count.class); + if (countAnnot != null) { + // @Count found, so build the (complex) count formula + DeployBeanPropertyAssocMany countProp = (DeployBeanPropertyAssocMany) descriptor.getBeanProperty(countAnnot.value()); + counter++; + String tmpTable = "f"+counter; + String sqlSelect = "coalesce(" + tmpTable + ".child_count, 0)"; + String parentId = countProp.getMappedBy() + "_id"; + String tableName = countProp.getBeanTable().getBaseTable(); + String sqlJoin = "left join (select " + parentId +", count(*) as child_count from " + tableName + " GROUP BY " + parentId + " )" + + " " + tmpTable + " on " + tmpTable + "." +parentId + " = ${ta}." + descriptor.idProperty().getDbColumn(); + prop.setSqlFormula(sqlSelect, sqlJoin); +// prop.setSqlFormula("f1.child_count", +// "join (select parent_id, count(*) as child_count from child_entity GROUP BY parent_id) f1 on f1.parent_id = ${ta}.id"); + } + } + +} diff --git a/ebean-test/src/test/java/org/tests/model/tevent/TEventOne.java b/ebean-test/src/test/java/org/tests/model/tevent/TEventOne.java index 896d5a147..3107e56a1 100644 --- a/ebean-test/src/test/java/org/tests/model/tevent/TEventOne.java +++ b/ebean-test/src/test/java/org/tests/model/tevent/TEventOne.java @@ -41,6 +41,11 @@ public class TEventOne { @OneToMany(mappedBy = "event", cascade = CascadeType.ALL) List logs; + @CustomFormulaAnnotationParser.Count("logs") + //@Formula(select = "f1.child_count", + //join = "left join (select event_id, count(*) as child_count from tevent_many GROUP BY event_id ) as f1 on f1.event_id = ${ta}.id") + Long customFormula; + public TEventOne(String name, Status status) { this.name = name; this.status = status; @@ -63,6 +68,10 @@ public class TEventOne { return count; } + public Long getCustomFormula() { + return customFormula; + } + public Double getTotalUnits() { return totalUnits; } diff --git a/ebean-test/src/test/java/org/tests/query/aggregation/TestAggregationCount.java b/ebean-test/src/test/java/org/tests/query/aggregation/TestAggregationCount.java index f60686f3d..5efd69bf8 100644 --- a/ebean-test/src/test/java/org/tests/query/aggregation/TestAggregationCount.java +++ b/ebean-test/src/test/java/org/tests/query/aggregation/TestAggregationCount.java @@ -48,7 +48,7 @@ public class TestAggregationCount extends BaseTestCase { List list = query.findList(); String sql = sqlOf(query, 5); - assertThat(sql).contains("select t0.id, t0.name, t0.status, t0.version, t0.event_id from tevent_one t0"); + assertThat(sql).contains("select t0.id, t0.name, t0.status, coalesce(f1.child_count, 0), t0.version, t0.event_id from tevent_one t0"); for (TEventOne eventOne : list) { // lazy loading on Aggregation properties