diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bf03d3b50..5e3fafdd4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,6 +31,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 package + run: mvn clean package 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 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-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; + } +} 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 e5c80afb8..2d210584d 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/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..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,13 +6,20 @@ 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; 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() @@ -22,7 +29,6 @@ public class TestBatchSaveWithGetBeanId extends BaseTestCase { @Test @IgnorePlatform(Platform.HANA) // HANA doesn't support insert batching public void test() { - Database server = DB.getDefault(); Customer model = new Customer(); model.setName("foo"); 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/model/basic/ResetBasicData.java b/ebean-test/src/test/java/org/tests/model/basic/ResetBasicData.java index 6a01db649..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 @@ -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(); @@ -56,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); @@ -157,7 +114,6 @@ public class ResetBasicData { } public static Order createOrderCustAndOrder(String custName) { - ResetBasicData me = new ResetBasicData(); Customer cust1 = insertCustomer(custName); return me.createOrder1(cust1); @@ -166,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")); @@ -185,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; } @@ -216,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); @@ -251,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")); @@ -271,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(); @@ -291,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); @@ -312,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); } } 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() { 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 ab8706f74..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 @@ -19,7 +19,7 @@ 11 11 - 12.13.0 + 12.14.1 1.6.0 2.12.1 true