*
{@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/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..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&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-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-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();
+ }
+}
diff --git a/ebean-autotune/pom.xml b/ebean-autotune/pom.xml
index 7b0dabb3b..4ced5a224 100644
--- a/ebean-autotune/pom.xml
+++ b/ebean-autotune/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
@@ -26,7 +26,7 @@
io.ebean
ebean-core
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
provided
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-bom/pom.xml b/ebean-bom/pom.xml
index c6219cfea..2293b2db9 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.3-SNAPSHOT
ebean bom
@@ -81,88 +81,88 @@
io.ebean
ebean
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
io.ebean
ebean-api
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
io.ebean
ebean-core
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
io.ebean
ebean-core-type
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
io.ebean
ebean-ddl-generator
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
io.ebean
ebean-externalmapping-api
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
io.ebean
ebean-externalmapping-xml
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
io.ebean
ebean-autotune
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
io.ebean
ebean-querybean
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
io.ebean
querybean-generator
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
provided
io.ebean
kotlin-querybean-generator
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
provided
io.ebean
ebean-test
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
test
io.ebean
ebean-postgis
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
io.ebean
ebean-redis
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
* 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/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- * 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 + * *
- *- *
- * 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;
- * ...
- *
- * }
- * *
{@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();
*
* }
* *
- *
{@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)
@@ -278,22 +264,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/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/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");
+
}
}
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;
diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml
index 98186aa7f..476ee7c2a 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.3-SNAPSHOT
ebean-redis
@@ -22,35 +22,35 @@
io.ebean
ebean-api
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
provided
io.ebean
ebean-core
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
provided
io.ebean
ebean-querybean
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
test
io.ebean
querybean-generator
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
test
io.ebean
ebean-test
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
test
diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml
index d880773a8..4b5e47899 100644
--- a/ebean-test/pom.xml
+++ b/ebean-test/pom.xml
@@ -4,27 +4,13 @@
ebean-parent
io.ebean
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
-
-
-
-
-
-
-
- scm:git:git@github.com:ebean-orm/ebean.git
- ebean-parent-12.6.5
-
ebean test
Testing support for Ebean
ebean-test
-
- 2.11.3
-
-
@@ -43,27 +29,34 @@
io.ebean
ebean-core
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
provided
io.ebean
ebean-ddl-generator
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
com.fasterxml.jackson.core
jackson-databind
- ${jackson-databind.version}
+ ${jackson.version}
+ true
+
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ ${jackson.version}
true
io.ebean
ebean-test-docker
- 4.1
+ 4.2
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"
+}
diff --git a/ebean/pom.xml b/ebean/pom.xml
index 337b3deba..7356b26ff 100644
--- a/ebean/pom.xml
+++ b/ebean/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
ebean composite
@@ -22,20 +22,20 @@
io.ebean
ebean-api
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
io.ebean
ebean-core
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
io.ebean
ebean-querybean
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml
index 07993d49e..8e4df4f39 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.3-SNAPSHOT
kotlin querybean generator
@@ -29,7 +29,7 @@
io.ebean
ebean-querybean
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
test
@@ -43,7 +43,7 @@
io.ebean
ebean-core
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
test
@@ -64,7 +64,7 @@
io.ebean
ebean-ddl-generator
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
test
@@ -106,7 +106,7 @@
io.ebean
kotlin-querybean-generator
- 12.6.1
+ 12.7.1
diff --git a/pom.xml b/pom.xml
index 88954f378..8e06ae121 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
io.ebean
ebean-parent
- 12.7.1-SNAPSHOT
+ 12.7.3-SNAPSHOT
pom
ebean parent
@@ -40,6 +40,7 @@
false
7.0
+ 2.12.1
diff --git a/querybean-generator/pom.xml b/querybean-generator/pom.xml
index 90140df71..bc6fc7019 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.3-SNAPSHOT
querybean generator