From 54530248a5ba2fdd9aa2a79adbf389f524ac2920 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Fri, 2 Mar 2018 13:25:55 +1300 Subject: [PATCH 1/4] #1312 - ENH: added SqlRow factory method to RawSqlService Support the PR added binaryOptimizedUUID --- src/main/java/io/ebean/RawSqlBuilder.java | 8 +++----- src/main/java/io/ebean/service/SpiRawSqlService.java | 7 +++++-- .../io/ebeaninternal/server/rawsql/DRawSqlService.java | 5 +++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/ebean/RawSqlBuilder.java b/src/main/java/io/ebean/RawSqlBuilder.java index 44121962c..0df4ac184 100644 --- a/src/main/java/io/ebean/RawSqlBuilder.java +++ b/src/main/java/io/ebean/RawSqlBuilder.java @@ -26,12 +26,10 @@ public interface RawSqlBuilder { } /** - * Create and return a SqlRow based on the resultSet and the dbTrueValue. - * @throws SQLException + * Create and return a SqlRow based on the resultSet with dbTrueValue and binaryOptimizedUUID options. */ - static SqlRow sqlRow(ResultSet resultSet, final String dbTrueValue) throws SQLException { - return XServiceProvider.rawSql().sqlRow(resultSet, dbTrueValue); - + static SqlRow sqlRow(ResultSet resultSet, final String dbTrueValue, boolean binaryOptimizedUUID) throws SQLException { + return XServiceProvider.rawSql().sqlRow(resultSet, dbTrueValue, binaryOptimizedUUID); } /** diff --git a/src/main/java/io/ebean/service/SpiRawSqlService.java b/src/main/java/io/ebean/service/SpiRawSqlService.java index 4d201fc58..323d3f055 100644 --- a/src/main/java/io/ebean/service/SpiRawSqlService.java +++ b/src/main/java/io/ebean/service/SpiRawSqlService.java @@ -29,7 +29,10 @@ public interface SpiRawSqlService { /** * Create based on a JDBC ResultSet. - * @throws SQLException + * + * @param resultSet The ResultSet row to read as a SqlRow + * @param dbTrueValue The DB true value + * @param binaryOptimizedUUID Flag set to true if the UUID value is stored as optimised binary(16) */ - SqlRow sqlRow(ResultSet resultSet, String dbTrueValue) throws SQLException; + SqlRow sqlRow(ResultSet resultSet, String dbTrueValue, boolean binaryOptimizedUUID) throws SQLException; } diff --git a/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java b/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java index 27f90a017..dd573111b 100644 --- a/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java +++ b/src/main/java/io/ebeaninternal/server/rawsql/DRawSqlService.java @@ -34,10 +34,11 @@ public class DRawSqlService implements SpiRawSqlService { } @Override - public SqlRow sqlRow(ResultSet resultSet, String dbTrueValue) throws SQLException { + public SqlRow sqlRow(ResultSet resultSet, String dbTrueValue, boolean binaryOptimizedUUID) throws SQLException { + ResultSetMetaData meta = resultSet.getMetaData(); int estCap = (int) (meta.getColumnCount() / 0.7f) + 1; - DefaultSqlRow ret = new DefaultSqlRow(estCap, 0.75f, dbTrueValue); + DefaultSqlRow ret = new DefaultSqlRow(estCap, 0.75f, dbTrueValue, binaryOptimizedUUID); for (int i = 1; i <= meta.getColumnCount(); i++) { String name = meta.getColumnLabel(i); From 09786a7062b554c9f415d39d26595184b766f963 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Fri, 2 Mar 2018 13:30:40 +1300 Subject: [PATCH 2/4] #1301 - No effective change - format lines --- .editorconfig | 1 + .../io/ebeaninternal/dbmigration/DefaultDbMigration.java | 3 +-- .../io/ebeaninternal/extraddl/model/ExtraDdlXmlReader.java | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.editorconfig b/.editorconfig index 837d975fa..18b710430 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,3 +10,4 @@ indent_style = space insert_final_newline = true trim_trailing_whitespace = true spaces_around_operators = true +max_line_length = 130 diff --git a/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java b/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java index 900e049f3..d767d62a6 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java +++ b/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java @@ -268,8 +268,7 @@ public class DefaultDbMigration implements DbMigration { if (extraDdl != null) { List ddlScript = extraDdl.getDdlScript(); for (DdlScript script : ddlScript) { - if (script.isDrop() == false - && ExtraDdlXmlReader.matchPlatform(dbPlatform.getName(), script.getPlatforms())) { + if (!script.isDrop() && ExtraDdlXmlReader.matchPlatform(dbPlatform.getName(), script.getPlatforms())) { writeExtraDdl(migrationDir, script); } } diff --git a/src/main/java/io/ebeaninternal/extraddl/model/ExtraDdlXmlReader.java b/src/main/java/io/ebeaninternal/extraddl/model/ExtraDdlXmlReader.java index b9c7e8392..629c9d202 100644 --- a/src/main/java/io/ebeaninternal/extraddl/model/ExtraDdlXmlReader.java +++ b/src/main/java/io/ebeaninternal/extraddl/model/ExtraDdlXmlReader.java @@ -1,10 +1,9 @@ package io.ebeaninternal.extraddl.model; +import io.ebean.util.StringHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.ebean.util.StringHelper; - import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; @@ -28,8 +27,7 @@ public class ExtraDdlXmlReader { } StringBuilder sb = new StringBuilder(300); for (DdlScript script : read.getDdlScript()) { - if (script.isDrop() == drops - && matchPlatform(platformName, script.getPlatforms())) { + if (script.isDrop() == drops && matchPlatform(platformName, script.getPlatforms())) { logger.debug("include script {}", script.getName()); String value = script.getValue(); sb.append(value); From 78912d75e0392c3ca363938427b70db9bc7bae6d Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Fri, 2 Mar 2018 13:45:31 +1300 Subject: [PATCH 3/4] #1306 - No effective change - javadoc update --- .../java/io/ebean/config/ServerConfig.java | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/ebean/config/ServerConfig.java b/src/main/java/io/ebean/config/ServerConfig.java index 679692b88..b0b341fb8 100644 --- a/src/main/java/io/ebean/config/ServerConfig.java +++ b/src/main/java/io/ebean/config/ServerConfig.java @@ -293,7 +293,7 @@ public class ServerConfig { /** * When true create a read only DataSource using readOnlyDataSourceConfig defaulting values from dataSourceConfig. * I believe this will default to true in some future release (as it has a nice performance benefit). - * + *

