mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-23 07:51:04 +00:00
Merge branch 'master' of github.com:ebean-orm/ebean
This commit is contained in:
+1
-1
@@ -293,6 +293,6 @@ ebean.tenant.schemaProvider
|
||||
ebean.updateAllPropertiesInBatch
|
||||
ebean.updateChangesOnly
|
||||
ebean.updatesDeleteMissingChildren
|
||||
ebean.useJavaxValidationNotNull
|
||||
ebean.useValidationNotNull
|
||||
ebean.useJtaTransactionManager
|
||||
|
||||
|
||||
+3
-16
@@ -4,18 +4,13 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean api</name>
|
||||
<description>ebean api</description>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
|
||||
<properties>
|
||||
<jackson-core.version>2.11.3</jackson-core.version>
|
||||
<jackson-databind.version>2.11.3</jackson-databind.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!--
|
||||
@@ -74,7 +69,7 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson-core.version}</version>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@@ -82,7 +77,7 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson-databind.version}</version>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@@ -102,14 +97,6 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- provided scope to read validation annotations Size etc -->
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
<version>1.1.0.Final</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
|
||||
@@ -14,7 +14,7 @@ import java.util.Optional;
|
||||
*
|
||||
* public class CustomerFinder extends BeanFinder<Long,Customer> {
|
||||
*
|
||||
* @Inject
|
||||
* @Inject
|
||||
* public CustomerFinder(Database database) {
|
||||
* super(Customer.class, database);
|
||||
* }
|
||||
|
||||
@@ -9,10 +9,10 @@ import java.util.Collection;
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* @Repository
|
||||
* @Repository
|
||||
* public class CustomerRepository extends BeanRepository<Long,Customer> {
|
||||
*
|
||||
* @Inject
|
||||
* @Inject
|
||||
* public CustomerRepository(Database server) {
|
||||
* super(Customer.class, server);
|
||||
* }
|
||||
|
||||
@@ -897,7 +897,7 @@ public interface Database {
|
||||
* <pre>{@code
|
||||
* public class Order { ...
|
||||
*
|
||||
* @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
|
||||
* @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
|
||||
* List<OrderDetail> details;
|
||||
* ...
|
||||
* }
|
||||
|
||||
@@ -149,7 +149,7 @@ public interface DocumentStore {
|
||||
* .setUseDocStore(true)
|
||||
* .where()... // perhaps add predicates
|
||||
* .findEachWhile(new Predicate<Order>() {
|
||||
* @Override
|
||||
* @Override
|
||||
* public void accept(Order bean) {
|
||||
* // process the bean
|
||||
*
|
||||
|
||||
@@ -346,7 +346,7 @@ public final class Ebean {
|
||||
* <pre>{@code
|
||||
* public class Order { ...
|
||||
*
|
||||
* @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
|
||||
* @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
|
||||
* List<OrderDetail> details;
|
||||
* ...
|
||||
* }
|
||||
|
||||
@@ -200,7 +200,7 @@ public interface ExpressionFactory {
|
||||
Expression gtOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Greater than or Equal to OR Null <code> >= or null </code>
|
||||
* Greater than or Equal to OR Null ({@code >= or null })
|
||||
* <p>
|
||||
* 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 <code> <= or null </code>
|
||||
* Less Than or Equal to OR Null ({@code <= or null })
|
||||
* <p>
|
||||
* A convenient expression combining LE and Is Null. Most often useful for range
|
||||
* expressions where the bottom range value is nullable.
|
||||
|
||||
@@ -909,7 +909,7 @@ public interface ExpressionList<T> {
|
||||
ExpressionList<T> gtOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Greater Than or Equal to OR Null - <code> >= or null </code>.
|
||||
* Greater Than or Equal to OR Null - ({@code >= or null }).
|
||||
*/
|
||||
ExpressionList<T> geOrNull(String propertyName, Object value);
|
||||
|
||||
@@ -930,7 +930,7 @@ public interface ExpressionList<T> {
|
||||
ExpressionList<T> ltOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Less Than or Equal to OR Null - <code> <= or null </code>.
|
||||
* Less Than or Equal to OR Null - ({@code <= or null }).
|
||||
*/
|
||||
ExpressionList<T> leOrNull(String propertyName, Object value);
|
||||
|
||||
|
||||
@@ -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<I, T> {
|
||||
* // ... add extra customer specific finder methods
|
||||
* }
|
||||
*
|
||||
* @Entity
|
||||
* @Entity
|
||||
* public class Customer extends BaseModel {
|
||||
*
|
||||
* public static final CustomerFinder find = new CustomerFinder();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -41,14 +41,14 @@ package io.ebean;
|
||||
* <h3>Example OrderAggregate</h3>
|
||||
* <pre>{@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;
|
||||
|
||||
@@ -22,7 +22,7 @@ import java.sql.SQLException;
|
||||
* //
|
||||
* class CustomerMapper implements RowMapper<CustomerDto> {
|
||||
*
|
||||
* @Override
|
||||
* @Override
|
||||
* public CustomerDto map(ResultSet rset, int rowNum) throws SQLException {
|
||||
*
|
||||
* long id = rset.getLong(1);
|
||||
|
||||
@@ -13,23 +13,23 @@ package io.ebean;
|
||||
* </p>
|
||||
* <pre>{@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 {
|
||||
* ...
|
||||
* }
|
||||
|
||||
@@ -87,7 +87,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
|
||||
@Override
|
||||
public void internalAddWithCheck(Object bean) {
|
||||
if (list == null || !containsInstance(bean)) {
|
||||
if (list == null || bean == null || !containsInstance(bean)) {
|
||||
internalAdd(bean);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,9 +70,8 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
|
||||
@Override
|
||||
public void internalAddWithCheck(Object bean) {
|
||||
if (set == null || !set.contains(bean)) {
|
||||
internalAdd(bean);
|
||||
}
|
||||
// set add() already de-dups so just add it
|
||||
internalAdd(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 <code>&x64;javax.validation.contstraints.NotNull</code>
|
||||
* Controls if Ebean should ignore <code>&x64;javax.validation.contstraints.NotNull</code> or
|
||||
* <code>&x64;jakarta.validation.contstraints.NotNull</code>
|
||||
* with respect to generating a <code>NOT NULL</code> column.
|
||||
* <p>
|
||||
* Normally when Ebean sees javax NotNull annotation it means that column is defined as NOT NULL.
|
||||
* Set this to <code>false</code> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+35
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<!-- <parent>-->
|
||||
<!-- <groupId>org.avaje</groupId>-->
|
||||
@@ -26,7 +26,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
+1
-4
@@ -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<Order> desc = getBeanDescriptor(Order.class);
|
||||
private final BeanDescriptor<Order> desc = getBeanDescriptor(Order.class);
|
||||
|
||||
@Test
|
||||
public void buildDetail() {
|
||||
|
||||
+15
-15
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean bom</name>
|
||||
@@ -81,88 +81,88 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-api</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-xml</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-autotune</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>querybean-generator</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>kotlin-querybean-generator</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-postgis</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-redis</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -4,30 +4,25 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<name>ebean core type</name>
|
||||
<description>ebean scalar types api</description>
|
||||
|
||||
<properties>
|
||||
<jackson-core.version>2.11.3</jackson-core.version>
|
||||
<jackson-databind.version>2.11.3</jackson-databind.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson-core.version}</version>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
+12
-11
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-core</artifactId>
|
||||
@@ -18,11 +18,6 @@
|
||||
<tag>ebean-parent-12.6.5</tag>
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<jackson-core.version>2.11.3</jackson-core.version>
|
||||
<jackson-databind.version>2.11.3</jackson-databind.version>
|
||||
</properties>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<!-- Note: to use this profile, you need to download manually the db2jcc4 driver.
|
||||
@@ -92,19 +87,19 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-api</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -138,6 +133,12 @@
|
||||
<version>1.1.0.Final</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
@@ -157,14 +158,14 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson-core.version}</version>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson-databind.version}</version>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -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<T> extends Query<T>, TxnProfileEventCodes {
|
||||
public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCodes {
|
||||
|
||||
enum Mode {
|
||||
NORMAL(false), LAZYLOAD_MANY(false), LAZYLOAD_BEAN(true), REFRESH_BEAN(true);
|
||||
@@ -289,6 +290,16 @@ public interface SpiQuery<T> extends Query<T>, 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.
|
||||
*/
|
||||
|
||||
@@ -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<String> properties);
|
||||
|
||||
/**
|
||||
* Specify the fetch properties for the given path.
|
||||
*/
|
||||
void fetchProperties(String name, Set<String> properties, FetchConfig config);
|
||||
|
||||
}
|
||||
@@ -727,7 +727,7 @@ public class BeanDescriptor<T> implements BeanType<T>, 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<T> implements BeanType<T>, 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<T> implements BeanType<T>, 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.
|
||||
*/
|
||||
|
||||
@@ -609,7 +609,8 @@ public abstract class BeanPropertyAssoc<T> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,15 +1139,6 @@ public class DeployBeanProperty {
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<Size> getMetaAnnotationSize() {
|
||||
final List<Size> size = getMetaAnnotations(Size.class);
|
||||
final List<Size.List> lists = getMetaAnnotations(Size.List.class);
|
||||
for (Size.List list : lists) {
|
||||
Collections.addAll(size, list.value());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
public Formula getMetaAnnotationFormula(Platform platform) {
|
||||
Formula fallback = null;
|
||||
for (Annotation ann : metaAnnotations) {
|
||||
|
||||
+4
-10
@@ -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;
|
||||
@@ -28,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.
|
||||
@@ -117,13 +114,10 @@ public class AnnotationAssocOnes extends AnnotationAssoc {
|
||||
if (nonNull != null) {
|
||||
prop.setNullable(false);
|
||||
}
|
||||
if (validationAnnotations) {
|
||||
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 (readConfig.isValidationNotNull(prop)) {
|
||||
// overrides optional attribute of ManyToOne etc
|
||||
prop.setNullable(false);
|
||||
prop.getTableJoin().setType(SqlJoinType.INNER);
|
||||
}
|
||||
|
||||
// check for manually defined joins
|
||||
|
||||
+4
-16
@@ -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,29 +256,19 @@ 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
|
||||
if (readConfig.isValidationNotNull(prop)) {
|
||||
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 (maxSize != -1) {
|
||||
int maxSize = readConfig.maxValidationSize(prop);
|
||||
if (maxSize > 0) {
|
||||
prop.setDbLength(maxSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initTenantId(DeployBeanProperty prop) {
|
||||
if (validationAnnotations) {
|
||||
if (readConfig.checkValidationAnnotations()) {
|
||||
initValidation(prop);
|
||||
}
|
||||
if (has(prop, TenantId.class)) {
|
||||
|
||||
@@ -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,14 +25,11 @@ public abstract class AnnotationParser extends AnnotationBase {
|
||||
|
||||
final Class<?> beanType;
|
||||
|
||||
final boolean validationAnnotations;
|
||||
|
||||
final ReadAnnotationConfig readConfig;
|
||||
|
||||
AnnotationParser(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig) {
|
||||
super(info.getUtil());
|
||||
this.readConfig = readConfig;
|
||||
this.validationAnnotations = readConfig.isJavaxValidationAnnotations();
|
||||
this.info = info;
|
||||
this.beanType = info.getDescriptor().getBeanType();
|
||||
this.descriptor = info.getDescriptor();
|
||||
@@ -127,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.isUseJavaxValidationNotNull()) {
|
||||
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]);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
package io.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
class InitMetaValidationAnnotation {
|
||||
|
||||
static void init(ReadAnnotationConfig readConfig) {
|
||||
readConfig.addMetaAnnotation(Size.class);
|
||||
readConfig.addMetaAnnotation(Size.List.class);
|
||||
}
|
||||
}
|
||||
+52
-8
@@ -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;
|
||||
@@ -21,9 +23,12 @@ 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;
|
||||
|
||||
private final boolean useValidationNotNull;
|
||||
private final ReadValidationAnnotations javaxValidation;
|
||||
private final ReadValidationAnnotations jakartaValidation;
|
||||
private final Set<Class<?>> metaAnnotations = new HashSet<>();
|
||||
|
||||
ReadAnnotationConfig(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, DatabaseConfig config) {
|
||||
@@ -33,20 +38,32 @@ class ReadAnnotationConfig {
|
||||
this.disableL2Cache = config.isDisableL2Cache();
|
||||
this.eagerFetchLobs = config.isEagerFetchLobs();
|
||||
this.idGeneratorAutomatic = config.isIdGeneratorAutomatic();
|
||||
this.javaxValidationAnnotations = generatedPropFactory.getClassLoadConfig().isJavaxValidationAnnotationsPresent();
|
||||
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;
|
||||
}
|
||||
@@ -71,10 +88,6 @@ class ReadAnnotationConfig {
|
||||
return idGeneratorAutomatic;
|
||||
}
|
||||
|
||||
boolean isJavaxValidationAnnotations() {
|
||||
return javaxValidationAnnotations;
|
||||
}
|
||||
|
||||
boolean isJacksonAnnotations() {
|
||||
return jacksonAnnotations;
|
||||
}
|
||||
@@ -82,4 +95,35 @@ class ReadAnnotationConfig {
|
||||
public Set<Class<?>> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,12 +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()) {
|
||||
InitMetaValidationAnnotation.init(readConfig);
|
||||
}
|
||||
if (readConfig.isJacksonAnnotations()) {
|
||||
InitMetaJacksonAnnotation.init(readConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+19
@@ -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);
|
||||
}
|
||||
+51
@@ -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<Size> getMetaAnnotationJavaxSize(DeployBeanProperty prop) {
|
||||
final List<Size> size = prop.getMetaAnnotations(Size.class);
|
||||
final List<Size.List> lists = prop.getMetaAnnotations(Size.List.class);
|
||||
for (Size.List list : lists) {
|
||||
Collections.addAll(size, list.value());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
}
|
||||
+52
@@ -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<Size> getMetaAnnotationJavaxSize(DeployBeanProperty prop) {
|
||||
final List<Size> size = prop.getMetaAnnotations(Size.class);
|
||||
final List<Size.List> lists = prop.getMetaAnnotations(Size.List.class);
|
||||
for (Size.List list : lists) {
|
||||
Collections.addAll(size, list.value());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -142,7 +142,7 @@ public class CQuery<T> 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<T> 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<T> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -442,9 +442,10 @@ class CQueryBuilder {
|
||||
int cols = 1 + metaData.getColumnCount();
|
||||
List<String> 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 {
|
||||
|
||||
@@ -11,6 +11,8 @@ import io.ebeaninternal.server.querydefn.SpiFetchGroup;
|
||||
*/
|
||||
class DFetchGroupBuilder<T> implements FetchGroupBuilder<T> {
|
||||
|
||||
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<T> implements FetchGroupBuilder<T> {
|
||||
|
||||
@Override
|
||||
public FetchGroupBuilder<T> fetch(String path) {
|
||||
detail.fetch(path, null, null);
|
||||
detail.fetchProperties(path, null, DEFAULT_FETCH);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchGroupBuilder<T> fetch(String path, FetchGroup nestedGroup) {
|
||||
return fetchNested(path, nestedGroup, null);
|
||||
return fetchNested(path, nestedGroup, DEFAULT_FETCH);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,7 +53,6 @@ class DFetchGroupBuilder<T> implements FetchGroupBuilder<T> {
|
||||
}
|
||||
|
||||
private FetchGroupBuilder<T> 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<T> implements FetchGroupBuilder<T> {
|
||||
|
||||
@Override
|
||||
public FetchGroupBuilder<T> fetchQuery(String path) {
|
||||
detail.fetch(path, null, FETCH_QUERY);
|
||||
detail.fetchProperties(path, null, FETCH_QUERY);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchGroupBuilder<T> fetchCache(String path) {
|
||||
detail.fetch(path, null, FETCH_CACHE);
|
||||
detail.fetchProperties(path, null, FETCH_CACHE);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchGroupBuilder<T> fetchLazy(String path) {
|
||||
detail.fetch(path, null, FETCH_LAZY);
|
||||
detail.fetchProperties(path, null, FETCH_LAZY);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchGroupBuilder<T> fetch(String path, String properties) {
|
||||
detail.fetch(path, properties, null);
|
||||
detail.fetch(path, properties, DEFAULT_FETCH);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<T> implements SpiFetchGroupQuery<T> {
|
||||
class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T>, SpiQueryFetch {
|
||||
|
||||
private static final FetchConfig FETCH_CACHE = FetchConfig.ofCache();
|
||||
|
||||
@@ -628,4 +629,14 @@ class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T> {
|
||||
public Query<T> orderById(boolean orderById) {
|
||||
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectProperties(Set<String> props) {
|
||||
detail.selectProperties(props);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetchProperties(String property, Set<String> columns, FetchConfig config) {
|
||||
detail.fetchProperties(property, columns, config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,8 +122,8 @@ class SqlTree {
|
||||
return inheritanceWhereSql;
|
||||
}
|
||||
|
||||
SqlTreeNode getRootNode() {
|
||||
return rootNode;
|
||||
SqlTreeRoot getRootNode() {
|
||||
return (SqlTreeRoot)rootNode;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
<T> Version<T> loadVersion(DbReadContext ctx) throws SQLException;
|
||||
|
||||
/**
|
||||
* Return true if the query has a many join.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -88,7 +87,6 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
*/
|
||||
SqlTreeNodeBean(String prefix, STreePropertyAssoc beanProp, SqlTreeProperties props,
|
||||
List<SqlTreeNode> myChildren, boolean withId, SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad) {
|
||||
|
||||
this(prefix, beanProp, beanProp.target(), props, myChildren, withId, null, temporalMode, disableLazyLoad);
|
||||
}
|
||||
|
||||
@@ -120,7 +118,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]);
|
||||
@@ -192,26 +191,12 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the version bean.
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Version<T> 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.
|
||||
*/
|
||||
private class LoadInherit extends Load {
|
||||
|
||||
LoadInherit(DbReadContext ctx, EntityBean parentBean) {
|
||||
private LoadInherit(DbReadContext ctx, EntityBean parentBean) {
|
||||
super(ctx, parentBean);
|
||||
}
|
||||
|
||||
@@ -232,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);
|
||||
}
|
||||
}
|
||||
@@ -244,7 +227,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
/**
|
||||
* Load a bean instance.
|
||||
*/
|
||||
private class Load {
|
||||
class Load {
|
||||
|
||||
final DbReadContext ctx;
|
||||
final EntityBean parentBean;
|
||||
@@ -262,12 +245,12 @@ 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;
|
||||
}
|
||||
|
||||
void initLazyParent() throws SQLException {
|
||||
private void initLazyParent() throws SQLException {
|
||||
if (lazyLoadParentIdBinder != null) {
|
||||
lazyLoadParentId = lazyLoadParentIdBinder.read(ctx);
|
||||
}
|
||||
@@ -279,12 +262,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) {
|
||||
@@ -325,7 +308,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
}
|
||||
}
|
||||
|
||||
void initSqlLoadBean() {
|
||||
private void initSqlLoadBean() {
|
||||
ctx.setCurrentPrefix(prefix, pathMap);
|
||||
ctx.propagateState(localBean);
|
||||
sqlBeanLoad = new SqlBeanLoad(ctx, localType, localBean, queryMode);
|
||||
@@ -337,33 +320,29 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
}
|
||||
}
|
||||
|
||||
void loadChildren() throws SQLException {
|
||||
//boolean lazyLoadMany = false;
|
||||
private void loadChildren() throws SQLException {
|
||||
if (localBean == null && queryMode == Mode.LAZYLOAD_MANY) {
|
||||
// batch lazy load many into existing contextBean
|
||||
localBean = contextBean;
|
||||
lazyLoadMany = true;
|
||||
}
|
||||
// recursively continue reading...
|
||||
for (SqlTreeNode aChildren : children) {
|
||||
// read each child... and let them set their
|
||||
// values back to this localBean
|
||||
aChildren.load(ctx, localBean, contextBean);
|
||||
for (SqlTreeNode child : children) {
|
||||
child.load(ctx, localBean, contextBean);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -384,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
|
||||
@@ -408,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)
|
||||
@@ -425,21 +402,20 @@ 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);
|
||||
}
|
||||
return localBean;
|
||||
|
||||
} else {
|
||||
if (lazyLoadParentId != null) {
|
||||
ctx.setLazyLoadedChildBean(contextBean, lazyLoadParentId);
|
||||
@@ -448,7 +424,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
}
|
||||
}
|
||||
|
||||
void initialise() throws SQLException {
|
||||
private void initialise() throws SQLException {
|
||||
initLazyParent();
|
||||
initBeanType();
|
||||
initPersistenceContext();
|
||||
@@ -457,6 +433,27 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
loadProperties();
|
||||
loadChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the load returning the loaded bean.
|
||||
*/
|
||||
EntityBean perform() throws SQLException {
|
||||
initialise();
|
||||
if (isLazyLoadManyRoot()) {
|
||||
return getContextBean();
|
||||
}
|
||||
postLoad();
|
||||
setBeanToParent();
|
||||
return complete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this bean was already in the context. If already in the
|
||||
* context we need to check if it is already contained in the collection.
|
||||
*/
|
||||
boolean isContextBean() {
|
||||
return localBean == null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -464,14 +461,14 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
*/
|
||||
@Override
|
||||
public EntityBean load(DbReadContext ctx, EntityBean parentBean, EntityBean contextParent) throws SQLException {
|
||||
Load load = (inheritInfo != null) ? new LoadInherit(ctx, parentBean) : new Load(ctx, parentBean);
|
||||
load.initialise();
|
||||
if (load.isLazyLoadManyRoot()) {
|
||||
return load.getContextBean();
|
||||
}
|
||||
load.postLoad();
|
||||
load.setBeanToParent();
|
||||
return load.complete();
|
||||
return createLoad(ctx, parentBean).perform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the loader with or without inheritance.
|
||||
*/
|
||||
Load createLoad(DbReadContext ctx, EntityBean parentBean) {
|
||||
return (inheritInfo != null) ? new LoadInherit(ctx, parentBean) : new Load(ctx, parentBean);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -489,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();
|
||||
@@ -527,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();
|
||||
@@ -579,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -618,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();
|
||||
}
|
||||
@@ -646,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -178,14 +178,6 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing.
|
||||
*/
|
||||
@Override
|
||||
public <T> Version<T> loadVersion(DbReadContext ctx) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMany() {
|
||||
return manyJoin;
|
||||
|
||||
-6
@@ -99,12 +99,6 @@ class SqlTreeNodeFormulaWhereJoin implements SqlTreeNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Version<T> loadVersion(DbReadContext ctx) {
|
||||
// nothing to do here
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMany() {
|
||||
return true;
|
||||
|
||||
@@ -18,6 +18,25 @@ final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
|
||||
this.manyProp = prop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMany() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityBean load(DbReadContext cquery, EntityBean parentBean, EntityBean contextParent) throws SQLException {
|
||||
// pass in null for parentBean because added to a collection rather than set to the parentBean
|
||||
SqlTreeNodeBean.Load load = createLoad(cquery, null);
|
||||
EntityBean detailBean = load.perform();
|
||||
if (contextParent != null) {
|
||||
// Add to the collection and initialise collection if needed
|
||||
// A null detailBean may initialise an empty collection
|
||||
// Check for bean existing in collection based on load.isContextBean()
|
||||
manyProp.addBeanToCollectionWithCreate(contextParent, detailBean, load.isContextBean());
|
||||
}
|
||||
return detailBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the property columns to the buffer.
|
||||
*/
|
||||
@@ -28,20 +47,6 @@ final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
|
||||
ctx.popTableAlias();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityBean load(DbReadContext cquery, EntityBean parentBean, EntityBean contextParent) throws SQLException {
|
||||
// pass in null for parentBean because the localBean
|
||||
// that is built is added to a collection rather than
|
||||
// being set to the parentBean directly
|
||||
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);
|
||||
}
|
||||
return detailBean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* append extraWhere to the join.
|
||||
*/
|
||||
@@ -64,9 +69,4 @@ final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
|
||||
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
super.appendFrom(ctx, joinType.autoToOuter());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMany() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,12 +144,6 @@ class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Version<T> loadVersion(DbReadContext ctx) {
|
||||
// nothing to do here
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMany() {
|
||||
return true;
|
||||
|
||||
@@ -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 <T> Version<T> 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;
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* At a high level this actually controls the reading of the data from the
|
||||
* jdbc resultSet and putting it into the bean etc.
|
||||
* </p>
|
||||
*/
|
||||
EntityBean load(DbReadContext ctx) throws SQLException;
|
||||
|
||||
/**
|
||||
* Load a version of a @History bean with effective dates.
|
||||
*/
|
||||
<T> Version<T> loadVersion(DbReadContext ctx) throws SQLException;
|
||||
|
||||
/**
|
||||
* Return a Scalar single attribute reader based on the first property.
|
||||
*/
|
||||
ScalarDataReader<?> getSingleAttributeReader();
|
||||
|
||||
}
|
||||
@@ -391,7 +391,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setUseDocStore(boolean useDocStore) {
|
||||
public Query<T> setUseDocStore(boolean useDocStore) {
|
||||
this.useDocStore = useDocStore;
|
||||
return this;
|
||||
}
|
||||
@@ -431,7 +431,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setAllowLoadErrors() {
|
||||
public Query<T> setAllowLoadErrors() {
|
||||
this.allowLoadErrors = true;
|
||||
return this;
|
||||
}
|
||||
@@ -452,21 +452,21 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> asOf(Timestamp asOfDateTime) {
|
||||
public Query<T> asOf(Timestamp asOfDateTime) {
|
||||
this.temporalMode = (asOfDateTime != null) ? TemporalMode.AS_OF : TemporalMode.CURRENT;
|
||||
this.asOf = asOfDateTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> asDraft() {
|
||||
public Query<T> asDraft() {
|
||||
this.temporalMode = TemporalMode.DRAFT;
|
||||
this.useBeanCache = CacheMode.OFF;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setIncludeSoftDeletes() {
|
||||
public Query<T> setIncludeSoftDeletes() {
|
||||
this.temporalMode = TemporalMode.SOFT_DELETED;
|
||||
return this;
|
||||
}
|
||||
@@ -489,7 +489,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setRawSql(RawSql rawSql) {
|
||||
public Query<T> setRawSql(RawSql rawSql) {
|
||||
this.rawSql = (SpiRawSql) rawSql;
|
||||
return this;
|
||||
}
|
||||
@@ -802,12 +802,12 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> copy() {
|
||||
public SpiQuery<T> copy() {
|
||||
return copy(server);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> copy(SpiEbeanServer server) {
|
||||
public SpiQuery<T> copy(SpiEbeanServer server) {
|
||||
DefaultOrmQuery<T> copy = new DefaultOrmQuery<>(beanDescriptor, server, expressionFactory);
|
||||
copy.transaction = transaction;
|
||||
copy.m2mIncludeJoin = m2mIncludeJoin;
|
||||
@@ -955,7 +955,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setAutoTune(boolean autoTune) {
|
||||
public Query<T> setAutoTune(boolean autoTune) {
|
||||
this.autoTune = autoTune;
|
||||
return this;
|
||||
}
|
||||
@@ -971,21 +971,21 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> forUpdate() {
|
||||
public Query<T> forUpdate() {
|
||||
return setForUpdateWithMode(LockWait.WAIT, LockType.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> forUpdateNoWait() {
|
||||
public Query<T> forUpdateNoWait() {
|
||||
return setForUpdateWithMode(LockWait.NOWAIT, LockType.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> forUpdateSkipLocked() {
|
||||
public Query<T> forUpdateSkipLocked() {
|
||||
return setForUpdateWithMode(LockWait.SKIPLOCKED, LockType.DEFAULT);
|
||||
}
|
||||
|
||||
private DefaultOrmQuery<T> setForUpdateWithMode(LockWait mode, LockType lockType) {
|
||||
private Query<T> setForUpdateWithMode(LockWait mode, LockType lockType) {
|
||||
this.forUpdate = mode;
|
||||
this.lockType = lockType;
|
||||
this.useBeanCache = CacheMode.OFF;
|
||||
@@ -1329,7 +1329,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setReadOnly(boolean readOnly) {
|
||||
public Query<T> setReadOnly(boolean readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
return this;
|
||||
}
|
||||
@@ -1375,82 +1375,110 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setUseQueryCache(CacheMode useQueryCache) {
|
||||
public Query<T> setUseQueryCache(CacheMode useQueryCache) {
|
||||
this.useQueryCache = useQueryCache;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setLoadBeanCache(boolean loadBeanCache) {
|
||||
public Query<T> setLoadBeanCache(boolean loadBeanCache) {
|
||||
this.useBeanCache = CacheMode.PUT;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setTimeout(int secs) {
|
||||
public Query<T> setTimeout(int secs) {
|
||||
this.timeout = secs;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> select(String columns) {
|
||||
public void selectProperties(Set<String> props) {
|
||||
detail.selectProperties(props);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetchProperties(String property, Set<String> 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 Query<T> select(String columns) {
|
||||
detail.select(columns);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> select(FetchGroup<T> fetchGroup) {
|
||||
public Query<T> select(FetchGroup<T> fetchGroup) {
|
||||
this.detail = ((SpiFetchGroup<T>) fetchGroup).detail();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> fetch(String property) {
|
||||
return fetch(property, null, null);
|
||||
public Query<T> fetch(String path) {
|
||||
return fetch(path, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> fetchQuery(String property) {
|
||||
return fetch(property, null, FETCH_QUERY);
|
||||
}
|
||||
|
||||
public Query<T> fetchCache(String property) {
|
||||
return fetch(property, null, FETCH_CACHE);
|
||||
public Query<T> fetch(String path, FetchConfig joinConfig) {
|
||||
return fetch(path, null, joinConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> fetchLazy(String property) {
|
||||
return fetch(property, null, FETCH_LAZY);
|
||||
public Query<T> fetch(String path, String properties) {
|
||||
return fetch(path, properties, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> fetch(String property, FetchConfig joinConfig) {
|
||||
return fetch(property, null, joinConfig);
|
||||
public Query<T> 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> fetch(String property, String columns) {
|
||||
return fetch(property, columns, null);
|
||||
public Query<T> fetchQuery(String path) {
|
||||
return fetchInternal(path, null, FETCH_QUERY);
|
||||
}
|
||||
|
||||
public Query<T> fetchCache(String path) {
|
||||
return fetchInternal(path, null, FETCH_CACHE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> fetchQuery(String property, String columns) {
|
||||
return fetch(property, columns, FETCH_QUERY);
|
||||
public Query<T> fetchLazy(String path) {
|
||||
return fetchInternal(path, null, FETCH_LAZY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> fetchCache(String property, String columns) {
|
||||
return fetch(property, columns, FETCH_CACHE);
|
||||
public Query<T> fetchQuery(String path, String properties) {
|
||||
return fetchInternal(path, properties, FETCH_QUERY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> fetchLazy(String property, String columns) {
|
||||
return fetch(property, columns, FETCH_LAZY);
|
||||
public Query<T> fetchCache(String path, String properties) {
|
||||
return fetchInternal(path, properties, FETCH_CACHE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> fetch(String property, String columns, FetchConfig config) {
|
||||
detail.fetch(property, columns, config);
|
||||
public Query<T> fetchLazy(String path, String properties) {
|
||||
return fetchInternal(path, properties, FETCH_LAZY);
|
||||
}
|
||||
|
||||
private Query<T> fetchInternal(String path, String properties, FetchConfig config) {
|
||||
detail.fetch(path, properties, config);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1613,7 +1641,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setParameter(Object value) {
|
||||
public Query<T> setParameter(Object value) {
|
||||
if (bindParams == null) {
|
||||
bindParams = new BindParams();
|
||||
}
|
||||
@@ -1622,7 +1650,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setParameters(Object... values) {
|
||||
public Query<T> setParameters(Object... values) {
|
||||
if (bindParams == null) {
|
||||
bindParams = new BindParams();
|
||||
}
|
||||
@@ -1636,7 +1664,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
* have in the query.
|
||||
*/
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setParameter(int position, Object value) {
|
||||
public Query<T> setParameter(int position, Object value) {
|
||||
if (bindParams == null) {
|
||||
bindParams = new BindParams();
|
||||
}
|
||||
@@ -1648,7 +1676,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
* Set a named bind parameter. Named parameters have a colon to prefix the name.
|
||||
*/
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setParameter(String name, Object value) {
|
||||
public Query<T> setParameter(String name, Object value) {
|
||||
if (namedParams != null) {
|
||||
ONamedParam param = namedParams.get(name);
|
||||
if (param != null) {
|
||||
@@ -1694,12 +1722,12 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public DefaultOrmQuery<T> orderBy(String orderByClause) {
|
||||
public Query<T> orderBy(String orderByClause) {
|
||||
return order(orderByClause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> order(String orderByClause) {
|
||||
public Query<T> order(String orderByClause) {
|
||||
if (orderByClause == null || orderByClause.trim().isEmpty()) {
|
||||
this.orderBy = null;
|
||||
} else {
|
||||
@@ -1710,12 +1738,12 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public DefaultOrmQuery<T> setOrderBy(OrderBy<T> orderBy) {
|
||||
public Query<T> setOrderBy(OrderBy<T> orderBy) {
|
||||
return setOrder(orderBy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setOrder(OrderBy<T> orderBy) {
|
||||
public Query<T> setOrder(OrderBy<T> orderBy) {
|
||||
this.orderBy = orderBy;
|
||||
if (orderBy != null) {
|
||||
orderBy.setQuery(this);
|
||||
@@ -1747,13 +1775,13 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
* Internally set to use SQL DISTINCT on the query but still have id property included.
|
||||
*/
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setDistinct(boolean distinct) {
|
||||
public Query<T> setDistinct(boolean distinct) {
|
||||
this.distinct = distinct;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setCountDistinct(CountDistinctOrder countDistinctOrder) {
|
||||
public Query<T> setCountDistinct(CountDistinctOrder countDistinctOrder) {
|
||||
this.countDistinctOrder = countDistinctOrder;
|
||||
return this;
|
||||
}
|
||||
@@ -1804,7 +1832,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setDisableLazyLoading(boolean disableLazyLoading) {
|
||||
public Query<T> setDisableLazyLoading(boolean disableLazyLoading) {
|
||||
this.disableLazyLoading = disableLazyLoading;
|
||||
return this;
|
||||
}
|
||||
@@ -1820,7 +1848,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setFirstRow(int firstRow) {
|
||||
public Query<T> setFirstRow(int firstRow) {
|
||||
this.firstRow = firstRow;
|
||||
return this;
|
||||
}
|
||||
@@ -1831,7 +1859,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setMaxRows(int maxRows) {
|
||||
public Query<T> setMaxRows(int maxRows) {
|
||||
this.maxRows = maxRows;
|
||||
return this;
|
||||
}
|
||||
@@ -1842,7 +1870,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setMapKey(String mapKey) {
|
||||
public Query<T> setMapKey(String mapKey) {
|
||||
this.mapKey = mapKey;
|
||||
return this;
|
||||
}
|
||||
@@ -1853,7 +1881,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> setId(Object id) {
|
||||
public Query<T> setId(Object id) {
|
||||
if (id == null) {
|
||||
throw new NullPointerException("The id is null");
|
||||
}
|
||||
@@ -1867,7 +1895,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> where(Expression expression) {
|
||||
public Query<T> where(Expression expression) {
|
||||
where().add(expression);
|
||||
return this;
|
||||
}
|
||||
@@ -1897,7 +1925,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> having(Expression expression) {
|
||||
public Query<T> having(Expression expression) {
|
||||
having().add(expression);
|
||||
return this;
|
||||
}
|
||||
@@ -2023,7 +2051,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> alias(String alias) {
|
||||
public Query<T> alias(String alias) {
|
||||
this.rootTableAlias = alias;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -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<String, OrmQueryProperties> entry : other.fetchPaths.entrySet()) {
|
||||
fetch(path + "." + entry.getKey(), entry.getValue().getProperties(), entry.getValue().getFetchConfig());
|
||||
fetchProperties(path + "." + entry.getKey(), entry.getValue(), entry.getValue().getFetchConfig());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ public class OrmQueryDetail implements Serializable {
|
||||
*/
|
||||
public String asStringDebug() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (!baseProps.isEmpty()) {
|
||||
if (baseProps.hasProperties()) {
|
||||
baseProps.asStringDebug("select ", sb);
|
||||
}
|
||||
if (fetchPaths != null) {
|
||||
@@ -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<String> 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<String> 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.
|
||||
*/
|
||||
|
||||
+34
-32
@@ -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<String> 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<String> included) {
|
||||
this(path, included, DEFAULT_FETCH);
|
||||
}
|
||||
|
||||
OrmQueryProperties(String path, Set<String> 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 <T> SpiExpressionList<T> filterMany(Query<T> 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 (hasProperties()) {
|
||||
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,19 +242,14 @@ public class OrmQueryProperties implements Serializable {
|
||||
}
|
||||
|
||||
public boolean hasSelectClause() {
|
||||
if ("*".equals(properties)) {
|
||||
// explicitly selected all properties
|
||||
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.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return properties == null || properties.isEmpty();
|
||||
boolean hasProperties() {
|
||||
return allProperties || included != null;
|
||||
}
|
||||
|
||||
public void asStringDebug(String prefix, StringBuilder sb) {
|
||||
@@ -250,8 +257,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 +278,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 +329,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 +405,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) {
|
||||
|
||||
+12
-41
@@ -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<String> included;
|
||||
|
||||
private Response(String properties, Set<String> included) {
|
||||
this.properties = properties;
|
||||
private Response(boolean allProperties, Set<String> 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<String> 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<String> splitRawSelect(String inputProperties) {
|
||||
private static Set<String> splitRawSelect(String inputProperties) {
|
||||
return DSelectColumnsParser.parse(inputProperties);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,14 @@ import java.util.UUID;
|
||||
interface ArrayElementConverter<T> {
|
||||
|
||||
/**
|
||||
* Convert the array element to the logical type.
|
||||
* Convert element it's json serialized form.
|
||||
*/
|
||||
T toElement(Object rawValue);
|
||||
T fromSerialized(Object rawValue);
|
||||
|
||||
/**
|
||||
* Convert the array element from it's DB array form.
|
||||
*/
|
||||
T fromDbArray(Object rawValue);
|
||||
|
||||
default Object[] toDbArray(Object[] objects) {
|
||||
return objects;
|
||||
@@ -46,7 +51,12 @@ interface ArrayElementConverter<T> {
|
||||
class LongConverter implements ArrayElementConverter<Long> {
|
||||
|
||||
@Override
|
||||
public Long toElement(Object rawValue) {
|
||||
public Long fromSerialized(Object rawValue) {
|
||||
return fromDbArray(rawValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long fromDbArray(Object rawValue) {
|
||||
if (rawValue instanceof Long) {
|
||||
return (Long) rawValue;
|
||||
} else {
|
||||
@@ -58,7 +68,12 @@ interface ArrayElementConverter<T> {
|
||||
class IntegerConverter implements ArrayElementConverter<Integer> {
|
||||
|
||||
@Override
|
||||
public Integer toElement(Object rawValue) {
|
||||
public Integer fromSerialized(Object rawValue) {
|
||||
return ((Number) rawValue).intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer fromDbArray(Object rawValue) {
|
||||
if (rawValue instanceof Integer) {
|
||||
return (Integer) rawValue;
|
||||
} else {
|
||||
@@ -70,7 +85,12 @@ interface ArrayElementConverter<T> {
|
||||
class DoubleConverter implements ArrayElementConverter<Double> {
|
||||
|
||||
@Override
|
||||
public Double toElement(Object rawValue) {
|
||||
public Double fromSerialized(Object rawValue) {
|
||||
return fromDbArray(rawValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double fromDbArray(Object rawValue) {
|
||||
if (rawValue instanceof Double) {
|
||||
return (Double) rawValue;
|
||||
} else {
|
||||
@@ -82,21 +102,34 @@ interface ArrayElementConverter<T> {
|
||||
/**
|
||||
* String converter (noop based).
|
||||
*/
|
||||
class StringConverter extends NoopConverter<String> {
|
||||
class StringConverter implements ArrayElementConverter<String> {
|
||||
|
||||
public String fromDbArray(Object rawValue) {
|
||||
return (String) rawValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromSerialized(Object rawValue) {
|
||||
return (String) rawValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UUID converter (noop based).
|
||||
* UUID converter.
|
||||
*/
|
||||
class UuidConverter extends NoopConverter<UUID> {
|
||||
}
|
||||
class UuidConverter implements ArrayElementConverter<UUID> {
|
||||
|
||||
class NoopConverter<T> implements ArrayElementConverter<T> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T toElement(Object rawValue) {
|
||||
return (T) rawValue;
|
||||
public java.util.UUID fromSerialized(Object rawValue) {
|
||||
return java.util.UUID.fromString((String)rawValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.util.UUID fromDbArray(Object rawValue) {
|
||||
if (rawValue instanceof UUID) {
|
||||
return (java.util.UUID) rawValue;
|
||||
}
|
||||
return java.util.UUID.fromString(rawValue.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +146,12 @@ interface ArrayElementConverter<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object toElement(Object rawValue) {
|
||||
public Object fromSerialized(Object rawValue) {
|
||||
return scalarType.parse((String) rawValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fromDbArray(Object rawValue) {
|
||||
return scalarType.toBeanType(rawValue);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.core.type.ScalarType;
|
||||
|
||||
public class ArrayElementConverterEnum implements ArrayElementConverter<String> {
|
||||
|
||||
final ScalarType<?> scalarType;
|
||||
|
||||
final Class<? extends Enum<?>> valueType1;
|
||||
|
||||
public ArrayElementConverterEnum(ScalarType<?> scalarType, Class<? extends Enum<?>> valueType1) {
|
||||
this.scalarType = scalarType;
|
||||
this.valueType1 = valueType1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toElement(Object rawValue) {
|
||||
// FIXME: Don't understand for what is this.
|
||||
Enum<?>[] enumConstants = valueType1.getEnumConstants();
|
||||
if (scalarType == null) {
|
||||
return rawValue.toString();
|
||||
}
|
||||
return scalarType.format(rawValue);
|
||||
}
|
||||
}
|
||||
@@ -105,7 +105,7 @@ public class ScalarTypeArrayList extends ScalarTypeArrayBase<List> implements Sc
|
||||
if (element == null) {
|
||||
list.add(null);
|
||||
} else {
|
||||
list.add(converter.toElement(element));
|
||||
list.add(converter.fromDbArray(element));
|
||||
}
|
||||
}
|
||||
return new ModifyAwareList(list);
|
||||
@@ -145,15 +145,27 @@ public class ScalarTypeArrayList extends ScalarTypeArrayBase<List> implements Sc
|
||||
@Override
|
||||
public List parse(String value) {
|
||||
try {
|
||||
return EJson.parseList(value, false);
|
||||
return convert(EJson.parseList(value, false));
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as List", value, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from the json types to the proper scalar types (uuid, enum, double etc)
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
private List convert(List<Object> rawList) {
|
||||
List list = new ArrayList(rawList.size());
|
||||
for (Object rawVal : rawList) {
|
||||
list.add(converter.fromSerialized(rawVal));
|
||||
}
|
||||
return new ModifyAwareList(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List jsonRead(JsonParser parser) throws IOException {
|
||||
return EJson.parseList(parser, parser.getCurrentToken());
|
||||
return convert(EJson.parseList(parser, parser.getCurrentToken()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,6 +17,7 @@ import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@@ -101,7 +102,7 @@ public class ScalarTypeArraySet extends ScalarTypeArrayBase<Set> implements Scal
|
||||
protected Set fromArray(Object[] array1) {
|
||||
Set set = new LinkedHashSet();
|
||||
for (Object element : array1) {
|
||||
set.add(converter.toElement(element));
|
||||
set.add(converter.fromDbArray(element));
|
||||
}
|
||||
return new ModifyAwareSet(set);
|
||||
}
|
||||
@@ -140,15 +141,26 @@ public class ScalarTypeArraySet extends ScalarTypeArrayBase<Set> implements Scal
|
||||
@Override
|
||||
public Set parse(String value) {
|
||||
try {
|
||||
return EJson.parseSet(value, false);
|
||||
return convert(EJson.parseList(value, false));
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as Set", value, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from the json types to the proper scalar types (uuid, enum, double etc)
|
||||
*/
|
||||
private Set convert(List<Object> rawList) {
|
||||
Set asSet = new LinkedHashSet();
|
||||
for (Object rawVal : rawList) {
|
||||
asSet.add(converter.fromSerialized(rawVal));
|
||||
}
|
||||
return new ModifyAwareSet(asSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set jsonRead(JsonParser parser) throws IOException {
|
||||
return EJson.parseSet(parser, parser.getCurrentToken());
|
||||
return convert(EJson.parseList(parser, parser.getCurrentToken()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+56
-57
@@ -18,8 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class OrmQueryPlanKeyTest extends BaseExpressionTest {
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private DefaultOrmQuery<Customer> 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<Customer> list_id_eq_42() {
|
||||
return (DefaultOrmQuery<Customer>) 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<Customer> q1 = query().select("name");
|
||||
DefaultOrmQuery<Customer> q1 = (DefaultOrmQuery<Customer>)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<Customer> 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) {
|
||||
|
||||
+9
-3
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
+3
-8
@@ -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<String>) 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebean.text.json.EJson;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -63,4 +68,82 @@ public class ScalarTypeArrayListTest extends BasePlatformArrayTypeFactoryTest {
|
||||
assertBindNullTo_Null(factory.typeFor(UUID.class, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void parse_withUuidType_expect_uuidTypeReturned() {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(UUID.class, true);
|
||||
|
||||
List<UUID> input = new ArrayList<>();
|
||||
input.add(UUID.randomUUID());
|
||||
input.add(UUID.randomUUID());
|
||||
|
||||
String formatToJson = scalarType.format(input);
|
||||
|
||||
Object parsed = scalarType.parse(formatToJson);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void parse_withIntegerType_expect_longIntegerReturned() {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(Integer.class, true);
|
||||
|
||||
List<Integer> input = new ArrayList<>();
|
||||
input.add(2);
|
||||
input.add(4);
|
||||
|
||||
String formatToJson = scalarType.format(input);
|
||||
|
||||
Object parsed = scalarType.parse(formatToJson);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void parse_withLongType_expect_longTypeReturned() {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(Long.class, true);
|
||||
|
||||
List<Long> input = new ArrayList<>();
|
||||
input.add(2L);
|
||||
input.add(4L);
|
||||
|
||||
String formatToJson = scalarType.format(input);
|
||||
|
||||
Object parsed = scalarType.parse(formatToJson);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_withDoubleType_expect_longTypeReturned() {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(Double.class, true);
|
||||
|
||||
List<Double> input = new ArrayList<>();
|
||||
input.add(2D);
|
||||
input.add(4D);
|
||||
|
||||
String formatToJson = scalarType.format(input);
|
||||
|
||||
Object parsed = scalarType.parse(formatToJson);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jsonRead_withUuidType() throws IOException {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(UUID.class, true);
|
||||
|
||||
List<UUID> input = new ArrayList<>();
|
||||
input.add(UUID.randomUUID());
|
||||
input.add(UUID.randomUUID());
|
||||
String asJson = EJson.write(input);
|
||||
|
||||
JsonParser parser = DB.json().createParser(new StringReader(asJson));
|
||||
|
||||
Object parsed = scalarType.jsonRead(parser);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebean.text.json.EJson;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.sql.SQLException;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -49,4 +57,82 @@ public class ScalarTypeArraySetH2Test extends BasePlatformArrayTypeFactoryTest {
|
||||
assertBindNullTo_Null(factory.typeFor(UUID.class, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void parse_withUuidType_expect_uuidTypeReturned() {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(UUID.class, true);
|
||||
|
||||
Set<UUID> input = new LinkedHashSet<>();
|
||||
input.add(UUID.randomUUID());
|
||||
input.add(UUID.randomUUID());
|
||||
|
||||
String formatToJson = scalarType.format(input);
|
||||
|
||||
Set<UUID> parsed = (Set<UUID>)scalarType.parse(formatToJson);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void parse_withIntegerType_expect_longIntegerReturned() {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(Integer.class, true);
|
||||
|
||||
Set<Integer> input = new LinkedHashSet<>();
|
||||
input.add(2);
|
||||
input.add(4);
|
||||
|
||||
String formatToJson = scalarType.format(input);
|
||||
|
||||
Set<Integer> parsed = (Set<Integer>)scalarType.parse(formatToJson);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void parse_withLongType_expect_longTypeReturned() {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(Long.class, true);
|
||||
|
||||
Set<Long> input = new LinkedHashSet<>();
|
||||
input.add(2L);
|
||||
input.add(4L);
|
||||
|
||||
String formatToJson = scalarType.format(input);
|
||||
|
||||
Set<Long> parsed = (Set<Long>)scalarType.parse(formatToJson);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse_withDoubleType_expect_longTypeReturned() {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(Double.class, true);
|
||||
|
||||
Set<Double> input = new LinkedHashSet<>();
|
||||
input.add(2D);
|
||||
input.add(4D);
|
||||
|
||||
String formatToJson = scalarType.format(input);
|
||||
|
||||
Object parsed = scalarType.parse(formatToJson);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jsonRead_withUuidType() throws IOException {
|
||||
|
||||
ScalarType<?> scalarType = factory.typeFor(UUID.class, true);
|
||||
|
||||
Set<UUID> input = new LinkedHashSet<>();
|
||||
input.add(UUID.randomUUID());
|
||||
input.add(UUID.randomUUID());
|
||||
String asJson = EJson.write(input);
|
||||
|
||||
JsonParser parser = DB.json().createParser(new StringReader(asJson));
|
||||
|
||||
Object parsed = scalarType.jsonRead(parser);
|
||||
assertThat(parsed).isEqualTo(input);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<TestJakartaAnnotationBaseEntity> descriptor = spiEbeanServer().getBeanDescriptor(TestJakartaAnnotationBaseEntity.class);
|
||||
BeanProperty bp = descriptor.findProperty("constraintAnnotation");
|
||||
assertEquals(40, bp.getDbLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void annotationClassIndexes() throws SecurityException {
|
||||
BeanDescriptor<TestAnnotationBaseEntity> descriptor = spiEbeanServer().getBeanDescriptor(TestAnnotationBaseEntity.class);
|
||||
@@ -173,6 +243,20 @@ public class TestAnnotationBase extends BaseTestCase {
|
||||
assertTrue(bp.isNullable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJakartaNotNullWithGroup() throws SecurityException {
|
||||
BeanDescriptor<TestJakartaAnnotationBaseEntity> 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 {
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.tests.model.array;
|
||||
|
||||
|
||||
import io.ebean.annotation.Cache;
|
||||
import io.ebean.annotation.DbArray;
|
||||
import io.ebean.annotation.NotNull;
|
||||
|
||||
@@ -13,6 +14,7 @@ import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Cache
|
||||
public class EArrayBean {
|
||||
|
||||
enum Status {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.tests.model.array;
|
||||
|
||||
|
||||
import io.ebean.annotation.Cache;
|
||||
import io.ebean.annotation.DbArray;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
@@ -11,6 +12,7 @@ import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Cache
|
||||
public class EArraySetBean {
|
||||
|
||||
@Id
|
||||
@@ -30,6 +32,9 @@ public class EArraySetBean {
|
||||
@DbArray
|
||||
Set<Double> doubs;
|
||||
|
||||
@DbArray
|
||||
Set<EArrayBean.Status> status;
|
||||
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
@@ -81,6 +86,14 @@ public class EArraySetBean {
|
||||
this.doubs = doubs;
|
||||
}
|
||||
|
||||
public Set<EArrayBean.Status> getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Set<EArrayBean.Status> status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,11 @@ public class TestDbArray_asSet extends BaseTestCase {
|
||||
bean.getOtherIds().add(97L);
|
||||
bean.setDoubs(doubles);
|
||||
|
||||
Set<EArrayBean.Status> status = new LinkedHashSet<>();
|
||||
status.add(EArrayBean.Status.ONE);
|
||||
status.add(EArrayBean.Status.TWO);
|
||||
bean.setStatus(status);
|
||||
|
||||
Ebean.save(bean);
|
||||
|
||||
found = Ebean.find(EArraySetBean.class, bean.getId());
|
||||
@@ -154,4 +159,31 @@ public class TestDbArray_asSet extends BaseTestCase {
|
||||
Ebean.saveAll(all);
|
||||
Ebean.deleteAll(all);
|
||||
}
|
||||
|
||||
@Test
|
||||
@IgnorePlatform(Platform.HANA)
|
||||
public void hitCache() {
|
||||
|
||||
Set<UUID> uids = new LinkedHashSet<>();
|
||||
uids.add(UUID.randomUUID());
|
||||
uids.add(UUID.randomUUID());
|
||||
|
||||
Set<EArrayBean.Status> statuses = new LinkedHashSet<>();
|
||||
statuses.add(EArrayBean.Status.ONE);
|
||||
statuses.add(EArrayBean.Status.THREE);
|
||||
|
||||
EArraySetBean bean = new EArraySetBean();
|
||||
bean.setName("hitCache");
|
||||
bean.setUids(uids);
|
||||
bean.setStatus(statuses);
|
||||
|
||||
Ebean.save(bean);
|
||||
// load cache
|
||||
Ebean.find(EArraySetBean.class, bean.getId());
|
||||
// hit cache
|
||||
EArraySetBean found = Ebean.find(EArraySetBean.class, bean.getId());
|
||||
|
||||
assertThat(found.getUids()).isEqualTo(uids);
|
||||
assertThat(found.getStatus()).isEqualTo(statuses);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,4 +249,31 @@ public class TestDbArray_basic extends BaseTestCase {
|
||||
assertThat(found.getIntEnums()).containsExactly(null, IntEnum.ZERO, null, IntEnum.TWO);
|
||||
Ebean.delete(bean);
|
||||
}
|
||||
|
||||
@Test
|
||||
@IgnorePlatform(Platform.HANA)
|
||||
public void hitCache() {
|
||||
|
||||
List<UUID> uids = new ArrayList<>();
|
||||
uids.add(UUID.randomUUID());
|
||||
uids.add(UUID.randomUUID());
|
||||
|
||||
List<EArrayBean.Status> statuses = new ArrayList<>();
|
||||
statuses.add(EArrayBean.Status.ONE);
|
||||
statuses.add(EArrayBean.Status.THREE);
|
||||
|
||||
EArrayBean bean = new EArrayBean();
|
||||
bean.setName("hitCache");
|
||||
bean.setUids(uids);
|
||||
bean.setStatuses(statuses);
|
||||
|
||||
Ebean.save(bean);
|
||||
// load cache
|
||||
Ebean.find(EArrayBean.class, bean.getId());
|
||||
// hit cache
|
||||
EArrayBean found = Ebean.find(EArrayBean.class, bean.getId());
|
||||
|
||||
assertThat(found.getUids()).isEqualTo(uids);
|
||||
assertThat(found.getStatuses()).isEqualTo(statuses);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -246,4 +247,102 @@ public class TestQueryFindNative extends BaseTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void findNativeWithOneFetchQuery() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
String sql = "select * from o_customer";
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
List<Customer> result = DB.findNative(Customer.class, sql)
|
||||
.fetchQuery("contacts")
|
||||
.findList();
|
||||
|
||||
assertThat(result).isNotEmpty();
|
||||
List<String> 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<Customer> result = DB.findNative(Customer.class, sql)
|
||||
.fetch("contacts", "firstName, lastName")
|
||||
.findList();
|
||||
|
||||
assertThat(result).isNotEmpty();
|
||||
List<String> loggedSql = LoggedSqlCollector.stop();
|
||||
|
||||
if (isH2()) {
|
||||
assertThat(loggedSql).hasSize(2);
|
||||
assertThat(loggedSql.get(0)).contains("from o_customer");
|
||||
assertThat(loggedSql.get(1)).contains("select t0.customer_id, t0.id, t0.first_name, t0.last_name from contact t0 where");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findNativeWithMultipleFetchQuery() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
String sql = "select * from o_customer";
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
List<Customer> result = DB.findNative(Customer.class, sql)
|
||||
.fetchQuery("orders")
|
||||
.fetchQuery("contacts")
|
||||
.findList();
|
||||
|
||||
assertThat(result).isNotEmpty();
|
||||
List<String> 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<Customer> result = DB.findNative(Customer.class, sql)
|
||||
// with nativeSql fetch (default) are converted to fetchQuery()
|
||||
.fetch("orders")
|
||||
.fetch("contacts", FetchConfig.ofDefault())
|
||||
.findList();
|
||||
|
||||
assertThat(result).isNotEmpty();
|
||||
List<String> 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"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Integer, Order> 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
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean ddl generation</name>
|
||||
@@ -29,14 +29,14 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core-type</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean external mapping api</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<!-- <parent>-->
|
||||
<!-- <groupId>org.avaje</groupId>-->
|
||||
@@ -33,7 +33,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-externalmapping-api</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -59,14 +59,14 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>ebean postgis</name>
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
<properties>
|
||||
<postgis.jdbc.version>2.2.1</postgis.jdbc.version>
|
||||
<jackson-core.version>2.11.3</jackson-core.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -24,14 +23,14 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson-core.version}</version>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@@ -75,7 +74,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
+11
-14
@@ -4,18 +4,8 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<!-- <parent>-->
|
||||
<!-- <groupId>org.avaje</groupId>-->
|
||||
<!-- <artifactId>java8-oss</artifactId>-->
|
||||
<!-- <version>2.3</version>-->
|
||||
<!-- </parent>-->
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-parent-12.6.5</tag>
|
||||
</scm>
|
||||
|
||||
<name>ebean querybean</name>
|
||||
<description>Ebean querybean support</description>
|
||||
@@ -27,7 +17,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -67,14 +57,21 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>querybean-generator</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -121,12 +121,12 @@ public class PBaseCompareable<R, T> extends PBaseValueEqual<R, T> {
|
||||
* Greater or equal to lower value and strictly less than upper value.
|
||||
* <p>
|
||||
* 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 < }).
|
||||
* </p>
|
||||
*
|
||||
* @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) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package io.ebean.typequery;
|
||||
|
||||
import io.ebean.Query;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
@@ -224,4 +226,25 @@ public abstract class PBaseValueEqual<R, T> extends TQPropertyBase<R> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<T, R> extends TQProperty<R> {
|
||||
|
||||
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<T, R> extends TQProperty<R> {
|
||||
|
||||
/**
|
||||
* Deprecated in favor of fetch().
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public R fetchAll() {
|
||||
return fetch();
|
||||
}
|
||||
@@ -100,8 +109,7 @@ public abstract class TQAssocBean<T, R> extends TQProperty<R> {
|
||||
*/
|
||||
@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<T, R> extends TQProperty<R> {
|
||||
*/
|
||||
@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<T, R> extends TQProperty<R> {
|
||||
*/
|
||||
@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<T, R> extends TQProperty<R> {
|
||||
*/
|
||||
@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<String> properties(TQProperty<?>... props) {
|
||||
Set<String> set = new LinkedHashSet<>();
|
||||
for (TQProperty<?> prop : props) {
|
||||
set.add(prop.propertyName());
|
||||
}
|
||||
return selectProps.toString();
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
@@ -41,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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* Provides common features for all root query beans
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* <h2>Example - QCustomer extends TQRootBean</h2>
|
||||
* <p>
|
||||
* These 'query beans' like QCustomer are generated using the <code>avaje-ebeanorm-typequery-generator</code>.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* public class QCustomer extends TQRootBean<Customer,QCustomer> {
|
||||
*
|
||||
* // properties
|
||||
* public PLong<QCustomer> id;
|
||||
*
|
||||
* public PString<QCustomer> name;
|
||||
* ...
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* <h2>Example - usage of QCustomer</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Date fiveDaysAgo = ...
|
||||
* Date fiveDaysAgo = ...
|
||||
*
|
||||
* List<Customer> 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<Customer> 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();
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* <h2>Resulting SQL where</h2>
|
||||
* <p>
|
||||
* <pre>{@code sql
|
||||
* <pre>{@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)
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T> the entity bean type (normal entity bean type e.g. Customer)
|
||||
@@ -278,22 +264,21 @@ public abstract class TQRootBean<T, R> {
|
||||
*/
|
||||
@SafeVarargs
|
||||
public final R select(TQProperty<R>... 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<String> properties(TQProperty<R>[] properties) {
|
||||
Set<String> props = new LinkedHashSet<>();
|
||||
for (TQProperty<R> property : properties) {
|
||||
props.add(property.propertyName());
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a path to load including all its properties.
|
||||
* <p>
|
||||
* The same as {@link #fetch(String, String)} with the fetchProperties as "*".
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers =
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<OrderDetail> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<Order> 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<String> 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<String> 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<String> 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<String> 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");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
ebean.ddl.generate=true
|
||||
ebean.ddl.run=true
|
||||
ebean.ddl.initSql=init-db.sql
|
||||
|
||||
datasource.default=h2
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
create schema foo;
|
||||
+6
-6
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ebean-redis</artifactId>
|
||||
@@ -22,35 +22,35 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-api</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-querybean</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>querybean-generator</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
+12
-19
@@ -4,27 +4,13 @@
|
||||
<parent>
|
||||
<artifactId>ebean-parent</artifactId>
|
||||
<groupId>io.ebean</groupId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<!-- <parent>-->
|
||||
<!-- <groupId>org.avaje</groupId>-->
|
||||
<!-- <artifactId>java8-oss</artifactId>-->
|
||||
<!-- <version>2.2</version>-->
|
||||
<!-- </parent>-->
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-parent-12.6.5</tag>
|
||||
</scm>
|
||||
|
||||
<name>ebean test</name>
|
||||
<description>Testing support for Ebean</description>
|
||||
<artifactId>ebean-test</artifactId>
|
||||
|
||||
<properties>
|
||||
<jackson-databind.version>2.11.3</jackson-databind.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
@@ -43,27 +29,34 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-core</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-ddl-generator</artifactId>
|
||||
<version>12.7.1-SNAPSHOT</version>
|
||||
<version>12.7.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson-databind.version}</version>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-test-docker</artifactId>
|
||||
<version>4.1</version>
|
||||
<version>4.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*
|
||||
* <h3>Assert contains</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* DbJson.of(order)
|
||||
* .assertContains("/order-partial.json");
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*
|
||||
* <h3>Assert content matches</h3>
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* DbJson.of(timedEntries)
|
||||
* .replace("id", "eventTime")
|
||||
* .assertContentMatches("/assertJson/full-1-timed.json");
|
||||
* DbJson.of(order)
|
||||
* .replace("id", "whenCreated", "whenModified)
|
||||
* .assertContentMatches("/order-full.json");
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* <h3>Assert contains</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* DbJson.of(timedEntries)
|
||||
* .replace("id", "eventTime")
|
||||
* .assertContentMatches("/assertJson/full-1-timed.json");
|
||||
* DbJson.of(order)
|
||||
* .assertContains("/order-partial.json");
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Assert content matches</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* DbJson.of(order)
|
||||
* .replace("id", "whenCreated", "whenModified)
|
||||
* .assertContentMatches("/order-full.json");
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@@ -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.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
@@ -103,7 +127,44 @@ public class DbJson {
|
||||
* }</pre>
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*
|
||||
* @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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*
|
||||
* @param resourcePath The resource path of the JSON content we are checking against.
|
||||
*/
|
||||
public void assertContainsResource(String resourcePath) {
|
||||
assertContains(readResource(resourcePath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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> T asBean(Class<T> cls) {
|
||||
return Json.read(cls, readResource(resourcePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return as a list of beans.
|
||||
*/
|
||||
<T> List<T> asList(Class<T> 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.
|
||||
* <pre>{@code
|
||||
*
|
||||
* PlainBean bean =
|
||||
* Json.resource("/example/plain-list.json").asBean(PlainBean.class);
|
||||
*
|
||||
* List<PlainBean> list =
|
||||
* Json.resource("/example/plain-list.json").asList(PlainBean.class);
|
||||
*
|
||||
* JsonNode jsonNode =
|
||||
* Json.resource("/example/plain-list.json").asJsonNode();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @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> T readFromResource(Class<T> type, String resourcePath) {
|
||||
return read(type, readResource(resourcePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a typed object from json content.
|
||||
*/
|
||||
public static <T> T read(Class<T> 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 <T> List<T> readList(Class<T> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String> path = new Stack<>();
|
||||
private final LinkedList<String> 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<Map.Entry<String, JsonNode>> expectedFields = expectedJsonNode.fields();
|
||||
while (expectedFields.hasNext()) {
|
||||
Map.Entry<String, JsonNode> 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(".[", "[");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user