From 4158ac466580dfab672b800ee634d1ecd7784b5b Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Tue, 25 Jan 2022 13:44:23 +1300 Subject: [PATCH 01/13] Refactor internals split result load functionality from SqlTreeNode into SqlTreeLoad --- .../io/ebeaninternal/server/query/CQuery.java | 2 +- .../server/query/CQueryBuilder.java | 8 +- .../server/query/CQueryPlan.java | 8 +- .../server/query/CQueryPlanRawSql.java | 2 +- .../ebeaninternal/server/query/SqlTree.java | 14 +- .../server/query/SqlTreeLoad.java | 28 ++ .../server/query/SqlTreeLoadBean.java | 373 ++++++++++++++++++ .../server/query/SqlTreeLoadManyRoot.java | 30 ++ .../server/query/SqlTreeLoadRoot.java | 43 ++ .../server/query/SqlTreeNode.java | 24 +- .../server/query/SqlTreeNodeBean.java | 365 ++--------------- .../server/query/SqlTreeNodeExtraJoin.java | 18 +- .../query/SqlTreeNodeFormulaWhereJoin.java | 14 +- .../server/query/SqlTreeNodeManyRoot.java | 22 +- .../query/SqlTreeNodeManyWhereJoin.java | 17 +- .../server/query/SqlTreeNodeRoot.java | 30 +- .../server/query/SqlTreePlan.java | 41 ++ 17 files changed, 588 insertions(+), 451 deletions(-) create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoad.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadBean.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadManyRoot.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadRoot.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreePlan.java diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java index 45a5b5a11..1870c1b6f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java @@ -193,7 +193,7 @@ public final class CQuery implements DbReadContext, CancelableQuery, SpiProfi // set the generated sql back to the query // so its available to the user... query.setGeneratedSql(queryPlan.getSql()); - SqlTree sqlTree = queryPlan.getSqlTree(); + SqlTreePlan sqlTree = queryPlan.getSqlTree(); this.rootNode = sqlTree.getRootNode(); this.manyProperty = sqlTree.getManyProperty(); this.sql = queryPlan.getSql(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java index 278603f5b..3367fbfe4 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java @@ -110,7 +110,7 @@ final class CQueryBuilder { sql = buildUpdateSql(request, rootTableAlias, predicates, sqlTree); } // cache the query plan - queryPlan = new CQueryPlan(request, sql, sqlTree, predicates.getLogWhereSql()); + queryPlan = new CQueryPlan(request, sql, sqlTree.plan(), predicates.getLogWhereSql()); request.putQueryPlan(queryPlan); return new CQueryUpdate(request, predicates, queryPlan); } @@ -197,7 +197,7 @@ final class CQueryBuilder { SqlTree sqlTree = createSqlTree(request, predicates); SqlLimitResponse s = buildSql(null, request, predicates, sqlTree); - queryPlan = new CQueryPlan(request, s.getSql(), sqlTree, predicates.getLogWhereSql()); + queryPlan = new CQueryPlan(request, s.getSql(), sqlTree.plan(), predicates.getLogWhereSql()); request.putQueryPlan(queryPlan); return new CQueryFetchSingleAttribute(request, predicates, queryPlan, query.isCountDistinct()); } @@ -288,7 +288,7 @@ final class CQueryBuilder { } } // cache the query plan - queryPlan = new CQueryPlan(request, sql, sqlTree, predicates.getLogWhereSql()); + queryPlan = new CQueryPlan(request, sql, sqlTree.plan(), predicates.getLogWhereSql()); request.putQueryPlan(queryPlan); return new CQueryRowCount(queryPlan, request, predicates); } @@ -343,7 +343,7 @@ final class CQueryBuilder { if (rawSql) { queryPlan = new CQueryPlanRawSql(request, res, sqlTree, predicates.getLogWhereSql()); } else { - queryPlan = new CQueryPlan(request, res, sqlTree, false, predicates.getLogWhereSql()); + queryPlan = new CQueryPlan(request, res, sqlTree.plan(), false, predicates.getLogWhereSql()); } BeanDescriptor desc = request.descriptor(); if (desc.isReadAuditing()) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java index bf84070f2..d07276920 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java @@ -55,7 +55,7 @@ public class CQueryPlan implements SpiQueryPlan { private final String sql; private final String hash; private final String logWhereSql; - private final SqlTree sqlTree; + private final SqlTreePlan sqlTree; /** * Encrypted properties required additional binding. @@ -76,7 +76,7 @@ public class CQueryPlan implements SpiQueryPlan { /** * Create a query plan based on a OrmQueryRequest. */ - CQueryPlan(OrmQueryRequest request, SqlLimitResponse sqlRes, SqlTree sqlTree, boolean rawSql, String logWhereSql) { + CQueryPlan(OrmQueryRequest request, SqlLimitResponse sqlRes, SqlTreePlan sqlTree, boolean rawSql, String logWhereSql) { this.server = request.server(); this.dataTimeZone = server.dataTimeZone(); this.beanType = request.descriptor().type(); @@ -101,7 +101,7 @@ public class CQueryPlan implements SpiQueryPlan { /** * Create a query plan for a raw sql query. */ - CQueryPlan(OrmQueryRequest request, String sql, SqlTree sqlTree, String logWhereSql) { + CQueryPlan(OrmQueryRequest request, String sql, SqlTreePlan sqlTree, String logWhereSql) { this.server = request.server(); this.dataTimeZone = server.dataTimeZone(); this.beanType = request.descriptor().type(); @@ -249,7 +249,7 @@ public class CQueryPlan implements SpiQueryPlan { return rawSql ? planKey.getPartialKey() + "_" + hash : planKey.getPartialKey(); } - final SqlTree getSqlTree() { + final SqlTreePlan getSqlTree() { return sqlTree; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanRawSql.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanRawSql.java index f35702ee1..935f730ec 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanRawSql.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanRawSql.java @@ -17,7 +17,7 @@ final class CQueryPlanRawSql extends CQueryPlan { private final int[] rsetIndexPositions; CQueryPlanRawSql(OrmQueryRequest request, SqlLimitResponse sqlRes, SqlTree sqlTree, String logWhereSql) { - super(request, sqlRes, sqlTree, true, logWhereSql); + super(request, sqlRes, sqlTree.plan(), true, logWhereSql); this.rsetIndexPositions = createIndexPositions(request, sqlTree); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java index 27e35c365..5b11be657 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java @@ -56,6 +56,10 @@ final class SqlTree { this.noJoins = !includeJoins; } + SqlTreePlan plan() { + return new SqlTreePlan(rootNode.createLoad(), manyProperty, encryptedProps, dependentTables()); + } + /** * Return true if the query mandates SQL Distinct due to ToMany inclusion. */ @@ -123,10 +127,6 @@ final class SqlTree { return inheritanceWhereSql; } - SqlTreeRoot getRootNode() { - return (SqlTreeRoot)rootNode; - } - /** * Return the property that is associated with the many. There can only be one * per SqlSelect. This can be null. @@ -135,10 +135,6 @@ final class SqlTree { return manyProperty; } - STreeProperty[] getEncryptedProps() { - return encryptedProps; - } - /** * Return true if the query has a many join. */ @@ -153,7 +149,7 @@ final class SqlTree { /** * Return the tables that are joined in this query. */ - Set dependentTables() { + private Set dependentTables() { Set tables = new LinkedHashSet<>(); rootNode.dependentTables(tables); return tables; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoad.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoad.java new file mode 100644 index 000000000..c5ac129b1 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoad.java @@ -0,0 +1,28 @@ +package io.ebeaninternal.server.query; + +import io.ebean.bean.EntityBean; +import io.ebean.core.type.ScalarDataReader; +import io.ebeaninternal.server.deploy.DbReadContext; + +import java.sql.SQLException; + +/** + * Tree node that loads an entity bean type. + */ +interface SqlTreeLoad { + + /** + * Load the appropriate information from the SqlSelectReader. + *

+ * At a high level this actually controls the reading of the data from the + * jdbc resultSet and putting it into the bean etc. + *