* autoReadOnlyDataSource is an unfortunate name for this config option but I haven't come up with a better one. */ private boolean autoReadOnlyDataSource; @@ -483,8 +483,7 @@ public class ServerConfig { /** * Should the javax.validation.constraints.NotNull enforce a notNull column in DB. - * If set to false, you have to use io.ebean.annotation.NotNull to explicityl create - * a not null column. + * If set to false, use io.ebean.annotation.NotNull or Column(nullable=true). */ private boolean useJavaxValidationNotNull = true; @@ -3034,14 +3033,12 @@ public class ServerConfig { } /** - * Controlws when Ebean should generate a NOT NULL column. - * If an io.ebean.annotation.NotNull is present, Ebean generates - * NOT NULL columns - * If set to true (default) Ebean generates also - * NOT NULL columns when a &x64;javax.validation.contstraints.NotNull - * annotation is present (and it is in Default group.) - * If set to false the &x64;javax.validation.contstraints.NotNull is - * ignored + * Controls if Ebean should ignore &x64;javax.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; @@ -3091,7 +3088,6 @@ public class ServerConfig { return dataSource; } - /** /** * Specify how UUID is stored. */ @@ -3164,5 +3160,5 @@ public class ServerConfig { VERSION4, VERSION1, VERSION1RND - }; + } } From 98e014f8c86366bd51da27b34d686a4cd3721b10 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Fri, 2 Mar 2018 14:30:38 +1300 Subject: [PATCH 4/4] #1300 - NEW: FetchCountDistinct ... add example to javadoc + support chaining use via ExpressionList --- src/main/java/io/ebean/ExpressionList.java | 30 +++ src/main/java/io/ebean/Query.java | 26 ++ .../expression/DefaultExpressionList.java | 5 + .../server/expression/JunctionExpression.java | 6 + .../query/other/TestQuerySingleAttribute.java | 230 ++++++++++-------- 5 files changed, 193 insertions(+), 104 deletions(-) diff --git a/src/main/java/io/ebean/ExpressionList.java b/src/main/java/io/ebean/ExpressionList.java index 1a5db3a21..588a6f7c0 100644 --- a/src/main/java/io/ebean/ExpressionList.java +++ b/src/main/java/io/ebean/ExpressionList.java @@ -480,6 +480,36 @@ public interface ExpressionList { */ Query setUseQueryCache(CacheMode useCache); + /** + * Extended version for setDistinct in conjunction with "findSingleAttributeList"; + * + *

