From dc65475dbbca9a703f71606688538957ba81766d Mon Sep 17 00:00:00 2001 From: robin Date: Tue, 16 Feb 2021 17:47:56 +1300 Subject: [PATCH 01/31] #2168 - Using ebean-test with redis container - hanging testing redis connectivity --- ebean-test/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml index d880773a8..92f4c590f 100644 --- a/ebean-test/pom.xml +++ b/ebean-test/pom.xml @@ -63,7 +63,7 @@ io.ebean ebean-test-docker - 4.1 + 4.2 From 98a89156bf14c970c5b6b7ba293e6838848cbe19 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Tue, 16 Feb 2021 17:51:47 +1300 Subject: [PATCH 02/31] Refactor query internals to reduce property parsing for partial select and fetch (#2167) * Update test only - update TestBasicClobNoVer to use DB and AssertJ * Refactor OrmQueryProperties internals to reduce raw property parsing - Remove String properties - Reduces parsing of properties via fetchProperties() selectProperties() methods * No effective change - tidy pom.xml for ebean-querybean * Refactor query internals adding fetchProperties() selectProperties() - Adds SpiQueryFetch - Used by query beans with Set properties passed (effectively skipping any parsing) --- .../java/io/ebeaninternal/api/SpiQuery.java | 13 +++- .../io/ebeaninternal/api/SpiQueryFetch.java | 22 ++++++ .../server/query/DFetchGroupBuilder.java | 15 ++-- .../server/query/DefaultFetchGroupQuery.java | 13 +++- .../server/querydefn/DefaultOrmQuery.java | 20 +++++ .../server/querydefn/OrmQueryDetail.java | 35 +++++++-- .../server/querydefn/OrmQueryProperties.java | 60 ++++++++------- .../querydefn/OrmQueryPropertiesParser.java | 53 +++---------- .../OrmQueryPropertiesParserTest.java | 12 ++- .../querydefn/OrmQueryPropertiesTest.java | 11 +-- .../tests/basic/lob/TestBasicClobNoVer.java | 35 ++++----- ebean-querybean/pom.xml | 17 ++--- .../java/io/ebean/typequery/TQAssocBean.java | 49 +++++++----- .../java/io/ebean/typequery/TQRootBean.java | 23 +++--- .../test/java/org/querytest/QOrderTest.java | 74 ++++++++++++++++++- 15 files changed, 297 insertions(+), 155 deletions(-) create mode 100644 ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryFetch.java diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java index f3b133bc2..4686a3644 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQuery.java @@ -20,6 +20,7 @@ import io.ebeaninternal.server.deploy.TableJoin; import io.ebeaninternal.server.query.CancelableQuery; import io.ebeaninternal.server.querydefn.NaturalKeyBindParam; import io.ebeaninternal.server.querydefn.OrmQueryDetail; +import io.ebeaninternal.server.querydefn.OrmQueryProperties; import io.ebeaninternal.server.querydefn.OrmUpdateProperties; import io.ebeaninternal.server.rawsql.SpiRawSql; @@ -30,7 +31,7 @@ import java.util.Set; /** * Object Relational query - Internal extension to Query object. */ -public interface SpiQuery extends Query, TxnProfileEventCodes { +public interface SpiQuery extends Query, SpiQueryFetch, TxnProfileEventCodes { enum Mode { NORMAL(false), LAZYLOAD_MANY(false), LAZYLOAD_BEAN(true), REFRESH_BEAN(true); @@ -289,6 +290,16 @@ public interface SpiQuery extends Query, TxnProfileEventCodes { */ boolean selectAllForLazyLoadProperty(); + /** + * Set the select properties. + */ + void selectProperties(OrmQueryProperties other); + + /** + * Set the fetch properties for the given path. + */ + void fetchProperties(String path, OrmQueryProperties other); + /** * Set the on a secondary query given the label, relativePath and profile location of the parent query. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryFetch.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryFetch.java new file mode 100644 index 000000000..396827978 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryFetch.java @@ -0,0 +1,22 @@ +package io.ebeaninternal.api; + +import io.ebean.FetchConfig; + +import java.util.Set; + +/** + * Query select and fetch properties (that avoids parsing). + */ +public interface SpiQueryFetch { + + /** + * Specify the select properties. + */ + void selectProperties(Set properties); + + /** + * Specify the fetch properties for the given path. + */ + void fetchProperties(String name, Set properties, FetchConfig config); + +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/DFetchGroupBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/DFetchGroupBuilder.java index f33985f46..6553b7ea4 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/DFetchGroupBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/DFetchGroupBuilder.java @@ -11,6 +11,8 @@ import io.ebeaninternal.server.querydefn.SpiFetchGroup; */ class DFetchGroupBuilder implements FetchGroupBuilder { + private static final FetchConfig DEFAULT_FETCH = FetchConfig.ofDefault(); + private static final FetchConfig FETCH_CACHE = FetchConfig.ofCache(); private static final FetchConfig FETCH_QUERY = FetchConfig.ofQuery(); @@ -31,13 +33,13 @@ class DFetchGroupBuilder implements FetchGroupBuilder { @Override public FetchGroupBuilder fetch(String path) { - detail.fetch(path, null, null); + detail.fetchProperties(path, null, DEFAULT_FETCH); return this; } @Override public FetchGroupBuilder fetch(String path, FetchGroup nestedGroup) { - return fetchNested(path, nestedGroup, null); + return fetchNested(path, nestedGroup, DEFAULT_FETCH); } @Override @@ -51,7 +53,6 @@ class DFetchGroupBuilder implements FetchGroupBuilder { } private FetchGroupBuilder fetchNested(String path, FetchGroup nestedGroup, FetchConfig fetchConfig) { - OrmQueryDetail nestedDetail = ((SpiFetchGroup) nestedGroup).underlying(); detail.addNested(path, nestedDetail, fetchConfig); return this; @@ -59,25 +60,25 @@ class DFetchGroupBuilder implements FetchGroupBuilder { @Override public FetchGroupBuilder fetchQuery(String path) { - detail.fetch(path, null, FETCH_QUERY); + detail.fetchProperties(path, null, FETCH_QUERY); return this; } @Override public FetchGroupBuilder fetchCache(String path) { - detail.fetch(path, null, FETCH_CACHE); + detail.fetchProperties(path, null, FETCH_CACHE); return this; } @Override public FetchGroupBuilder fetchLazy(String path) { - detail.fetch(path, null, FETCH_LAZY); + detail.fetchProperties(path, null, FETCH_LAZY); return this; } @Override public FetchGroupBuilder fetch(String path, String properties) { - detail.fetch(path, properties, null); + detail.fetch(path, properties, DEFAULT_FETCH); return this; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultFetchGroupQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultFetchGroupQuery.java index f7bfdadf1..5da152185 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultFetchGroupQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/DefaultFetchGroupQuery.java @@ -25,6 +25,7 @@ import io.ebean.Transaction; import io.ebean.UpdateQuery; import io.ebean.Version; import io.ebean.service.SpiFetchGroupQuery; +import io.ebeaninternal.api.SpiQueryFetch; import io.ebeaninternal.server.querydefn.OrmQueryDetail; import io.ebeaninternal.server.querydefn.SpiFetchGroup; @@ -43,7 +44,7 @@ import java.util.stream.Stream; /** * Implementation of FetchGroup query for use to create FetchGroup via query beans. */ -class DefaultFetchGroupQuery implements SpiFetchGroupQuery { +class DefaultFetchGroupQuery implements SpiFetchGroupQuery, SpiQueryFetch { private static final FetchConfig FETCH_CACHE = FetchConfig.ofCache(); @@ -628,4 +629,14 @@ class DefaultFetchGroupQuery implements SpiFetchGroupQuery { public Query orderById(boolean orderById) { throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup"); } + + @Override + public void selectProperties(Set props) { + detail.selectProperties(props); + } + + @Override + public void fetchProperties(String property, Set columns, FetchConfig config) { + detail.fetchProperties(property, columns, config); + } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index d625e87be..e19212e96 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1392,6 +1392,26 @@ public class DefaultOrmQuery implements SpiQuery { return this; } + @Override + public void selectProperties(Set props) { + detail.selectProperties(props); + } + + @Override + public void fetchProperties(String property, Set columns, FetchConfig config) { + detail.fetchProperties(property, columns, config); + } + + @Override + public void selectProperties(OrmQueryProperties properties) { + detail.selectProperties(properties); + } + + @Override + public void fetchProperties(String path, OrmQueryProperties other) { + detail.fetchProperties(path, other); + } + @Override public DefaultOrmQuery select(String columns) { detail.select(columns); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java index b8837111e..1c441cbf4 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java @@ -59,9 +59,9 @@ public class OrmQueryDetail implements Serializable { * Add a nested OrmQueryDetail to this detail. */ public void addNested(String path, OrmQueryDetail other, FetchConfig config) { - fetch(path, other.baseProps.getProperties(), config); + fetchProperties(path, other.baseProps, config); for (Map.Entry entry : other.fetchPaths.entrySet()) { - fetch(path + "." + entry.getKey(), entry.getValue().getProperties(), entry.getValue().getFetchConfig()); + fetchProperties(path + "." + entry.getKey(), entry.getValue(), entry.getValue().getFetchConfig()); } } @@ -133,8 +133,19 @@ public class OrmQueryDetail implements Serializable { /** * set the properties to include on the base / root entity. */ - public void select(String columns) { - baseProps = new OrmQueryProperties(null, columns, null); + public void select(String properties) { + baseProps = new OrmQueryProperties(null, properties, null); + } + + /** + * Set select properties that are already parsed. + */ + public void selectProperties(Set properties) { + baseProps = new OrmQueryProperties(null, properties, OrmQueryProperties.DEFAULT_FETCH); + } + + void selectProperties(OrmQueryProperties other) { + baseProps = new OrmQueryProperties(null, other, OrmQueryProperties.DEFAULT_FETCH); } boolean containsProperty(String property) { @@ -262,10 +273,24 @@ public class OrmQueryDetail implements Serializable { * @param partialProps the properties on the join property to include */ public void fetch(String path, String partialProps, FetchConfig fetchConfig) { - fetch(new OrmQueryProperties(path, partialProps, fetchConfig)); } + /** + * Set fetch properties that are already parsed. + */ + public void fetchProperties(String path, Set properties, FetchConfig fetchConfig) { + fetch(new OrmQueryProperties(path, properties, fetchConfig)); + } + + void fetchProperties(String path, OrmQueryProperties other) { + fetchProperties(path, other, other.getFetchConfig()); + } + + void fetchProperties(String path, OrmQueryProperties other, FetchConfig fetchConfig) { + fetch(new OrmQueryProperties(path, other, fetchConfig)); + } + /** * Add for raw sql etc when the properties are already parsed into a set. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryProperties.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryProperties.java index 704cead7f..e84828f09 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryProperties.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryProperties.java @@ -30,7 +30,7 @@ public class OrmQueryProperties implements Serializable { private final String parentPath; private final String path; - private final String properties; + private final boolean allProperties; private final Set included; private final FetchConfig fetchConfig; private final boolean cache; @@ -77,7 +77,7 @@ public class OrmQueryProperties implements Serializable { public OrmQueryProperties(String path) { this.path = path; this.parentPath = SplitName.parent(path); - this.properties = null; + this.allProperties = false; this.included = null; this.cache = false; this.fetchConfig = DEFAULT_FETCH; @@ -91,7 +91,7 @@ public class OrmQueryProperties implements Serializable { this.path = path; this.parentPath = SplitName.parent(path); OrmQueryPropertiesParser.Response response = OrmQueryPropertiesParser.parse(rawProperties); - this.properties = response.properties; + this.allProperties = response.allProperties; this.included = response.included; if (fetchConfig != null) { this.fetchConfig = fetchConfig; @@ -103,12 +103,25 @@ public class OrmQueryProperties implements Serializable { } public OrmQueryProperties(String path, Set included) { + this(path, included, DEFAULT_FETCH); + } + + OrmQueryProperties(String path, Set included, FetchConfig fetchConfig) { this.path = path; this.parentPath = SplitName.parent(path); this.included = included; - this.properties = String.join(",", included); - this.cache = false; - this.fetchConfig = DEFAULT_FETCH; + this.allProperties = false; + this.fetchConfig = fetchConfig; + this.cache = fetchConfig.isCache(); + } + + OrmQueryProperties(String path, OrmQueryProperties other, FetchConfig fetchConfig) { + this.path = path; + this.parentPath = SplitName.parent(path); + this.allProperties = other.allProperties; + this.included = other.included; + this.cache = other.cache; + this.fetchConfig = fetchConfig; } /** @@ -118,7 +131,7 @@ public class OrmQueryProperties implements Serializable { this.fetchConfig = sourceFetchConfig; this.parentPath = source.parentPath; this.path = source.path; - this.properties = source.properties; + this.allProperties = source.allProperties; this.cache = source.cache; this.filterMany = source.filterMany; this.markForQueryJoin = source.markForQueryJoin; @@ -158,7 +171,7 @@ public class OrmQueryProperties implements Serializable { * Return the expressions used to filter on this path. This should be a many path to use this * method. */ - @SuppressWarnings({"rawtypes","unchecked"}) + @SuppressWarnings({"rawtypes", "unchecked"}) public SpiExpressionList filterMany(Query rootQuery) { if (filterMany == null) { FilterExprPath exprPath = new FilterExprPath(path); @@ -201,9 +214,8 @@ public class OrmQueryProperties implements Serializable { */ @SuppressWarnings("unchecked") public void configureBeanQuery(SpiQuery query) { - - if (properties != null && !properties.isEmpty()) { - query.select(properties); + if (!isEmpty()) { + query.selectProperties(this); } if (filterMany != null) { @@ -219,7 +231,7 @@ public class OrmQueryProperties implements Serializable { for (OrmQueryProperties p : secondaryChildren) { String path = p.getPath(); path = path.substring(trimPath); - query.fetch(path, p.getProperties(), p.getFetchConfig()); + query.fetchProperties(path, p); query.setFilterMany(path, p.getFilterManyTrimPath(trimPath)); } } @@ -230,8 +242,7 @@ public class OrmQueryProperties implements Serializable { } public boolean hasSelectClause() { - if ("*".equals(properties)) { - // explicitly selected all properties + if (allProperties) { return true; } // explicitly selected some properties @@ -241,8 +252,8 @@ public class OrmQueryProperties implements Serializable { /** * Return true if the properties and configuration are empty. */ - public boolean isEmpty() { - return properties == null || properties.isEmpty(); + boolean isEmpty() { + return !allProperties && included == null; } public void asStringDebug(String prefix, StringBuilder sb) { @@ -250,8 +261,10 @@ public class OrmQueryProperties implements Serializable { if (path != null) { sb.append(path).append(" "); } - if (!isEmpty()) { - sb.append("(").append(properties).append(")"); + if (allProperties) { + sb.append("(*)"); + } else if (included != null) { + sb.append("(").append(String.join(",", included)).append(")"); } } @@ -269,17 +282,11 @@ public class OrmQueryProperties implements Serializable { secondaryChildren.add(child); } - /** - * Return the raw properties. - */ - public String getProperties() { - return properties; - } - /** * Return true if this includes all properties on the path. */ public boolean allProperties() { + // this is really "default" properties return included == null; } @@ -326,7 +333,6 @@ public class OrmQueryProperties implements Serializable { if (includedBeanJoin != null && includedBeanJoin.contains(propName)) { return false; } - // all properties included return included == null || included.contains(propName); } @@ -403,7 +409,7 @@ public class OrmQueryProperties implements Serializable { if (path != null) { builder.append(path); } - if (included != null){ + if (included != null) { builder.append("/i").append(included); } if (secondaryQueryJoins != null) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryPropertiesParser.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryPropertiesParser.java index f6f925891..568a6165c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryPropertiesParser.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryPropertiesParser.java @@ -7,72 +7,43 @@ import java.util.Set; /** * Parses the path properties string. */ -class OrmQueryPropertiesParser { +final class OrmQueryPropertiesParser { - private static final Response EMPTY = new Response(); + private static final Response EMPTY = new Response(false, null); + private static final Response ALL = new Response(true, null); /** * Immutable response of the parsed properties and options. */ static class Response { - final String properties; + final boolean allProperties; final Set included; - private Response(String properties, Set included) { - this.properties = properties; + private Response(boolean allProperties, Set included) { + this.allProperties = allProperties; this.included = included; } - - private Response() { - this.properties = ""; - this.included = null; - } } /** * Parses the path properties string returning the parsed properties and options. * In general it is comma delimited with some special strings like +lazy(20). */ - public static Response parse(String rawProperties) { - return new OrmQueryPropertiesParser(rawProperties).parse(); - } - - private final String inputProperties; - private boolean allProperties; - - private OrmQueryPropertiesParser(String inputProperties) { - this.inputProperties = inputProperties; - } - - /** - * Parse the raw string properties input. - */ - private Response parse() { - if (inputProperties == null || inputProperties.isEmpty()) { + static Response parse(String rawProperties) { + if (rawProperties == null || rawProperties.isEmpty()) { return EMPTY; } - if (inputProperties.equals("*")) { - return new Response("*", null); + if (rawProperties.equals("*")) { + return ALL; } - Set fields = splitRawSelect(inputProperties); - for (String val : fields) { - if (val.equals("*")) { - allProperties = true; - break; - } - } - String properties = allProperties ? "*" : inputProperties; - if (fields.isEmpty()) { - fields = null; - } - return new Response(properties, fields); + return new Response(false, splitRawSelect(rawProperties)); } /** * Split allowing 'dynamic function based properties'. */ - private Set splitRawSelect(String inputProperties) { + private static Set splitRawSelect(String inputProperties) { return DSelectColumnsParser.parse(inputProperties); } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/OrmQueryPropertiesParserTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/OrmQueryPropertiesParserTest.java index 5160c664e..a7c103b83 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/OrmQueryPropertiesParserTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/OrmQueryPropertiesParserTest.java @@ -11,7 +11,8 @@ public class OrmQueryPropertiesParserTest { OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse(null); assertAllDefaults(res); - assertThat(res.properties).isEqualTo(""); + assertThat(res.allProperties).isFalse(); + assertThat(res.included).isNull(); } @Test @@ -19,7 +20,8 @@ public class OrmQueryPropertiesParserTest { OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse(""); assertAllDefaults(res); - assertThat(res.properties).isEqualTo(""); + assertThat(res.allProperties).isFalse(); + assertThat(res.included).isNull(); } @Test @@ -27,13 +29,15 @@ public class OrmQueryPropertiesParserTest { OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("*"); assertAllDefaults(res); - assertThat(res.properties).isEqualTo("*"); + assertThat(res.allProperties).isTrue(); + assertThat(res.included).isNull(); } @Test public void when_no_spaces() { OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("id,name"); + assertThat(res.allProperties).isFalse(); assertThat(res.included).containsExactly("id", "name"); } @@ -41,6 +45,7 @@ public class OrmQueryPropertiesParserTest { public void when_spaced() { OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("id, name"); + assertThat(res.allProperties).isFalse(); assertThat(res.included).containsExactly("id", "name"); } @@ -48,6 +53,7 @@ public class OrmQueryPropertiesParserTest { public void when_formula() { OrmQueryPropertiesParser.Response res = OrmQueryPropertiesParser.parse("a,MD5(id::text) as b,c"); + assertThat(res.allProperties).isFalse(); assertThat(res.included).containsExactly("a", "MD5(id::text) as b", "c"); } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/OrmQueryPropertiesTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/OrmQueryPropertiesTest.java index b2ed90fcb..8df439a58 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/OrmQueryPropertiesTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/OrmQueryPropertiesTest.java @@ -14,17 +14,12 @@ public class OrmQueryPropertiesTest { return sb.toString(); } - @Test(expected = NullPointerException.class) - public void construct_with_propertySet_when_null() { - new OrmQueryProperties(null, (LinkedHashSet) null); - } - @Test public void construct_with_propertySet_when_empty() { OrmQueryProperties p1 = new OrmQueryProperties(null, new LinkedHashSet<>()); - assertThat(p1.getProperties()).isEqualTo(""); assertThat(p1.allProperties()).isFalse(); + assertThat(p1.getIncluded()).isEmpty(); } @Test @@ -34,7 +29,7 @@ public class OrmQueryPropertiesTest { set.add("name"); OrmQueryProperties p1 = new OrmQueryProperties(null, set); - assertThat(p1.getProperties()).isEqualTo("name"); + assertThat(p1.getIncluded()).containsOnly("name"); assertThat(p1.allProperties()).isFalse(); } @@ -47,7 +42,7 @@ public class OrmQueryPropertiesTest { set.add("startDate"); OrmQueryProperties p1 = new OrmQueryProperties(null, set); - assertThat(p1.getProperties()).isEqualTo("id,name,startDate"); + assertThat(p1.getIncluded()).containsOnly("id", "name", "startDate"); assertThat(p1.allProperties()).isFalse(); } diff --git a/ebean-core/src/test/java/org/tests/basic/lob/TestBasicClobNoVer.java b/ebean-core/src/test/java/org/tests/basic/lob/TestBasicClobNoVer.java index 0d791db50..f33a42a42 100644 --- a/ebean-core/src/test/java/org/tests/basic/lob/TestBasicClobNoVer.java +++ b/ebean-core/src/test/java/org/tests/basic/lob/TestBasicClobNoVer.java @@ -1,12 +1,10 @@ package org.tests.basic.lob; import io.ebean.BaseTestCase; -import io.ebean.Ebean; -import io.ebean.EbeanServer; +import io.ebean.DB; import io.ebean.Query; import org.tests.model.basic.EBasicClobNoVer; import org.ebeantest.LoggedSqlCollector; -import org.junit.Assert; import org.junit.Test; import java.util.List; @@ -21,53 +19,50 @@ public class TestBasicClobNoVer extends BaseTestCase { EBasicClobNoVer entity = new EBasicClobNoVer(); entity.setName("test"); entity.setDescription("initialClobValue"); - EbeanServer server = Ebean.getServer(null); - server.save(entity); - - - String sqlNoClob = "select t0.id, t0.name from ebasic_clob_no_ver t0 where t0.id = ?"; - String sqlWithClob = "select t0.id, t0.name, t0.description from ebasic_clob_no_ver t0 where t0.id = ?"; - + DB.save(entity); // Clob by default is Fetch Lazy - Query defaultQuery = Ebean.find(EBasicClobNoVer.class).setId(entity.getId()); + Query defaultQuery = DB.find(EBasicClobNoVer.class).setId(entity.getId()); defaultQuery.findOne(); String sql = sqlOf(defaultQuery, 2); - Assert.assertTrue("Clob is fetch lazy by default", sql.contains(sqlNoClob)); + // default SQL select excludes clob + String sqlNoClob = "select t0.id, t0.name from ebasic_clob_no_ver t0 where t0.id = ?"; + assertThat(sql).contains(sqlNoClob); // Explicitly select * including Clob - Query explicitQuery = Ebean.find(EBasicClobNoVer.class).setId(entity.getId()).select("*"); + Query explicitQuery = DB.find(EBasicClobNoVer.class).setId(entity.getId()).select("*"); explicitQuery.findOne(); sql = sqlOf(explicitQuery, 2); - Assert.assertTrue("Explicitly include Clob", sql.contains(sqlWithClob)); + // Explicitly include Clob + String sqlWithClob = "select t0.id, t0.name, t0.description from ebasic_clob_no_ver t0 where t0.id = ?"; + assertThat(sql).contains(sqlWithClob); // Update description to test refresh EBasicClobNoVer updateBean = new EBasicClobNoVer(); updateBean.setId(entity.getId()); updateBean.setDescription("modified"); - Ebean.update(updateBean); + DB.update(updateBean); // Test refresh function - Assert.assertEquals("initialClobValue", entity.getDescription()); + assertThat(entity.getDescription()).isEqualTo("initialClobValue"); LoggedSqlCollector.start(); // Refresh query includes all properties - server.refresh(entity); + DB.refresh(entity); // Assert all properties fetched in refresh List loggedSql = LoggedSqlCollector.stop(); - Assert.assertEquals(1, loggedSql.size()); + assertThat(loggedSql).hasSize(1); assertThat(trimSql(loggedSql.get(0), 2)).contains(sqlWithClob); - Assert.assertEquals("modified", entity.getDescription()); - + assertThat(entity.getDescription()).isEqualTo("modified"); } } diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml index 2fe2570a4..0bdeeb257 100644 --- a/ebean-querybean/pom.xml +++ b/ebean-querybean/pom.xml @@ -6,16 +6,6 @@ io.ebean 12.7.1-SNAPSHOT - - - - - - - - scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.6.5 - ebean querybean Ebean querybean support @@ -78,6 +68,13 @@ test + + io.ebean + ebean-test + 12.7.1-SNAPSHOT + test + + org.avaje.composite junit diff --git a/ebean-querybean/src/main/java/io/ebean/typequery/TQAssocBean.java b/ebean-querybean/src/main/java/io/ebean/typequery/TQAssocBean.java index 9f56b6b13..c54862151 100644 --- a/ebean-querybean/src/main/java/io/ebean/typequery/TQAssocBean.java +++ b/ebean-querybean/src/main/java/io/ebean/typequery/TQAssocBean.java @@ -1,6 +1,11 @@ package io.ebean.typequery; import io.ebean.ExpressionList; +import io.ebean.FetchConfig; +import io.ebeaninternal.api.SpiQueryFetch; + +import java.util.LinkedHashSet; +import java.util.Set; /** * Base type for associated beans. @@ -11,6 +16,11 @@ import io.ebean.ExpressionList; @SuppressWarnings("rawtypes") public abstract class TQAssocBean extends TQProperty { + private static final FetchConfig FETCH_DEFAULT = FetchConfig.ofDefault(); + private static final FetchConfig FETCH_QUERY = FetchConfig.ofQuery(); + private static final FetchConfig FETCH_LAZY = FetchConfig.ofLazy(); + private static final FetchConfig FETCH_CACHE = FetchConfig.ofCache(); + /** * Construct with a property name and root instance. * @@ -88,9 +98,8 @@ public abstract class TQAssocBean extends TQProperty { /** * Deprecated in favor of fetch(). - * - * @deprecated */ + @Deprecated public R fetchAll() { return fetch(); } @@ -100,8 +109,7 @@ public abstract class TQAssocBean extends TQProperty { */ @SafeVarargs protected final R fetchProperties(TQProperty... props) { - ((TQRootBean) _root).query().fetch(_name, properties(props)); - return _root; + return fetchWithProperties(FETCH_DEFAULT, props); } /** @@ -109,8 +117,7 @@ public abstract class TQAssocBean extends TQProperty { */ @SafeVarargs protected final R fetchQueryProperties(TQProperty... props) { - ((TQRootBean) _root).query().fetchQuery(_name, properties(props)); - return _root; + return fetchWithProperties(FETCH_QUERY, props); } /** @@ -118,8 +125,7 @@ public abstract class TQAssocBean extends TQProperty { */ @SafeVarargs protected final R fetchCacheProperties(TQProperty... props) { - ((TQRootBean) _root).query().fetchCache(_name, properties(props)); - return _root; + return fetchWithProperties(FETCH_CACHE, props); } /** @@ -127,23 +133,26 @@ public abstract class TQAssocBean extends TQProperty { */ @SafeVarargs protected final R fetchLazyProperties(TQProperty... props) { - ((TQRootBean) _root).query().fetchLazy(_name, properties(props)); + return fetchWithProperties(FETCH_LAZY, props); + } + + @SafeVarargs + private final R fetchWithProperties(FetchConfig config, TQProperty... props) { + spiQuery().fetchProperties(_name, properties(props), config); return _root; } - /** - * Append the properties as a comma delimited string. - */ + private final SpiQueryFetch spiQuery() { + return (SpiQueryFetch)((TQRootBean) _root).query(); + } + @SafeVarargs - protected final String properties(TQProperty... props) { - StringBuilder selectProps = new StringBuilder(50); - for (int i = 0; i < props.length; i++) { - if (i > 0) { - selectProps.append(","); - } - selectProps.append(props[i].propertyName()); + private final Set properties(TQProperty... props) { + Set set = new LinkedHashSet<>(); + for (TQProperty prop : props) { + set.add(prop.propertyName()); } - return selectProps.toString(); + return set; } /** diff --git a/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java b/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java index 72876a00d..de8f846a6 100644 --- a/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java +++ b/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java @@ -25,6 +25,7 @@ import io.ebean.search.TextQueryString; import io.ebean.search.TextSimple; import io.ebean.service.SpiFetchGroupQuery; import io.ebean.text.PathProperties; +import io.ebeaninternal.api.SpiQueryFetch; import io.ebeaninternal.server.util.ArrayStack; import javax.annotation.Nonnull; @@ -32,6 +33,7 @@ import javax.annotation.Nullable; import java.sql.Connection; import java.sql.Timestamp; import java.util.Collection; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -278,22 +280,21 @@ public abstract class TQRootBean { */ @SafeVarargs public final R select(TQProperty... properties) { - StringBuilder selectProps = new StringBuilder(50); - for (int i = 0; i < properties.length; i++) { - if (i > 0) { - selectProps.append(","); - } - selectProps.append(properties[i].propertyName()); - } - query.select(selectProps.toString()); + ((SpiQueryFetch)query).selectProperties(properties(properties)); return root; } + private Set properties(TQProperty[] properties) { + Set props = new LinkedHashSet<>(); + for (TQProperty property : properties) { + props.add(property.propertyName()); + } + return props; + } + /** * Specify a path to load including all its properties. - *

