mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
31
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82247fd4e4 | ||
|
|
27f86548a6 | ||
|
|
7e8424328f | ||
|
|
923828ce87 | ||
|
|
e195f20981 | ||
|
|
86d0216159 | ||
|
|
a4698ea1b6 | ||
|
|
888384f7db | ||
|
|
817c946eeb | ||
|
|
1749c2cb4c | ||
|
|
3afd3c4530 | ||
|
|
e5f98f1b7a | ||
|
|
2e496c9e1e | ||
|
|
5975694538 | ||
|
|
03a42f7f6e | ||
|
|
84f1e05eb4 | ||
|
|
da20ced1ed | ||
|
|
1f1a6e2c04 | ||
|
|
6b83800a83 | ||
|
|
c344551e4d | ||
|
|
6b4aff7191 | ||
|
|
fc7fae35c9 | ||
|
|
7929d8c0b8 | ||
|
|
058766dd92 | ||
|
|
4fdb8a77b4 | ||
|
|
56be1fc047 | ||
|
|
192a946cf6 | ||
|
|
aa7b15fd6e | ||
|
|
cb74ef01e0 | ||
|
|
829dfcb013 | ||
|
|
25b6d2a5a0 |
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>11.34.1</version>
|
||||
<version>11.35.3</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ebean</name>
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-11.34.1</tag>
|
||||
<tag>ebean-11.35.3</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -120,10 +120,16 @@
|
||||
<version>4.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-types</artifactId>
|
||||
<version>1.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-datasource</artifactId>
|
||||
<version>4.3.4</version>
|
||||
<version>4.5.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -188,6 +194,21 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Provided scope to compile with java 11 -->
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jaxb</groupId>
|
||||
<artifactId>jaxb-runtime</artifactId>
|
||||
<version>2.4.0-b180830.0438</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
<version>1.3.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Provided scope for Postgres JSON/JSONB support -->
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
@@ -222,7 +243,7 @@
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-agentloader</artifactId>
|
||||
<version>2.1.2</version>
|
||||
<version>3.0.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -327,9 +348,10 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.5</version>
|
||||
<version>2.22.1</version>
|
||||
<configuration>
|
||||
<useSystemClassLoader>false</useSystemClassLoader>
|
||||
<trimStackTrace>false</trimStackTrace>
|
||||
<failIfNoTests>false</failIfNoTests>
|
||||
<includes>
|
||||
<include>**/Test*.java</include>
|
||||
|
||||
@@ -71,7 +71,6 @@ public class Expr {
|
||||
* </p>
|
||||
*/
|
||||
public static Expression inRange(String propertyName, Object value1, Object value2) {
|
||||
|
||||
return Ebean.getExpressionFactory().inRange(propertyName, value1, value2);
|
||||
}
|
||||
|
||||
@@ -79,7 +78,6 @@ public class Expr {
|
||||
* Between - property between the two given values.
|
||||
*/
|
||||
public static Expression between(String propertyName, Object value1, Object value2) {
|
||||
|
||||
return Ebean.getExpressionFactory().between(propertyName, value1, value2);
|
||||
}
|
||||
|
||||
@@ -87,7 +85,6 @@ public class Expr {
|
||||
* Between - value between two given properties.
|
||||
*/
|
||||
public static Expression between(String lowProperty, String highProperty, Object value) {
|
||||
|
||||
return Ebean.getExpressionFactory().betweenProperties(lowProperty, highProperty, value);
|
||||
}
|
||||
|
||||
@@ -253,6 +250,43 @@ public class Expr {
|
||||
return Ebean.getExpressionFactory().in(propertyName, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* In where null or empty values means that no predicate is added to the query.
|
||||
* <p>
|
||||
* That is, only add the IN predicate if the values are not null or empty.
|
||||
* <p>
|
||||
* Without this we typically need to code an <code>if</code> block to only add
|
||||
* the IN predicate if the collection is not empty like:
|
||||
* </p>
|
||||
*
|
||||
* <h3>Without inOrEmpty()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where() // add some predicates
|
||||
* .eq("status", Status.NEW);
|
||||
*
|
||||
* if (ids != null && !ids.isEmpty()) {
|
||||
* query.where().in("customer.id", ids);
|
||||
* }
|
||||
*
|
||||
* query.findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Using inOrEmpty()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where()
|
||||
* .eq("status", Status.NEW)
|
||||
* .inOrEmpty("customer.id", ids)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public static Expression inOrEmpty(String propertyName, Collection<?> values) {
|
||||
return Ebean.getExpressionFactory().inOrEmpty(propertyName, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Id Equal to - ID property is equal to the value.
|
||||
*/
|
||||
|
||||
@@ -122,6 +122,11 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression eq(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Equal To or Null - property equal to the given value or null.
|
||||
*/
|
||||
Expression eqOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Not Equal To - property not equal to the given value.
|
||||
*/
|
||||
@@ -157,6 +162,25 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression inRange(String propertyName, Object value1, Object value2);
|
||||
|
||||
/**
|
||||
* Value in Range between 2 properties.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* .startDate.inRangeWith(endDate, now)
|
||||
*
|
||||
* // which equates to
|
||||
* startDate <= now and (endDate > now or endDate is null)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* This is a convenience expression combining a number of simple expressions.
|
||||
* The most common use of this could be called "effective dating" where 2 date or
|
||||
* timestamp columns represent the date range in which
|
||||
*/
|
||||
Expression inRangeWith(String lowProperty, String highProperty, Object value);
|
||||
|
||||
/**
|
||||
* Between - property between the two given values.
|
||||
*/
|
||||
@@ -167,6 +191,14 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression betweenProperties(String lowProperty, String highProperty, Object value);
|
||||
|
||||
/**
|
||||
* Greater Than Or Null - property greater than the given value or null.
|
||||
* <p>
|
||||
* A convenient expression combining GT and Is Null. Most often useful for range
|
||||
* expressions where the top range value is nullable.
|
||||
*/
|
||||
Expression gtOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Greater Than - property greater than the given value.
|
||||
*/
|
||||
@@ -178,6 +210,14 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression ge(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Less Than or Null - property less than the given value or null.
|
||||
* <p>
|
||||
* A convenient expression combining LT and Is Null. Most often useful for range
|
||||
* expressions where the bottom range value is nullable.
|
||||
*/
|
||||
Expression ltOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Less Than - property less than the given value.
|
||||
*/
|
||||
@@ -285,6 +325,41 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression in(String propertyName, Collection<?> values);
|
||||
|
||||
/**
|
||||
* In where null or empty values means that no predicate is added to the query.
|
||||
* <p>
|
||||
* That is, only add the IN predicate if the values are not null or empty.
|
||||
* <p>
|
||||
* Without this we typically need to code an <code>if</code> block to only add
|
||||
* the IN predicate if the collection is not empty like:
|
||||
* </p>
|
||||
*
|
||||
* <h3>Without inOrEmpty()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where() // add some predicates
|
||||
* .eq("status", Status.NEW);
|
||||
*
|
||||
* if (ids != null && !ids.isEmpty()) {
|
||||
* query.where().in("customer.id", ids);
|
||||
* }
|
||||
*
|
||||
* query.findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Using inOrEmpty()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where()
|
||||
* .eq("status", Status.NEW)
|
||||
* .inOrEmpty("customer.id", ids)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
Expression inOrEmpty(String propertyName, Collection<?> values);
|
||||
|
||||
/**
|
||||
* Not In - property has a value in the array of values.
|
||||
*/
|
||||
|
||||
@@ -785,6 +785,11 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> eq(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Equal To or Null - property is equal to a given value or null.
|
||||
*/
|
||||
ExpressionList<T> eqOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Not Equal To - property not equal to the given value.
|
||||
*/
|
||||
@@ -802,6 +807,25 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> ine(String propertyName, String value);
|
||||
|
||||
/**
|
||||
* Value in Range between 2 properties.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* .startDate.inRangeWith(endDate, now)
|
||||
*
|
||||
* // which equates to
|
||||
* startDate <= now and (endDate > now or endDate is null)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* This is a convenience expression combining a number of simple expressions.
|
||||
* The most common use of this could be called "effective dating" where 2 date or
|
||||
* timestamp columns represent the date range in which
|
||||
*/
|
||||
ExpressionList<T> inRangeWith(String lowProperty, String highProperty, Object value);
|
||||
|
||||
/**
|
||||
* In Range - property >= value1 and property < value2.
|
||||
* <p>
|
||||
@@ -825,6 +849,11 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> gt(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Greater Than or Null - property greater than the given value or null.
|
||||
*/
|
||||
ExpressionList<T> gtOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Greater Than or Equal to - property greater than or equal to the given
|
||||
* value.
|
||||
@@ -836,6 +865,11 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> lt(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Less Than or Null - property less than the given value or null.
|
||||
*/
|
||||
ExpressionList<T> ltOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Less Than or Equal to - property less than or equal to the given value.
|
||||
*/
|
||||
@@ -969,6 +1003,41 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> in(String propertyName, Collection<?> values);
|
||||
|
||||
/**
|
||||
* In where null or empty values means that no predicate is added to the query.
|
||||
* <p>
|
||||
* That is, only add the IN predicate if the values are not null or empty.
|
||||
* <p>
|
||||
* Without this we typically need to code an <code>if</code> block to only add
|
||||
* the IN predicate if the collection is not empty like:
|
||||
* </p>
|
||||
*
|
||||
* <h3>Without inOrEmpty()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where() // add some predicates
|
||||
* .eq("status", Status.NEW);
|
||||
*
|
||||
* if (ids != null && !ids.isEmpty()) {
|
||||
* query.where().in("customer.id", ids);
|
||||
* }
|
||||
*
|
||||
* query.findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Using inOrEmpty()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where()
|
||||
* .eq("status", Status.NEW)
|
||||
* .inOrEmpty("customer.id", ids)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> inOrEmpty(String propertyName, Collection<?> values);
|
||||
|
||||
/**
|
||||
* In - using a subQuery.
|
||||
* <p>
|
||||
@@ -1213,6 +1282,68 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> raw(String raw);
|
||||
|
||||
/**
|
||||
* Only add the raw expression if the values is not null or empty.
|
||||
* <p>
|
||||
* This is a pure convenience expression to make it nicer to deal with the pattern where we use
|
||||
* raw() expression with a subquery and only want to add the subquery predicate when the collection
|
||||
* of values is not empty.
|
||||
* </p>
|
||||
* <h3>Without inOrEmpty()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where() // add some predicates
|
||||
* .eq("status", Status.NEW);
|
||||
*
|
||||
* // common pattern - we can use rawOrEmpty() instead
|
||||
* if (orderIds != null && !orderIds.isEmpty()) {
|
||||
* query.where().raw("t0.customer_id in (select o.customer_id from orders o where o.id in (?1))", orderIds);
|
||||
* }
|
||||
*
|
||||
* query.findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Using rawOrEmpty()</h3>
|
||||
* Note that in the example below we use the <code>?1</code> bind parameter to get "parameter expansion"
|
||||
* for each element in the collection.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where()
|
||||
* .eq("status", Status.NEW)
|
||||
* // only add the expression if orderIds is not empty
|
||||
* .rawOrEmpty("t0.customer_id in (select o.customer_id from orders o where o.id in (?1))", orderIds);
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Postgres ANY</h3>
|
||||
* With Postgres we would often use the SQL <code>ANY</code> expression and array parameter binding
|
||||
* rather than <code>IN</code>.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where()
|
||||
* .eq("status", Status.NEW)
|
||||
* .rawOrEmpty("t0.customer_id in (select o.customer_id from orders o where o.id = any(?))", orderIds);
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* Note that we need to cast the Postgres array for UUID types like:
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* " ... = any(?::uuid[])"
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param raw The raw expression that is typically a subquery
|
||||
* @param values The values which is typically a list or set of id values.
|
||||
*/
|
||||
ExpressionList<T> rawOrEmpty(String raw, Collection<?> values);
|
||||
|
||||
/**
|
||||
* Add a match expression.
|
||||
*
|
||||
|
||||
@@ -47,6 +47,11 @@ public class PlatformConfig {
|
||||
*/
|
||||
private DbUuid dbUuid = DbUuid.AUTO_VARCHAR;
|
||||
|
||||
/**
|
||||
* Set to true to force InetAddress to map to Varchar (for Postgres rather than INET)
|
||||
*/
|
||||
private boolean databaseInetAddressVarchar;
|
||||
|
||||
/**
|
||||
* Modify the default mapping of standard types such as default precision for DECIMAL etc.
|
||||
*/
|
||||
@@ -181,6 +186,20 @@ public class PlatformConfig {
|
||||
this.idType = idType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if InetAddress should map to varchar column (rather than Postgres INET).
|
||||
*/
|
||||
public boolean isDatabaseInetAddressVarchar() {
|
||||
return databaseInetAddressVarchar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to force InetAddress to map to varchar column.
|
||||
*/
|
||||
public void setDatabaseInetAddressVarchar(boolean databaseInetAddressVarchar) {
|
||||
this.databaseInetAddressVarchar = databaseInetAddressVarchar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom type mapping.
|
||||
* <p>
|
||||
@@ -235,6 +254,7 @@ public class PlatformConfig {
|
||||
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize);
|
||||
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
|
||||
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
|
||||
databaseInetAddressVarchar = p.getBoolean("databaseInetAddressVarchar", databaseInetAddressVarchar);
|
||||
|
||||
DbUuid dbUuid = p.getEnum(DbUuid.class, "dbuuid", null);
|
||||
if (dbUuid != null) {
|
||||
|
||||
@@ -27,6 +27,9 @@ public class DbPlatformTypeMapping {
|
||||
|
||||
private static final DbPlatformType BOOLEAN_LOGICAL = new BooleanLogicalType();
|
||||
|
||||
private static final DbPlatformType INET_NATIVE = new DbPlatformType("inet", false);
|
||||
private static final DbPlatformType INET_VARCHAR = new DbPlatformType("varchar", 50);
|
||||
|
||||
private static final DbPlatformType UUID_NATIVE = new DbPlatformType("uuid", false);
|
||||
@SuppressWarnings("unused")
|
||||
private static final DbPlatformType UUID_PLACEHOLDER = new DbPlatformType("uuidPlaceholder");
|
||||
@@ -106,6 +109,7 @@ public class DbPlatformTypeMapping {
|
||||
put(DbType.JSONBLOB, new DbPlatformType("jsonblob"));
|
||||
put(DbType.JSONVARCHAR, new DbPlatformType("jsonvarchar", 1000));
|
||||
put(DbType.UUID, UUID_NATIVE);
|
||||
put(DbType.INET, INET_NATIVE);
|
||||
|
||||
} else {
|
||||
put(DbType.VARCHAR, new DbPlatformType("varchar", 255));
|
||||
@@ -121,6 +125,7 @@ public class DbPlatformTypeMapping {
|
||||
put(DbType.JSONVARCHAR, JSON_VARCHAR_PLACEHOLDER);
|
||||
// default to native UUID and override on platform configure()
|
||||
put(DbType.UUID, UUID_NATIVE);
|
||||
put(DbType.INET, INET_VARCHAR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ public enum DbType {
|
||||
ARRAY(Types.ARRAY),
|
||||
|
||||
UUID(ExtraDbTypes.UUID),
|
||||
INET(ExtraDbTypes.INET),
|
||||
CDIR(ExtraDbTypes.CDIR),
|
||||
|
||||
POINT(ExtraDbTypes.POINT),
|
||||
POLYGON(ExtraDbTypes.POLYGON),
|
||||
|
||||
@@ -40,6 +40,9 @@ public interface ExtraDbTypes {
|
||||
*/
|
||||
int JSONBlob = 5005;
|
||||
|
||||
int INET = 5020;
|
||||
int CDIR = 5021;
|
||||
|
||||
/**
|
||||
* Geo Point
|
||||
*/
|
||||
|
||||
@@ -60,6 +60,7 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
DbPlatformType dbBytea = new DbPlatformType("bytea", false);
|
||||
|
||||
dbTypeMap.put(DbType.UUID, new DbPlatformType("uuid", false));
|
||||
dbTypeMap.put(DbType.INET, new DbPlatformType("inet", false));
|
||||
dbTypeMap.put(DbType.HSTORE, new DbPlatformType("hstore", false));
|
||||
dbTypeMap.put(DbType.JSON, new DbPlatformType("json", false));
|
||||
dbTypeMap.put(DbType.JSONB, new DbPlatformType("jsonb", false));
|
||||
|
||||
@@ -97,6 +97,8 @@ class Loader {
|
||||
if (fileName != null) {
|
||||
loadWithExtensionCheck(fileName);
|
||||
}
|
||||
} else {
|
||||
loadWithExtensionCheck(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,12 +33,12 @@ class LastMigration {
|
||||
*/
|
||||
static String lastVersion(File migDirectory, File modelDir) {
|
||||
|
||||
List<String> fileNames = new ArrayList<>();
|
||||
List<MigrationVersion> versions = new ArrayList<>();
|
||||
|
||||
File[] sqlFiles = migDirectory.listFiles(pathname -> includeSqlFile(pathname.getName().toLowerCase()));
|
||||
if (sqlFiles != null) {
|
||||
for (File file : sqlFiles) {
|
||||
fileNames.add(trim(file.getName()));
|
||||
versions.add(trimAndParse(file.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,14 +46,14 @@ class LastMigration {
|
||||
File[] xmlFiles = modelDir.listFiles(pathname -> includeModelFile(pathname.getName().toLowerCase()));
|
||||
if (xmlFiles != null) {
|
||||
for (File file : xmlFiles) {
|
||||
fileNames.add(trim(file.getName()));
|
||||
versions.add(trimAndParse(file.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(fileNames);
|
||||
if (!fileNames.isEmpty()) {
|
||||
return fileNames.get(fileNames.size() - 1);
|
||||
Collections.sort(versions);
|
||||
if (!versions.isEmpty()) {
|
||||
return versions.get(versions.size() - 1).asString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -69,23 +69,14 @@ class LastMigration {
|
||||
return lowerFileName.endsWith(MODEL_XML);
|
||||
}
|
||||
|
||||
private static String trim(String name) {
|
||||
name = name.toLowerCase();
|
||||
char c = name.charAt(0);
|
||||
if (c == 'v') {
|
||||
name = name.substring(1);
|
||||
}
|
||||
int p = name.indexOf("__");
|
||||
if (p > -1) {
|
||||
name = name.substring(0, p);
|
||||
}
|
||||
private static MigrationVersion trimAndParse(String name) {
|
||||
if (name.endsWith(SQL)) {
|
||||
name = name.substring(0, name.length() - 4);
|
||||
}
|
||||
if (name.endsWith(MODEL_XML)) {
|
||||
name = name.substring(0, name.length() - 10);
|
||||
}
|
||||
return name;
|
||||
return MigrationVersion.parse(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
/**
|
||||
* Many to many intersection table changes that are held for later batch processing.
|
||||
*/
|
||||
private SaveManyBeans saveManyIntersection;
|
||||
private List<SaveManyBeans> saveManyIntersections;
|
||||
|
||||
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
|
||||
PersistExecute persistExecute, PersistRequest.Type type, int flags) {
|
||||
@@ -965,8 +965,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
|
||||
private void saveQueuedManyIntersection() {
|
||||
if (saveManyIntersection != null) {
|
||||
saveManyIntersection.saveIntersectionBatch();
|
||||
if (saveManyIntersections != null) {
|
||||
saveManyIntersections.forEach(SaveManyBeans::saveIntersectionBatch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1479,7 +1479,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
/**
|
||||
* The intersection table updates to the batch executed later on postExecute.
|
||||
*/
|
||||
public void setManyIntersection(SaveManyBeans saveManyIntersection) {
|
||||
this.saveManyIntersection = saveManyIntersection;
|
||||
public void addManyIntersection(SaveManyBeans saveManyIntersection) {
|
||||
if (this.saveManyIntersections == null) {
|
||||
this.saveManyIntersections = new ArrayList<>();
|
||||
}
|
||||
this.saveManyIntersections.add(saveManyIntersection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,6 +162,11 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
return new SimpleExpression(propertyName, Op.EQ, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression eqOrNull(String propertyName, Object value) {
|
||||
return or(eq(propertyName, value), isNull(propertyName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Not Equal To - property not equal to the given value.
|
||||
*/
|
||||
@@ -221,6 +226,11 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
return new InRangeExpression(propertyName, value1, value2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression inRangeWith(String lowProperty, String highProperty, Object value) {
|
||||
return and(le(lowProperty, value), gtOrNull(highProperty, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Between - property between the two given values.
|
||||
*/
|
||||
@@ -245,6 +255,14 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
return new SimpleExpression(propertyName, Op.GT, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Greater Than or null - property greater than the given value or null.
|
||||
*/
|
||||
@Override
|
||||
public Expression gtOrNull(String propertyName, Object value) {
|
||||
return or(gt(propertyName, value), isNull(propertyName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Greater Than or Equal to - property greater than or equal to the given
|
||||
* value.
|
||||
@@ -254,6 +272,14 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
return new SimpleExpression(propertyName, Op.GT_EQ, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Less Than or null - property less than the given value or null.
|
||||
*/
|
||||
@Override
|
||||
public Expression ltOrNull(String propertyName, Object value) {
|
||||
return or(lt(propertyName, value), isNull(propertyName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Less Than - property less than the given value.
|
||||
*/
|
||||
@@ -429,6 +455,16 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
return new InExpression(propertyName, values, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* In where null or empty values means that no predicate is added to the query.
|
||||
* <p>
|
||||
* That is, only add the IN predicate if the values are not null or empty.
|
||||
*/
|
||||
@Override
|
||||
public Expression inOrEmpty(String propertyName, Collection<?> values) {
|
||||
return new InExpression(propertyName, values, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* In - property has a value in the array of values.
|
||||
*/
|
||||
|
||||
@@ -764,6 +764,12 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> eqOrNull(String propertyName, Object value) {
|
||||
add(expr.eqOrNull(propertyName, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> ieq(String propertyName, String value) {
|
||||
add(expr.ieq(propertyName, value));
|
||||
@@ -794,6 +800,12 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> inRangeWith(String lowProperty, String highProperty, Object value) {
|
||||
add(expr.inRangeWith(lowProperty, highProperty, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> inRange(String propertyName, Object value1, Object value2) {
|
||||
add(expr.inRange(propertyName, value1, value2));
|
||||
@@ -836,6 +848,12 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> gtOrNull(String propertyName, Object value) {
|
||||
add(expr.gtOrNull(propertyName, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> icontains(String propertyName, String value) {
|
||||
add(expr.icontains(propertyName, value));
|
||||
@@ -894,6 +912,14 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> inOrEmpty(String propertyName, Collection<?> values) {
|
||||
if (notEmpty(values)) {
|
||||
add(expr.in(propertyName, values));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> in(String propertyName, Object... values) {
|
||||
add(expr.in(propertyName, values));
|
||||
@@ -990,6 +1016,12 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> ltOrNull(String propertyName, Object value) {
|
||||
add(expr.ltOrNull(propertyName, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> not(Expression exp) {
|
||||
add(expr.not(exp));
|
||||
@@ -1044,6 +1076,18 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> rawOrEmpty(String raw, Collection<?> values) {
|
||||
if (notEmpty(values)) {
|
||||
add(expr.raw(raw, values));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean notEmpty(Collection<?> values) {
|
||||
return values != null && !values.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> startsWith(String propertyName, String value) {
|
||||
add(expr.startsWith(propertyName, value));
|
||||
|
||||
@@ -17,8 +17,16 @@ import java.util.List;
|
||||
|
||||
class InExpression extends AbstractExpression {
|
||||
|
||||
private static final String SQL_TRUE = "1=1";
|
||||
private static final String SQL_FALSE = "1=0";
|
||||
|
||||
private final boolean not;
|
||||
|
||||
/**
|
||||
* Set to true when adding "1=1" predicate (due to null or empty sourceValues).
|
||||
*/
|
||||
private final boolean empty;
|
||||
|
||||
private final Collection<?> sourceValues;
|
||||
|
||||
private List<Object> bindValues;
|
||||
@@ -26,18 +34,27 @@ class InExpression extends AbstractExpression {
|
||||
private boolean multiValueSupported;
|
||||
|
||||
InExpression(String propertyName, Collection<?> sourceValues, boolean not) {
|
||||
this(propertyName, sourceValues, not, false);
|
||||
}
|
||||
|
||||
InExpression(String propertyName, Collection<?> sourceValues, boolean not, boolean orEmpty) {
|
||||
super(propertyName);
|
||||
this.sourceValues = sourceValues;
|
||||
this.not = not;
|
||||
this.empty = orEmpty && (sourceValues == null || sourceValues.isEmpty());
|
||||
}
|
||||
|
||||
InExpression(String propertyName, Object[] array, boolean not) {
|
||||
super(propertyName);
|
||||
this.sourceValues = Arrays.asList(array);
|
||||
this.not = not;
|
||||
this.empty = false;
|
||||
}
|
||||
|
||||
private List<Object> values() {
|
||||
if (empty || sourceValues == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Object> vals = new ArrayList<>(sourceValues.size());
|
||||
for (Object sourceValue : sourceValues) {
|
||||
assert sourceValue != null : "null is not allowed in in-queries";
|
||||
@@ -48,8 +65,8 @@ class InExpression extends AbstractExpression {
|
||||
|
||||
@Override
|
||||
public boolean naturalKey(NaturalKeyQueryData<?> data) {
|
||||
// can't use naturalKey cache for NOT IN
|
||||
if (not) {
|
||||
// can't use naturalKey cache for NOT IN or when "empty"
|
||||
if (not || empty) {
|
||||
return false;
|
||||
}
|
||||
List<Object> copy = data.matchIn(propName, bindValues);
|
||||
@@ -70,11 +87,16 @@ class InExpression extends AbstractExpression {
|
||||
|
||||
@Override
|
||||
public void writeDocQuery(DocQueryContext context) throws IOException {
|
||||
context.writeIn(propName, values().toArray(), not);
|
||||
if (!empty) {
|
||||
context.writeIn(propName, values().toArray(), not);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
if (empty) {
|
||||
return;
|
||||
}
|
||||
for (Object value : bindValues) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException("null values in 'in(...)' queries must be handled separately!");
|
||||
@@ -108,10 +130,12 @@ class InExpression extends AbstractExpression {
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
if (empty) {
|
||||
request.append(SQL_TRUE);
|
||||
return;
|
||||
}
|
||||
if (bindValues.isEmpty()) {
|
||||
String expr = not ? "1=1" : "1=0";
|
||||
request.append(expr);
|
||||
request.append(not ? SQL_TRUE : SQL_FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -142,10 +166,14 @@ class InExpression extends AbstractExpression {
|
||||
builder.append("In[");
|
||||
}
|
||||
builder.append(propName);
|
||||
builder.append(" ?");
|
||||
if (!multiValueSupported) {
|
||||
// query plan specific to the number of parameters in the IN clause
|
||||
builder.append(bindValues.size());
|
||||
if (empty) {
|
||||
builder.append("empty");
|
||||
} else {
|
||||
builder.append(" ?");
|
||||
if (!multiValueSupported) {
|
||||
// query plan specific to the number of parameters in the IN clause
|
||||
builder.append(bindValues.size());
|
||||
}
|
||||
}
|
||||
builder.append("]");
|
||||
}
|
||||
|
||||
@@ -280,6 +280,11 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
|
||||
return exprList.and(expOne, expTwo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> inRangeWith(String lowProperty, String highProperty, Object value) {
|
||||
return exprList.inRangeWith(lowProperty, highProperty, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> inRange(String propertyName, Object value1, Object value2) {
|
||||
return exprList.inRange(propertyName, value1, value2);
|
||||
@@ -310,6 +315,11 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
|
||||
return exprList.eq(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> eqOrNull(String propertyName, Object value) {
|
||||
return exprList.eqOrNull(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> exampleLike(Object example) {
|
||||
return exprList.exampleLike(example);
|
||||
@@ -597,6 +607,11 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
|
||||
return exprList.gt(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> gtOrNull(String propertyName, Object value) {
|
||||
return exprList.gtOrNull(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> having() {
|
||||
throw new IllegalStateException("having() not allowed on Junction expression list");
|
||||
@@ -657,6 +672,11 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
|
||||
return exprList.in(propertyName, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> inOrEmpty(String propertyName, Collection<?> values) {
|
||||
return exprList.inOrEmpty(propertyName, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> in(String propertyName, Object... values) {
|
||||
return exprList.in(propertyName, values);
|
||||
@@ -732,6 +752,11 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
|
||||
return exprList.lt(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> ltOrNull(String propertyName, Object value) {
|
||||
return exprList.ltOrNull(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> ne(String propertyName, Object value) {
|
||||
return exprList.ne(propertyName, value);
|
||||
@@ -782,6 +807,11 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
|
||||
return exprList.raw(raw, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> rawOrEmpty(String raw, Collection<?> values) {
|
||||
return exprList.rawOrEmpty(raw, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> raw(String raw) {
|
||||
return exprList.raw(raw);
|
||||
|
||||
@@ -69,6 +69,7 @@ public final class BatchControl {
|
||||
* Size of the largest buffer.
|
||||
*/
|
||||
private int bufferMax;
|
||||
private int topCounter;
|
||||
|
||||
private Queue earlyQueue;
|
||||
private Queue lateQueue;
|
||||
@@ -246,6 +247,7 @@ public final class BatchControl {
|
||||
pstmtHolder.clear();
|
||||
beanHoldMap.clear();
|
||||
maxDepth = 0;
|
||||
topCounter = 0;
|
||||
}
|
||||
|
||||
private void flushBuffer(boolean resetTop) throws BatchedSqlException {
|
||||
@@ -319,10 +321,8 @@ public final class BatchControl {
|
||||
if (maybe != -1) {
|
||||
beanDepth = maybe;
|
||||
} else {
|
||||
// we can't be certain of the relative ordering for this type so
|
||||
// flush and reset the batch as we are changing the type of our top level
|
||||
// bean so just keep it simple and flush and reset the top
|
||||
flushReset();
|
||||
// additional "top level" bean type ordered by save() order
|
||||
beanDepth += ++topCounter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import io.ebeaninternal.server.expression.platform.DbExpressionHandler;
|
||||
import io.ebeaninternal.server.persist.platform.MultiValueBind;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
import io.ebeaninternal.server.type.PostgresHelper;
|
||||
import io.ebeaninternal.server.type.RsetDataReader;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.TypeManager;
|
||||
@@ -374,6 +375,11 @@ public class Binder {
|
||||
b.setObject(data);
|
||||
break;
|
||||
|
||||
case DbPlatformType.INET:
|
||||
// data is always a String at this point
|
||||
b.setObject(PostgresHelper.asInet(data.toString()));
|
||||
break;
|
||||
|
||||
case java.sql.Types.OTHER:
|
||||
b.setObject(data, dataType);
|
||||
break;
|
||||
|
||||
@@ -247,7 +247,7 @@ public class SaveManyBeans extends SaveManyBase {
|
||||
if (request.isQueueManyIntersection()) {
|
||||
// queue/delay until bean persist request is flushed
|
||||
this.deleteMissing = deleteMissingChildren;
|
||||
request.setManyIntersection(this);
|
||||
request.addManyIntersection(this);
|
||||
} else {
|
||||
saveAssocManyIntersection(deleteMissingChildren, false);
|
||||
}
|
||||
|
||||
@@ -82,8 +82,8 @@ abstract class AbstractMultiValueBind extends MultiValueBind {
|
||||
//case NCLOB:
|
||||
case NCHAR:
|
||||
case NVARCHAR:
|
||||
return "varchar";
|
||||
case ExtraDbTypes.UUID: // Db Native UUID
|
||||
case ExtraDbTypes.UUID: // Postgres cast to uuid[]
|
||||
case ExtraDbTypes.INET: // Postgres cast to inet[]
|
||||
return "varchar";
|
||||
|
||||
default:
|
||||
|
||||
@@ -14,6 +14,9 @@ public class PostgresMultiValueBind extends AbstractMultiValueBind {
|
||||
if (dbType == ExtraDbTypes.UUID) {
|
||||
return (not) ? " != all(?::uuid[])" : " = any(?::uuid[])";
|
||||
}
|
||||
if (dbType == ExtraDbTypes.INET) {
|
||||
return (not) ? " != all(?::inet[])" : " = any(?::inet[])";
|
||||
}
|
||||
String arrayType = getArrayType(dbType);
|
||||
if (arrayType == null) {
|
||||
return super.getInExpression(not, type, size);
|
||||
|
||||
@@ -334,6 +334,24 @@ public final class ConvertInetAddresses {
|
||||
return ip.getHostAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the host address without the square brackets around IPv6 addresses.
|
||||
*/
|
||||
public static String toHostAddress(InetAddress ip) {
|
||||
return ip.getHostAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the IPv4 or IPv6 address without quare brackets around IPv6 addresses.
|
||||
*/
|
||||
public static InetAddress fromHost(String hostAddr) {
|
||||
if (hostAddr.startsWith("[")) {
|
||||
// IPv6 address
|
||||
hostAddr = hostAddr.substring(1, hostAddr.length() - 1);
|
||||
}
|
||||
return forString(hostAddr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an InetAddress representing the literal IPv4 or IPv6 host
|
||||
* portion of a URL, encoded in the format specified by RFC 3986 section 3.2.2.
|
||||
|
||||
@@ -14,6 +14,8 @@ import io.ebean.config.ScalarTypeConverter;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.types.Cdir;
|
||||
import io.ebean.types.Inet;
|
||||
import io.ebean.util.AnnotationUtil;
|
||||
import io.ebeaninternal.api.ExtraTypeFactory;
|
||||
import io.ebeaninternal.dbmigration.DbOffline;
|
||||
@@ -37,6 +39,8 @@ import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
@@ -128,7 +132,6 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
private final ScalarType<?> dateType = new ScalarTypeDate();
|
||||
|
||||
private final ScalarType<?> inetAddressType = new ScalarTypeInetAddress();
|
||||
private final ScalarType<?> urlType = new ScalarTypeURL();
|
||||
private final ScalarType<?> uriType = new ScalarTypeURI();
|
||||
private final ScalarType<?> localeType = new ScalarTypeLocale();
|
||||
@@ -975,8 +978,21 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
addType(UUID.class, uuidType);
|
||||
}
|
||||
|
||||
if (offlineMigrationGeneration || (postgres && !config.getPlatformConfig().isDatabaseInetAddressVarchar())) {
|
||||
addInetAddressType(new ScalarTypeInetAddressPostgres());
|
||||
} else {
|
||||
addInetAddressType(new ScalarTypeInetAddress());
|
||||
}
|
||||
|
||||
if (offlineMigrationGeneration || postgres) {
|
||||
addType(Cdir.class, new ScalarTypeCdir.Postgres());
|
||||
addType(Inet.class, new ScalarTypeInet.Postgres());
|
||||
} else {
|
||||
addType(Cdir.class, new ScalarTypeCdir.Varchar());
|
||||
addType(Inet.class, new ScalarTypeInet.Varchar());
|
||||
}
|
||||
|
||||
addType(File.class, fileType);
|
||||
addType(InetAddress.class, inetAddressType);
|
||||
addType(Locale.class, localeType);
|
||||
addType(Currency.class, currencyType);
|
||||
addType(TimeZone.class, timeZoneType);
|
||||
@@ -1063,4 +1079,10 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
nativeMap.put(Types.TIMESTAMP, timestampType);
|
||||
}
|
||||
|
||||
private void addInetAddressType(ScalarType scalarType) {
|
||||
addType(InetAddress.class, scalarType);
|
||||
addType(Inet4Address.class, scalarType);
|
||||
addType(Inet6Address.class, scalarType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,12 @@ public class PostgresHelper {
|
||||
*/
|
||||
public static final String JSONB_TYPE = "jsonb";
|
||||
|
||||
public static final String INET_TYPE = "inet";
|
||||
|
||||
public static Object asInet(String value) throws SQLException {
|
||||
return asObject(INET_TYPE, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct and return Postgres specific PG object.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.config.dbplatform.ExtraDbTypes;
|
||||
import io.ebean.types.Cdir;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* ScalarType for Cdir to Varchar or Postgres CDIR.
|
||||
*/
|
||||
public abstract class ScalarTypeCdir extends ScalarTypeBaseVarchar<Cdir> {
|
||||
|
||||
ScalarTypeCdir() {
|
||||
super(Cdir.class, false, ExtraDbTypes.INET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract void bind(DataBind b, Cdir value) throws SQLException;
|
||||
|
||||
@Override
|
||||
public Cdir convertFromDbString(String dbValue) {
|
||||
return parse(dbValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertToDbString(Cdir beanValue) {
|
||||
return formatValue(beanValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(Cdir value) {
|
||||
return value.getAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cdir parse(String value) {
|
||||
return new Cdir(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cdir to Varchar.
|
||||
*/
|
||||
public static class Varchar extends ScalarTypeCdir {
|
||||
|
||||
@Override
|
||||
public void bind(DataBind b, Cdir value) throws SQLException {
|
||||
if (value == null) {
|
||||
b.setNull(Types.VARCHAR);
|
||||
} else {
|
||||
b.setString(convertToDbString(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cdir to Postgres CDIR.
|
||||
*/
|
||||
public static class Postgres extends ScalarTypeCdir {
|
||||
|
||||
@Override
|
||||
public void bind(DataBind b, Cdir value) throws SQLException {
|
||||
if (value == null) {
|
||||
b.setNull(Types.OTHER);
|
||||
} else {
|
||||
String strValue = convertToDbString(value);
|
||||
b.setObject(PostgresHelper.asInet(strValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.config.dbplatform.ExtraDbTypes;
|
||||
import io.ebean.types.Inet;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* ScalarType for Inet to Varchar or Postgres INET.
|
||||
*/
|
||||
public abstract class ScalarTypeInet extends ScalarTypeBaseVarchar<Inet> {
|
||||
|
||||
ScalarTypeInet(int jdbcType) {
|
||||
super(Inet.class, false, jdbcType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract void bind(DataBind b, Inet value) throws SQLException;
|
||||
|
||||
@Override
|
||||
public Inet convertFromDbString(String dbValue) {
|
||||
return parse(dbValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertToDbString(Inet beanValue) {
|
||||
return formatValue(beanValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(Inet value) {
|
||||
return value.getAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inet parse(String value) {
|
||||
return new Inet(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inet to Varchar.
|
||||
*/
|
||||
public static class Varchar extends ScalarTypeInet {
|
||||
|
||||
Varchar() {
|
||||
super(Types.VARCHAR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBind b, Inet value) throws SQLException {
|
||||
if (value == null) {
|
||||
b.setNull(Types.VARCHAR);
|
||||
} else {
|
||||
b.setString(convertToDbString(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inet to Postgres INET.
|
||||
*/
|
||||
public static class Postgres extends ScalarTypeInet {
|
||||
|
||||
Postgres() {
|
||||
super(ExtraDbTypes.INET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBind b, Inet value) throws SQLException {
|
||||
if (value == null) {
|
||||
b.setNull(Types.OTHER);
|
||||
} else {
|
||||
String strValue = convertToDbString(value);
|
||||
b.setObject(PostgresHelper.asInet(strValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.config.dbplatform.ExtraDbTypes;
|
||||
import io.ebean.text.TextException;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* ScalarType for InetAddress to Postgres INET.
|
||||
*/
|
||||
public class ScalarTypeInetAddressPostgres extends ScalarTypeBaseVarchar<InetAddress> {
|
||||
|
||||
public ScalarTypeInetAddressPostgres() {
|
||||
super(InetAddress.class, false, ExtraDbTypes.INET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBind b, InetAddress value) throws SQLException {
|
||||
if (value == null) {
|
||||
b.setNull(Types.OTHER);
|
||||
} else {
|
||||
String strValue = convertToDbString(value);
|
||||
b.setObject(PostgresHelper.asInet(strValue));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress convertFromDbString(String dbValue) {
|
||||
try {
|
||||
return parse(dbValue);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException("Error with InetAddresses [" + dbValue + "] " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertToDbString(InetAddress beanValue) {
|
||||
return formatValue(beanValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(InetAddress v) {
|
||||
return ConvertInetAddresses.toHostAddress(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress parse(String value) {
|
||||
try {
|
||||
return ConvertInetAddresses.fromHost(value);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new TextException("Error with InetAddresses [{}]", value, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,7 +11,25 @@ import static org.assertj.core.api.StrictAssertions.assertThat;
|
||||
public class InExpressionTest extends BaseExpressionTest {
|
||||
|
||||
@Test
|
||||
public void queryPlanHash_given_diffPropertyName_should_differentPlanHash() throws Exception {
|
||||
public void queryPlanHash_given_diffEmpty_should_differentPlanHash() {
|
||||
|
||||
List<Integer> emptyValues = values();
|
||||
|
||||
InExpression ex1 = new InExpression("foo", emptyValues, false, true);
|
||||
InExpression ex2 = new InExpression("foo", emptyValues, false);
|
||||
InExpression ex3 = new InExpression("foo", emptyValues, false, true);
|
||||
InExpression ex4 = new InExpression("foo", null, false, true);
|
||||
|
||||
ex1.prepareExpression(multi());
|
||||
ex2.prepareExpression(multi());
|
||||
|
||||
different(ex1, ex2);
|
||||
same(ex1, ex3); // same empty
|
||||
same(ex1, ex4); // same null
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryPlanHash_given_diffPropertyName_should_differentPlanHash() {
|
||||
|
||||
List<Integer> values = values(42, 92);
|
||||
|
||||
@@ -25,7 +43,7 @@ public class InExpressionTest extends BaseExpressionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryPlanHash_given_diffBindCount_should_differentPlanHash() throws Exception {
|
||||
public void queryPlanHash_given_diffBindCount_should_differentPlanHash() {
|
||||
|
||||
List<Integer> values1 = values(42, 92);
|
||||
List<Integer> values2 = values(42, 92, 82);
|
||||
@@ -39,7 +57,7 @@ public class InExpressionTest extends BaseExpressionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryPlanHash_given_diffBindCount_withMultiSupport_samePlanHash() throws Exception {
|
||||
public void queryPlanHash_given_diffBindCount_withMultiSupport_samePlanHash() {
|
||||
|
||||
List<Integer> values1 = values(42, 92);
|
||||
List<Integer> values2 = values(42, 92, 82);
|
||||
@@ -53,7 +71,7 @@ public class InExpressionTest extends BaseExpressionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryPlanHash_given_diffNotFlag_should_differentPlanHash() throws Exception {
|
||||
public void queryPlanHash_given_diffNotFlag_should_differentPlanHash() {
|
||||
|
||||
List<Integer> values = values(42, 92);
|
||||
|
||||
@@ -67,7 +85,7 @@ public class InExpressionTest extends BaseExpressionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryPlanHash_given_sameNotFlag_should_samePlanHash() throws Exception {
|
||||
public void queryPlanHash_given_sameNotFlag_should_samePlanHash() {
|
||||
|
||||
List<Integer> values = values(42, 92);
|
||||
|
||||
|
||||
@@ -3,22 +3,28 @@ package io.ebeaninternal.server.transaction;
|
||||
import io.ebean.config.ProfilingConfig;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class DefaultProfileHandlerTest {
|
||||
@Test
|
||||
public void createProfileStream() throws Exception {
|
||||
|
||||
DefaultProfileHandler handler = new DefaultProfileHandler(new ProfilingConfig());
|
||||
@Test
|
||||
public void createProfileStream() {
|
||||
|
||||
ProfilingConfig profilingConfig = new ProfilingConfig();
|
||||
profilingConfig.setDirectory("target/profiling");
|
||||
|
||||
DefaultProfileHandler handler = new DefaultProfileHandler(profilingConfig);
|
||||
|
||||
assertNotNull(handler.createProfileStream(12));
|
||||
assertNull(handler.createProfileStream(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createProfileStream_when_specificIncludeIds() throws Exception {
|
||||
public void createProfileStream_when_specificIncludeIds() {
|
||||
|
||||
ProfilingConfig config = new ProfilingConfig();
|
||||
config.setDirectory("target/profiling");
|
||||
config.setIncludeProfileIds(new int[]{100,101});
|
||||
|
||||
DefaultProfileHandler handler = new DefaultProfileHandler(config);
|
||||
|
||||
@@ -1,34 +1,46 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ConvertInetAddressTest {
|
||||
|
||||
@Test
|
||||
public void forString() {
|
||||
|
||||
InetAddress addr = ConvertInetAddresses.forString("128.1.10.23");
|
||||
Assert.assertNotNull(addr);
|
||||
Assert.assertEquals("128.1.10.23", addr.getHostAddress());
|
||||
assertEquals("128.1.10.23", addr.getHostAddress());
|
||||
|
||||
String ip6addr = "2001:db8:85a3:0:0:8a2e:370:7334";
|
||||
InetAddress addr6 = ConvertInetAddresses.forString(ip6addr);
|
||||
String uriAddr6 = ConvertInetAddresses.toUriString(addr6);
|
||||
Assert.assertEquals("[" + ip6addr + "]", uriAddr6);
|
||||
Assert.assertEquals(ip6addr, addr6.getHostAddress());
|
||||
assertEquals("[" + ip6addr + "]", uriAddr6);
|
||||
assertEquals(ip6addr, addr6.getHostAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ipv6_fromHost_getHostAddress() {
|
||||
InetAddress addr2 = ConvertInetAddresses.fromHost("2001:4f8:3:ba:2e0:81ff:fe22:d1f1");
|
||||
assertEquals("2001:4f8:3:ba:2e0:81ff:fe22:d1f1", addr2.getHostAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ipv6_fromHost_getHostAddress_2() {
|
||||
InetAddress addr2 = ConvertInetAddresses.fromHost("2001:db8:85a3:0:0:8a2e:370:7334");
|
||||
assertEquals("2001:db8:85a3:0:0:8a2e:370:7334", addr2.getHostAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toUriString() {
|
||||
|
||||
InetAddress addr = ConvertInetAddresses.forString("128.1.10.23");
|
||||
Assert.assertEquals("128.1.10.23", addr.getHostAddress());
|
||||
assertEquals("128.1.10.23", addr.getHostAddress());
|
||||
|
||||
String uriAddr = ConvertInetAddresses.toUriString(addr);
|
||||
Assert.assertEquals("128.1.10.23", uriAddr);
|
||||
assertEquals("128.1.10.23", uriAddr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,13 +16,21 @@ public class ScalarTypeInstantTest {
|
||||
|
||||
ScalarTypeInstant type = new ScalarTypeInstant(JsonConfig.DateTime.MILLIS);
|
||||
|
||||
private Instant now() {
|
||||
// in JDK11 Instant.now() returns a nanosecond precise instant.
|
||||
// as ScalarTypeInstant is only milliSecond precise, tests may fail with
|
||||
// expected:<2019-02-10T15:39:36.702700200Z> but was:<2019-02-10T15:39:36.702Z>
|
||||
// if we use Instant.now() - so we use this workaround here.
|
||||
return Instant.ofEpochMilli(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadData() throws Exception {
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
ObjectOutputStream out = new ObjectOutputStream(os);
|
||||
|
||||
Instant now = Instant.now();
|
||||
Instant now = now();
|
||||
type.writeData(out, now);
|
||||
type.writeData(out, null);
|
||||
out.flush();
|
||||
@@ -41,7 +49,7 @@ public class ScalarTypeInstantTest {
|
||||
@Test
|
||||
public void testToJdbcType() throws Exception {
|
||||
|
||||
Instant now = Instant.now();
|
||||
Instant now = now();
|
||||
Timestamp timestamp = Timestamp.from(now);
|
||||
Object val1 = type.toJdbcType(now);
|
||||
Object val2 = type.toJdbcType(timestamp);
|
||||
@@ -53,7 +61,7 @@ public class ScalarTypeInstantTest {
|
||||
@Test
|
||||
public void testToBeanType() throws Exception {
|
||||
|
||||
Instant now = Instant.now();
|
||||
Instant now = now();
|
||||
Instant val1 = type.toBeanType(now);
|
||||
Instant val2 = type.toBeanType(Timestamp.from(now));
|
||||
|
||||
@@ -64,7 +72,7 @@ public class ScalarTypeInstantTest {
|
||||
@Test
|
||||
public void testFormatValue() throws Exception {
|
||||
|
||||
Instant now = Instant.now();
|
||||
Instant now = now();
|
||||
Timestamp timestamp = Timestamp.from(now);
|
||||
String formatted = type.formatValue(now);
|
||||
assertEquals("" + timestamp.getTime(), formatted);
|
||||
@@ -73,7 +81,7 @@ public class ScalarTypeInstantTest {
|
||||
@Test
|
||||
public void testParse_when_epochMillis() throws Exception {
|
||||
|
||||
Instant now = Instant.now();
|
||||
Instant now = now();
|
||||
Timestamp timestamp = Timestamp.from(now);
|
||||
Instant val1 = type.parse("" + timestamp.getTime());
|
||||
assertEquals(now, val1);
|
||||
@@ -82,7 +90,7 @@ public class ScalarTypeInstantTest {
|
||||
@Test
|
||||
public void testParse_when_timestampForm() throws Exception {
|
||||
|
||||
Instant now = Instant.now();
|
||||
Instant now = now();
|
||||
Timestamp timestamp = Timestamp.from(now);
|
||||
Instant val1 = type.parse(timestamp.toString());
|
||||
assertEquals(now, val1);
|
||||
@@ -91,7 +99,7 @@ public class ScalarTypeInstantTest {
|
||||
@Test
|
||||
public void testFormatAndParse() throws Exception {
|
||||
|
||||
Instant now = Instant.now();
|
||||
Instant now = now();
|
||||
|
||||
String format = type.format(now);
|
||||
Instant val1 = type.parse(format);
|
||||
@@ -107,7 +115,7 @@ public class ScalarTypeInstantTest {
|
||||
@Test
|
||||
public void testConvertFromMillis() throws Exception {
|
||||
|
||||
Instant now = Instant.now();
|
||||
Instant now = now();
|
||||
Instant val = type.convertFromMillis(now.toEpochMilli());
|
||||
assertEquals(now, val);
|
||||
}
|
||||
@@ -116,7 +124,7 @@ public class ScalarTypeInstantTest {
|
||||
public void testJsonRead() throws Exception {
|
||||
|
||||
|
||||
Instant now = Instant.now();
|
||||
Instant now = now();
|
||||
|
||||
JsonTester<Instant> jsonTester = new JsonTester<>(type);
|
||||
jsonTester.test(now);
|
||||
|
||||
@@ -28,7 +28,7 @@ public class ScalarTypeLocalDateTimeTest {
|
||||
@Test
|
||||
public void testConvertToMillis() throws Exception {
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime now = LocalDateTime.now().withNano(123_000_000); // jdk11 workaround
|
||||
long asMillis = type.convertToMillis(now);
|
||||
LocalDateTime fromMillis = type.convertFromMillis(asMillis);
|
||||
|
||||
@@ -74,7 +74,7 @@ public class ScalarTypeLocalDateTimeTest {
|
||||
@Test
|
||||
public void testJson() throws Exception {
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime now = LocalDateTime.now().withNano(123_000_000); // jdk11 workaround
|
||||
|
||||
JsonTester<LocalDateTime> jsonTester = new JsonTester<>(type);
|
||||
jsonTester.test(now);
|
||||
|
||||
@@ -61,7 +61,7 @@ public class ScalarTypeOffsetDateTimeTest {
|
||||
@Test
|
||||
public void testJson() throws Exception {
|
||||
|
||||
OffsetDateTime now = OffsetDateTime.now();
|
||||
OffsetDateTime now = OffsetDateTime.now().withNano(123_000_000); // jdk11 workaround
|
||||
|
||||
JsonTester<OffsetDateTime> jsonTester = new JsonTester<>(type);
|
||||
jsonTester.test(now);
|
||||
|
||||
@@ -62,7 +62,7 @@ public class ScalarTypeZonedDateTimeTest {
|
||||
@Test
|
||||
public void testJson() throws Exception {
|
||||
|
||||
ZonedDateTime now = ZonedDateTime.now();
|
||||
ZonedDateTime now = ZonedDateTime.now().withNano(123_000_000); // jdk11 workaround
|
||||
|
||||
JsonTester<ZonedDateTime> jsonTester = new JsonTester<>(type);
|
||||
jsonTester.test(now);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.tests.basic;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.Query;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Order;
|
||||
@@ -9,6 +9,8 @@ import org.tests.model.basic.ResetBasicData;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestQueryWhereInRange extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
@@ -18,14 +20,77 @@ public class TestQueryWhereInRange extends BaseTestCase {
|
||||
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
Query<Order> query= Ebean.find(Order.class)
|
||||
Query<Order> query = DB.find(Order.class)
|
||||
.where().inRange("orderDate", today.minusDays(7), today)
|
||||
.isNotNull("id")
|
||||
.query();
|
||||
|
||||
query.findList();
|
||||
|
||||
sqlOf(query).contains("(order_date >= ? and order_date < ?)");
|
||||
assertThat(sqlOf(query)).contains("where (t0.order_date >= ? and t0.order_date < ?) and ");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eqOrNull() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
Query<Order> query = DB.find(Order.class)
|
||||
.where().eqOrNull("orderDate", today)
|
||||
.query();
|
||||
|
||||
query.findList();
|
||||
|
||||
assertThat(query.getGeneratedSql()).contains("where (t0.order_date = ? or t0.order_date is null)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gtOrNull() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
Query<Order> query = DB.find(Order.class)
|
||||
.where().gtOrNull("orderDate", today)
|
||||
.query();
|
||||
|
||||
query.findList();
|
||||
|
||||
assertThat(query.getGeneratedSql()).contains("where (t0.order_date > ? or t0.order_date is null)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ltOrNull() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
Query<Order> query = DB.find(Order.class)
|
||||
.where().ltOrNull("orderDate", today)
|
||||
.query();
|
||||
|
||||
query.findList();
|
||||
|
||||
assertThat(query.getGeneratedSql()).contains(" where (t0.order_date < ? or t0.order_date is null)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inRangeWith() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
Query<Order> query = DB.find(Order.class)
|
||||
.where().inRangeWith("orderDate", "shipDate", today)
|
||||
.query();
|
||||
|
||||
query.findList();
|
||||
|
||||
assertThat(query.getGeneratedSql()).contains("(t0.order_date <= ? and (t0.ship_date > ? or t0.ship_date is null))");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +1,153 @@
|
||||
package org.tests.basic.type;
|
||||
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.TransactionalTestCase;
|
||||
|
||||
import org.tests.model.basic.EWithInetAddr;
|
||||
import org.junit.Assert;
|
||||
import io.ebean.types.Cdir;
|
||||
import io.ebean.types.Inet;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.EWithInetAddr;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class TestInetAddressType extends TransactionalTestCase {
|
||||
|
||||
@Test
|
||||
public void testIp4() throws UnknownHostException {
|
||||
|
||||
insertUpdateDeleteFind("120.12.12.56");
|
||||
insertUpdateDeleteFind("120.12.12.56", "120.12.12.56");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIp6() throws UnknownHostException {
|
||||
|
||||
insertUpdateDeleteFind("2001:db8:85a3:0:0:8a2e:370:7334");
|
||||
if (isPostgres()) {
|
||||
insertUpdateDeleteFind("2001:db8:85a3:0:0:8a2e:370:7334", "2001:db8:85a3::8a2e:370:7334");
|
||||
} else {
|
||||
insertUpdateDeleteFind("2001:db8:85a3:0:0:8a2e:370:7334", "2001:db8:85a3:0:0:8a2e:370:7334");
|
||||
}
|
||||
}
|
||||
|
||||
private void insertUpdateDeleteFind(String ipAddress) throws UnknownHostException {
|
||||
@Test
|
||||
public void test_inet_queryIn() {
|
||||
|
||||
List<Inet> addrs = Inet.listOf("120.12.12.56", "120.12.12.57");
|
||||
|
||||
DB.find(EWithInetAddr.class)
|
||||
.where()
|
||||
.in("inet2", addrs)
|
||||
.findList();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_inetAdress_queryIn() throws UnknownHostException {
|
||||
|
||||
List<InetAddress> addrs = new ArrayList<>();
|
||||
addrs.add(InetAddress.getByName("120.12.12.56"));
|
||||
addrs.add(InetAddress.getByName("120.12.12.57"));
|
||||
|
||||
DB.find(EWithInetAddr.class)
|
||||
.where()
|
||||
.in("inetAddress", addrs)
|
||||
.findList();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_inet_queryEq() {
|
||||
|
||||
DB.find(EWithInetAddr.class)
|
||||
.where()
|
||||
.eq("inet2", new Inet("120.12.12.58"))
|
||||
.findList();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_inet4address_queryEq() throws UnknownHostException {
|
||||
|
||||
DB.find(EWithInetAddr.class)
|
||||
.where()
|
||||
.eq("inetAddress", InetAddress.getByName("120.12.12.58"))
|
||||
.findList();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_inet6address_queryEq() throws UnknownHostException {
|
||||
|
||||
DB.find(EWithInetAddr.class)
|
||||
.where()
|
||||
.eq("inetAddress", InetAddress.getByName("2001:db8:85a3:0:0:8a2e:370:7334"))
|
||||
.findList();
|
||||
}
|
||||
|
||||
private void insertUpdateDeleteFind(String ipAddress, String expected) throws UnknownHostException {
|
||||
|
||||
EWithInetAddr bean1 = new EWithInetAddr();
|
||||
bean1.setName("jim");
|
||||
|
||||
InetAddress address1 = InetAddress.getByName(ipAddress);
|
||||
bean1.setInetAddress(address1);
|
||||
bean1.setInet2(new Inet(ipAddress));
|
||||
bean1.setCdir(new Cdir(ipAddress));
|
||||
|
||||
Ebean.save(bean1);
|
||||
|
||||
EWithInetAddr bean2 = Ebean.find(EWithInetAddr.class, bean1.getId());
|
||||
DB.save(bean1);
|
||||
|
||||
EWithInetAddr bean2 = DB.find(EWithInetAddr.class, bean1.getId());
|
||||
InetAddress address2 = bean2.getInetAddress();
|
||||
Assert.assertNotNull(address2.getHostAddress());
|
||||
Assert.assertEquals(address1.getHostAddress(), address2.getHostAddress());
|
||||
assertNotNull(address2.getHostAddress());
|
||||
assertThat(address1.getHostAddress()).isEqualTo(address2.getHostAddress());
|
||||
assertThat(bean2.getInet2().getAddress()).isEqualTo(expected);
|
||||
assertThat(bean2.getCdir().getAddress()).isEqualTo(expected);
|
||||
|
||||
bean2.setName("modJim");
|
||||
bean2.setInetAddress(InetAddress.getByName("120.12.20.80"));
|
||||
Ebean.save(bean2);
|
||||
Ebean.delete(bean2);
|
||||
bean1.setInet2(new Inet("120.12.20.80"));
|
||||
bean1.setCdir(new Cdir("120.12.20.80"));
|
||||
DB.save(bean2);
|
||||
DB.delete(bean2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void use_null() throws UnknownHostException {
|
||||
public void testIp6_ranges() {
|
||||
|
||||
insertFindDeleteRange("2001:4f8:3:ba::/64");
|
||||
insertFindDeleteRange("2001:4f8:3:ba:2e0:81ff:fe22:d1f1/64");
|
||||
}
|
||||
|
||||
private void insertFindDeleteRange(String ipAddressRange) {
|
||||
|
||||
EWithInetAddr bean1 = new EWithInetAddr();
|
||||
bean1.setName("withRange");
|
||||
bean1.setInet2(new Inet(ipAddressRange));
|
||||
bean1.setCdir(new Cdir(ipAddressRange));
|
||||
|
||||
DB.save(bean1);
|
||||
|
||||
EWithInetAddr bean2 = DB.find(EWithInetAddr.class, bean1.getId());
|
||||
assertNotNull(bean2.getInet2());
|
||||
assertThat(bean2.getInet2().getAddress()).isEqualTo(ipAddressRange);
|
||||
assertThat(bean2.getCdir().getAddress()).isEqualTo(ipAddressRange);
|
||||
|
||||
DB.delete(bean2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void use_null() {
|
||||
|
||||
EWithInetAddr bean1 = new EWithInetAddr();
|
||||
bean1.setName("jim");
|
||||
|
||||
Ebean.save(bean1);
|
||||
DB.save(bean1);
|
||||
|
||||
EWithInetAddr bean2 = Ebean.find(EWithInetAddr.class, bean1.getId());
|
||||
InetAddress address2 = bean2.getInetAddress();
|
||||
Assert.assertNull(address2);
|
||||
EWithInetAddr bean2 = DB.find(EWithInetAddr.class, bean1.getId());
|
||||
assertNull(bean2.getInetAddress());
|
||||
assertNull(bean2.getInet2());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -57,5 +57,11 @@ public class TestSqlRowUUID extends BaseTestCase {
|
||||
.findList();
|
||||
|
||||
assertThat(result).hasSize(1);
|
||||
|
||||
List<TUuidEntity> list = Ebean.find(TUuidEntity.class)
|
||||
.where().rawOrEmpty("id = any(?::uuid[])", ids)
|
||||
.findList();
|
||||
|
||||
assertThat(list).hasSize(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.tests.insert;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.plugin.Property;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -14,80 +16,74 @@ public class TestInsertCheckUnique extends BaseTestCase {
|
||||
|
||||
@Before
|
||||
public void clearDb() {
|
||||
server().find(Document.class).asDraft().where().contains("title", "UniqueKey").delete();
|
||||
server().find(Document.class).asDraft().where().isNull("title").delete();
|
||||
DB.find(Document.class).asDraft().where().contains("title", "UniqueKey").delete();
|
||||
DB.find(Document.class).asDraft().where().isNull("title").delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insert_duplicateKey() {
|
||||
server().beginTransaction();
|
||||
try {
|
||||
|
||||
try (Transaction transaction = DB.beginTransaction()) {
|
||||
Document doc1 = new Document();
|
||||
doc1.setTitle("ThisIsAUniqueKey");
|
||||
doc1.setTitle("AUniqueKey_duplicateCheck");
|
||||
doc1.setBody("one");
|
||||
|
||||
assertThat(server().checkUniqueness(doc1)).isEmpty();
|
||||
assertThat(DB.checkUniqueness(doc1)).isEmpty();
|
||||
doc1.save();
|
||||
|
||||
Document doc2 = new Document();
|
||||
doc2.setTitle("ThisIsAUniqueKey");
|
||||
doc2.setTitle("AUniqueKey_duplicateCheck");
|
||||
doc2.setBody("clashes with doc1");
|
||||
|
||||
assertThat(server().checkUniqueness(doc2)).isEmpty();
|
||||
assertThat(DB.checkUniqueness(doc2)).isEmpty();
|
||||
|
||||
server().publish(doc1.getClass(), doc1.getId());
|
||||
DB.getDefault().publish(doc1.getClass(), doc1.getId());
|
||||
|
||||
assertThat(server().checkUniqueness(doc2).toString()).contains("title");
|
||||
} finally {
|
||||
server().endTransaction();
|
||||
assertThat(DB.checkUniqueness(doc2).toString()).contains("title");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insert_duplicateNull() {
|
||||
server().beginTransaction();
|
||||
try {
|
||||
try (Transaction transaction = DB.beginTransaction()) {
|
||||
Document doc1 = new Document();
|
||||
doc1.setTitle(null);
|
||||
doc1.setBody("one");
|
||||
|
||||
assertThat(server().checkUniqueness(doc1)).isEmpty();
|
||||
assertThat(DB.checkUniqueness(doc1)).isEmpty();
|
||||
doc1.save();
|
||||
|
||||
Document doc2 = new Document();
|
||||
doc2.setTitle(null);
|
||||
doc2.setBody("clashes with doc1");
|
||||
|
||||
assertThat(server().checkUniqueness(doc2)).isEmpty();
|
||||
assertThat(DB.checkUniqueness(doc2)).isEmpty();
|
||||
|
||||
server().publish(doc1.getClass(), doc1.getId());
|
||||
DB.getDefault().publish(doc1.getClass(), doc1.getId());
|
||||
|
||||
assertThat(server().checkUniqueness(doc2)).isEmpty();
|
||||
assertThat(DB.checkUniqueness(doc2)).isEmpty();
|
||||
|
||||
doc2.save();
|
||||
server().publish(doc2.getClass(), doc2.getId());
|
||||
} finally {
|
||||
server().endTransaction();
|
||||
DB.getDefault().publish(doc2.getClass(), doc2.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void example() {
|
||||
|
||||
server().beginTransaction();
|
||||
try {
|
||||
try (Transaction transaction = DB.beginTransaction()) {
|
||||
Document doc1 = new Document();
|
||||
doc1.setTitle("One flew over the cuckoo's nest");
|
||||
doc1.setBody("one");
|
||||
doc1.save();
|
||||
|
||||
server().publish(doc1.getClass(), doc1.getId());
|
||||
DB.getDefault().publish(doc1.getClass(), doc1.getId());
|
||||
|
||||
Document doc2 = new Document();
|
||||
doc2.setTitle("One flew over the cuckoo's nest");
|
||||
doc2.setBody("clashes with doc1");
|
||||
|
||||
Set<Property> properties = server().checkUniqueness(doc2);
|
||||
Set<Property> properties = DB.checkUniqueness(doc2);
|
||||
if (properties.isEmpty()) {
|
||||
// it is unique ... carry on
|
||||
} else {
|
||||
@@ -110,9 +106,7 @@ public class TestInsertCheckUnique extends BaseTestCase {
|
||||
}
|
||||
|
||||
|
||||
assertThat(server().checkUniqueness(doc2).toString()).contains("title");
|
||||
} finally {
|
||||
server().endTransaction();
|
||||
assertThat(DB.checkUniqueness(doc2).toString()).contains("title");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package org.tests.model.basic;
|
||||
|
||||
import io.ebean.types.Cdir;
|
||||
import io.ebean.types.Inet;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
@@ -20,6 +23,10 @@ public class EWithInetAddr {
|
||||
|
||||
InetAddress inetAddress;
|
||||
|
||||
Inet inet2;
|
||||
|
||||
Cdir cdir;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
@@ -52,4 +59,19 @@ public class EWithInetAddr {
|
||||
this.inetAddress = inetAddress;
|
||||
}
|
||||
|
||||
public Inet getInet2() {
|
||||
return inet2;
|
||||
}
|
||||
|
||||
public void setInet2(Inet inet2) {
|
||||
this.inet2 = inet2;
|
||||
}
|
||||
|
||||
public Cdir getCdir() {
|
||||
return cdir;
|
||||
}
|
||||
|
||||
public void setCdir(Cdir cdir) {
|
||||
this.cdir = cdir;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ public class TestElementCollectionBasic extends BaseTestCase {
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
eventLog();
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
EcPerson person = new EcPerson("Fiona021");
|
||||
|
||||
@@ -151,10 +151,11 @@ public class TestOuterJoin extends BaseTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOuterOnFetch() throws Exception {
|
||||
public void testOuterOnFetch() {
|
||||
|
||||
LoggedSql.start();
|
||||
|
||||
List<Order> orders1 = Ebean.find(Order.class).findList();
|
||||
List<Order> orders1 = Ebean.find(Order.class).orderBy("id").findList();
|
||||
|
||||
assertThat(LoggedSql.collect().get(0))
|
||||
.contains(" join o_customer") // ensure that we do not left join the customer
|
||||
@@ -165,7 +166,7 @@ public class TestOuterJoin extends BaseTestCase {
|
||||
LoggedSql.start();
|
||||
|
||||
List<Order> orders2 = Ebean.find(Order.class)
|
||||
.fetch("details", "id").findList();
|
||||
.fetch("details", "id").orderBy("id").findList();
|
||||
|
||||
assertThat(LoggedSql.collect().get(0))
|
||||
.contains(" left join o_order_detail ");
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package org.tests.query;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.Query;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.CKeyParent;
|
||||
import org.tests.model.basic.Order;
|
||||
import org.tests.model.basic.ResetBasicData;
|
||||
import org.tests.model.basic.Vehicle;
|
||||
import org.tests.model.basic.VehicleDriver;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -27,10 +27,10 @@ public class TestSubQuery extends BaseTestCase {
|
||||
productIds.add(4);
|
||||
productIds.add(5);
|
||||
|
||||
Query<Order> sq = Ebean.createQuery(Order.class).select("id").where()
|
||||
Query<Order> sq = DB.find(Order.class).select("id").where()
|
||||
.in("details.product.id", productIds).query();
|
||||
|
||||
Ebean.find(Order.class).where().in("id", sq).findList();
|
||||
DB.find(Order.class).where().in("id", sq).findList();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -41,19 +41,19 @@ public class TestSubQuery extends BaseTestCase {
|
||||
List<Integer> productIds = new ArrayList<>();
|
||||
productIds.add(3);
|
||||
|
||||
Query<Order> sq = Ebean.createQuery(Order.class).select("id").where()
|
||||
Query<Order> sq = DB.createQuery(Order.class).select("id").where()
|
||||
.isIn("details.product.id", productIds).query();
|
||||
|
||||
Ebean.find(Order.class).where().isIn("id", sq).findList();
|
||||
DB.find(Order.class).where().isIn("id", sq).findList();
|
||||
}
|
||||
|
||||
public void testCompositeKey() {
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<CKeyParent> sq = Ebean.createQuery(CKeyParent.class).select("id.oneKey")
|
||||
Query<CKeyParent> sq = DB.createQuery(CKeyParent.class).select("id.oneKey")
|
||||
.setAutoTune(false).where().query();
|
||||
|
||||
Query<CKeyParent> pq = Ebean.find(CKeyParent.class).where().in("id.oneKey", sq).query();
|
||||
Query<CKeyParent> pq = DB.find(CKeyParent.class).where().in("id.oneKey", sq).query();
|
||||
|
||||
pq.findList();
|
||||
|
||||
@@ -93,10 +93,10 @@ public class TestSubQuery extends BaseTestCase {
|
||||
public void testInheritance2() {
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<VehicleDriver> sq = Ebean.createQuery(VehicleDriver.class).select("vehicle")
|
||||
Query<VehicleDriver> sq = DB.createQuery(VehicleDriver.class).select("vehicle")
|
||||
.setAutoTune(false).where().query();
|
||||
|
||||
Query<Vehicle> pq = Ebean.find(Vehicle.class).where().in("id", sq).query();
|
||||
Query<Vehicle> pq = DB.find(Vehicle.class).where().in("id", sq).query();
|
||||
|
||||
pq.findList();
|
||||
|
||||
@@ -118,10 +118,10 @@ public class TestSubQuery extends BaseTestCase {
|
||||
public void testInheritance3() {
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<VehicleDriver> sq = Ebean.createQuery(VehicleDriver.class).select("vehicle")
|
||||
Query<VehicleDriver> sq = DB.createQuery(VehicleDriver.class).select("vehicle")
|
||||
.setAutoTune(false).where().eq("vehicle.licenseNumber", "abc").query();
|
||||
|
||||
Query<Vehicle> pq = Ebean.find(Vehicle.class).where().in("id", sq).query();
|
||||
Query<Vehicle> pq = DB.find(Vehicle.class).where().in("id", sq).query();
|
||||
|
||||
pq.findList();
|
||||
|
||||
@@ -139,10 +139,10 @@ public class TestSubQuery extends BaseTestCase {
|
||||
public void testInheritance4() {
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<VehicleDriver> sq = Ebean.createQuery(VehicleDriver.class).select("vehicle.id")
|
||||
Query<VehicleDriver> sq = DB.createQuery(VehicleDriver.class).select("vehicle.id")
|
||||
.setAutoTune(false).where().query();
|
||||
|
||||
Query<Vehicle> pq = Ebean.find(Vehicle.class).where().in("id", sq).query();
|
||||
Query<Vehicle> pq = DB.find(Vehicle.class).where().in("id", sq).query();
|
||||
|
||||
pq.findList();
|
||||
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
package org.tests.query;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.Query;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Country;
|
||||
import org.tests.model.basic.Order;
|
||||
import org.tests.model.basic.ResetBasicData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
public class TestWhereIn extends BaseTestCase {
|
||||
|
||||
@@ -15,25 +22,123 @@ public class TestWhereIn extends BaseTestCase {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Country> query = Ebean.find(Country.class)
|
||||
Query<Country> query = DB.find(Country.class)
|
||||
.where().in("code", "NZ", "AU")
|
||||
.query();
|
||||
|
||||
query.findList();
|
||||
platformAssertIn(sqlOf(query), "");
|
||||
platformAssertIn(sqlOf(query), "where t0.code");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNotInVarchar() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Country> query = Ebean.find(Country.class)
|
||||
Query<Country> query = DB.find(Country.class)
|
||||
.where().notIn("code", "NZ", "SA", "US")
|
||||
.query();
|
||||
|
||||
query.findList();
|
||||
platformAssertNotIn(sqlOf(query), "");
|
||||
platformAssertNotIn(sqlOf(query), "where t0.code");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInOrEmpty_expect_noJoinWhenEmpty() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Order> query = DB.find(Order.class)
|
||||
.select("id")
|
||||
.where().inOrEmpty("customer.billingAddress.id", new ArrayList<>()).query();
|
||||
|
||||
query.findList();
|
||||
assertThat(sqlOf(query)).isEqualTo("select t0.id from o_order t0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInOrEmpty_expect_joinWhenNotEmpty() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Order> query = DB.find(Order.class)
|
||||
.select("id")
|
||||
.where().inOrEmpty("customer.billingAddress.id", Arrays.asList(1)).query();
|
||||
|
||||
query.findList();
|
||||
assertThat(sqlOf(query)).contains("select t0.id from o_order t0 join o_customer t1 on t1.id = t0.kcustomer_id where t1.billing_address_id ");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testInOrEmpty_when_null() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Country> query = DB.find(Country.class)
|
||||
.where().inOrEmpty("code", null).query();
|
||||
|
||||
query.findList();
|
||||
assertThat(sqlOf(query)).isEqualTo("select t0.code, t0.name from o_country t0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInOrEmpty_when_empty() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Country> query = DB.find(Country.class)
|
||||
.where().inOrEmpty("code", new ArrayList<>()).query();
|
||||
|
||||
query.findList();
|
||||
assertThat(sqlOf(query)).isEqualTo("select t0.code, t0.name from o_country t0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIn_when_empty() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Country> query = DB.find(Country.class)
|
||||
.where().in("code", new ArrayList<>()).query();
|
||||
|
||||
query.findList();
|
||||
assertThat(sqlOf(query)).contains("where 1=0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIn_when_null() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Country> query = DB.find(Country.class)
|
||||
.where().in("code", (Collection)null).query();
|
||||
|
||||
query.findList();
|
||||
assertThat(sqlOf(query)).contains("where 1=0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotIn_when_empty() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Country> query = DB.find(Country.class)
|
||||
.where().notIn("code", new ArrayList<>()).query();
|
||||
|
||||
query.findList();
|
||||
assertThat(sqlOf(query)).contains("where 1=1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotIn_when_null() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Country> query = DB.find(Country.class)
|
||||
.where().notIn("code", (Collection)null).query();
|
||||
|
||||
query.findList();
|
||||
assertThat(sqlOf(query)).contains("where 1=1");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.ebean.Expr;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.annotation.ForPlatform;
|
||||
import io.ebean.annotation.Platform;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Customer;
|
||||
import org.tests.model.basic.Order;
|
||||
@@ -13,6 +14,7 @@ import org.tests.model.basic.OrderDetail;
|
||||
import org.tests.model.basic.ResetBasicData;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
@@ -65,6 +67,56 @@ public class TestWhereRawClause extends BaseTestCase {
|
||||
assertThat(sqlOf(query)).contains(" t0.id in (select c.id from o_customer c where c.name in (?,?,?))");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRawOrEmpty_when_notEmpty() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
List<String> vals = asList("Rob", "Fiona", "Jack");
|
||||
|
||||
Query<Customer> query = Ebean.find(Customer.class)
|
||||
.select("name")
|
||||
.where()
|
||||
.rawOrEmpty("id in (select c.id from o_customer c where c.name in (?1))", vals)
|
||||
.query();
|
||||
|
||||
List<Customer> list = query.findList();
|
||||
assertThat(list).isNotEmpty();
|
||||
assertThat(sqlOf(query)).contains(" t0.id in (select c.id from o_customer c where c.name in (?,?,?))");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRawOrEmpty_when_null() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Customer> query = Ebean.find(Customer.class)
|
||||
.select("name")
|
||||
.where()
|
||||
.rawOrEmpty("id in (select c.id from o_customer c where c.name in (?1))", null)
|
||||
.query();
|
||||
|
||||
List<Customer> list = query.findList();
|
||||
assertThat(list).isNotEmpty();
|
||||
assertThat(sqlOf(query)).isEqualTo("select t0.id, t0.name from o_customer t0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRawOrEmpty_when_empty() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Customer> query = Ebean.find(Customer.class)
|
||||
.select("name")
|
||||
.where()
|
||||
.rawOrEmpty("id in (select c.id from o_customer c where c.name in (?1))", Collections.emptySet())
|
||||
.query();
|
||||
|
||||
List<Customer> list = query.findList();
|
||||
assertThat(list).isNotEmpty();
|
||||
assertThat(sqlOf(query)).isEqualTo("select t0.id, t0.name from o_customer t0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRaw_bindExpansion() {
|
||||
|
||||
@@ -94,6 +146,33 @@ public class TestWhereRawClause extends BaseTestCase {
|
||||
assertThat(list).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ForPlatform(Platform.POSTGRES)
|
||||
public void testRawOrEmpty_PostgresArray() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Ebean.find(Customer.class)
|
||||
.select("name").where().rawOrEmpty("name = any(?)", asList("Rob", "Fiona", "Jack"))
|
||||
.findList();
|
||||
|
||||
Ebean.find(Customer.class)
|
||||
.select("name").where().rawOrEmpty("name = any(?)", asList())
|
||||
.findList();
|
||||
|
||||
Ebean.find(Customer.class)
|
||||
.select("name").where().rawOrEmpty("name = any(?)", null)
|
||||
.findList();
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
|
||||
assertThat(sql.get(0)).isEqualTo("select t0.id, t0.name from o_customer t0 where t0.name = any(?); --bind(Array[3]={Rob,Fiona,Jack})");
|
||||
assertThat(sql.get(1)).isEqualTo("select t0.id, t0.name from o_customer t0; --bind()");
|
||||
assertThat(sql.get(2)).isEqualTo("select t0.id, t0.name from o_customer t0; --bind()");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRawWithBindParams() {
|
||||
|
||||
|
||||
@@ -6,11 +6,13 @@ import io.ebean.Ebean;
|
||||
import io.ebean.Transaction;
|
||||
import org.junit.Test;
|
||||
import org.tests.sp.model.car.Car;
|
||||
import org.tests.sp.model.car.Door;
|
||||
import org.tests.sp.model.car.Wheel;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
@@ -24,15 +26,24 @@ public class TestManyToManySaveTwice extends BaseTestCase {
|
||||
Wheel w0 = new Wheel("wx0");
|
||||
Wheel w1 = new Wheel("wx1");
|
||||
|
||||
Door d0 = new Door("dx0");
|
||||
Door d1 = new Door("dx1");
|
||||
|
||||
List<Wheel> wheels = new LinkedList<>();
|
||||
wheels.add(w0);
|
||||
wheels.add(w1);
|
||||
|
||||
List<Door> doors = new LinkedList<>();
|
||||
doors.add(d0);
|
||||
doors.add(d1);
|
||||
|
||||
DB.saveAll(wheels);
|
||||
DB.saveAll(doors);
|
||||
|
||||
Car c0 = new Car("cx0");
|
||||
c0.getWheels().add(w0);
|
||||
c0.getWheels().add(w1);
|
||||
c0.getDoors().add(d0);
|
||||
|
||||
Car c1 = new Car("cx1");
|
||||
c1.getWheels().add(w1);
|
||||
@@ -45,6 +56,16 @@ public class TestManyToManySaveTwice extends BaseTestCase {
|
||||
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
c0 = Ebean.find(Car.class, c0.getId());
|
||||
c1 = Ebean.find(Car.class, c1.getId());
|
||||
|
||||
assertThat(c0.getWheels()).hasSize(2);
|
||||
assertThat(c0.getDoors()).hasSize(1);
|
||||
|
||||
assertThat(c1.getWheels()).hasSize(1);
|
||||
assertThat(c1.getDoors()).hasSize(0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +122,9 @@ public class TestManyToManySaveTwice extends BaseTestCase {
|
||||
|
||||
private void delete() {
|
||||
DB.sqlUpdate("delete from sp_car_car_wheels").execute();
|
||||
DB.sqlUpdate("delete from sp_car_car_doors").execute();
|
||||
DB.sqlUpdate("delete from sp_car_wheel").execute();
|
||||
DB.sqlUpdate("delete from sp_car_door").execute();
|
||||
DB.sqlUpdate("delete from sp_car_car").execute();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ public class Car extends IdEntity {
|
||||
@JoinTable(name = "sp_car_car_wheels", joinColumns = {@JoinColumn(name = "car")}, inverseJoinColumns = {@JoinColumn(name = "wheel")})
|
||||
private List<Wheel> wheels;
|
||||
|
||||
@ManyToMany(cascade = CascadeType.ALL)
|
||||
@JoinTable(name = "sp_car_car_doors", joinColumns = {@JoinColumn(name = "car")}, inverseJoinColumns = {@JoinColumn(name = "door")})
|
||||
private List<Door> doors;
|
||||
|
||||
public Car(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
@@ -37,4 +41,12 @@ public class Car extends IdEntity {
|
||||
public void setWheels(List<Wheel> wheels) {
|
||||
this.wheels = wheels;
|
||||
}
|
||||
|
||||
public List<Door> getDoors() {
|
||||
return doors;
|
||||
}
|
||||
|
||||
public void setDoors(List<Door> doors) {
|
||||
this.doors = doors;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.tests.sp.model.car;
|
||||
|
||||
import org.tests.sp.model.IdEntity;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "sp_car_door")
|
||||
public class Door extends IdEntity {
|
||||
|
||||
private static final long serialVersionUID = 2399600193947163469L;
|
||||
|
||||
private String name;
|
||||
|
||||
public Door(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,19 @@
|
||||
package org.tests.transaction;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.annotation.Transactional;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.m2m.MnyB;
|
||||
import org.tests.model.m2m.MnyTopic;
|
||||
import org.tests.model.m2m.Role;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestBatchModelFlush extends BaseTestCase {
|
||||
|
||||
@@ -30,4 +39,59 @@ public class TestBatchModelFlush extends BaseTestCase {
|
||||
// the rest is flushed on commit
|
||||
new MnyB("TestBatchModelFlush_5").save();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleTopLevel_expect_singleFlush() {
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
// 2 unrelated "top level" beans being persisted
|
||||
MnyB m0 = new MnyB("BatchMultipleTop_0");
|
||||
MnyB m1 = new MnyB("BatchMultipleTop_1");
|
||||
Role r0 = new Role("Role_0");
|
||||
Role r1 = new Role("Role_1");
|
||||
|
||||
MnyTopic t0 = new MnyTopic("MnyTopic_0");
|
||||
MnyTopic t1 = new MnyTopic("MnyTopic_1");
|
||||
|
||||
try (Transaction transaction = DB.beginTransaction()) {
|
||||
transaction.setBatchMode(true);
|
||||
|
||||
m0.save();
|
||||
DB.save(r0);
|
||||
DB.save(t0);
|
||||
DB.save(t1);
|
||||
|
||||
m1.save();
|
||||
DB.save(r1);
|
||||
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
|
||||
// DEBUG io.ebean.SUM - txn[1001] BatchControl flush [MnyB:100 i:2, Role:101 i:2, MnyTopic:102 i:2]
|
||||
|
||||
assertThat(sql).hasSize(9);
|
||||
|
||||
// first saved to batch - (depth 100)
|
||||
assertThat(sql.get(0)).contains("insert into mny_b");
|
||||
assertThat(sql.get(1)).contains(" -- bind(BatchMultipleTop_0");
|
||||
assertThat(sql.get(2)).contains(" -- bind(BatchMultipleTop_1");
|
||||
// second saved to batch - (depth 101)
|
||||
assertThat(sql.get(3)).contains("insert into mt_role");
|
||||
assertThat(sql.get(4)).contains(" -- bind(");
|
||||
assertThat(sql.get(5)).contains(" -- bind(");
|
||||
// third saved to batch - (depth 102)
|
||||
assertThat(sql.get(6)).contains("insert into mny_topic");
|
||||
assertThat(sql.get(7)).contains(" -- bind(MnyTopic_0");
|
||||
assertThat(sql.get(8)).contains(" -- bind(MnyTopic_1");
|
||||
|
||||
DB.delete(t0);
|
||||
DB.delete(t1);
|
||||
DB.delete(r0);
|
||||
DB.delete(r1);
|
||||
DB.delete(m0);
|
||||
DB.delete(m1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user