mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b05d2343b | ||
|
|
fc748ea9c1 | ||
|
|
7b8d2a2f9d | ||
|
|
f8b5a7f312 | ||
|
|
ae261d3782 | ||
|
|
d2ae044d1f | ||
|
|
03189ac92a | ||
|
|
2067568a5e | ||
|
|
57417610a6 | ||
|
|
4cec878242 | ||
|
|
2e7aec3b3d | ||
|
|
104dc599e8 | ||
|
|
e769abf737 | ||
|
|
b79bb892f7 | ||
|
|
8f93f780fd | ||
|
|
158a23a34d | ||
|
|
523ddbab63 | ||
|
|
00d4298065 | ||
|
|
4eb1aded2d | ||
|
|
221f5ad879 | ||
|
|
75e3b9276e | ||
|
|
29c51ecb95 | ||
|
|
8c535ba837 |
@@ -3,13 +3,13 @@
|
||||
|
||||
<parent>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-javaparent</artifactId>
|
||||
<version>1.2</version>
|
||||
<artifactId>oss-parent</artifactId>
|
||||
<version>1.1</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>7.7.1</version>
|
||||
<version>7.8.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>avaje-ebeanorm</name>
|
||||
@@ -36,9 +36,8 @@
|
||||
</developers>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:https://github.com/ebean-orm/avaje-ebeanorm.git</connection>
|
||||
<developerConnection>scm:git:https://github.com/ebean-orm/avaje-ebeanorm.git</developerConnection>
|
||||
<url>https://github.com/ebean-orm/avaje-ebeanorm.git</url>
|
||||
<tag>avaje-ebeanorm-7.8.1</tag>
|
||||
</scm>
|
||||
|
||||
<dependencies>
|
||||
@@ -64,14 +63,7 @@
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-datasource</artifactId>
|
||||
<version>1.1.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- bring in cluster support by default -->
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-cluster</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<version>1.1.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -92,12 +84,11 @@
|
||||
<version>[1.7.1,1.7.99)</version>
|
||||
</dependency>
|
||||
|
||||
<!-- provided scope for Spring boot loader classpath scanning support -->
|
||||
<!-- Jackson core used internally by Ebean -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-loader</artifactId>
|
||||
<version>1.2.7.RELEASE</version>
|
||||
<scope>provided</scope>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>2.6.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- provided scope for JsonNode support -->
|
||||
@@ -108,13 +99,6 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Jackson core used internally by Ebean -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>2.6.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.transaction</groupId>
|
||||
<artifactId>jta</artifactId>
|
||||
@@ -152,6 +136,8 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Test scope -->
|
||||
|
||||
<!--<dependency>-->
|
||||
<!--<groupId>oracle</groupId>-->
|
||||
<!--<artifactId>oracle-jdbc</artifactId>-->
|
||||
@@ -228,9 +214,9 @@
|
||||
</dependencies>
|
||||
|
||||
|
||||
<!-- Enhance the meta beans -->
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
@@ -240,12 +226,12 @@
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
|
||||
<version>4.9.2</version>
|
||||
<executions>
|
||||
<!-- Not going to enhance Model bean -->
|
||||
<execution>
|
||||
<id>test</id>
|
||||
<phase>process-test-classes</phase>
|
||||
@@ -282,7 +268,8 @@
|
||||
</systemProperties>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.5</version>
|
||||
@@ -292,6 +279,7 @@
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
@@ -317,7 +305,6 @@
|
||||
<linksource>true</linksource>
|
||||
<overview>src/main/java/com/avaje/ebean/overview.html</overview>
|
||||
</configuration>
|
||||
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
@@ -327,6 +314,7 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
|
||||
@@ -728,11 +728,25 @@ public final class Ebean {
|
||||
return serverMgr.getDefaultServer().delete(beanType, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete permanent the bean given its type and id.
|
||||
*/
|
||||
public static int deletePermanent(Class<?> beanType, Object id) {
|
||||
return serverMgr.getDefaultServer().deletePermanent(beanType, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete several beans given their type and id values.
|
||||
*/
|
||||
public static void deleteAll(Class<?> beanType, Collection<?> ids) {
|
||||
serverMgr.getDefaultServer().deleteAll(beanType, ids);
|
||||
public static int deleteAll(Class<?> beanType, Collection<?> ids) {
|
||||
return serverMgr.getDefaultServer().deleteAll(beanType, ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete permanent several beans given their type and id values.
|
||||
*/
|
||||
public static int deleteAllPermanent(Class<?> beanType, Collection<?> ids) {
|
||||
return serverMgr.getDefaultServer().deleteAllPermanent(beanType, ids);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.annotation.CacheStrategy;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.meta.MetaInfoManager;
|
||||
@@ -1351,6 +1350,16 @@ public interface EbeanServer {
|
||||
*/
|
||||
int delete(Class<?> beanType, Object id, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Delete permanent given the bean type and id.
|
||||
*/
|
||||
int deletePermanent(Class<?> beanType, Object id);
|
||||
|
||||
/**
|
||||
* Delete permanent given the bean type and id with an explicit transaction.
|
||||
*/
|
||||
int deletePermanent(Class<?> beanType, Object id, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Delete all the beans in the collection.
|
||||
*/
|
||||
@@ -1364,13 +1373,22 @@ public interface EbeanServer {
|
||||
/**
|
||||
* Delete several beans given their type and id values.
|
||||
*/
|
||||
void deleteAll(Class<?> beanType, Collection<?> ids);
|
||||
int deleteAll(Class<?> beanType, Collection<?> ids);
|
||||
|
||||
/**
|
||||
* Delete several beans given their type and id values with an explicit
|
||||
* transaction.
|
||||
* Delete several beans given their type and id values with an explicit transaction.
|
||||
*/
|
||||
void deleteAll(Class<?> beanType, Collection<?> ids, Transaction transaction);
|
||||
int deleteAll(Class<?> beanType, Collection<?> ids, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Delete permanent for several beans given their type and id values.
|
||||
*/
|
||||
int deleteAllPermanent(Class<?> beanType, Collection<?> ids);
|
||||
|
||||
/**
|
||||
* Delete permanent for several beans given their type and id values with an explicit transaction.
|
||||
*/
|
||||
int deleteAllPermanent(Class<?> beanType, Collection<?> ids, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute a Sql Update Delete or Insert statement. This returns the number of
|
||||
|
||||
@@ -221,24 +221,39 @@ public final class OrderBy<T> implements Serializable {
|
||||
|
||||
private boolean ascending;
|
||||
|
||||
private String nulls;
|
||||
|
||||
private String highLow;
|
||||
|
||||
public Property(String property, boolean ascending) {
|
||||
this.property = property;
|
||||
this.ascending = ascending;
|
||||
}
|
||||
|
||||
public Property(String property, boolean ascending, String nulls, String highLow) {
|
||||
this.property = property;
|
||||
this.ascending = ascending;
|
||||
this.nulls = nulls;
|
||||
this.highLow = highLow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this Property with the path trimmed.
|
||||
*/
|
||||
public Property copyWithTrim(String path) {
|
||||
return new Property(property.substring(path.length() + 1), ascending);
|
||||
return new Property(property.substring(path.length() + 1), ascending, nulls, highLow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hc = property.hashCode();
|
||||
hc = hc * 31 + (ascending ? 0 : 1);
|
||||
hc = hc * 31 + (nulls == null ? 0 : nulls.hashCode());
|
||||
hc = hc * 31 + (highLow == null ? 0 : highLow.hashCode());
|
||||
return hc;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
@@ -246,10 +261,11 @@ public final class OrderBy<T> implements Serializable {
|
||||
if (!(obj instanceof Property)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Property e = (Property) obj;
|
||||
return e.ascending == ascending
|
||||
&& e.property.equals(property);
|
||||
if (ascending != e.ascending) return false;
|
||||
if (!property.equals(e.property)) return false;
|
||||
if (nulls != null ? !nulls.equals(e.nulls) : e.nulls != null) return false;
|
||||
return highLow != null ? highLow.equals(e.highLow) : e.highLow == null;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
@@ -257,10 +273,20 @@ public final class OrderBy<T> implements Serializable {
|
||||
}
|
||||
|
||||
public String toStringFormat() {
|
||||
if (ascending) {
|
||||
return property;
|
||||
if (nulls == null) {
|
||||
if (ascending) {
|
||||
return property;
|
||||
} else {
|
||||
return property + " desc";
|
||||
}
|
||||
} else {
|
||||
return property + " desc";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(property);
|
||||
if (!ascending) {
|
||||
sb.append(" ").append("desc");
|
||||
}
|
||||
sb.append(" ").append(nulls).append(" ").append(highLow);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +308,7 @@ public final class OrderBy<T> implements Serializable {
|
||||
* Return a copy of this property.
|
||||
*/
|
||||
public Property copy() {
|
||||
return new Property(property, ascending);
|
||||
return new Property(property, ascending, nulls, highLow);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,7 +349,6 @@ public final class OrderBy<T> implements Serializable {
|
||||
|
||||
String[] chunks = orderByClause.split(",");
|
||||
for (int i = 0; i < chunks.length; i++) {
|
||||
|
||||
String[] pairs = chunks[i].split(" ");
|
||||
Property p = parseProperty(pairs);
|
||||
if (p != null) {
|
||||
@@ -353,8 +378,12 @@ public final class OrderBy<T> implements Serializable {
|
||||
boolean asc = isAscending(wordList.get(1));
|
||||
return new Property(wordList.get(0), asc);
|
||||
}
|
||||
String m = "Expecting a max of 2 words in [" + Arrays.toString(pairs)
|
||||
+ "] but got " + wordList.size();
|
||||
if (wordList.size() == 4) {
|
||||
// nulls high or nulls low as 3rd and 4th
|
||||
boolean asc = isAscending(wordList.get(1));
|
||||
return new Property(wordList.get(0), asc, wordList.get(2), wordList.get(3));
|
||||
}
|
||||
String m = "Expecting a 1, 2 or 4 words in [" + Arrays.toString(pairs) + "] but got " + wordList;
|
||||
throw new RuntimeException(m);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Deprecated in favor of @DocEmbedded (i.e. renamed to @DocEmbedded)
|
||||
*/
|
||||
@Target({ ElementType.FIELD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Deprecated
|
||||
public @interface DocStoreEmbedded {
|
||||
|
||||
/**
|
||||
* The properties on the embedded bean to include in the index.
|
||||
*/
|
||||
String doc() default "";
|
||||
|
||||
}
|
||||
@@ -6,19 +6,13 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specify explicit sql for multiple select statements. Need to use this if you
|
||||
* have more than one SqlSelect for a given bean.
|
||||
* Mark and entity bean that is used solely via RawSql.
|
||||
* <p>
|
||||
* FUTURE: Support explicit sql for SqlInsert, SqlUpdate and SqlDelete.
|
||||
* This means the entity bean has no base table specified (via @Table).
|
||||
* </p>
|
||||
*/
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Sql {
|
||||
|
||||
/**
|
||||
* The sql select statements.
|
||||
*/
|
||||
SqlSelect[] select() default { @SqlSelect };
|
||||
|
||||
}
|
||||
|
||||
@@ -1,298 +0,0 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.avaje.ebean.Query;
|
||||
|
||||
/**
|
||||
* Specify an explicit sql select statement to use for querying an entity bean.
|
||||
* <p>
|
||||
* The reason for using explicit sql is that you want better control over the
|
||||
* exact sql or sql that Ebean does not generate for you (such as group by,
|
||||
* union, intersection, window functions, recursive queries).
|
||||
* </p>
|
||||
* <p>
|
||||
* An example of two sql select queries deployed on the ReportTopic entity bean.
|
||||
* The first one has no name specified so it becomes the default query. The
|
||||
* second query extends the first adding a where clause with a named parameter.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* ...
|
||||
* @Entity
|
||||
* @Sql(select = {
|
||||
* @SqlSelect(query =
|
||||
* "select t.id, t.title, count(p.id) as score "+
|
||||
* "from f_topic t "+
|
||||
* "join f_topic_post p on p.topic_id = t.id "+
|
||||
* "group by t.id, t.title"),
|
||||
* @SqlSelect(
|
||||
* name = "with.title",
|
||||
* extend = "default",
|
||||
* debug = true,
|
||||
* where = "title like :likeTitle")
|
||||
* })
|
||||
* public class ReportTopic
|
||||
* @Id Integer id;
|
||||
* String title;
|
||||
* Double score;
|
||||
* ...
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* An example using the first "default" query.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
*
|
||||
* List<ReportTopic> list =
|
||||
* Ebean.find(ReportTopic.class)
|
||||
* .having().gt("score", 0)
|
||||
* .findList();
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* The resulting sql, note the having clause has been added.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* select t.id, t.title, count(p.id) as score
|
||||
* from f_topic t join f_topic_post p on p.topic_id = t.id
|
||||
* group by t.id, t.title
|
||||
* having count(p.id) > ?
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* An example using the second query. Note the named parameter "likeTitle" must
|
||||
* be set.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* List<ReportTopic> list =
|
||||
* Ebean.find(ReportTopic.class, "with.title")
|
||||
* .set("likeTitle", "a%")
|
||||
* .findList();
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Ebean tries to parse the sql in the query to determine 4 things
|
||||
* <li>Location for inserting WHERE expressions (if required)</li>
|
||||
* <li>Location for inserting HAVING expressions (if required)</li>
|
||||
* <li>Mapping of columns to bean properties</li>
|
||||
* <li>The order by clause</li>
|
||||
* </p>
|
||||
* <p>
|
||||
* If Ebean is unable to parse out this information (perhaps because the sql
|
||||
* contains multiple select from keywords etc) then you need to manually specify
|
||||
* it.
|
||||
* </p>
|
||||
* <p>
|
||||
* Insert ${where} or ${andWhere} into the location where Ebean can insert any
|
||||
* expressions added to the where clause. Use ${andWhere} if the sql already has
|
||||
* the WHERE keyword and Ebean will instead start with a AND keyword.
|
||||
* </p>
|
||||
* <p>
|
||||
* Insert ${having} or ${andHaving} into the location where Ebean can insert any
|
||||
* expressions added to the having clause. Use ${andHaving} if the sql already
|
||||
* has a HAVING keyword and Ebean will instead start with a AND keyword.
|
||||
* </p>
|
||||
* <p>
|
||||
* Use the columnMapping property if Ebean is unable to determine the columns
|
||||
* and map them to bean properties.
|
||||
* </p>
|
||||
* <p>
|
||||
* Example with ${andWhere} & ${having}.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* @SqlSelect(
|
||||
* name = "explicit.where",
|
||||
* query =
|
||||
* "select t.id, t.title, count(p.id) as score "+
|
||||
* "from f_topic t, f_topic_post p "+
|
||||
* "where p.topic_id = t.id ${andWhere} "+
|
||||
* "group by t.id, t.title ${having}"),
|
||||
* </pre>
|
||||
*/
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Deprecated
|
||||
public @interface SqlSelect {
|
||||
|
||||
/**
|
||||
* The name of the query. If left blank this is assumed to be the default
|
||||
* query for this bean type.
|
||||
* <p>
|
||||
* This will default to "default" and in that case becomes the default query
|
||||
* used for the bean.
|
||||
* </p>
|
||||
*/
|
||||
String name() default "default";
|
||||
|
||||
/**
|
||||
* The tableAlias used when adding where expressions to the query.
|
||||
*/
|
||||
String tableAlias() default "";
|
||||
|
||||
/**
|
||||
* The sql select statement.
|
||||
* <p>
|
||||
* If this query <em>extends</em> another then this string is appended to the
|
||||
* parent query string. Often when using <em>extend</em> you will leave the
|
||||
* query part blank and just specify a where and/or having clauses.
|
||||
* </p>
|
||||
* <p>
|
||||
* This sql <em>CAN NOT</em> contain named parameters. You have to put these
|
||||
* in the separate where and/or having sections.
|
||||
* </p>
|
||||
* <p>
|
||||
* Ebean automatically tries to determine the location in the sql string for
|
||||
* putting in additional where or having clauses. If Ebean is unable to
|
||||
* successfully determine this then you have to explicitly specify these
|
||||
* locations by including
|
||||
* <em>${where} or ${andWhere} and ${having} or ${andHaving}</em> in the sql.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>${where}</b> location of where clause (and will add WHERE ... ) <br/>
|
||||
* Use this when there is no where clause in the sql. If expressions are added
|
||||
* to the where clause Ebean will put them in at this location starting with
|
||||
* the WHERE keyword.
|
||||
* <p>
|
||||
* <p>
|
||||
* <b>${andWhere}</b> <br/>
|
||||
* Use this instead of ${where} if there IS an existing where clause in the
|
||||
* sql. Ebean will add the expressions starting with the AND keyword.
|
||||
* <p>
|
||||
* <b>${having}</b> location of having clause (and will add HAVING... ) <br/>
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>${andHaving}</b> <br/>
|
||||
* Use this instead of ${having} when there IS an existing HAVING clause.
|
||||
* Ebean will add the expressions starting with the AND keyword.
|
||||
* </p>
|
||||
* <p>
|
||||
* You can include one of ${where} OR ${andWhere} but not both.
|
||||
* </p>
|
||||
* <p>
|
||||
* You can include one of ${having} OR ${andHaving} but not both.
|
||||
* </p>
|
||||
*/
|
||||
String query() default "";
|
||||
|
||||
/**
|
||||
* Specify the name of a sql-select query that this one 'extends'.
|
||||
* <p>
|
||||
* When a query is extended the sql query contents are appended together. The
|
||||
* where and having clauses are NOT appended but overridden.
|
||||
* </p>
|
||||
*/
|
||||
String extend() default "";
|
||||
|
||||
/**
|
||||
* Specify a where clause typically containing named parameters.
|
||||
* <p>
|
||||
* If a where clause is specified with named parameters then they will need to
|
||||
* be set on the query via {@link Query#setParameter(String, Object)}.
|
||||
* </p>
|
||||
* <p>
|
||||
* In the example below the query specifies a where clause that includes a
|
||||
* named parameter "likeTitle".
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* ...
|
||||
* @Entity
|
||||
* @Sql(select = {
|
||||
* ...
|
||||
* @SqlSelect(
|
||||
* name = "with.title",
|
||||
* extend = "default",
|
||||
* debug = true,
|
||||
* where = "title like :likeTitle")
|
||||
* })
|
||||
* public class ReportTopic
|
||||
* ...
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Example use of the above named query.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
*
|
||||
* Query<ReportTopic> query0 = Ebean.createQuery(ReportTopic.class, "with.title");
|
||||
*
|
||||
* query0.set("likeTitle", "Bana%");
|
||||
*
|
||||
* List<ReportTopic> list0 = query0.findList();
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
String where() default "";
|
||||
|
||||
/**
|
||||
* Specify a having clause typically containing named parameters.
|
||||
* <p>
|
||||
* If a having clause is specified with named parameters then they will need
|
||||
* to be set on the query via {@link Query#setParameter(String, Object)}.
|
||||
* </p>
|
||||
*/
|
||||
String having() default "";
|
||||
|
||||
/**
|
||||
* (Optional) Explicitly specify column to property mapping.
|
||||
* <p>
|
||||
* This is required when Ebean is unable to parse the sql. This could occur if
|
||||
* the sql contains multiple select keywords etc.
|
||||
* </p>
|
||||
* <p>
|
||||
* Specify the columns and property names they map to in the format.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* column1 propertyName1, column2 propertyName2, ...
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Optionally put a AS keyword between the column and property.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* // the AS keyword is optional
|
||||
* column1 AS propertyName1, column2 propertyName2, ...
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* <b>column</b> should contain the table alias if there is one
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>propertyName</b> should match the property name.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Example mapping 5 columns to properties.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* columnMapping="t.id, t.bug_body description, t.bug_title as title, count(p.id) as scoreValue",
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Without this set Ebean will parse the sql looking for the select clause and
|
||||
* try to map the columns to property names. It is expected that Ebean will
|
||||
* not be able to successfully parse some sql and for those cases you should
|
||||
* specify the column to property mapping explicitly.
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
String columnMapping() default "";
|
||||
|
||||
/**
|
||||
* Set this to true to have debug output when Ebean parses the sql-select.
|
||||
*/
|
||||
boolean debug() default false;
|
||||
}
|
||||
@@ -33,6 +33,13 @@ public class ClassLoadConfig {
|
||||
return isPresent("java.time.LocalDate");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if Java7 is present.
|
||||
*/
|
||||
public boolean isJava7Present() {
|
||||
return isPresent("java.nio.file.Path");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the Joda types are available and should be supported.
|
||||
*/
|
||||
|
||||
@@ -366,8 +366,8 @@ public class DbMigrationConfig {
|
||||
metaTable = properties.get("migration.metaTable", metaTable);
|
||||
runPlaceholders = properties.get("migration.placeholders", runPlaceholders);
|
||||
|
||||
String adminUser = properties.get("datasource."+serverName+".user", dbUser);
|
||||
adminUser = properties.get("datasource."+serverName+".adminuser", adminUser);
|
||||
String adminUser = properties.get("datasource."+serverName+".username", dbUser);
|
||||
adminUser = properties.get("datasource."+serverName+".adminusername", adminUser);
|
||||
dbUser = properties.get("migration.dbuser", adminUser);
|
||||
|
||||
String adminPwd = properties.get("datasource."+serverName+".password", dbPassword);
|
||||
|
||||
@@ -224,6 +224,11 @@ public class ServerConfig {
|
||||
|
||||
private boolean eagerFetchLobs;
|
||||
|
||||
/**
|
||||
* Timezone used to get/set Timestamp values via JDBC.
|
||||
*/
|
||||
private String dataTimeZone;
|
||||
|
||||
private boolean ddlGenerate;
|
||||
|
||||
private boolean ddlRun;
|
||||
@@ -841,6 +846,23 @@ public class ServerConfig {
|
||||
this.migrationConfig = migrationConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time zone to use when reading/writing Timestamps via JDBC.
|
||||
* <p>
|
||||
* When set a Calendar object is used in JDBC calls when reading/writing Timestamp objects.
|
||||
* </p>
|
||||
*/
|
||||
public String getDataTimeZone() {
|
||||
return System.getProperty("ebean.dataTimeZone", dataTimeZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time zone to use when reading/writing Timestamps via JDBC.
|
||||
*/
|
||||
public void setDataTimeZone(String dataTimeZone) {
|
||||
this.dataTimeZone = dataTimeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the suffix appended to the base table to derive the view that contains the union
|
||||
* of the base table and the history table in order to support asOf queries.
|
||||
@@ -2369,6 +2391,7 @@ public class ServerConfig {
|
||||
changeLogIncludeInserts = p.getBoolean("changeLogIncludeInserts", changeLogIncludeInserts);
|
||||
expressionEqualsWithNullAsNoop = p.getBoolean("expressionEqualsWithNullAsNoop", expressionEqualsWithNullAsNoop);
|
||||
|
||||
dataTimeZone = p.get("dataTimeZone", dataTimeZone);
|
||||
asOfViewSuffix = p.get("asOfViewSuffix", asOfViewSuffix);
|
||||
asOfSysPeriod = p.get("asOfSysPeriod", asOfSysPeriod);
|
||||
historyTableSuffix = p.get("historyTableSuffix", historyTableSuffix);
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.sql.DatabaseMetaData;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Database platform specific settings.
|
||||
@@ -175,6 +176,10 @@ public class DatabasePlatform {
|
||||
public DatabasePlatform() {
|
||||
}
|
||||
|
||||
public void configure(Properties properties) {
|
||||
// by default do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the DatabasePlatform.
|
||||
* <p>
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Types;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Postgres v9 specific platform.
|
||||
@@ -50,6 +51,7 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
|
||||
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
|
||||
dbTypeMap.put(Types.DECIMAL, new DbType("decimal", 38));
|
||||
dbTypeMap.put(Types.TIMESTAMP, new DbType("timestamptz"));
|
||||
|
||||
dbTypeMap.put(Types.BINARY, dbBytea);
|
||||
dbTypeMap.put(Types.VARBINARY, dbBytea);
|
||||
@@ -58,7 +60,16 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
dbTypeMap.put(Types.CLOB, dbTypeText);
|
||||
dbTypeMap.put(Types.LONGVARBINARY, dbBytea);
|
||||
dbTypeMap.put(Types.LONGVARCHAR, dbTypeText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(Properties properties) {
|
||||
super.configure(properties);
|
||||
String tsType = properties.getProperty("ebean.postgres.timestamp");
|
||||
if (tsType != null) {
|
||||
// set timestamp type to "timestamp" without time zone
|
||||
dbTypeMap.put(Types.TIMESTAMP, new DbType(tsType));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.util.List;
|
||||
*/
|
||||
public class MigrationRunner {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MigrationRunner.class);
|
||||
public static final Logger logger = LoggerFactory.getLogger("org.avaje.ebean.DbMigration");
|
||||
|
||||
private final EbeanServer server;
|
||||
|
||||
@@ -48,8 +48,16 @@ public class MigrationRunner {
|
||||
|
||||
String migrationUser = migrationConfig.getDbUser();
|
||||
String migrationPwd = migrationConfig.getDbPassword();
|
||||
if (migrationUser == null) {
|
||||
throw new IllegalStateException("No DB migration user specified (to run the db migration) ?");
|
||||
}
|
||||
|
||||
DataSource dataSource = server.getPluginApi().getDataSource();
|
||||
if (dataSource == null) {
|
||||
throw new IllegalStateException("No dataSource when trying to run migration? "
|
||||
+"Maybe trying to generate DBMigration when ebean.migration.run=true is set? "
|
||||
+"Perhaps need to set ebean.migration.run=false in test-ebean.properties?");
|
||||
}
|
||||
|
||||
Connection connection;
|
||||
try {
|
||||
@@ -59,8 +67,8 @@ public class MigrationRunner {
|
||||
}
|
||||
|
||||
try {
|
||||
logger.debug("using db user [{}] to run migrations ...");
|
||||
connection.setAutoCommit(false);
|
||||
|
||||
runMigrations(resources, connection);
|
||||
|
||||
connection.commit();
|
||||
@@ -85,6 +93,8 @@ public class MigrationRunner {
|
||||
// get the migrations in version order
|
||||
List<LocalMigrationResource> localVersions = resources.getVersions();
|
||||
|
||||
logger.info("local migrations:{} existing migrations:{}", localVersions.size(), table.size());
|
||||
|
||||
LocalMigrationResource priorVersion = null;
|
||||
|
||||
// run migrations in order
|
||||
|
||||
@@ -45,7 +45,7 @@ class MigrationMetaRow {
|
||||
MigrationMetaRow(SqlRow row) {
|
||||
id = row.getInteger("id");
|
||||
status = row.getString("status");
|
||||
runVersion = row.getString("row_version");
|
||||
runVersion = row.getString("run_version");
|
||||
depVersion = row.getString("dep_version");
|
||||
comment = row.getString("comment");
|
||||
checksum = row.getInteger("checksum");
|
||||
@@ -53,6 +53,10 @@ class MigrationMetaRow {
|
||||
runBy = row.getString("run_by");
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "id:" + id + " status:" + status + " runVersion:" + runVersion + " comment:" + comment + " runOn:" + runOn + " runBy:" + runBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the id for this migration.
|
||||
*/
|
||||
|
||||
@@ -7,11 +7,11 @@ import com.avaje.ebean.SqlUpdate;
|
||||
import com.avaje.ebean.config.DbMigrationConfig;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.dbmigration.MigrationRunner;
|
||||
import com.avaje.ebean.plugin.SpiServer;
|
||||
import com.avaje.ebeaninternal.server.transaction.ExternalJdbcTransaction;
|
||||
import com.avaje.ebeaninternal.util.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
@@ -27,7 +27,7 @@ import java.util.Map;
|
||||
*/
|
||||
public class MigrationTable {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MigrationTable.class);
|
||||
private static final Logger logger = MigrationRunner.logger;
|
||||
|
||||
private final Connection connection;
|
||||
|
||||
@@ -70,6 +70,13 @@ public class MigrationTable {
|
||||
this.envUserName = System.getProperty("user.name");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of migrations in the DB migration table.
|
||||
*/
|
||||
public int size() {
|
||||
return migrations.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the ScriptTransform for placeholder key/value replacement.
|
||||
*/
|
||||
@@ -229,6 +236,10 @@ public class MigrationTable {
|
||||
*/
|
||||
private void addMigration(MigrationMetaRow metaRow) {
|
||||
lastMigration = metaRow;
|
||||
migrations.put(metaRow.getRunVersion(), metaRow);
|
||||
String runVersion = metaRow.getRunVersion();
|
||||
if (runVersion == null) {
|
||||
throw new IllegalStateException("No runVersion in db migration table row? " + metaRow);
|
||||
}
|
||||
migrations.put(runVersion, metaRow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,33 +46,4 @@ public abstract class AbstractBeanPersistListener implements BeanPersistListener
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify that a bean was inserted on another node of the cluster.
|
||||
*
|
||||
* @param id the id value of the inserted bean
|
||||
*/
|
||||
@Override
|
||||
public void remoteInsert(Object id) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify that a bean was updated on another node of the cluster.
|
||||
*
|
||||
* @param id the id value of the updated bean.
|
||||
*/
|
||||
@Override
|
||||
public void remoteUpdate(Object id) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify that a bean was deleted on another node of the cluster.
|
||||
*
|
||||
* @param id the id value of the deleted bean.
|
||||
*/
|
||||
@Override
|
||||
public void remoteDelete(Object id) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,28 +74,4 @@ public interface BeanPersistListener {
|
||||
*/
|
||||
boolean deleted(Object bean);
|
||||
|
||||
/**
|
||||
* Notify that a bean was inserted on another node of the cluster.
|
||||
*
|
||||
* @param id
|
||||
* the id value of the inserted bean
|
||||
*/
|
||||
void remoteInsert(Object id);
|
||||
|
||||
/**
|
||||
* Notify that a bean was updated on another node of the cluster.
|
||||
*
|
||||
* @param id
|
||||
* the id value of the updated bean.
|
||||
*/
|
||||
void remoteUpdate(Object id);
|
||||
|
||||
/**
|
||||
* Notify that a bean was deleted on another node of the cluster.
|
||||
*
|
||||
* @param id
|
||||
* the id value of the deleted bean.
|
||||
*/
|
||||
void remoteDelete(Object id);
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.avaje.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.query.CQuery;
|
||||
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -187,4 +188,9 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
* user context information (user id, user ip address etc).
|
||||
*/
|
||||
ReadAuditPrepare getReadAuditPrepare();
|
||||
|
||||
/**
|
||||
* Return the DataTimeZone to use when reading/writing timestamps via JDBC.
|
||||
*/
|
||||
DataTimeZone getDataTimeZone();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.core;
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -12,35 +13,35 @@ import org.slf4j.LoggerFactory;
|
||||
public abstract class BeanRequest {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(BeanRequest.class);
|
||||
|
||||
/**
|
||||
* The server processing the request.
|
||||
*/
|
||||
protected final SpiEbeanServer ebeanServer;
|
||||
|
||||
/**
|
||||
* The transaction this is part of.
|
||||
*/
|
||||
protected SpiTransaction transaction;
|
||||
/**
|
||||
* The server processing the request.
|
||||
*/
|
||||
protected final SpiEbeanServer ebeanServer;
|
||||
|
||||
protected boolean createdTransaction;
|
||||
/**
|
||||
* The transaction this is part of.
|
||||
*/
|
||||
protected SpiTransaction transaction;
|
||||
|
||||
public BeanRequest(SpiEbeanServer ebeanServer, SpiTransaction t) {
|
||||
this.ebeanServer = ebeanServer;
|
||||
this.transaction = t;
|
||||
}
|
||||
protected boolean createdTransaction;
|
||||
|
||||
/**
|
||||
* A helper method for creating an implicit transaction is it is required.
|
||||
* <p>
|
||||
* A transaction may have been passed in or active in the thread local. If
|
||||
* not then create one implicitly to handle the request.
|
||||
* </p>
|
||||
public BeanRequest(SpiEbeanServer ebeanServer, SpiTransaction t) {
|
||||
this.ebeanServer = ebeanServer;
|
||||
this.transaction = t;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method for creating an implicit transaction is it is required.
|
||||
* <p>
|
||||
* A transaction may have been passed in or active in the thread local. If
|
||||
* not then create one implicitly to handle the request.
|
||||
* </p>
|
||||
*
|
||||
* @return True if a transaction was set (from current or created).
|
||||
*/
|
||||
public boolean createImplicitTransIfRequired() {
|
||||
if (transaction != null) {
|
||||
*/
|
||||
public boolean createImplicitTransIfRequired() {
|
||||
if (transaction != null) {
|
||||
return false;
|
||||
}
|
||||
transaction = ebeanServer.getCurrentServerTransaction();
|
||||
@@ -50,7 +51,7 @@ public abstract class BeanRequest {
|
||||
createdTransaction = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit this transaction if it was created for this request.
|
||||
@@ -61,40 +62,40 @@ public abstract class BeanRequest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback the transaction if it was created for this request.
|
||||
*/
|
||||
public void rollbackTransIfRequired() {
|
||||
if (createdTransaction) {
|
||||
try {
|
||||
transaction.rollback();
|
||||
} catch (Exception e) {
|
||||
// Just log this and carry on. A previous exception has been
|
||||
// thrown and if this rollback throws exception it likely means
|
||||
// that the connection is broken (and the datasource and db will cleanup)
|
||||
log.error("Error trying to rollback a transaction (after a prior exception thrown)", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Rollback the transaction if it was created for this request.
|
||||
*/
|
||||
public void rollbackTransIfRequired() {
|
||||
if (createdTransaction) {
|
||||
try {
|
||||
transaction.rollback();
|
||||
} catch (Exception e) {
|
||||
// Just log this and carry on. A previous exception has been
|
||||
// thrown and if this rollback throws exception it likely means
|
||||
// that the connection is broken (and the datasource and db will cleanup)
|
||||
log.error("Error trying to rollback a transaction (after a prior exception thrown)", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the server processing the request. Made available for
|
||||
* BeanController and BeanFinder.
|
||||
*/
|
||||
public EbeanServer getEbeanServer() {
|
||||
return ebeanServer;
|
||||
}
|
||||
/**
|
||||
* Return the server processing the request. Made available for
|
||||
* BeanController and BeanFinder.
|
||||
*/
|
||||
public EbeanServer getEbeanServer() {
|
||||
return ebeanServer;
|
||||
}
|
||||
|
||||
public SpiEbeanServer getServer() {
|
||||
return ebeanServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Transaction associated with this request.
|
||||
*/
|
||||
public SpiTransaction getTransaction() {
|
||||
return transaction;
|
||||
}
|
||||
/**
|
||||
* Return the Transaction associated with this request.
|
||||
*/
|
||||
public SpiTransaction getTransaction() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if SQL should be logged for this transaction.
|
||||
@@ -109,4 +110,11 @@ public abstract class BeanRequest {
|
||||
public boolean isLogSummary() {
|
||||
return transaction.isLogSummary();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DataTimeZone to use.
|
||||
*/
|
||||
public DataTimeZone getDataTimeZone() {
|
||||
return ebeanServer.getDataTimeZone();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public class DefaultContainer implements SpiContainer {
|
||||
// inform the NamingConvention of the associated DatabasePlatform
|
||||
serverConfig.getNamingConvention().setDatabasePlatform(serverConfig.getDatabasePlatform());
|
||||
|
||||
ServerCacheManager cacheManager = getCacheManager(serverConfig);
|
||||
ServerCacheManager cacheManager = getCacheManager(online, serverConfig);
|
||||
|
||||
SpiBackgroundExecutor bgExecutor = createBackgroundExecutor(serverConfig);
|
||||
|
||||
@@ -147,9 +147,10 @@ public class DefaultContainer implements SpiContainer {
|
||||
/**
|
||||
* Create and return the CacheManager.
|
||||
*/
|
||||
private ServerCacheManager getCacheManager(ServerConfig serverConfig) {
|
||||
private ServerCacheManager getCacheManager(boolean online, ServerConfig serverConfig) {
|
||||
|
||||
if (serverConfig.isDisableL2Cache()) {
|
||||
if (!online || serverConfig.isDisableL2Cache()) {
|
||||
// use local only L2 cache implementation as placeholder
|
||||
return new DefaultServerCacheManager();
|
||||
}
|
||||
|
||||
@@ -240,6 +241,7 @@ public class DefaultContainer implements SpiContainer {
|
||||
if (dbPlatform == null) {
|
||||
DatabasePlatformFactory factory = new DatabasePlatformFactory();
|
||||
DatabasePlatform db = factory.create(config);
|
||||
db.configure(config.getProperties());
|
||||
config.setDatabasePlatform(db);
|
||||
logger.info("DatabasePlatform name:" + config.getName() + " platform:" + db.getName());
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
|
||||
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
|
||||
import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import com.avaje.ebeaninternal.util.ParamTypeHelper;
|
||||
import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreIntegration;
|
||||
@@ -108,6 +109,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private final TransactionScopeManager transactionScopeManager;
|
||||
|
||||
private final DataTimeZone dataTimeZone;
|
||||
|
||||
private final CallStackFactory callStackFactory = new DefaultCallStackFactory();
|
||||
|
||||
private final int maxCallStack;
|
||||
@@ -228,6 +231,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
this.beanLoader = new DefaultBeanLoader(this);
|
||||
this.jsonContext = config.createJsonContext(this);
|
||||
this.dataTimeZone = config.getDataTimeZone();
|
||||
|
||||
DocStoreIntegration docStoreComponents = config.createDocStoreIntegration(this);
|
||||
this.transactionManager = config.createTransactionManager(docStoreComponents.updateProcessor());
|
||||
@@ -284,7 +288,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
public DatabasePlatform getDatabasePlatform() {
|
||||
return databasePlatform;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DataTimeZone getDataTimeZone() {
|
||||
return dataTimeZone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaInfoManager getMetaInfoManager() {
|
||||
return metaInfoManager;
|
||||
@@ -1756,11 +1765,23 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
public int delete(Class<?> beanType, Object id, Transaction t) {
|
||||
return delete(beanType, id, t, false);
|
||||
}
|
||||
|
||||
public int deletePermanent(Class<?> beanType, Object id) {
|
||||
return delete(beanType, id, null, true);
|
||||
}
|
||||
|
||||
public int deletePermanent(Class<?> beanType, Object id, Transaction t) {
|
||||
return delete(beanType, id, t, true);
|
||||
}
|
||||
|
||||
private int delete(Class<?> beanType, Object id, Transaction t, boolean permanent) {
|
||||
|
||||
TransWrapper wrap = initTransIfRequired(t);
|
||||
try {
|
||||
SpiTransaction trans = wrap.transaction;
|
||||
int rowCount = persister.delete(beanType, id, trans);
|
||||
int rowCount = persister.delete(beanType, id, trans, permanent);
|
||||
wrap.commitIfCreated();
|
||||
|
||||
return rowCount;
|
||||
@@ -1772,18 +1793,33 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAll(Class<?> beanType, Collection<?> ids) {
|
||||
deleteAll(beanType, ids, null);
|
||||
public int deleteAll(Class<?> beanType, Collection<?> ids) {
|
||||
return deleteAll(beanType, ids, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAll(Class<?> beanType, Collection<?> ids, Transaction t) {
|
||||
public int deleteAll(Class<?> beanType, Collection<?> ids, Transaction t) {
|
||||
return deleteAll(beanType, ids, t, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteAllPermanent(Class<?> beanType, Collection<?> ids) {
|
||||
return deleteAll(beanType, ids, null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteAllPermanent(Class<?> beanType, Collection<?> ids, Transaction t) {
|
||||
return deleteAll(beanType, ids, t, true);
|
||||
}
|
||||
|
||||
private int deleteAll(Class<?> beanType, Collection<?> ids, Transaction t, boolean permanent) {
|
||||
|
||||
TransWrapper wrap = initTransIfRequired(t);
|
||||
try {
|
||||
SpiTransaction trans = wrap.transaction;
|
||||
persister.deleteMany(beanType, ids, trans);
|
||||
int count = persister.deleteMany(beanType, ids, trans, permanent);
|
||||
wrap.commitIfCreated();
|
||||
return count;
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
wrap.rollbackIfCreated();
|
||||
|
||||
@@ -22,6 +22,9 @@ import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogListener;
|
||||
import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogPrepare;
|
||||
import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogRegister;
|
||||
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.CloneDataTimeZone;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.NoDataTimeZone;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.SimpleDataTimeZone;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import com.avaje.ebeaninternal.server.deploy.DeployOrmXml;
|
||||
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
|
||||
@@ -29,6 +32,7 @@ import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
|
||||
import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import org.avaje.datasource.DataSourcePool;
|
||||
import com.avaje.ebeaninternal.server.persist.Binder;
|
||||
import com.avaje.ebeaninternal.server.persist.DefaultPersister;
|
||||
@@ -79,6 +83,8 @@ public class InternalConfiguration {
|
||||
|
||||
private final TypeManager typeManager;
|
||||
|
||||
private final DataTimeZone dataTimeZone;
|
||||
|
||||
private final Binder binder;
|
||||
|
||||
private final DeployCreateProperties deployCreateProperties;
|
||||
@@ -136,7 +142,8 @@ public class InternalConfiguration {
|
||||
|
||||
DatabasePlatform databasePlatform = serverConfig.getDatabasePlatform();
|
||||
|
||||
this.binder = getBinder(typeManager, databasePlatform);
|
||||
this.dataTimeZone = initDataTimeZone();
|
||||
this.binder = getBinder(typeManager, databasePlatform, dataTimeZone);
|
||||
this.cQueryEngine = new CQueryEngine(databasePlatform, binder, asOfTableMapping, serverConfig.getAsOfSysPeriod(), draftTableMap);
|
||||
}
|
||||
|
||||
@@ -218,15 +225,15 @@ public class InternalConfiguration {
|
||||
/**
|
||||
* For 'As Of' queries return the number of bind variables per predicate.
|
||||
*/
|
||||
private Binder getBinder(TypeManager typeManager, DatabasePlatform databasePlatform) {
|
||||
private Binder getBinder(TypeManager typeManager, DatabasePlatform databasePlatform, DataTimeZone dataTimeZone) {
|
||||
|
||||
JsonExpressionHandler jsonHandler = getJsonExpressionHandler(databasePlatform);
|
||||
|
||||
DbHistorySupport historySupport = databasePlatform.getHistorySupport();
|
||||
if (historySupport == null) {
|
||||
return new Binder(typeManager, 0, false, jsonHandler);
|
||||
return new Binder(typeManager, 0, false, jsonHandler, dataTimeZone);
|
||||
}
|
||||
return new Binder(typeManager, historySupport.getBindCount(), historySupport.isBindWithFromClause(), jsonHandler);
|
||||
return new Binder(typeManager, historySupport.getBindCount(), historySupport.isBindWithFromClause(), jsonHandler, dataTimeZone);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,4 +388,24 @@ public class InternalConfiguration {
|
||||
return new DefaultTransactionScopeManager(transactionManager);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the DataTimeZone implementation to use.
|
||||
*/
|
||||
private DataTimeZone initDataTimeZone() {
|
||||
|
||||
String tz = serverConfig.getDataTimeZone();
|
||||
if (tz == null) {
|
||||
return new NoDataTimeZone();
|
||||
}
|
||||
if (getDatabasePlatform().getName().toLowerCase().startsWith("oracle")) {
|
||||
return new CloneDataTimeZone(tz);
|
||||
} else {
|
||||
return new SimpleDataTimeZone(tz);
|
||||
}
|
||||
}
|
||||
|
||||
public DataTimeZone getDataTimeZone() {
|
||||
return dataTimeZone;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
/**
|
||||
* Set the cache notify status.
|
||||
*/
|
||||
public void setNotifyCache() {
|
||||
private void setNotifyCache() {
|
||||
this.notifyCache = beanDescriptor.isCacheNotify(publish);
|
||||
}
|
||||
|
||||
@@ -327,7 +327,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify/Update the local L2 cache after the transaction has successfully committed.
|
||||
* Collect L2 cache changes to be applied after the transaction has successfully committed.
|
||||
*/
|
||||
public void notifyCache(CacheChangeSet changeSet) {
|
||||
if (notifyCache) {
|
||||
@@ -340,7 +340,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
break;
|
||||
case DELETE:
|
||||
case SOFT_DELETE:
|
||||
// Bean deleted from cache early via postDelete()
|
||||
beanDescriptor.cacheHandleDelete(idValue, this, changeSet);
|
||||
break;
|
||||
default:
|
||||
@@ -949,7 +948,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
* Takes into account transaction setting and JDBC batch.
|
||||
* </p>
|
||||
*/
|
||||
public boolean determineUpdateAllLoadedProperties() {
|
||||
private boolean determineUpdateAllLoadedProperties() {
|
||||
|
||||
Boolean txnUpdateAll = transaction.isUpdateAllLoadedProperties();
|
||||
if (txnUpdateAll != null) {
|
||||
|
||||
@@ -61,7 +61,7 @@ public interface Persister {
|
||||
* This will also cascade delete one level of children.
|
||||
* </p>
|
||||
*/
|
||||
int delete(Class<?> beanType, Object id, Transaction transaction);
|
||||
int delete(Class<?> beanType, Object id, Transaction transaction, boolean permanent);
|
||||
|
||||
/**
|
||||
* Delete the bean.
|
||||
@@ -71,7 +71,7 @@ public interface Persister {
|
||||
/**
|
||||
* Delete multiple beans given a collection of Id values.
|
||||
*/
|
||||
void deleteMany(Class<?> beanType, Collection<?> ids, Transaction transaction);
|
||||
int deleteMany(Class<?> beanType, Collection<?> ids, Transaction transaction, boolean permanent);
|
||||
|
||||
/**
|
||||
* Execute the Update.
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.avaje.ebeaninternal.server.core.timezone;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* Implementation of DataTimeZone that clones the Calendar instance.
|
||||
* <p>
|
||||
* Used with Oracle JDBC driver as that wants to mutate the Calender.
|
||||
* </p>
|
||||
*/
|
||||
public class CloneDataTimeZone extends SimpleDataTimeZone {
|
||||
|
||||
public CloneDataTimeZone(String zoneId) {
|
||||
super(zoneId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Calendar getTimeZone() {
|
||||
// return cloned copy for Oracle to muck around with
|
||||
return (Calendar)zone.clone();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.avaje.ebeaninternal.server.core.timezone;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* Define if a Calendar representing the time zone should be used in JDBC calls.
|
||||
*/
|
||||
public interface DataTimeZone {
|
||||
|
||||
/**
|
||||
* Return the Calendar to use for Timezone information.
|
||||
*/
|
||||
Calendar getTimeZone();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.avaje.ebeaninternal.server.core.timezone;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* Implementation of DataTimeZone when no time zone is specified.
|
||||
*/
|
||||
public class NoDataTimeZone implements DataTimeZone {
|
||||
|
||||
@Override
|
||||
public Calendar getTimeZone() {
|
||||
// return null so Calendar is not used
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.avaje.ebeaninternal.server.core.timezone;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Implementation of DataTimeZone when single Calendar instance is used.
|
||||
*/
|
||||
public class SimpleDataTimeZone implements DataTimeZone {
|
||||
|
||||
protected final Calendar zone;
|
||||
|
||||
public SimpleDataTimeZone(String zoneId) {
|
||||
this.zone = Calendar.getInstance(TimeZone.getTimeZone(zoneId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Calendar getTimeZone() {
|
||||
// return null so Calendar is not used
|
||||
return zone;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.deploy;
|
||||
import com.avaje.ebean.OrderBy;
|
||||
import com.avaje.ebean.PersistenceContextScope;
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebean.RawSql;
|
||||
import com.avaje.ebean.SqlUpdate;
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.ValuePair;
|
||||
@@ -2719,6 +2720,10 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
if (idProperty != null && !idProperty.isEmbedded()) {
|
||||
OrderBy<T> orderBy = query.getOrderBy();
|
||||
if (orderBy == null || orderBy.isEmpty()) {
|
||||
RawSql rawSql = query.getRawSql();
|
||||
if (rawSql != null) {
|
||||
query.order(rawSql.getSql().getOrderBy());
|
||||
}
|
||||
query.order().asc(idProperty.getName());
|
||||
} else if (!orderBy.containsProperty(idProperty.getName())){
|
||||
query.order().asc(idProperty.getName());
|
||||
|
||||
@@ -83,7 +83,6 @@ public class ChainedBeanPersistListener implements BeanPersistListener {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean deleted(Object bean) {
|
||||
boolean notifyCluster = false;
|
||||
for (int i = 0; i < chain.length; i++) {
|
||||
@@ -104,24 +103,6 @@ public class ChainedBeanPersistListener implements BeanPersistListener {
|
||||
return notifyCluster;
|
||||
}
|
||||
|
||||
public void remoteDelete(Object id) {
|
||||
for (int i = 0; i < chain.length; i++) {
|
||||
chain[i].remoteDelete(id);
|
||||
}
|
||||
}
|
||||
|
||||
public void remoteInsert(Object id) {
|
||||
for (int i = 0; i < chain.length; i++) {
|
||||
chain[i].remoteInsert(id);
|
||||
}
|
||||
}
|
||||
|
||||
public void remoteUpdate(Object id) {
|
||||
for (int i = 0; i < chain.length; i++) {
|
||||
chain[i].remoteUpdate(id);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean updated(Object bean, Set<String> updatedProperties) {
|
||||
boolean notifyCluster = false;
|
||||
for (int i = 0; i < chain.length; i++) {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebean.annotation.SqlSelect;
|
||||
|
||||
/**
|
||||
* Meta data for a sql-select object.
|
||||
* <p>
|
||||
@@ -19,18 +17,6 @@ public class DRawSqlMeta {
|
||||
private String having;
|
||||
private String columnMapping;
|
||||
|
||||
|
||||
public DRawSqlMeta(SqlSelect sqlSelect) {
|
||||
this.debug = sqlSelect.debug();
|
||||
this.name = sqlSelect.name();
|
||||
this.tableAlias = toNull(sqlSelect.tableAlias());
|
||||
this.extend = toNull(sqlSelect.extend());
|
||||
this.having = toNull(sqlSelect.having());
|
||||
this.where = toNull(sqlSelect.where());
|
||||
this.columnMapping = toNull(sqlSelect.columnMapping());
|
||||
this.query = toNull(sqlSelect.query());
|
||||
}
|
||||
|
||||
public DRawSqlMeta(String name, String extend, String query, boolean debug,
|
||||
String where, String having, String columnMapping) {
|
||||
|
||||
@@ -129,11 +115,4 @@ public class DRawSqlMeta {
|
||||
}
|
||||
}
|
||||
|
||||
private static String toNull(String s) {
|
||||
if (s != null && s.equals("")){
|
||||
return null;
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -36,6 +36,17 @@ public class GeneratedInsertJavaTime {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instant support.
|
||||
*/
|
||||
public static class InstantDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toInstant(now);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* LocalDateTime support.
|
||||
*/
|
||||
|
||||
+16
@@ -31,6 +31,22 @@ public class GeneratedUpdateJavaTime {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instant support.
|
||||
*/
|
||||
public static class InstantDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toInstant(now);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toInstant(now);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* LocalDateTime support.
|
||||
*/
|
||||
|
||||
+2
@@ -5,6 +5,7 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
@@ -27,6 +28,7 @@ public class InsertTimestampFactory {
|
||||
map.put(long.class, longTime);
|
||||
|
||||
if (classLoadConfig.isJavaTimePresent()) {
|
||||
map.put(Instant.class, new GeneratedInsertJavaTime.InstantDT());
|
||||
map.put(LocalDateTime.class, new GeneratedInsertJavaTime.LocalDT());
|
||||
map.put(OffsetDateTime.class, new GeneratedInsertJavaTime.OffsetDT());
|
||||
map.put(ZonedDateTime.class, new GeneratedInsertJavaTime.ZonedDT());
|
||||
|
||||
+7
@@ -11,6 +11,13 @@ import java.time.ZonedDateTime;
|
||||
*/
|
||||
public class JavaTimeUtils {
|
||||
|
||||
/**
|
||||
* Return the system millis time as a LocalDateTime.
|
||||
*/
|
||||
public static Object toInstant(long systemMillis) {
|
||||
return Instant.ofEpochMilli(systemMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the system millis time as a LocalDateTime.
|
||||
*/
|
||||
|
||||
+2
@@ -5,6 +5,7 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
@@ -27,6 +28,7 @@ public class UpdateTimestampFactory {
|
||||
map.put(long.class, longTime);
|
||||
|
||||
if (classLoadConfig.isJavaTimePresent()) {
|
||||
map.put(Instant.class, new GeneratedUpdateJavaTime.InstantDT());
|
||||
map.put(LocalDateTime.class, new GeneratedUpdateJavaTime.LocalDT());
|
||||
map.put(OffsetDateTime.class, new GeneratedUpdateJavaTime.OffsetDT());
|
||||
map.put(ZonedDateTime.class, new GeneratedUpdateJavaTime.ZonedDT());
|
||||
|
||||
@@ -95,10 +95,6 @@ public class AnnotationFields extends AnnotationParser {
|
||||
if (docEmbedded != null) {
|
||||
prop.setDocStoreEmbedded(docEmbedded.doc());
|
||||
}
|
||||
DocStoreEmbedded docStoreEmbedded = get(prop, DocStoreEmbedded.class);
|
||||
if (docStoreEmbedded != null) {
|
||||
prop.setDocStoreEmbedded(docStoreEmbedded.doc());
|
||||
}
|
||||
|
||||
if (prop instanceof DeployBeanPropertyAssocOne<?>) {
|
||||
if (prop.isId() && !prop.isEmbedded()) {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import com.avaje.ebean.annotation.Sql;
|
||||
import com.avaje.ebean.annotation.SqlSelect;
|
||||
import com.avaje.ebeaninternal.server.deploy.DRawSqlMeta;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
/**
|
||||
* Read the class level deployment annotations.
|
||||
@@ -17,25 +16,8 @@ public class AnnotationSql extends AnnotationParser {
|
||||
Class<?> cls = descriptor.getBeanType();
|
||||
Sql sql = cls.getAnnotation(Sql.class);
|
||||
if (sql != null) {
|
||||
setSql(sql);
|
||||
}
|
||||
|
||||
SqlSelect sqlSelect = cls.getAnnotation(SqlSelect.class);
|
||||
if (sqlSelect != null) {
|
||||
setSqlSelect(sqlSelect);
|
||||
descriptor.setEntityType(BeanDescriptor.EntityType.SQL);
|
||||
}
|
||||
}
|
||||
|
||||
private void setSql(Sql sql) {
|
||||
SqlSelect[] select = sql.select();
|
||||
for (int i = 0; i < select.length; i++) {
|
||||
setSqlSelect(select[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void setSqlSelect(SqlSelect sqlSelect) {
|
||||
|
||||
DRawSqlMeta rawSqlMeta = new DRawSqlMeta(sqlSelect);
|
||||
descriptor.add(rawSqlMeta);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.lib;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This is the ShutdownHook that gets added to Runtime.
|
||||
* It will try to shutdown the system cleanly when the JVM exits.
|
||||
@@ -9,14 +8,14 @@ package com.avaje.ebeaninternal.server.lib;
|
||||
*/
|
||||
class ShutdownHook extends Thread {
|
||||
|
||||
ShutdownHook() {
|
||||
}
|
||||
ShutdownHook() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired by the JVM Runtime on shutdown.
|
||||
*/
|
||||
public void run() {
|
||||
ShutdownManager.shutdown();
|
||||
}
|
||||
/**
|
||||
* Fired by the JVM Runtime on shutdown.
|
||||
*/
|
||||
public void run() {
|
||||
ShutdownManager.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.persist;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
@@ -13,6 +14,7 @@ import com.avaje.ebean.config.dbplatform.DbType;
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.server.core.JsonExpressionHandler;
|
||||
import com.avaje.ebeaninternal.server.core.Message;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
import com.avaje.ebeaninternal.server.type.TypeManager;
|
||||
@@ -34,14 +36,19 @@ public class Binder {
|
||||
|
||||
private final JsonExpressionHandler jsonExpressionHandler;
|
||||
|
||||
private final DataTimeZone dataTimeZone;
|
||||
|
||||
/**
|
||||
* Set the PreparedStatement with which to bind variables to.
|
||||
*/
|
||||
public Binder(TypeManager typeManager, int asOfBindCount, boolean bindAsOfWithFromClause, JsonExpressionHandler jsonExpressionHandler) {
|
||||
public Binder(TypeManager typeManager, int asOfBindCount, boolean bindAsOfWithFromClause,
|
||||
JsonExpressionHandler jsonExpressionHandler, DataTimeZone dataTimeZone) {
|
||||
|
||||
this.typeManager = typeManager;
|
||||
this.asOfBindCount = asOfBindCount;
|
||||
this.bindAsOfWithFromClause = bindAsOfWithFromClause;
|
||||
this.jsonExpressionHandler = jsonExpressionHandler;
|
||||
this.dataTimeZone = dataTimeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,6 +98,13 @@ public class Binder {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the parameters to the preparedStatement returning the bind log.
|
||||
*/
|
||||
public String bind(BindParams bindParams, PreparedStatement statement) throws SQLException {
|
||||
return bind(bindParams, new DataBind(dataTimeZone, statement));
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the list of positionedParameters in BindParams.
|
||||
*/
|
||||
@@ -392,4 +406,11 @@ public class Binder {
|
||||
public JsonExpressionHandler getJsonExpressionHandler() {
|
||||
return jsonExpressionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a DataBind for the statement.
|
||||
*/
|
||||
public DataBind dataBind(PreparedStatement stmt) {
|
||||
return new DataBind(dataTimeZone, stmt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,17 +559,16 @@ public final class DefaultPersister implements Persister {
|
||||
private void deleteList(List<?> beanList, Transaction t, boolean softDelete) {
|
||||
for (int i = 0; i < beanList.size(); i++) {
|
||||
deleteRecurse((EntityBean) beanList.get(i), t, softDelete);
|
||||
//delete((EntityBean) beanList.get(i), t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete by a List of Id's.
|
||||
*/
|
||||
public void deleteMany(Class<?> beanType, Collection<?> ids, Transaction transaction) {
|
||||
public int deleteMany(Class<?> beanType, Collection<?> ids, Transaction transaction, boolean permanent) {
|
||||
|
||||
if (ids == null || ids.size() == 0) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
BeanDescriptor<?> descriptor = beanDescriptorManager.getBeanDescriptor(beanType);
|
||||
@@ -580,19 +579,18 @@ public final class DefaultPersister implements Persister {
|
||||
idList.add(descriptor.convertId(id));
|
||||
}
|
||||
|
||||
delete(descriptor, null, idList, transaction, descriptor.isSoftDelete());
|
||||
boolean softDelete = !permanent && descriptor.isSoftDelete();
|
||||
return delete(descriptor, null, idList, transaction, softDelete);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete by Id.
|
||||
*/
|
||||
public int delete(Class<?> beanType, Object id, Transaction transaction) {
|
||||
|
||||
public int delete(Class<?> beanType, Object id, Transaction transaction, boolean permanent) {
|
||||
BeanDescriptor<?> descriptor = beanDescriptorManager.getBeanDescriptor(beanType);
|
||||
|
||||
// convert to appropriate type if required
|
||||
id = descriptor.convertId(id);
|
||||
return delete(descriptor, id, null, transaction, descriptor.isSoftDelete());
|
||||
boolean softDelete = !permanent && descriptor.isSoftDelete();
|
||||
return delete(descriptor, id, null, transaction, softDelete);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.api.SpiCallableSql;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestCallableSql;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.util.BindParamsParser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -96,7 +95,7 @@ public class ExeCallableSql {
|
||||
|
||||
String bindLog = null;
|
||||
if (!bindParams.isEmpty()) {
|
||||
bindLog = binder.bind(bindParams, new DataBind(cstmt));
|
||||
bindLog = binder.bind(bindParams, cstmt);
|
||||
}
|
||||
|
||||
request.setBindLog(bindLog);
|
||||
|
||||
@@ -113,7 +113,7 @@ public class ExeOrmUpdate {
|
||||
|
||||
String bindLog = null;
|
||||
if (!bindParams.isEmpty()) {
|
||||
bindLog = binder.bind(bindParams, new DataBind(pstmt));
|
||||
bindLog = binder.bind(bindParams, pstmt);
|
||||
}
|
||||
|
||||
request.setBindLog(bindLog);
|
||||
|
||||
@@ -100,7 +100,7 @@ public class ExeUpdateSql {
|
||||
|
||||
String bindLog = null;
|
||||
if (!bindParams.isEmpty()) {
|
||||
bindLog = binder.bind(bindParams, new DataBind(pstmt));
|
||||
bindLog = binder.bind(bindParams, pstmt);
|
||||
}
|
||||
|
||||
request.setBindLog(bindLog);
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
package com.avaje.ebeaninternal.server.persist.dml;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.persistence.OptimisticLockException;
|
||||
|
||||
import com.avaje.ebeaninternal.api.DerivedRelationshipData;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Delete bean handler.
|
||||
@@ -37,7 +35,7 @@ public class DeleteHandler extends DmlHandler {
|
||||
} else {
|
||||
pstmt = getPstmt(t, sql, false);
|
||||
}
|
||||
dataBind = new DataBind(pstmt);
|
||||
dataBind = bind(pstmt);
|
||||
meta.bind(persistRequest, this);
|
||||
logSql(sql);
|
||||
}
|
||||
|
||||
@@ -74,6 +74,13 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
|
||||
return persistRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind to the statement returning the DataBind.
|
||||
*/
|
||||
protected DataBind bind(PreparedStatement stmt) {
|
||||
return new DataBind(persistRequest.getDataTimeZone(), stmt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sql and bind the statement.
|
||||
*/
|
||||
|
||||
@@ -9,7 +9,6 @@ import com.avaje.ebeaninternal.server.core.PersistRequestBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.persist.DmlUtil;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -98,9 +97,7 @@ public class InsertHandler extends DmlHandler {
|
||||
} else {
|
||||
pstmt = getPstmt(t, sql, useGeneratedKeys);
|
||||
}
|
||||
dataBind = new DataBind(pstmt);
|
||||
|
||||
// bind the bean property values
|
||||
dataBind = bind(pstmt);
|
||||
meta.bind(this, bean, withId, persistRequest.isPublish());
|
||||
|
||||
logSql(sql);
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.avaje.ebeaninternal.api.DerivedRelationshipData;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.api.SpiUpdatePlan;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import java.sql.PreparedStatement;
|
||||
@@ -47,8 +46,7 @@ public class UpdateHandler extends DmlHandler {
|
||||
} else {
|
||||
pstmt = getPstmt(t, sql, false);
|
||||
}
|
||||
dataBind = new DataBind(pstmt);
|
||||
|
||||
dataBind = bind(pstmt);
|
||||
meta.bind(persistRequest, this, updatePlan);
|
||||
|
||||
setUpdateGenValues();
|
||||
|
||||
@@ -335,11 +335,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
|
||||
pstmt.setFetchSize(query.getBufferFetchSizeHint());
|
||||
}
|
||||
|
||||
DataBind dataBind = new DataBind(pstmt);
|
||||
|
||||
// bind keys for encrypted properties
|
||||
queryPlan.bindEncryptedProperties(dataBind);
|
||||
|
||||
DataBind dataBind = queryPlan.bindEncryptedProperties(pstmt);
|
||||
bindLog = predicates.bind(dataBind);
|
||||
|
||||
// executeQuery
|
||||
|
||||
@@ -105,7 +105,7 @@ public class CQueryDelete {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
}
|
||||
|
||||
bindLog = predicates.bind(new DataBind(pstmt));
|
||||
bindLog = predicates.bind(pstmt);
|
||||
rowCount = pstmt.executeUpdate();
|
||||
|
||||
long exeNano = System.nanoTime() - startNano;
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.type.DataReader;
|
||||
import com.avaje.ebeaninternal.server.type.RsetDataReader;
|
||||
import org.slf4j.Logger;
|
||||
@@ -144,10 +143,10 @@ public class CQueryFetchIds {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
}
|
||||
|
||||
bindLog = predicates.bind(new DataBind(pstmt));
|
||||
bindLog = predicates.bind(pstmt);
|
||||
|
||||
ResultSet rset = pstmt.executeQuery();
|
||||
dataReader = new RsetDataReader(rset);
|
||||
dataReader = new RsetDataReader(request.getDataTimeZone(), rset);
|
||||
|
||||
boolean hitMaxRows = false;
|
||||
boolean hasMoreRows = false;
|
||||
|
||||
@@ -9,11 +9,13 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.type.DataReader;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import com.avaje.ebeaninternal.server.type.RsetDataReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@@ -65,6 +67,8 @@ public class CQueryPlan {
|
||||
|
||||
private final Class<?> beanType;
|
||||
|
||||
protected final DataTimeZone dataTimeZone;
|
||||
|
||||
/**
|
||||
* Key used to identify the query plan in audit logging.
|
||||
*/
|
||||
@@ -76,6 +80,7 @@ public class CQueryPlan {
|
||||
public CQueryPlan(OrmQueryRequest<?> request, SqlLimitResponse sqlRes, SqlTree sqlTree, boolean rawSql, String logWhereSql) {
|
||||
|
||||
this.server = request.getServer();
|
||||
this.dataTimeZone = server.getDataTimeZone();
|
||||
this.beanType = request.getBeanDescriptor().getBeanType();
|
||||
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
|
||||
this.planKey = request.getQueryPlanKey();
|
||||
@@ -100,6 +105,7 @@ public class CQueryPlan {
|
||||
boolean rawSql, boolean rowNumberIncluded, String logWhereSql) {
|
||||
|
||||
this.server = request.getServer();
|
||||
this.dataTimeZone = server.getDataTimeZone();
|
||||
this.beanType = request.getBeanDescriptor().getBeanType();
|
||||
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
|
||||
this.planKey = buildPlanKey(sql, rawSql, rowNumberIncluded, logWhereSql);
|
||||
@@ -127,17 +133,21 @@ public class CQueryPlan {
|
||||
}
|
||||
|
||||
public DataReader createDataReader(ResultSet rset) {
|
||||
|
||||
return new RsetDataReader(rset);
|
||||
return new RsetDataReader(dataTimeZone, rset);
|
||||
}
|
||||
|
||||
public void bindEncryptedProperties(DataBind dataBind) throws SQLException {
|
||||
/**
|
||||
* Bind keys for encrypted properties if necessary returning the DataBind.
|
||||
*/
|
||||
public DataBind bindEncryptedProperties(PreparedStatement stmt) throws SQLException {
|
||||
DataBind dataBind = new DataBind(dataTimeZone, stmt);
|
||||
if (encryptedProps != null) {
|
||||
for (int i = 0; i < encryptedProps.length; i++) {
|
||||
String key = encryptedProps[i].getEncryptKey().getStringValue();
|
||||
dataBind.setString(key);
|
||||
}
|
||||
}
|
||||
return dataBind;
|
||||
}
|
||||
|
||||
public boolean isAutoTuned() {
|
||||
|
||||
@@ -24,9 +24,9 @@ public class CQueryPlanRawSql extends CQueryPlan {
|
||||
this.rsetIndexPositions = createIndexPositions(request, sqlTree);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataReader createDataReader(ResultSet rset) {
|
||||
|
||||
return new RsetDataReaderIndexed(rset, rsetIndexPositions, isRowNumberIncluded());
|
||||
return new RsetDataReaderIndexed(dataTimeZone, rset, rsetIndexPositions, isRowNumberIncluded());
|
||||
}
|
||||
|
||||
private int[] createIndexPositions(OrmQueryRequest<?> request, SqlTree sqlTree) {
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.avaje.ebeaninternal.server.expression.DefaultExpressionRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.HashSet;
|
||||
@@ -125,6 +126,10 @@ public class CQueryPredicates {
|
||||
this.idValue = query.getId();
|
||||
}
|
||||
|
||||
public String bind(PreparedStatement stmt) throws SQLException {
|
||||
return bind(binder.dataBind(stmt));
|
||||
}
|
||||
|
||||
public String bind(DataBind dataBind) throws SQLException {
|
||||
|
||||
if (query.isVersionsBetween() && binder.isBindAsOfWithFromClause()) {
|
||||
|
||||
@@ -116,8 +116,7 @@ public class CQueryRowCount {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
}
|
||||
|
||||
bindLog = predicates.bind(new DataBind(pstmt));
|
||||
|
||||
bindLog = predicates.bind(pstmt);
|
||||
rset = pstmt.executeQuery();
|
||||
|
||||
if (!rset.next()) {
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
|
||||
}
|
||||
|
||||
if (!bindParams.isEmpty()) {
|
||||
bindLog = binder.bind(bindParams, new DataBind(pstmt));
|
||||
bindLog = binder.bind(bindParams, pstmt);
|
||||
}
|
||||
|
||||
if (request.isLogSql()) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.avaje.ebeaninternal.server.transaction;
|
||||
|
||||
import com.avaje.ebean.event.BeanPersistListener;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.cluster.BinaryMessage;
|
||||
import com.avaje.ebeaninternal.server.cluster.BinaryMessageList;
|
||||
@@ -215,41 +214,24 @@ public class BeanPersistIds {
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the cache and local BeanPersistListener of this event that came
|
||||
* from another server in the cluster.
|
||||
* Notify the cache of this event that came from another server in the cluster.
|
||||
*/
|
||||
void notifyCacheAndListener() {
|
||||
|
||||
BeanPersistListener listener = beanDescriptor.getPersistListener();
|
||||
|
||||
// any change invalidates the query cache
|
||||
beanDescriptor.queryCacheClear();
|
||||
|
||||
if (insertIds != null) {
|
||||
if (listener != null) {
|
||||
for (int i = 0; i < insertIds.size(); i++) {
|
||||
listener.remoteInsert(insertIds.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (updateIds != null) {
|
||||
for (int i = 0; i < updateIds.size(); i++) {
|
||||
Object id = updateIds.get(i);
|
||||
beanDescriptor.cacheHandleDeleteById(id);
|
||||
if (listener != null) {
|
||||
listener.remoteUpdate(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deleteIds != null) {
|
||||
for (int i = 0; i < deleteIds.size(); i++) {
|
||||
Object id = deleteIds.get(i);
|
||||
beanDescriptor.cacheHandleDeleteById(id);
|
||||
if (listener != null) {
|
||||
listener.remoteDelete(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
@@ -9,16 +11,20 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Calendar;
|
||||
|
||||
public class DataBind {
|
||||
|
||||
private final DataTimeZone dataTimeZone;
|
||||
|
||||
private final PreparedStatement pstmt;
|
||||
|
||||
private final StringBuilder bindLog = new StringBuilder();
|
||||
|
||||
private int pos;
|
||||
|
||||
public DataBind(PreparedStatement pstmt) {
|
||||
public DataBind(DataTimeZone dataTimeZone, PreparedStatement pstmt) {
|
||||
this.dataTimeZone = dataTimeZone;
|
||||
this.pstmt = pstmt;
|
||||
}
|
||||
|
||||
@@ -108,7 +114,12 @@ public class DataBind {
|
||||
}
|
||||
|
||||
public void setTimestamp(Timestamp v) throws SQLException {
|
||||
pstmt.setTimestamp(++pos, v);
|
||||
Calendar timeZone = dataTimeZone.getTimeZone();
|
||||
if (timeZone != null) {
|
||||
pstmt.setTimestamp(++pos, v, timeZone);
|
||||
} else {
|
||||
pstmt.setTimestamp(++pos, v);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTime(Time v) throws SQLException {
|
||||
|
||||
@@ -125,6 +125,8 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
|
||||
private final boolean objectMapperPresent;
|
||||
|
||||
private final boolean java7Present;
|
||||
|
||||
// OPTIONAL ScalarTypes registered if Jackson/JsonNode is in the classpath
|
||||
|
||||
/**
|
||||
@@ -153,6 +155,7 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
*/
|
||||
public DefaultTypeManager(ServerConfig config, BootupClasses bootupClasses) {
|
||||
|
||||
this.java7Present = config.getClassLoadConfig().isJava7Present();
|
||||
this.jsonDateTime = config.getJsonDateTime();
|
||||
this.checkImmutable = new CheckImmutable(this);
|
||||
this.reflectScalarBuilder = new ReflectionBasedTypeBuilder(this);
|
||||
@@ -282,19 +285,33 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
/**
|
||||
* This can return null if no matching ScalarType is found.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> ScalarType<T> getScalarType(Class<T> type) {
|
||||
ScalarType<T> found = (ScalarType<T>) typeMap.get(type);
|
||||
public ScalarType<?> getScalarType(Class<?> type) {
|
||||
ScalarType<?> found = typeMap.get(type);
|
||||
if (found == null) {
|
||||
if (type.getName().equals("org.joda.time.LocalTime")) {
|
||||
throw new IllegalStateException(
|
||||
"ScalarType of Joda LocalTime not defined. You need to set ServerConfig.jodaLocalTimeMode to"
|
||||
+ " either 'normal' or 'utc'. UTC is the old mode using UTC timezone but local time zone is now preferred as 'normal' mode.");
|
||||
}
|
||||
found = checkInterfaceTypes(type);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private ScalarType<?> checkInterfaceTypes(Class<?> type) {
|
||||
if (java7Present) {
|
||||
return checkJava7InterfaceTypes(type);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ScalarType<?> checkJava7InterfaceTypes(Class<?> type) {
|
||||
if (java.nio.file.Path.class.isAssignableFrom(type)) {
|
||||
return typeMap.get(java.nio.file.Path.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ScalarDataReader<?> getScalarDataReader(Class<?> propertyType, int sqlType) {
|
||||
|
||||
if (sqlType == 0) {
|
||||
@@ -362,11 +379,11 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
* </p>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> ScalarType<T> getScalarType(Class<T> type, int jdbcType) {
|
||||
public ScalarType<?> getScalarType(Class<?> type, int jdbcType) {
|
||||
|
||||
// File is a special Lob so check for that first
|
||||
if (File.class.equals(type)) {
|
||||
return (ScalarType<T>) fileType;
|
||||
return fileType;
|
||||
}
|
||||
|
||||
// check for Clob, LongVarchar etc ...
|
||||
@@ -375,23 +392,23 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
ScalarType<?> scalarType = getLobTypes(jdbcType);
|
||||
if (scalarType != null) {
|
||||
// it is a specific Lob type...
|
||||
return (ScalarType<T>) scalarType;
|
||||
return scalarType;
|
||||
}
|
||||
|
||||
scalarType = typeMap.get(type);
|
||||
if (scalarType != null) {
|
||||
if (jdbcType == 0 || scalarType.getJdbcType() == jdbcType) {
|
||||
// matching type
|
||||
return (ScalarType<T>) scalarType;
|
||||
return scalarType;
|
||||
}
|
||||
}
|
||||
// a util Date with jdbcType not matching server wide settings
|
||||
if (type.equals(java.util.Date.class)) {
|
||||
return (ScalarType<T>) extraTypeFactory.createUtilDate(jsonDateTime, jdbcType);
|
||||
return extraTypeFactory.createUtilDate(jsonDateTime, jdbcType);
|
||||
}
|
||||
// a Calendar with jdbcType not matching server wide settings
|
||||
if (type.equals(java.util.Calendar.class)) {
|
||||
return (ScalarType<T>) extraTypeFactory.createCalendar(jsonDateTime, jdbcType);
|
||||
return extraTypeFactory.createCalendar(jsonDateTime, jdbcType);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unmatched ScalarType for " + type + " jdbcType:" + jdbcType);
|
||||
@@ -772,6 +789,11 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
}
|
||||
|
||||
protected void initialiseJavaTimeTypes(JsonConfig.DateTime mode, ServerConfig config) {
|
||||
|
||||
if (java7Present) {
|
||||
typeMap.put(java.nio.file.Path.class, new ScalarTypePath());
|
||||
}
|
||||
|
||||
if (config.getClassLoadConfig().isJavaTimePresent()) {
|
||||
logger.debug("Registering java.time data types");
|
||||
typeMap.put(java.time.LocalDate.class, new ScalarTypeLocalDate());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebeaninternal.server.core.Message;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -14,6 +15,7 @@ import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Calendar;
|
||||
|
||||
public class RsetDataReader implements DataReader {
|
||||
|
||||
@@ -23,11 +25,14 @@ public class RsetDataReader implements DataReader {
|
||||
|
||||
static final int stringInitialSize = 512;
|
||||
|
||||
private final DataTimeZone dataTimeZone;
|
||||
|
||||
private final ResultSet rset;
|
||||
|
||||
protected int pos;
|
||||
|
||||
public RsetDataReader(ResultSet rset) {
|
||||
public RsetDataReader(DataTimeZone dataTimeZone, ResultSet rset) {
|
||||
this.dataTimeZone = dataTimeZone;
|
||||
this.rset = rset;
|
||||
}
|
||||
|
||||
@@ -149,9 +154,13 @@ public class RsetDataReader implements DataReader {
|
||||
return rset.getTime(pos());
|
||||
}
|
||||
|
||||
|
||||
public Timestamp getTimestamp() throws SQLException {
|
||||
return rset.getTimestamp(pos());
|
||||
Calendar cal = dataTimeZone.getTimeZone();
|
||||
if (cal != null) {
|
||||
return rset.getTimestamp(pos(), cal);
|
||||
} else {
|
||||
return rset.getTimestamp(pos());
|
||||
}
|
||||
}
|
||||
|
||||
public String getStringFromStream() throws SQLException {
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
|
||||
public class RsetDataReaderIndexed extends RsetDataReader {
|
||||
|
||||
private final int[] rsetIndexPositions;
|
||||
|
||||
public RsetDataReaderIndexed(ResultSet rset, int[] rsetIndexPositions, boolean rowNumberIncluded) {
|
||||
super(rset);
|
||||
public RsetDataReaderIndexed(DataTimeZone dataTimeZone, ResultSet rset, int[] rsetIndexPositions, boolean rowNumberIncluded) {
|
||||
super(dataTimeZone, rset);
|
||||
if (!rowNumberIncluded) {
|
||||
this.rsetIndexPositions = rsetIndexPositions;
|
||||
} else {
|
||||
|
||||
@@ -6,7 +6,6 @@ import java.sql.Timestamp;
|
||||
import java.sql.Types;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
/**
|
||||
* ScalarType for java.sql.Timestamp.
|
||||
@@ -24,7 +23,7 @@ public class ScalarTypeLocalDateTime extends ScalarTypeBaseDateTime<LocalDateTim
|
||||
|
||||
@Override
|
||||
public long convertToMillis(LocalDateTime value) {
|
||||
return value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||
return Timestamp.valueOf(value).getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -33,20 +32,18 @@ public class ScalarTypeLocalDateTime extends ScalarTypeBaseDateTime<LocalDateTim
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toJsonISO8601(LocalDateTime value) {
|
||||
return value.atZone(ZoneId.systemDefault()).toInstant().toString();
|
||||
protected String toJsonISO8601(LocalDateTime dateTime) {
|
||||
return dateTime.atZone(ZoneId.systemDefault()).toInstant().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime convertFromTimestamp(Timestamp ts) {
|
||||
return ts.toLocalDateTime();
|
||||
public LocalDateTime convertFromTimestamp(Timestamp timestamp) {
|
||||
return timestamp.toLocalDateTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timestamp convertToTimestamp(LocalDateTime t) {
|
||||
|
||||
ZonedDateTime zonedDateTime = t.atZone(ZoneId.systemDefault());
|
||||
return Timestamp.from(zonedDateTime.toInstant());
|
||||
public Timestamp convertToTimestamp(LocalDateTime dateTime) {
|
||||
return Timestamp.valueOf(dateTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* ScalarType for java.nio.file.Path which converts to and from a VARCHAR database column.
|
||||
*/
|
||||
public class ScalarTypePath extends ScalarTypeBaseVarchar<Path> {
|
||||
|
||||
public ScalarTypePath() {
|
||||
super(Path.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path convertFromDbString(String dbValue) {
|
||||
try {
|
||||
return Paths.get(new URI(dbValue));
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException("Error with Path URI [" + dbValue + "] " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertToDbString(Path beanValue) {
|
||||
return beanValue.toUri().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(Path path) {
|
||||
return convertToDbString(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path parse(String value) {
|
||||
return convertFromDbString(value);
|
||||
}
|
||||
}
|
||||
@@ -42,14 +42,14 @@ public interface TypeManager {
|
||||
/**
|
||||
* Return the ScalarType for a given logical type.
|
||||
*/
|
||||
<T> ScalarType<T> getScalarType(Class<T> type);
|
||||
ScalarType<?> getScalarType(Class<?> type);
|
||||
|
||||
/**
|
||||
* For java.util.Date and java.util.Calendar additionally pass the jdbc type
|
||||
* that you would like the ScalarType to map to. This is because these types
|
||||
* can map to different java.sql.Types depending on the property.
|
||||
*/
|
||||
<T> ScalarType<T> getScalarType(Class<T> type, int jdbcType);
|
||||
ScalarType<?> getScalarType(Class<?> type, int jdbcType);
|
||||
|
||||
/**
|
||||
* Create a ScalarType for an Enum using a mapping (rather than JPA Ordinal
|
||||
|
||||
@@ -30,7 +30,6 @@ public class EbeanServer_deleteAllByIdTest {
|
||||
List<String> loggedSql = LoggedSqlCollector.stop();
|
||||
assertThat(loggedSql).hasSize(1);
|
||||
assertThat(loggedSql.get(0)).contains("delete from e_basicver where id in (?,?,?)");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -59,6 +58,53 @@ public class EbeanServer_deleteAllByIdTest {
|
||||
assertThat(loggedSql.get(0)).contains("delete from e_basicver where id in (?,?,?)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteAllPermanentById() {
|
||||
|
||||
List<EBasicVer> someBeans = beans(3);
|
||||
|
||||
Ebean.saveAll(someBeans);
|
||||
List<Integer> ids = new ArrayList<Integer>();
|
||||
for (EBasicVer someBean : someBeans) {
|
||||
ids.add(someBean.getId());
|
||||
}
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Ebean.deleteAllPermanent(EBasicVer.class, ids);
|
||||
|
||||
List<String> loggedSql = LoggedSqlCollector.stop();
|
||||
assertThat(loggedSql).hasSize(1);
|
||||
assertThat(loggedSql.get(0)).contains("delete from e_basicver where id in (?,?,?)");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void deleteAllPermanentById_withTransaction() {
|
||||
|
||||
List<EBasicVer> someBeans = beans(3);
|
||||
|
||||
Ebean.saveAll(someBeans);
|
||||
List<Integer> ids = new ArrayList<Integer>();
|
||||
for (EBasicVer someBean : someBeans) {
|
||||
ids.add(someBean.getId());
|
||||
}
|
||||
|
||||
EbeanServer server = Ebean.getDefaultServer();
|
||||
// act
|
||||
LoggedSqlCollector.start();
|
||||
Transaction txn = server.beginTransaction();
|
||||
try {
|
||||
server.deleteAllPermanent(EBasicVer.class, ids, txn);
|
||||
txn.commit();
|
||||
} finally {
|
||||
txn.end();
|
||||
}
|
||||
List<String> loggedSql = LoggedSqlCollector.stop();
|
||||
assertThat(loggedSql).hasSize(1);
|
||||
assertThat(loggedSql.get(0)).contains("delete from e_basicver where id in (?,?,?)");
|
||||
}
|
||||
|
||||
private List<EBasicVer> beans(int count) {
|
||||
List<EBasicVer> beans = new ArrayList<EBasicVer>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
|
||||
@@ -12,10 +12,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
public class EbeanServer_deleteByIdTest {
|
||||
|
||||
@Test
|
||||
public void deleteAllById() {
|
||||
public void deleteById() {
|
||||
|
||||
EBasicVer someBean = bean("foo1");
|
||||
|
||||
Ebean.save(someBean);
|
||||
|
||||
// act
|
||||
@@ -28,7 +27,23 @@ public class EbeanServer_deleteByIdTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteAllById_withTransaction() {
|
||||
public void deletePermanentById() {
|
||||
|
||||
EBasicVer someBean = bean("foo1");
|
||||
Ebean.save(someBean);
|
||||
|
||||
// act
|
||||
LoggedSqlCollector.start();
|
||||
Ebean.deletePermanent(EBasicVer.class, someBean.getId());
|
||||
|
||||
List<String> loggedSql = LoggedSqlCollector.stop();
|
||||
assertThat(loggedSql).hasSize(1);
|
||||
assertThat(loggedSql.get(0)).contains("delete from e_basicver where id = ?");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void deleteById_withTransaction() {
|
||||
|
||||
EBasicVer someBean = bean("foo1");
|
||||
Ebean.save(someBean);
|
||||
@@ -48,6 +63,27 @@ public class EbeanServer_deleteByIdTest {
|
||||
assertThat(loggedSql.get(0)).contains("delete from e_basicver where id = ?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deletePermanentById_withTransaction() {
|
||||
|
||||
EBasicVer someBean = bean("foo2");
|
||||
Ebean.save(someBean);
|
||||
|
||||
EbeanServer server = Ebean.getDefaultServer();
|
||||
// act
|
||||
LoggedSqlCollector.start();
|
||||
Transaction txn = server.beginTransaction();
|
||||
try {
|
||||
server.deletePermanent(EBasicVer.class, someBean.getId(), txn);
|
||||
txn.commit();
|
||||
} finally {
|
||||
txn.end();
|
||||
}
|
||||
List<String> loggedSql = LoggedSqlCollector.stop();
|
||||
assertThat(loggedSql).hasSize(1);
|
||||
assertThat(loggedSql.get(0)).contains("delete from e_basicver where id = ?");
|
||||
}
|
||||
|
||||
private EBasicVer bean(String name) {
|
||||
EBasicVer bean = new EBasicVer();
|
||||
bean.setName(name);
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class PropertyTest {
|
||||
|
||||
@Test
|
||||
public void equals() throws Exception {
|
||||
|
||||
assertEquals(prop("foo", true), prop("foo", true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void diff_basic() throws Exception {
|
||||
|
||||
assertNotEquals(prop("foo", true), prop("bar", true));
|
||||
assertNotEquals(prop("foo", true), prop("foo", false));
|
||||
assertNotEquals(prop("foo", false), prop("foo", true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void diff_nulls() throws Exception {
|
||||
|
||||
assertEquals(prop("foo", true, "nulls", "high"), prop("foo", true, "nulls", "high"));
|
||||
assertEquals(prop("foo", true, "nulls", "low"), prop("foo", true, "nulls", "low"));
|
||||
|
||||
assertNotEquals(prop("foo", true), prop("foo", true, "nulls", "high"));
|
||||
assertNotEquals(prop("foo", true, "nulls", "high"), prop("foo", true));
|
||||
assertNotEquals(prop("foo", true, "nulls", "high"), prop("foo", true, "nulls", "low"));
|
||||
assertNotEquals(prop("foo", true, "nulls", "low"), prop("foo", true, "nulls", "high"));
|
||||
}
|
||||
|
||||
private OrderBy.Property prop(String name, boolean asc) {
|
||||
return new OrderBy.Property(name, asc, null, null);
|
||||
}
|
||||
|
||||
private OrderBy.Property prop(String name, boolean asc, String nulls, String highLow) {
|
||||
return new OrderBy.Property(name, asc, nulls, highLow);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -89,7 +89,7 @@ public class TestTypeManager extends BaseTestCase {
|
||||
String val;
|
||||
|
||||
public DummyDataReader(String val) {
|
||||
super(null);
|
||||
super(null, null);
|
||||
this.val = val;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.avaje.ebean.text.json.JsonContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.query.CQuery;
|
||||
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
@@ -44,6 +45,11 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataTimeZone getDataTimeZone() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiServer getPluginApi() {
|
||||
return null;
|
||||
@@ -569,6 +575,16 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deletePermanent(Class<?> beanType, Object id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deletePermanent(Class<?> beanType, Object id, Transaction transaction) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int execute(SqlUpdate updSql) {
|
||||
return 0;
|
||||
@@ -750,13 +766,23 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAll(Class<?> beanType, Collection<?> ids) {
|
||||
|
||||
public int deleteAll(Class<?> beanType, Collection<?> ids) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAll(Class<?> beanType, Collection<?> ids, Transaction transaction) {
|
||||
public int deleteAll(Class<?> beanType, Collection<?> ids, Transaction transaction) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteAllPermanent(Class<?> beanType, Collection<?> ids) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteAllPermanent(Class<?> beanType, Collection<?> ids, Transaction transaction) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+11
@@ -5,6 +5,7 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@@ -14,6 +15,16 @@ public class InsertTimestampFactoryTest {
|
||||
|
||||
InsertTimestampFactory factory = new InsertTimestampFactory(new ClassLoadConfig());
|
||||
|
||||
@Test
|
||||
public void test_createdTimestamp_Instant() {
|
||||
|
||||
DeployBeanProperty prop = new DeployBeanProperty(null, Instant.class, null, null);
|
||||
|
||||
GeneratedProperty insertTimestamp = factory.createInsertTimestamp(prop);
|
||||
Object value = insertTimestamp.getInsertValue(null, null, System.currentTimeMillis());
|
||||
assertTrue(value instanceof Instant);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_createdTimestamp_LocalDateTime() {
|
||||
|
||||
|
||||
+11
@@ -5,6 +5,7 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@@ -15,6 +16,16 @@ public class UpdateTimestampFactoryTest {
|
||||
|
||||
UpdateTimestampFactory factory = new UpdateTimestampFactory(new ClassLoadConfig());
|
||||
|
||||
@Test
|
||||
public void test_createdTimestamp_Instant() {
|
||||
|
||||
DeployBeanProperty prop = new DeployBeanProperty(null, Instant.class, null, null);
|
||||
|
||||
GeneratedProperty generatedProperty = factory.createUpdateTimestamp(prop);
|
||||
Object value = generatedProperty.getInsertValue(null, null, System.currentTimeMillis());
|
||||
assertTrue(value instanceof Instant);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_createdTimestamp_LocalDateTime() {
|
||||
|
||||
|
||||
@@ -16,16 +16,25 @@ public class ScalarTypeLocalDateTimeTest {
|
||||
LocalDateTime warmUp = LocalDateTime.now();
|
||||
|
||||
@Test
|
||||
public void testConvertToMillis() throws Exception {
|
||||
public void testNowToMillis() throws Exception {
|
||||
|
||||
warmUp.hashCode();
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
long toMillis = type.convertToMillis(LocalDateTime.now());
|
||||
|
||||
assertTrue(toMillis - now < 30);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToMillis() throws Exception {
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
long asMillis = type.convertToMillis(now);
|
||||
LocalDateTime fromMillis = type.convertFromMillis(asMillis);
|
||||
|
||||
assertEquals(now, fromMillis);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertFromTimestamp() throws Exception {
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ScalarTypePathTest {
|
||||
|
||||
private ScalarTypePath type = new ScalarTypePath();
|
||||
|
||||
@Test
|
||||
public void convertFromDbString() throws Exception {
|
||||
|
||||
Path path = Paths.get("/tmp");
|
||||
|
||||
String asString = type.convertToDbString(path);
|
||||
Path converted = type.convertFromDbString(asString);
|
||||
|
||||
assertEquals(path, converted);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formatAndParse() throws Exception {
|
||||
|
||||
Path path = Paths.get("/tmp");
|
||||
|
||||
String asString = type.formatValue(path);
|
||||
Path converted = type.parse(asString);
|
||||
|
||||
assertEquals(path, converted);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -82,7 +82,6 @@ public class DocLinkTest extends BaseTestCase {
|
||||
Ebean.delete(link1);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDeletePermanent_whenPublished2() {
|
||||
|
||||
|
||||
@@ -31,16 +31,19 @@ public class TestGeneratedProperties extends BaseTestCase {
|
||||
assertNotNull(bean.getZdtUpdated());
|
||||
assertNotNull(bean.getLongCreated());
|
||||
assertNotNull(bean.getLongUpdated());
|
||||
assertNotNull(bean.getInstantCreated());
|
||||
assertNotNull(bean.getInstantUpdated());
|
||||
|
||||
assertThat(bean.getWhenCreated().toInstant().toEpochMilli()).isEqualTo(bean.getLongCreated());
|
||||
assertThat(bean.getWhenModified().toInstant().toEpochMilli()).isEqualTo(bean.getLongCreated());
|
||||
assertThat(bean.getWhenModified().toInstant().toEpochMilli()).isEqualTo(bean.getLongCreated());
|
||||
|
||||
bean.setName("updating...");
|
||||
Ebean.save(bean);
|
||||
|
||||
assertThat(bean.getWhenModified()).isNotEqualTo(bean.getLongCreated());
|
||||
assertThat(bean.getWhenModified().toInstant().toEpochMilli()).isEqualTo(bean.getLongUpdated());
|
||||
assertThat(bean.getInstantUpdated().toEpochMilli()).isEqualTo(bean.getLongUpdated());
|
||||
assertThat(bean.getInstantCreated().toEpochMilli()).isEqualTo(bean.getLongCreated());
|
||||
|
||||
Ebean.delete(bean);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
@@ -54,6 +55,12 @@ public class EGenProps {
|
||||
@UpdatedTimestamp
|
||||
ZonedDateTime zdtUpdated;
|
||||
|
||||
@CreatedTimestamp
|
||||
Instant instantCreated;
|
||||
|
||||
@UpdatedTimestamp
|
||||
Instant instantUpdated;
|
||||
|
||||
@CreatedTimestamp
|
||||
long longCreated;
|
||||
|
||||
@@ -179,4 +186,20 @@ public class EGenProps {
|
||||
public void setLongUpdated(long longUpdated) {
|
||||
this.longUpdated = longUpdated;
|
||||
}
|
||||
|
||||
public Instant getInstantCreated() {
|
||||
return instantCreated;
|
||||
}
|
||||
|
||||
public void setInstantCreated(Instant instantCreated) {
|
||||
this.instantCreated = instantCreated;
|
||||
}
|
||||
|
||||
public Instant getInstantUpdated() {
|
||||
return instantUpdated;
|
||||
}
|
||||
|
||||
public void setInstantUpdated(Instant instantUpdated) {
|
||||
this.instantUpdated = instantUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.avaje.tests.model.basic;
|
||||
import com.avaje.ebean.annotation.ChangeLog;
|
||||
import com.avaje.ebean.annotation.DocEmbedded;
|
||||
import com.avaje.ebean.annotation.DocStore;
|
||||
import com.avaje.ebean.annotation.DocStoreEmbedded;
|
||||
import com.avaje.ebean.annotation.Formula;
|
||||
import com.avaje.ebean.annotation.WhenCreated;
|
||||
import com.avaje.ebean.annotation.Where;
|
||||
@@ -102,7 +101,7 @@ public class Order implements Serializable {
|
||||
@Where(clause = "${ta}.id > 0")
|
||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "order")
|
||||
@OrderBy("id asc, orderQty asc, cretime desc")
|
||||
@DocStoreEmbedded
|
||||
@DocEmbedded
|
||||
List<OrderDetail> details;
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "order")
|
||||
|
||||
@@ -17,6 +17,13 @@ public class Album extends BaseModel {
|
||||
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
|
||||
private Cover cover;
|
||||
|
||||
public Album(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Album() {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,13 @@ public class Cover extends Model {
|
||||
|
||||
protected String s3Url;
|
||||
|
||||
public Cover() {
|
||||
}
|
||||
|
||||
public Cover(String s3Url) {
|
||||
this.s3Url = s3Url;
|
||||
}
|
||||
|
||||
public String getS3Url() {
|
||||
return this.s3Url;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
package com.avaje.tests.model.onetoone.album;
|
||||
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.Transaction;
|
||||
import org.avaje.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class DeleteById_SoftDelete_Tests {
|
||||
|
||||
@Test
|
||||
public void ebean_deleteById_when_softDelete() {
|
||||
|
||||
Cover cover = new Cover("a1");
|
||||
cover.save();
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Ebean.delete(Cover.class, cover.getId());
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("update cover set deleted=true where id = ?");
|
||||
|
||||
cover.deletePermanent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteById_when_softDelete() {
|
||||
|
||||
Cover cover = new Cover("a1");
|
||||
cover.save();
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Ebean.getDefaultServer().delete(Cover.class, cover.getId(), null);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("update cover set deleted=true where id = ?");
|
||||
|
||||
cover.deletePermanent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deletePermanentById_when_softDelete() {
|
||||
|
||||
Cover cover = new Cover("a2");
|
||||
cover.save();
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Ebean.getDefaultServer().deletePermanent(Cover.class, cover.getId(), null);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("delete from cover where id = ?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteAllById_when_softDelete() {
|
||||
|
||||
EbeanServer server = Ebean.getDefaultServer();
|
||||
List<Cover> beans = beans(2);
|
||||
server.saveAll(beans);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
server.deleteAll(Cover.class, ids(beans));
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("update cover set deleted=true where id in (?,?)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteAllById_when_softDelete_withTransaction() {
|
||||
|
||||
EbeanServer server = Ebean.getDefaultServer();
|
||||
List<Cover> beans = beans(2);
|
||||
server.saveAll(beans);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Transaction transaction = server.beginTransaction();
|
||||
try {
|
||||
server.deleteAll(Cover.class, ids(beans), transaction);
|
||||
server.commitTransaction();
|
||||
} finally {
|
||||
server.endTransaction();
|
||||
}
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("update cover set deleted=true where id in (?,?)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ebean_deleteAllPermanentById_when_softDelete() {
|
||||
|
||||
List<Cover> beans = beans(2);
|
||||
Ebean.saveAll(beans);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Ebean.deleteAllPermanent(Cover.class, ids(beans));
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("delete from cover where id in (?,?)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteAllPermanentById_when_softDelete() {
|
||||
|
||||
EbeanServer server = Ebean.getDefaultServer();
|
||||
List<Cover> beans = beans(2);
|
||||
server.saveAll(beans);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
server.deleteAllPermanent(Cover.class, ids(beans));
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("delete from cover where id in (?,?)");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void deleteAllPermanentById_when_softDelete_withTransaction() {
|
||||
|
||||
EbeanServer server = Ebean.getDefaultServer();
|
||||
List<Cover> beans = beans(2);
|
||||
server.saveAll(beans);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Transaction transaction = server.beginTransaction();
|
||||
try {
|
||||
server.deleteAllPermanent(Cover.class, ids(beans), transaction);
|
||||
server.commitTransaction();
|
||||
} finally {
|
||||
server.endTransaction();
|
||||
}
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("delete from cover where id in (?,?)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ebean_deleteAll_when_softDelete() {
|
||||
|
||||
List<Cover> beans = beans(2);
|
||||
Ebean.saveAll(beans);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Ebean.deleteAll(beans);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("update cover set s3url=?, deleted=? where id=?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteAll_when_softDelete() {
|
||||
|
||||
EbeanServer server = Ebean.getDefaultServer();
|
||||
List<Cover> beans = beans(2);
|
||||
server.saveAll(beans);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
server.deleteAll(beans);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("update cover set s3url=?, deleted=? where id=?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteAll_when_softDelete_withTransaction() {
|
||||
|
||||
EbeanServer server = Ebean.getDefaultServer();
|
||||
List<Cover> beans = beans(2);
|
||||
server.saveAll(beans);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Transaction transaction = server.beginTransaction();
|
||||
try {
|
||||
server.deleteAll(beans, transaction);
|
||||
server.commitTransaction();
|
||||
} finally {
|
||||
server.endTransaction();
|
||||
}
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("update cover set s3url=?, deleted=? where id=?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteAllPermanent_when_softDelete() {
|
||||
|
||||
EbeanServer server = Ebean.getDefaultServer();
|
||||
List<Cover> beans = beans(2);
|
||||
server.saveAll(beans);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
server.deleteAllPermanent(beans);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from cover where id=?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteAllPermanent_when_softDelete_withTransaction() {
|
||||
|
||||
EbeanServer server = Ebean.getDefaultServer();
|
||||
List<Cover> beans = beans(2);
|
||||
server.saveAll(beans);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Transaction transaction = server.beginTransaction();
|
||||
try {
|
||||
server.deleteAllPermanent(beans, transaction);
|
||||
server.commitTransaction();
|
||||
} finally {
|
||||
server.endTransaction();
|
||||
}
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from cover where id=?");
|
||||
}
|
||||
|
||||
private List<Long> ids(List<Cover> beans) {
|
||||
List<Long> ids = new ArrayList<Long>();
|
||||
for (Cover someBean : beans) {
|
||||
ids.add(someBean.getId());
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
private List<Cover> beans(int count) {
|
||||
List<Cover> beans = new ArrayList<Cover>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
beans.add(new Cover("delById " + i));
|
||||
}
|
||||
return beans;
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ public class TestOneToOneHardDelete {
|
||||
assertThat(sql.get(1)).contains("update album set deleted=?, last_update=? where id=?");
|
||||
assertThat(sql.get(2)).contains("update cover set deleted=? where id=?");
|
||||
|
||||
Album found2 = Album.find.where().includeSoftDeletes().findUnique();
|
||||
Album found2 = Album.find.where().setIncludeSoftDeletes().findUnique();
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
import java.nio.file.Path;
|
||||
import java.time.*;
|
||||
|
||||
@Entity
|
||||
@@ -41,6 +42,8 @@ public class SomeNewTypesBean {
|
||||
|
||||
ZoneOffset zoneOffset;
|
||||
|
||||
Path path;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
@@ -144,4 +147,12 @@ public class SomeNewTypesBean {
|
||||
public void setZoneOffset(ZoneOffset zoneOffset) {
|
||||
this.zoneOffset = zoneOffset;
|
||||
}
|
||||
|
||||
public Path getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(Path path) {
|
||||
this.path = path;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.tests.rawsql;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import com.avaje.tests.model.basic.Order;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -17,6 +18,8 @@ import com.avaje.ebean.RawSqlBuilder;
|
||||
import com.avaje.tests.model.basic.Customer;
|
||||
import com.avaje.tests.model.basic.ResetBasicData;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestRawSqlOrmQuery extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
@@ -107,4 +110,71 @@ public class TestRawSqlOrmQuery extends BaseTestCase {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPaging_with_existingRawSqlOrderBy_expect_id_appendToOrderBy() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
RawSql rawSql = RawSqlBuilder.parse("select o.id, o.order_date, o.ship_date from o_order o order by o.ship_date desc nulls last")
|
||||
.columnMapping("o.id", "id")
|
||||
.columnMapping("o.order_date", "orderDate")
|
||||
.columnMapping("o.ship_date", "shipDate")
|
||||
.create();
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class);
|
||||
query.setUseCache(false).setUseQueryCache(false);
|
||||
query.setRawSql(rawSql);
|
||||
|
||||
query.setMaxRows(100);
|
||||
query.findList();
|
||||
|
||||
assertThat(query.getGeneratedSql()).contains("order by o.ship_date desc nulls last, o.id limit 100");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPaging_when_setOrderBy_expect_id_appendToOrderBy() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
RawSql rawSql = RawSqlBuilder.parse("select o.id, o.order_date, o.ship_date from o_order o order by o.ship_date desc nulls last")
|
||||
.columnMapping("o.id", "id")
|
||||
.columnMapping("o.order_date", "orderDate")
|
||||
.columnMapping("o.ship_date", "shipDate")
|
||||
.create();
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class);
|
||||
query.setUseCache(false).setUseQueryCache(false);
|
||||
query.setRawSql(rawSql);
|
||||
|
||||
query.setMaxRows(100);
|
||||
query.order("coalesce(shipDate, now()) desc");
|
||||
query.findList();
|
||||
|
||||
assertThat(query.getGeneratedSql()).contains("order by coalesce(o.ship_date, now()) desc, o.id limit 100");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPaging_when_setOrderBy_containsId_expect_leaveAsIs() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
RawSql rawSql = RawSqlBuilder.parse("select o.id, o.order_date, o.ship_date from o_order o order by o.ship_date desc nulls last")
|
||||
.columnMapping("o.id", "id")
|
||||
.columnMapping("o.order_date", "orderDate")
|
||||
.columnMapping("o.ship_date", "shipDate")
|
||||
.create();
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class);
|
||||
query.setUseCache(false).setUseQueryCache(false);
|
||||
query.setRawSql(rawSql);
|
||||
|
||||
query.setMaxRows(100);
|
||||
query.order("id desc");
|
||||
PagedList<Order> pagedList = query.findPagedList();
|
||||
pagedList.getList();
|
||||
pagedList.getTotalRowCount();
|
||||
|
||||
assertThat(query.getGeneratedSql()).contains("order by o.id desc limit 100");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.avaje.tests.timezone;
|
||||
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.Transaction;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Calendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Must run this manually with various JVM timezones for insert and fetch.
|
||||
*/
|
||||
public class TimezoneTests {
|
||||
|
||||
private final Timestamp nowTs = new Timestamp(1460000000000L);
|
||||
|
||||
public TimezoneTests() {
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void rawJdbc() throws SQLException {
|
||||
|
||||
// insert("Local");
|
||||
// insert("UTC");
|
||||
// insert("America/Los_Angeles");
|
||||
|
||||
// System.out.println("Local");
|
||||
// fetch();
|
||||
|
||||
// System.out.println("UTC");
|
||||
// setZone("UTC");
|
||||
// fetch();
|
||||
|
||||
System.out.println("LA");
|
||||
setZone("America/Los_Angeles");
|
||||
fetch();
|
||||
}
|
||||
|
||||
private void fetch() throws SQLException {
|
||||
Transaction transaction = Ebean.beginTransaction();
|
||||
Connection connection = transaction.getConnection();
|
||||
|
||||
PreparedStatement statement = connection.prepareStatement("select * from tztest");
|
||||
ResultSet resultSet = statement.executeQuery();
|
||||
while (resultSet.next()) {
|
||||
System.out.println(" zone:"+resultSet.getString("zone"));
|
||||
System.out.println(" ts:"+tsof(resultSet.getTimestamp("ts")));
|
||||
System.out.println(" tstz:"+tsof(resultSet.getTimestamp("tstz")));
|
||||
System.out.println(" ts1:"+tsof(resultSet.getTimestamp("ts1", cal())));
|
||||
System.out.println("tstz1:"+tsof(resultSet.getTimestamp("tstz1", cal())));
|
||||
}
|
||||
System.out.println("");
|
||||
resultSet.close();
|
||||
statement.close();
|
||||
transaction.end();
|
||||
}
|
||||
|
||||
private String tsof(Timestamp timestamp) {
|
||||
return ""+timestamp.getTime()+", "+timestamp.toString()+", "+timestamp.toInstant();
|
||||
}
|
||||
|
||||
private void setZone(String zone) {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone(zone));
|
||||
}
|
||||
|
||||
private void insert(String zone) throws SQLException {
|
||||
|
||||
String insert = "insert into tztest (zone, ts, tstz, ts1, tstz1) values (?,?,?,?,?)";
|
||||
|
||||
if (!zone.equalsIgnoreCase("local")) {
|
||||
setZone(zone);
|
||||
}
|
||||
|
||||
Transaction transaction = Ebean.beginTransaction();
|
||||
Connection connection = transaction.getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(insert);
|
||||
statement.setString(1, zone);
|
||||
statement.setTimestamp(2, nowTs);
|
||||
statement.setTimestamp(3, nowTs);
|
||||
statement.setTimestamp(4, nowTs, cal());
|
||||
statement.setTimestamp(5, nowTs, cal());
|
||||
statement.executeUpdate();
|
||||
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
private Calendar cal() {
|
||||
|
||||
Calendar instance = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
|
||||
//Calendar instance = Calendar.getInstance(TimeZone.getDefault());
|
||||
return (Calendar) instance.clone();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.avaje.tests.timezone;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
@Entity
|
||||
@Table(name="tz_bean")
|
||||
public class TzBean {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
@Column(name = "mode")
|
||||
String mode;
|
||||
|
||||
Timestamp ts;
|
||||
Timestamp tstz;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public void setMode(String mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public Timestamp getTs() {
|
||||
return ts;
|
||||
}
|
||||
|
||||
public void setTs(Timestamp ts) {
|
||||
this.ts = ts;
|
||||
}
|
||||
|
||||
public Timestamp getTstz() {
|
||||
return tstz;
|
||||
}
|
||||
|
||||
public void setTstz(Timestamp tstz) {
|
||||
this.tstz = tstz;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.avaje.tests.timezone;
|
||||
|
||||
import com.avaje.ebean.Ebean;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
//import java.util.TimeZone;
|
||||
|
||||
|
||||
|
||||
// Postgres
|
||||
// manually create table
|
||||
// create table tz_bean (id serial, mode varchar(40), ts timestamp, tstz timestamp with time zone);
|
||||
|
||||
// MySql
|
||||
// manually create table (note there is no timestamp with time zone type)
|
||||
// create table tz_bean (id serial, mode varchar(40), ts timestamp, tstz timestamp);
|
||||
|
||||
// Oracle
|
||||
// create table tz_bean (id integer, run_mode varchar(40), ts timestamp, tstz timestamp);
|
||||
|
||||
/**
|
||||
* Must run this test manually with various time zones for insert and fetch.
|
||||
*/
|
||||
public class TzBeanTests {
|
||||
|
||||
private final Timestamp nowTs = new Timestamp(1460000000000L);
|
||||
|
||||
public TzBeanTests() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Run this 5 times with different modes.
|
||||
*/
|
||||
@Ignore
|
||||
@Test
|
||||
public void insert_one_at_a_time() {
|
||||
|
||||
//String mode = "noCal + local tz";
|
||||
|
||||
// String mode = "noCal + LA";
|
||||
// TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
|
||||
|
||||
// String mode = "noCal + UTC";
|
||||
// TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||
|
||||
// System.setProperty("ebean.dataTimeZone", "UTC");
|
||||
// TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
|
||||
// String mode = "UTC + LA";
|
||||
|
||||
System.setProperty("ebean.dataTimeZone", "UTC");
|
||||
String mode = "UTC + NZST";
|
||||
|
||||
TzBean bean = new TzBean();
|
||||
bean.setMode(mode);
|
||||
bean.setTs(nowTs);
|
||||
bean.setTstz(nowTs);
|
||||
|
||||
Ebean.save(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* In separate JVM/execution fetch the beans with various timezones etc.
|
||||
*/
|
||||
@Ignore
|
||||
@Test
|
||||
public void fetch_beans() {
|
||||
|
||||
|
||||
// set the jvm timezone
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||
// TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
|
||||
|
||||
// set the Calendar time zone to use in JDBC calls
|
||||
System.setProperty("ebean.dataTimeZone", "UTC");
|
||||
|
||||
List<TzBean> list = Ebean.find(TzBean.class)
|
||||
.findList();
|
||||
|
||||
for (TzBean bean : list) {
|
||||
System.out.println(" mode:"+bean.getMode());
|
||||
System.out.println(" ts:"+tsof(bean.getTs()));
|
||||
System.out.println(" tstz:"+tsof(bean.getTstz()));
|
||||
}
|
||||
}
|
||||
|
||||
private String tsof(Timestamp timestamp) {
|
||||
return ""+timestamp.getTime()+", "+timestamp.toString()+", "+timestamp.toInstant();
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import com.avaje.tests.model.types.SomeNewTypesBean;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.*;
|
||||
import java.util.List;
|
||||
|
||||
@@ -30,6 +31,7 @@ public class TestNewTypes extends BaseTestCase {
|
||||
bean.setZoneId(ZoneId.systemDefault());
|
||||
bean.setZoneOffset(ZonedDateTime.now().getOffset());
|
||||
bean.setYearMonth(YearMonth.of(2014, 9));
|
||||
bean.setPath(Paths.get("/tmp"));
|
||||
|
||||
|
||||
Ebean.save(bean);
|
||||
@@ -69,6 +71,9 @@ public class TestNewTypes extends BaseTestCase {
|
||||
list = Ebean.find(SomeNewTypesBean.class).where().le("month", Month.SEPTEMBER).findList();
|
||||
assertTrue(!list.isEmpty());
|
||||
|
||||
list = Ebean.find(SomeNewTypesBean.class).where().eq("path", Paths.get("/tmp")).findList();
|
||||
assertTrue(!list.isEmpty());
|
||||
|
||||
SomeNewTypesBean fetched = Ebean.find(SomeNewTypesBean.class, bean.getId());
|
||||
|
||||
assertEquals(bean.getZoneId(), fetched.getZoneId());
|
||||
@@ -80,6 +85,7 @@ public class TestNewTypes extends BaseTestCase {
|
||||
assertEquals(bean.getLocalDateTime(), fetched.getLocalDateTime());
|
||||
assertEquals(bean.getOffsetDateTime(), fetched.getOffsetDateTime());
|
||||
assertEquals(bean.getInstant(), fetched.getInstant());
|
||||
assertEquals(bean.getPath(), fetched.getPath());
|
||||
|
||||
String asJson = Ebean.json().toJson(fetched);
|
||||
|
||||
@@ -94,6 +100,7 @@ public class TestNewTypes extends BaseTestCase {
|
||||
assertEquals(bean.getLocalDateTime(), toBean.getLocalDateTime());
|
||||
assertEquals(bean.getOffsetDateTime(), toBean.getOffsetDateTime());
|
||||
assertEquals(bean.getInstant(), toBean.getInstant());
|
||||
assertEquals(bean.getPath(), toBean.getPath());
|
||||
|
||||
}
|
||||
|
||||
@@ -115,6 +122,7 @@ public class TestNewTypes extends BaseTestCase {
|
||||
assertNull(fetched.getLocalDateTime());
|
||||
assertNull(fetched.getOffsetDateTime());
|
||||
assertNull(fetched.getInstant());
|
||||
assertNull(fetched.getPath());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,28 @@ public class TestOrderByParse extends BaseTestCase {
|
||||
assertFalse(o1.containsProperty("junk"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseNullsHigh() {
|
||||
|
||||
OrderBy<Object> o1 = new OrderBy<Object>("id desc nulls high");
|
||||
assertTrue(o1.getProperties().size() == 1);
|
||||
assertTrue(o1.getProperties().get(0).getProperty().equals("id"));
|
||||
assertTrue(!o1.getProperties().get(0).isAscending());
|
||||
assertTrue(o1.toStringFormat().equals("id desc nulls high"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseNullsHigh_with_second() {
|
||||
|
||||
OrderBy<Object> o1 = new OrderBy<Object>("id desc nulls high, name");
|
||||
assertTrue(o1.getProperties().size() == 2);
|
||||
assertTrue(o1.getProperties().get(0).getProperty().equals("id"));
|
||||
assertTrue(!o1.getProperties().get(0).isAscending());
|
||||
assertTrue(o1.toStringFormat().equals("id desc nulls high, name"));
|
||||
assertTrue(o1.getProperties().get(1).getProperty().equals("name"));
|
||||
assertTrue(o1.getProperties().get(1).isAscending());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsingTwo() {
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@ datasource.hsqldb.databaseDriver=org.hsqldb.jdbcDriver
|
||||
datasource.mysql.username=unit
|
||||
datasource.mysql.password=unit
|
||||
datasource.mysql.databaseUrl=jdbc:mysql://127.0.0.1:3306/unit
|
||||
#datasource.mysql.databaseUrl=jdbc:mysql://127.0.0.1:3306/unit?useLegacyDatetimeCode=false
|
||||
datasource.mysql.databaseDriver=com.mysql.jdbc.Driver
|
||||
|
||||
|
||||
@@ -110,6 +111,9 @@ datasource.ora.password=unit
|
||||
datasource.ora.databaseUrl=jdbc:oracle:thin:@//127.0.0.1:1521/orcl
|
||||
datasource.ora.databaseDriver=oracle.jdbc.driver.OracleDriver
|
||||
|
||||
## set this to use timestamp rather than timestamptz
|
||||
#ebean.postgres.timestamp=timestamp
|
||||
|
||||
datasource.pg.username=unit
|
||||
datasource.pg.password=unit
|
||||
datasource.pg.databaseUrl=jdbc:postgresql://127.0.0.1:5432/unit
|
||||
|
||||
Reference in New Issue
Block a user