+ */ + EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean contextBean) throws SQLException; + + /** + * Return the reader for the single attribute query. + */ + ScalarDataReader getSingleAttributeReader(); + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadBean.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadBean.java new file mode 100644 index 000000000..516a00537 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadBean.java @@ -0,0 +1,373 @@ +package io.ebeaninternal.server.query; + +import io.ebean.bean.BeanCollection; +import io.ebean.bean.EntityBean; +import io.ebean.bean.EntityBeanIntercept; +import io.ebean.bean.PersistenceContext; +import io.ebean.core.type.ScalarDataReader; +import io.ebeaninternal.api.SpiQuery; +import io.ebeaninternal.api.SpiQuery.Mode; +import io.ebeaninternal.server.deploy.DbReadContext; +import io.ebeaninternal.server.deploy.InheritInfo; +import io.ebeaninternal.server.deploy.id.IdBinder; + +import java.sql.SQLException; +import java.util.Map; + +/** + * Normal bean included in the query. + */ +class SqlTreeLoadBean implements SqlTreeLoad { + + final STreeType desc; + final IdBinder idBinder; + final SqlTreeLoad[] children; + private final boolean partialObject; + private final STreeProperty[] properties; + private final STreePropertyAssoc nodeBeanProp; + final boolean readId; + private final boolean readIdNormal; + private final boolean disableLazyLoad; + private final InheritInfo inheritInfo; + final String prefix; + private final Map pathMap; + final STreePropertyAssocMany lazyLoadParent; + private final SpiQuery.TemporalMode temporalMode; + private final boolean temporalVersions; + final IdBinder lazyLoadParentIdBinder; + + SqlTreeLoadBean(SqlTreeNodeBean node) { + this.lazyLoadParent = node.lazyLoadParent; + this.lazyLoadParentIdBinder = node.lazyLoadParentIdBinder; + this.prefix = node.prefix; + this.desc = node.desc; + this.inheritInfo = desc.inheritInfo(); + this.idBinder = desc.idBinder(); + this.temporalMode = node.temporalMode; + this.temporalVersions = node.temporalVersions; + this.nodeBeanProp = node.nodeBeanProp; + this.readId = node.readId;//!aggregationRoot && withId && desc.hasId(); + this.readIdNormal = readId && !temporalVersions; + this.disableLazyLoad = node.disableLazyLoad;// disableLazyLoad || !readIdNormal || desc.isRawSqlBased(); + this.partialObject = node.partialObject;//props.isPartialObject(); + this.properties = node.properties;//props.getProps(); + this.pathMap = node.pathMap;//createPathMap(prefix, desc); + this.children = node.createLoadChildren(); + } + + boolean isRoot() { + return false; + } + + @Override + public final ScalarDataReader getSingleAttributeReader() { + if (properties == null || properties.length == 0) { + // if we have no property ask first children (in a distinct select with join) + if (children.length == 0) { + // expected to be a findIds query + return desc.idBinder().getBeanProperty(); + } + return children[0].getSingleAttributeReader(); + } + if (properties[0] instanceof STreePropertyAssocOne) { + STreePropertyAssocOne assocOne = (STreePropertyAssocOne)properties[0]; + if (assocOne.isAssocId()) { + return assocOne.idReader(); + } + } + return properties[0]; + } + + /** + * Load that takes into account inheritance. + */ + private final class LoadInherit extends Load { + + private LoadInherit(DbReadContext ctx, EntityBean parentBean) { + super(ctx, parentBean); + } + + @Override + void initBeanType() throws SQLException { + InheritInfo localInfo = readId ? inheritInfo.readType(ctx) : desc.inheritInfo(); + if (localInfo == null) { + // the bean must be null + localIdBinder = idBinder; + localDesc = desc; + } else { + localBean = localInfo.createEntityBean(); + localType = localInfo.getType(); + localIdBinder = localInfo.getIdBinder(); + localDesc = localInfo.desc(); + } + } + + @Override + void loadProperties() { + // take account of inheritance + for (STreeProperty property : properties) { + localDesc.inheritanceLoad(sqlBeanLoad, property, ctx); + } + } + } + + /** + * Load a bean instance. + */ + class Load { + + final DbReadContext ctx; + final EntityBean parentBean; + + Object lazyLoadParentId; + Class localType; + STreeType localDesc; + IdBinder localIdBinder; + EntityBean localBean; + + Mode queryMode; + PersistenceContext persistenceContext; + Object id; + EntityBean contextBean; + SqlBeanLoad sqlBeanLoad; + boolean lazyLoadMany; + + private Load(DbReadContext ctx, EntityBean parentBean) { + this.ctx = ctx; + this.parentBean = parentBean; + } + + private void initLazyParent() throws SQLException { + if (lazyLoadParentIdBinder != null) { + lazyLoadParentId = lazyLoadParentIdBinder.read(ctx); + } + } + + void initBeanType() throws SQLException { + localDesc = desc; + localBean = desc.createEntityBean(); + localIdBinder = idBinder; + } + + private void initPersistenceContext() { + queryMode = ctx.getQueryMode(); + persistenceContext = (!readIdNormal) ? null : ctx.getPersistenceContext(); + } + + private void readId() throws SQLException { + if (readId) { + id = localIdBinder.readSet(ctx, localBean); + if (id == null) { + readIdNullBean(); + } else if (!temporalVersions) { + readIdBean(); + } + } + } + + private void readIdBean() { + // check the PersistenceContext to see if the bean already exists + contextBean = (EntityBean) localDesc.contextPutIfAbsent(persistenceContext, id, localBean); + if (contextBean == null) { + // bean just added to the persistenceContext + contextBean = localBean; + } else { + // bean already exists in persistenceContext + + if (queryMode.isLoadContextBean()) { + // if explicitly set loadContextBean to true, then reload + localBean = contextBean; + } else if (!contextBean._ebean_getIntercept().isFullyLoadedBean()) { + // reload if contextBean is partial object + localBean = contextBean; + // and switch to lazyLoad query mode in order not to overwrite + // existing properties in SqlBeanLoad::load + queryMode = Mode.LAZYLOAD_BEAN; + } else { + // ignore the DB data... + localBean = null; + } + } + } + + private void readIdNullBean() { + // bean must be null... + localBean = null; + // ... but there may exist as reference bean in parent which has to be marked as deleted. + if (parentBean != null && nodeBeanProp instanceof STreePropertyAssocOne) { + contextBean = ((STreePropertyAssocOne)nodeBeanProp).getValueAsEntityBean(parentBean); + if (contextBean != null) { + desc.markAsDeleted(contextBean); + } + } + } + + private void initSqlLoadBean() { + ctx.setCurrentPrefix(prefix, pathMap); + ctx.propagateState(localBean); + sqlBeanLoad = new SqlBeanLoad(ctx, localType, localBean, queryMode); + } + + void loadProperties() { + for (STreeProperty property : properties) { + property.load(sqlBeanLoad); + } + } + + private void loadChildren() throws SQLException { + if (localBean == null && queryMode == Mode.LAZYLOAD_MANY) { + // batch lazy load many into existing contextBean + localBean = contextBean; + lazyLoadMany = true; + } + for (SqlTreeLoad child : children) { + child.load(ctx, localBean, contextBean); + } + } + + private boolean isLazyLoadManyRoot() { + return queryMode == Mode.LAZYLOAD_MANY && isRoot(); + } + + private EntityBean getContextBean() { + return contextBean; + } + + private void postLoad() { + if (!lazyLoadMany && localBean != null) { + ctx.setCurrentPrefix(prefix, pathMap); + if (readIdNormal) { + createListProxies(); + } + if (temporalMode == SpiQuery.TemporalMode.DRAFT) { + localDesc.setDraft(localBean); + } + localDesc.postLoad(localBean); + + EntityBeanIntercept ebi = localBean._ebean_getIntercept(); + ebi.setPersistenceContext(persistenceContext); + if (Mode.LAZYLOAD_BEAN == queryMode) { + // Lazy Load does not reset the dirty state + ebi.setLoadedLazy(); + } else if (readId) { + // normal bean loading + ebi.setLoaded(); + } + + if (disableLazyLoad) { + // bean does not have an Id or is SqlSelect based + ebi.setDisableLazyLoad(true); + } else if (partialObject) { + if (readId) { + // register for lazy loading + ctx.register(null, ebi); + } + } else { + ebi.setFullyLoadedBean(true); + } + + if (ctx.isAutoTuneProfiling() && !disableLazyLoad) { + // collect autoTune profiling for this bean... + ctx.profileBean(ebi, prefix); + } + } + } + + /** + * Create lazy loading proxies for the Many's except for the one that is + * included in the actual query. + */ + private void createListProxies() { + STreePropertyAssocMany fetchedMany = ctx.getManyProperty(); + boolean forceNewReference = queryMode == Mode.REFRESH_BEAN; + for (STreePropertyAssocMany many : localDesc.propsMany()) { + if (many != fetchedMany) { + // create a proxy for the many (deferred fetching) + BeanCollection ref = many.createReference(localBean, forceNewReference); + if (ref != null) { + if (disableLazyLoad) { + ref.setDisableLazyLoad(true); + } + if (!ref.isRegisteredWithLoadContext()) { + ctx.register(many.asMany(), ref); + } + } + } + } + } + + private void setBeanToParent() { + if (parentBean != null) { + // set this back to the parentBean + nodeBeanProp.setValue(parentBean, contextBean); + } + } + + private EntityBean complete() { + if (!readIdNormal) { + // a bean with no Id (never found in context) + if (lazyLoadParentId != null) { + ctx.setLazyLoadedChildBean(localBean, lazyLoadParentId); + } + return localBean; + } else { + if (lazyLoadParentId != null) { + ctx.setLazyLoadedChildBean(contextBean, lazyLoadParentId); + } + return contextBean; + } + } + + private void initialise() throws SQLException { + initLazyParent(); + initBeanType(); + initPersistenceContext(); + readId(); + initSqlLoadBean(); + loadProperties(); + loadChildren(); + } + + /** + * Perform the load returning the loaded bean. + */ + final EntityBean perform() throws SQLException { + initialise(); + if (isLazyLoadManyRoot()) { + return getContextBean(); + } + postLoad(); + setBeanToParent(); + return complete(); + } + + /** + * Return true if this bean was already in the context. If already in the + * context we need to check if it is already contained in the collection. + */ + final boolean isContextBean() { + return localBean == null; + } + } + + /** + * read the properties from the resultSet. + */ + @Override + public EntityBean load(DbReadContext ctx, EntityBean parentBean, EntityBean contextParent) throws SQLException { + return createLoad(ctx, parentBean).perform(); + } + + /** + * Create the loader with or without inheritance. + */ + final Load createLoad(DbReadContext ctx, EntityBean parentBean) { + return (inheritInfo != null) ? new LoadInherit(ctx, parentBean) : new Load(ctx, parentBean); + } + + @Override + public String toString() { + return "SqlTreeLoadBean: " + desc; + } + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadManyRoot.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadManyRoot.java new file mode 100644 index 000000000..3622e770e --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadManyRoot.java @@ -0,0 +1,30 @@ +package io.ebeaninternal.server.query; + +import io.ebean.bean.EntityBean; +import io.ebeaninternal.server.deploy.DbReadContext; + +import java.sql.SQLException; + +final class SqlTreeLoadManyRoot extends SqlTreeLoadBean { + + private final STreePropertyAssocMany manyProp; + + SqlTreeLoadManyRoot(SqlTreeNodeManyRoot node) { + super(node); + this.manyProp = node.manyProp; + } + + @Override + public EntityBean load(DbReadContext cquery, EntityBean parentBean, EntityBean contextParent) throws SQLException { + // pass in null for parentBean because added to a collection rather than set to the parentBean + Load load = createLoad(cquery, null); + EntityBean detailBean = load.perform(); + if (contextParent != null) { + // Add to the collection and initialise collection if needed + // A null detailBean may initialise an empty collection + // Check for bean existing in collection based on load.isContextBean() + manyProp.addBeanToCollectionWithCreate(contextParent, detailBean, load.isContextBean()); + } + return detailBean; + } +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadRoot.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadRoot.java new file mode 100644 index 000000000..d1792ecbe --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadRoot.java @@ -0,0 +1,43 @@ +package io.ebeaninternal.server.query; + +import io.ebean.Version; +import io.ebean.bean.EntityBean; +import io.ebeaninternal.server.deploy.DbReadContext; + +import java.sql.SQLException; +import java.sql.Timestamp; + +/** + * Represents the root node of the Sql Tree. + */ +final class SqlTreeLoadRoot extends SqlTreeLoadBean implements SqlTreeRoot { + + SqlTreeLoadRoot(SqlTreeNodeBean node) { + super(node); + } + + @Override + protected boolean isRoot() { + return true; + } + + @Override + public EntityBean load(DbReadContext ctx) throws SQLException { + return load(ctx, null, null); + } + + /** + * Read the version bean. + */ + @Override + @SuppressWarnings("unchecked") + public Version loadVersion(DbReadContext ctx) throws SQLException { + // read the sys period lower and upper bounds + // these are always the first 2 columns in the resultSet + Timestamp start = ctx.getDataReader().getTimestamp(); + Timestamp end = ctx.getDataReader().getTimestamp(); + T bean = (T) load(ctx, null, null); + return new Version<>(bean, start, end); + } + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNode.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNode.java index 39844cb59..54632d9ca 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNode.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNode.java @@ -1,13 +1,8 @@ package io.ebeaninternal.server.query; -import io.ebean.Version; -import io.ebean.bean.EntityBean; -import io.ebean.core.type.ScalarDataReader; import io.ebeaninternal.api.SpiQuery; -import io.ebeaninternal.server.deploy.DbReadContext; import io.ebeaninternal.server.deploy.DbSqlContext; -import java.sql.SQLException; import java.util.List; import java.util.Set; @@ -64,25 +59,11 @@ interface SqlTreeNode { */ void addSoftDeletePredicate(SpiQuery query); - /** - * Load the appropriate information from the SqlSelectReader. - *

- * At a high level this actually controls the reading of the data from the - * jdbc resultSet and putting it into the bean etc. - *

- */ - EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean contextBean) throws SQLException; - /** * Return true if the query has a many join. */ boolean hasMany(); - /** - * Return the reader for the single attribute query. - */ - ScalarDataReader getSingleAttributeReader(); - /** * Return true if the query is known to only have a single property selected. */ @@ -92,4 +73,9 @@ interface SqlTreeNode { * Add dependent tables to the given set. */ void dependentTables(Set tables); + + /** + * Create the loader for this node. + */ + SqlTreeLoad createLoad(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java index 547eededc..4836fa3fd 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java @@ -1,30 +1,20 @@ package io.ebeaninternal.server.query; -import io.ebean.bean.BeanCollection; -import io.ebean.bean.EntityBean; -import io.ebean.bean.EntityBeanIntercept; -import io.ebean.bean.PersistenceContext; -import io.ebean.core.type.ScalarDataReader; import io.ebean.util.SplitName; import io.ebeaninternal.api.SpiQuery; -import io.ebeaninternal.api.SpiQuery.Mode; -import io.ebeaninternal.server.deploy.DbReadContext; import io.ebeaninternal.server.deploy.DbSqlContext; import io.ebeaninternal.server.deploy.InheritInfo; import io.ebeaninternal.server.deploy.TableJoin; import io.ebeaninternal.server.deploy.id.IdBinder; -import java.sql.SQLException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * Normal bean included in the query. */ class SqlTreeNodeBean implements SqlTreeNode { + private static final SqlTreeLoad[] NO_LOAD_CHILDREN = new SqlTreeLoad[0]; private static final SqlTreeNode[] NO_CHILDREN = new SqlTreeNode[0]; final STreeType desc; @@ -36,33 +26,33 @@ class SqlTreeNodeBean implements SqlTreeNode { /** * Set to true if this is a partial object fetch. */ - private final boolean partialObject; - private final STreeProperty[] properties; + final boolean partialObject; + final STreeProperty[] properties; /** * Extra where clause added by Where annotation on associated many. */ - private final String extraWhere; - private final STreePropertyAssoc nodeBeanProp; + final String extraWhere; + final STreePropertyAssoc nodeBeanProp; /** * False if report bean and has no id property. */ final boolean readId; - private final boolean readIdNormal; - private final boolean disableLazyLoad; - private final InheritInfo inheritInfo; + final boolean readIdNormal; + final boolean disableLazyLoad; + final InheritInfo inheritInfo; final String prefix; - private final Map pathMap; + final Map pathMap; final STreePropertyAssocMany lazyLoadParent; - private final SpiQuery.TemporalMode temporalMode; - private final boolean temporalVersions; - private final IdBinder lazyLoadParentIdBinder; + final SpiQuery.TemporalMode temporalMode; + final boolean temporalVersions; + final IdBinder lazyLoadParentIdBinder; String baseTableAlias; /** * Table alias set if this bean node includes a join to a intersection * table and that table has history support. */ - private boolean intersectionAsOfTableAlias; - private final boolean aggregation; + boolean intersectionAsOfTableAlias; + final boolean aggregation; /** * Construct for leaf node. @@ -108,8 +98,23 @@ class SqlTreeNodeBean implements SqlTreeNode { pathMap = createPathMap(prefix, desc); } - boolean isRoot() { - return false; + @Override + public SqlTreeLoad createLoad() { + return new SqlTreeLoadBean(this); + } + + protected SqlTreeLoad[] createLoadChildren() { + if (children.length == 0) { + return NO_LOAD_CHILDREN; + } + List loadChildren = new ArrayList<>(children.length); + for (SqlTreeNode child : children) { + SqlTreeLoad load = child.createLoad(); + if (load != null) { + loadChildren.add(load); + } + } + return loadChildren.toArray(new SqlTreeLoad[0]); } @Override @@ -117,25 +122,6 @@ class SqlTreeNodeBean implements SqlTreeNode { return properties != null && properties.length == 1 && children.length == 0; } - @Override - public final ScalarDataReader getSingleAttributeReader() { - if (properties == null || properties.length == 0) { - // if we have no property ask first children (in a distinct select with join) - if (children.length == 0) { - // expected to be a findIds query - return desc.idBinder().getBeanProperty(); - } - return children[0].getSingleAttributeReader(); - } - if (properties[0] instanceof STreePropertyAssocOne) { - STreePropertyAssocOne assocOne = (STreePropertyAssocOne)properties[0]; - if (assocOne.isAssocId()) { - return assocOne.idReader(); - } - } - return properties[0]; - } - private Map createPathMap(String prefix, STreeType desc) { HashMap m = new HashMap<>(); for (STreePropertyAssocMany many : desc.propsMany()) { @@ -173,293 +159,6 @@ class SqlTreeNodeBean implements SqlTreeNode { } } - /** - * Load that takes into account inheritance. - */ - private final class LoadInherit extends Load { - - private LoadInherit(DbReadContext ctx, EntityBean parentBean) { - super(ctx, parentBean); - } - - @Override - void initBeanType() throws SQLException { - InheritInfo localInfo = readId ? inheritInfo.readType(ctx) : desc.inheritInfo(); - if (localInfo == null) { - // the bean must be null - localIdBinder = idBinder; - localDesc = desc; - } else { - localBean = localInfo.createEntityBean(); - localType = localInfo.getType(); - localIdBinder = localInfo.getIdBinder(); - localDesc = localInfo.desc(); - } - } - - @Override - void loadProperties() { - // take account of inheritance - for (STreeProperty property : properties) { - localDesc.inheritanceLoad(sqlBeanLoad, property, ctx); - } - } - } - - /** - * Load a bean instance. - */ - class Load { - - final DbReadContext ctx; - final EntityBean parentBean; - - Object lazyLoadParentId; - Class localType; - STreeType localDesc; - IdBinder localIdBinder; - EntityBean localBean; - - Mode queryMode; - PersistenceContext persistenceContext; - Object id; - EntityBean contextBean; - SqlBeanLoad sqlBeanLoad; - boolean lazyLoadMany; - - private Load(DbReadContext ctx, EntityBean parentBean) { - this.ctx = ctx; - this.parentBean = parentBean; - } - - private void initLazyParent() throws SQLException { - if (lazyLoadParentIdBinder != null) { - lazyLoadParentId = lazyLoadParentIdBinder.read(ctx); - } - } - - void initBeanType() throws SQLException { - localDesc = desc; - localBean = desc.createEntityBean(); - localIdBinder = idBinder; - } - - private void initPersistenceContext() { - queryMode = ctx.getQueryMode(); - persistenceContext = (!readIdNormal) ? null : ctx.getPersistenceContext(); - } - - private void readId() throws SQLException { - if (readId) { - id = localIdBinder.readSet(ctx, localBean); - if (id == null) { - readIdNullBean(); - } else if (!temporalVersions) { - readIdBean(); - } - } - } - - private void readIdBean() { - // check the PersistenceContext to see if the bean already exists - contextBean = (EntityBean) localDesc.contextPutIfAbsent(persistenceContext, id, localBean); - if (contextBean == null) { - // bean just added to the persistenceContext - contextBean = localBean; - } else { - // bean already exists in persistenceContext - - if (queryMode.isLoadContextBean()) { - // if explicitly set loadContextBean to true, then reload - localBean = contextBean; - } else if (!contextBean._ebean_getIntercept().isFullyLoadedBean()) { - // reload if contextBean is partial object - localBean = contextBean; - // and switch to lazyLoad query mode in order not to overwrite - // existing properties in SqlBeanLoad::load - queryMode = Mode.LAZYLOAD_BEAN; - } else { - // ignore the DB data... - localBean = null; - } - } - } - - private void readIdNullBean() { - // bean must be null... - localBean = null; - // ... but there may exist as reference bean in parent which has to be marked as deleted. - if (parentBean != null && nodeBeanProp instanceof STreePropertyAssocOne) { - contextBean = ((STreePropertyAssocOne)nodeBeanProp).getValueAsEntityBean(parentBean); - if (contextBean != null) { - desc.markAsDeleted(contextBean); - } - } - } - - private void initSqlLoadBean() { - ctx.setCurrentPrefix(prefix, pathMap); - ctx.propagateState(localBean); - sqlBeanLoad = new SqlBeanLoad(ctx, localType, localBean, queryMode); - } - - void loadProperties() { - for (STreeProperty property : properties) { - property.load(sqlBeanLoad); - } - } - - private void loadChildren() throws SQLException { - if (localBean == null && queryMode == Mode.LAZYLOAD_MANY) { - // batch lazy load many into existing contextBean - localBean = contextBean; - lazyLoadMany = true; - } - for (SqlTreeNode child : children) { - child.load(ctx, localBean, contextBean); - } - } - - private boolean isLazyLoadManyRoot() { - return queryMode == Mode.LAZYLOAD_MANY && isRoot(); - } - - private EntityBean getContextBean() { - return contextBean; - } - - private void postLoad() { - if (!lazyLoadMany && localBean != null) { - ctx.setCurrentPrefix(prefix, pathMap); - if (readIdNormal) { - createListProxies(); - } - if (temporalMode == SpiQuery.TemporalMode.DRAFT) { - localDesc.setDraft(localBean); - } - localDesc.postLoad(localBean); - - EntityBeanIntercept ebi = localBean._ebean_getIntercept(); - ebi.setPersistenceContext(persistenceContext); - if (Mode.LAZYLOAD_BEAN == queryMode) { - // Lazy Load does not reset the dirty state - ebi.setLoadedLazy(); - } else if (readId) { - // normal bean loading - ebi.setLoaded(); - } - - if (disableLazyLoad) { - // bean does not have an Id or is SqlSelect based - ebi.setDisableLazyLoad(true); - } else if (partialObject) { - if (readId) { - // register for lazy loading - ctx.register(null, ebi); - } - } else { - ebi.setFullyLoadedBean(true); - } - - if (ctx.isAutoTuneProfiling() && !disableLazyLoad) { - // collect autoTune profiling for this bean... - ctx.profileBean(ebi, prefix); - } - } - } - - /** - * Create lazy loading proxies for the Many's except for the one that is - * included in the actual query. - */ - private void createListProxies() { - STreePropertyAssocMany fetchedMany = ctx.getManyProperty(); - boolean forceNewReference = queryMode == Mode.REFRESH_BEAN; - for (STreePropertyAssocMany many : localDesc.propsMany()) { - if (many != fetchedMany) { - // create a proxy for the many (deferred fetching) - BeanCollection ref = many.createReference(localBean, forceNewReference); - if (ref != null) { - if (disableLazyLoad) { - ref.setDisableLazyLoad(true); - } - if (!ref.isRegisteredWithLoadContext()) { - ctx.register(many.asMany(), ref); - } - } - } - } - } - - private void setBeanToParent() { - if (parentBean != null) { - // set this back to the parentBean - nodeBeanProp.setValue(parentBean, contextBean); - } - } - - private EntityBean complete() { - if (!readIdNormal) { - // a bean with no Id (never found in context) - if (lazyLoadParentId != null) { - ctx.setLazyLoadedChildBean(localBean, lazyLoadParentId); - } - return localBean; - } else { - if (lazyLoadParentId != null) { - ctx.setLazyLoadedChildBean(contextBean, lazyLoadParentId); - } - return contextBean; - } - } - - private void initialise() throws SQLException { - initLazyParent(); - initBeanType(); - initPersistenceContext(); - readId(); - initSqlLoadBean(); - loadProperties(); - loadChildren(); - } - - /** - * Perform the load returning the loaded bean. - */ - final EntityBean perform() throws SQLException { - initialise(); - if (isLazyLoadManyRoot()) { - return getContextBean(); - } - postLoad(); - setBeanToParent(); - return complete(); - } - - /** - * Return true if this bean was already in the context. If already in the - * context we need to check if it is already contained in the collection. - */ - final boolean isContextBean() { - return localBean == null; - } - } - - /** - * read the properties from the resultSet. - */ - @Override - public EntityBean load(DbReadContext ctx, EntityBean parentBean, EntityBean contextParent) throws SQLException { - return createLoad(ctx, parentBean).perform(); - } - - /** - * Create the loader with or without inheritance. - */ - final Load createLoad(DbReadContext ctx, EntityBean parentBean) { - return (inheritInfo != null) ? new LoadInherit(ctx, parentBean) : new Load(ctx, parentBean); - } - @Override public final void appendGroupBy(DbSqlContext ctx, boolean subQuery) { ctx.pushJoin(prefix); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java index e7ca5d9ab..42d2395b5 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java @@ -38,6 +38,11 @@ final class SqlTreeNodeExtraJoin implements SqlTreeNode { this.manyJoin = assocBeanProperty instanceof STreePropertyAssocMany; } + @Override + public SqlTreeLoad createLoad() { + return null; + } + @Override public boolean isSingleProperty() { return false; @@ -73,11 +78,6 @@ final class SqlTreeNodeExtraJoin implements SqlTreeNode { // nothing to do here } - @Override - public ScalarType getSingleAttributeReader() { - throw new IllegalStateException("No expected"); - } - /** * Return true if the extra join is a many join. *

@@ -184,14 +184,6 @@ final class SqlTreeNodeExtraJoin implements SqlTreeNode { public void appendWhere(DbSqlContext ctx) { } - /** - * Does nothing. - */ - @Override - public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) { - return null; - } - @Override public boolean hasMany() { return manyJoin; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeFormulaWhereJoin.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeFormulaWhereJoin.java index 06a71133a..502c28a6d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeFormulaWhereJoin.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeFormulaWhereJoin.java @@ -33,13 +33,13 @@ final class SqlTreeNodeFormulaWhereJoin implements SqlTreeNode { } @Override - public boolean isSingleProperty() { - return true; + public SqlTreeLoad createLoad() { + return null; } @Override - public ScalarType getSingleAttributeReader() { - throw new IllegalStateException("No expected"); + public boolean isSingleProperty() { + return true; } @Override @@ -98,12 +98,6 @@ final class SqlTreeNodeFormulaWhereJoin implements SqlTreeNode { // nothing to do here } - @Override - public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) { - // nothing to do here - return null; - } - @Override public boolean hasMany() { return true; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyRoot.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyRoot.java index c030e96bc..a8b323533 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyRoot.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyRoot.java @@ -1,16 +1,13 @@ package io.ebeaninternal.server.query; -import io.ebean.bean.EntityBean; import io.ebeaninternal.api.SpiQuery; -import io.ebeaninternal.server.deploy.DbReadContext; import io.ebeaninternal.server.deploy.DbSqlContext; -import java.sql.SQLException; import java.util.List; final class SqlTreeNodeManyRoot extends SqlTreeNodeBean { - private final STreePropertyAssocMany manyProp; + final STreePropertyAssocMany manyProp; SqlTreeNodeManyRoot(String prefix, STreePropertyAssocMany prop, SqlTreeProperties props, List myList, boolean withId, SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad) { @@ -19,22 +16,13 @@ final class SqlTreeNodeManyRoot extends SqlTreeNodeBean { } @Override - public boolean hasMany() { - return true; + public SqlTreeLoad createLoad() { + return new SqlTreeLoadManyRoot(this); } @Override - public EntityBean load(DbReadContext cquery, EntityBean parentBean, EntityBean contextParent) throws SQLException { - // pass in null for parentBean because added to a collection rather than set to the parentBean - SqlTreeNodeBean.Load load = createLoad(cquery, null); - EntityBean detailBean = load.perform(); - if (contextParent != null) { - // Add to the collection and initialise collection if needed - // A null detailBean may initialise an empty collection - // Check for bean existing in collection based on load.isContextBean() - manyProp.addBeanToCollectionWithCreate(contextParent, detailBean, load.isContextBean()); - } - return detailBean; + public boolean hasMany() { + return true; } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java index 73c7a18b2..cb2ba54e7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java @@ -1,10 +1,7 @@ package io.ebeaninternal.server.query; -import io.ebean.bean.EntityBean; -import io.ebean.core.type.ScalarType; import io.ebean.util.SplitName; import io.ebeaninternal.api.SpiQuery; -import io.ebeaninternal.server.deploy.DbReadContext; import io.ebeaninternal.server.deploy.DbSqlContext; import io.ebeaninternal.server.deploy.TableJoin; @@ -37,13 +34,13 @@ final class SqlTreeNodeManyWhereJoin implements SqlTreeNode { } @Override - public boolean isSingleProperty() { - return false; + public SqlTreeLoad createLoad() { + return null; } @Override - public ScalarType getSingleAttributeReader() { - throw new IllegalStateException("No expected"); + public boolean isSingleProperty() { + return false; } @Override @@ -132,12 +129,6 @@ final class SqlTreeNodeManyWhereJoin implements SqlTreeNode { // nothing to do here } - @Override - public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) { - // nothing to do here - return null; - } - @Override public boolean hasMany() { return true; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeRoot.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeRoot.java index a58c51cc5..c25024624 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeRoot.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeRoot.java @@ -1,21 +1,16 @@ package io.ebeaninternal.server.query; -import io.ebean.Version; -import io.ebean.bean.EntityBean; import io.ebeaninternal.api.SpiQuery; -import io.ebeaninternal.server.deploy.DbReadContext; import io.ebeaninternal.server.deploy.DbSqlContext; import io.ebeaninternal.server.deploy.TableJoin; -import java.sql.SQLException; -import java.sql.Timestamp; import java.util.List; import java.util.Set; /** * Represents the root node of the Sql Tree. */ -final class SqlTreeNodeRoot extends SqlTreeNodeBean implements SqlTreeRoot { +final class SqlTreeNodeRoot extends SqlTreeNodeBean { private final TableJoin includeJoin; private final boolean sqlDistinct; @@ -34,27 +29,8 @@ final class SqlTreeNodeRoot extends SqlTreeNodeBean implements SqlTreeRoot { } @Override - protected boolean isRoot() { - return true; - } - - @Override - public EntityBean load(DbReadContext ctx) throws SQLException { - return load(ctx, null, null); - } - - /** - * Read the version bean. - */ - @Override - @SuppressWarnings("unchecked") - public Version loadVersion(DbReadContext ctx) throws SQLException { - // read the sys period lower and upper bounds - // these are always the first 2 columns in the resultSet - Timestamp start = ctx.getDataReader().getTimestamp(); - Timestamp end = ctx.getDataReader().getTimestamp(); - T bean = (T) load(ctx, null, null); - return new Version<>(bean, start, end); + public SqlTreeLoad createLoad() { + return new SqlTreeLoadRoot(this); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreePlan.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreePlan.java new file mode 100644 index 000000000..7a0e929ef --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreePlan.java @@ -0,0 +1,41 @@ +package io.ebeaninternal.server.query; + +import java.util.Set; + +final class SqlTreePlan { + + private final SqlTreeLoad rootNode; + private final STreePropertyAssocMany manyProperty; + private final STreeProperty[] encryptedProps; + private final Set dependentTables; + + SqlTreePlan(SqlTreeLoad rootNode, STreePropertyAssocMany manyProperty, STreeProperty[] encryptedProps, Set dependentTables) { + this.rootNode = rootNode; + this.manyProperty = manyProperty; + this.encryptedProps = encryptedProps; + this.dependentTables = dependentTables; + } + + SqlTreeRoot getRootNode() { + return (SqlTreeRoot)rootNode; + } + + /** + * Return the property that is associated with the many. There can only be one + * per SqlSelect. This can be null. + */ + STreePropertyAssocMany getManyProperty() { + return manyProperty; + } + + STreeProperty[] getEncryptedProps() { + return encryptedProps; + } + + /** + * Return the tables that are joined in this query. + */ + Set dependentTables() { + return dependentTables; + } +} From 5cb486c74572671346f532e60270994bcbbd5a25 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 26 Jan 2022 14:18:02 +1300 Subject: [PATCH 02/13] CI Build without maven cache --- .github/workflows/build.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bf03d3b50..2bb559ac1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,14 +23,14 @@ jobs: with: java-version: ${{ matrix.java_version }} distribution: 'zulu' - - name: Maven cache - uses: actions/cache@v2 - env: - cache-name: maven-cache - with: - path: - ~/.m2 - key: build-${{ env.cache-name }} +# - name: Maven cache +# uses: actions/cache@v2 +# env: +# cache-name: maven-cache +# with: +# path: +# ~/.m2 +# key: build-${{ env.cache-name }} - name: Build with Maven - run: mvn package + run: mvn clean package From 4a8231a6ccad66b2a73be4478a3939e53bafad19 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 26 Jan 2022 14:38:52 +1300 Subject: [PATCH 03/13] CI Build using adopt distribution --- .github/workflows/build.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2bb559ac1..0cd698dc0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,7 +22,8 @@ jobs: uses: actions/setup-java@v2 with: java-version: ${{ matrix.java_version }} - distribution: 'zulu' +# distribution: 'zulu' + distribution: 'adopt' # - name: Maven cache # uses: actions/cache@v2 # env: @@ -32,5 +33,5 @@ jobs: # ~/.m2 # key: build-${{ env.cache-name }} - name: Build with Maven - run: mvn clean package + run: mvn --batch-mode clean package From fa561e51974bf5ae6c506a2d19479eb85f3b9f95 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 26 Jan 2022 14:52:32 +1300 Subject: [PATCH 04/13] CI Build with single test and bump enhancement tiles --- .github/workflows/build.yml | 6 +++++- ebean-autotune/pom.xml | 2 +- ebean-externalmapping-xml/pom.xml | 2 +- ebean-kotlin/pom.xml | 2 +- ebean-postgis/pom.xml | 2 +- ebean-querybean/pom.xml | 2 +- ebean-redis/pom.xml | 2 +- ebean-test/pom.xml | 2 +- tests/test-kotlin/pom.xml | 2 +- 9 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0cd698dc0..3c38cb55e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,6 +32,10 @@ jobs: # path: # ~/.m2 # key: build-${{ env.cache-name }} + - name: Maven version + run: mvn --version + - name: Maven single test + run: mvn --batch-mode clean verify -Dtest="io.ebeaninternal.server.core.DefaultServer_getReferenceTest" -DfailIfNoTests=false - name: Build with Maven - run: mvn --batch-mode clean package + run: mvn --batch-mode clean verify diff --git a/ebean-autotune/pom.xml b/ebean-autotune/pom.xml index 9dbe36921..835b74dbe 100644 --- a/ebean-autotune/pom.xml +++ b/ebean-autotune/pom.xml @@ -63,7 +63,7 @@ true - io.ebean.tile:enhancement:12.11.3 + io.ebean.tile:enhancement:12.14.1 io.avaje.tile:moditech-module:1.0 diff --git a/ebean-externalmapping-xml/pom.xml b/ebean-externalmapping-xml/pom.xml index 94047ec2e..f7568dc73 100644 --- a/ebean-externalmapping-xml/pom.xml +++ b/ebean-externalmapping-xml/pom.xml @@ -89,7 +89,7 @@ - io.ebean.tile:enhancement:12.11.3 + io.ebean.tile:enhancement:12.14.1 io.avaje.tile:moditech-module:1.0 diff --git a/ebean-kotlin/pom.xml b/ebean-kotlin/pom.xml index 35923973c..8d8a3d3b9 100644 --- a/ebean-kotlin/pom.xml +++ b/ebean-kotlin/pom.xml @@ -14,7 +14,7 @@ 1.8 1.8 - 12.13.0 + 12.14.1 1.6.0 diff --git a/ebean-postgis/pom.xml b/ebean-postgis/pom.xml index ac4553b77..caa1ccf81 100644 --- a/ebean-postgis/pom.xml +++ b/ebean-postgis/pom.xml @@ -105,7 +105,7 @@ true - io.ebean.tile:enhancement:12.11.3 + io.ebean.tile:enhancement:12.14.1 io.avaje.tile:moditech-module:1.0 diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml index 6ecbfafad..210e2249e 100644 --- a/ebean-querybean/pom.xml +++ b/ebean-querybean/pom.xml @@ -100,7 +100,7 @@ true - io.ebean.tile:enhancement:12.11.3 + io.ebean.tile:enhancement:12.14.1 io.avaje.tile:moditech-module:1.0 diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml index e811c97ad..f33064c74 100644 --- a/ebean-redis/pom.xml +++ b/ebean-redis/pom.xml @@ -80,7 +80,7 @@ true - io.ebean.tile:enhancement:12.11.3 + io.ebean.tile:enhancement:12.14.1 io.avaje.tile:moditech-module:1.0 diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml index a11b27133..b837a02d1 100644 --- a/ebean-test/pom.xml +++ b/ebean-test/pom.xml @@ -249,7 +249,7 @@ true - io.ebean.tile:enhancement:12.11.3 + io.ebean.tile:enhancement:12.14.1 io.avaje.tile:moditech-module:1.0 diff --git a/tests/test-kotlin/pom.xml b/tests/test-kotlin/pom.xml index ab8706f74..6bc7e47c7 100644 --- a/tests/test-kotlin/pom.xml +++ b/tests/test-kotlin/pom.xml @@ -19,7 +19,7 @@ 11 11 - 12.13.0 + 12.14.1 1.6.0 2.12.1 true From a1d6b280bca68190573d0b45b200796de3355cd2 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 26 Jan 2022 14:58:38 +1300 Subject: [PATCH 05/13] Bump test parent poms --- tests/pom.xml | 2 +- tests/test-kotlin/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/pom.xml b/tests/pom.xml index b3592f647..ac7678298 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -4,7 +4,7 @@ org.avaje java8-oss - 2.3 + 3.3 diff --git a/tests/test-kotlin/pom.xml b/tests/test-kotlin/pom.xml index 6bc7e47c7..c4138a900 100644 --- a/tests/test-kotlin/pom.xml +++ b/tests/test-kotlin/pom.xml @@ -6,7 +6,7 @@ org.avaje java11-oss - 2.3 + 3.4 From 1a54fc12adf0ad3b3d30269892ab68fbd9d3d8b6 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 26 Jan 2022 15:48:03 +1300 Subject: [PATCH 06/13] Test logging enabled for investigating CI Build issue --- ebean-test/src/test/resources/logback-test.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ebean-test/src/test/resources/logback-test.xml b/ebean-test/src/test/resources/logback-test.xml index bfe4a1ede..f280298f8 100644 --- a/ebean-test/src/test/resources/logback-test.xml +++ b/ebean-test/src/test/resources/logback-test.xml @@ -79,11 +79,11 @@ - - + + - + From 2f824c99ea894bd9a5473fc9eb3fa2e381e7e96a Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 26 Jan 2022 16:53:38 +1300 Subject: [PATCH 07/13] Fix issues with test ordering --- .../server/core/DefaultServer_getReferenceTest.java | 4 ++-- .../server/deploy/BeanPropertyAssocManyTest.java | 11 +++++++---- .../tests/batchinsert/TestBatchSaveWithGetBeanId.java | 2 ++ .../java/org/tests/insert/TestInsertCollection.java | 8 +++++++- .../tests/query/other/TestQuerySingleAttribute.java | 6 +++--- .../java/org/tests/update/TestStatelessUpdate.java | 7 +++++++ .../test/java/org/tests/update/TestUpdatePartial.java | 7 +++++++ 7 files changed, 35 insertions(+), 10 deletions(-) diff --git a/ebean-test/src/test/java/io/ebeaninternal/server/core/DefaultServer_getReferenceTest.java b/ebean-test/src/test/java/io/ebeaninternal/server/core/DefaultServer_getReferenceTest.java index bbf2786b4..e638d9d00 100644 --- a/ebean-test/src/test/java/io/ebeaninternal/server/core/DefaultServer_getReferenceTest.java +++ b/ebean-test/src/test/java/io/ebeaninternal/server/core/DefaultServer_getReferenceTest.java @@ -24,8 +24,8 @@ public class DefaultServer_getReferenceTest extends BaseTestCase { ResetBasicData.reset(); DB.execute(() -> { - Customer loaded = DB.find(Customer.class, 1); - Customer reference = DB.reference(Customer.class, 1); + Customer loaded = DB.find(Customer.class).where().eq("name", "Rob").findOne(); + Customer reference = DB.reference(Customer.class, loaded.getId()); assertThat(loaded).isSameAs(reference); }); } diff --git a/ebean-test/src/test/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyTest.java b/ebean-test/src/test/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyTest.java index c63966f21..eadc01968 100644 --- a/ebean-test/src/test/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyTest.java +++ b/ebean-test/src/test/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyTest.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.deploy; import io.ebean.BaseTestCase; +import io.ebean.DB; import io.ebean.bean.BeanCollection; import io.ebean.bean.EntityBean; import io.ebean.common.BeanList; @@ -66,11 +67,13 @@ public class BeanPropertyAssocManyTest extends BaseTestCase { ResetBasicData.reset(); - List customerIds = new ArrayList<>(); - customerIds.add(1L); - customerIds.add(2L); + List ids = DB.find(Customer.class).orderBy("id").setMaxRows(2).findIds(); - List contactIdsForOne = contacts().findIdsByParentId(1L, null, null, null, true); + List customerIds = new ArrayList<>(); + customerIds.add(ids.get(0)); + customerIds.add(ids.get(1)); + + List contactIdsForOne = contacts().findIdsByParentId(ids.get(0), null, null, null, true); List contactIdsForMultiple = contacts().findIdsByParentId(null, customerIds, null, null, true); diff --git a/ebean-test/src/test/java/org/tests/batchinsert/TestBatchSaveWithGetBeanId.java b/ebean-test/src/test/java/org/tests/batchinsert/TestBatchSaveWithGetBeanId.java index 7239731d5..029bac16f 100644 --- a/ebean-test/src/test/java/org/tests/batchinsert/TestBatchSaveWithGetBeanId.java +++ b/ebean-test/src/test/java/org/tests/batchinsert/TestBatchSaveWithGetBeanId.java @@ -8,6 +8,7 @@ import io.ebean.annotation.Platform; import io.ebean.annotation.Transactional; import org.junit.jupiter.api.Test; import org.tests.model.basic.Customer; +import org.tests.model.basic.ResetBasicData; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -22,6 +23,7 @@ public class TestBatchSaveWithGetBeanId extends BaseTestCase { @Test @IgnorePlatform(Platform.HANA) // HANA doesn't support insert batching public void test() { + ResetBasicData.reset(); Database server = DB.getDefault(); Customer model = new Customer(); diff --git a/ebean-test/src/test/java/org/tests/insert/TestInsertCollection.java b/ebean-test/src/test/java/org/tests/insert/TestInsertCollection.java index 0d3679d91..773bffb02 100644 --- a/ebean-test/src/test/java/org/tests/insert/TestInsertCollection.java +++ b/ebean-test/src/test/java/org/tests/insert/TestInsertCollection.java @@ -2,8 +2,10 @@ package org.tests.insert; import io.ebean.BaseTestCase; import io.ebean.DB; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.tests.model.basic.Customer; +import org.tests.model.basic.ResetBasicData; import java.util.ArrayList; import java.util.List; @@ -12,9 +14,13 @@ import static org.junit.jupiter.api.Assertions.*; public class TestInsertCollection extends BaseTestCase { + @BeforeAll + static void before() { + ResetBasicData.reset(); + } + @Test public void test() { - Customer cust1 = new Customer(); cust1.setName("jim"); diff --git a/ebean-test/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java b/ebean-test/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java index 43f7162cb..8329e0d1e 100644 --- a/ebean-test/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java +++ b/ebean-test/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java @@ -131,7 +131,7 @@ public class TestQuerySingleAttribute extends BaseTestCase { Query query = DB.find(Customer.class) .select("name") - .where().eq("id", 1).query(); + .where().eq("name", "Rob").query(); String name = query.findSingleAttribute(); @@ -248,7 +248,7 @@ public class TestQuerySingleAttribute extends BaseTestCase { String name = DB.find(Customer.class) .select("name") - .where().eq("id", 1) + .where().eq("name", "Rob") .findSingleAttribute(); assertThat(name).isNotNull(); @@ -646,7 +646,7 @@ public class TestQuerySingleAttribute extends BaseTestCase { .findSingleAttributeList(); assertThat(list1.get(0)).isInstanceOf(CountedValue.class); //assertThat(list1.toString()).isEqualTo("[1: Tracy, 3: Jim1, 1: Jack, 3: Fred1, 1: Fiona, 3: Bugs1]"); - + query = DB.find(Contact.class).select("firstName"); list1 = query .setCountDistinct(CountDistinctOrder.NO_ORDERING) diff --git a/ebean-test/src/test/java/org/tests/update/TestStatelessUpdate.java b/ebean-test/src/test/java/org/tests/update/TestStatelessUpdate.java index e8243bdd1..d03da5ffb 100644 --- a/ebean-test/src/test/java/org/tests/update/TestStatelessUpdate.java +++ b/ebean-test/src/test/java/org/tests/update/TestStatelessUpdate.java @@ -3,11 +3,13 @@ package org.tests.update; import io.ebean.DB; import io.ebean.TransactionalTestCase; import io.ebean.test.LoggedSql; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.tests.model.basic.Contact; import org.tests.model.basic.Customer; import org.tests.model.basic.EBasic; import org.tests.model.basic.EBasic.Status; +import org.tests.model.basic.ResetBasicData; import javax.persistence.EntityNotFoundException; import java.util.*; @@ -17,6 +19,11 @@ import static org.junit.jupiter.api.Assertions.*; public class TestStatelessUpdate extends TransactionalTestCase { + @BeforeAll + static void before() { + ResetBasicData.reset(); + } + @Test public void test() { diff --git a/ebean-test/src/test/java/org/tests/update/TestUpdatePartial.java b/ebean-test/src/test/java/org/tests/update/TestUpdatePartial.java index 978ef0589..e4fecaed8 100644 --- a/ebean-test/src/test/java/org/tests/update/TestUpdatePartial.java +++ b/ebean-test/src/test/java/org/tests/update/TestUpdatePartial.java @@ -3,14 +3,21 @@ package org.tests.update; import io.ebean.BaseTestCase; import io.ebean.DB; import io.ebean.SqlRow; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.tests.model.basic.Customer; +import org.tests.model.basic.ResetBasicData; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; public class TestUpdatePartial extends BaseTestCase { + @BeforeAll + static void before() { + ResetBasicData.reset(); + } + @Test public void test() { From 774cab327d1063497cac631c347787f46f970d72 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 26 Jan 2022 17:23:18 +1300 Subject: [PATCH 08/13] Fix issues with test ordering - ordering in ResetBasicData --- .../src/test/java/org/tests/model/basic/ResetBasicData.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ebean-test/src/test/java/org/tests/model/basic/ResetBasicData.java b/ebean-test/src/test/java/org/tests/model/basic/ResetBasicData.java index 6a01db649..80260a350 100644 --- a/ebean-test/src/test/java/org/tests/model/basic/ResetBasicData.java +++ b/ebean-test/src/test/java/org/tests/model/basic/ResetBasicData.java @@ -2,6 +2,7 @@ package org.tests.model.basic; import io.ebean.DB; import io.ebean.Database; +import io.ebean.Transaction; import org.tests.model.basic.Order.Status; import java.sql.Date; @@ -47,6 +48,7 @@ public class ResetBasicData { // the test rely on the products being in there return; } + Transaction.current().setBatchMode(false); //me.deleteAll(); me.insertCountries(); me.insertProducts(); From 7e0ac5a73a080a0c0578a00b992aedda16de5df9 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 26 Jan 2022 21:47:00 +1300 Subject: [PATCH 09/13] Fix issues with tests - tidy ResetBasicData --- .../TestBatchSaveWithGetBeanId.java | 8 +- .../org/tests/model/basic/ResetBasicData.java | 142 +++++------------- 2 files changed, 42 insertions(+), 108 deletions(-) diff --git a/ebean-test/src/test/java/org/tests/batchinsert/TestBatchSaveWithGetBeanId.java b/ebean-test/src/test/java/org/tests/batchinsert/TestBatchSaveWithGetBeanId.java index 029bac16f..7495f8560 100644 --- a/ebean-test/src/test/java/org/tests/batchinsert/TestBatchSaveWithGetBeanId.java +++ b/ebean-test/src/test/java/org/tests/batchinsert/TestBatchSaveWithGetBeanId.java @@ -6,6 +6,7 @@ import io.ebean.Database; import io.ebean.annotation.IgnorePlatform; import io.ebean.annotation.Platform; import io.ebean.annotation.Transactional; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.tests.model.basic.Customer; import org.tests.model.basic.ResetBasicData; @@ -14,6 +15,11 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; public class TestBatchSaveWithGetBeanId extends BaseTestCase { + @BeforeAll + static void before() { + ResetBasicData.reset(); + } + /** * Making this transaction with batchSize means that the insert * below does not occur immediately ... and the getBeanId() @@ -23,8 +29,6 @@ public class TestBatchSaveWithGetBeanId extends BaseTestCase { @Test @IgnorePlatform(Platform.HANA) // HANA doesn't support insert batching public void test() { - ResetBasicData.reset(); - Database server = DB.getDefault(); Customer model = new Customer(); model.setName("foo"); diff --git a/ebean-test/src/test/java/org/tests/model/basic/ResetBasicData.java b/ebean-test/src/test/java/org/tests/model/basic/ResetBasicData.java index 80260a350..819bc406c 100644 --- a/ebean-test/src/test/java/org/tests/model/basic/ResetBasicData.java +++ b/ebean-test/src/test/java/org/tests/model/basic/ResetBasicData.java @@ -58,100 +58,55 @@ public class ResetBasicData { } - public void deleteAll() { - DB.execute(() -> { + private void insertCountries() { + Country c = new Country(); + c.setCode("NZ"); + c.setName("New Zealand"); + server.save(c); - // orm update use bean name and bean properties - server.sqlUpdate("delete from o_cached_bean_child").execute(); - server.sqlUpdate("delete from o_cached_bean_country").execute(); - server.sqlUpdate("delete from o_cached_bean").execute(); - - server.createUpdate(OrderShipment.class, "delete from orderShipment").execute(); - - server.createUpdate(OrderDetail.class, "delete from orderDetail").execute(); - - server.createUpdate(Order.class, "delete from order").execute(); - - server.createUpdate(Contact.class, "delete from contact").execute(); - - server.createUpdate(Customer.class, "delete from Customer").execute(); - - server.createUpdate(Address.class, "delete from address").execute(); - - // sql update uses table and column names - server.sqlUpdate("delete from o_country").execute(); - server.sqlUpdate("delete from o_product").execute(); - - }); + Country au = new Country(); + au.setCode("AU"); + au.setName("Australia"); + server.save(au); } - public void insertCountries() { + private void insertProducts() { + Product p = new Product(); + p.setName("Chair"); + p.setSku("C001"); + server.save(p); - if (server.find(Country.class).findCount() > 0) { - return; - } + p = new Product(); + p.setName("Desk"); + p.setSku("DSK1"); + server.save(p); - server.execute(() -> { - Country c = new Country(); - c.setCode("NZ"); - c.setName("New Zealand"); - server.save(c); + p = new Product(); + p.setName("Computer"); + p.setSku("C002"); + server.save(p); - Country au = new Country(); - au.setCode("AU"); - au.setName("Australia"); - server.save(au); - }); + p = new Product(); + p.setName("Printer"); + p.setSku("C003"); + server.save(p); } + private void insertTestCustAndOrders() { + Customer cust1 = insertCustomer("Rob"); + Customer cust2 = insertCustomerNoAddress(); + insertCustomerFiona(); + insertCustomerNoContacts("NocCust"); - public void insertProducts() { - - if (server.find(Product.class).findCount() > 0) { - return; - } - server.execute(() -> { - Product p = new Product(); - p.setName("Chair"); - p.setSku("C001"); - server.save(p); - - p = new Product(); - p.setName("Desk"); - p.setSku("DSK1"); - server.save(p); - - p = new Product(); - p.setName("Computer"); - p.setSku("C002"); - server.save(p); - - p = new Product(); - p.setName("Printer"); - p.setSku("C003"); - server.save(p); - }); - } - - public void insertTestCustAndOrders() { - - DB.execute(() -> { - Customer cust1 = insertCustomer("Rob"); - Customer cust2 = insertCustomerNoAddress(); - insertCustomerFiona(); - insertCustomerNoContacts("NocCust"); - - createOrder1(cust1); - createOrder2(cust2); - createOrder3(cust1); - createOrder4(cust1); - createOrder5(cust2); - }); + createOrder1(cust1); + createOrder2(cust2); + createOrder3(cust1); + createOrder4(cust1); + createOrder5(cust2); } public static Customer createCustAndOrder(String custName) { - ResetBasicData me = new ResetBasicData(); Customer cust1 = insertCustomer(custName); me.createOrder1(cust1); @@ -159,7 +114,6 @@ public class ResetBasicData { } public static Order createOrderCustAndOrder(String custName) { - ResetBasicData me = new ResetBasicData(); Customer cust1 = insertCustomer(custName); return me.createOrder1(cust1); @@ -168,10 +122,8 @@ public class ResetBasicData { private static int contactEmailNum = 1; private Customer insertCustomerFiona() { - Customer c = createCustomer("Fiona", "12 Apple St", "West Coast Rd", 1, "2009-08-31"); c.setStatus(Customer.Status.ACTIVE); - c.addContact(createContact("Fiona", "Black")); c.addContact(createContact("Tracy", "Red")); @@ -187,22 +139,18 @@ public class ResetBasicData { } private Customer insertCustomerNoContacts(String name) { - Customer c = createCustomer("Roger", "15 Kumera Way", "Bos town", 1, "2010-04-10"); c.setName(name); c.setStatus(Customer.Status.ACTIVE); - DB.save(c); return c; } private Customer insertCustomerNoAddress() { - Customer c = new Customer(); c.setName("Cust NoAddress"); c.setStatus(Customer.Status.NEW); c.addContact(createContact("Jack", "Black")); - DB.save(c); return c; } @@ -218,7 +166,6 @@ public class ResetBasicData { } public static Customer createCustomer(String name, String shippingStreet, String billingStreet, int contactSuffix, String annDate) { - Customer c = new Customer(); c.setName(name); c.setStatus(Customer.Status.NEW); @@ -253,17 +200,14 @@ public class ResetBasicData { c.setBillingAddress(billingAddr); } - return c; } private Order createOrder1(Customer customer) { - Product product1 = DB.reference(Product.class, 1); Product product2 = DB.reference(Product.class, 2); Product product3 = DB.reference(Product.class, 3); - Order order = new Order(); order.setCustomer(customer); order.setOrderDate(Date.valueOf("2018-07-01")); @@ -273,16 +217,12 @@ public class ResetBasicData { details.add(new OrderDetail(product2, 3, 1.10)); details.add(new OrderDetail(product3, 1, 2.00)); order.setDetails(details); - - order.addShipment(new OrderShipment()); - DB.save(order); return order; } private void createOrder2(Customer customer) { - Product product1 = DB.reference(Product.class, 1); Order order = new Order(); @@ -293,14 +233,11 @@ public class ResetBasicData { List details = new ArrayList<>(); details.add(new OrderDetail(product1, 4, 10.50)); order.setDetails(details); - order.addShipment(new OrderShipment()); - DB.save(order); } private void createOrder3(Customer customer) { - Product product1 = DB.reference(Product.class, 1); Product product3 = DB.reference(Product.class, 3); @@ -314,30 +251,23 @@ public class ResetBasicData { details.add(new OrderDetail(product3, 40, 2.10)); details.add(new OrderDetail(product1, 5, 10.00)); order.setDetails(details); - order.addShipment(new OrderShipment()); - DB.save(order); } private void createOrder4(Customer customer) { - Order order = new Order(); order.setCustomer(customer); order.setOrderDate(Date.valueOf("2018-07-04")); - order.addShipment(new OrderShipment()); - DB.save(order); } private void createOrder5(Customer customer) { - Order order = new Order(); order.setCustomer(customer); order.setOrderDate(Date.valueOf("2018-06-28")); order.addShipment(new OrderShipment()); - DB.save(order); } } From 5466c6a350872e00f95fa74fba53dffe047a9443 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 26 Jan 2022 21:51:48 +1300 Subject: [PATCH 10/13] Reduce logging when running Build tests --- .github/workflows/build.yml | 25 +++++++++---------- .../src/test/resources/logback-test.xml | 6 ++--- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3c38cb55e..5e3fafdd4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,20 +22,19 @@ jobs: uses: actions/setup-java@v2 with: java-version: ${{ matrix.java_version }} -# distribution: 'zulu' - distribution: 'adopt' -# - name: Maven cache -# uses: actions/cache@v2 -# env: -# cache-name: maven-cache -# with: -# path: -# ~/.m2 -# key: build-${{ env.cache-name }} + distribution: 'zulu' + - name: Maven cache + uses: actions/cache@v2 + env: + cache-name: maven-cache + with: + path: + ~/.m2 + key: build-${{ env.cache-name }} - name: Maven version run: mvn --version - - name: Maven single test - run: mvn --batch-mode clean verify -Dtest="io.ebeaninternal.server.core.DefaultServer_getReferenceTest" -DfailIfNoTests=false +# - name: Maven single test +# run: mvn --batch-mode clean verify -Dtest="io.ebeaninternal.server.core.DefaultServer_getReferenceTest" -DfailIfNoTests=false - name: Build with Maven - run: mvn --batch-mode clean verify + run: mvn clean package diff --git a/ebean-test/src/test/resources/logback-test.xml b/ebean-test/src/test/resources/logback-test.xml index f280298f8..bfe4a1ede 100644 --- a/ebean-test/src/test/resources/logback-test.xml +++ b/ebean-test/src/test/resources/logback-test.xml @@ -79,11 +79,11 @@ - - + + - + From ffdafc796ba7218138eb098d13f32c2f0d65714e Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 26 Jan 2022 21:54:19 +1300 Subject: [PATCH 11/13] Touch README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 30dc2c06b..a560d8c82 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,3 @@ Goto [https://ebean.io/docs/](https://ebean.io/docs/) [Maven central - all related projects](http://search.maven.org/#search%7Cga%7C1%7Cebean "maven central all related projects") -## Other versions -* [![Maven Central : ebean-agent](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent) - ebean-agent -* [![Maven Central : ebean-maven-plugin](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin) - ebean-maven-plugin From 23061473c7a8905c4a3d92eee17d2b853fc44bb3 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Tue, 25 Jan 2022 13:44:23 +1300 Subject: [PATCH 12/13] Refactor internals split result load functionality from SqlTreeNode into SqlTreeLoad --- .../io/ebeaninternal/server/query/CQuery.java | 2 +- .../server/query/CQueryBuilder.java | 8 +- .../server/query/CQueryPlan.java | 8 +- .../server/query/CQueryPlanRawSql.java | 2 +- .../ebeaninternal/server/query/SqlTree.java | 14 +- .../server/query/SqlTreeLoad.java | 28 ++ .../server/query/SqlTreeLoadBean.java | 373 ++++++++++++++++++ .../server/query/SqlTreeLoadManyRoot.java | 30 ++ .../server/query/SqlTreeLoadRoot.java | 43 ++ .../server/query/SqlTreeNode.java | 24 +- .../server/query/SqlTreeNodeBean.java | 365 ++--------------- .../server/query/SqlTreeNodeExtraJoin.java | 18 +- .../query/SqlTreeNodeFormulaWhereJoin.java | 14 +- .../server/query/SqlTreeNodeManyRoot.java | 22 +- .../query/SqlTreeNodeManyWhereJoin.java | 17 +- .../server/query/SqlTreeNodeRoot.java | 30 +- .../server/query/SqlTreePlan.java | 41 ++ 17 files changed, 588 insertions(+), 451 deletions(-) create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoad.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadBean.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadManyRoot.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadRoot.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreePlan.java diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java index 45a5b5a11..1870c1b6f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQuery.java @@ -193,7 +193,7 @@ public final class CQuery implements DbReadContext, CancelableQuery, SpiProfi // set the generated sql back to the query // so its available to the user... query.setGeneratedSql(queryPlan.getSql()); - SqlTree sqlTree = queryPlan.getSqlTree(); + SqlTreePlan sqlTree = queryPlan.getSqlTree(); this.rootNode = sqlTree.getRootNode(); this.manyProperty = sqlTree.getManyProperty(); this.sql = queryPlan.getSql(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java index 278603f5b..3367fbfe4 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java @@ -110,7 +110,7 @@ final class CQueryBuilder { sql = buildUpdateSql(request, rootTableAlias, predicates, sqlTree); } // cache the query plan - queryPlan = new CQueryPlan(request, sql, sqlTree, predicates.getLogWhereSql()); + queryPlan = new CQueryPlan(request, sql, sqlTree.plan(), predicates.getLogWhereSql()); request.putQueryPlan(queryPlan); return new CQueryUpdate(request, predicates, queryPlan); } @@ -197,7 +197,7 @@ final class CQueryBuilder { SqlTree sqlTree = createSqlTree(request, predicates); SqlLimitResponse s = buildSql(null, request, predicates, sqlTree); - queryPlan = new CQueryPlan(request, s.getSql(), sqlTree, predicates.getLogWhereSql()); + queryPlan = new CQueryPlan(request, s.getSql(), sqlTree.plan(), predicates.getLogWhereSql()); request.putQueryPlan(queryPlan); return new CQueryFetchSingleAttribute(request, predicates, queryPlan, query.isCountDistinct()); } @@ -288,7 +288,7 @@ final class CQueryBuilder { } } // cache the query plan - queryPlan = new CQueryPlan(request, sql, sqlTree, predicates.getLogWhereSql()); + queryPlan = new CQueryPlan(request, sql, sqlTree.plan(), predicates.getLogWhereSql()); request.putQueryPlan(queryPlan); return new CQueryRowCount(queryPlan, request, predicates); } @@ -343,7 +343,7 @@ final class CQueryBuilder { if (rawSql) { queryPlan = new CQueryPlanRawSql(request, res, sqlTree, predicates.getLogWhereSql()); } else { - queryPlan = new CQueryPlan(request, res, sqlTree, false, predicates.getLogWhereSql()); + queryPlan = new CQueryPlan(request, res, sqlTree.plan(), false, predicates.getLogWhereSql()); } BeanDescriptor desc = request.descriptor(); if (desc.isReadAuditing()) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java index bf84070f2..d07276920 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java @@ -55,7 +55,7 @@ public class CQueryPlan implements SpiQueryPlan { private final String sql; private final String hash; private final String logWhereSql; - private final SqlTree sqlTree; + private final SqlTreePlan sqlTree; /** * Encrypted properties required additional binding. @@ -76,7 +76,7 @@ public class CQueryPlan implements SpiQueryPlan { /** * Create a query plan based on a OrmQueryRequest. */ - CQueryPlan(OrmQueryRequest request, SqlLimitResponse sqlRes, SqlTree sqlTree, boolean rawSql, String logWhereSql) { + CQueryPlan(OrmQueryRequest request, SqlLimitResponse sqlRes, SqlTreePlan sqlTree, boolean rawSql, String logWhereSql) { this.server = request.server(); this.dataTimeZone = server.dataTimeZone(); this.beanType = request.descriptor().type(); @@ -101,7 +101,7 @@ public class CQueryPlan implements SpiQueryPlan { /** * Create a query plan for a raw sql query. */ - CQueryPlan(OrmQueryRequest request, String sql, SqlTree sqlTree, String logWhereSql) { + CQueryPlan(OrmQueryRequest request, String sql, SqlTreePlan sqlTree, String logWhereSql) { this.server = request.server(); this.dataTimeZone = server.dataTimeZone(); this.beanType = request.descriptor().type(); @@ -249,7 +249,7 @@ public class CQueryPlan implements SpiQueryPlan { return rawSql ? planKey.getPartialKey() + "_" + hash : planKey.getPartialKey(); } - final SqlTree getSqlTree() { + final SqlTreePlan getSqlTree() { return sqlTree; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanRawSql.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanRawSql.java index f35702ee1..935f730ec 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanRawSql.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanRawSql.java @@ -17,7 +17,7 @@ final class CQueryPlanRawSql extends CQueryPlan { private final int[] rsetIndexPositions; CQueryPlanRawSql(OrmQueryRequest request, SqlLimitResponse sqlRes, SqlTree sqlTree, String logWhereSql) { - super(request, sqlRes, sqlTree, true, logWhereSql); + super(request, sqlRes, sqlTree.plan(), true, logWhereSql); this.rsetIndexPositions = createIndexPositions(request, sqlTree); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java index 27e35c365..5b11be657 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java @@ -56,6 +56,10 @@ final class SqlTree { this.noJoins = !includeJoins; } + SqlTreePlan plan() { + return new SqlTreePlan(rootNode.createLoad(), manyProperty, encryptedProps, dependentTables()); + } + /** * Return true if the query mandates SQL Distinct due to ToMany inclusion. */ @@ -123,10 +127,6 @@ final class SqlTree { return inheritanceWhereSql; } - SqlTreeRoot getRootNode() { - return (SqlTreeRoot)rootNode; - } - /** * Return the property that is associated with the many. There can only be one * per SqlSelect. This can be null. @@ -135,10 +135,6 @@ final class SqlTree { return manyProperty; } - STreeProperty[] getEncryptedProps() { - return encryptedProps; - } - /** * Return true if the query has a many join. */ @@ -153,7 +149,7 @@ final class SqlTree { /** * Return the tables that are joined in this query. */ - Set dependentTables() { + private Set dependentTables() { Set tables = new LinkedHashSet<>(); rootNode.dependentTables(tables); return tables; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoad.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoad.java new file mode 100644 index 000000000..c5ac129b1 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoad.java @@ -0,0 +1,28 @@ +package io.ebeaninternal.server.query; + +import io.ebean.bean.EntityBean; +import io.ebean.core.type.ScalarDataReader; +import io.ebeaninternal.server.deploy.DbReadContext; + +import java.sql.SQLException; + +/** + * Tree node that loads an entity bean type. + */ +interface SqlTreeLoad { + + /** + * Load the appropriate information from the SqlSelectReader. + *

+ * At a high level this actually controls the reading of the data from the + * jdbc resultSet and putting it into the bean etc. + *

+ */ + EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean contextBean) throws SQLException; + + /** + * Return the reader for the single attribute query. + */ + ScalarDataReader getSingleAttributeReader(); + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadBean.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadBean.java new file mode 100644 index 000000000..516a00537 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadBean.java @@ -0,0 +1,373 @@ +package io.ebeaninternal.server.query; + +import io.ebean.bean.BeanCollection; +import io.ebean.bean.EntityBean; +import io.ebean.bean.EntityBeanIntercept; +import io.ebean.bean.PersistenceContext; +import io.ebean.core.type.ScalarDataReader; +import io.ebeaninternal.api.SpiQuery; +import io.ebeaninternal.api.SpiQuery.Mode; +import io.ebeaninternal.server.deploy.DbReadContext; +import io.ebeaninternal.server.deploy.InheritInfo; +import io.ebeaninternal.server.deploy.id.IdBinder; + +import java.sql.SQLException; +import java.util.Map; + +/** + * Normal bean included in the query. + */ +class SqlTreeLoadBean implements SqlTreeLoad { + + final STreeType desc; + final IdBinder idBinder; + final SqlTreeLoad[] children; + private final boolean partialObject; + private final STreeProperty[] properties; + private final STreePropertyAssoc nodeBeanProp; + final boolean readId; + private final boolean readIdNormal; + private final boolean disableLazyLoad; + private final InheritInfo inheritInfo; + final String prefix; + private final Map pathMap; + final STreePropertyAssocMany lazyLoadParent; + private final SpiQuery.TemporalMode temporalMode; + private final boolean temporalVersions; + final IdBinder lazyLoadParentIdBinder; + + SqlTreeLoadBean(SqlTreeNodeBean node) { + this.lazyLoadParent = node.lazyLoadParent; + this.lazyLoadParentIdBinder = node.lazyLoadParentIdBinder; + this.prefix = node.prefix; + this.desc = node.desc; + this.inheritInfo = desc.inheritInfo(); + this.idBinder = desc.idBinder(); + this.temporalMode = node.temporalMode; + this.temporalVersions = node.temporalVersions; + this.nodeBeanProp = node.nodeBeanProp; + this.readId = node.readId;//!aggregationRoot && withId && desc.hasId(); + this.readIdNormal = readId && !temporalVersions; + this.disableLazyLoad = node.disableLazyLoad;// disableLazyLoad || !readIdNormal || desc.isRawSqlBased(); + this.partialObject = node.partialObject;//props.isPartialObject(); + this.properties = node.properties;//props.getProps(); + this.pathMap = node.pathMap;//createPathMap(prefix, desc); + this.children = node.createLoadChildren(); + } + + boolean isRoot() { + return false; + } + + @Override + public final ScalarDataReader getSingleAttributeReader() { + if (properties == null || properties.length == 0) { + // if we have no property ask first children (in a distinct select with join) + if (children.length == 0) { + // expected to be a findIds query + return desc.idBinder().getBeanProperty(); + } + return children[0].getSingleAttributeReader(); + } + if (properties[0] instanceof STreePropertyAssocOne) { + STreePropertyAssocOne assocOne = (STreePropertyAssocOne)properties[0]; + if (assocOne.isAssocId()) { + return assocOne.idReader(); + } + } + return properties[0]; + } + + /** + * Load that takes into account inheritance. + */ + private final class LoadInherit extends Load { + + private LoadInherit(DbReadContext ctx, EntityBean parentBean) { + super(ctx, parentBean); + } + + @Override + void initBeanType() throws SQLException { + InheritInfo localInfo = readId ? inheritInfo.readType(ctx) : desc.inheritInfo(); + if (localInfo == null) { + // the bean must be null + localIdBinder = idBinder; + localDesc = desc; + } else { + localBean = localInfo.createEntityBean(); + localType = localInfo.getType(); + localIdBinder = localInfo.getIdBinder(); + localDesc = localInfo.desc(); + } + } + + @Override + void loadProperties() { + // take account of inheritance + for (STreeProperty property : properties) { + localDesc.inheritanceLoad(sqlBeanLoad, property, ctx); + } + } + } + + /** + * Load a bean instance. + */ + class Load { + + final DbReadContext ctx; + final EntityBean parentBean; + + Object lazyLoadParentId; + Class localType; + STreeType localDesc; + IdBinder localIdBinder; + EntityBean localBean; + + Mode queryMode; + PersistenceContext persistenceContext; + Object id; + EntityBean contextBean; + SqlBeanLoad sqlBeanLoad; + boolean lazyLoadMany; + + private Load(DbReadContext ctx, EntityBean parentBean) { + this.ctx = ctx; + this.parentBean = parentBean; + } + + private void initLazyParent() throws SQLException { + if (lazyLoadParentIdBinder != null) { + lazyLoadParentId = lazyLoadParentIdBinder.read(ctx); + } + } + + void initBeanType() throws SQLException { + localDesc = desc; + localBean = desc.createEntityBean(); + localIdBinder = idBinder; + } + + private void initPersistenceContext() { + queryMode = ctx.getQueryMode(); + persistenceContext = (!readIdNormal) ? null : ctx.getPersistenceContext(); + } + + private void readId() throws SQLException { + if (readId) { + id = localIdBinder.readSet(ctx, localBean); + if (id == null) { + readIdNullBean(); + } else if (!temporalVersions) { + readIdBean(); + } + } + } + + private void readIdBean() { + // check the PersistenceContext to see if the bean already exists + contextBean = (EntityBean) localDesc.contextPutIfAbsent(persistenceContext, id, localBean); + if (contextBean == null) { + // bean just added to the persistenceContext + contextBean = localBean; + } else { + // bean already exists in persistenceContext + + if (queryMode.isLoadContextBean()) { + // if explicitly set loadContextBean to true, then reload + localBean = contextBean; + } else if (!contextBean._ebean_getIntercept().isFullyLoadedBean()) { + // reload if contextBean is partial object + localBean = contextBean; + // and switch to lazyLoad query mode in order not to overwrite + // existing properties in SqlBeanLoad::load + queryMode = Mode.LAZYLOAD_BEAN; + } else { + // ignore the DB data... + localBean = null; + } + } + } + + private void readIdNullBean() { + // bean must be null... + localBean = null; + // ... but there may exist as reference bean in parent which has to be marked as deleted. + if (parentBean != null && nodeBeanProp instanceof STreePropertyAssocOne) { + contextBean = ((STreePropertyAssocOne)nodeBeanProp).getValueAsEntityBean(parentBean); + if (contextBean != null) { + desc.markAsDeleted(contextBean); + } + } + } + + private void initSqlLoadBean() { + ctx.setCurrentPrefix(prefix, pathMap); + ctx.propagateState(localBean); + sqlBeanLoad = new SqlBeanLoad(ctx, localType, localBean, queryMode); + } + + void loadProperties() { + for (STreeProperty property : properties) { + property.load(sqlBeanLoad); + } + } + + private void loadChildren() throws SQLException { + if (localBean == null && queryMode == Mode.LAZYLOAD_MANY) { + // batch lazy load many into existing contextBean + localBean = contextBean; + lazyLoadMany = true; + } + for (SqlTreeLoad child : children) { + child.load(ctx, localBean, contextBean); + } + } + + private boolean isLazyLoadManyRoot() { + return queryMode == Mode.LAZYLOAD_MANY && isRoot(); + } + + private EntityBean getContextBean() { + return contextBean; + } + + private void postLoad() { + if (!lazyLoadMany && localBean != null) { + ctx.setCurrentPrefix(prefix, pathMap); + if (readIdNormal) { + createListProxies(); + } + if (temporalMode == SpiQuery.TemporalMode.DRAFT) { + localDesc.setDraft(localBean); + } + localDesc.postLoad(localBean); + + EntityBeanIntercept ebi = localBean._ebean_getIntercept(); + ebi.setPersistenceContext(persistenceContext); + if (Mode.LAZYLOAD_BEAN == queryMode) { + // Lazy Load does not reset the dirty state + ebi.setLoadedLazy(); + } else if (readId) { + // normal bean loading + ebi.setLoaded(); + } + + if (disableLazyLoad) { + // bean does not have an Id or is SqlSelect based + ebi.setDisableLazyLoad(true); + } else if (partialObject) { + if (readId) { + // register for lazy loading + ctx.register(null, ebi); + } + } else { + ebi.setFullyLoadedBean(true); + } + + if (ctx.isAutoTuneProfiling() && !disableLazyLoad) { + // collect autoTune profiling for this bean... + ctx.profileBean(ebi, prefix); + } + } + } + + /** + * Create lazy loading proxies for the Many's except for the one that is + * included in the actual query. + */ + private void createListProxies() { + STreePropertyAssocMany fetchedMany = ctx.getManyProperty(); + boolean forceNewReference = queryMode == Mode.REFRESH_BEAN; + for (STreePropertyAssocMany many : localDesc.propsMany()) { + if (many != fetchedMany) { + // create a proxy for the many (deferred fetching) + BeanCollection ref = many.createReference(localBean, forceNewReference); + if (ref != null) { + if (disableLazyLoad) { + ref.setDisableLazyLoad(true); + } + if (!ref.isRegisteredWithLoadContext()) { + ctx.register(many.asMany(), ref); + } + } + } + } + } + + private void setBeanToParent() { + if (parentBean != null) { + // set this back to the parentBean + nodeBeanProp.setValue(parentBean, contextBean); + } + } + + private EntityBean complete() { + if (!readIdNormal) { + // a bean with no Id (never found in context) + if (lazyLoadParentId != null) { + ctx.setLazyLoadedChildBean(localBean, lazyLoadParentId); + } + return localBean; + } else { + if (lazyLoadParentId != null) { + ctx.setLazyLoadedChildBean(contextBean, lazyLoadParentId); + } + return contextBean; + } + } + + private void initialise() throws SQLException { + initLazyParent(); + initBeanType(); + initPersistenceContext(); + readId(); + initSqlLoadBean(); + loadProperties(); + loadChildren(); + } + + /** + * Perform the load returning the loaded bean. + */ + final EntityBean perform() throws SQLException { + initialise(); + if (isLazyLoadManyRoot()) { + return getContextBean(); + } + postLoad(); + setBeanToParent(); + return complete(); + } + + /** + * Return true if this bean was already in the context. If already in the + * context we need to check if it is already contained in the collection. + */ + final boolean isContextBean() { + return localBean == null; + } + } + + /** + * read the properties from the resultSet. + */ + @Override + public EntityBean load(DbReadContext ctx, EntityBean parentBean, EntityBean contextParent) throws SQLException { + return createLoad(ctx, parentBean).perform(); + } + + /** + * Create the loader with or without inheritance. + */ + final Load createLoad(DbReadContext ctx, EntityBean parentBean) { + return (inheritInfo != null) ? new LoadInherit(ctx, parentBean) : new Load(ctx, parentBean); + } + + @Override + public String toString() { + return "SqlTreeLoadBean: " + desc; + } + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadManyRoot.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadManyRoot.java new file mode 100644 index 000000000..3622e770e --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadManyRoot.java @@ -0,0 +1,30 @@ +package io.ebeaninternal.server.query; + +import io.ebean.bean.EntityBean; +import io.ebeaninternal.server.deploy.DbReadContext; + +import java.sql.SQLException; + +final class SqlTreeLoadManyRoot extends SqlTreeLoadBean { + + private final STreePropertyAssocMany manyProp; + + SqlTreeLoadManyRoot(SqlTreeNodeManyRoot node) { + super(node); + this.manyProp = node.manyProp; + } + + @Override + public EntityBean load(DbReadContext cquery, EntityBean parentBean, EntityBean contextParent) throws SQLException { + // pass in null for parentBean because added to a collection rather than set to the parentBean + Load load = createLoad(cquery, null); + EntityBean detailBean = load.perform(); + if (contextParent != null) { + // Add to the collection and initialise collection if needed + // A null detailBean may initialise an empty collection + // Check for bean existing in collection based on load.isContextBean() + manyProp.addBeanToCollectionWithCreate(contextParent, detailBean, load.isContextBean()); + } + return detailBean; + } +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadRoot.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadRoot.java new file mode 100644 index 000000000..d1792ecbe --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeLoadRoot.java @@ -0,0 +1,43 @@ +package io.ebeaninternal.server.query; + +import io.ebean.Version; +import io.ebean.bean.EntityBean; +import io.ebeaninternal.server.deploy.DbReadContext; + +import java.sql.SQLException; +import java.sql.Timestamp; + +/** + * Represents the root node of the Sql Tree. + */ +final class SqlTreeLoadRoot extends SqlTreeLoadBean implements SqlTreeRoot { + + SqlTreeLoadRoot(SqlTreeNodeBean node) { + super(node); + } + + @Override + protected boolean isRoot() { + return true; + } + + @Override + public EntityBean load(DbReadContext ctx) throws SQLException { + return load(ctx, null, null); + } + + /** + * Read the version bean. + */ + @Override + @SuppressWarnings("unchecked") + public Version loadVersion(DbReadContext ctx) throws SQLException { + // read the sys period lower and upper bounds + // these are always the first 2 columns in the resultSet + Timestamp start = ctx.getDataReader().getTimestamp(); + Timestamp end = ctx.getDataReader().getTimestamp(); + T bean = (T) load(ctx, null, null); + return new Version<>(bean, start, end); + } + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNode.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNode.java index 39844cb59..54632d9ca 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNode.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNode.java @@ -1,13 +1,8 @@ package io.ebeaninternal.server.query; -import io.ebean.Version; -import io.ebean.bean.EntityBean; -import io.ebean.core.type.ScalarDataReader; import io.ebeaninternal.api.SpiQuery; -import io.ebeaninternal.server.deploy.DbReadContext; import io.ebeaninternal.server.deploy.DbSqlContext; -import java.sql.SQLException; import java.util.List; import java.util.Set; @@ -64,25 +59,11 @@ interface SqlTreeNode { */ void addSoftDeletePredicate(SpiQuery query); - /** - * Load the appropriate information from the SqlSelectReader. - *

- * At a high level this actually controls the reading of the data from the - * jdbc resultSet and putting it into the bean etc. - *

- */ - EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean contextBean) throws SQLException; - /** * Return true if the query has a many join. */ boolean hasMany(); - /** - * Return the reader for the single attribute query. - */ - ScalarDataReader getSingleAttributeReader(); - /** * Return true if the query is known to only have a single property selected. */ @@ -92,4 +73,9 @@ interface SqlTreeNode { * Add dependent tables to the given set. */ void dependentTables(Set tables); + + /** + * Create the loader for this node. + */ + SqlTreeLoad createLoad(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java index 547eededc..4836fa3fd 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java @@ -1,30 +1,20 @@ package io.ebeaninternal.server.query; -import io.ebean.bean.BeanCollection; -import io.ebean.bean.EntityBean; -import io.ebean.bean.EntityBeanIntercept; -import io.ebean.bean.PersistenceContext; -import io.ebean.core.type.ScalarDataReader; import io.ebean.util.SplitName; import io.ebeaninternal.api.SpiQuery; -import io.ebeaninternal.api.SpiQuery.Mode; -import io.ebeaninternal.server.deploy.DbReadContext; import io.ebeaninternal.server.deploy.DbSqlContext; import io.ebeaninternal.server.deploy.InheritInfo; import io.ebeaninternal.server.deploy.TableJoin; import io.ebeaninternal.server.deploy.id.IdBinder; -import java.sql.SQLException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * Normal bean included in the query. */ class SqlTreeNodeBean implements SqlTreeNode { + private static final SqlTreeLoad[] NO_LOAD_CHILDREN = new SqlTreeLoad[0]; private static final SqlTreeNode[] NO_CHILDREN = new SqlTreeNode[0]; final STreeType desc; @@ -36,33 +26,33 @@ class SqlTreeNodeBean implements SqlTreeNode { /** * Set to true if this is a partial object fetch. */ - private final boolean partialObject; - private final STreeProperty[] properties; + final boolean partialObject; + final STreeProperty[] properties; /** * Extra where clause added by Where annotation on associated many. */ - private final String extraWhere; - private final STreePropertyAssoc nodeBeanProp; + final String extraWhere; + final STreePropertyAssoc nodeBeanProp; /** * False if report bean and has no id property. */ final boolean readId; - private final boolean readIdNormal; - private final boolean disableLazyLoad; - private final InheritInfo inheritInfo; + final boolean readIdNormal; + final boolean disableLazyLoad; + final InheritInfo inheritInfo; final String prefix; - private final Map pathMap; + final Map pathMap; final STreePropertyAssocMany lazyLoadParent; - private final SpiQuery.TemporalMode temporalMode; - private final boolean temporalVersions; - private final IdBinder lazyLoadParentIdBinder; + final SpiQuery.TemporalMode temporalMode; + final boolean temporalVersions; + final IdBinder lazyLoadParentIdBinder; String baseTableAlias; /** * Table alias set if this bean node includes a join to a intersection * table and that table has history support. */ - private boolean intersectionAsOfTableAlias; - private final boolean aggregation; + boolean intersectionAsOfTableAlias; + final boolean aggregation; /** * Construct for leaf node. @@ -108,8 +98,23 @@ class SqlTreeNodeBean implements SqlTreeNode { pathMap = createPathMap(prefix, desc); } - boolean isRoot() { - return false; + @Override + public SqlTreeLoad createLoad() { + return new SqlTreeLoadBean(this); + } + + protected SqlTreeLoad[] createLoadChildren() { + if (children.length == 0) { + return NO_LOAD_CHILDREN; + } + List loadChildren = new ArrayList<>(children.length); + for (SqlTreeNode child : children) { + SqlTreeLoad load = child.createLoad(); + if (load != null) { + loadChildren.add(load); + } + } + return loadChildren.toArray(new SqlTreeLoad[0]); } @Override @@ -117,25 +122,6 @@ class SqlTreeNodeBean implements SqlTreeNode { return properties != null && properties.length == 1 && children.length == 0; } - @Override - public final ScalarDataReader getSingleAttributeReader() { - if (properties == null || properties.length == 0) { - // if we have no property ask first children (in a distinct select with join) - if (children.length == 0) { - // expected to be a findIds query - return desc.idBinder().getBeanProperty(); - } - return children[0].getSingleAttributeReader(); - } - if (properties[0] instanceof STreePropertyAssocOne) { - STreePropertyAssocOne assocOne = (STreePropertyAssocOne)properties[0]; - if (assocOne.isAssocId()) { - return assocOne.idReader(); - } - } - return properties[0]; - } - private Map createPathMap(String prefix, STreeType desc) { HashMap m = new HashMap<>(); for (STreePropertyAssocMany many : desc.propsMany()) { @@ -173,293 +159,6 @@ class SqlTreeNodeBean implements SqlTreeNode { } } - /** - * Load that takes into account inheritance. - */ - private final class LoadInherit extends Load { - - private LoadInherit(DbReadContext ctx, EntityBean parentBean) { - super(ctx, parentBean); - } - - @Override - void initBeanType() throws SQLException { - InheritInfo localInfo = readId ? inheritInfo.readType(ctx) : desc.inheritInfo(); - if (localInfo == null) { - // the bean must be null - localIdBinder = idBinder; - localDesc = desc; - } else { - localBean = localInfo.createEntityBean(); - localType = localInfo.getType(); - localIdBinder = localInfo.getIdBinder(); - localDesc = localInfo.desc(); - } - } - - @Override - void loadProperties() { - // take account of inheritance - for (STreeProperty property : properties) { - localDesc.inheritanceLoad(sqlBeanLoad, property, ctx); - } - } - } - - /** - * Load a bean instance. - */ - class Load { - - final DbReadContext ctx; - final EntityBean parentBean; - - Object lazyLoadParentId; - Class localType; - STreeType localDesc; - IdBinder localIdBinder; - EntityBean localBean; - - Mode queryMode; - PersistenceContext persistenceContext; - Object id; - EntityBean contextBean; - SqlBeanLoad sqlBeanLoad; - boolean lazyLoadMany; - - private Load(DbReadContext ctx, EntityBean parentBean) { - this.ctx = ctx; - this.parentBean = parentBean; - } - - private void initLazyParent() throws SQLException { - if (lazyLoadParentIdBinder != null) { - lazyLoadParentId = lazyLoadParentIdBinder.read(ctx); - } - } - - void initBeanType() throws SQLException { - localDesc = desc; - localBean = desc.createEntityBean(); - localIdBinder = idBinder; - } - - private void initPersistenceContext() { - queryMode = ctx.getQueryMode(); - persistenceContext = (!readIdNormal) ? null : ctx.getPersistenceContext(); - } - - private void readId() throws SQLException { - if (readId) { - id = localIdBinder.readSet(ctx, localBean); - if (id == null) { - readIdNullBean(); - } else if (!temporalVersions) { - readIdBean(); - } - } - } - - private void readIdBean() { - // check the PersistenceContext to see if the bean already exists - contextBean = (EntityBean) localDesc.contextPutIfAbsent(persistenceContext, id, localBean); - if (contextBean == null) { - // bean just added to the persistenceContext - contextBean = localBean; - } else { - // bean already exists in persistenceContext - - if (queryMode.isLoadContextBean()) { - // if explicitly set loadContextBean to true, then reload - localBean = contextBean; - } else if (!contextBean._ebean_getIntercept().isFullyLoadedBean()) { - // reload if contextBean is partial object - localBean = contextBean; - // and switch to lazyLoad query mode in order not to overwrite - // existing properties in SqlBeanLoad::load - queryMode = Mode.LAZYLOAD_BEAN; - } else { - // ignore the DB data... - localBean = null; - } - } - } - - private void readIdNullBean() { - // bean must be null... - localBean = null; - // ... but there may exist as reference bean in parent which has to be marked as deleted. - if (parentBean != null && nodeBeanProp instanceof STreePropertyAssocOne) { - contextBean = ((STreePropertyAssocOne)nodeBeanProp).getValueAsEntityBean(parentBean); - if (contextBean != null) { - desc.markAsDeleted(contextBean); - } - } - } - - private void initSqlLoadBean() { - ctx.setCurrentPrefix(prefix, pathMap); - ctx.propagateState(localBean); - sqlBeanLoad = new SqlBeanLoad(ctx, localType, localBean, queryMode); - } - - void loadProperties() { - for (STreeProperty property : properties) { - property.load(sqlBeanLoad); - } - } - - private void loadChildren() throws SQLException { - if (localBean == null && queryMode == Mode.LAZYLOAD_MANY) { - // batch lazy load many into existing contextBean - localBean = contextBean; - lazyLoadMany = true; - } - for (SqlTreeNode child : children) { - child.load(ctx, localBean, contextBean); - } - } - - private boolean isLazyLoadManyRoot() { - return queryMode == Mode.LAZYLOAD_MANY && isRoot(); - } - - private EntityBean getContextBean() { - return contextBean; - } - - private void postLoad() { - if (!lazyLoadMany && localBean != null) { - ctx.setCurrentPrefix(prefix, pathMap); - if (readIdNormal) { - createListProxies(); - } - if (temporalMode == SpiQuery.TemporalMode.DRAFT) { - localDesc.setDraft(localBean); - } - localDesc.postLoad(localBean); - - EntityBeanIntercept ebi = localBean._ebean_getIntercept(); - ebi.setPersistenceContext(persistenceContext); - if (Mode.LAZYLOAD_BEAN == queryMode) { - // Lazy Load does not reset the dirty state - ebi.setLoadedLazy(); - } else if (readId) { - // normal bean loading - ebi.setLoaded(); - } - - if (disableLazyLoad) { - // bean does not have an Id or is SqlSelect based - ebi.setDisableLazyLoad(true); - } else if (partialObject) { - if (readId) { - // register for lazy loading - ctx.register(null, ebi); - } - } else { - ebi.setFullyLoadedBean(true); - } - - if (ctx.isAutoTuneProfiling() && !disableLazyLoad) { - // collect autoTune profiling for this bean... - ctx.profileBean(ebi, prefix); - } - } - } - - /** - * Create lazy loading proxies for the Many's except for the one that is - * included in the actual query. - */ - private void createListProxies() { - STreePropertyAssocMany fetchedMany = ctx.getManyProperty(); - boolean forceNewReference = queryMode == Mode.REFRESH_BEAN; - for (STreePropertyAssocMany many : localDesc.propsMany()) { - if (many != fetchedMany) { - // create a proxy for the many (deferred fetching) - BeanCollection ref = many.createReference(localBean, forceNewReference); - if (ref != null) { - if (disableLazyLoad) { - ref.setDisableLazyLoad(true); - } - if (!ref.isRegisteredWithLoadContext()) { - ctx.register(many.asMany(), ref); - } - } - } - } - } - - private void setBeanToParent() { - if (parentBean != null) { - // set this back to the parentBean - nodeBeanProp.setValue(parentBean, contextBean); - } - } - - private EntityBean complete() { - if (!readIdNormal) { - // a bean with no Id (never found in context) - if (lazyLoadParentId != null) { - ctx.setLazyLoadedChildBean(localBean, lazyLoadParentId); - } - return localBean; - } else { - if (lazyLoadParentId != null) { - ctx.setLazyLoadedChildBean(contextBean, lazyLoadParentId); - } - return contextBean; - } - } - - private void initialise() throws SQLException { - initLazyParent(); - initBeanType(); - initPersistenceContext(); - readId(); - initSqlLoadBean(); - loadProperties(); - loadChildren(); - } - - /** - * Perform the load returning the loaded bean. - */ - final EntityBean perform() throws SQLException { - initialise(); - if (isLazyLoadManyRoot()) { - return getContextBean(); - } - postLoad(); - setBeanToParent(); - return complete(); - } - - /** - * Return true if this bean was already in the context. If already in the - * context we need to check if it is already contained in the collection. - */ - final boolean isContextBean() { - return localBean == null; - } - } - - /** - * read the properties from the resultSet. - */ - @Override - public EntityBean load(DbReadContext ctx, EntityBean parentBean, EntityBean contextParent) throws SQLException { - return createLoad(ctx, parentBean).perform(); - } - - /** - * Create the loader with or without inheritance. - */ - final Load createLoad(DbReadContext ctx, EntityBean parentBean) { - return (inheritInfo != null) ? new LoadInherit(ctx, parentBean) : new Load(ctx, parentBean); - } - @Override public final void appendGroupBy(DbSqlContext ctx, boolean subQuery) { ctx.pushJoin(prefix); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java index e7ca5d9ab..42d2395b5 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java @@ -38,6 +38,11 @@ final class SqlTreeNodeExtraJoin implements SqlTreeNode { this.manyJoin = assocBeanProperty instanceof STreePropertyAssocMany; } + @Override + public SqlTreeLoad createLoad() { + return null; + } + @Override public boolean isSingleProperty() { return false; @@ -73,11 +78,6 @@ final class SqlTreeNodeExtraJoin implements SqlTreeNode { // nothing to do here } - @Override - public ScalarType getSingleAttributeReader() { - throw new IllegalStateException("No expected"); - } - /** * Return true if the extra join is a many join. *

@@ -184,14 +184,6 @@ final class SqlTreeNodeExtraJoin implements SqlTreeNode { public void appendWhere(DbSqlContext ctx) { } - /** - * Does nothing. - */ - @Override - public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) { - return null; - } - @Override public boolean hasMany() { return manyJoin; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeFormulaWhereJoin.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeFormulaWhereJoin.java index 06a71133a..502c28a6d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeFormulaWhereJoin.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeFormulaWhereJoin.java @@ -33,13 +33,13 @@ final class SqlTreeNodeFormulaWhereJoin implements SqlTreeNode { } @Override - public boolean isSingleProperty() { - return true; + public SqlTreeLoad createLoad() { + return null; } @Override - public ScalarType getSingleAttributeReader() { - throw new IllegalStateException("No expected"); + public boolean isSingleProperty() { + return true; } @Override @@ -98,12 +98,6 @@ final class SqlTreeNodeFormulaWhereJoin implements SqlTreeNode { // nothing to do here } - @Override - public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) { - // nothing to do here - return null; - } - @Override public boolean hasMany() { return true; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyRoot.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyRoot.java index c030e96bc..a8b323533 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyRoot.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyRoot.java @@ -1,16 +1,13 @@ package io.ebeaninternal.server.query; -import io.ebean.bean.EntityBean; import io.ebeaninternal.api.SpiQuery; -import io.ebeaninternal.server.deploy.DbReadContext; import io.ebeaninternal.server.deploy.DbSqlContext; -import java.sql.SQLException; import java.util.List; final class SqlTreeNodeManyRoot extends SqlTreeNodeBean { - private final STreePropertyAssocMany manyProp; + final STreePropertyAssocMany manyProp; SqlTreeNodeManyRoot(String prefix, STreePropertyAssocMany prop, SqlTreeProperties props, List myList, boolean withId, SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad) { @@ -19,22 +16,13 @@ final class SqlTreeNodeManyRoot extends SqlTreeNodeBean { } @Override - public boolean hasMany() { - return true; + public SqlTreeLoad createLoad() { + return new SqlTreeLoadManyRoot(this); } @Override - public EntityBean load(DbReadContext cquery, EntityBean parentBean, EntityBean contextParent) throws SQLException { - // pass in null for parentBean because added to a collection rather than set to the parentBean - SqlTreeNodeBean.Load load = createLoad(cquery, null); - EntityBean detailBean = load.perform(); - if (contextParent != null) { - // Add to the collection and initialise collection if needed - // A null detailBean may initialise an empty collection - // Check for bean existing in collection based on load.isContextBean() - manyProp.addBeanToCollectionWithCreate(contextParent, detailBean, load.isContextBean()); - } - return detailBean; + public boolean hasMany() { + return true; } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java index 73c7a18b2..cb2ba54e7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java @@ -1,10 +1,7 @@ package io.ebeaninternal.server.query; -import io.ebean.bean.EntityBean; -import io.ebean.core.type.ScalarType; import io.ebean.util.SplitName; import io.ebeaninternal.api.SpiQuery; -import io.ebeaninternal.server.deploy.DbReadContext; import io.ebeaninternal.server.deploy.DbSqlContext; import io.ebeaninternal.server.deploy.TableJoin; @@ -37,13 +34,13 @@ final class SqlTreeNodeManyWhereJoin implements SqlTreeNode { } @Override - public boolean isSingleProperty() { - return false; + public SqlTreeLoad createLoad() { + return null; } @Override - public ScalarType getSingleAttributeReader() { - throw new IllegalStateException("No expected"); + public boolean isSingleProperty() { + return false; } @Override @@ -132,12 +129,6 @@ final class SqlTreeNodeManyWhereJoin implements SqlTreeNode { // nothing to do here } - @Override - public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) { - // nothing to do here - return null; - } - @Override public boolean hasMany() { return true; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeRoot.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeRoot.java index a58c51cc5..c25024624 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeRoot.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeRoot.java @@ -1,21 +1,16 @@ package io.ebeaninternal.server.query; -import io.ebean.Version; -import io.ebean.bean.EntityBean; import io.ebeaninternal.api.SpiQuery; -import io.ebeaninternal.server.deploy.DbReadContext; import io.ebeaninternal.server.deploy.DbSqlContext; import io.ebeaninternal.server.deploy.TableJoin; -import java.sql.SQLException; -import java.sql.Timestamp; import java.util.List; import java.util.Set; /** * Represents the root node of the Sql Tree. */ -final class SqlTreeNodeRoot extends SqlTreeNodeBean implements SqlTreeRoot { +final class SqlTreeNodeRoot extends SqlTreeNodeBean { private final TableJoin includeJoin; private final boolean sqlDistinct; @@ -34,27 +29,8 @@ final class SqlTreeNodeRoot extends SqlTreeNodeBean implements SqlTreeRoot { } @Override - protected boolean isRoot() { - return true; - } - - @Override - public EntityBean load(DbReadContext ctx) throws SQLException { - return load(ctx, null, null); - } - - /** - * Read the version bean. - */ - @Override - @SuppressWarnings("unchecked") - public Version loadVersion(DbReadContext ctx) throws SQLException { - // read the sys period lower and upper bounds - // these are always the first 2 columns in the resultSet - Timestamp start = ctx.getDataReader().getTimestamp(); - Timestamp end = ctx.getDataReader().getTimestamp(); - T bean = (T) load(ctx, null, null); - return new Version<>(bean, start, end); + public SqlTreeLoad createLoad() { + return new SqlTreeLoadRoot(this); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreePlan.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreePlan.java new file mode 100644 index 000000000..7a0e929ef --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreePlan.java @@ -0,0 +1,41 @@ +package io.ebeaninternal.server.query; + +import java.util.Set; + +final class SqlTreePlan { + + private final SqlTreeLoad rootNode; + private final STreePropertyAssocMany manyProperty; + private final STreeProperty[] encryptedProps; + private final Set dependentTables; + + SqlTreePlan(SqlTreeLoad rootNode, STreePropertyAssocMany manyProperty, STreeProperty[] encryptedProps, Set dependentTables) { + this.rootNode = rootNode; + this.manyProperty = manyProperty; + this.encryptedProps = encryptedProps; + this.dependentTables = dependentTables; + } + + SqlTreeRoot getRootNode() { + return (SqlTreeRoot)rootNode; + } + + /** + * Return the property that is associated with the many. There can only be one + * per SqlSelect. This can be null. + */ + STreePropertyAssocMany getManyProperty() { + return manyProperty; + } + + STreeProperty[] getEncryptedProps() { + return encryptedProps; + } + + /** + * Return the tables that are joined in this query. + */ + Set dependentTables() { + return dependentTables; + } +} From 17f7be72f46da3b0e928ec3bdfa4d5706487dc14 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 26 Jan 2022 21:54:19 +1300 Subject: [PATCH 13/13] Touch README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 30dc2c06b..a560d8c82 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,3 @@ Goto [https://ebean.io/docs/](https://ebean.io/docs/) [Maven central - all related projects](http://search.maven.org/#search%7Cga%7C1%7Cebean "maven central all related projects") -## Other versions -* [![Maven Central : ebean-agent](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent) - ebean-agent -* [![Maven Central : ebean-maven-plugin](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin) - ebean-maven-plugin