{@code
+   *
+   *  List> orderStatusCount =
+   *
+   *     Ebean.find(Order.class)
+   *      .select("status")
+   *      .where()
+   *      .gt("orderDate", LocalDate.now().minusMonths(3))
+   *
+   *      // fetch as single attribute with a COUNT
+   *      .setCountDistinct(CountDistinctOrder.COUNT_DESC_ATTR_ASC)
+   *      .findSingleAttributeList();
+   *
+   *     for (CountedValue entry : orderStatusCount) {
+   *       System.out.println(" count:" + entry.getCount()+" orderStatus:" + entry.getValue() );
+   *     }
+   *
+   *   // produces
+   *
+   *   count:3 orderStatus:NEW
+   *   count:1 orderStatus:SHIPPED
+   *   count:1 orderStatus:COMPLETE
+   *
+   * }
+ */ + Query setCountDistinct(CountDistinctOrder orderBy); + /** * Calls {@link #setUseQueryCache(CacheMode)} with ON or OFF. * diff --git a/src/main/java/io/ebean/Query.java b/src/main/java/io/ebean/Query.java index b69e0c631..c7e4ed104 100644 --- a/src/main/java/io/ebean/Query.java +++ b/src/main/java/io/ebean/Query.java @@ -1279,6 +1279,32 @@ public interface Query { /** * Extended version for setDistinct in conjunction with "findSingleAttributeList"; + * + *
{@code
+   *
+   *  List> orderStatusCount =
+   *
+   *     Ebean.find(Order.class)
+   *      .select("status")
+   *      .where()
+   *      .gt("orderDate", LocalDate.now().minusMonths(3))
+   *
+   *      // fetch as single attribute with a COUNT
+   *      .setCountDistinct(CountDistinctOrder.COUNT_DESC_ATTR_ASC)
+   *      .findSingleAttributeList();
+   *
+   *     for (CountedValue entry : orderStatusCount) {
+   *       System.out.println(" count:" + entry.getCount()+" orderStatus:" + entry.getValue() );
+   *     }
+   *
+   *   // produces
+   *
+   *   count:3 orderStatus:NEW
+   *   count:1 orderStatus:SHIPPED
+   *   count:1 orderStatus:COMPLETE
+   *
+   * }
+ * */ Query setCountDistinct(CountDistinctOrder orderBy); diff --git a/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java b/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java index 49a6daec2..ec0e9b738 100644 --- a/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java +++ b/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.CacheMode; +import io.ebean.CountDistinctOrder; import io.ebean.Expression; import io.ebean.ExpressionFactory; import io.ebean.ExpressionList; @@ -493,6 +494,10 @@ public class DefaultExpressionList implements SpiExpressionList { return query.setUseQueryCache(useCache); } + public Query setCountDistinct(CountDistinctOrder orderBy) { + return query.setCountDistinct(orderBy); + } + @Override public Query setUseDocStore(boolean useDocsStore) { return query.setUseDocStore(useDocsStore); diff --git a/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java b/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java index d54096cc6..6b54867ff 100644 --- a/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java +++ b/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.expression; import io.ebean.CacheMode; +import io.ebean.CountDistinctOrder; import io.ebean.Expression; import io.ebean.ExpressionList; import io.ebean.FetchPath; @@ -792,6 +793,11 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression return exprList.setDisableReadAuditing(); } + @Override + public Query setCountDistinct(CountDistinctOrder orderBy) { + return exprList.setCountDistinct(orderBy); + } + @Override public ExpressionList startsWith(String propertyName, String value) { return exprList.startsWith(propertyName, value); diff --git a/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java b/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java index 101c0326f..ade6d2a91 100644 --- a/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java +++ b/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java @@ -12,9 +12,11 @@ import org.tests.inherit.Data; import org.tests.inherit.EUncle; import org.tests.model.basic.Contact; import org.tests.model.basic.Customer; +import org.tests.model.basic.Order; import org.tests.model.basic.ResetBasicData; import java.sql.Date; +import java.time.LocalDate; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -161,11 +163,11 @@ public class TestQuerySingleAttribute extends BaseTestCase { } @Test - public void distinctOnIdProperty(){ + public void distinctOnIdProperty() { Query query = Ebean.find(Customer.class) - .setDistinct(true) - .select("id") - .setMaxRows(100); + .setDistinct(true) + .select("id") + .setMaxRows(100); List ids = query.findSingleAttributeList(); if (isSqlServer()) { @@ -185,8 +187,8 @@ public class TestQuerySingleAttribute extends BaseTestCase { ResetBasicData.reset(); Query query = Ebean.find(Customer.class) - .setDistinct(true) - .fetch("billingAddress","city"); + .setDistinct(true) + .fetch("billingAddress", "city"); List cities = query.findSingleAttributeList(); @@ -200,22 +202,23 @@ public class TestQuerySingleAttribute extends BaseTestCase { ResetBasicData.reset(); Query query = Ebean.find(Contact.class) - .setDistinct(true) - .fetch("customer.billingAddress","city"); + .setDistinct(true) + .fetch("customer.billingAddress", "city"); List cities = query.findSingleAttributeList(); assertThat(sqlOf(query)).contains("select distinct t2.city from contact t0 join o_customer t1 on t1.id = t0.customer_id left join o_address t2 on t2.id = t1.billing_address_id"); assertThat(cities).contains("Auckland").containsNull(); } + @Test public void distinctSelectOnInheritedBean() { ResetBasicData.reset(); Query query = Ebean.find(ChildA.class) - .setDistinct(true) - .select("more"); + .setDistinct(true) + .select("more"); query.findSingleAttributeList(); assertThat(sqlOf(query)).contains("select distinct t0.more from rawinherit_parent t0 where t0.type = 'A' "); @@ -228,8 +231,8 @@ public class TestQuerySingleAttribute extends BaseTestCase { ResetBasicData.reset(); Query query = Ebean.find(EUncle.class) - .setDistinct(true) - .fetch("parent","more"); + .setDistinct(true) + .fetch("parent", "more"); query.findSingleAttributeList(); @@ -239,10 +242,10 @@ public class TestQuerySingleAttribute extends BaseTestCase { // hmm - same problem when not using distinct @Test - public void findSingleOnIdProperty(){ + public void findSingleOnIdProperty() { Query query = Ebean.find(Customer.class) - .select("id") - .setMaxRows(100); + .select("id") + .setMaxRows(100); List ids = query.findSingleAttributeList(); if (isSqlServer()) { @@ -261,7 +264,7 @@ public class TestQuerySingleAttribute extends BaseTestCase { ResetBasicData.reset(); Query query = Ebean.find(Customer.class) - .fetch("billingAddress","city"); + .fetch("billingAddress", "city"); List cities = query.findSingleAttributeList(); @@ -275,7 +278,7 @@ public class TestQuerySingleAttribute extends BaseTestCase { ResetBasicData.reset(); Query query = Ebean.find(ChildA.class) - .select("more"); + .select("more"); query.findSingleAttributeList(); assertThat(sqlOf(query)).contains("select t0.more from rawinherit_parent t0 where t0.type = 'A'"); @@ -288,7 +291,7 @@ public class TestQuerySingleAttribute extends BaseTestCase { ResetBasicData.reset(); Query query = Ebean.find(EUncle.class) - .fetch("parent","more"); + .fetch("parent", "more"); query.findSingleAttributeList(); @@ -301,7 +304,7 @@ public class TestQuerySingleAttribute extends BaseTestCase { ResetBasicData.reset(); Query query = Ebean.find(EUncle.class) - .fetch("parent","more"); + .fetch("parent", "more"); Ebean.getDefaultServer().findSingleAttributeList(query, null); @@ -315,15 +318,15 @@ public class TestQuerySingleAttribute extends BaseTestCase { ResetBasicData.reset(); Query query = Ebean.find(Data.class) - .setDistinct(true) - .fetch("parents","more") - .setMaxRows(100); + .setDistinct(true) + .fetch("parents", "more") + .setMaxRows(100); query.findSingleAttributeList(); assertThat(sqlOf(query)).contains("select distinct t0.more from rawinherit_data t0 " - + "join rawinherit_parent_rawinherit_data t1 on t0.id = t1.rawinherit_data_id " - + "join parent t2 on t1.rawinherit_parent_id = t2.id"); + + "join rawinherit_parent_rawinherit_data t1 on t0.id = t1.rawinherit_data_id " + + "join parent t2 on t1.rawinherit_parent_id = t2.id"); } @Test @@ -348,7 +351,7 @@ public class TestQuerySingleAttribute extends BaseTestCase { Query query = Ebean.find(Contact.class) .setDistinct(true) - .fetch("customer","billingAddress") + .fetch("customer", "billingAddress") .orderBy().desc("customer.billingAddress"); query.findSingleAttributeList(); @@ -362,19 +365,19 @@ public class TestQuerySingleAttribute extends BaseTestCase { ResetBasicData.reset(); Query query = Ebean.find(Contact.class) - .setDistinct(true) - .fetch("customer","billingAddress") - .where().eq("customer.billingAddress.city", "Auckland") - .orderBy().desc("customer.billingAddress.id"); + .setDistinct(true) + .fetch("customer", "billingAddress") + .where().eq("customer.billingAddress.city", "Auckland") + .orderBy().desc("customer.billingAddress.id"); List ids = query.findSingleAttributeList(); assertThat(ids).isNotEmpty(); assertThat(sqlOf(query)).contains("select distinct t1.billing_address_id from contact t0 " - + "join o_customer t1 on t1.id = t0.customer_id " // two spaces! - + "left join o_address t2 on t2.id = t1.billing_address_id " - + "where t2.city = ? " - + "order by t1.billing_address_id desc"); + + "join o_customer t1 on t1.id = t0.customer_id " // two spaces! + + "left join o_address t2 on t2.id = t1.billing_address_id " + + "where t2.city = ? " + + "order by t1.billing_address_id desc"); } @Test @@ -383,19 +386,19 @@ public class TestQuerySingleAttribute extends BaseTestCase { ResetBasicData.reset(); Query query = Ebean.find(Contact.class) - .setDistinct(true) - .fetch("customer","billingAddress") - .where().eq("customer.billingAddress.city", "Auckland") - .orderBy().desc("customer.billingAddress.id"); + .setDistinct(true) + .fetch("customer", "billingAddress") + .where().eq("customer.billingAddress.city", "Auckland") + .orderBy().desc("customer.billingAddress.id"); List ids = query.findSingleAttributeList(); assertThat(ids).isNotEmpty(); assertThat(sqlOf(query)).contains("select distinct t1.billing_address_id from contact t0 " - + "join o_customer t1 on t1.id = t0.customer_id " - + "left join o_address t2 on t2.id = t1.billing_address_id " - + "where t2.city = ? " - + "order by t1.billing_address_id desc"); + + "join o_customer t1 on t1.id = t0.customer_id " + + "left join o_address t2 on t2.id = t1.billing_address_id " + + "where t2.city = ? " + + "order by t1.billing_address_id desc"); } @Test @@ -404,19 +407,19 @@ public class TestQuerySingleAttribute extends BaseTestCase { ResetBasicData.reset(); Query query = Ebean.find(Contact.class) - .setDistinct(true) - .fetch("customer","billingAddress") - .where().eq("customer.billingAddress.city", "Auckland") - .orderBy().desc("customer.billingAddress.id"); + .setDistinct(true) + .fetch("customer", "billingAddress") + .where().eq("customer.billingAddress.city", "Auckland") + .orderBy().desc("customer.billingAddress.id"); List ids = query.findSingleAttributeList(); assertThat(ids).isNotEmpty(); assertThat(sqlOf(query)).contains("select distinct t1.billing_address_id from contact t0 " - + "join o_customer t1 on t1.id = t0.customer_id " - + "left join o_address t2 on t2.id = t1.billing_address_id " - + "where t2.city = ? " - + "order by t1.billing_address"); + + "join o_customer t1 on t1.id = t0.customer_id " + + "left join o_address t2 on t2.id = t1.billing_address_id " + + "where t2.city = ? " + + "order by t1.billing_address"); } @Test @@ -425,21 +428,22 @@ public class TestQuerySingleAttribute extends BaseTestCase { ResetBasicData.reset(); Query query = Ebean.find(Contact.class) - .setDistinct(true) - .fetch("customer","billingAddress") - .where().eq("customer.shippingAddress.city", "Auckland") // query on shippingAddress - .orderBy().desc("customer.billingAddress.id"); + .setDistinct(true) + .fetch("customer", "billingAddress") + .where().eq("customer.shippingAddress.city", "Auckland") // query on shippingAddress + .orderBy().desc("customer.billingAddress.id"); List ids = query.findSingleAttributeList(); assertThat(ids).isNotEmpty(); assertThat(sqlOf(query)).contains("select distinct t1.billing_address_id from contact t0 " - + "join o_customer t1 on t1.id = t0.customer_id " - + "left join o_address t2 on t2.id = t1.shipping_address_id " - + "where t2.city = ? " - + "order by t1.billing_address_id desc"); + + "join o_customer t1 on t1.id = t0.customer_id " + + "left join o_address t2 on t2.id = t1.shipping_address_id " + + "where t2.city = ? " + + "order by t1.billing_address_id desc"); } + @Test public void distinctWithCascadedFetchCount() { @@ -450,15 +454,15 @@ public class TestQuerySingleAttribute extends BaseTestCase { assertThat(count).isGreaterThanOrEqualTo(12); Query query = Ebean.find(Contact.class) - .select("firstName"); + .select("firstName"); List> list1 = query - .setCountDistinct(CountDistinctOrder.ATTR_ASC) + .setCountDistinct(CountDistinctOrder.ATTR_ASC) - .findSingleAttributeList(); + .findSingleAttributeList(); assertThat(sqlOf(query)).contains("select r1.attribute_, count(*) from (" - + "select t0.first_name as attribute_ from contact t0" - + ") r1 group by r1.attribute_ order by r1.attribute_"); + + "select t0.first_name as attribute_ from contact t0" + + ") r1 group by r1.attribute_ order by r1.attribute_"); assertThat(list1.get(0)).isInstanceOf(CountedValue.class); // FIXME: These asserts will fail, because other tests will interfere. see #1298 //assertThat(list1.toString()).isEqualTo("[3: Bugs1, 1: Fiona, 3: Fred1, 1: Jack, 3: Jim1, 1: Tracy]"); @@ -466,85 +470,103 @@ public class TestQuerySingleAttribute extends BaseTestCase { query = Ebean.find(Contact.class).select("firstName"); list1 = query - .setCountDistinct(CountDistinctOrder.ATTR_DESC) - .findSingleAttributeList(); + .setCountDistinct(CountDistinctOrder.ATTR_DESC) + .findSingleAttributeList(); assertThat(list1.get(0)).isInstanceOf(CountedValue.class); //assertThat(list1.toString()).isEqualTo("[1: Tracy, 3: Jim1, 1: Jack, 3: Fred1, 1: Fiona, 3: Bugs1]"); query = Ebean.find(Contact.class).select("firstName"); list1 = query - .setCountDistinct(CountDistinctOrder.COUNT_ASC_ATTR_DESC) - .findSingleAttributeList(); + .setCountDistinct(CountDistinctOrder.COUNT_ASC_ATTR_DESC) + .findSingleAttributeList(); assertThat(list1.get(0)).isInstanceOf(CountedValue.class); //assertThat(list1.toString()).isEqualTo("[1: Tracy, 1: Jack, 1: Fiona, 3: Jim1, 3: Fred1, 3: Bugs1]"); - query = Ebean.find(Contact.class).fetch("customer.shippingAddress","line1");//("firstName") + query = Ebean.find(Contact.class).fetch("customer.shippingAddress", "line1");//("firstName") List> list2 = query - .setCountDistinct(CountDistinctOrder.ATTR_ASC) - .findSingleAttributeList(); + .setCountDistinct(CountDistinctOrder.ATTR_ASC) + .findSingleAttributeList(); assertThat(sqlOf(query)).contains("select r1.attribute_, count(*) from (" - + "select t2.line_1 as attribute_ " - + "from contact t0 join o_customer t1 on t1.id = t0.customer_id " - + "left join o_address t2 on t2.id = t1.shipping_address_id " - + ") r1 group by r1.attribute_ order by r1.attribute_"); + + "select t2.line_1 as attribute_ " + + "from contact t0 join o_customer t1 on t1.id = t0.customer_id " + + "left join o_address t2 on t2.id = t1.shipping_address_id " + + ") r1 group by r1.attribute_ order by r1.attribute_"); assertThat(list2.get(0)).isInstanceOf(CountedValue.class); //assertThat(list2.toString()).isEqualTo("[1: null, 3: 1 Banana St, 5: 12 Apple St, 3: 15 Kumera Way]"); query = Ebean.find(Contact.class).select("firstName") - .where().eq("customer.shippingAddress.line1", "12 Apple St").query(); + .where().eq("customer.shippingAddress.line1", "12 Apple St").query(); List> list3 = query - .setCountDistinct(CountDistinctOrder.ATTR_ASC) - .findSingleAttributeList(); + .setCountDistinct(CountDistinctOrder.ATTR_ASC) + .findSingleAttributeList(); assertThat(sqlOf(query)).contains("select r1.attribute_, count(*) from (" - + "select t0.first_name as attribute_ from contact t0 " - + "join o_customer t1 on t1.id = t0.customer_id " - + "left join o_address t2 on t2.id = t1.shipping_address_id where t2.line_1 = ? " - + ") r1 group by r1.attribute_ order by r1.attribute_"); + + "select t0.first_name as attribute_ from contact t0 " + + "join o_customer t1 on t1.id = t0.customer_id " + + "left join o_address t2 on t2.id = t1.shipping_address_id where t2.line_1 = ? " + + ") r1 group by r1.attribute_ order by r1.attribute_"); assertThat(list3.get(0)).isInstanceOf(CountedValue.class); //assertThat(list3.toString()).isEqualTo("[1: Bugs1, 1: Fiona, 1: Fred1, 1: Jim1, 1: Tracy]"); - query = Ebean.find(Contact.class).fetch("customer.billingAddress","line1") - .where().or() - .ne("customer.shippingAddress.line1", "12 Apple St") - .isNull("customer.shippingAddress.line1") - .endOr().query(); + query = Ebean.find(Contact.class).fetch("customer.billingAddress", "line1") + .where().or() + .ne("customer.shippingAddress.line1", "12 Apple St") + .isNull("customer.shippingAddress.line1") + .endOr().query(); List> list4 = query - .setCountDistinct(CountDistinctOrder.ATTR_ASC) - .findSingleAttributeList(); + .setCountDistinct(CountDistinctOrder.ATTR_ASC) + .findSingleAttributeList(); assertThat(sqlOf(query)).contains("select r1.attribute_, count(*) from (" - + "select t2.line_1 as attribute_ " - + "from contact t0 join o_customer t1 on t1.id = t0.customer_id " - + "left join o_address t2 on t2.id = t1.billing_address_id " - + "left join o_address t3 on t3.id = t1.shipping_address_id " - + "where (t3.line_1 <> ? or t3.line_1 is null ) " - + ") r1 group by r1.attribute_ order by r1.attribute_"); + + "select t2.line_1 as attribute_ " + + "from contact t0 join o_customer t1 on t1.id = t0.customer_id " + + "left join o_address t2 on t2.id = t1.billing_address_id " + + "left join o_address t3 on t3.id = t1.shipping_address_id " + + "where (t3.line_1 <> ? or t3.line_1 is null ) " + + ") r1 group by r1.attribute_ order by r1.attribute_"); assertThat(list4.get(0)).isInstanceOf(CountedValue.class); //assertThat(list4.toString()).isEqualTo("[1: null, 3: Bos town, 3: P.O.Box 1234]"); // Test Limiter for MSSQL - query = Ebean.find(Contact.class).fetch("customer.billingAddress","line1").setFirstRow(1).setMaxRows(2); + query = Ebean.find(Contact.class).fetch("customer.billingAddress", "line1").setFirstRow(1).setMaxRows(2); List> list5 = query - .where().isNotNull("customer.billingAddress.line1").query() - .setCountDistinct(CountDistinctOrder.ATTR_DESC) - .findSingleAttributeList(); + .where().isNotNull("customer.billingAddress.line1").query() + .setCountDistinct(CountDistinctOrder.ATTR_DESC) + .findSingleAttributeList(); assertThat(sqlOf(query)).contains("select r1.attribute_, count(*) from (" - + "select t2.line_1 as attribute_ from contact t0 " - + "join o_customer t1 on t1.id = t0.customer_id " - + "left join o_address t2 on t2.id = t1.billing_address_id " - + "where t2.line_1 is not null " - + ") r1 group by r1.attribute_ order by r1.attribute_ desc "); + + "select t2.line_1 as attribute_ from contact t0 " + + "join o_customer t1 on t1.id = t0.customer_id " + + "left join o_address t2 on t2.id = t1.billing_address_id " + + "where t2.line_1 is not null " + + ") r1 group by r1.attribute_ order by r1.attribute_ desc "); if (isSqlServer()) { - assertThat(sqlOf(query)).endsWith(" fetch next 2 rows only"); + assertThat(sqlOf(query)).endsWith(" fetch next 2 rows only"); } else if (isDb2()) { assertThat(query.getGeneratedSql()).endsWith("FETCH FIRST 2 ROWS ONLY"); } else { - assertThat(sqlOf(query)).endsWith(" limit 2 offset 1"); + assertThat(sqlOf(query)).endsWith(" limit 2 offset 1"); } assertThat(list5.get(0)).isInstanceOf(CountedValue.class); //assertThat(list5.toString()).isEqualTo("[3: P.O.Box 1234, 3: Bos town]"); } + @Test + public void example() { + + ResetBasicData.reset(); + + List> orderStatusCount = + + Ebean.find(Order.class) + .select("status") + .where() + .gt("orderDate", LocalDate.now().minusMonths(3)) + .setCountDistinct(CountDistinctOrder.COUNT_DESC_ATTR_ASC) + .findSingleAttributeList(); + + for (CountedValue entry : orderStatusCount) { + System.out.println(" count:" + entry.getCount()+" orderStatus:" + entry.getValue() ); + } + } }