- * The same as {@link #fetch(String, String)} with the fetchProperties as "*". - *

+ * *
{@code
    *
    * List customers =
diff --git a/ebean-querybean/src/test/java/org/querytest/QOrderTest.java b/ebean-querybean/src/test/java/org/querytest/QOrderTest.java
index ab98f0e67..2f470fdb5 100644
--- a/ebean-querybean/src/test/java/org/querytest/QOrderTest.java
+++ b/ebean-querybean/src/test/java/org/querytest/QOrderTest.java
@@ -1,11 +1,17 @@
 package org.querytest;
 
+import io.ebean.DB;
 import io.ebean.FetchGroup;
+import io.ebean.test.LoggedSql;
 import org.example.domain.Order;
 import org.example.domain.query.QCustomer;
 import org.example.domain.query.QOrder;
 import org.junit.Test;
 
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
 public class QOrderTest {
 
   private static final QCustomer cu = QCustomer.alias();
@@ -17,10 +23,14 @@ public class QOrderTest {
     .customer.fetchCache(cu.name, cu.status, cu.registered, cu.comments)
     .buildFetchGroup();
 
+  private static final FetchGroup fg2 = QOrder.forFetchGroup()
+    .select(or.status)
+    .customer.fetch(cu.name)
+    .buildFetchGroup();
+
   @Test
   public void fetchCache() {
 
-
     new QOrder()
       .status.eq(Order.Status.NEW)
       .customer.fetchCache(cu.name, cu.registered)
@@ -35,10 +45,72 @@ public class QOrderTest {
   @Test
   public void viaFetchGraph() {
 
+    DB.getDefault();
+    LoggedSql.start();
+
     new QOrder()
       .status.eq(Order.Status.NEW)
       .select(fg)
       .findList();
+
+
+    final List sql = LoggedSql.stop();
+    assertThat(sql).hasSize(1);
+    assertThat(sql.get(0)).contains("select t0.id, t0.status, t0.ship_date, t0.customer_id from o_order t0 where");
+  }
+
+  @Test
+  public void viaFetchGraph_withJoin() {
+
+    DB.getDefault();
+    LoggedSql.start();
+
+    new QOrder()
+      .status.eq(Order.Status.NEW)
+      .select(fg2)
+      .findList();
+
+
+    final List sql = LoggedSql.stop();
+    assertThat(sql).hasSize(1);
+    assertThat(sql.get(0)).contains("select t0.id, t0.status, t1.id, t1.name from o_order t0 join be_customer t1 on t1.id = t0.customer_id where");
+  }
+
+  @Test
+  public void select_partial() {
+
+    DB.getDefault();
+    LoggedSql.start();
+
+    final QOrder o = QOrder.alias();
+
+    new QOrder()
+      .select(o.status, o.orderDate)
+      .findList();
+
+    final List sql = LoggedSql.stop();
+    assertThat(sql).hasSize(1);
+    assertThat(sql.get(0)).contains("select t0.id, t0.status, t0.order_date from o_order t0");
+  }
+
+  @Test
+  public void fetch_partial() {
+
+    DB.getDefault();
+    LoggedSql.start();
+
+    final QOrder o = QOrder.alias();
+    final QCustomer c = QCustomer.alias();
+
+    new QOrder()
+      .select(o.status)
+      .customer.fetch(c.email, c.name)
+      .findList();
+
+    final List sql = LoggedSql.stop();
+    assertThat(sql).hasSize(1);
+    assertThat(sql.get(0)).contains("select t0.id, t0.status, t1.id, t1.email, t1.name from o_order t0 join be_customer t1 on t1.id = t0.customer_id");
+
   }
 
 }

From e5ec72df5cbc0a836ac36773b0b7d5602f8d4938 Mon Sep 17 00:00:00 2001
From: robin 
Date: Tue, 16 Feb 2021 20:37:58 +1300
Subject: [PATCH 03/31] Refactor internals OrmQueryProperties invert isEmpty()
 -> nonEmpty()

---
 .../server/autotune/service/ProfileOriginTest.java          | 5 +----
 .../io/ebeaninternal/server/querydefn/OrmQueryDetail.java   | 2 +-
 .../ebeaninternal/server/querydefn/OrmQueryProperties.java  | 6 +++---
 3 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/ebean-autotune/src/test/java/io/ebeaninternal/server/autotune/service/ProfileOriginTest.java b/ebean-autotune/src/test/java/io/ebeaninternal/server/autotune/service/ProfileOriginTest.java
index ec0a09596..c3995d215 100644
--- a/ebean-autotune/src/test/java/io/ebeaninternal/server/autotune/service/ProfileOriginTest.java
+++ b/ebean-autotune/src/test/java/io/ebeaninternal/server/autotune/service/ProfileOriginTest.java
@@ -11,12 +11,9 @@ import org.tests.model.basic.Order;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
-//import org.tests.model.basic.ResetBasicData;
-
 public class ProfileOriginTest extends BaseTestCase {
 
-
-  private BeanDescriptor desc = getBeanDescriptor(Order.class);
+  private final BeanDescriptor desc = getBeanDescriptor(Order.class);
 
   @Test
   public void buildDetail() {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java
index 1c441cbf4..ba03b7c78 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java
@@ -111,7 +111,7 @@ public class OrmQueryDetail implements Serializable {
    */
   public String asStringDebug() {
     StringBuilder sb = new StringBuilder();
-    if (!baseProps.isEmpty()) {
+    if (baseProps.notEmpty()) {
       baseProps.asStringDebug("select ", sb);
     }
     if (fetchPaths != null) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryProperties.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryProperties.java
index e84828f09..eb41bb40f 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryProperties.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryProperties.java
@@ -214,7 +214,7 @@ public class OrmQueryProperties implements Serializable {
    */
   @SuppressWarnings("unchecked")
   public void configureBeanQuery(SpiQuery query) {
-    if (!isEmpty()) {
+    if (notEmpty()) {
       query.selectProperties(this);
     }
 
@@ -252,8 +252,8 @@ public class OrmQueryProperties implements Serializable {
   /**
    * Return true if the properties and configuration are empty.
    */
-  boolean isEmpty() {
-    return !allProperties && included == null;
+  boolean notEmpty() {
+    return allProperties || included != null;
   }
 
   public void asStringDebug(String prefix, StringBuilder sb) {

From de439e7695b3731c16f1672ced953217d9759391 Mon Sep 17 00:00:00 2001
From: robin 
Date: Tue, 16 Feb 2021 23:20:44 +1300
Subject: [PATCH 04/31] Improve Refactor internals OrmQueryProperties invert
 isEmpty()

Rename to hasProperties() and improve javadoc
---
 .../server/querydefn/OrmQueryDetail.java             |  2 +-
 .../server/querydefn/OrmQueryProperties.java         | 12 ++++--------
 2 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java
index ba03b7c78..6f96fbbea 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryDetail.java
@@ -111,7 +111,7 @@ public class OrmQueryDetail implements Serializable {
    */
   public String asStringDebug() {
     StringBuilder sb = new StringBuilder();
-    if (baseProps.notEmpty()) {
+    if (baseProps.hasProperties()) {
       baseProps.asStringDebug("select ", sb);
     }
     if (fetchPaths != null) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryProperties.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryProperties.java
index eb41bb40f..d1a3aeafc 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryProperties.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/OrmQueryProperties.java
@@ -214,7 +214,7 @@ public class OrmQueryProperties implements Serializable {
    */
   @SuppressWarnings("unchecked")
   public void configureBeanQuery(SpiQuery query) {
-    if (notEmpty()) {
+    if (hasProperties()) {
       query.selectProperties(this);
     }
 
@@ -242,17 +242,13 @@ public class OrmQueryProperties implements Serializable {
   }
 
   public boolean hasSelectClause() {
-    if (allProperties) {
-      return true;
-    }
-    // explicitly selected some properties
-    return included != null || filterMany != null;
+    return allProperties || included != null || filterMany != null;
   }
 
   /**
-   * Return true if the properties and configuration are empty.
+   * Return true if explicit properties have been specified.
    */
-  boolean notEmpty() {
+  boolean hasProperties() {
     return allProperties || included != null;
   }
 

From 3bb4126e2652425f89c6ffb5db0b0aace1ecd004 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Camilo?= 
Date: Wed, 17 Feb 2021 05:04:43 +0000
Subject: [PATCH 05/31] #2169 Support for
 jakarta.validation.constraints.NotNull and (#2170)

jakarta.validation.constraints.Size
---
 CONFIGURATION.md                              |  2 +-
 ebean-api/pom.xml                             |  6 ++
 .../java/io/ebean/config/ClassLoadConfig.java |  7 ++
 .../java/io/ebean/config/DatabaseConfig.java  | 15 ++--
 ebean-core/pom.xml                            |  6 ++
 .../deploy/meta/DeployBeanProperty.java       | 11 ++-
 .../deploy/parse/AnnotationAssocOnes.java     | 12 ++-
 .../server/deploy/parse/AnnotationFields.java | 37 ++++++--
 .../server/deploy/parse/AnnotationParser.java |  9 +-
 .../server/deploy/parse/DeployUtil.java       |  8 +-
 .../InitMetaJakartaValidationAnnotation.java  | 11 +++
 ...=> InitMetaJavaxValidationAnnotation.java} |  2 +-
 .../deploy/parse/ReadAnnotationConfig.java    |  6 ++
 .../server/deploy/parse/ReadAnnotations.java  |  5 +-
 .../org/tests/basic/TestAnnotationBase.java   | 84 +++++++++++++++++++
 15 files changed, 191 insertions(+), 30 deletions(-)
 create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJakartaValidationAnnotation.java
 rename ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/{InitMetaValidationAnnotation.java => InitMetaJavaxValidationAnnotation.java} (85%)

diff --git a/CONFIGURATION.md b/CONFIGURATION.md
index 16fa6d698..46be98f0b 100644
--- a/CONFIGURATION.md
+++ b/CONFIGURATION.md
@@ -293,6 +293,6 @@ ebean.tenant.schemaProvider
 ebean.updateAllPropertiesInBatch
 ebean.updateChangesOnly
 ebean.updatesDeleteMissingChildren
-ebean.useJavaxValidationNotNull
+ebean.useValidationNotNull
 ebean.useJtaTransactionManager
 
diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml
index 0d8e67b00..1edc0a655 100644
--- a/ebean-api/pom.xml
+++ b/ebean-api/pom.xml
@@ -109,6 +109,12 @@
       1.1.0.Final
       true
     
+    
+      jakarta.validation
+      jakarta.validation-api
+      3.0.0
+      true
+    
 
     
       javax.servlet
diff --git a/ebean-api/src/main/java/io/ebean/config/ClassLoadConfig.java b/ebean-api/src/main/java/io/ebean/config/ClassLoadConfig.java
index 821905405..764fa86a1 100644
--- a/ebean-api/src/main/java/io/ebean/config/ClassLoadConfig.java
+++ b/ebean-api/src/main/java/io/ebean/config/ClassLoadConfig.java
@@ -40,6 +40,13 @@ public class ClassLoadConfig {
     return isPresent("javax.validation.constraints.NotNull");
   }
 
+  /**
+   * Return true if jakarta validation annotations like Size and NotNull are present.
+   */
+  public boolean isJakartaValidationAnnotationsPresent() {
+    return isPresent("jakarta.validation.constraints.NotNull");
+  }
+
   /**
    * Return true if javax PostConstruct annotation is present (maybe not in java9).
    * If not we don't support PostConstruct lifecycle events.
diff --git a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java
index 7ea8e56e9..036a39040 100644
--- a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java
+++ b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java
@@ -489,7 +489,7 @@ public class DatabaseConfig {
    * Should the javax.validation.constraints.NotNull enforce a notNull column in DB.
    * If set to false, use io.ebean.annotation.NotNull or Column(nullable=true).
    */
-  private boolean useJavaxValidationNotNull = true;
+  private boolean useValidationNotNull = true;
 
   /**
    * Generally we want to perform L2 cache notification in the background and not impact
@@ -2809,7 +2809,7 @@ public class DatabaseConfig {
     enabledL2Regions = p.get("enabledL2Regions", enabledL2Regions);
     notifyL2CacheInForeground = p.getBoolean("notifyL2CacheInForeground", notifyL2CacheInForeground);
     useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
-    useJavaxValidationNotNull = p.getBoolean("useJavaxValidationNotNull", useJavaxValidationNotNull);
+    useValidationNotNull = p.getBoolean("useValidationNotNull", useValidationNotNull);
     autoReadOnlyDataSource = p.getBoolean("autoReadOnlyDataSource", autoReadOnlyDataSource);
     idGeneratorAutomatic = p.getBoolean("idGeneratorAutomatic", idGeneratorAutomatic);
 
@@ -3056,20 +3056,21 @@ public class DatabaseConfig {
   /**
    * Returns if we use javax.validation.constraints.NotNull
    */
-  public boolean isUseJavaxValidationNotNull() {
-    return useJavaxValidationNotNull;
+  public boolean isUseValidationNotNull() {
+    return useValidationNotNull;
   }
 
   /**
-   * Controls if Ebean should ignore &x64;javax.validation.contstraints.NotNull
+   * Controls if Ebean should ignore &x64;javax.validation.contstraints.NotNull or
+   * &x64;jakarta.validation.contstraints.NotNull
    * with respect to generating a NOT NULL column.
    * 

* Normally when Ebean sees javax NotNull annotation it means that column is defined as NOT NULL. * Set this to false and the javax NotNull annotation is effectively ignored (and * we instead use Ebean's own NotNull annotation or JPA Column(nullable=false) annotation. */ - public void setUseJavaxValidationNotNull(boolean useJavaxValidationNotNull) { - this.useJavaxValidationNotNull = useJavaxValidationNotNull; + public void setUseValidationNotNull(boolean useValidationNotNull) { + this.useValidationNotNull = useValidationNotNull; } /** diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index 20d9cfe6c..dcdad1b0e 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -138,6 +138,12 @@ 1.1.0.Final true + + jakarta.validation + jakarta.validation-api + 3.0.0 + true + joda-time diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java index df3a46094..bccc21bcc 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java @@ -1140,7 +1140,7 @@ public class DeployBeanProperty { return result; } - public List getMetaAnnotationSize() { + public List getMetaAnnotationJavaxSize() { final List size = getMetaAnnotations(Size.class); final List lists = getMetaAnnotations(Size.List.class); for (Size.List list : lists) { @@ -1149,6 +1149,15 @@ public class DeployBeanProperty { return size; } + public List getMetaAnnotationJakartaSize() { + final List size = getMetaAnnotations(jakarta.validation.constraints.Size.class); + final List lists = getMetaAnnotations(jakarta.validation.constraints.Size.List.class); + for (jakarta.validation.constraints.Size.List list : lists) { + Collections.addAll(size, list.value()); + } + return size; + } + public Formula getMetaAnnotationFormula(Platform platform) { Formula fallback = null; for (Annotation ann : metaAnnotations) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java index 6750633cb..aa38d93e6 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java @@ -4,13 +4,11 @@ import io.ebean.annotation.DbForeignKey; import io.ebean.annotation.FetchPreference; import io.ebean.annotation.TenantId; import io.ebean.annotation.Where; -import io.ebean.config.BeanNotRegisteredException; import io.ebean.config.NamingConvention; import io.ebeaninternal.server.deploy.BeanDescriptorManager; import io.ebeaninternal.server.deploy.BeanTable; import io.ebeaninternal.server.deploy.PropertyForeignKey; import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc; import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; import io.ebeaninternal.server.deploy.meta.DeployTableJoinColumn; import io.ebeaninternal.server.query.SqlJoinType; @@ -117,7 +115,7 @@ public class AnnotationAssocOnes extends AnnotationAssoc { if (nonNull != null) { prop.setNullable(false); } - if (validationAnnotations) { + if (javaxValidationAnnotations) { NotNull notNull = get(prop, NotNull.class); if (notNull != null && isEbeanValidationGroups(notNull.groups())) { prop.setNullable(false); @@ -125,6 +123,14 @@ public class AnnotationAssocOnes extends AnnotationAssoc { prop.getTableJoin().setType(SqlJoinType.INNER); } } + if (jakartaValidationAnnotations) { + jakarta.validation.constraints.NotNull notNull = get(prop, jakarta.validation.constraints.NotNull.class); + if (notNull != null && isEbeanValidationGroups(notNull.groups())) { + prop.setNullable(false); + // overrides optional attribute of ManyToOne etc + prop.getTableJoin().setType(SqlJoinType.INNER); + } + } // check for manually defined joins BeanTable beanTable = prop.getBeanTable(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFields.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFields.java index aafbfa182..413929098 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFields.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFields.java @@ -258,19 +258,38 @@ public class AnnotationFields extends AnnotationParser { } private void initValidation(DeployBeanProperty prop) { - NotNull notNull = get(prop, NotNull.class); - if (notNull != null && isEbeanValidationGroups(notNull.groups())) { - // Not null on all validation groups so enable - // DDL generation of Not Null Constraint - prop.setNullable(false); + if (javaxValidationAnnotations) { + NotNull notNull = get(prop, NotNull.class); + if (notNull != null && isEbeanValidationGroups(notNull.groups())) { + // Not null on all validation groups so enable + // DDL generation of Not Null Constraint + prop.setNullable(false); + } + } + if (jakartaValidationAnnotations) { + jakarta.validation.constraints.NotNull notNull = get(prop, jakarta.validation.constraints.NotNull.class); + if (notNull != null && isEbeanValidationGroups(notNull.groups())) { + // Not null on all validation groups so enable + // DDL generation of Not Null Constraint + prop.setNullable(false); + } } if (!prop.isLob()) { // take the max size of all @Size annotations int maxSize = -1; - for (Size size : prop.getMetaAnnotationSize()) { - if (size.max() < Integer.MAX_VALUE) { - maxSize = Math.max(maxSize, size.max()); + if (javaxValidationAnnotations) { + for (Size size : prop.getMetaAnnotationJavaxSize()) { + if (size.max() < Integer.MAX_VALUE) { + maxSize = Math.max(maxSize, size.max()); + } + } + } + if (jakartaValidationAnnotations) { + for (jakarta.validation.constraints.Size size : prop.getMetaAnnotationJakartaSize()) { + if (size.max() < Integer.MAX_VALUE) { + maxSize = Math.max(maxSize, size.max()); + } } } if (maxSize != -1) { @@ -280,7 +299,7 @@ public class AnnotationFields extends AnnotationParser { } private void initTenantId(DeployBeanProperty prop) { - if (validationAnnotations) { + if (javaxValidationAnnotations || jakartaValidationAnnotations) { initValidation(prop); } if (has(prop, TenantId.class)) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationParser.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationParser.java index 64851135d..8a27981b4 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationParser.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationParser.java @@ -26,14 +26,17 @@ public abstract class AnnotationParser extends AnnotationBase { final Class beanType; - final boolean validationAnnotations; + final boolean javaxValidationAnnotations; + + final boolean jakartaValidationAnnotations; final ReadAnnotationConfig readConfig; AnnotationParser(DeployBeanInfo info, ReadAnnotationConfig readConfig) { super(info.getUtil()); this.readConfig = readConfig; - this.validationAnnotations = readConfig.isJavaxValidationAnnotations(); + this.javaxValidationAnnotations = readConfig.isJavaxValidationAnnotations(); + this.jakartaValidationAnnotations = readConfig.isJakartaValidationAnnotations(); this.info = info; this.beanType = info.getDescriptor().getBeanType(); this.descriptor = info.getDescriptor(); @@ -132,7 +135,7 @@ public abstract class AnnotationParser extends AnnotationBase { * can be applied to DDL generation. */ boolean isEbeanValidationGroups(Class[] groups) { - if (!util.isUseJavaxValidationNotNull()) { + if (!util.isUseValidationNotNull()) { return false; } return groups.length == 0 || groups.length == 1 && Default.class.isAssignableFrom(groups[0]); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java index 3a0f8d826..65c7cf45e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java @@ -57,7 +57,7 @@ public class DeployUtil { private final Encryptor bytesEncryptor; - private final boolean useJavaxValidationNotNull; + private final boolean useValidationNotNull; public DeployUtil(TypeManager typeMgr, DatabaseConfig config) { this.typeManager = typeMgr; @@ -67,7 +67,7 @@ public class DeployUtil { this.encryptKeyManager = config.getEncryptKeyManager(); Encryptor be = config.getEncryptor(); this.bytesEncryptor = be != null ? be : new SimpleAesEncryptor(); - this.useJavaxValidationNotNull = config.isUseJavaxValidationNotNull(); + this.useValidationNotNull = config.isUseValidationNotNull(); } public TypeManager getTypeManager() { @@ -286,8 +286,8 @@ public class DeployUtil { return type.equals(String.class); } - boolean isUseJavaxValidationNotNull() { - return useJavaxValidationNotNull; + boolean isUseValidationNotNull() { + return useValidationNotNull; } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJakartaValidationAnnotation.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJakartaValidationAnnotation.java new file mode 100644 index 000000000..41ea9a8c7 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJakartaValidationAnnotation.java @@ -0,0 +1,11 @@ +package io.ebeaninternal.server.deploy.parse; + +import jakarta.validation.constraints.Size; + +class InitMetaJakartaValidationAnnotation { + + static void init(ReadAnnotationConfig readConfig) { + readConfig.addMetaAnnotation(Size.class); + readConfig.addMetaAnnotation(Size.List.class); + } +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaValidationAnnotation.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJavaxValidationAnnotation.java similarity index 85% rename from ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaValidationAnnotation.java rename to ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJavaxValidationAnnotation.java index 9d5ac9e30..495d26ff5 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaValidationAnnotation.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJavaxValidationAnnotation.java @@ -2,7 +2,7 @@ package io.ebeaninternal.server.deploy.parse; import javax.validation.constraints.Size; -class InitMetaValidationAnnotation { +class InitMetaJavaxValidationAnnotation { static void init(ReadAnnotationConfig readConfig) { readConfig.addMetaAnnotation(Size.class); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotationConfig.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotationConfig.java index bf96849b0..d7d46e9ce 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotationConfig.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotationConfig.java @@ -21,6 +21,7 @@ class ReadAnnotationConfig { private final boolean disableL2Cache; private final boolean eagerFetchLobs; private final boolean javaxValidationAnnotations; + private final boolean jakartaValidationAnnotations; private final boolean jacksonAnnotations; private final boolean idGeneratorAutomatic; @@ -34,6 +35,7 @@ class ReadAnnotationConfig { this.eagerFetchLobs = config.isEagerFetchLobs(); this.idGeneratorAutomatic = config.isIdGeneratorAutomatic(); this.javaxValidationAnnotations = generatedPropFactory.getClassLoadConfig().isJavaxValidationAnnotationsPresent(); + this.jakartaValidationAnnotations = generatedPropFactory.getClassLoadConfig().isJakartaValidationAnnotationsPresent(); this.jacksonAnnotations = generatedPropFactory.getClassLoadConfig().isJacksonAnnotationsPresent(); this.metaAnnotations.add(Column.class); this.metaAnnotations.add(Formula.class); @@ -75,6 +77,10 @@ class ReadAnnotationConfig { return javaxValidationAnnotations; } + boolean isJakartaValidationAnnotations() { + return jakartaValidationAnnotations; + } + boolean isJacksonAnnotations() { return jacksonAnnotations; } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotations.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotations.java index 86a6a15f0..796a975aa 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotations.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotations.java @@ -15,7 +15,10 @@ public class ReadAnnotations { public ReadAnnotations(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, DatabaseConfig config) { this.readConfig = new ReadAnnotationConfig(generatedPropFactory, asOfViewSuffix, versionsBetweenSuffix, config); if (readConfig.isJavaxValidationAnnotations()) { - InitMetaValidationAnnotation.init(readConfig); + InitMetaJavaxValidationAnnotation.init(readConfig); + } + if (readConfig.isJakartaValidationAnnotations()) { + InitMetaJakartaValidationAnnotation.init(readConfig); } if (readConfig.isJacksonAnnotations()) { InitMetaJacksonAnnotation.init(readConfig); diff --git a/ebean-core/src/test/java/org/tests/basic/TestAnnotationBase.java b/ebean-core/src/test/java/org/tests/basic/TestAnnotationBase.java index aa0e0e926..c5781174d 100644 --- a/ebean-core/src/test/java/org/tests/basic/TestAnnotationBase.java +++ b/ebean-core/src/test/java/org/tests/basic/TestAnnotationBase.java @@ -144,6 +144,69 @@ public class TestAnnotationBase extends BaseTestCase { } } + @Entity + public static class TestJakartaAnnotationBaseEntity extends MappedBaseEntity { + @Where(clause = "SELECT 'mysql' from 1", platforms = Platform.MYSQL) + @Where(clause = "SELECT 'h2' from 1", platforms = H2) + @Where(clause = "SELECT 'other' from 1") + private String direct; + + @MetaTest + private String meta; + + @MetaTest + @Where(clause = "SELECT 'oracle' from 1", platforms = Platform.ORACLE) + private String mixed; + + @jakarta.validation.constraints.Size.List({ + @jakarta.validation.constraints.Size(max = 10, message = "max length for you is 10"), + @jakarta.validation.constraints.Size(min = 1), + @jakarta.validation.constraints.Size(max = 40, message = "max value for you is 40", groups = ValidationGroupSomething.class) + }) + private String constraintAnnotation; + + @NotNull + private String null1; + + + @NotNull(groups = ValidationGroupSomething.class) + private String null2; + + private String null3; + + public String getConstraintAnnotation() { + return constraintAnnotation; + } + + public void setConstraintAnnotation(String constraintAnnotation) { + this.constraintAnnotation = constraintAnnotation; + } + + public String getNull1() { + return null1; + } + + public void setNull1(String null1) { + this.null1 = null1; + } + + public String getNull2() { + return null2; + } + + public void setNull2(String null2) { + this.null2 = null2; + } + + public String getNull3() { + return null3; + } + + public void setNull3(String null3) { + this.null3 = null3; + } + } + @Test public void testFindMaxSize() throws SecurityException { @@ -152,6 +215,13 @@ public class TestAnnotationBase extends BaseTestCase { assertEquals(40, bp.getDbLength()); } + @Test + public void testFindJakartaMaxSize() throws SecurityException { + BeanDescriptor descriptor = spiEbeanServer().getBeanDescriptor(TestJakartaAnnotationBaseEntity.class); + BeanProperty bp = descriptor.findProperty("constraintAnnotation"); + assertEquals(40, bp.getDbLength()); + } + @Test public void annotationClassIndexes() throws SecurityException { BeanDescriptor descriptor = spiEbeanServer().getBeanDescriptor(TestAnnotationBaseEntity.class); @@ -173,6 +243,20 @@ public class TestAnnotationBase extends BaseTestCase { assertTrue(bp.isNullable()); } + @Test + public void testJakartaNotNullWithGroup() throws SecurityException { + BeanDescriptor descriptor = spiEbeanServer().getBeanDescriptor(TestJakartaAnnotationBaseEntity.class); + + BeanProperty bp = descriptor.findProperty("null1"); + assertFalse(bp.isNullable()); + + bp = descriptor.findProperty("null2"); + assertTrue(bp.isNullable()); + + bp = descriptor.findProperty("null3"); + assertTrue(bp.isNullable()); + } + @Test public void testFindAnnotation() throws NoSuchFieldException, SecurityException { From b4ae721998f1e2b1b7982fefd1029e8669f1ed5b Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 17 Feb 2021 22:23:07 +1300 Subject: [PATCH 06/31] #2169 Refactor after #2170 tidy javax/jakarta validation annotation reading (#2171) Introduces ReadValidationAnnotations with javax and jakarta implementations and moves the validation annotation reading there --- ebean-api/pom.xml | 14 ---- .../deploy/meta/DeployBeanProperty.java | 19 ------ .../deploy/parse/AnnotationAssocOnes.java | 20 ++---- .../server/deploy/parse/AnnotationFields.java | 41 ++---------- .../server/deploy/parse/AnnotationParser.java | 18 ------ .../InitMetaJakartaValidationAnnotation.java | 11 ---- .../InitMetaJavaxValidationAnnotation.java | 11 ---- .../deploy/parse/ReadAnnotationConfig.java | 64 +++++++++++++++---- .../server/deploy/parse/ReadAnnotations.java | 9 --- .../parse/ReadValidationAnnotations.java | 19 ++++++ .../ReadValidationAnnotationsJakarta.java | 51 +++++++++++++++ .../parse/ReadValidationAnnotationsJavax.java | 52 +++++++++++++++ 12 files changed, 182 insertions(+), 147 deletions(-) delete mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJakartaValidationAnnotation.java delete mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJavaxValidationAnnotation.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadValidationAnnotations.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadValidationAnnotationsJakarta.java create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadValidationAnnotationsJavax.java diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index 1edc0a655..80d8db37a 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -102,20 +102,6 @@ true - - - javax.validation - validation-api - 1.1.0.Final - true - - - jakarta.validation - jakarta.validation-api - 3.0.0 - true - - javax.servlet javax.servlet-api diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java index bccc21bcc..5b9e8d687 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java @@ -34,7 +34,6 @@ import javax.persistence.EmbeddedId; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.Version; -import javax.validation.constraints.Size; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Type; @@ -1140,24 +1139,6 @@ public class DeployBeanProperty { return result; } - public List getMetaAnnotationJavaxSize() { - final List size = getMetaAnnotations(Size.class); - final List lists = getMetaAnnotations(Size.List.class); - for (Size.List list : lists) { - Collections.addAll(size, list.value()); - } - return size; - } - - public List getMetaAnnotationJakartaSize() { - final List size = getMetaAnnotations(jakarta.validation.constraints.Size.class); - final List lists = getMetaAnnotations(jakarta.validation.constraints.Size.List.class); - for (jakarta.validation.constraints.Size.List list : lists) { - Collections.addAll(size, list.value()); - } - return size; - } - public Formula getMetaAnnotationFormula(Platform platform) { Formula fallback = null; for (Annotation ann : metaAnnotations) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java index aa38d93e6..d313ce925 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java @@ -26,7 +26,6 @@ import javax.persistence.JoinTable; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import javax.persistence.PrimaryKeyJoinColumn; -import javax.validation.constraints.NotNull; /** * Read the deployment annotations for Associated One beans. @@ -115,21 +114,10 @@ public class AnnotationAssocOnes extends AnnotationAssoc { if (nonNull != null) { prop.setNullable(false); } - if (javaxValidationAnnotations) { - NotNull notNull = get(prop, NotNull.class); - if (notNull != null && isEbeanValidationGroups(notNull.groups())) { - prop.setNullable(false); - // overrides optional attribute of ManyToOne etc - prop.getTableJoin().setType(SqlJoinType.INNER); - } - } - if (jakartaValidationAnnotations) { - jakarta.validation.constraints.NotNull notNull = get(prop, jakarta.validation.constraints.NotNull.class); - if (notNull != null && isEbeanValidationGroups(notNull.groups())) { - prop.setNullable(false); - // overrides optional attribute of ManyToOne etc - prop.getTableJoin().setType(SqlJoinType.INNER); - } + if (readConfig.isValidationNotNull(prop)) { + // overrides optional attribute of ManyToOne etc + prop.setNullable(false); + prop.getTableJoin().setType(SqlJoinType.INNER); } // check for manually defined joins diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFields.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFields.java index 413929098..8079f156f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFields.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationFields.java @@ -69,8 +69,6 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient; import javax.persistence.Version; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; import java.sql.Types; import java.util.Set; import java.util.UUID; @@ -258,48 +256,19 @@ public class AnnotationFields extends AnnotationParser { } private void initValidation(DeployBeanProperty prop) { - if (javaxValidationAnnotations) { - NotNull notNull = get(prop, NotNull.class); - if (notNull != null && isEbeanValidationGroups(notNull.groups())) { - // Not null on all validation groups so enable - // DDL generation of Not Null Constraint - prop.setNullable(false); - } + if (readConfig.isValidationNotNull(prop)) { + prop.setNullable(false); } - if (jakartaValidationAnnotations) { - jakarta.validation.constraints.NotNull notNull = get(prop, jakarta.validation.constraints.NotNull.class); - if (notNull != null && isEbeanValidationGroups(notNull.groups())) { - // Not null on all validation groups so enable - // DDL generation of Not Null Constraint - prop.setNullable(false); - } - } - if (!prop.isLob()) { - // take the max size of all @Size annotations - int maxSize = -1; - if (javaxValidationAnnotations) { - for (Size size : prop.getMetaAnnotationJavaxSize()) { - if (size.max() < Integer.MAX_VALUE) { - maxSize = Math.max(maxSize, size.max()); - } - } - } - if (jakartaValidationAnnotations) { - for (jakarta.validation.constraints.Size size : prop.getMetaAnnotationJakartaSize()) { - if (size.max() < Integer.MAX_VALUE) { - maxSize = Math.max(maxSize, size.max()); - } - } - } - if (maxSize != -1) { + int maxSize = readConfig.maxValidationSize(prop); + if (maxSize > 0) { prop.setDbLength(maxSize); } } } private void initTenantId(DeployBeanProperty prop) { - if (javaxValidationAnnotations || jakartaValidationAnnotations) { + if (readConfig.checkValidationAnnotations()) { initValidation(prop); } if (has(prop, TenantId.class)) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationParser.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationParser.java index 8a27981b4..af015e1f5 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationParser.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationParser.java @@ -9,7 +9,6 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; import javax.persistence.AttributeOverride; import javax.persistence.CascadeType; import javax.persistence.Column; -import javax.validation.groups.Default; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -26,17 +25,11 @@ public abstract class AnnotationParser extends AnnotationBase { final Class beanType; - final boolean javaxValidationAnnotations; - - final boolean jakartaValidationAnnotations; - final ReadAnnotationConfig readConfig; AnnotationParser(DeployBeanInfo info, ReadAnnotationConfig readConfig) { super(info.getUtil()); this.readConfig = readConfig; - this.javaxValidationAnnotations = readConfig.isJavaxValidationAnnotations(); - this.jakartaValidationAnnotations = readConfig.isJakartaValidationAnnotations(); this.info = info; this.beanType = info.getDescriptor().getBeanType(); this.descriptor = info.getDescriptor(); @@ -130,17 +123,6 @@ public abstract class AnnotationParser extends AnnotationBase { } } - /** - * Return true if the validation groups are {@link Default} (respectively empty) - * can be applied to DDL generation. - */ - boolean isEbeanValidationGroups(Class[] groups) { - if (!util.isUseValidationNotNull()) { - return false; - } - return groups.length == 0 || groups.length == 1 && Default.class.isAssignableFrom(groups[0]); - } - String[] convertColumnNames(String[] columnNames) { for (int i = 0; i < columnNames.length; i++) { columnNames[i] = databasePlatform.convertQuotedIdentifiers(columnNames[i]); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJakartaValidationAnnotation.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJakartaValidationAnnotation.java deleted file mode 100644 index 41ea9a8c7..000000000 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJakartaValidationAnnotation.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.ebeaninternal.server.deploy.parse; - -import jakarta.validation.constraints.Size; - -class InitMetaJakartaValidationAnnotation { - - static void init(ReadAnnotationConfig readConfig) { - readConfig.addMetaAnnotation(Size.class); - readConfig.addMetaAnnotation(Size.List.class); - } -} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJavaxValidationAnnotation.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJavaxValidationAnnotation.java deleted file mode 100644 index 495d26ff5..000000000 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/InitMetaJavaxValidationAnnotation.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.ebeaninternal.server.deploy.parse; - -import javax.validation.constraints.Size; - -class InitMetaJavaxValidationAnnotation { - - static void init(ReadAnnotationConfig readConfig) { - readConfig.addMetaAnnotation(Size.class); - readConfig.addMetaAnnotation(Size.List.class); - } -} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotationConfig.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotationConfig.java index d7d46e9ce..1daf37564 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotationConfig.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotationConfig.java @@ -3,8 +3,10 @@ package io.ebeaninternal.server.deploy.parse; import io.ebean.annotation.Aggregation; import io.ebean.annotation.Formula; import io.ebean.annotation.Where; +import io.ebean.config.ClassLoadConfig; import io.ebean.config.DatabaseConfig; import io.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory; +import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; import javax.persistence.Column; import java.util.HashSet; @@ -24,7 +26,9 @@ class ReadAnnotationConfig { private final boolean jakartaValidationAnnotations; private final boolean jacksonAnnotations; private final boolean idGeneratorAutomatic; - + private final boolean useValidationNotNull; + private final ReadValidationAnnotations javaxValidation; + private final ReadValidationAnnotations jakartaValidation; private final Set> metaAnnotations = new HashSet<>(); ReadAnnotationConfig(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, DatabaseConfig config) { @@ -34,21 +38,32 @@ class ReadAnnotationConfig { this.disableL2Cache = config.isDisableL2Cache(); this.eagerFetchLobs = config.isEagerFetchLobs(); this.idGeneratorAutomatic = config.isIdGeneratorAutomatic(); - this.javaxValidationAnnotations = generatedPropFactory.getClassLoadConfig().isJavaxValidationAnnotationsPresent(); - this.jakartaValidationAnnotations = generatedPropFactory.getClassLoadConfig().isJakartaValidationAnnotationsPresent(); - this.jacksonAnnotations = generatedPropFactory.getClassLoadConfig().isJacksonAnnotationsPresent(); + this.useValidationNotNull = config.isUseValidationNotNull(); + ClassLoadConfig classLoadConfig = generatedPropFactory.getClassLoadConfig(); + this.javaxValidationAnnotations = classLoadConfig.isJavaxValidationAnnotationsPresent(); + this.jakartaValidationAnnotations = classLoadConfig.isJakartaValidationAnnotationsPresent(); + this.jacksonAnnotations = classLoadConfig.isJacksonAnnotationsPresent(); this.metaAnnotations.add(Column.class); this.metaAnnotations.add(Formula.class); this.metaAnnotations.add(Formula.List.class); this.metaAnnotations.add(Where.class); this.metaAnnotations.add(Where.List.class); this.metaAnnotations.add(Aggregation.class); + this.javaxValidation = javaxValidationAnnotations ? new ReadValidationAnnotationsJavax(this) : null; + this.jakartaValidation = jakartaValidationAnnotations ? new ReadValidationAnnotationsJakarta(this) : null; + if (jacksonAnnotations) { + InitMetaJacksonAnnotation.init(this); + } } - public void addMetaAnnotation(Class annotation) { + void addMetaAnnotation(Class annotation) { metaAnnotations.add(annotation); } + boolean checkValidationAnnotations() { + return javaxValidationAnnotations || jakartaValidationAnnotations; + } + GeneratedPropertyFactory getGeneratedPropFactory() { return generatedPropFactory; } @@ -73,14 +88,6 @@ class ReadAnnotationConfig { return idGeneratorAutomatic; } - boolean isJavaxValidationAnnotations() { - return javaxValidationAnnotations; - } - - boolean isJakartaValidationAnnotations() { - return jakartaValidationAnnotations; - } - boolean isJacksonAnnotations() { return jacksonAnnotations; } @@ -88,4 +95,35 @@ class ReadAnnotationConfig { public Set> getMetaAnnotations() { return metaAnnotations; } + + /** + * Return true if a NotNull validation annotation is on the property. + */ + boolean isValidationNotNull(DeployBeanProperty property) { + if (!useValidationNotNull) { + return false; + } + if (javaxValidation != null && javaxValidation.isValidationNotNull(property)) { + return true; + } + if (jakartaValidation != null && jakartaValidation.isValidationNotNull(property)) { + return true; + } + return false; + } + + /** + * Return the max size of all validation @Size annotations. + */ + int maxValidationSize(DeployBeanProperty prop) { + int maxSize = 0; + if (javaxValidation != null) { + maxSize = Math.max(maxSize, javaxValidation.maxSize(prop)); + } + if (jakartaValidation != null) { + maxSize = Math.max(maxSize, jakartaValidation.maxSize(prop)); + } + return maxSize; + } + } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotations.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotations.java index 796a975aa..da2bc6030 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotations.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadAnnotations.java @@ -14,15 +14,6 @@ public class ReadAnnotations { public ReadAnnotations(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, DatabaseConfig config) { this.readConfig = new ReadAnnotationConfig(generatedPropFactory, asOfViewSuffix, versionsBetweenSuffix, config); - if (readConfig.isJavaxValidationAnnotations()) { - InitMetaJavaxValidationAnnotation.init(readConfig); - } - if (readConfig.isJakartaValidationAnnotations()) { - InitMetaJakartaValidationAnnotation.init(readConfig); - } - if (readConfig.isJacksonAnnotations()) { - InitMetaJacksonAnnotation.init(readConfig); - } } /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadValidationAnnotations.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadValidationAnnotations.java new file mode 100644 index 000000000..a2c0b8890 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadValidationAnnotations.java @@ -0,0 +1,19 @@ +package io.ebeaninternal.server.deploy.parse; + +import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; + +/** + * Reads validation NotNull and Size annotations for mapping. + */ +interface ReadValidationAnnotations { + + /** + * Return true if the property has a NotNull validation annotation. + */ + boolean isValidationNotNull(DeployBeanProperty property); + + /** + * Return the max value of the Size validation annotations on the property. + */ + int maxSize(DeployBeanProperty property); +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadValidationAnnotationsJakarta.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadValidationAnnotationsJakarta.java new file mode 100644 index 000000000..016e9f13a --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadValidationAnnotationsJakarta.java @@ -0,0 +1,51 @@ +package io.ebeaninternal.server.deploy.parse; + +import io.ebean.util.AnnotationUtil; +import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import jakarta.validation.groups.Default; + +import java.util.Collections; +import java.util.List; + +/** + * Jakarta validation annotations reader. + */ +class ReadValidationAnnotationsJakarta implements ReadValidationAnnotations { + + ReadValidationAnnotationsJakarta(ReadAnnotationConfig readConfig) { + readConfig.addMetaAnnotation(Size.class); + readConfig.addMetaAnnotation(Size.List.class); + } + + @Override + public boolean isValidationNotNull(DeployBeanProperty property) { + NotNull notNull = AnnotationUtil.get(property.getField(), NotNull.class); + return (notNull != null && isEbeanValidationGroups(notNull.groups())); + } + + private boolean isEbeanValidationGroups(Class[] groups) { + return groups.length == 0 || groups.length == 1 && Default.class.isAssignableFrom(groups[0]); + } + + @Override + public int maxSize(DeployBeanProperty property) { + int maxSize = 0; + for (Size size : getMetaAnnotationJavaxSize(property)) { + if (size.max() < Integer.MAX_VALUE) { + maxSize = Math.max(maxSize, size.max()); + } + } + return maxSize; + } + + private List getMetaAnnotationJavaxSize(DeployBeanProperty prop) { + final List size = prop.getMetaAnnotations(Size.class); + final List lists = prop.getMetaAnnotations(Size.List.class); + for (Size.List list : lists) { + Collections.addAll(size, list.value()); + } + return size; + } +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadValidationAnnotationsJavax.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadValidationAnnotationsJavax.java new file mode 100644 index 000000000..a690e69de --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/ReadValidationAnnotationsJavax.java @@ -0,0 +1,52 @@ +package io.ebeaninternal.server.deploy.parse; + +import io.ebean.util.AnnotationUtil; +import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import javax.validation.groups.Default; +import java.util.Collections; +import java.util.List; + +/** + * Javax validation annotations reader. + */ +class ReadValidationAnnotationsJavax implements ReadValidationAnnotations { + + ReadValidationAnnotationsJavax(ReadAnnotationConfig readConfig) { + readConfig.addMetaAnnotation(Size.class); + readConfig.addMetaAnnotation(Size.List.class); + } + + @Override + public boolean isValidationNotNull(DeployBeanProperty property) { + NotNull notNull = AnnotationUtil.get(property.getField(), NotNull.class); + return (notNull != null && isEbeanValidationGroups(notNull.groups())); + } + + private boolean isEbeanValidationGroups(Class[] groups) { + return groups.length == 0 || groups.length == 1 && Default.class.isAssignableFrom(groups[0]); + } + + @Override + public int maxSize(DeployBeanProperty prop) { + int maxSize = 0; + for (Size size : getMetaAnnotationJavaxSize(prop)) { + if (size.max() < Integer.MAX_VALUE) { + maxSize = Math.max(maxSize, size.max()); + } + } + return maxSize; + } + + private List getMetaAnnotationJavaxSize(DeployBeanProperty prop) { + final List size = prop.getMetaAnnotations(Size.class); + final List lists = prop.getMetaAnnotations(Size.List.class); + for (Size.List list : lists) { + Collections.addAll(size, list.value()); + } + return size; + } + +} From b7da2cbd97d0c80e6763433861852138cd990161 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Fri, 19 Feb 2021 09:29:06 +1300 Subject: [PATCH 07/31] #2173 - ebean-test: Add assertContains - DbJson.of(entityBean).assertContains(...) --- ebean-test/pom.xml | 20 +-- .../src/main/java/io/ebean/test/DbJson.java | 101 ++++++++--- .../src/main/java/io/ebean/test/IOUtils.java | 12 ++ .../src/main/java/io/ebean/test/Json.java | 168 ++++++++++++++++++ .../io/ebean/test/JsonAssertContains.java | 118 ++++++++++++ .../test/java/io/ebean/test/DbJsonTest.java | 33 ++++ .../io/ebean/test/JsonAssertContainsTest.java | 86 +++++++++ .../src/test/java/io/ebean/test/JsonTest.java | 75 ++++++++ .../test/java/org/test/BSimpleWithGen.java | 7 + .../src/test/java/org/test/PlainBean.java | 22 +++ .../test/resources/bean/contains-minimal.json | 4 + .../resources/bean/contains-with-version.json | 5 + .../src/test/resources/bean/example-bean.json | 1 + .../src/test/resources/bean/example-list.json | 2 + .../resources/contains/check-null-actual.json | 9 + .../contains/check-null-expected.json | 7 + .../resources/contains/check-type-actual.json | 3 + .../contains/check-type-expected.json | 3 + .../contains/original-subset-modified.json | 26 +++ .../resources/contains/original-subset.json | 15 ++ .../src/test/resources/contains/original.json | 27 +++ .../test/resources/example/plain-list.json | 10 ++ .../src/test/resources/example/plain.json | 4 + 23 files changed, 727 insertions(+), 31 deletions(-) create mode 100644 ebean-test/src/main/java/io/ebean/test/Json.java create mode 100644 ebean-test/src/main/java/io/ebean/test/JsonAssertContains.java create mode 100644 ebean-test/src/test/java/io/ebean/test/JsonAssertContainsTest.java create mode 100644 ebean-test/src/test/java/io/ebean/test/JsonTest.java create mode 100644 ebean-test/src/test/java/org/test/PlainBean.java create mode 100644 ebean-test/src/test/resources/bean/contains-minimal.json create mode 100644 ebean-test/src/test/resources/bean/contains-with-version.json create mode 100644 ebean-test/src/test/resources/contains/check-null-actual.json create mode 100644 ebean-test/src/test/resources/contains/check-null-expected.json create mode 100644 ebean-test/src/test/resources/contains/check-type-actual.json create mode 100644 ebean-test/src/test/resources/contains/check-type-expected.json create mode 100644 ebean-test/src/test/resources/contains/original-subset-modified.json create mode 100644 ebean-test/src/test/resources/contains/original-subset.json create mode 100644 ebean-test/src/test/resources/contains/original.json create mode 100644 ebean-test/src/test/resources/example/plain-list.json create mode 100644 ebean-test/src/test/resources/example/plain.json diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml index 92f4c590f..caef82218 100644 --- a/ebean-test/pom.xml +++ b/ebean-test/pom.xml @@ -1,21 +1,12 @@ - + 4.0.0 ebean-parent io.ebean 12.7.1-SNAPSHOT - - - - - - - - scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.6.5 - ebean test Testing support for Ebean @@ -60,6 +51,13 @@ true + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson-databind.version} + true + + io.ebean ebean-test-docker diff --git a/ebean-test/src/main/java/io/ebean/test/DbJson.java b/ebean-test/src/main/java/io/ebean/test/DbJson.java index 79de39099..3675063c1 100644 --- a/ebean-test/src/main/java/io/ebean/test/DbJson.java +++ b/ebean-test/src/main/java/io/ebean/test/DbJson.java @@ -2,34 +2,63 @@ package io.ebean.test; import io.ebean.DB; -import java.io.IOException; -import java.io.InputStream; - import static org.assertj.core.api.Assertions.assertThat; /** * Helper for testing to assert that the JSON form of an entity - * or list of entities match a String / typically test resource. + * or list of entities is as expected. + *

+ * Using assertContains() we can match the JSON form of an entity + * against a subset of JSON content. Typically the subset of JSON + * excludes generated properties like id, when created and + * when modified properties. + *

+ * + *

Assert contains

+ *
{@code
+ *
+ *    DbJson.of(order)
+ *      .assertContains("/order-partial.json");
+ *
+ * }
+ * + * + *

Assert content matches

+ *

+ * Using assertContentMatches() we are doing an exact content match. + * We typically need to replace generated property values. This assert + * will start to fail if the model changes like adding a property to + * the entity so we should use it less widely due to the maintenance + * burden we have with it. + *

* *
{@code
  *
- *    DbJson.of(timedEntries)
- *      .replace("id", "eventTime")
- *      .assertContentMatches("/assertJson/full-1-timed.json");
+ *    DbJson.of(order)
+ *      .replace("id", "whenCreated", "whenModified)
+ *      .assertContentMatches("/order-full.json");
  *
  * }
*/ public class DbJson { /** - * Create a PrettyJson object that has the JSON form of the - * entity bean or beans. + * Create a PrettyJson object that has the JSON form of the entity bean or beans. * + *

Assert contains

*
{@code
    *
-   *    DbJson.of(timedEntries)
-   *      .replace("id", "eventTime")
-   *      .assertContentMatches("/assertJson/full-1-timed.json");
+   *    DbJson.of(order)
+   *      .assertContains("/order-partial.json");
+   *
+   * }
+ * + *

Assert content matches

+ *
{@code
+   *
+   *    DbJson.of(order)
+   *      .replace("id", "whenCreated", "whenModified)
+   *      .assertContentMatches("/order-full.json");
    *
    * }
*/ @@ -41,12 +70,7 @@ public class DbJson { * Read the content for the given resource path. */ public static String readResource(String resourcePath) { - InputStream is = DbJson.class.getResourceAsStream(resourcePath); - try { - return IOUtils.readUtf8(is).trim(); - } catch (IOException e) { - throw new IllegalArgumentException(e); - } + return IOUtils.readResource(resourcePath); } /** @@ -92,7 +116,7 @@ public class DbJson { } /** - * Assert the json matches the content at the given resource path. + * Assert the json exactly matches the content at the given resource path. * *
{@code
      *
@@ -103,7 +127,44 @@ public class DbJson {
      * }
*/ public void assertContentMatches(String resourcePath) { - assertThat(rawJson).isEqualTo(readResource(resourcePath)); + assertThat(lineEnd(rawJson)).isEqualTo(lineEnd(readResource(resourcePath))); + } + + /** + * Normalise line ending characters to just use new line. + */ + private String lineEnd(String content) { + return content.replace("\r\n", "\n"); + } + + /** + * Assert the DB json contains the given json content. + *

+ * With this "contains" check the DB Json can contain more content than what + * it is checked against. Typically the DB json can contain generated properties + * like id values, when created, when modified etc and we leave these out of the + * json content we are checking against. + *

+ * + * @param json The subset json content that should be contained by the DB json. + */ + public void assertContains(String json) { + Json.assertContains(rawJson, json); + } + + /** + * Assert the DB Json contains the Json at the given resource path. + *

+ * With this "contains" check the DB Json can contain more content than what + * it is checked against. Typically the DB json can contain generated properties + * like id values, when created, when modified etc and we leave these out of the + * json content we are checking against. + *

+ * + * @param resourcePath The resource path of the JSON content we are checking against. + */ + public void assertContainsResource(String resourcePath) { + assertContains(readResource(resourcePath)); } } } diff --git a/ebean-test/src/main/java/io/ebean/test/IOUtils.java b/ebean-test/src/main/java/io/ebean/test/IOUtils.java index 79b706706..a9b98086b 100644 --- a/ebean-test/src/main/java/io/ebean/test/IOUtils.java +++ b/ebean-test/src/main/java/io/ebean/test/IOUtils.java @@ -11,6 +11,18 @@ import java.nio.charset.StandardCharsets; */ class IOUtils { + /** + * Read the content for the given resource path. + */ + static String readResource(String resourcePath) { + try { + InputStream is = IOUtils.class.getResourceAsStream(resourcePath); + return IOUtils.readUtf8(is).trim(); + } catch (IOException e) { + throw new IllegalArgumentException("Error reading resource " + resourcePath, e); + } + } + /** * Reads the entire contents of the specified input stream and return them as UTF-8 string. */ diff --git a/ebean-test/src/main/java/io/ebean/test/Json.java b/ebean-test/src/main/java/io/ebean/test/Json.java new file mode 100644 index 000000000..7612757e3 --- /dev/null +++ b/ebean-test/src/main/java/io/ebean/test/Json.java @@ -0,0 +1,168 @@ +package io.ebean.test; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.type.CollectionType; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import java.io.IOException; +import java.util.List; + +/** + * Helper to convert to and from json using Jackson object mapper and + * perform some useful asserts based on json content. + */ +public class Json { + + /** + * For reading resource json content into a bean, list or jsonNode in a fluid style. + */ + public static class Resource { + private final String resourcePath; + + private Resource(String resourcePath) { + this.resourcePath = resourcePath; + } + + /** + * Return as a plain bean. + */ + T asBean(Class cls) { + return Json.read(cls, readResource(resourcePath)); + } + + /** + * Return as a list of beans. + */ + List asList(Class cls) { + return Json.readList(cls, readResource(resourcePath)); + } + + /** + * Return as a JsonNode. + */ + JsonNode asNode() { + return Json.readNodeFromResource(resourcePath); + } + } + + private static final ObjectMapper MAPPER = initMapper(); + + /** + * For fluid style reading resource json content and return as a + * bean, list of bean or JsonNode. + *
{@code
+   *
+   *   PlainBean bean =
+   *     Json.resource("/example/plain-list.json").asBean(PlainBean.class);
+   *
+   *   List list =
+   *     Json.resource("/example/plain-list.json").asList(PlainBean.class);
+   *
+   *   JsonNode jsonNode =
+   *     Json.resource("/example/plain-list.json").asJsonNode();
+   *
+   * }
+ * + * @param resourcePath The resource path where the json content is read from + * @return The resource to convert to a bean or jsonNode etc + */ + public static Resource resource(String resourcePath) { + return new Resource(resourcePath); + } + + /** + * Assert all the fields in the expectedJson are present in actualJson and values match. + */ + public static void assertContains(String actualJson, String expectedJson) { + assertContains(readNode(actualJson), readNode(expectedJson)); + } + + /** + * Assert all the fields in the expectedJson are present in actualJson and values match. + */ + public static void assertContains(JsonNode actualJsonNode, JsonNode expectedJsonNode) { + JsonAssertContains.assertContains(actualJsonNode, expectedJsonNode); + } + + /** + * Read the content for the given resource path. + */ + public static String readResource(String resourcePath) { + return IOUtils.readResource(resourcePath); + } + + /** + * Return a bean from json content of a resource path. + */ + public static T readFromResource(Class type, String resourcePath) { + return read(type, readResource(resourcePath)); + } + + /** + * Return a typed object from json content. + */ + public static T read(Class type, String json) { + try { + return MAPPER.readValue(json, type); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Return a list of a given type from json content. + */ + public static List readList(Class type, String json) { + final CollectionType collectionType = MAPPER.getTypeFactory().constructCollectionType(List.class, type); + try { + return MAPPER.readValue(json, collectionType); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Parse json into a generic JsonNode structure from resource. + */ + public static JsonNode readNodeFromResource(String resourcePath) { + return readNode(IOUtils.readResource(resourcePath)); + } + + /** + * Parse json into a generic JsonNode structure. + */ + public static JsonNode readNode(String json) { + try { + return MAPPER.readTree(json); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Serialize object to string + */ + public static String toJsonString(Object bean) { + try { + return MAPPER.writeValueAsString(bean); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static ObjectMapper initMapper() { + return new ObjectMapper() + .registerModule(new JavaTimeModule()) + .configure(SerializationFeature.INDENT_OUTPUT, true) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false) + .configure(SerializationFeature.INDENT_OUTPUT, true) + .setSerializationInclusion(JsonInclude.Include.NON_NULL); + } + +} diff --git a/ebean-test/src/main/java/io/ebean/test/JsonAssertContains.java b/ebean-test/src/main/java/io/ebean/test/JsonAssertContains.java new file mode 100644 index 000000000..25e1e065a --- /dev/null +++ b/ebean-test/src/main/java/io/ebean/test/JsonAssertContains.java @@ -0,0 +1,118 @@ +package io.ebean.test; + +import com.fasterxml.jackson.databind.JsonNode; +import org.assertj.core.api.Assertions; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Stack; + +/** + * Perform traversal of JsonNodes comparing against an expected JsonNode that + * typically contains a subset of the data (typically excludes any generated properties + * like when modified timestamps etc). + */ +class JsonAssertContains { + + private final Stack path = new Stack<>(); + private final LinkedList errors = new LinkedList<>(); + + static void assertContains(JsonNode actualJsonNode, JsonNode expectedJsonNode) { + new JsonAssertContains().contains(actualJsonNode, expectedJsonNode); + } + + private void contains(JsonNode actualJsonNode, JsonNode expectedJsonNode) { + checkRecursive(null, actualJsonNode, expectedJsonNode); + if (!errors.isEmpty()) { + String errorsString = String.join("\n", errors); + errorsString += "\nExpected JSON fields: " + expectedJsonNode; + errorsString += "\nActual JSON: " + actualJsonNode; + Assertions.fail(errorsString); + } + } + + private void checkRecursive(String name, JsonNode actualJsonNode, JsonNode expectedJsonNode) { + if (name != null) { + path.push(name); + } + if (checkNull(actualJsonNode, expectedJsonNode)) { + if (checkType(actualJsonNode, expectedJsonNode)) { + if (checkArray(actualJsonNode, expectedJsonNode)) { + if (checkObject(actualJsonNode, expectedJsonNode)) { + checkValue(actualJsonNode, expectedJsonNode); + } + } + } + } + if (name != null) { + path.pop(); + } + } + + private boolean checkNull(JsonNode actualJsonNode, JsonNode expectedJsonNode) { + if (actualJsonNode == null) { + errors.add(String.format("Expected field '%s' to be '%s' but was null", path(), expectedJsonNode)); + return false; + } + return true; + } + + private boolean checkType(JsonNode actualJsonNode, JsonNode expectedJsonNode) { + if (!expectedJsonNode.getNodeType().equals(actualJsonNode.getNodeType())) { + errors.add(String.format("Expected field '%s' to be of type '%s' but was '%s'", path(), expectedJsonNode.getNodeType(), actualJsonNode.getNodeType())); + return false; + } + return true; + } + + private boolean checkArray(JsonNode actualJsonNode, JsonNode expectedJsonNode) { + if (!expectedJsonNode.isArray()) { + return true; + } + for (int i = 0; i < expectedJsonNode.size(); i++) { + checkRecursive("[" + i + "]", actualJsonNode.get(i), expectedJsonNode.get(i)); + } + // do not continue (object or scalar type check) + return false; + } + + private boolean checkObject(JsonNode actualJsonNode, JsonNode expectedJsonNode) { + if (!expectedJsonNode.isObject()) { + return true; + } + Iterator> expectedFields = expectedJsonNode.fields(); + while (expectedFields.hasNext()) { + Map.Entry expectedField = expectedFields.next(); + String expectedKey = expectedField.getKey(); + JsonNode actualNode = actualJsonNode.get(expectedKey); + if (actualNode == null) { + errors.add(String.format("Expected field '%s' to be present", path(expectedKey))); + } else { + checkRecursive(expectedKey, actualNode, expectedField.getValue()); + } + } + // do not continue (scalar type check) + return false; + } + + private void checkValue(JsonNode actualJsonNode, JsonNode expectedJsonNode) { + if (!expectedJsonNode.equals(actualJsonNode)) { + errors.add(String.format("Expected field '%s' to be equal to '%s' but was '%s'", path(), expectedJsonNode, actualJsonNode)); + } + } + + String path(String expectedKey) { + if (path.isEmpty()) { + return expectedKey; + } + return path() + "." + expectedKey; + } + + String path() { + if (path.isEmpty()) { + return ""; + } + return String.join(".", path).replace(".[", "["); + } +} diff --git a/ebean-test/src/test/java/io/ebean/test/DbJsonTest.java b/ebean-test/src/test/java/io/ebean/test/DbJsonTest.java index dc841c88f..365c019cd 100644 --- a/ebean-test/src/test/java/io/ebean/test/DbJsonTest.java +++ b/ebean-test/src/test/java/io/ebean/test/DbJsonTest.java @@ -1,11 +1,13 @@ package io.ebean.test; +import com.fasterxml.jackson.databind.JsonNode; import io.ebean.DB; import org.junit.Test; import org.test.BSimpleWithGen; import java.util.List; +import static io.ebean.test.DbJson.readResource; import static org.assertj.core.api.Assertions.assertThat; @@ -36,6 +38,37 @@ public class DbJsonTest { //.withPlaceholder("_") .replace("id", "whenModified") .assertContentMatches("/bean/example-list.json"); + } + @Test + public void assertContains_pass() { + + BSimpleWithGen bean = new BSimpleWithGen("something-contains-me", "YeahNah"); + DB.save(bean); + + BSimpleWithGen found = DB.find(BSimpleWithGen.class, bean.getId()); + + DbJson.of(found).assertContainsResource("/bean/contains-minimal.json"); + DbJson.of(found).assertContains(readResource("/bean/contains-with-version.json")); + + DB.delete(bean); + } + + @Test + public void asJson() { + + BSimpleWithGen bean = new BSimpleWithGen("other"); + DB.save(bean); + + String asJson = DbJson.of(bean) + .withPlaceholder("\"*Replaced*\"") + .replace("id") + .asJson(); + + JsonNode node = Json.readNode(asJson); + assertThat(node.get("id").asText()).isEqualTo("*Replaced*"); + assertThat(node.get("name").asText()).isEqualTo("other"); + + DB.delete(bean); } } diff --git a/ebean-test/src/test/java/io/ebean/test/JsonAssertContainsTest.java b/ebean-test/src/test/java/io/ebean/test/JsonAssertContainsTest.java new file mode 100644 index 000000000..2c25776f9 --- /dev/null +++ b/ebean-test/src/test/java/io/ebean/test/JsonAssertContainsTest.java @@ -0,0 +1,86 @@ +package io.ebean.test; + +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.Test; + +import java.util.stream.Stream; + +import static io.ebean.test.Json.readNodeFromResource; +import static org.assertj.core.api.Assertions.assertThat; + + +public class JsonAssertContainsTest { + + @Test + public void assertContains_itself() { + JsonNode original = readNodeFromResource("/contains/original.json"); + JsonAssertContains.assertContains(original, original); + } + + @Test + public void assertContains_subset() { + JsonNode original = readNodeFromResource("/contains/original.json"); + JsonNode expected = readNodeFromResource("/contains/original-subset.json"); + JsonAssertContains.assertContains(original, expected); + } + + + @Test + public void testContainsFails() { + JsonNode original = readNodeFromResource("/contains/original.json"); + JsonNode expected = readNodeFromResource("/contains/original-subset-modified.json"); + try { + JsonAssertContains.assertContains(original, expected); + } catch (AssertionError e) { + String exceptionMessage = e.getMessage(); + Stream.of("Expected field 'someString1' to be equal to '\"aaaa\"' but was '\"string1\"", + "Expected field 'someValue1' to be equal to '99' but was '1'", + "Expected field 'someArray1[0]' to be of type 'STRING' but was 'NUMBER", + "Expected field 'someArray2[0].value1' to be of type 'ARRAY' but was 'NUMBER'", + "Expected field 'someArray2[0].value2' to be of type 'OBJECT' but was 'STRING'", + "Expected field 'someArray2[0].array1[0]' to be '\"1\"' but was null", + "Expected field 'someArray2[0].object1.val5' to be present", + "Expected field 'someArray2[0].object1.val6' to be present", + "Expected field 'someArray2[0].object2' to be of type 'NULL' but was 'OBJECT'", + "Expected field 'someArray2[0].objectNull' to be of type 'OBJECT' but was 'NULL'") + .forEach(assertionError -> assertThat(exceptionMessage).contains(assertionError)); + } + } + + + @Test + public void assertContains_checkNull() { + JsonNode original = readNodeFromResource("/contains/check-null-actual.json"); + JsonNode expected = readNodeFromResource("/contains/check-null-expected.json"); + try { + JsonAssertContains.assertContains(original, expected); + } catch (AssertionError e) { + String exceptionMessage = e.getMessage(); + Stream.of("Expected field 'someNull' to be of type 'NULL' but was 'STRING'", + "Expected field 'extra' to be present") + .forEach(assertionError -> assertThat(exceptionMessage).contains(assertionError)); + } + } + + @Test + public void assertContains_checkType() { + JsonNode original = readNodeFromResource("/contains/check-type-actual.json"); + JsonNode expected = readNodeFromResource("/contains/check-type-expected.json"); + try { + JsonAssertContains.assertContains(original, expected); + } catch (AssertionError e) { + String exceptionMessage = e.getMessage(); + Stream.of("Expected field 'some' to be of type 'NUMBER' but was 'STRING'") + .forEach(assertionError -> assertThat(exceptionMessage).contains(assertionError)); + } + } + + @Test + public void path_when_empty() { + JsonAssertContains contains = new JsonAssertContains(); + + assertThat(contains.path()).isEqualTo(""); + assertThat(contains.path("a")).isEqualTo("a"); + assertThat(contains.path("b")).isEqualTo("b"); + } +} diff --git a/ebean-test/src/test/java/io/ebean/test/JsonTest.java b/ebean-test/src/test/java/io/ebean/test/JsonTest.java new file mode 100644 index 000000000..baf7a1563 --- /dev/null +++ b/ebean-test/src/test/java/io/ebean/test/JsonTest.java @@ -0,0 +1,75 @@ +package io.ebean.test; + +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.Test; +import org.test.PlainBean; + +import java.util.List; + +import static io.ebean.test.Json.readNodeFromResource; +import static io.ebean.test.Json.readResource; +import static org.assertj.core.api.Assertions.assertThat; + +public class JsonTest { + + @Test + public void assertContains_subset() { + JsonNode original = readNodeFromResource("/contains/original.json"); + JsonNode expected = readNodeFromResource("/contains/original-subset.json"); + Json.assertContains(original, expected); + } + + @Test + public void readNode() { + JsonNode original = Json.readNode(readResource("/contains/original.json")); + Json.assertContains(original, original); + } + + @Test + public void resource_asNode() { + JsonNode original = Json.resource("/contains/original.json").asNode(); + JsonNode nonFluid = Json.readNode(readResource("/contains/original.json")); + assertThat(original).isEqualTo(nonFluid); + } + + @Test + public void readBean_resource_asBean() { + // traditional style from resource + PlainBean bean1 = Json.readFromResource(PlainBean.class, "/example/plain.json"); + assertThat(bean1.id).isEqualTo(42); + assertThat(bean1.name).isEqualTo("foo"); + + // traditional style given json content + PlainBean bean2 = Json.read(PlainBean.class, readResource("/example/plain.json")); + assertThat(bean2.id).isEqualTo(42); + assertThat(bean2.name).isEqualTo("foo"); + + // fluid style - resource asBean() + PlainBean bean3 = Json.resource("/example/plain.json").asBean(PlainBean.class); + assertThat(bean3.id).isEqualTo(42); + assertThat(bean3.name).isEqualTo("foo"); + } + + @Test + public void readList() { + List list = Json.readList(PlainBean.class, readResource("/example/plain-list.json")); + + String asJson = Json.toJsonString(list); + assertThat(list).hasSize(2); + assertThat(list.get(0).name).isEqualTo("foo"); + assertThat(list.get(1).name).isEqualTo("bar"); + + assertThat(asJson).contains("\"name\" : \"foo\""); + } + + @Test + public void resourceAsList() { + // fluid style - resource asList() + List list = Json.resource("/example/plain-list.json").asList(PlainBean.class); + + // traditional style + List list2 = Json.readList(PlainBean.class, readResource("/example/plain-list.json")); + assertThat(list).isEqualTo(list2); + } + +} diff --git a/ebean-test/src/test/java/org/test/BSimpleWithGen.java b/ebean-test/src/test/java/org/test/BSimpleWithGen.java index cf3c7f2f6..5ee189229 100644 --- a/ebean-test/src/test/java/org/test/BSimpleWithGen.java +++ b/ebean-test/src/test/java/org/test/BSimpleWithGen.java @@ -18,6 +18,8 @@ public class BSimpleWithGen { private String name; + private String other; + @Transient private Map> someMap; @@ -31,6 +33,11 @@ public class BSimpleWithGen { this.name = name; } + public BSimpleWithGen(String name, String other) { + this.name = name; + this.other = other; + } + public Integer getId() { return id; } diff --git a/ebean-test/src/test/java/org/test/PlainBean.java b/ebean-test/src/test/java/org/test/PlainBean.java new file mode 100644 index 000000000..103e67425 --- /dev/null +++ b/ebean-test/src/test/java/org/test/PlainBean.java @@ -0,0 +1,22 @@ +package org.test; + +import java.util.Objects; + +public class PlainBean { + + public int id; + public String name; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PlainBean plainBean = (PlainBean) o; + return id == plainBean.id && name.equals(plainBean.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } +} diff --git a/ebean-test/src/test/resources/bean/contains-minimal.json b/ebean-test/src/test/resources/bean/contains-minimal.json new file mode 100644 index 000000000..564060eec --- /dev/null +++ b/ebean-test/src/test/resources/bean/contains-minimal.json @@ -0,0 +1,4 @@ +{ + "name": "something-contains-me", + "version": 1 +} diff --git a/ebean-test/src/test/resources/bean/contains-with-version.json b/ebean-test/src/test/resources/bean/contains-with-version.json new file mode 100644 index 000000000..894b0bf14 --- /dev/null +++ b/ebean-test/src/test/resources/bean/contains-with-version.json @@ -0,0 +1,5 @@ +{ + "name": "something-contains-me", + "other": "YeahNah", + "version": 1 +} diff --git a/ebean-test/src/test/resources/bean/example-bean.json b/ebean-test/src/test/resources/bean/example-bean.json index 593babff3..e9a935767 100644 --- a/ebean-test/src/test/resources/bean/example-bean.json +++ b/ebean-test/src/test/resources/bean/example-bean.json @@ -1,6 +1,7 @@ { "id": _, "name": "something", + "other": null, "whenModified": _, "version": 1 } diff --git a/ebean-test/src/test/resources/bean/example-list.json b/ebean-test/src/test/resources/bean/example-list.json index 21264b0a7..8c7b8b681 100644 --- a/ebean-test/src/test/resources/bean/example-list.json +++ b/ebean-test/src/test/resources/bean/example-list.json @@ -1,11 +1,13 @@ [ { "id": "*", "name": "something", + "other": null, "whenModified": "*", "version": 1 }, { "id": "*", "name": "other", + "other": null, "whenModified": "*", "version": 1 } ] diff --git a/ebean-test/src/test/resources/contains/check-null-actual.json b/ebean-test/src/test/resources/contains/check-null-actual.json new file mode 100644 index 000000000..992d2aef4 --- /dev/null +++ b/ebean-test/src/test/resources/contains/check-null-actual.json @@ -0,0 +1,9 @@ +{ + "some": "val", + "someNull": "actualNotNull", + "matchNull": null, + "nullObject": { + "id": 42 + } + +} diff --git a/ebean-test/src/test/resources/contains/check-null-expected.json b/ebean-test/src/test/resources/contains/check-null-expected.json new file mode 100644 index 000000000..ce4163032 --- /dev/null +++ b/ebean-test/src/test/resources/contains/check-null-expected.json @@ -0,0 +1,7 @@ +{ + "some": "val", + "someNull": null, + "extra": "notThere", + "matchNull": null, + "nullObject": null +} diff --git a/ebean-test/src/test/resources/contains/check-type-actual.json b/ebean-test/src/test/resources/contains/check-type-actual.json new file mode 100644 index 000000000..6b240843c --- /dev/null +++ b/ebean-test/src/test/resources/contains/check-type-actual.json @@ -0,0 +1,3 @@ +{ + "some": "val" +} diff --git a/ebean-test/src/test/resources/contains/check-type-expected.json b/ebean-test/src/test/resources/contains/check-type-expected.json new file mode 100644 index 000000000..5b0cf6957 --- /dev/null +++ b/ebean-test/src/test/resources/contains/check-type-expected.json @@ -0,0 +1,3 @@ +{ + "some": 42 +} diff --git a/ebean-test/src/test/resources/contains/original-subset-modified.json b/ebean-test/src/test/resources/contains/original-subset-modified.json new file mode 100644 index 000000000..107d557d2 --- /dev/null +++ b/ebean-test/src/test/resources/contains/original-subset-modified.json @@ -0,0 +1,26 @@ +{ + "someString1": "aaaa", + "someString2": "string2", + "someValue1": 99, + "someValue2": 2, + "someArray1": [ + "a" + ], + "someArray2": [ + { + "value1": [], + "value2": {}, + "array1": [ + "1" + ], + "object1": { + "val5": "a", + "val6": 1 + }, + "object2": null, + "objectNull": { + "val": [] + } + } + ] +} diff --git a/ebean-test/src/test/resources/contains/original-subset.json b/ebean-test/src/test/resources/contains/original-subset.json new file mode 100644 index 000000000..e5823e2a4 --- /dev/null +++ b/ebean-test/src/test/resources/contains/original-subset.json @@ -0,0 +1,15 @@ +{ + "someString1": "string1", + "someValue1": 1, + "someArray2": [ + { + "value1": 11, + "array1": [], + "object2": { + "v1": "v1", + "v4": [] + }, + "objectNull": null + } + ] +} diff --git a/ebean-test/src/test/resources/contains/original.json b/ebean-test/src/test/resources/contains/original.json new file mode 100644 index 000000000..063affac4 --- /dev/null +++ b/ebean-test/src/test/resources/contains/original.json @@ -0,0 +1,27 @@ +{ + "someString1": "string1", + "someString2": "string2", + "someValue1": 1, + "someValue2": 2, + "someArray1": [ + 1, + 2, + 3, + 4 + ], + "someArray2": [ + { + "value1": 11, + "value2": "22", + "array1": [], + "object1": {}, + "object2": { + "v1": "v1", + "v2": 1, + "v3": {}, + "v4": [] + }, + "objectNull": null + } + ] +} diff --git a/ebean-test/src/test/resources/example/plain-list.json b/ebean-test/src/test/resources/example/plain-list.json new file mode 100644 index 000000000..49bc4c34d --- /dev/null +++ b/ebean-test/src/test/resources/example/plain-list.json @@ -0,0 +1,10 @@ +[ + { + "id": 42, + "name": "foo" + }, + { + "id": 55, + "name": "bar" + } +] diff --git a/ebean-test/src/test/resources/example/plain.json b/ebean-test/src/test/resources/example/plain.json new file mode 100644 index 000000000..63632de85 --- /dev/null +++ b/ebean-test/src/test/resources/example/plain.json @@ -0,0 +1,4 @@ +{ + "id": 42, + "name": "foo" +} From 99d7a4e4b4ac8d41accd08ccf380abd37111305a Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Fri, 19 Feb 2021 09:43:56 +1300 Subject: [PATCH 08/31] Tidy up pom jackson dependency versions (all optional dependencies) and bump to 2.12.1 --- ebean-api/pom.xml | 9 ++------- ebean-core-type/pom.xml | 7 +------ ebean-core/pom.xml | 9 ++------- ebean-postgis/pom.xml | 3 +-- ebean-test/pom.xml | 8 ++------ pom.xml | 1 + 6 files changed, 9 insertions(+), 28 deletions(-) diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index 80d8db37a..dc2bd347e 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -11,11 +11,6 @@ ebean api ebean-api - - 2.11.3 - 2.11.3 - - @@ -14,7 +14,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.6.5 + ebean-parent-12.7.1 ebean autotune @@ -26,7 +26,7 @@ io.ebean ebean-core - 12.7.1-SNAPSHOT + 12.7.1 provided diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml index c6219cfea..7e773ec45 100644 --- a/ebean-bom/pom.xml +++ b/ebean-bom/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1-SNAPSHOT + 12.7.1 ebean bom @@ -81,88 +81,88 @@ io.ebean ebean - 12.7.1-SNAPSHOT + 12.7.1 io.ebean ebean-api - 12.7.1-SNAPSHOT + 12.7.1 io.ebean ebean-core - 12.7.1-SNAPSHOT + 12.7.1 io.ebean ebean-core-type - 12.7.1-SNAPSHOT + 12.7.1 io.ebean ebean-ddl-generator - 12.7.1-SNAPSHOT + 12.7.1 io.ebean ebean-externalmapping-api - 12.7.1-SNAPSHOT + 12.7.1 io.ebean ebean-externalmapping-xml - 12.7.1-SNAPSHOT + 12.7.1 io.ebean ebean-autotune - 12.7.1-SNAPSHOT + 12.7.1 io.ebean ebean-querybean - 12.7.1-SNAPSHOT + 12.7.1 io.ebean querybean-generator - 12.7.1-SNAPSHOT + 12.7.1 provided io.ebean kotlin-querybean-generator - 12.7.1-SNAPSHOT + 12.7.1 provided io.ebean ebean-test - 12.7.1-SNAPSHOT + 12.7.1 test io.ebean ebean-postgis - 12.7.1-SNAPSHOT + 12.7.1 io.ebean ebean-redis - 12.7.1-SNAPSHOT + 12.7.1 diff --git a/ebean-core-type/pom.xml b/ebean-core-type/pom.xml index 36e38b2da..dbe59f45e 100644 --- a/ebean-core-type/pom.xml +++ b/ebean-core-type/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1-SNAPSHOT + 12.7.1 ebean-core-type @@ -16,7 +16,7 @@ io.ebean ebean-api - 12.7.1-SNAPSHOT + 12.7.1 diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index e335e97c3..48f2a741d 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -3,7 +3,7 @@ ebean-parent io.ebean - 12.7.1-SNAPSHOT + 12.7.1 ebean-core @@ -15,7 +15,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.6.5 + ebean-parent-12.7.1 @@ -87,19 +87,19 @@ io.ebean ebean-api - 12.7.1-SNAPSHOT + 12.7.1 io.ebean ebean-core-type - 12.7.1-SNAPSHOT + 12.7.1 io.ebean ebean-externalmapping-api - 12.7.1-SNAPSHOT + 12.7.1 diff --git a/ebean-ddl-generator/pom.xml b/ebean-ddl-generator/pom.xml index 983902210..0662d9339 100644 --- a/ebean-ddl-generator/pom.xml +++ b/ebean-ddl-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1-SNAPSHOT + 12.7.1 ebean ddl generation @@ -29,14 +29,14 @@ io.ebean ebean-core-type - 12.7.1-SNAPSHOT + 12.7.1 provided io.ebean ebean-core - 12.7.1-SNAPSHOT + 12.7.1 provided diff --git a/ebean-externalmapping-api/pom.xml b/ebean-externalmapping-api/pom.xml index 54bb5c7bb..416410762 100644 --- a/ebean-externalmapping-api/pom.xml +++ b/ebean-externalmapping-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1-SNAPSHOT + 12.7.1 ebean external mapping api diff --git a/ebean-externalmapping-xml/pom.xml b/ebean-externalmapping-xml/pom.xml index 98f90d1ac..310ae0299 100644 --- a/ebean-externalmapping-xml/pom.xml +++ b/ebean-externalmapping-xml/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1-SNAPSHOT + 12.7.1 @@ -14,7 +14,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.6.5 + ebean-parent-12.7.1 ebean external mapping xml @@ -33,7 +33,7 @@ io.ebean ebean-externalmapping-api - 12.7.1-SNAPSHOT + 12.7.1 @@ -59,14 +59,14 @@ io.ebean ebean-core - 12.7.1-SNAPSHOT + 12.7.1 test io.ebean ebean-ddl-generator - 12.7.1-SNAPSHOT + 12.7.1 test diff --git a/ebean-postgis/pom.xml b/ebean-postgis/pom.xml index 61271ef1f..3e5346f2e 100644 --- a/ebean-postgis/pom.xml +++ b/ebean-postgis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1-SNAPSHOT + 12.7.1 ebean postgis @@ -23,7 +23,7 @@ io.ebean ebean-core - 12.7.1-SNAPSHOT + 12.7.1 provided @@ -74,7 +74,7 @@ io.ebean ebean-test - 12.7.1-SNAPSHOT + 12.7.1 test diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml index 0bdeeb257..d105a5abc 100644 --- a/ebean-querybean/pom.xml +++ b/ebean-querybean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1-SNAPSHOT + 12.7.1 ebean querybean @@ -17,7 +17,7 @@ io.ebean ebean-core - 12.7.1-SNAPSHOT + 12.7.1 provided @@ -57,21 +57,21 @@ io.ebean ebean-ddl-generator - 12.7.1-SNAPSHOT + 12.7.1 test io.ebean querybean-generator - 12.7.1-SNAPSHOT + 12.7.1 test io.ebean ebean-test - 12.7.1-SNAPSHOT + 12.7.1 test diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml index 98186aa7f..a1f4b6c04 100644 --- a/ebean-redis/pom.xml +++ b/ebean-redis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1-SNAPSHOT + 12.7.1 ebean-redis @@ -22,35 +22,35 @@ io.ebean ebean-api - 12.7.1-SNAPSHOT + 12.7.1 provided io.ebean ebean-core - 12.7.1-SNAPSHOT + 12.7.1 provided io.ebean ebean-querybean - 12.7.1-SNAPSHOT + 12.7.1 test io.ebean querybean-generator - 12.7.1-SNAPSHOT + 12.7.1 test io.ebean ebean-test - 12.7.1-SNAPSHOT + 12.7.1 test diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml index 420986916..ac9a323db 100644 --- a/ebean-test/pom.xml +++ b/ebean-test/pom.xml @@ -1,11 +1,10 @@ - + 4.0.0 ebean-parent io.ebean - 12.7.1-SNAPSHOT + 12.7.1 ebean test @@ -30,14 +29,14 @@ io.ebean ebean-core - 12.7.1-SNAPSHOT + 12.7.1 provided io.ebean ebean-ddl-generator - 12.7.1-SNAPSHOT + 12.7.1 diff --git a/ebean/pom.xml b/ebean/pom.xml index 337b3deba..b0cad2be9 100644 --- a/ebean/pom.xml +++ b/ebean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1-SNAPSHOT + 12.7.1 ebean composite @@ -22,20 +22,20 @@ io.ebean ebean-api - 12.7.1-SNAPSHOT + 12.7.1 io.ebean ebean-core - 12.7.1-SNAPSHOT + 12.7.1 io.ebean ebean-querybean - 12.7.1-SNAPSHOT + 12.7.1 diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml index 07993d49e..02d01a09f 100644 --- a/kotlin-querybean-generator/pom.xml +++ b/kotlin-querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1-SNAPSHOT + 12.7.1 kotlin querybean generator @@ -29,7 +29,7 @@ io.ebean ebean-querybean - 12.7.1-SNAPSHOT + 12.7.1 test @@ -43,7 +43,7 @@ io.ebean ebean-core - 12.7.1-SNAPSHOT + 12.7.1 test @@ -64,7 +64,7 @@ io.ebean ebean-ddl-generator - 12.7.1-SNAPSHOT + 12.7.1 test diff --git a/pom.xml b/pom.xml index 12b4d3540..547bd731e 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ io.ebean ebean-parent - 12.7.1-SNAPSHOT + 12.7.1 pom ebean parent @@ -18,7 +18,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.6.5 + ebean-parent-12.7.1 diff --git a/querybean-generator/pom.xml b/querybean-generator/pom.xml index 90140df71..64c2c4d91 100644 --- a/querybean-generator/pom.xml +++ b/querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1-SNAPSHOT + 12.7.1 querybean generator From b77c62e0db470698a62b1e3335e62ea1959ac91a Mon Sep 17 00:00:00 2001 From: robin Date: Fri, 19 Feb 2021 22:51:03 +1300 Subject: [PATCH 11/31] [maven-release-plugin] prepare for next development iteration --- ebean-api/pom.xml | 2 +- ebean-autotune/pom.xml | 6 +++--- ebean-bom/pom.xml | 30 +++++++++++++++--------------- ebean-core-type/pom.xml | 4 ++-- ebean-core/pom.xml | 10 +++++----- ebean-ddl-generator/pom.xml | 6 +++--- ebean-externalmapping-api/pom.xml | 2 +- ebean-externalmapping-xml/pom.xml | 10 +++++----- ebean-postgis/pom.xml | 6 +++--- ebean-querybean/pom.xml | 10 +++++----- ebean-redis/pom.xml | 12 ++++++------ ebean-test/pom.xml | 6 +++--- ebean/pom.xml | 8 ++++---- kotlin-querybean-generator/pom.xml | 8 ++++---- pom.xml | 4 ++-- querybean-generator/pom.xml | 2 +- 16 files changed, 63 insertions(+), 63 deletions(-) diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index 188db1c49..79962ff51 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1 + 12.7.2-SNAPSHOT ebean api diff --git a/ebean-autotune/pom.xml b/ebean-autotune/pom.xml index 878f95090..b86469ddd 100644 --- a/ebean-autotune/pom.xml +++ b/ebean-autotune/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1 + 12.7.2-SNAPSHOT @@ -14,7 +14,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.7.1 + ebean-parent-12.6.5 ebean autotune @@ -26,7 +26,7 @@ io.ebean ebean-core - 12.7.1 + 12.7.2-SNAPSHOT provided diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml index 7e773ec45..c47601c85 100644 --- a/ebean-bom/pom.xml +++ b/ebean-bom/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1 + 12.7.2-SNAPSHOT ebean bom @@ -81,88 +81,88 @@ io.ebean ebean - 12.7.1 + 12.7.2-SNAPSHOT io.ebean ebean-api - 12.7.1 + 12.7.2-SNAPSHOT io.ebean ebean-core - 12.7.1 + 12.7.2-SNAPSHOT io.ebean ebean-core-type - 12.7.1 + 12.7.2-SNAPSHOT io.ebean ebean-ddl-generator - 12.7.1 + 12.7.2-SNAPSHOT io.ebean ebean-externalmapping-api - 12.7.1 + 12.7.2-SNAPSHOT io.ebean ebean-externalmapping-xml - 12.7.1 + 12.7.2-SNAPSHOT io.ebean ebean-autotune - 12.7.1 + 12.7.2-SNAPSHOT io.ebean ebean-querybean - 12.7.1 + 12.7.2-SNAPSHOT io.ebean querybean-generator - 12.7.1 + 12.7.2-SNAPSHOT provided io.ebean kotlin-querybean-generator - 12.7.1 + 12.7.2-SNAPSHOT provided io.ebean ebean-test - 12.7.1 + 12.7.2-SNAPSHOT test io.ebean ebean-postgis - 12.7.1 + 12.7.2-SNAPSHOT io.ebean ebean-redis - 12.7.1 + 12.7.2-SNAPSHOT diff --git a/ebean-core-type/pom.xml b/ebean-core-type/pom.xml index dbe59f45e..f0db31343 100644 --- a/ebean-core-type/pom.xml +++ b/ebean-core-type/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1 + 12.7.2-SNAPSHOT ebean-core-type @@ -16,7 +16,7 @@ io.ebean ebean-api - 12.7.1 + 12.7.2-SNAPSHOT diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index 48f2a741d..bab9e7624 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -3,7 +3,7 @@ ebean-parent io.ebean - 12.7.1 + 12.7.2-SNAPSHOT ebean-core @@ -15,7 +15,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.7.1 + ebean-parent-12.6.5 @@ -87,19 +87,19 @@ io.ebean ebean-api - 12.7.1 + 12.7.2-SNAPSHOT io.ebean ebean-core-type - 12.7.1 + 12.7.2-SNAPSHOT io.ebean ebean-externalmapping-api - 12.7.1 + 12.7.2-SNAPSHOT diff --git a/ebean-ddl-generator/pom.xml b/ebean-ddl-generator/pom.xml index 0662d9339..90f3270e4 100644 --- a/ebean-ddl-generator/pom.xml +++ b/ebean-ddl-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1 + 12.7.2-SNAPSHOT ebean ddl generation @@ -29,14 +29,14 @@ io.ebean ebean-core-type - 12.7.1 + 12.7.2-SNAPSHOT provided io.ebean ebean-core - 12.7.1 + 12.7.2-SNAPSHOT provided diff --git a/ebean-externalmapping-api/pom.xml b/ebean-externalmapping-api/pom.xml index 416410762..76e5ced23 100644 --- a/ebean-externalmapping-api/pom.xml +++ b/ebean-externalmapping-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1 + 12.7.2-SNAPSHOT ebean external mapping api diff --git a/ebean-externalmapping-xml/pom.xml b/ebean-externalmapping-xml/pom.xml index 310ae0299..620a1de73 100644 --- a/ebean-externalmapping-xml/pom.xml +++ b/ebean-externalmapping-xml/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1 + 12.7.2-SNAPSHOT @@ -14,7 +14,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.7.1 + ebean-parent-12.6.5 ebean external mapping xml @@ -33,7 +33,7 @@ io.ebean ebean-externalmapping-api - 12.7.1 + 12.7.2-SNAPSHOT @@ -59,14 +59,14 @@ io.ebean ebean-core - 12.7.1 + 12.7.2-SNAPSHOT test io.ebean ebean-ddl-generator - 12.7.1 + 12.7.2-SNAPSHOT test diff --git a/ebean-postgis/pom.xml b/ebean-postgis/pom.xml index 3e5346f2e..d8acdb157 100644 --- a/ebean-postgis/pom.xml +++ b/ebean-postgis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1 + 12.7.2-SNAPSHOT ebean postgis @@ -23,7 +23,7 @@ io.ebean ebean-core - 12.7.1 + 12.7.2-SNAPSHOT provided @@ -74,7 +74,7 @@ io.ebean ebean-test - 12.7.1 + 12.7.2-SNAPSHOT test diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml index d105a5abc..52ab4f7c0 100644 --- a/ebean-querybean/pom.xml +++ b/ebean-querybean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1 + 12.7.2-SNAPSHOT ebean querybean @@ -17,7 +17,7 @@ io.ebean ebean-core - 12.7.1 + 12.7.2-SNAPSHOT provided @@ -57,21 +57,21 @@ io.ebean ebean-ddl-generator - 12.7.1 + 12.7.2-SNAPSHOT test io.ebean querybean-generator - 12.7.1 + 12.7.2-SNAPSHOT test io.ebean ebean-test - 12.7.1 + 12.7.2-SNAPSHOT test diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml index a1f4b6c04..cf4f83208 100644 --- a/ebean-redis/pom.xml +++ b/ebean-redis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1 + 12.7.2-SNAPSHOT ebean-redis @@ -22,35 +22,35 @@ io.ebean ebean-api - 12.7.1 + 12.7.2-SNAPSHOT provided io.ebean ebean-core - 12.7.1 + 12.7.2-SNAPSHOT provided io.ebean ebean-querybean - 12.7.1 + 12.7.2-SNAPSHOT test io.ebean querybean-generator - 12.7.1 + 12.7.2-SNAPSHOT test io.ebean ebean-test - 12.7.1 + 12.7.2-SNAPSHOT test diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml index ac9a323db..32810609f 100644 --- a/ebean-test/pom.xml +++ b/ebean-test/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1 + 12.7.2-SNAPSHOT ebean test @@ -29,14 +29,14 @@ io.ebean ebean-core - 12.7.1 + 12.7.2-SNAPSHOT provided io.ebean ebean-ddl-generator - 12.7.1 + 12.7.2-SNAPSHOT diff --git a/ebean/pom.xml b/ebean/pom.xml index b0cad2be9..e1bf6282a 100644 --- a/ebean/pom.xml +++ b/ebean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1 + 12.7.2-SNAPSHOT ebean composite @@ -22,20 +22,20 @@ io.ebean ebean-api - 12.7.1 + 12.7.2-SNAPSHOT io.ebean ebean-core - 12.7.1 + 12.7.2-SNAPSHOT io.ebean ebean-querybean - 12.7.1 + 12.7.2-SNAPSHOT diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml index 02d01a09f..8529dbe7f 100644 --- a/kotlin-querybean-generator/pom.xml +++ b/kotlin-querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1 + 12.7.2-SNAPSHOT kotlin querybean generator @@ -29,7 +29,7 @@ io.ebean ebean-querybean - 12.7.1 + 12.7.2-SNAPSHOT test @@ -43,7 +43,7 @@ io.ebean ebean-core - 12.7.1 + 12.7.2-SNAPSHOT test @@ -64,7 +64,7 @@ io.ebean ebean-ddl-generator - 12.7.1 + 12.7.2-SNAPSHOT test diff --git a/pom.xml b/pom.xml index 547bd731e..6edda0cd5 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ io.ebean ebean-parent - 12.7.1 + 12.7.2-SNAPSHOT pom ebean parent @@ -18,7 +18,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.7.1 + ebean-parent-12.6.5 diff --git a/querybean-generator/pom.xml b/querybean-generator/pom.xml index 64c2c4d91..9f5303d0f 100644 --- a/querybean-generator/pom.xml +++ b/querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.1 + 12.7.2-SNAPSHOT querybean generator From d036af8d1a5b2ed3ccff01ea95c4c8bc8cbf0938 Mon Sep 17 00:00:00 2001 From: Ben Kempe Date: Sun, 28 Feb 2021 19:06:51 +0100 Subject: [PATCH 12/31] Add more assertions for nested one-to-many fetch --- .../tests/query/joins/TestQueryJoinManyNonRoot.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ebean-core/src/test/java/org/tests/query/joins/TestQueryJoinManyNonRoot.java b/ebean-core/src/test/java/org/tests/query/joins/TestQueryJoinManyNonRoot.java index 82eb65935..b5f2f61bf 100644 --- a/ebean-core/src/test/java/org/tests/query/joins/TestQueryJoinManyNonRoot.java +++ b/ebean-core/src/test/java/org/tests/query/joins/TestQueryJoinManyNonRoot.java @@ -11,6 +11,7 @@ import org.junit.Assert; import org.junit.Test; import java.util.List; +import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -54,6 +55,17 @@ public class TestQueryJoinManyNonRoot extends BaseTestCase { assertTrue(sql.contains("join o_customer t1 on t1.id ")); assertTrue(sql.contains("left join contact t2 on")); + Map ordersById = Ebean.find(Order.class).setMapKey("id").where().gt("id", 0).query().findMap(); + for (Order o: list) { + int withoutFetch = ordersById.get(o.getId()).getCustomer().getContacts().size(); + int withFetch = o.getCustomer().getContacts().size(); + assertEquals(String.format("order.customer.contacts for order %d did not match. " + + "Items without fetch: %d, with fetch: %d", o.getId(), withoutFetch, withFetch), + withoutFetch, + withFetch); + } + + // select t0.id c0, t0.status c1, t0.order_date c2, t0.ship_date c3, t1.name c4, t0.cretime c5, t0.updtime c6, // t1.id c7, t1.status c8, t1.name c9, t1.smallnote c10, t1.anniversary c11, t1.cretime c12, t1.updtime c13, t1.billing_address_id c14, t1.shipping_address_id c15, // t2.id c16, t2.first_name c17, t2.last_name c18, t2.phone c19, t2.mobile c20, t2.email c21, t2.cretime c22, t2.updtime c23, t2.customer_id c24, t2.group_id c25 From 6acf93c97804deba3e7e54fcbeae4a41ded8b88a Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Mon, 1 Mar 2021 13:38:40 +1300 Subject: [PATCH 13/31] #2178 - Fix for duplicate instances added to collection for nested one-to-many fetch This is an initial fix for this case. --- .../java/io/ebeaninternal/server/query/SqlTreeNodeManyRoot.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6b050f51a..a1f6ea357 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 @@ -36,7 +36,7 @@ final class SqlTreeNodeManyRoot extends SqlTreeNodeBean { EntityBean detailBean = super.load(cquery, null, null); // initialise the collection and add detailBean if it is not null if (contextParent != null) { - manyProp.addBeanToCollectionWithCreate(contextParent, detailBean, false); + manyProp.addBeanToCollectionWithCreate(contextParent, detailBean, true); } return detailBean; } From 5ebad53072e5b72446ed6ce9cb4828b79134f343 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Mon, 1 Mar 2021 14:00:40 +1300 Subject: [PATCH 14/31] #2179 - Improve internals for outer join resulting in null many bean Refactor improving the BeanList.internalAddWithCheck() for the null bean case --- ebean-api/src/main/java/io/ebean/common/BeanList.java | 2 +- ebean-api/src/main/java/io/ebean/common/BeanMap.java | 2 +- ebean-api/src/main/java/io/ebean/common/BeanSet.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/common/BeanList.java b/ebean-api/src/main/java/io/ebean/common/BeanList.java index 0aee7f211..0aa774a81 100644 --- a/ebean-api/src/main/java/io/ebean/common/BeanList.java +++ b/ebean-api/src/main/java/io/ebean/common/BeanList.java @@ -87,7 +87,7 @@ public final class BeanList extends AbstractBeanCollection implements List @Override public void internalAddWithCheck(Object bean) { - if (list == null || !containsInstance(bean)) { + if (list == null || bean == null || !containsInstance(bean)) { internalAdd(bean); } } diff --git a/ebean-api/src/main/java/io/ebean/common/BeanMap.java b/ebean-api/src/main/java/io/ebean/common/BeanMap.java index b2ded02c9..09b37d38a 100644 --- a/ebean-api/src/main/java/io/ebean/common/BeanMap.java +++ b/ebean-api/src/main/java/io/ebean/common/BeanMap.java @@ -77,7 +77,7 @@ public final class BeanMap extends AbstractBeanCollection implements Ma } public void internalPutWithCheck(Object key, Object bean) { - if (map == null || !map.containsKey(key)) { + if (map == null || key == null || !map.containsKey(key)) { internalPut(key, bean); } } diff --git a/ebean-api/src/main/java/io/ebean/common/BeanSet.java b/ebean-api/src/main/java/io/ebean/common/BeanSet.java index ec4dc3829..91f0ae672 100644 --- a/ebean-api/src/main/java/io/ebean/common/BeanSet.java +++ b/ebean-api/src/main/java/io/ebean/common/BeanSet.java @@ -70,7 +70,7 @@ public final class BeanSet extends AbstractBeanCollection implements Set Date: Mon, 1 Mar 2021 17:44:13 +1300 Subject: [PATCH 15/31] Refactor internals add SqlTreeRoot as follow up to #2179 Adds SqlTreeRoot interface to make it more explicit that SqlTreeNodeRoot is the only valid top level node in the sql tree (and as such has the root level load() method) --- .../io/ebeaninternal/server/query/CQuery.java | 8 ++--- .../ebeaninternal/server/query/SqlTree.java | 4 +-- .../server/query/SqlTreeNode.java | 5 --- .../server/query/SqlTreeNodeBean.java | 14 -------- .../server/query/SqlTreeNodeExtraJoin.java | 8 ----- .../query/SqlTreeNodeFormulaWhereJoin.java | 6 ---- .../query/SqlTreeNodeManyWhereJoin.java | 6 ---- .../server/query/SqlTreeNodeRoot.java | 26 +++++++++++++- .../server/query/SqlTreeRoot.java | 34 +++++++++++++++++++ 9 files changed, 65 insertions(+), 46 deletions(-) create mode 100644 ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeRoot.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 861ccc337..9f30e9a29 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 @@ -142,7 +142,7 @@ public class CQuery implements DbReadContext, CancelableQuery, SpiProfileTran * Tree that knows how to build the master and detail beans from the * resultSet. */ - private final SqlTreeNode rootNode; + private final SqlTreeRoot rootNode; /** * For master detail query. @@ -444,13 +444,13 @@ public class CQuery implements DbReadContext, CancelableQuery, SpiProfileTran if (manyProperty == null) { // only single resultSet row required to build object so we are done // read a single resultSet row into single bean - nextBean = rootNode.load(this, null, null); + nextBean = rootNode.load(this); return true; } if (nextBean == null) { // very first read - nextBean = rootNode.load(this, null, null); + nextBean = rootNode.load(this); } else { // nextBean set to previously read currentBean nextBean = currentBean; @@ -478,7 +478,7 @@ public class CQuery implements DbReadContext, CancelableQuery, SpiProfileTran * is different to the nextBean (false if we need to read more rows). */ private boolean checkForDifferentBean() throws SQLException { - currentBean = rootNode.load(this, null, null); + currentBean = rootNode.load(this); return currentBean != nextBean; } 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 c537ace29..1befa650b 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 @@ -122,8 +122,8 @@ class SqlTree { return inheritanceWhereSql; } - SqlTreeNode getRootNode() { - return rootNode; + SqlTreeRoot getRootNode() { + return (SqlTreeRoot)rootNode; } /** 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 b341802d1..39844cb59 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 @@ -73,11 +73,6 @@ interface SqlTreeNode { */ EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean contextBean) throws SQLException; - /** - * Load a version of a @History bean with effective dates. - */ - Version loadVersion(DbReadContext ctx) throws SQLException; - /** * Return true if the query has a many join. */ 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 010793506..51ed19d32 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 @@ -192,20 +192,6 @@ class SqlTreeNodeBean implements SqlTreeNode { } } - /** - * 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); - } - /** * Load that takes into account inheritance. */ 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 fbab8e6b1..074457a30 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 @@ -178,14 +178,6 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode { return null; } - /** - * Does nothing. - */ - @Override - public Version loadVersion(DbReadContext ctx) { - 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 f5e47eac5..20a3037da 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 @@ -99,12 +99,6 @@ class SqlTreeNodeFormulaWhereJoin implements SqlTreeNode { return null; } - @Override - public Version loadVersion(DbReadContext ctx) { - // nothing to do here - return null; - } - @Override 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 f3d1999ca..c537c4bf7 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 @@ -144,12 +144,6 @@ class SqlTreeNodeManyWhereJoin implements SqlTreeNode { return null; } - @Override - public Version loadVersion(DbReadContext ctx) { - // 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 3a7fffd98..e52477cf1 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,16 +1,21 @@ 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 { +final class SqlTreeNodeRoot extends SqlTreeNodeBean implements SqlTreeRoot { private final TableJoin includeJoin; @@ -35,6 +40,25 @@ final class SqlTreeNodeRoot extends SqlTreeNodeBean { 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); + } + @Override public boolean isSqlDistinct() { return sqlDistinct; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeRoot.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeRoot.java new file mode 100644 index 000000000..64938bc22 --- /dev/null +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeRoot.java @@ -0,0 +1,34 @@ +package io.ebeaninternal.server.query; + +import io.ebean.Version; +import io.ebean.bean.EntityBean; +import io.ebean.core.type.ScalarDataReader; +import io.ebeaninternal.server.deploy.DbReadContext; + +import java.sql.SQLException; + +/** + * The root level node of the SqlTree. + */ +interface SqlTreeRoot { + + /** + * Load the bean from the DbReadContext. + *

+ * 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) throws SQLException; + + /** + * Load a version of a @History bean with effective dates. + */ + Version loadVersion(DbReadContext ctx) throws SQLException; + + /** + * Return a Scalar single attribute reader based on the first property. + */ + ScalarDataReader getSingleAttributeReader(); + +} From 68138bc0099f707a4f9191a7cfb1ddfd2ef40e39 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Mon, 1 Mar 2021 17:45:52 +1300 Subject: [PATCH 16/31] Refactor internals no effective change - reorder methods of SqlTreeNodeManyRoot --- .../server/query/SqlTreeNodeManyRoot.java | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) 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 a1f6ea357..4947514d1 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 @@ -18,14 +18,9 @@ final class SqlTreeNodeManyRoot extends SqlTreeNodeBean { this.manyProp = prop; } - /** - * Append the property columns to the buffer. - */ @Override - public void appendDistinctOn(DbSqlContext ctx, boolean subQuery) { - ctx.pushTableAlias(prefix); - appendSelectId(ctx, idBinder.getBeanProperty()); - ctx.popTableAlias(); + public boolean hasMany() { + return true; } @Override @@ -41,6 +36,15 @@ final class SqlTreeNodeManyRoot extends SqlTreeNodeBean { return detailBean; } + /** + * Append the property columns to the buffer. + */ + @Override + public void appendDistinctOn(DbSqlContext ctx, boolean subQuery) { + ctx.pushTableAlias(prefix); + appendSelectId(ctx, idBinder.getBeanProperty()); + ctx.popTableAlias(); + } /** * append extraWhere to the join. @@ -64,9 +68,4 @@ final class SqlTreeNodeManyRoot extends SqlTreeNodeBean { public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { super.appendFrom(ctx, joinType.autoToOuter()); } - - @Override - public boolean hasMany() { - return true; - } } From 5a73c85e6eab847037c83ea53d5eabbe11862e78 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Mon, 1 Mar 2021 18:36:49 +1300 Subject: [PATCH 17/31] Refactor internals - SqlTreeNodeBean add derived readIdNormal + private methods This is no effective change --- .../server/query/SqlTreeNodeBean.java | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) 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 51ed19d32..011d9f1a7 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,6 +1,5 @@ package io.ebeaninternal.server.query; -import io.ebean.Version; import io.ebean.bean.BeanCollection; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; @@ -16,7 +15,6 @@ import io.ebeaninternal.server.deploy.TableJoin; import io.ebeaninternal.server.deploy.id.IdBinder; import java.sql.SQLException; -import java.sql.Timestamp; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -56,6 +54,7 @@ class SqlTreeNodeBean implements SqlTreeNode { * False if report bean and has no id property. */ final boolean readId; + private final boolean readIdNormal; private final boolean disableLazyLoad; @@ -120,7 +119,8 @@ class SqlTreeNodeBean implements SqlTreeNode { boolean aggregationRoot = props.isAggregationRoot(); // the bean has an Id property and we want to use it this.readId = !aggregationRoot && withId && desc.hasId(); - this.disableLazyLoad = disableLazyLoad || !readId || desc.isRawSqlBased() || temporalVersions; + this.readIdNormal = readId && !temporalVersions; + this.disableLazyLoad = disableLazyLoad || !readIdNormal || desc.isRawSqlBased(); this.partialObject = props.isPartialObject(); this.properties = props.getProps(); this.children = myChildren == null ? NO_CHILDREN : myChildren.toArray(new SqlTreeNode[0]); @@ -253,7 +253,7 @@ class SqlTreeNodeBean implements SqlTreeNode { this.parentBean = parentBean; } - void initLazyParent() throws SQLException { + private void initLazyParent() throws SQLException { if (lazyLoadParentIdBinder != null) { lazyLoadParentId = lazyLoadParentIdBinder.read(ctx); } @@ -265,12 +265,12 @@ class SqlTreeNodeBean implements SqlTreeNode { localIdBinder = idBinder; } - void initPersistenceContext() { + private void initPersistenceContext() { queryMode = ctx.getQueryMode(); - persistenceContext = (!readId || temporalVersions) ? null : ctx.getPersistenceContext(); + persistenceContext = (!readIdNormal) ? null : ctx.getPersistenceContext(); } - void readId() throws SQLException { + private void readId() throws SQLException { if (readId) { id = localIdBinder.readSet(ctx, localBean); if (id == null) { @@ -311,7 +311,7 @@ class SqlTreeNodeBean implements SqlTreeNode { } } - void initSqlLoadBean() { + private void initSqlLoadBean() { ctx.setCurrentPrefix(prefix, pathMap); ctx.propagateState(localBean); sqlBeanLoad = new SqlBeanLoad(ctx, localType, localBean, queryMode); @@ -323,7 +323,7 @@ class SqlTreeNodeBean implements SqlTreeNode { } } - void loadChildren() throws SQLException { + private void loadChildren() throws SQLException { //boolean lazyLoadMany = false; if (localBean == null && queryMode == Mode.LAZYLOAD_MANY) { // batch lazy load many into existing contextBean @@ -338,18 +338,18 @@ class SqlTreeNodeBean implements SqlTreeNode { } } - boolean isLazyLoadManyRoot() { + private boolean isLazyLoadManyRoot() { return queryMode == Mode.LAZYLOAD_MANY && isRoot(); } - EntityBean getContextBean() { + private EntityBean getContextBean() { return contextBean; } - void postLoad() { + private void postLoad() { if (!lazyLoadMany && localBean != null) { ctx.setCurrentPrefix(prefix, pathMap); - if (readId && !temporalVersions) { + if (readIdNormal) { createListProxies(); } if (temporalMode == SpiQuery.TemporalMode.DRAFT) { @@ -411,15 +411,15 @@ class SqlTreeNodeBean implements SqlTreeNode { } } - void setBeanToParent() { + private void setBeanToParent() { if (parentBean != null) { // set this back to the parentBean nodeBeanProp.setValue(parentBean, contextBean); } } - EntityBean complete() { - if (!readId || temporalVersions) { + private EntityBean complete() { + if (!readIdNormal) { // a bean with no Id (never found in context) if (lazyLoadParentId != null) { ctx.setLazyLoadedChildBean(localBean, lazyLoadParentId); @@ -434,7 +434,7 @@ class SqlTreeNodeBean implements SqlTreeNode { } } - void initialise() throws SQLException { + private void initialise() throws SQLException { initLazyParent(); initBeanType(); initPersistenceContext(); From 297c1d7abb2b28bb5457a9f51e14644159a52193 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Mon, 1 Mar 2021 20:27:50 +1300 Subject: [PATCH 18/31] #2179 - Improved fix for (2179) Fetch on nested one-to-many collections results in duplicates The improvement here is that SqlTreeNodeManyRoot uses load and isContextBean() so that it only checks if the collection already contains the detailBean if the detailBean was already in the persistence context (aka not newly loaded and added). The vast majority case is that detailBean is loaded fresh so for the common case we can skip the extra collection contains() check. --- .../main/java/io/ebean/common/BeanSet.java | 5 +- .../server/query/SqlTreeNodeBean.java | 48 ++++++++++++------- .../server/query/SqlTreeNodeManyRoot.java | 13 ++--- 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/common/BeanSet.java b/ebean-api/src/main/java/io/ebean/common/BeanSet.java index 91f0ae672..880ece106 100644 --- a/ebean-api/src/main/java/io/ebean/common/BeanSet.java +++ b/ebean-api/src/main/java/io/ebean/common/BeanSet.java @@ -70,9 +70,8 @@ public final class BeanSet extends AbstractBeanCollection implements Set Date: Mon, 1 Mar 2021 21:19:25 +1300 Subject: [PATCH 19/31] No effective change - refactor tidy SqlTreeNodeBean --- .../server/query/SqlTreeNodeBean.java | 37 +++++++------------ 1 file changed, 13 insertions(+), 24 deletions(-) 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 b9df45760..1c9bff694 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 @@ -87,7 +87,6 @@ class SqlTreeNodeBean implements SqlTreeNode { */ SqlTreeNodeBean(String prefix, STreePropertyAssoc beanProp, SqlTreeProperties props, List myChildren, boolean withId, SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad) { - this(prefix, beanProp, beanProp.target(), props, myChildren, withId, null, temporalMode, disableLazyLoad); } @@ -197,7 +196,7 @@ class SqlTreeNodeBean implements SqlTreeNode { */ private class LoadInherit extends Load { - LoadInherit(DbReadContext ctx, EntityBean parentBean) { + private LoadInherit(DbReadContext ctx, EntityBean parentBean) { super(ctx, parentBean); } @@ -218,10 +217,8 @@ class SqlTreeNodeBean implements SqlTreeNode { @Override void loadProperties() { - // take account of inheritance and due to subclassing approach - // need to get a 'local' version of the property + // take account of inheritance for (STreeProperty property : properties) { - // get a local version of the BeanProperty localDesc.inheritanceLoad(sqlBeanLoad, property, ctx); } } @@ -248,7 +245,7 @@ class SqlTreeNodeBean implements SqlTreeNode { SqlBeanLoad sqlBeanLoad; boolean lazyLoadMany; - Load(DbReadContext ctx, EntityBean parentBean) { + private Load(DbReadContext ctx, EntityBean parentBean) { this.ctx = ctx; this.parentBean = parentBean; } @@ -366,7 +363,6 @@ class SqlTreeNodeBean implements SqlTreeNode { if (disableLazyLoad) { // bean does not have an Id or is SqlSelect based ebi.setDisableLazyLoad(true); - } else if (partialObject) { if (readId) { // register for lazy loading @@ -390,7 +386,6 @@ class SqlTreeNodeBean implements SqlTreeNode { private void createListProxies() { STreePropertyAssocMany fetchedMany = ctx.getManyProperty(); boolean forceNewReference = queryMode == Mode.REFRESH_BEAN; - // load the List/Set/Map proxy objects (deferred fetching of lists) for (STreePropertyAssocMany many : localDesc.propsMany()) { if (many != fetchedMany) { // create a proxy for the many (deferred fetching) @@ -491,8 +486,8 @@ class SqlTreeNodeBean implements SqlTreeNode { property.appendSelect(ctx, subQuery); } } - for (SqlTreeNode aChildren : children) { - aChildren.appendGroupBy(ctx, subQuery); + for (SqlTreeNode child : children) { + child.appendGroupBy(ctx, subQuery); } ctx.popTableAlias(); ctx.popJoin(); @@ -529,11 +524,8 @@ class SqlTreeNodeBean implements SqlTreeNode { appendSelectId(ctx, idBinder.getBeanProperty()); } appendSelect(ctx, subQuery, properties); - - for (SqlTreeNode aChildren : children) { - // read each child... and let them set their - // values back to this localBean - aChildren.appendSelect(ctx, subQuery); + for (SqlTreeNode child : children) { + child.appendSelect(ctx, subQuery); } ctx.popTableAlias(); ctx.popJoin(); @@ -581,10 +573,8 @@ class SqlTreeNodeBean implements SqlTreeNode { } } appendExtraWhere(ctx); - for (SqlTreeNode aChildren : children) { - // recursively add to the where clause any - // fixed predicates (extraWhere etc) - aChildren.appendWhere(ctx); + for (SqlTreeNode child : children) { + child.appendWhere(ctx); } } @@ -620,10 +610,9 @@ class SqlTreeNodeBean implements SqlTreeNode { property.appendFrom(ctx, joinType); } - for (SqlTreeNode aChildren : children) { - aChildren.appendFrom(ctx, joinType); + for (SqlTreeNode child : children) { + child.appendFrom(ctx, joinType); } - ctx.popTableAlias(); ctx.popJoin(); } @@ -648,8 +637,8 @@ class SqlTreeNodeBean implements SqlTreeNode { if (intersectionAsOfTableAlias) { query.incrementAsOfTableCount(); } - for (SqlTreeNode aChildren : children) { - aChildren.addAsOfTableAlias(query); + for (SqlTreeNode child : children) { + child.addAsOfTableAlias(query); } } From 464f48d71726a164bbbc70505e532d8b1d49445c Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Mon, 1 Mar 2021 21:34:17 +1300 Subject: [PATCH 20/31] Bump test kotlin-querybean-generator version --- kotlin-querybean-generator/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml index 8529dbe7f..4373c3275 100644 --- a/kotlin-querybean-generator/pom.xml +++ b/kotlin-querybean-generator/pom.xml @@ -106,7 +106,7 @@ io.ebean kotlin-querybean-generator - 12.6.1 + 12.7.1 From 6315edfc214066a63336f42d2e4f2ce4503904bc Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Mon, 1 Mar 2021 22:08:17 +1300 Subject: [PATCH 21/31] #2177 - findNative not mapping bean when schema specified in entity mapping --- .../server/deploy/BeanDescriptor.java | 17 +++- .../server/query/CQueryBuilder.java | 3 +- .../test/java/org/example/domain/Product.java | 2 +- .../org/querytest/FindNative_withSchema.java | 78 +++++++++++++++++++ .../resources/application-test.properties | 1 + .../src/test/resources/init-db.sql | 1 + 6 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 ebean-querybean/src/test/java/org/querytest/FindNative_withSchema.java create mode 100644 ebean-querybean/src/test/resources/init-db.sql diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index c2c69b164..a2d902f13 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -727,7 +727,7 @@ public class BeanDescriptor implements BeanType, STreeType { String[] cols = indexDef.getColumns(); BeanProperty[] props = new BeanProperty[cols.length]; for (int i = 0; i < cols.length; i++) { - String propName = findBeanPath("", cols[i]); + String propName = findBeanPath("", "", cols[i]); if (propName == null) { return; } @@ -2472,13 +2472,16 @@ public class BeanDescriptor implements BeanType, STreeType { /** * Return the property path given the db table and column. */ - public String findBeanPath(String tableName, String columnName) { - if (tableName.isEmpty() || tableName.equalsIgnoreCase(baseTable)) { + public String findBeanPath(String schemaName, String tableName, String columnName) { + if (matchBaseTable(schemaName, tableName)) { return columnPath.get(columnName); } BeanPropertyAssoc assocProperty = tablePath.get(tableName); + if (assocProperty == null) { + assocProperty = tablePath.get(schemaName + "." + tableName); + } if (assocProperty != null) { - String relativePath = assocProperty.getTargetDescriptor().findBeanPath(tableName, columnName); + String relativePath = assocProperty.getTargetDescriptor().findBeanPath(schemaName, tableName, columnName); if (relativePath != null) { return SplitName.add(assocProperty.getName(), relativePath); } @@ -2486,6 +2489,12 @@ public class BeanDescriptor implements BeanType, STreeType { return null; } + private boolean matchBaseTable(String schemaName, String tableName) { + return tableName.isEmpty() + || baseTable.equalsIgnoreCase(tableName) + || baseTable.equalsIgnoreCase(schemaName + "." + tableName); + } + /** * Return a 'dynamic property' used to read a formula. */ 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 75aa7e3cc..ab008a5cf 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 @@ -442,9 +442,10 @@ class CQueryBuilder { int cols = 1 + metaData.getColumnCount(); List propertyNames = new ArrayList<>(cols - 1); for (int i = 1; i < cols; i++) { + String schemaName = metaData.getSchemaName(i).toLowerCase(); String tableName = metaData.getTableName(i).toLowerCase(); String columnName = metaData.getColumnName(i).toLowerCase(); - String path = desc.findBeanPath(tableName, columnName); + String path = desc.findBeanPath(schemaName, tableName, columnName); if (path != null) { propertyNames.add(path); } else { diff --git a/ebean-querybean/src/test/java/org/example/domain/Product.java b/ebean-querybean/src/test/java/org/example/domain/Product.java index e41d4fc34..ed58daa05 100644 --- a/ebean-querybean/src/test/java/org/example/domain/Product.java +++ b/ebean-querybean/src/test/java/org/example/domain/Product.java @@ -8,7 +8,7 @@ import javax.validation.constraints.Size; * Product entity bean. */ @Entity -@Table(name = "o_product") +@Table(name = "o_product", schema = "foo") public class Product extends BaseModel { @Size(max = 20) diff --git a/ebean-querybean/src/test/java/org/querytest/FindNative_withSchema.java b/ebean-querybean/src/test/java/org/querytest/FindNative_withSchema.java new file mode 100644 index 000000000..59146648d --- /dev/null +++ b/ebean-querybean/src/test/java/org/querytest/FindNative_withSchema.java @@ -0,0 +1,78 @@ +package org.querytest; + +import io.ebean.DB; +import org.example.domain.Customer; +import org.example.domain.Order; +import org.example.domain.OrderDetail; +import org.example.domain.Product; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class FindNative_withSchema { + + @Test + public void test_rootLevelUsesSchema() { + + Product p = new Product(); + p.setName("prod1"); + p.setSku("p1"); + p.save(); + + String sql = "select * from foo.o_product p where p.sku = ?"; + + Product product = DB.findNative(Product.class, sql) + .setParameter("p1") + .findOne(); + + assertThat(product).isNotNull(); + assertThat(product.getName()).isEqualTo("prod1"); + } + + @Test + public void test_joinToSchema() { + + Order order = setupData(); + + String sql = "select l.*, p.* " + + " from o_order_detail l " + + " join foo.o_product p on p.id = l.product_id " + + " where l.order_id = ?"; + + List lines = DB.findNative(OrderDetail.class, sql) + .setParameter(order.getId()) + .findList(); + + assertThat(lines).hasSize(1); + OrderDetail line = lines.get(0); + Product product = line.getProduct(); + + // check normal bean population + assertThat(line.getOrderQty()).isEqualTo(10); + assertThat(line.getOrder().getId()).isEqualTo(order.getId()); + // check associated bean with schema is populated + assertThat(product).isNotNull(); + assertThat(product.getName()).isEqualTo("prod2"); + } + + private Order setupData() { + Product p = new Product(); + p.setName("prod2"); + p.setSku("p2"); + p.save(); + + Customer customer = new Customer(); + customer.setName("junk"); + customer.save(); + Order order = new Order(); + order.setCustomer(customer); + + OrderDetail line = new OrderDetail(p, 10, 10.0); + order.getDetails().add(line); + + order.save(); + return order; + } +} diff --git a/ebean-querybean/src/test/resources/application-test.properties b/ebean-querybean/src/test/resources/application-test.properties index 68e06a3f9..76f2d661f 100644 --- a/ebean-querybean/src/test/resources/application-test.properties +++ b/ebean-querybean/src/test/resources/application-test.properties @@ -1,5 +1,6 @@ ebean.ddl.generate=true ebean.ddl.run=true +ebean.ddl.initSql=init-db.sql datasource.default=h2 diff --git a/ebean-querybean/src/test/resources/init-db.sql b/ebean-querybean/src/test/resources/init-db.sql new file mode 100644 index 000000000..fde39032e --- /dev/null +++ b/ebean-querybean/src/test/resources/init-db.sql @@ -0,0 +1 @@ +create schema foo; From 7bfacb095b0de1d39bccb54325272c7774dfac1b Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Mon, 1 Mar 2021 22:47:34 +1300 Subject: [PATCH 22/31] #2181 - Oracle 9,10,11 rownum based limiting for SqlQuery and DtoQuery (rather than Oracle 12g row limiting) --- .../dbplatform/oracle/Oracle11Platform.java | 1 + .../oracle/OracleRownumBasicLimiter.java | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OracleRownumBasicLimiter.java diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/Oracle11Platform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/Oracle11Platform.java index e13155f3f..cb32a7470 100644 --- a/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/Oracle11Platform.java +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/Oracle11Platform.java @@ -13,6 +13,7 @@ public class Oracle11Platform extends OraclePlatform { this.platform = Platform.ORACLE11; this.columnAliasPrefix = "c"; this.sqlLimiter = new OracleRownumSqlLimiter(); + this.basicSqlLimiter = new OracleRownumBasicLimiter(); dbIdentity.setIdType(IdType.SEQUENCE); } } diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OracleRownumBasicLimiter.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OracleRownumBasicLimiter.java new file mode 100644 index 000000000..e51df98cb --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OracleRownumBasicLimiter.java @@ -0,0 +1,35 @@ +package io.ebean.config.dbplatform.oracle; + +import io.ebean.config.dbplatform.BasicSqlLimiter; + +/** + * Row limiter for Oracle 9,10,11 using rownum. + */ +public class OracleRownumBasicLimiter implements BasicSqlLimiter { + + @Override + public String limit(String dbSql, int firstRow, int maxRows) { + if (firstRow < 1 && maxRows < 1) { + return dbSql; + } + StringBuilder sb = new StringBuilder(60 + dbSql.length()); + int lastRow = maxRows; + if (lastRow > 0) { + lastRow += firstRow; + } + sb.append("select * from (select "); + if (maxRows > 0) { + sb.append("/*+ FIRST_ROWS(").append(maxRows).append(") */ "); + } + sb.append("a.*, rownum rn_ from ("); + sb.append(dbSql).append(") a "); + if (lastRow > 0) { + sb.append(" where rownum <= ").append(lastRow); + } + sb.append(") "); + if (firstRow > 0) { + sb.append(" where rn_ > ").append(firstRow); + } + return sb.toString(); + } +} From 1a26cd717f75ba6c2584b5066c1af6b476984510 Mon Sep 17 00:00:00 2001 From: robin Date: Mon, 1 Mar 2021 23:40:06 +1300 Subject: [PATCH 23/31] #2182 - Tidy javadoc - this may mean JDK 15 is required to generate javadoc --- .../src/main/java/io/ebean/BeanFinder.java | 2 +- .../main/java/io/ebean/BeanRepository.java | 4 +- .../src/main/java/io/ebean/Database.java | 2 +- .../src/main/java/io/ebean/DocumentStore.java | 2 +- ebean-api/src/main/java/io/ebean/Ebean.java | 2 +- .../main/java/io/ebean/ExpressionFactory.java | 4 +- .../main/java/io/ebean/ExpressionList.java | 4 +- ebean-api/src/main/java/io/ebean/Finder.java | 4 +- ebean-api/src/main/java/io/ebean/Model.java | 12 ++-- ebean-api/src/main/java/io/ebean/Pairs.java | 2 +- ebean-api/src/main/java/io/ebean/RawSql.java | 8 +-- .../src/main/java/io/ebean/RowMapper.java | 2 +- ebean-api/src/main/java/io/ebean/Update.java | 12 ++-- .../ebean/config/DatabaseConfigProvider.java | 2 +- .../io/ebean/config/ServerConfigProvider.java | 2 +- .../config/dbplatform/DbDefaultValue.java | 2 +- .../io/ebean/typequery/PBaseCompareable.java | 8 +-- .../java/io/ebean/typequery/TQRootBean.java | 56 +++++++------------ 18 files changed, 57 insertions(+), 73 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/BeanFinder.java b/ebean-api/src/main/java/io/ebean/BeanFinder.java index 00aec8de5..2c8e04eec 100644 --- a/ebean-api/src/main/java/io/ebean/BeanFinder.java +++ b/ebean-api/src/main/java/io/ebean/BeanFinder.java @@ -14,7 +14,7 @@ import java.util.Optional; * * public class CustomerFinder extends BeanFinder { * - * ï¼ Inject + * @Inject * public CustomerFinder(Database database) { * super(Customer.class, database); * } diff --git a/ebean-api/src/main/java/io/ebean/BeanRepository.java b/ebean-api/src/main/java/io/ebean/BeanRepository.java index 19f390582..b89a49b39 100644 --- a/ebean-api/src/main/java/io/ebean/BeanRepository.java +++ b/ebean-api/src/main/java/io/ebean/BeanRepository.java @@ -9,10 +9,10 @@ import java.util.Collection; *

*

{@code
  *
- * ï¼ Repository
+ * @Repository
  * public class CustomerRepository extends BeanRepository {
  *
- *   ï¼ Inject
+ *   @Inject
  *   public CustomerRepository(Database server) {
  *     super(Customer.class, server);
  *   }
diff --git a/ebean-api/src/main/java/io/ebean/Database.java b/ebean-api/src/main/java/io/ebean/Database.java
index 5bbf29ad3..bd38253de 100644
--- a/ebean-api/src/main/java/io/ebean/Database.java
+++ b/ebean-api/src/main/java/io/ebean/Database.java
@@ -897,7 +897,7 @@ public interface Database {
    * 
{@code
    *   public class Order { ...
    *
-   *     ï¼ OneToMany(cascade=CascadeType.ALL, mappedBy="order")
+   *     @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
    * 	   List details;
    * 	   ...
    *   }
diff --git a/ebean-api/src/main/java/io/ebean/DocumentStore.java b/ebean-api/src/main/java/io/ebean/DocumentStore.java
index a4f75132d..0a2353ff0 100644
--- a/ebean-api/src/main/java/io/ebean/DocumentStore.java
+++ b/ebean-api/src/main/java/io/ebean/DocumentStore.java
@@ -149,7 +149,7 @@ public interface DocumentStore {
    *    .setUseDocStore(true)
    *    .where()... // perhaps add predicates
    *    .findEachWhile(new Predicate() {
-   *      ï¼ Override
+   *      @Override
    *      public void accept(Order bean) {
    *        // process the bean
    *
diff --git a/ebean-api/src/main/java/io/ebean/Ebean.java b/ebean-api/src/main/java/io/ebean/Ebean.java
index f68a0666b..fb0e34db3 100644
--- a/ebean-api/src/main/java/io/ebean/Ebean.java
+++ b/ebean-api/src/main/java/io/ebean/Ebean.java
@@ -346,7 +346,7 @@ public final class Ebean {
    * 
{@code
    *   public class Order { ...
    *
-   *     ï¼ OneToMany(cascade=CascadeType.ALL, mappedBy="order")
+   *     @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
    * 	   List details;
    * 	   ...
    *   }
diff --git a/ebean-api/src/main/java/io/ebean/ExpressionFactory.java b/ebean-api/src/main/java/io/ebean/ExpressionFactory.java
index 1e5c4ebf5..3bcd5cb39 100644
--- a/ebean-api/src/main/java/io/ebean/ExpressionFactory.java
+++ b/ebean-api/src/main/java/io/ebean/ExpressionFactory.java
@@ -200,7 +200,7 @@ public interface ExpressionFactory {
   Expression gtOrNull(String propertyName, Object value);
 
   /**
-   * Greater than or Equal to OR Null  >= or null 
+   * Greater than or Equal to OR Null ({@code >= or null })
    * 

* A convenient expression combining GE and Is Null. Most often useful for range * expressions where the top range value is nullable. @@ -227,7 +227,7 @@ public interface ExpressionFactory { Expression ltOrNull(String propertyName, Object value); /** - * Less Than or Equal to OR Null <= or null + * Less Than or Equal to OR Null ({@code <= or null }) *

* A convenient expression combining LE and Is Null. Most often useful for range * expressions where the bottom range value is nullable. diff --git a/ebean-api/src/main/java/io/ebean/ExpressionList.java b/ebean-api/src/main/java/io/ebean/ExpressionList.java index e137f64ba..7e4e0f164 100644 --- a/ebean-api/src/main/java/io/ebean/ExpressionList.java +++ b/ebean-api/src/main/java/io/ebean/ExpressionList.java @@ -909,7 +909,7 @@ public interface ExpressionList { ExpressionList gtOrNull(String propertyName, Object value); /** - * Greater Than or Equal to OR Null - >= or null . + * Greater Than or Equal to OR Null - ({@code >= or null }). */ ExpressionList geOrNull(String propertyName, Object value); @@ -930,7 +930,7 @@ public interface ExpressionList { ExpressionList ltOrNull(String propertyName, Object value); /** - * Less Than or Equal to OR Null - <= or null . + * Less Than or Equal to OR Null - ({@code <= or null }). */ ExpressionList leOrNull(String propertyName, Object value); diff --git a/ebean-api/src/main/java/io/ebean/Finder.java b/ebean-api/src/main/java/io/ebean/Finder.java index e91238344..3c9318e7d 100644 --- a/ebean-api/src/main/java/io/ebean/Finder.java +++ b/ebean-api/src/main/java/io/ebean/Finder.java @@ -37,7 +37,7 @@ import java.util.List; * } * } * - * ï¼ Entity + * @Entity * public class Customer extends BaseModel { * * public static final CustomerFinder find = new CustomerFinder(); @@ -80,7 +80,7 @@ public class Finder { * // ... add extra customer specific finder methods * } * - * ï¼ Entity + * @Entity * public class Customer extends BaseModel { * * public static final CustomerFinder find = new CustomerFinder(); diff --git a/ebean-api/src/main/java/io/ebean/Model.java b/ebean-api/src/main/java/io/ebean/Model.java index 5fe2205e1..ac2f11ea7 100644 --- a/ebean-api/src/main/java/io/ebean/Model.java +++ b/ebean-api/src/main/java/io/ebean/Model.java @@ -32,16 +32,16 @@ import io.ebean.bean.EntityBean; * // Typically there is a common base model that has some * // common properties like the ones below * - * ï¼ MappedSuperclass + * @MappedSuperclass * public class BaseModel extends Model { * - * ï¼ Id Long id; + * @Id Long id; * - * ï¼ Version Long version; + * @Version Long version; * - * ï¼ WhenCreated Timestamp whenCreated; + * @WhenCreated Timestamp whenCreated; * - * ï¼ WhenUpdated Timestamp whenUpdated; + * @WhenUpdated Timestamp whenUpdated; * * ... * } @@ -52,7 +52,7 @@ import io.ebean.bean.EntityBean; * * // Extend the mappedSuperclass * - * ï¼ Entity ï¼ Table(name="o_account") + * @Entity @Table(name="o_account") * public class Customer extends BaseModel { * * String name; diff --git a/ebean-api/src/main/java/io/ebean/Pairs.java b/ebean-api/src/main/java/io/ebean/Pairs.java index 4f609f95a..bd5ee5137 100644 --- a/ebean-api/src/main/java/io/ebean/Pairs.java +++ b/ebean-api/src/main/java/io/ebean/Pairs.java @@ -18,7 +18,7 @@ import java.util.List; * * // where a bean is annotated with a complex * // natural key made of several properties - * ï¼ Cache(naturalKey = {"store","code","sku"}) + * @Cache(naturalKey = {"store","code","sku"}) * * * Pairs pairs = new Pairs("sku", "code"); diff --git a/ebean-api/src/main/java/io/ebean/RawSql.java b/ebean-api/src/main/java/io/ebean/RawSql.java index e0e20714b..901f1a43c 100644 --- a/ebean-api/src/main/java/io/ebean/RawSql.java +++ b/ebean-api/src/main/java/io/ebean/RawSql.java @@ -41,14 +41,14 @@ package io.ebean; *

Example OrderAggregate

*
{@code
  *  ...
- *   // ï¼ Sql indicates to that this bean
+ *   // @Sql indicates to that this bean
  *   // is based on RawSql rather than a table
  *
- *   ï¼ Entity
- *   ï¼ Sql
+ *   @Entity
+ *   @Sql
  *   public class OrderAggregate {
  *
- *    ï¼ OneToOne
+ *    @OneToOne
  *    Order order;
  *
  *    Double totalAmount;
diff --git a/ebean-api/src/main/java/io/ebean/RowMapper.java b/ebean-api/src/main/java/io/ebean/RowMapper.java
index 47e96498d..b1d59c984 100644
--- a/ebean-api/src/main/java/io/ebean/RowMapper.java
+++ b/ebean-api/src/main/java/io/ebean/RowMapper.java
@@ -22,7 +22,7 @@ import java.sql.SQLException;
  *    //
  *    class CustomerMapper implements RowMapper {
  *
- *     ï¼ Override
+ *     @Override
  *     public CustomerDto map(ResultSet rset, int rowNum) throws SQLException {
  *
  *       long id = rset.getLong(1);
diff --git a/ebean-api/src/main/java/io/ebean/Update.java b/ebean-api/src/main/java/io/ebean/Update.java
index 25aed9315..ece973392 100644
--- a/ebean-api/src/main/java/io/ebean/Update.java
+++ b/ebean-api/src/main/java/io/ebean/Update.java
@@ -13,23 +13,23 @@ package io.ebean;
  * 

*
{@code
  *    ...
- *   ï¼ NamedUpdates(value = {
- *     ï¼ NamedUpdate(
+ *   @NamedUpdates(value = {
+ *     @NamedUpdate(
  *       name = "setTitle",
  *       notifyCache = false,
  *       update = "update topic set title = :title, postCount = :count where id = :id"),
- *    ï¼ NamedUpdate(
+ *    @NamedUpdate(
  *       name = "setPostCount",
  *       notifyCache = false,
  *       update = "update f_topic set post_count = :postCount where id = :id"),
- *    ï¼ NamedUpdate(
+ *    @NamedUpdate(
  *       name = "incrementPostCount",
  *       notifyCache = false,
  *       update = "update Topic set postCount = postCount + 1 where id = :id")
  *       //update = "update f_topic set post_count = post_count + 1 where id = :id")
  *   })
- *   ï¼ Entity
- *   ï¼ Table(name = "f_topic")
+ *   @Entity
+ *   @Table(name = "f_topic")
  *   public class Topic {
  *     ...
  *   }
diff --git a/ebean-api/src/main/java/io/ebean/config/DatabaseConfigProvider.java b/ebean-api/src/main/java/io/ebean/config/DatabaseConfigProvider.java
index 0a1f3e768..066af5fc4 100644
--- a/ebean-api/src/main/java/io/ebean/config/DatabaseConfigProvider.java
+++ b/ebean-api/src/main/java/io/ebean/config/DatabaseConfigProvider.java
@@ -15,7 +15,7 @@ package io.ebean.config;
  *
  * public class EbeanConfigProvider implements DatabaseConfigProvider {
  *
- *   ï¼ Override
+ *   @Override
  *   public void apply(DatabaseConfig config) {
  *
  *     // register the entity bean classes explicitly
diff --git a/ebean-api/src/main/java/io/ebean/config/ServerConfigProvider.java b/ebean-api/src/main/java/io/ebean/config/ServerConfigProvider.java
index f992b004d..1ba45907d 100644
--- a/ebean-api/src/main/java/io/ebean/config/ServerConfigProvider.java
+++ b/ebean-api/src/main/java/io/ebean/config/ServerConfigProvider.java
@@ -16,7 +16,7 @@ package io.ebean.config;
  *
  * public class EbeanConfigProvider implements ServerConfigProvider {
  *
- *   ï¼ Override
+ *   @Override
  *   public void apply(ServerConfig config) {
  *
  *     // register the entity bean classes explicitly
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/DbDefaultValue.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/DbDefaultValue.java
index b74465f35..851fc2707 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/DbDefaultValue.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/DbDefaultValue.java
@@ -79,7 +79,7 @@ public class DbDefaultValue {
   }
 
   /**
-   * This method checks & convert the {@link DbDefault#value()} to a valid SQL literal.
+   * This method checks and converts the {@link DbDefault#value()} to a valid SQL literal.
    *
    * This is mainly to quote string literals and verify integer/dates for correctness.
    * 

diff --git a/ebean-querybean/src/main/java/io/ebean/typequery/PBaseCompareable.java b/ebean-querybean/src/main/java/io/ebean/typequery/PBaseCompareable.java index c9488f63c..4e0ba7c1d 100644 --- a/ebean-querybean/src/main/java/io/ebean/typequery/PBaseCompareable.java +++ b/ebean-querybean/src/main/java/io/ebean/typequery/PBaseCompareable.java @@ -121,12 +121,12 @@ public class PBaseCompareable extends PBaseValueEqual { * Greater or equal to lower value and strictly less than upper value. *

* This is generally preferable over Between for date and datetime types - * as SQL Between is inclusive on the upper bound (<=) and generally we - * need the upper bound to be exclusive (<). + * as SQL Between is inclusive on the upper bound ({@code <= }) and generally + * we need the upper bound to be exclusive ({@code < }). *

* - * @param lower the lower bind value (>=) - * @param upper the upper bind value (<) + * @param lower the lower bind value ({@code >= }) + * @param upper the upper bind value ({@code < }) * @return the root query bean instance */ public final R inRange(T lower, T upper) { diff --git a/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java b/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java index de8f846a6..4deab6037 100644 --- a/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java +++ b/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java @@ -43,55 +43,39 @@ import java.util.function.Predicate; import java.util.stream.Stream; /** - * Base root query bean. + * Base root query bean providing common features for all root query beans. *

- * With code generation for each entity bean type a query bean is created that extends this. + * For each entity bean querybean-generator generates a query bean that extends TQRootBean. *

- * Provides common features for all root query beans + * *

- *

- *

Example - QCustomer extends TQRootBean

- *

- * These 'query beans' like QCustomer are generated using the avaje-ebeanorm-typequery-generator. - *

- *
{@code
- *
- *   public class QCustomer extends TQRootBean {
- *
- *     // properties
- *     public PLong id;
- *
- *     public PString name;
- *     ...
- *
- * }
- *

*

Example - usage of QCustomer

*
{@code
  *
- *    Date fiveDaysAgo = ...
+ *  Date fiveDaysAgo = ...
  *
- *    List customers =
- *        new QCustomer()
- *          .name.ilike("rob")
- *          .status.equalTo(Customer.Status.GOOD)
- *          .registered.after(fiveDaysAgo)
- *          .contacts.email.endsWith("@foo.com")
- *          .orderBy()
- *            .name.asc()
- *            .registered.desc()
- *          .findList();
+ *  List customers =
+ *      new QCustomer()
+ *        .name.ilike("rob")
+ *        .status.equalTo(Customer.Status.GOOD)
+ *        .registered.after(fiveDaysAgo)
+ *        .contacts.email.endsWith("@foo.com")
+ *        .orderBy()
+ *          .name.asc()
+ *          .registered.desc()
+ *        .findList();
  *
  * }
*

*

Resulting SQL where

*

- *

{@code sql
+ * 
{@code
  *
- *     where lower(t0.name) like ?  and t0.status = ?  and t0.registered > ?  and u1.email like ?
- *     order by t0.name, t0.registered desc;
+ *   where lower(t0.name) like ?  and t0.status = ?  and t0.registered > ?  and u1.email like ?
+ *   order by t0.name, t0.registered desc;
+ *
+ *   --bind(rob,GOOD,Mon Jul 27 12:05:37 NZST 2015,%@foo.com)
  *
- *     --bind(rob,GOOD,Mon Jul 27 12:05:37 NZST 2015,%@foo.com)
  * }
* * @param the entity bean type (normal entity bean type e.g. Customer) @@ -280,7 +264,7 @@ public abstract class TQRootBean { */ @SafeVarargs public final R select(TQProperty... properties) { - ((SpiQueryFetch)query).selectProperties(properties(properties)); + ((SpiQueryFetch) query).selectProperties(properties(properties)); return root; } From 907b182931372719924674de312f8d63319da69b Mon Sep 17 00:00:00 2001 From: robin Date: Mon, 1 Mar 2021 23:45:05 +1300 Subject: [PATCH 24/31] [maven-release-plugin] prepare release ebean-parent-12.7.2 --- ebean-api/pom.xml | 2 +- ebean-autotune/pom.xml | 6 +++--- ebean-bom/pom.xml | 30 +++++++++++++++--------------- ebean-core-type/pom.xml | 4 ++-- ebean-core/pom.xml | 10 +++++----- ebean-ddl-generator/pom.xml | 6 +++--- ebean-externalmapping-api/pom.xml | 2 +- ebean-externalmapping-xml/pom.xml | 10 +++++----- ebean-postgis/pom.xml | 6 +++--- ebean-querybean/pom.xml | 10 +++++----- ebean-redis/pom.xml | 12 ++++++------ ebean-test/pom.xml | 6 +++--- ebean/pom.xml | 8 ++++---- kotlin-querybean-generator/pom.xml | 8 ++++---- pom.xml | 4 ++-- querybean-generator/pom.xml | 2 +- 16 files changed, 63 insertions(+), 63 deletions(-) diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index 79962ff51..1aed817f7 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2-SNAPSHOT + 12.7.2 ebean api diff --git a/ebean-autotune/pom.xml b/ebean-autotune/pom.xml index b86469ddd..0646db6e7 100644 --- a/ebean-autotune/pom.xml +++ b/ebean-autotune/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2-SNAPSHOT + 12.7.2 @@ -14,7 +14,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.6.5 + ebean-parent-12.7.2 ebean autotune @@ -26,7 +26,7 @@ io.ebean ebean-core - 12.7.2-SNAPSHOT + 12.7.2 provided diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml index c47601c85..4cec55855 100644 --- a/ebean-bom/pom.xml +++ b/ebean-bom/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2-SNAPSHOT + 12.7.2 ebean bom @@ -81,88 +81,88 @@ io.ebean ebean - 12.7.2-SNAPSHOT + 12.7.2 io.ebean ebean-api - 12.7.2-SNAPSHOT + 12.7.2 io.ebean ebean-core - 12.7.2-SNAPSHOT + 12.7.2 io.ebean ebean-core-type - 12.7.2-SNAPSHOT + 12.7.2 io.ebean ebean-ddl-generator - 12.7.2-SNAPSHOT + 12.7.2 io.ebean ebean-externalmapping-api - 12.7.2-SNAPSHOT + 12.7.2 io.ebean ebean-externalmapping-xml - 12.7.2-SNAPSHOT + 12.7.2 io.ebean ebean-autotune - 12.7.2-SNAPSHOT + 12.7.2 io.ebean ebean-querybean - 12.7.2-SNAPSHOT + 12.7.2 io.ebean querybean-generator - 12.7.2-SNAPSHOT + 12.7.2 provided io.ebean kotlin-querybean-generator - 12.7.2-SNAPSHOT + 12.7.2 provided io.ebean ebean-test - 12.7.2-SNAPSHOT + 12.7.2 test io.ebean ebean-postgis - 12.7.2-SNAPSHOT + 12.7.2 io.ebean ebean-redis - 12.7.2-SNAPSHOT + 12.7.2 diff --git a/ebean-core-type/pom.xml b/ebean-core-type/pom.xml index f0db31343..ae4039137 100644 --- a/ebean-core-type/pom.xml +++ b/ebean-core-type/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2-SNAPSHOT + 12.7.2 ebean-core-type @@ -16,7 +16,7 @@ io.ebean ebean-api - 12.7.2-SNAPSHOT + 12.7.2 diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index bab9e7624..9eb1f764a 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -3,7 +3,7 @@ ebean-parent io.ebean - 12.7.2-SNAPSHOT + 12.7.2 ebean-core @@ -15,7 +15,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.6.5 + ebean-parent-12.7.2 @@ -87,19 +87,19 @@ io.ebean ebean-api - 12.7.2-SNAPSHOT + 12.7.2 io.ebean ebean-core-type - 12.7.2-SNAPSHOT + 12.7.2 io.ebean ebean-externalmapping-api - 12.7.2-SNAPSHOT + 12.7.2 diff --git a/ebean-ddl-generator/pom.xml b/ebean-ddl-generator/pom.xml index 90f3270e4..3aada00a8 100644 --- a/ebean-ddl-generator/pom.xml +++ b/ebean-ddl-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2-SNAPSHOT + 12.7.2 ebean ddl generation @@ -29,14 +29,14 @@ io.ebean ebean-core-type - 12.7.2-SNAPSHOT + 12.7.2 provided io.ebean ebean-core - 12.7.2-SNAPSHOT + 12.7.2 provided diff --git a/ebean-externalmapping-api/pom.xml b/ebean-externalmapping-api/pom.xml index 76e5ced23..e27b3dfce 100644 --- a/ebean-externalmapping-api/pom.xml +++ b/ebean-externalmapping-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2-SNAPSHOT + 12.7.2 ebean external mapping api diff --git a/ebean-externalmapping-xml/pom.xml b/ebean-externalmapping-xml/pom.xml index 620a1de73..42fa08b21 100644 --- a/ebean-externalmapping-xml/pom.xml +++ b/ebean-externalmapping-xml/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2-SNAPSHOT + 12.7.2 @@ -14,7 +14,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.6.5 + ebean-parent-12.7.2 ebean external mapping xml @@ -33,7 +33,7 @@ io.ebean ebean-externalmapping-api - 12.7.2-SNAPSHOT + 12.7.2 @@ -59,14 +59,14 @@ io.ebean ebean-core - 12.7.2-SNAPSHOT + 12.7.2 test io.ebean ebean-ddl-generator - 12.7.2-SNAPSHOT + 12.7.2 test diff --git a/ebean-postgis/pom.xml b/ebean-postgis/pom.xml index d8acdb157..583dc83a3 100644 --- a/ebean-postgis/pom.xml +++ b/ebean-postgis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2-SNAPSHOT + 12.7.2 ebean postgis @@ -23,7 +23,7 @@ io.ebean ebean-core - 12.7.2-SNAPSHOT + 12.7.2 provided @@ -74,7 +74,7 @@ io.ebean ebean-test - 12.7.2-SNAPSHOT + 12.7.2 test diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml index 52ab4f7c0..86a5bf64c 100644 --- a/ebean-querybean/pom.xml +++ b/ebean-querybean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2-SNAPSHOT + 12.7.2 ebean querybean @@ -17,7 +17,7 @@ io.ebean ebean-core - 12.7.2-SNAPSHOT + 12.7.2 provided @@ -57,21 +57,21 @@ io.ebean ebean-ddl-generator - 12.7.2-SNAPSHOT + 12.7.2 test io.ebean querybean-generator - 12.7.2-SNAPSHOT + 12.7.2 test io.ebean ebean-test - 12.7.2-SNAPSHOT + 12.7.2 test diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml index cf4f83208..eef70ea0f 100644 --- a/ebean-redis/pom.xml +++ b/ebean-redis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2-SNAPSHOT + 12.7.2 ebean-redis @@ -22,35 +22,35 @@ io.ebean ebean-api - 12.7.2-SNAPSHOT + 12.7.2 provided io.ebean ebean-core - 12.7.2-SNAPSHOT + 12.7.2 provided io.ebean ebean-querybean - 12.7.2-SNAPSHOT + 12.7.2 test io.ebean querybean-generator - 12.7.2-SNAPSHOT + 12.7.2 test io.ebean ebean-test - 12.7.2-SNAPSHOT + 12.7.2 test diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml index 32810609f..246de76b3 100644 --- a/ebean-test/pom.xml +++ b/ebean-test/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2-SNAPSHOT + 12.7.2 ebean test @@ -29,14 +29,14 @@ io.ebean ebean-core - 12.7.2-SNAPSHOT + 12.7.2 provided io.ebean ebean-ddl-generator - 12.7.2-SNAPSHOT + 12.7.2 diff --git a/ebean/pom.xml b/ebean/pom.xml index e1bf6282a..77f88c51e 100644 --- a/ebean/pom.xml +++ b/ebean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2-SNAPSHOT + 12.7.2 ebean composite @@ -22,20 +22,20 @@ io.ebean ebean-api - 12.7.2-SNAPSHOT + 12.7.2 io.ebean ebean-core - 12.7.2-SNAPSHOT + 12.7.2 io.ebean ebean-querybean - 12.7.2-SNAPSHOT + 12.7.2 diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml index 4373c3275..7e72e1e3c 100644 --- a/kotlin-querybean-generator/pom.xml +++ b/kotlin-querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2-SNAPSHOT + 12.7.2 kotlin querybean generator @@ -29,7 +29,7 @@ io.ebean ebean-querybean - 12.7.2-SNAPSHOT + 12.7.2 test @@ -43,7 +43,7 @@ io.ebean ebean-core - 12.7.2-SNAPSHOT + 12.7.2 test @@ -64,7 +64,7 @@ io.ebean ebean-ddl-generator - 12.7.2-SNAPSHOT + 12.7.2 test diff --git a/pom.xml b/pom.xml index 6edda0cd5..9448cc54e 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ io.ebean ebean-parent - 12.7.2-SNAPSHOT + 12.7.2 pom ebean parent @@ -18,7 +18,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.6.5 + ebean-parent-12.7.2 diff --git a/querybean-generator/pom.xml b/querybean-generator/pom.xml index 9f5303d0f..be2082334 100644 --- a/querybean-generator/pom.xml +++ b/querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2-SNAPSHOT + 12.7.2 querybean generator From e4d0341e67201d9107dc694f06967ed1ea27792f Mon Sep 17 00:00:00 2001 From: robin Date: Mon, 1 Mar 2021 23:45:15 +1300 Subject: [PATCH 25/31] [maven-release-plugin] prepare for next development iteration --- ebean-api/pom.xml | 2 +- ebean-autotune/pom.xml | 6 +++--- ebean-bom/pom.xml | 30 +++++++++++++++--------------- ebean-core-type/pom.xml | 4 ++-- ebean-core/pom.xml | 10 +++++----- ebean-ddl-generator/pom.xml | 6 +++--- ebean-externalmapping-api/pom.xml | 2 +- ebean-externalmapping-xml/pom.xml | 10 +++++----- ebean-postgis/pom.xml | 6 +++--- ebean-querybean/pom.xml | 10 +++++----- ebean-redis/pom.xml | 12 ++++++------ ebean-test/pom.xml | 6 +++--- ebean/pom.xml | 8 ++++---- kotlin-querybean-generator/pom.xml | 8 ++++---- pom.xml | 4 ++-- querybean-generator/pom.xml | 2 +- 16 files changed, 63 insertions(+), 63 deletions(-) diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index 1aed817f7..c073c2699 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2 + 12.7.3-SNAPSHOT ebean api diff --git a/ebean-autotune/pom.xml b/ebean-autotune/pom.xml index 0646db6e7..4ced5a224 100644 --- a/ebean-autotune/pom.xml +++ b/ebean-autotune/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2 + 12.7.3-SNAPSHOT @@ -14,7 +14,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.7.2 + ebean-parent-12.6.5 ebean autotune @@ -26,7 +26,7 @@ io.ebean ebean-core - 12.7.2 + 12.7.3-SNAPSHOT provided diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml index 4cec55855..2293b2db9 100644 --- a/ebean-bom/pom.xml +++ b/ebean-bom/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2 + 12.7.3-SNAPSHOT ebean bom @@ -81,88 +81,88 @@ io.ebean ebean - 12.7.2 + 12.7.3-SNAPSHOT io.ebean ebean-api - 12.7.2 + 12.7.3-SNAPSHOT io.ebean ebean-core - 12.7.2 + 12.7.3-SNAPSHOT io.ebean ebean-core-type - 12.7.2 + 12.7.3-SNAPSHOT io.ebean ebean-ddl-generator - 12.7.2 + 12.7.3-SNAPSHOT io.ebean ebean-externalmapping-api - 12.7.2 + 12.7.3-SNAPSHOT io.ebean ebean-externalmapping-xml - 12.7.2 + 12.7.3-SNAPSHOT io.ebean ebean-autotune - 12.7.2 + 12.7.3-SNAPSHOT io.ebean ebean-querybean - 12.7.2 + 12.7.3-SNAPSHOT io.ebean querybean-generator - 12.7.2 + 12.7.3-SNAPSHOT provided io.ebean kotlin-querybean-generator - 12.7.2 + 12.7.3-SNAPSHOT provided io.ebean ebean-test - 12.7.2 + 12.7.3-SNAPSHOT test io.ebean ebean-postgis - 12.7.2 + 12.7.3-SNAPSHOT io.ebean ebean-redis - 12.7.2 + 12.7.3-SNAPSHOT diff --git a/ebean-core-type/pom.xml b/ebean-core-type/pom.xml index ae4039137..9e7a3c6eb 100644 --- a/ebean-core-type/pom.xml +++ b/ebean-core-type/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2 + 12.7.3-SNAPSHOT ebean-core-type @@ -16,7 +16,7 @@ io.ebean ebean-api - 12.7.2 + 12.7.3-SNAPSHOT diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index 9eb1f764a..b493318d1 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -3,7 +3,7 @@ ebean-parent io.ebean - 12.7.2 + 12.7.3-SNAPSHOT ebean-core @@ -15,7 +15,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.7.2 + ebean-parent-12.6.5 @@ -87,19 +87,19 @@ io.ebean ebean-api - 12.7.2 + 12.7.3-SNAPSHOT io.ebean ebean-core-type - 12.7.2 + 12.7.3-SNAPSHOT io.ebean ebean-externalmapping-api - 12.7.2 + 12.7.3-SNAPSHOT diff --git a/ebean-ddl-generator/pom.xml b/ebean-ddl-generator/pom.xml index 3aada00a8..571f4e090 100644 --- a/ebean-ddl-generator/pom.xml +++ b/ebean-ddl-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2 + 12.7.3-SNAPSHOT ebean ddl generation @@ -29,14 +29,14 @@ io.ebean ebean-core-type - 12.7.2 + 12.7.3-SNAPSHOT provided io.ebean ebean-core - 12.7.2 + 12.7.3-SNAPSHOT provided diff --git a/ebean-externalmapping-api/pom.xml b/ebean-externalmapping-api/pom.xml index e27b3dfce..4c33334e1 100644 --- a/ebean-externalmapping-api/pom.xml +++ b/ebean-externalmapping-api/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2 + 12.7.3-SNAPSHOT ebean external mapping api diff --git a/ebean-externalmapping-xml/pom.xml b/ebean-externalmapping-xml/pom.xml index 42fa08b21..0ae68425e 100644 --- a/ebean-externalmapping-xml/pom.xml +++ b/ebean-externalmapping-xml/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2 + 12.7.3-SNAPSHOT @@ -14,7 +14,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.7.2 + ebean-parent-12.6.5 ebean external mapping xml @@ -33,7 +33,7 @@ io.ebean ebean-externalmapping-api - 12.7.2 + 12.7.3-SNAPSHOT @@ -59,14 +59,14 @@ io.ebean ebean-core - 12.7.2 + 12.7.3-SNAPSHOT test io.ebean ebean-ddl-generator - 12.7.2 + 12.7.3-SNAPSHOT test diff --git a/ebean-postgis/pom.xml b/ebean-postgis/pom.xml index 583dc83a3..99fe84dca 100644 --- a/ebean-postgis/pom.xml +++ b/ebean-postgis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2 + 12.7.3-SNAPSHOT ebean postgis @@ -23,7 +23,7 @@ io.ebean ebean-core - 12.7.2 + 12.7.3-SNAPSHOT provided @@ -74,7 +74,7 @@ io.ebean ebean-test - 12.7.2 + 12.7.3-SNAPSHOT test diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml index 86a5bf64c..132ef56e2 100644 --- a/ebean-querybean/pom.xml +++ b/ebean-querybean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2 + 12.7.3-SNAPSHOT ebean querybean @@ -17,7 +17,7 @@ io.ebean ebean-core - 12.7.2 + 12.7.3-SNAPSHOT provided @@ -57,21 +57,21 @@ io.ebean ebean-ddl-generator - 12.7.2 + 12.7.3-SNAPSHOT test io.ebean querybean-generator - 12.7.2 + 12.7.3-SNAPSHOT test io.ebean ebean-test - 12.7.2 + 12.7.3-SNAPSHOT test diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml index eef70ea0f..476ee7c2a 100644 --- a/ebean-redis/pom.xml +++ b/ebean-redis/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2 + 12.7.3-SNAPSHOT ebean-redis @@ -22,35 +22,35 @@ io.ebean ebean-api - 12.7.2 + 12.7.3-SNAPSHOT provided io.ebean ebean-core - 12.7.2 + 12.7.3-SNAPSHOT provided io.ebean ebean-querybean - 12.7.2 + 12.7.3-SNAPSHOT test io.ebean querybean-generator - 12.7.2 + 12.7.3-SNAPSHOT test io.ebean ebean-test - 12.7.2 + 12.7.3-SNAPSHOT test diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml index 246de76b3..4b5e47899 100644 --- a/ebean-test/pom.xml +++ b/ebean-test/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2 + 12.7.3-SNAPSHOT ebean test @@ -29,14 +29,14 @@ io.ebean ebean-core - 12.7.2 + 12.7.3-SNAPSHOT provided io.ebean ebean-ddl-generator - 12.7.2 + 12.7.3-SNAPSHOT diff --git a/ebean/pom.xml b/ebean/pom.xml index 77f88c51e..7356b26ff 100644 --- a/ebean/pom.xml +++ b/ebean/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2 + 12.7.3-SNAPSHOT ebean composite @@ -22,20 +22,20 @@ io.ebean ebean-api - 12.7.2 + 12.7.3-SNAPSHOT io.ebean ebean-core - 12.7.2 + 12.7.3-SNAPSHOT io.ebean ebean-querybean - 12.7.2 + 12.7.3-SNAPSHOT diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml index 7e72e1e3c..8e4df4f39 100644 --- a/kotlin-querybean-generator/pom.xml +++ b/kotlin-querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2 + 12.7.3-SNAPSHOT kotlin querybean generator @@ -29,7 +29,7 @@ io.ebean ebean-querybean - 12.7.2 + 12.7.3-SNAPSHOT test @@ -43,7 +43,7 @@ io.ebean ebean-core - 12.7.2 + 12.7.3-SNAPSHOT test @@ -64,7 +64,7 @@ io.ebean ebean-ddl-generator - 12.7.2 + 12.7.3-SNAPSHOT test diff --git a/pom.xml b/pom.xml index 9448cc54e..8e06ae121 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ io.ebean ebean-parent - 12.7.2 + 12.7.3-SNAPSHOT pom ebean parent @@ -18,7 +18,7 @@ scm:git:git@github.com:ebean-orm/ebean.git - ebean-parent-12.7.2 + ebean-parent-12.6.5 diff --git a/querybean-generator/pom.xml b/querybean-generator/pom.xml index be2082334..bc6fc7019 100644 --- a/querybean-generator/pom.xml +++ b/querybean-generator/pom.xml @@ -4,7 +4,7 @@ ebean-parent io.ebean - 12.7.2 + 12.7.3-SNAPSHOT querybean generator From 79dde23ec6b8e022e5b510e04288dd9929e1d5c9 Mon Sep 17 00:00:00 2001 From: Ben Kempe Date: Mon, 1 Mar 2021 12:16:22 +0100 Subject: [PATCH 26/31] added tests for findNative fetches --- .../org/tests/query/TestQueryFindNative.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/ebean-core/src/test/java/org/tests/query/TestQueryFindNative.java b/ebean-core/src/test/java/org/tests/query/TestQueryFindNative.java index ff4acac9d..7cc4ac9c6 100644 --- a/ebean-core/src/test/java/org/tests/query/TestQueryFindNative.java +++ b/ebean-core/src/test/java/org/tests/query/TestQueryFindNative.java @@ -246,4 +246,101 @@ public class TestQueryFindNative extends BaseTestCase { } } + + @Test + public void findNativeWithOneFetchQuery() { + + ResetBasicData.reset(); + + String sql = "select * from o_customer"; + + LoggedSqlCollector.start(); + + List result = DB.findNative(Customer.class, sql) + .fetchQuery("contacts") + .findList(); + + assertThat(result).isNotEmpty(); + List loggedSql = LoggedSqlCollector.stop(); + + if (isH2()) { + assertThat(loggedSql).hasSize(2); + assertThat(loggedSql.get(0)).contains("from o_customer"); + assertThat(loggedSql.get(1)).contains("from contact"); + } + } + + @Test + public void findNativeWithOneFetch() { + + ResetBasicData.reset(); + + String sql = "select * from o_customer"; + + LoggedSqlCollector.start(); + + List result = DB.findNative(Customer.class, sql) + .fetch("contacts") + .findList(); + + assertThat(result).isNotEmpty(); + List loggedSql = LoggedSqlCollector.stop(); + + if (isH2()) { + assertThat(loggedSql).hasSize(2); + assertThat(loggedSql.get(0)).contains("from o_customer"); + assertThat(loggedSql.get(1)).contains("from contact"); + } + } + + @Test + public void findNativeWithMultipleFetchQuery() { + + ResetBasicData.reset(); + + String sql = "select * from o_customer"; + + LoggedSqlCollector.start(); + + List result = DB.findNative(Customer.class, sql) + .fetchQuery("orders") + .fetchQuery("contacts") + .findList(); + + assertThat(result).isNotEmpty(); + List loggedSql = LoggedSqlCollector.stop(); + + if (isH2()) { + assertThat(loggedSql).hasSize(3); + assertThat(loggedSql.get(0)).contains("from o_customer"); + assertThat(loggedSql).anyMatch(s -> s.contains("from o_order")); + assertThat(loggedSql).anyMatch(s -> s.contains("from contact")); + } + } + + @Test + public void findNativeWithMultipleFetch() { + + ResetBasicData.reset(); + + String sql = "select * from o_customer"; + + LoggedSqlCollector.start(); + + List result = DB.findNative(Customer.class, sql) + .fetch("orders") + .fetch("contacts") + .findList(); + + assertThat(result).isNotEmpty(); + List loggedSql = LoggedSqlCollector.stop(); + + if (isH2()) { + assertThat(loggedSql).hasSize(3); + assertThat(loggedSql.get(0)).contains("from o_customer"); + assertThat(loggedSql).anyMatch(s -> s.contains("from o_order")); + assertThat(loggedSql).anyMatch(s -> s.contains("from contact")); + } + } + } From af4f70e3a7162cf2fe0bec0a955f6241cbfa3d6b Mon Sep 17 00:00:00 2001 From: Ben Kempe Date: Mon, 1 Mar 2021 13:21:05 +0100 Subject: [PATCH 27/31] add in(subQuery) support for query beans --- .../io/ebean/typequery/PBaseValueEqual.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ebean-querybean/src/main/java/io/ebean/typequery/PBaseValueEqual.java b/ebean-querybean/src/main/java/io/ebean/typequery/PBaseValueEqual.java index c102d9991..3883afe66 100644 --- a/ebean-querybean/src/main/java/io/ebean/typequery/PBaseValueEqual.java +++ b/ebean-querybean/src/main/java/io/ebean/typequery/PBaseValueEqual.java @@ -1,5 +1,7 @@ package io.ebean.typequery; +import io.ebean.Query; + import java.util.Collection; /** @@ -224,4 +226,25 @@ public abstract class PBaseValueEqual extends TQPropertyBase { expr().in(_name, values); return _root; } + + /** + * Is in the result of a subquery. + * + * @param subQuery values provided by a subQuery + * @return the root query bean instance + */ + public final R in(Query subQuery) { + expr().in(_name, subQuery); + return _root; + } + + /** + * Is in the result of a subquery. Synonym for in(). + * + * @param subQuery values provided by a subQuery + * @return the root query bean instance + */ + public final R isIn(Query subQuery) { + return in(subQuery); + } } From f416309863f69b48126b25054fc4406c227753d2 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Tue, 2 Mar 2021 14:44:54 +1300 Subject: [PATCH 28/31] No effective change - change DefaultOrmQuery methods to return Query rather than DefaultOrmQuery This is no effective functional change but we do need to update the test OrmQueryPlanKeyTest to cast now. --- .../server/querydefn/DefaultOrmQuery.java | 82 ++++++------- .../server/querydefn/OrmQueryPlanKeyTest.java | 113 +++++++++--------- 2 files changed, 97 insertions(+), 98 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index e19212e96..7a012d034 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -391,7 +391,7 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery setUseDocStore(boolean useDocStore) { + public Query setUseDocStore(boolean useDocStore) { this.useDocStore = useDocStore; return this; } @@ -431,7 +431,7 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery setAllowLoadErrors() { + public Query setAllowLoadErrors() { this.allowLoadErrors = true; return this; } @@ -452,21 +452,21 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery asOf(Timestamp asOfDateTime) { + public Query asOf(Timestamp asOfDateTime) { this.temporalMode = (asOfDateTime != null) ? TemporalMode.AS_OF : TemporalMode.CURRENT; this.asOf = asOfDateTime; return this; } @Override - public DefaultOrmQuery asDraft() { + public Query asDraft() { this.temporalMode = TemporalMode.DRAFT; this.useBeanCache = CacheMode.OFF; return this; } @Override - public DefaultOrmQuery setIncludeSoftDeletes() { + public Query setIncludeSoftDeletes() { this.temporalMode = TemporalMode.SOFT_DELETED; return this; } @@ -489,7 +489,7 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery setRawSql(RawSql rawSql) { + public Query setRawSql(RawSql rawSql) { this.rawSql = (SpiRawSql) rawSql; return this; } @@ -802,12 +802,12 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery copy() { + public SpiQuery copy() { return copy(server); } @Override - public DefaultOrmQuery copy(SpiEbeanServer server) { + public SpiQuery copy(SpiEbeanServer server) { DefaultOrmQuery copy = new DefaultOrmQuery<>(beanDescriptor, server, expressionFactory); copy.transaction = transaction; copy.m2mIncludeJoin = m2mIncludeJoin; @@ -955,7 +955,7 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery setAutoTune(boolean autoTune) { + public Query setAutoTune(boolean autoTune) { this.autoTune = autoTune; return this; } @@ -971,21 +971,21 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery forUpdate() { + public Query forUpdate() { return setForUpdateWithMode(LockWait.WAIT, LockType.DEFAULT); } @Override - public DefaultOrmQuery forUpdateNoWait() { + public Query forUpdateNoWait() { return setForUpdateWithMode(LockWait.NOWAIT, LockType.DEFAULT); } @Override - public DefaultOrmQuery forUpdateSkipLocked() { + public Query forUpdateSkipLocked() { return setForUpdateWithMode(LockWait.SKIPLOCKED, LockType.DEFAULT); } - private DefaultOrmQuery setForUpdateWithMode(LockWait mode, LockType lockType) { + private Query setForUpdateWithMode(LockWait mode, LockType lockType) { this.forUpdate = mode; this.lockType = lockType; this.useBeanCache = CacheMode.OFF; @@ -1329,7 +1329,7 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery setReadOnly(boolean readOnly) { + public Query setReadOnly(boolean readOnly) { this.readOnly = readOnly; return this; } @@ -1375,19 +1375,19 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery setUseQueryCache(CacheMode useQueryCache) { + public Query setUseQueryCache(CacheMode useQueryCache) { this.useQueryCache = useQueryCache; return this; } @Override - public DefaultOrmQuery setLoadBeanCache(boolean loadBeanCache) { + public Query setLoadBeanCache(boolean loadBeanCache) { this.useBeanCache = CacheMode.PUT; return this; } @Override - public DefaultOrmQuery setTimeout(int secs) { + public Query setTimeout(int secs) { this.timeout = secs; return this; } @@ -1413,19 +1413,19 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery select(String columns) { + public Query select(String columns) { detail.select(columns); return this; } @Override - public DefaultOrmQuery select(FetchGroup fetchGroup) { + public Query select(FetchGroup fetchGroup) { this.detail = ((SpiFetchGroup) fetchGroup).detail(); return this; } @Override - public DefaultOrmQuery fetch(String property) { + public Query fetch(String property) { return fetch(property, null, null); } @@ -1444,12 +1444,12 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery fetch(String property, FetchConfig joinConfig) { + public Query fetch(String property, FetchConfig joinConfig) { return fetch(property, null, joinConfig); } @Override - public DefaultOrmQuery fetch(String property, String columns) { + public Query fetch(String property, String columns) { return fetch(property, columns, null); } @@ -1469,7 +1469,7 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery fetch(String property, String columns, FetchConfig config) { + public Query fetch(String property, String columns, FetchConfig config) { detail.fetch(property, columns, config); return this; } @@ -1633,7 +1633,7 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery setParameter(Object value) { + public Query setParameter(Object value) { if (bindParams == null) { bindParams = new BindParams(); } @@ -1642,7 +1642,7 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery setParameters(Object... values) { + public Query setParameters(Object... values) { if (bindParams == null) { bindParams = new BindParams(); } @@ -1656,7 +1656,7 @@ public class DefaultOrmQuery implements SpiQuery { * have in the query. */ @Override - public DefaultOrmQuery setParameter(int position, Object value) { + public Query setParameter(int position, Object value) { if (bindParams == null) { bindParams = new BindParams(); } @@ -1668,7 +1668,7 @@ public class DefaultOrmQuery implements SpiQuery { * Set a named bind parameter. Named parameters have a colon to prefix the name. */ @Override - public DefaultOrmQuery setParameter(String name, Object value) { + public Query setParameter(String name, Object value) { if (namedParams != null) { ONamedParam param = namedParams.get(name); if (param != null) { @@ -1714,12 +1714,12 @@ public class DefaultOrmQuery implements SpiQuery { @Override @Deprecated - public DefaultOrmQuery orderBy(String orderByClause) { + public Query orderBy(String orderByClause) { return order(orderByClause); } @Override - public DefaultOrmQuery order(String orderByClause) { + public Query order(String orderByClause) { if (orderByClause == null || orderByClause.trim().isEmpty()) { this.orderBy = null; } else { @@ -1730,12 +1730,12 @@ public class DefaultOrmQuery implements SpiQuery { @Override @Deprecated - public DefaultOrmQuery setOrderBy(OrderBy orderBy) { + public Query setOrderBy(OrderBy orderBy) { return setOrder(orderBy); } @Override - public DefaultOrmQuery setOrder(OrderBy orderBy) { + public Query setOrder(OrderBy orderBy) { this.orderBy = orderBy; if (orderBy != null) { orderBy.setQuery(this); @@ -1767,13 +1767,13 @@ public class DefaultOrmQuery implements SpiQuery { * Internally set to use SQL DISTINCT on the query but still have id property included. */ @Override - public DefaultOrmQuery setDistinct(boolean distinct) { + public Query setDistinct(boolean distinct) { this.distinct = distinct; return this; } @Override - public DefaultOrmQuery setCountDistinct(CountDistinctOrder countDistinctOrder) { + public Query setCountDistinct(CountDistinctOrder countDistinctOrder) { this.countDistinctOrder = countDistinctOrder; return this; } @@ -1824,7 +1824,7 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery setDisableLazyLoading(boolean disableLazyLoading) { + public Query setDisableLazyLoading(boolean disableLazyLoading) { this.disableLazyLoading = disableLazyLoading; return this; } @@ -1840,7 +1840,7 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery setFirstRow(int firstRow) { + public Query setFirstRow(int firstRow) { this.firstRow = firstRow; return this; } @@ -1851,7 +1851,7 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery setMaxRows(int maxRows) { + public Query setMaxRows(int maxRows) { this.maxRows = maxRows; return this; } @@ -1862,7 +1862,7 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery setMapKey(String mapKey) { + public Query setMapKey(String mapKey) { this.mapKey = mapKey; return this; } @@ -1873,7 +1873,7 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery setId(Object id) { + public Query setId(Object id) { if (id == null) { throw new NullPointerException("The id is null"); } @@ -1887,7 +1887,7 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery where(Expression expression) { + public Query where(Expression expression) { where().add(expression); return this; } @@ -1917,7 +1917,7 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery having(Expression expression) { + public Query having(Expression expression) { having().add(expression); return this; } @@ -2043,7 +2043,7 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public DefaultOrmQuery alias(String alias) { + public Query alias(String alias) { this.rootTableAlias = alias; return this; } diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/OrmQueryPlanKeyTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/OrmQueryPlanKeyTest.java index 0c2f07e98..305f9122b 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/OrmQueryPlanKeyTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/querydefn/OrmQueryPlanKeyTest.java @@ -18,8 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat; public class OrmQueryPlanKeyTest extends BaseExpressionTest { - - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "rawtypes"}) private DefaultOrmQuery query() { return (DefaultOrmQuery) server().find(Customer.class); } @@ -85,41 +84,40 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest { @Test public void equals_when_firstRowsDifferent() { - CQueryPlanKey key1 = query().setFirstRow(10).createQueryPlanKey(); - CQueryPlanKey key2 = query().createQueryPlanKey(); + CQueryPlanKey key1 = planKey(query().setFirstRow(10)); + CQueryPlanKey key2 = planKey(query()); assertDifferent(key1, key2); - key1 = query().setFirstRow(10).createQueryPlanKey(); - key2 = query().setFirstRow(9).createQueryPlanKey(); + key1 = planKey(query().setFirstRow(10)); + key2 = planKey(query().setFirstRow(9)); assertDifferent(key1, key2); - key1 = query().createQueryPlanKey(); - key2 = query().setFirstRow(9).createQueryPlanKey(); + key1 = planKey(query()); + key2 = planKey(query().setFirstRow(9)); assertDifferent(key1, key2); } @Test public void equals_when_maxRowsDifferent() { - CQueryPlanKey key1 = query().setMaxRows(10).createQueryPlanKey(); - CQueryPlanKey key2 = query().createQueryPlanKey(); + CQueryPlanKey key1 = planKey(query().setMaxRows(10)); + CQueryPlanKey key2 = planKey(query()); assertDifferent(key1, key2); - key1 = query().setMaxRows(10).createQueryPlanKey(); - key2 = query().setMaxRows(9).createQueryPlanKey(); + key1 = planKey(query().setMaxRows(10)); + key2 = planKey(query().setMaxRows(9)); assertDifferent(key1, key2); - key1 = query().createQueryPlanKey(); - key2 = query().setMaxRows(9).createQueryPlanKey(); + key1 = planKey(query()); + key2 = planKey(query().setMaxRows(9)); assertDifferent(key1, key2); - } @Test public void equals_when_firstRowsMaxRowsSame() { - CQueryPlanKey key1 = query().setMaxRows(10).setFirstRow(20).createQueryPlanKey(); - CQueryPlanKey key2 = query().setFirstRow(20).setMaxRows(10).createQueryPlanKey(); + CQueryPlanKey key1 = planKey(query().setMaxRows(10).setFirstRow(20)); + CQueryPlanKey key2 = planKey(query().setFirstRow(20).setMaxRows(10)); assertSame(key1, key2); } @@ -132,43 +130,43 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest { @Test public void equals_when_diffOrderByNull() { - CQueryPlanKey key1 = query().order("id").createQueryPlanKey(); - CQueryPlanKey key2 = query().createQueryPlanKey(); + CQueryPlanKey key1 = planKey(query().order("id")); + CQueryPlanKey key2 = planKey(query()); assertDifferent(key1, key2); - key1 = ((DefaultOrmQuery) query().order().asc("id")).createQueryPlanKey(); - key2 = query().createQueryPlanKey(); + key1 = planKey(query().order().asc("id")); + key2 = planKey(query()); assertDifferent(key1, key2); } @Test public void equals_when_orderBySame() { - CQueryPlanKey key1 = query().order("id, name").createQueryPlanKey(); - CQueryPlanKey key2 = query().order("id, name").createQueryPlanKey(); + CQueryPlanKey key1 = planKey(query().order("id, name")); + CQueryPlanKey key2 = planKey(query().order("id, name")); assertSame(key1, key2); } @Test public void equals_when_diffDistinct() { - CQueryPlanKey key1 = query().setDistinct(true).createQueryPlanKey(); - CQueryPlanKey key2 = query().createQueryPlanKey(); + CQueryPlanKey key1 = planKey(query().setDistinct(true)); + CQueryPlanKey key2 = planKey(query()); assertDifferent(key1, key2); } @Test public void equals_when_sameDistinct() { - CQueryPlanKey key1 = query().setDistinct(true).createQueryPlanKey(); - CQueryPlanKey key2 = query().setDistinct(true).createQueryPlanKey(); + CQueryPlanKey key1 = planKey(query().setDistinct(true)); + CQueryPlanKey key2 = planKey(query().setDistinct(true)); assertSame(key1, key2); } @Test public void equals_when_useDocStore() { - CQueryPlanKey key1 = query().setUseDocStore(true).createQueryPlanKey(); - CQueryPlanKey key2 = query().createQueryPlanKey(); + CQueryPlanKey key1 = planKey(query().setUseDocStore(true)); + CQueryPlanKey key2 = planKey(query()); assertDifferent(key1, key2); } @@ -176,79 +174,78 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest { @Test public void equals_when_diffMapKey() { - CQueryPlanKey key1 = query().setMapKey("name").createQueryPlanKey(); - CQueryPlanKey key2 = query().createQueryPlanKey(); + CQueryPlanKey key1 = planKey(query().setMapKey("name")); + CQueryPlanKey key2 = planKey(query()); assertDifferent(key1, key2); - CQueryPlanKey key3 = query().setMapKey("email").createQueryPlanKey(); + CQueryPlanKey key3 = planKey(query().setMapKey("email")); assertDifferent(key1, key3); - CQueryPlanKey key4 = query().setMapKey("name").createQueryPlanKey(); + CQueryPlanKey key4 = planKey(query().setMapKey("name")); assertSame(key1, key4); } @Test public void equals_when_diffIdNull() { - CQueryPlanKey key1 = query().setId(42).createQueryPlanKey(); - CQueryPlanKey key2 = query().createQueryPlanKey(); + CQueryPlanKey key1 = planKey(query().setId(42)); + CQueryPlanKey key2 = planKey(query()); assertDifferent(key1, key2); } @Test public void equals_when_idBothGiven() { - CQueryPlanKey key1 = query().setId(42).createQueryPlanKey(); - CQueryPlanKey key2 = query().setId(23).createQueryPlanKey(); + CQueryPlanKey key1 = planKey(query().setId(42)); + CQueryPlanKey key2 = planKey(query().setId(23)); assertSame(key1, key2); } @Test public void equals_when_diffTemporalMode() { - CQueryPlanKey key1 = query().createQueryPlanKey(); - CQueryPlanKey key2 = query().asDraft().createQueryPlanKey(); + CQueryPlanKey key1 = planKey(query()); + CQueryPlanKey key2 = planKey(query().asDraft()); assertDifferent(key1, key2); - CQueryPlanKey key3 = query().asOf(new Timestamp(System.currentTimeMillis())).createQueryPlanKey(); + CQueryPlanKey key3 = planKey(query().asOf(new Timestamp(System.currentTimeMillis()))); assertDifferent(key1, key3); - CQueryPlanKey key4 = query().setIncludeSoftDeletes().createQueryPlanKey(); + CQueryPlanKey key4 = planKey(query().setIncludeSoftDeletes()); assertDifferent(key1, key4); } @Test public void equals_when_diffForUpdate() { - CQueryPlanKey key1 = query().forUpdate().createQueryPlanKey(); - CQueryPlanKey key2 = query().createQueryPlanKey(); + CQueryPlanKey key1 = planKey(query().forUpdate()); + CQueryPlanKey key2 = planKey(query()); assertDifferent(key1, key2); - CQueryPlanKey key3 = query().forUpdateNoWait().createQueryPlanKey(); + CQueryPlanKey key3 = planKey(query().forUpdateNoWait()); assertDifferent(key1, key3); - CQueryPlanKey key4 = query().forUpdateSkipLocked().createQueryPlanKey(); + CQueryPlanKey key4 = planKey(query().forUpdateSkipLocked()); assertDifferent(key1, key4); - CQueryPlanKey key5 = query().forUpdate().createQueryPlanKey(); + CQueryPlanKey key5 = planKey(query().forUpdate()); assertSame(key1, key5); } @Test public void equals_when_diffRootAliasNull() { - CQueryPlanKey key1 = query().alias("alias").createQueryPlanKey(); - CQueryPlanKey key2 = query().createQueryPlanKey(); + CQueryPlanKey key1 = planKey(query().alias("alias")); + CQueryPlanKey key2 = planKey(query()); assertDifferent(key1, key2); - CQueryPlanKey key3 = query().alias("diff").createQueryPlanKey(); + CQueryPlanKey key3 = planKey(query().alias("diff")); assertDifferent(key1, key3); - CQueryPlanKey key4 = query().alias("alias").createQueryPlanKey(); + CQueryPlanKey key4 = planKey(query().alias("alias")); assertSame(key1, key4); } - private DefaultOrmQuery list_id_eq_42() { return (DefaultOrmQuery) server().find(Customer.class) .where().eq("id", 42).query(); @@ -298,7 +295,6 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest { assertDifferent(key1, key4); } - @Test public void equals_when_sameHaving() { @@ -309,7 +305,7 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest { @Test public void equals_when_manualId_andSelectClause() { - DefaultOrmQuery q1 = query().select("name"); + DefaultOrmQuery q1 = (DefaultOrmQuery)query().select("name"); q1.setManualId(); assertDifferent(q1, query().select("name")); @@ -327,16 +323,19 @@ public class OrmQueryPlanKeyTest extends BaseExpressionTest { return planKey(id.query()); } + @SuppressWarnings({"rawtypes"}) private CQueryPlanKey planKey(Query query) { return ((DefaultOrmQuery) query).createQueryPlanKey(); } - private void assertDifferent(DefaultOrmQuery q1, DefaultOrmQuery q2) { - assertDifferent(q1.createQueryPlanKey(), q2.createQueryPlanKey()); + @SuppressWarnings({"unchecked", "rawtypes"}) + private void assertDifferent(Query q1, Query q2) { + assertDifferent(planKey(q1), planKey(q2)); } - private void assertSame(DefaultOrmQuery q1, DefaultOrmQuery q2) { - assertSame(q1.createQueryPlanKey(), q2.createQueryPlanKey()); + @SuppressWarnings({"unchecked", "rawtypes"}) + private void assertSame(Query q1, Query q2) { + assertSame(planKey(q1), planKey(q2)); } private void assertDifferent(CQueryPlanKey key1, CQueryPlanKey key2) { From fe62ad2ea2c251f7135658f394505bd4058759af Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Tue, 2 Mar 2021 15:28:17 +1300 Subject: [PATCH 29/31] #2184 - Initial refactor of DefaultOrmQuery to support 2184 - Add fetchInternal() and point fetchQuery() fetchLazy() and fetchCache() to it - Move the fetch() methods so they are located together --- .../server/querydefn/DefaultOrmQuery.java | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 7a012d034..cd1782ad7 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1425,52 +1425,56 @@ public class DefaultOrmQuery implements SpiQuery { } @Override - public Query fetch(String property) { - return fetch(property, null, null); + public Query fetch(String path) { + return fetch(path, null, null); } @Override - public Query fetchQuery(String property) { - return fetch(property, null, FETCH_QUERY); - } - - public Query fetchCache(String property) { - return fetch(property, null, FETCH_CACHE); + public Query fetch(String path, FetchConfig joinConfig) { + return fetch(path, null, joinConfig); } @Override - public Query fetchLazy(String property) { - return fetch(property, null, FETCH_LAZY); + public Query fetch(String path, String properties) { + return fetch(path, properties, null); } @Override - public Query fetch(String property, FetchConfig joinConfig) { - return fetch(property, null, joinConfig); + public Query fetch(String path, String properties, FetchConfig config) { + return fetchInternal(path, properties, config); } @Override - public Query fetch(String property, String columns) { - return fetch(property, columns, null); + public Query fetchQuery(String path) { + return fetchInternal(path, null, FETCH_QUERY); + } + + public Query fetchCache(String path) { + return fetchInternal(path, null, FETCH_CACHE); } @Override - public Query fetchQuery(String property, String columns) { - return fetch(property, columns, FETCH_QUERY); + public Query fetchLazy(String path) { + return fetchInternal(path, null, FETCH_LAZY); } @Override - public Query fetchCache(String property, String columns) { - return fetch(property, columns, FETCH_CACHE); + public Query fetchQuery(String path, String properties) { + return fetchInternal(path, properties, FETCH_QUERY); } @Override - public Query fetchLazy(String property, String columns) { - return fetch(property, columns, FETCH_LAZY); + public Query fetchCache(String path, String properties) { + return fetchInternal(path, properties, FETCH_CACHE); } @Override - public Query fetch(String property, String columns, FetchConfig config) { - detail.fetch(property, columns, config); + public Query fetchLazy(String path, String properties) { + return fetchInternal(path, properties, FETCH_LAZY); + } + + private Query fetchInternal(String path, String properties, FetchConfig config) { + detail.fetch(path, properties, config); return this; } From 81e445e386496a9c76e011f0bbdfcf283c19314a Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Tue, 2 Mar 2021 15:47:07 +1300 Subject: [PATCH 30/31] #2184 - Fix for findNative() with fetch() - convert fetch to fetchQuery with nativeSql When using nativeSql (think of it as the "root query") we can't use "fetch joins" (FetchConfig.ofDefault()) as that means to prefer to use a SQL JOIN. In this nativeSql case we need to effectively automatically convert fetch() to fetchQuery() --- .../ebeaninternal/server/querydefn/DefaultOrmQuery.java | 4 ++++ .../test/java/org/tests/query/TestQueryFindNative.java | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index cd1782ad7..6f72febb9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1441,6 +1441,10 @@ public class DefaultOrmQuery implements SpiQuery { @Override public Query fetch(String path, String properties, FetchConfig config) { + if (nativeSql != null && (config == null || config.isJoin())) { + // can't use fetch join with nativeSql (as the root query) + config = FETCH_QUERY; + } return fetchInternal(path, properties, config); } diff --git a/ebean-core/src/test/java/org/tests/query/TestQueryFindNative.java b/ebean-core/src/test/java/org/tests/query/TestQueryFindNative.java index 7cc4ac9c6..8e11d3328 100644 --- a/ebean-core/src/test/java/org/tests/query/TestQueryFindNative.java +++ b/ebean-core/src/test/java/org/tests/query/TestQueryFindNative.java @@ -2,6 +2,7 @@ package org.tests.query; import io.ebean.BaseTestCase; import io.ebean.DB; +import io.ebean.FetchConfig; import io.ebean.PagedList; import org.assertj.core.util.Lists; import org.ebeantest.LoggedSqlCollector; @@ -280,7 +281,7 @@ public class TestQueryFindNative extends BaseTestCase { LoggedSqlCollector.start(); List result = DB.findNative(Customer.class, sql) - .fetch("contacts") + .fetch("contacts", "firstName, lastName") .findList(); assertThat(result).isNotEmpty(); @@ -289,7 +290,7 @@ public class TestQueryFindNative extends BaseTestCase { if (isH2()) { assertThat(loggedSql).hasSize(2); assertThat(loggedSql.get(0)).contains("from o_customer"); - assertThat(loggedSql.get(1)).contains("from contact"); + assertThat(loggedSql.get(1)).contains("select t0.customer_id, t0.id, t0.first_name, t0.last_name from contact t0 where"); } } @@ -328,8 +329,9 @@ public class TestQueryFindNative extends BaseTestCase { LoggedSqlCollector.start(); List result = DB.findNative(Customer.class, sql) + // with nativeSql fetch (default) are converted to fetchQuery() .fetch("orders") - .fetch("contacts") + .fetch("contacts", FetchConfig.ofDefault()) .findList(); assertThat(result).isNotEmpty(); From c86f965d36a280f5a323aa7873a726bbd87d4994 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Thu, 4 Mar 2021 08:55:11 +1300 Subject: [PATCH 31/31] #2187 - Improve error message thrown when @JoinTable @JoinColumn not satisfied. Added " or a @JoinColumn needs an explicit referencedColumnName specified?"; --- .../java/io/ebeaninternal/server/deploy/BeanPropertyAssoc.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssoc.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssoc.java index a2a091ef6..739021196 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssoc.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssoc.java @@ -609,7 +609,8 @@ public abstract class BeanPropertyAssoc extends BeanProperty implements STree String msg = "Error with the Join on [" + getFullBeanName() + "]. Could not find the matching foreign key for [" + matchColumn + "] in table[" + searchTable + "]?" - + " Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped?"; + + " Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped? " + + " or a @JoinColumn needs an explicit referencedColumnName specified?"; throw new PersistenceException(msg); } }