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 | |
|---|---|---|---|
|
|
b4d49161af | ||
|
|
8a68e72305 | ||
|
|
07fe1cde6b | ||
|
|
9da5ce01a7 | ||
|
|
5bcfce9cd4 | ||
|
|
a6d276d4d3 | ||
|
|
5b100e6d37 | ||
|
|
bf75af85c1 | ||
|
|
5cf2d3c39a | ||
|
|
840c5f8992 | ||
|
|
f867cd4c01 | ||
|
|
ad17da5b36 | ||
|
|
9350155c00 | ||
|
|
8a84eaf7a7 | ||
|
|
f0fab77ff7 | ||
|
|
41aace9dc4 | ||
|
|
7c9d752c3d | ||
|
|
81531b6cda | ||
|
|
b70f3574d9 | ||
|
|
be214273cc | ||
|
|
69c2810fbe | ||
|
|
82da81116e | ||
|
|
7d5e135f56 |
@@ -0,0 +1,9 @@
|
||||
## Expected behavior
|
||||
|
||||
## Actual behavior
|
||||
|
||||
### Steps to reproduce
|
||||
|
||||
```java
|
||||
// some java code
|
||||
```
|
||||
@@ -1,14 +1,17 @@
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm) - avaje-ebeanorm
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-agent) - avaje-ebeanorm-agent
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-mavenenhancer) - avaje-ebeanorm-mavenenhancer
|
||||
# Need help?
|
||||
Post questions or issues to the Ebean google group - https://groups.google.com/forum/#!forum/ebean
|
||||
|
||||
|
||||
Ebean ORM
|
||||
==============
|
||||
[https://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Website")
|
||||
# Documentation
|
||||
Goto [https://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Website")
|
||||
|
||||
|
||||
## Maven cental links:
|
||||
[Maven central - avaje-ebeanorm](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.avaje.ebeanorm%22%20AND%20a%3A%22avaje-ebeanorm%22 "maven central ebeanorm")
|
||||
|
||||
[Maven central - all related projects](http://search.maven.org/#search%7Cga%7C1%7Cavaje-ebeanorm "maven central ebeanorm")
|
||||
|
||||
## Current versions
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm) - avaje-ebeanorm
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-agent) - avaje-ebeanorm-agent
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-mavenenhancer) - avaje-ebeanorm-mavenenhancer
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>7.8.1</version>
|
||||
<version>7.9.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>avaje-ebeanorm</name>
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:https://github.com/ebean-orm/avaje-ebeanorm.git</developerConnection>
|
||||
<tag>avaje-ebeanorm-7.8.1</tag>
|
||||
<tag>avaje-ebeanorm-7.9.1</tag>
|
||||
</scm>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -1013,22 +1013,6 @@ public interface EbeanServer {
|
||||
*/
|
||||
<T> FutureList<T> findFutureList(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute find list SQL query in a background thread.
|
||||
* <p>
|
||||
* This returns a Future object which can be used to cancel, check the
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
* </p>
|
||||
*
|
||||
* @param query
|
||||
* the query to execute in the background
|
||||
* @param transaction
|
||||
* the transaction (can be null).
|
||||
* @return a Future object for the list result of the query
|
||||
*/
|
||||
SqlFutureList findFutureList(SqlQuery query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Return a PagedList for this query using pageIndex and pageSize.
|
||||
* <p>
|
||||
@@ -1193,40 +1177,23 @@ public interface EbeanServer {
|
||||
List<SqlRow> findList(SqlQuery query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the sql query returning a set of MapBean.
|
||||
* Execute the SqlQuery iterating a row at a time.
|
||||
* <p>
|
||||
* Generally you are able to use {@link SqlQuery#findSet()} rather than
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
|
||||
* </p>
|
||||
*
|
||||
* @param query
|
||||
* the query to execute.
|
||||
* @param transaction
|
||||
* the transaction to use (can be null).
|
||||
* @return the set of fetched MapBean.
|
||||
*
|
||||
* @see SqlQuery#findSet()
|
||||
*/
|
||||
Set<SqlRow> findSet(SqlQuery query, Transaction transaction);
|
||||
void findEach(SqlQuery query, QueryEachConsumer<SqlRow> consumer, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the sql query returning a map of MapBean.
|
||||
* Execute the SqlQuery iterating a row at a time with the ability to stop consuming part way through.
|
||||
* <p>
|
||||
* Generally you are able to use {@link SqlQuery#findMap()} rather than
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* Returning false after processing a row stops the iteration through the query results.
|
||||
* </p>
|
||||
* <p>
|
||||
* This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
|
||||
* </p>
|
||||
*
|
||||
* @param query
|
||||
* the query to execute.
|
||||
* @param transaction
|
||||
* the transaction to use (can be null).
|
||||
* @return the set of fetched MapBean.
|
||||
*
|
||||
* @see SqlQuery#findMap()
|
||||
*/
|
||||
Map<?, SqlRow> findMap(SqlQuery query, Transaction transaction);
|
||||
void findEachWhile(SqlQuery query, QueryEachWhileConsumer<SqlRow> consumer, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the sql query returning a single MapBean or null.
|
||||
|
||||
@@ -362,29 +362,6 @@ public abstract class Model {
|
||||
super(serverName, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Please migrate to use {@link Find} or constructor <code>Finder(Class)</code> that
|
||||
* does not have the idType parameter.
|
||||
* <p/>
|
||||
* Create with the type of the ID property and entity bean and specific server name.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public Finder(Class<I> idType, Class<T> type) {
|
||||
super(null, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Please migrate to use the constructor <code>Finder(String, Class)</code> that
|
||||
* does not have the idType parameter.
|
||||
* <p/>
|
||||
* Create with the type of the ID property and entity bean and specific server name.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public Finder(String serverName, Class<I> idType, Class<T> type) {
|
||||
super(serverName, type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* The SqlFutureList represents the result of a background SQL query execution.
|
||||
*
|
||||
* <p>
|
||||
* It extends the java.util.concurrent.Future.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* // create a query
|
||||
* String sql = ... ;
|
||||
* SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
|
||||
*
|
||||
* // execute the query in a background thread
|
||||
* SqlFutureList sqlFuture = sqlQuery.findFutureList();
|
||||
*
|
||||
* // do something else ... we will sleep
|
||||
* Thread.sleep(3000);
|
||||
* System.out.println("end of sleep");
|
||||
*
|
||||
* if (!futureList.isDone()){
|
||||
* // we can cancel the query execution
|
||||
* futureList.cancel(true);
|
||||
* }
|
||||
*
|
||||
* System.out.println("and... done:"+futureList.isDone());
|
||||
*
|
||||
* if (!futureList.isCancelled()){
|
||||
* // wait for the query to finish and return the list
|
||||
* List<SqlRow> list = futureList.get();
|
||||
* System.out.println("list:"+list);
|
||||
* }
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author rob
|
||||
*
|
||||
*/
|
||||
public interface SqlFutureList extends Future<List<SqlRow>> {
|
||||
|
||||
SqlQuery getQuery();
|
||||
|
||||
}
|
||||
@@ -21,45 +21,48 @@ import java.util.Set;
|
||||
* conversion support added.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* // its typically a good idea to use a named query
|
||||
* // and put the sql in the orm.xml instead of in your code
|
||||
* <pre>{@code
|
||||
*
|
||||
* // its typically a good idea to use a named query
|
||||
* // and put the sql in the orm.xml instead of in your code
|
||||
*
|
||||
* String sql = "select id, name from customer where name like :name and status_code = :status";
|
||||
* String sql = "select id, name from customer where name like :name and status_code = :status";
|
||||
*
|
||||
* SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
|
||||
* sqlQuery.setParameter("name", "Acme%");
|
||||
* sqlQuery.setParameter("status", "ACTIVE");
|
||||
* SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
|
||||
* sqlQuery.setParameter("name", "Acme%");
|
||||
* sqlQuery.setParameter("status", "ACTIVE");
|
||||
*
|
||||
* // execute the query returning a List of MapBean objects
|
||||
* List<SqlRow> list = sqlQuery.findList();
|
||||
* </pre>
|
||||
* // execute the query returning a List of MapBean objects
|
||||
* List<SqlRow> list = sqlQuery.findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*/
|
||||
public interface SqlQuery extends Serializable {
|
||||
|
||||
/**
|
||||
* Cancel the query if support by the underlying database and driver.
|
||||
* <p>
|
||||
* This must be called from a different thread to the one executing the query.
|
||||
* </p>
|
||||
*/
|
||||
void cancel();
|
||||
|
||||
/**
|
||||
* Execute the query returning a list.
|
||||
*/
|
||||
List<SqlRow> findList();
|
||||
|
||||
/**
|
||||
* Execute the query returning a set.
|
||||
* Execute the SqlQuery iterating a row at a time.
|
||||
* <p>
|
||||
* This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
|
||||
* </p>
|
||||
*/
|
||||
Set<SqlRow> findSet();
|
||||
void findEach(QueryEachConsumer<SqlRow> consumer);
|
||||
|
||||
/**
|
||||
* Execute the query returning a map.
|
||||
* Execute the SqlQuery iterating a row at a time with the ability to stop consuming part way through.
|
||||
* <p>
|
||||
* Returning false after processing a row stops the iteration through the query results.
|
||||
* </p>
|
||||
* <p>
|
||||
* This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
|
||||
* </p>
|
||||
*/
|
||||
Map<?, SqlRow> findMap();
|
||||
void findEachWhile(QueryEachWhileConsumer<SqlRow> consumer);
|
||||
|
||||
/**
|
||||
* Execute the query returning a single row or null.
|
||||
@@ -70,19 +73,6 @@ public interface SqlQuery extends Serializable {
|
||||
*/
|
||||
SqlRow findUnique();
|
||||
|
||||
/**
|
||||
* Execute find list SQL query in a background thread.
|
||||
* <p>
|
||||
* This returns a Future object which can be used to cancel, check the
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
* </p>
|
||||
*
|
||||
* @return a Future object for the list result of the query
|
||||
* @deprecated
|
||||
*/
|
||||
SqlFutureList findFutureList();
|
||||
|
||||
/**
|
||||
* The same as bind for named parameters.
|
||||
*/
|
||||
@@ -93,19 +83,6 @@ public interface SqlQuery extends Serializable {
|
||||
*/
|
||||
SqlQuery setParameter(int position, Object value);
|
||||
|
||||
/**
|
||||
* Set a listener to process the query on a row by row basis.
|
||||
* <p>
|
||||
* It this case the rows are not loaded into the persistence context and
|
||||
* instead can be processed by the query listener.
|
||||
* </p>
|
||||
* <p>
|
||||
* Use this when you want to process a large query and do not want to hold the
|
||||
* entire query result in memory.
|
||||
* </p>
|
||||
*/
|
||||
SqlQuery setListener(SqlQueryListener queryListener);
|
||||
|
||||
/**
|
||||
* Set the index of the first row of the results to return.
|
||||
*/
|
||||
@@ -116,16 +93,6 @@ public interface SqlQuery extends Serializable {
|
||||
*/
|
||||
SqlQuery setMaxRows(int maxRows);
|
||||
|
||||
/**
|
||||
* Set the index after which fetching continues in a background thread.
|
||||
*/
|
||||
SqlQuery setBackgroundFetchAfter(int backgroundFetchAfter);
|
||||
|
||||
/**
|
||||
* Set the column to use to determine the keys for a Map.
|
||||
*/
|
||||
SqlQuery setMapKey(String mapKey);
|
||||
|
||||
/**
|
||||
* Set a timeout on this query.
|
||||
* <p>
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* Provides a mechanism for processing a SqlQuery one SqlRow at a time.
|
||||
* <p>
|
||||
* This is useful when the query will return a large number of results and you
|
||||
* want to process the beans one at a time rather than have all of the beans in
|
||||
* memory at once.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* SqlQueryListener listener = ...;
|
||||
*
|
||||
* SqlQuery query = Ebean.createSqlQuery("my.large.query");
|
||||
*
|
||||
* // set the listener that will process each row one at a time
|
||||
* query.setListener(listener);
|
||||
*
|
||||
* // execute the query. Note that the returned
|
||||
* // list will be empty ... so don't bother assigning it...
|
||||
* query.findList();
|
||||
* </pre>
|
||||
*/
|
||||
public interface SqlQueryListener {
|
||||
|
||||
/**
|
||||
* Process the bean that has just been read.
|
||||
* <p>
|
||||
* Note this bean will not be added to the List Set or Map.
|
||||
* </p>
|
||||
*/
|
||||
void process(SqlRow bean);
|
||||
}
|
||||
+22
-11
@@ -5,25 +5,36 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.avaje.ebean.Query;
|
||||
|
||||
/**
|
||||
* Specify the default cache use specific entity type.
|
||||
*/
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface CacheStrategy {
|
||||
public @interface Cache {
|
||||
|
||||
/**
|
||||
* When set to true the bean cache will be used unless explicitly stated not
|
||||
* to in a query via {@link Query#setUseCache(boolean)}.
|
||||
*/
|
||||
boolean useBeanCache() default true;
|
||||
|
||||
/**
|
||||
* A single property that is a natural unique identifier for the bean.
|
||||
* Set this to true to enable the use of query cache.
|
||||
* <p>
|
||||
* When a findUnique query is used with this property as the sole expression
|
||||
* By default query caching is disabled as the query cache invalidates
|
||||
* frequently and so it is typically used for specific bean types and cases.
|
||||
* </p>
|
||||
*/
|
||||
boolean enableQueryCache() default false;
|
||||
|
||||
/**
|
||||
* Set this to false to disable the use of bean cache.
|
||||
* <p>
|
||||
* By default bean caching is expected so this defaults to true. We might
|
||||
* set this to false on a bean type that we want to use query caching but no
|
||||
* bean caching (and this is expected to be a rare case).
|
||||
* </p>
|
||||
*/
|
||||
boolean enableBeanCache() default true;
|
||||
|
||||
/**
|
||||
* Specify the property that is a natural unique identifier for the bean.
|
||||
* <p>
|
||||
* When a findUnique() query is used with this property as the sole expression
|
||||
* then there will be a lookup into the L2 natural key cache.
|
||||
* </p>
|
||||
*/
|
||||
+4
-3
@@ -6,14 +6,15 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specify cache tuning for a specific entity type.
|
||||
* Cache tuning hints for the L2 bean cache of a specific entity type.
|
||||
* <p>
|
||||
* If this is not specified then the system default settings are used.
|
||||
* Note that this is not useful when distributed L2 bean caches are used like
|
||||
* ElasticSearch, Hazelcast, Ignite etc.
|
||||
* </p>
|
||||
*/
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface CacheTuning {
|
||||
public @interface CacheBeanTuning {
|
||||
|
||||
/**
|
||||
* The maximum size for the cache.
|
||||
@@ -1,19 +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;
|
||||
|
||||
/**
|
||||
* Specify explicit ConcurrencyMode for entity bean.
|
||||
*/
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface EntityConcurrencyMode {
|
||||
|
||||
/**
|
||||
* The ConcurrencyMode value.
|
||||
*/
|
||||
ConcurrencyMode value();
|
||||
}
|
||||
@@ -1,90 +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;
|
||||
|
||||
/**
|
||||
* For mapping the values of an Enum to and from Database values.
|
||||
* <p>
|
||||
* Also refer to the {@link EnumValue} approach which probably the preferred now
|
||||
* (preferred over using this EnumMapping annotation).
|
||||
* </p>
|
||||
* <p>
|
||||
* Both of the approaches defined in the JPA have significant problems!!!
|
||||
* </p>
|
||||
* <p>
|
||||
* Using the ordinal value is VERY RISKY because that depends on the compile
|
||||
* order of the enum values. Aka if you change the order of the enum values you
|
||||
* have changed their ordinal values and now your DB values are WRONG - a HUGE
|
||||
* disaster!!!.
|
||||
* </p>
|
||||
* <p>
|
||||
* Using the String values of enums is fairly restrictive because in a Database
|
||||
* these values are usually truncated into short codes (e.g. "A" short for
|
||||
* "ACTIVE") so space used in the database is minimised. Making your enum names
|
||||
* match the database values would give them very short less meaningful names -
|
||||
* not a great solution.
|
||||
* </p>
|
||||
* <p>
|
||||
* You can use this annotation to control the mapping of your enums to database
|
||||
* values.
|
||||
* </p>
|
||||
* <p>
|
||||
* The design of this using nameValuePairs is not optimal for safety or
|
||||
* refactoring so if you have a better solution I'm all ears. The other
|
||||
* solutions would probably involve modifying each enumeration with a method
|
||||
* which may be ok.
|
||||
* </p>
|
||||
* <p>
|
||||
* An example mapping the UserState enum.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* ...
|
||||
* @EnumMapping(nameValuePairs="NEW=N, ACTIVE=A, INACTIVE=I")
|
||||
* public enum UserState {
|
||||
* NEW,
|
||||
* ACTIVE,
|
||||
* INACTIVE;
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @see EnumValue
|
||||
*/
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface EnumMapping {
|
||||
|
||||
/**
|
||||
* A comma delimited list of name=value pairs.
|
||||
* <p>
|
||||
* e.g. "ACTIVE=A, INACTIVE=I, NEW=N".
|
||||
* </p>
|
||||
* <p>
|
||||
* Where ACTIVE, INACTIVE and NEW are the enumeration values and "A", "I" and
|
||||
* "N" are the database values.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is not really an optimal approach so if you have a better one I'm all
|
||||
* ears - thanks.
|
||||
* </p>
|
||||
*/
|
||||
String nameValuePairs();
|
||||
|
||||
/**
|
||||
* Defaults to mapping values to database VARCHAR type. If this is set to true
|
||||
* then the values will be converted to INTEGER and mapped to the database
|
||||
* integer type.
|
||||
* <p>
|
||||
* e.g. "ACTIVE=1, INACTIVE=0, NEW=2".
|
||||
* </p>
|
||||
*/
|
||||
boolean integerType() default false;
|
||||
|
||||
/**
|
||||
* The length of DB column if mapping to string values.
|
||||
*/
|
||||
int length() default 0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* Annotate an entity bean with @View to indicates the bean is based on a view.
|
||||
* <p>
|
||||
* As such typically the view is defined in <code>extra-ddl.xml</code> using
|
||||
* <code>create or replace view ...</code>.
|
||||
* </p>
|
||||
* <p>
|
||||
* When using extra-ddl.xml Ebean will run the resulting DDL script after the
|
||||
* <code>create-all</code> DDL (which is typically used during development) and for
|
||||
* DB Migration will copy the scripts as <code>repeatable migration scripts</code> that
|
||||
* will be run by FlywayDb (or Ebean's own migration runner) when their MD5 hash changes.
|
||||
* </p>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface View {
|
||||
|
||||
/**
|
||||
* The name of the view this entity bean is based on.
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Tables this view is dependent on.
|
||||
* <p>
|
||||
* This is used with l2 caching to invalidate the query cache. Changes to these
|
||||
* tables invalidate the query cache for the entity based on this view.
|
||||
* </p>
|
||||
*/
|
||||
String[] dependentTables() default {};
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.avaje.ebean.cache;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
|
||||
/**
|
||||
* Represents part of the "L2" server side cache.
|
||||
* <p>
|
||||
@@ -17,27 +15,6 @@ import com.avaje.ebean.EbeanServer;
|
||||
*/
|
||||
public interface ServerCache {
|
||||
|
||||
/**
|
||||
* Just after a cache is created this init method is called. This is so that a
|
||||
* cache implementation can make use of the BackgroundExecutor service to
|
||||
* trim/cleanup itself or use the EbeanServer to populate itself.
|
||||
* <p>
|
||||
* This method is called after the cache is constructed but before the cache
|
||||
* is made available for use.
|
||||
* </p>
|
||||
*/
|
||||
void init(EbeanServer ebeanServer);
|
||||
|
||||
/**
|
||||
* Return the configuration options for this cache.
|
||||
*/
|
||||
ServerCacheOptions getOptions();
|
||||
|
||||
/**
|
||||
* Update the configuration options for this cache.
|
||||
*/
|
||||
void setOptions(ServerCacheOptions options);
|
||||
|
||||
/**
|
||||
* Return the value given the key.
|
||||
*/
|
||||
|
||||
@@ -1,23 +1,10 @@
|
||||
package com.avaje.ebean.cache;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
|
||||
/**
|
||||
* Defines method for constructing caches for beans and queries.
|
||||
*/
|
||||
public interface ServerCacheFactory {
|
||||
|
||||
/**
|
||||
* Just after the ServerCacheFactory is constructed this method is called
|
||||
* passing the EbeanServer.
|
||||
* <p>
|
||||
* This is so that a cache implementation can utilise the EbeanServer to
|
||||
* populate itself or use the BackgroundExecutor service to schedule periodic
|
||||
* cache trimming/cleanup.
|
||||
* </p>
|
||||
*/
|
||||
void init(EbeanServer ebeanServer);
|
||||
|
||||
/**
|
||||
* Create the cache for the given type with options.
|
||||
*/
|
||||
|
||||
+3
-15
@@ -1,25 +1,10 @@
|
||||
package com.avaje.ebean.cache;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
|
||||
/**
|
||||
* The cache service for server side caching of beans and query results.
|
||||
*/
|
||||
public interface ServerCacheManager {
|
||||
|
||||
/**
|
||||
* This method is called just after the construction of the
|
||||
* ServerCacheManager.
|
||||
* <p>
|
||||
* The EbeanServer is provided so that cache implementations can make use of
|
||||
* EbeanServer and BackgroundExecutor for automatically populating and
|
||||
* background trimming of the cache.
|
||||
* </p>
|
||||
*/
|
||||
void init(EbeanServer server);
|
||||
|
||||
void setCaching(Class<?> beanType, boolean useCache);
|
||||
|
||||
/**
|
||||
* Return true if there is an active bean cache for this type of bean.
|
||||
*/
|
||||
@@ -35,6 +20,9 @@ public interface ServerCacheManager {
|
||||
*/
|
||||
ServerCache getBeanCache(Class<?> beanType);
|
||||
|
||||
/**
|
||||
* Return the cache for associated many properties of a bean type.
|
||||
*/
|
||||
ServerCache getCollectionIdsCache(Class<?> beanType, String propertyName);
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.avaje.ebean.cache;
|
||||
|
||||
import com.avaje.ebean.annotation.CacheQueryTuning;
|
||||
import com.avaje.ebean.annotation.CacheTuning;
|
||||
import com.avaje.ebean.annotation.CacheBeanTuning;
|
||||
|
||||
/**
|
||||
* Options for controlling a cache.
|
||||
@@ -23,11 +23,11 @@ public class ServerCacheOptions {
|
||||
/**
|
||||
* Create from the cacheTuning deployment annotation.
|
||||
*/
|
||||
public ServerCacheOptions(CacheTuning cacheTuning) {
|
||||
this.maxSize = cacheTuning.maxSize();
|
||||
this.maxIdleSecs = cacheTuning.maxIdleSecs();
|
||||
this.maxSecsToLive = cacheTuning.maxSecsToLive();
|
||||
this.trimFrequency = cacheTuning.trimFrequency();
|
||||
public ServerCacheOptions(CacheBeanTuning tuning) {
|
||||
this.maxSize = tuning.maxSize();
|
||||
this.maxIdleSecs = tuning.maxIdleSecs();
|
||||
this.maxSecsToLive = tuning.maxSecsToLive();
|
||||
this.trimFrequency = tuning.trimFrequency();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.avaje.ebean.cache;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
|
||||
/**
|
||||
* The plugin interface that creates a ServerCacheFactory.
|
||||
*/
|
||||
public interface ServerCachePlugin {
|
||||
|
||||
/**
|
||||
* Create the ServerCacheFactory given the server config and background executor service.
|
||||
*/
|
||||
ServerCacheFactory create(ServerConfig config, BackgroundExecutor executor);
|
||||
}
|
||||
@@ -60,6 +60,11 @@ public class DbMigrationConfig {
|
||||
|
||||
protected String applySuffix = ".sql";
|
||||
|
||||
/**
|
||||
* Set this to "V" to be compatible with FlywayDB.
|
||||
*/
|
||||
protected String applyPrefix = "";
|
||||
|
||||
protected String modelSuffix = ".model.xml";
|
||||
|
||||
protected boolean includeGeneratedFileComment;
|
||||
@@ -177,6 +182,20 @@ public class DbMigrationConfig {
|
||||
this.applySuffix = applySuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the apply prefix.
|
||||
*/
|
||||
public String getApplyPrefix() {
|
||||
return applyPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the apply prefix. This might be set to "V" for use with FlywayDB.
|
||||
*/
|
||||
public void setApplyPrefix(String applyPrefix) {
|
||||
this.applyPrefix = applyPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the generated file comment should be included.
|
||||
*/
|
||||
@@ -351,6 +370,7 @@ public class DbMigrationConfig {
|
||||
} else {
|
||||
modelPath = properties.get("migration.modelPath", modelPath);
|
||||
}
|
||||
applyPrefix = properties.get("migration.applyPrefix", applyPrefix);
|
||||
applySuffix = properties.get("migration.applySuffix", applySuffix);
|
||||
modelSuffix = properties.get("migration.modelSuffix", modelSuffix);
|
||||
includeGeneratedFileComment = properties.getBoolean("migration.includeGeneratedFileComment", includeGeneratedFileComment);
|
||||
@@ -360,7 +380,7 @@ public class DbMigrationConfig {
|
||||
|
||||
generate = properties.getBoolean("migration.generate", generate);
|
||||
version = properties.get("migration.version", version);
|
||||
this.name = properties.get("migration.name", this.name);
|
||||
name = properties.get("migration.name", name);
|
||||
|
||||
runMigration = properties.getBoolean("migration.run", runMigration);
|
||||
metaTable = properties.get("migration.metaTable", metaTable);
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.avaje.ebean.PersistenceContextScope;
|
||||
import com.avaje.ebean.annotation.Encrypted;
|
||||
import com.avaje.ebean.cache.ServerCacheFactory;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.cache.ServerCachePlugin;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.config.dbplatform.DbEncrypt;
|
||||
import com.avaje.ebean.event.BeanFindController;
|
||||
@@ -356,7 +357,7 @@ public class ServerConfig {
|
||||
|
||||
private DbEncrypt dbEncrypt;
|
||||
|
||||
private ServerCacheFactory serverCacheFactory;
|
||||
private ServerCachePlugin serverCachePlugin;
|
||||
|
||||
private ServerCacheManager serverCacheManager;
|
||||
|
||||
@@ -938,17 +939,17 @@ public class ServerConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ServerCacheFactory.
|
||||
* Return the ServerCachePlugin.
|
||||
*/
|
||||
public ServerCacheFactory getServerCacheFactory() {
|
||||
return serverCacheFactory;
|
||||
public ServerCachePlugin getServerCachePlugin() {
|
||||
return serverCachePlugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ServerCacheFactory to use.
|
||||
* Set the ServerCachePlugin to use.
|
||||
*/
|
||||
public void setServerCacheFactory(ServerCacheFactory serverCacheFactory) {
|
||||
this.serverCacheFactory = serverCacheFactory;
|
||||
public void setServerCachePlugin(ServerCachePlugin serverCachePlugin) {
|
||||
this.serverCachePlugin = serverCachePlugin;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2352,7 +2353,7 @@ public class ServerConfig {
|
||||
encryptDeployManager = createInstance(p, EncryptDeployManager.class, "encryptDeployManager", encryptDeployManager);
|
||||
encryptor = createInstance(p, Encryptor.class, "encryptor", encryptor);
|
||||
dbEncrypt = createInstance(p, DbEncrypt.class, "dbEncrypt", dbEncrypt);
|
||||
serverCacheFactory = createInstance(p, ServerCacheFactory.class, "serverCacheFactory", serverCacheFactory);
|
||||
serverCachePlugin = createInstance(p, ServerCachePlugin.class, "serverCachePlugin", serverCachePlugin);
|
||||
serverCacheManager = createInstance(p, ServerCacheManager.class, "serverCacheManager", serverCacheManager);
|
||||
cacheWarmingDelay = p.getInt("cacheWarmingDelay", cacheWarmingDelay);
|
||||
classPathReaderClassName = p.get("classpathreader");
|
||||
|
||||
@@ -25,10 +25,14 @@ import com.avaje.ebean.dbmigration.model.ModelContainer;
|
||||
import com.avaje.ebean.dbmigration.model.ModelDiff;
|
||||
import com.avaje.ebean.dbmigration.model.PlatformDdlWriter;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.extraddl.model.DdlScript;
|
||||
import com.avaje.ebeaninternal.extraddl.model.ExtraDdl;
|
||||
import com.avaje.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -42,7 +46,7 @@ import java.util.List;
|
||||
* and drop objects (drop tables, drop columns).
|
||||
* </p>
|
||||
* <p>
|
||||
* This does not run the migration or ddl scripts but just generates them.
|
||||
* This does not run the migration or ddl scripts but just generates them.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
@@ -98,7 +102,7 @@ public class DbMigration {
|
||||
|
||||
/**
|
||||
* Set the path from the current working directory to the application resources.
|
||||
*
|
||||
* <p>
|
||||
* This defaults to maven style 'src/main/resources'.
|
||||
*/
|
||||
public void setPathToResources(String pathToResources) {
|
||||
@@ -169,7 +173,7 @@ public class DbMigration {
|
||||
/**
|
||||
* Generate the next migration xml file and associated apply and rollback sql scripts.
|
||||
* <p>
|
||||
* This does not run the migration or ddl scripts but just generates them.
|
||||
* This does not run the migration or ddl scripts but just generates them.
|
||||
* </p>
|
||||
* <h3>Example: Run for a single specific platform</h3>
|
||||
* <pre>{@code
|
||||
@@ -181,7 +185,7 @@ public class DbMigration {
|
||||
* migration.generateMigration();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* <h3>Example: Run migration generating DDL for multiple platforms</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
@@ -206,6 +210,8 @@ public class DbMigration {
|
||||
try {
|
||||
Request request = createRequest();
|
||||
|
||||
generateExtraDdl(request);
|
||||
|
||||
String pendingVersion = generatePendingDrop();
|
||||
if (pendingVersion != null) {
|
||||
generatePendingDrop(request, pendingVersion);
|
||||
@@ -220,6 +226,49 @@ public class DbMigration {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate "repeatable" migration scripts.
|
||||
* <p>
|
||||
* These take scrips from extra-dll.xml (typically views) and outputs "repeatable"
|
||||
* migration scripts (starting with "R__") to be run by FlywayDb or Ebean's own
|
||||
* migration runner.
|
||||
* </p>
|
||||
*/
|
||||
private void generateExtraDdl(Request request) throws IOException {
|
||||
|
||||
if (databasePlatform != null) {
|
||||
ExtraDdl extraDdl = ExtraDdlXmlReader.read("/extra-ddl.xml");
|
||||
if (extraDdl != null) {
|
||||
List<DdlScript> ddlScript = extraDdl.getDdlScript();
|
||||
for (DdlScript script : ddlScript) {
|
||||
if (ExtraDdlXmlReader.matchPlatform(databasePlatform.getName(), script.getPlatforms())) {
|
||||
writeExtraDdl(request, script);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write (or override) the "repeatable" migration script.
|
||||
*/
|
||||
private void writeExtraDdl(Request request, DdlScript script) throws IOException {
|
||||
|
||||
String fullName = repeatableMigrationName(script.getName());
|
||||
|
||||
logger.info("writing repeatable script {}", fullName);
|
||||
|
||||
File file = new File(request.migrationDir, fullName);
|
||||
FileWriter writer = new FileWriter(file);
|
||||
writer.write(script.getValue());
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}
|
||||
|
||||
private String repeatableMigrationName(String scriptName) {
|
||||
return "R__" + scriptName.replace(' ', '_') + migrationConfig.getApplySuffix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the diff migration.
|
||||
*/
|
||||
@@ -319,7 +368,7 @@ public class DbMigration {
|
||||
// history ddl generation (triggers, history tables etc)
|
||||
DdlWrite write = new DdlWrite(new MConfiguration(), request.current);
|
||||
PlatformDdlWriter writer = createDdlWriter(databasePlatform, "");
|
||||
writer.processMigration(dbMigration, write, request.migrationDir , fullVersion);
|
||||
writer.processMigration(dbMigration, write, request.migrationDir, fullVersion);
|
||||
}
|
||||
writeExtraPlatformDdl(fullVersion, request.currentModel, dbMigration, request.migrationDir);
|
||||
}
|
||||
@@ -339,7 +388,7 @@ public class DbMigration {
|
||||
|
||||
/**
|
||||
* Return the full version for the migration being generated.
|
||||
*
|
||||
* <p>
|
||||
* The full version can contain a comment suffix after a "__" double underscore.
|
||||
*/
|
||||
private String getFullVersion(MigrationModel migrationModel, String dropsFor) {
|
||||
@@ -349,7 +398,7 @@ public class DbMigration {
|
||||
version = migrationModel.getNextVersion(initialVersion);
|
||||
}
|
||||
|
||||
String fullVersion = version;
|
||||
String fullVersion = migrationConfig.getApplyPrefix() + version;
|
||||
if (migrationConfig.getName() != null) {
|
||||
fullVersion += "__" + toUnderScore(migrationConfig.getName());
|
||||
|
||||
@@ -366,7 +415,7 @@ public class DbMigration {
|
||||
* Replace spaces with underscores.
|
||||
*/
|
||||
private String toUnderScore(String name) {
|
||||
return name.replace(' ','_');
|
||||
return name.replace(' ', '_');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -444,7 +493,7 @@ public class DbMigration {
|
||||
*/
|
||||
protected File getModelDirectory(File migrationDirectory) {
|
||||
String modelPath = migrationConfig.getModelPath();
|
||||
if (modelPath == null || modelPath.isEmpty()) {
|
||||
if (modelPath == null || modelPath.isEmpty()) {
|
||||
return migrationDirectory;
|
||||
}
|
||||
File modelDir = new File(migrationDirectory, migrationConfig.getModelPath());
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package com.avaje.ebean.dbmigration;
|
||||
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.dbmigration.model.CurrentModel;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.util.JdbcClose;
|
||||
import com.avaje.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.File;
|
||||
@@ -15,6 +16,7 @@ import java.io.InputStreamReader;
|
||||
import java.io.LineNumberReader;
|
||||
import java.io.Reader;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Controls the generation and execution of "Create All" and "Drop All" DDL scripts.
|
||||
@@ -89,12 +91,19 @@ public class DdlGenerator {
|
||||
public int runScript(boolean expectErrors, String content, String scriptName) {
|
||||
|
||||
DdlRunner runner = new DdlRunner(expectErrors, scriptName);
|
||||
// get a connection without threadLocal
|
||||
Connection connection = server.createTransaction().getConnection();
|
||||
|
||||
Transaction transaction = server.createTransaction();
|
||||
Connection connection = transaction.getConnection();
|
||||
try {
|
||||
return runner.runAll(content, connection);
|
||||
int count = runner.runAll(content, connection);
|
||||
transaction.commit();
|
||||
return count;
|
||||
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException("Failed to run script", e);
|
||||
|
||||
} finally {
|
||||
JdbcClose.close(connection);
|
||||
transaction.end();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,6 +121,14 @@ public class DdlGenerator {
|
||||
createAllContent = readFile(getCreateFileName());
|
||||
}
|
||||
runScript(false, createAllContent, getCreateFileName());
|
||||
|
||||
String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl");
|
||||
if (!"true".equalsIgnoreCase(ignoreExtraDdl)) {
|
||||
String extraApply = ExtraDdlXmlReader.buildExtra(server.getDatabasePlatform().getName());
|
||||
if (extraApply != null) {
|
||||
runScript(false, extraApply, "extra-dll");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void runInitSql() throws IOException {
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.avaje.ebean.dbmigration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.StringReader;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
@@ -18,11 +17,11 @@ public class DdlRunner {
|
||||
|
||||
protected static final Logger logger = LoggerFactory.getLogger(DdlRunner.class);
|
||||
|
||||
protected DdlParser ddlParser = new DdlParser();
|
||||
private DdlParser ddlParser = new DdlParser();
|
||||
|
||||
protected final String scriptName;
|
||||
private final String scriptName;
|
||||
|
||||
protected final boolean expectErrors;
|
||||
private final boolean expectErrors;
|
||||
|
||||
/**
|
||||
* Construct with a script name (for logging) and flag indicating if errors are expected.
|
||||
@@ -35,7 +34,7 @@ public class DdlRunner {
|
||||
/**
|
||||
* Parse the content into sql statements and execute them in a transaction.
|
||||
*/
|
||||
public int runAll(String content, Connection connection) {
|
||||
public int runAll(String content, Connection connection) throws SQLException {
|
||||
|
||||
List<String> statements = ddlParser.parse(new StringReader(content));
|
||||
return runStatements(statements, connection);
|
||||
@@ -44,31 +43,7 @@ public class DdlRunner {
|
||||
/**
|
||||
* Execute all the statements in a single transaction.
|
||||
*/
|
||||
public int runStatements(List<String> statements, Connection connection) {
|
||||
|
||||
try {
|
||||
int statementCount = runStatements(expectErrors, statements, connection);
|
||||
connection.commit();
|
||||
return statementCount;
|
||||
|
||||
} catch (Exception e) {
|
||||
rollback(connection);
|
||||
throw new PersistenceException("Error: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void rollback(Connection connection) {
|
||||
try {
|
||||
connection.rollback();
|
||||
} catch (SQLException e) {
|
||||
logger.error("Error trying to rollback connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the list of statements.
|
||||
*/
|
||||
private int runStatements(boolean expectErrors, List<String> statements, Connection c) {
|
||||
private int runStatements(List<String> statements, Connection connection) throws SQLException {
|
||||
|
||||
List<String> noDuplicates = new ArrayList<String>();
|
||||
|
||||
@@ -82,7 +57,7 @@ public class DdlRunner {
|
||||
|
||||
for (int i = 0; i < noDuplicates.size(); i++) {
|
||||
String xOfy = (i + 1) + " of " + noDuplicates.size();
|
||||
runStatement(expectErrors, xOfy, noDuplicates.get(i), c);
|
||||
runStatement(expectErrors, xOfy, noDuplicates.get(i), connection);
|
||||
}
|
||||
|
||||
return noDuplicates.size();
|
||||
@@ -91,7 +66,7 @@ public class DdlRunner {
|
||||
/**
|
||||
* Execute the statement.
|
||||
*/
|
||||
private void runStatement(boolean expectErrors, String oneOf, String stmt, Connection c) {
|
||||
private void runStatement(boolean expectErrors, String oneOf, String stmt, Connection c) throws SQLException {
|
||||
|
||||
PreparedStatement pstmt = null;
|
||||
try {
|
||||
@@ -111,12 +86,12 @@ public class DdlRunner {
|
||||
pstmt = c.prepareStatement(stmt);
|
||||
pstmt.execute();
|
||||
|
||||
} catch (Exception e) {
|
||||
} catch (SQLException e) {
|
||||
if (expectErrors) {
|
||||
logger.debug(" ... ignoring error executing " + getSummary(stmt) + " error: " + e.getMessage());
|
||||
} else {
|
||||
String msg = "Error executing stmt[" + stmt + "] error[" + e.getMessage() + "]";
|
||||
throw new RuntimeException(msg, e);
|
||||
throw new SQLException(msg, e);
|
||||
}
|
||||
|
||||
} finally {
|
||||
|
||||
@@ -104,6 +104,7 @@ public class MigrationRunner {
|
||||
break;
|
||||
}
|
||||
priorVersion = localVersion;
|
||||
connection.commit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,10 @@ import java.util.Arrays;
|
||||
*/
|
||||
public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
|
||||
private static final int[] REPEAT_ORDERING = {Integer.MAX_VALUE};
|
||||
|
||||
private static final boolean[] REPEAT_UNDERSCORES = {false};
|
||||
|
||||
/**
|
||||
* The raw version text.
|
||||
*/
|
||||
@@ -21,6 +25,19 @@ public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
|
||||
private final String comment;
|
||||
|
||||
/**
|
||||
* Construct for "repeatable" version.
|
||||
*/
|
||||
private MigrationVersion(String raw, String comment) {
|
||||
this.raw = raw;
|
||||
this.comment = comment;
|
||||
this.ordering = REPEAT_ORDERING;
|
||||
this.underscores = REPEAT_UNDERSCORES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for "normal" version.
|
||||
*/
|
||||
private MigrationVersion(String raw, int[] ordering, boolean[] underscores, String comment) {
|
||||
this.raw = raw;
|
||||
this.ordering = ordering;
|
||||
@@ -28,6 +45,13 @@ public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a "repeatable" version.
|
||||
*/
|
||||
public boolean isRepeatable() {
|
||||
return ordering == REPEAT_ORDERING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full version.
|
||||
*/
|
||||
@@ -76,11 +100,15 @@ public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
|
||||
/**
|
||||
* Returns the version part of the string.
|
||||
*
|
||||
* <p>
|
||||
* Normalised means always use '.' delimiters (no underscores).
|
||||
* NextVersion means bump/increase the last version number by 1.
|
||||
*/
|
||||
private String formattedVersion(boolean normalised, boolean nextVersion) {
|
||||
|
||||
if (ordering == REPEAT_ORDERING) {
|
||||
return "R";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < ordering.length; i++) {
|
||||
if (i < ordering.length - 1) {
|
||||
@@ -110,8 +138,7 @@ public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
return (ordering[i] > other.ordering[i]) ? 1 : -1;
|
||||
}
|
||||
}
|
||||
// considered the same
|
||||
return 0;
|
||||
return comment.compareTo(other.comment);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,6 +153,10 @@ public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
*/
|
||||
public static MigrationVersion parse(String raw) {
|
||||
|
||||
if (raw.startsWith("V") || raw.startsWith("v")) {
|
||||
raw = raw.substring(1);
|
||||
}
|
||||
|
||||
String comment = "";
|
||||
String value = raw;
|
||||
int commentStart = raw.indexOf("__");
|
||||
@@ -139,6 +170,11 @@ public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
|
||||
String[] sections = value.split("\\.");
|
||||
|
||||
if ("r".equalsIgnoreCase(sections[0])) {
|
||||
// a "repeatable" version (does not have a version number)
|
||||
return new MigrationVersion(raw, comment);
|
||||
}
|
||||
|
||||
boolean[] underscores = new boolean[sections.length];
|
||||
int[] ordering = new int[sections.length];
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ public class VisitAllUsing {
|
||||
|
||||
public void visitAllBeans() {
|
||||
for (BeanDescriptor<?> desc : descriptors) {
|
||||
if (desc.getBaseTable() != null) {
|
||||
if (desc.isBaseTable()) {
|
||||
visitBean(desc, visitor);
|
||||
}
|
||||
}
|
||||
@@ -150,8 +150,8 @@ public class VisitAllUsing {
|
||||
}
|
||||
|
||||
public void visit(InheritInfo inheritInfo) {
|
||||
BeanProperty[] propertiesLocal = inheritInfo.getBeanDescriptor().propertiesLocal();
|
||||
for (int i = 0; i <propertiesLocal.length ; i++) {
|
||||
BeanProperty[] propertiesLocal = inheritInfo.desc().propertiesLocal();
|
||||
for (int i = 0; i < propertiesLocal.length; i++) {
|
||||
owner.visit(pv, propertiesLocal[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,16 @@ import org.avaje.classpath.scanner.Resource;
|
||||
*/
|
||||
public class LocalMigrationResource implements Comparable<LocalMigrationResource> {
|
||||
|
||||
/**
|
||||
* Code for repeatable migrations.
|
||||
*/
|
||||
private static final String REPEAT_TYPE = "R";
|
||||
|
||||
/**
|
||||
* Code for version migrations.
|
||||
*/
|
||||
private static final String VERSION_TYPE = "V";
|
||||
|
||||
private final MigrationVersion version;
|
||||
|
||||
private final String location;
|
||||
@@ -27,6 +37,32 @@ public class LocalMigrationResource implements Comparable<LocalMigrationResource
|
||||
return version.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the underlying version is "repeatable".
|
||||
*/
|
||||
public boolean isRepeatable() {
|
||||
return version.isRepeatable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the "key" that identifies the migration.
|
||||
*/
|
||||
public String key() {
|
||||
if (isRepeatable()) {
|
||||
return version.getComment().toLowerCase();
|
||||
} else {
|
||||
return version.normalised();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the migration comment.
|
||||
*/
|
||||
public String getComment() {
|
||||
String comment = version.getComment();
|
||||
return (comment == null || comment.isEmpty()) ? "-" : comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default ordering by version.
|
||||
*/
|
||||
@@ -55,4 +91,11 @@ public class LocalMigrationResource implements Comparable<LocalMigrationResource
|
||||
public String getContent() {
|
||||
return resource.loadAsString("UTF-8");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type code ("R" or "V") for this migration.
|
||||
*/
|
||||
public String getType() {
|
||||
return isRepeatable() ? REPEAT_TYPE : VERSION_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,9 @@ class MigrationMetaRow {
|
||||
|
||||
private int id;
|
||||
|
||||
private String status;
|
||||
private String type;
|
||||
|
||||
private String runVersion;
|
||||
|
||||
private String depVersion;
|
||||
private String version;
|
||||
|
||||
private String comment;
|
||||
|
||||
@@ -26,17 +24,20 @@ class MigrationMetaRow {
|
||||
|
||||
private String runBy;
|
||||
|
||||
private long runTime;
|
||||
|
||||
/**
|
||||
* Construct for inserting into table.
|
||||
*/
|
||||
MigrationMetaRow(int id, String runVersion, String priorVersion, String comment, int checksum, String runBy) {
|
||||
MigrationMetaRow(int id, String type, String version, String comment, int checksum, String runBy, Timestamp runOn, long runTime) {
|
||||
this.id = id;
|
||||
this.runVersion = runVersion;
|
||||
this.depVersion = priorVersion;
|
||||
this.type = type;
|
||||
this.version = version;
|
||||
this.checksum = checksum;
|
||||
this.comment = comment;
|
||||
this.runBy = runBy;
|
||||
this.runOn = new Timestamp(System.currentTimeMillis());
|
||||
this.runOn = runOn;
|
||||
this.runTime = runTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,17 +45,17 @@ class MigrationMetaRow {
|
||||
*/
|
||||
MigrationMetaRow(SqlRow row) {
|
||||
id = row.getInteger("id");
|
||||
status = row.getString("status");
|
||||
runVersion = row.getString("run_version");
|
||||
depVersion = row.getString("dep_version");
|
||||
comment = row.getString("comment");
|
||||
checksum = row.getInteger("checksum");
|
||||
type = row.getString("mtype");
|
||||
version = row.getString("mversion");
|
||||
comment = row.getString("mcomment");
|
||||
checksum = row.getInteger("mchecksum");
|
||||
runOn = row.getTimestamp("run_on");
|
||||
runBy = row.getString("run_by");
|
||||
runTime = row.getLong("run_time");
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "id:" + id + " status:" + status + " runVersion:" + runVersion + " comment:" + comment + " runOn:" + runOn + " runBy:" + runBy;
|
||||
return "id:" + id + " type:" + type + " runVersion:" + version + " comment:" + comment + " runOn:" + runOn + " runBy:" + runBy;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,8 +68,8 @@ class MigrationMetaRow {
|
||||
/**
|
||||
* Return the normalised version for this migration.
|
||||
*/
|
||||
String getRunVersion() {
|
||||
return runVersion;
|
||||
String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,14 +84,14 @@ class MigrationMetaRow {
|
||||
*/
|
||||
void bindInsert(SqlUpdate insert) {
|
||||
insert.setParameter(1, id);
|
||||
insert.setParameter(2, "success");
|
||||
insert.setParameter(3, runVersion);
|
||||
insert.setParameter(4, depVersion);
|
||||
insert.setParameter(2, type);
|
||||
insert.setParameter(3, "SUCCESS");
|
||||
insert.setParameter(4, version);
|
||||
insert.setParameter(5, comment);
|
||||
insert.setParameter(6, checksum);
|
||||
insert.setParameter(7, runOn);
|
||||
insert.setParameter(8, runBy);
|
||||
insert.setParameter(9, "ip");
|
||||
insert.setParameter(9, runTime);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,7 +99,8 @@ class MigrationMetaRow {
|
||||
*/
|
||||
static String insertSql(String table) {
|
||||
return "insert into " + table
|
||||
+ " (id, status, run_version, dep_version, comment, checksum, run_on, run_by, run_ip)"
|
||||
+ " (id, mtype, mstatus, mversion, mcomment, mchecksum, run_on, run_by, run_time)"
|
||||
+ " values (?,?,?,?,?,?,?,?,?)";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebean.dbmigration.runner;
|
||||
import com.avaje.ebean.dbmigration.DdlRunner;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Runs the DDL migration scripts.
|
||||
@@ -21,7 +22,7 @@ public class MigrationScriptRunner {
|
||||
/**
|
||||
* Execute all the DDL statements in the script.
|
||||
*/
|
||||
public int runScript(boolean expectErrors, String content, String scriptName) {
|
||||
int runScript(boolean expectErrors, String content, String scriptName) throws SQLException {
|
||||
|
||||
DdlRunner runner = new DdlRunner(expectErrors, scriptName);
|
||||
return runner.runAll(content, connection);
|
||||
|
||||
@@ -17,6 +17,7 @@ import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -29,7 +30,7 @@ public class MigrationTable {
|
||||
|
||||
private static final Logger logger = MigrationRunner.logger;
|
||||
|
||||
private final Connection connection;
|
||||
private final Connection connection;
|
||||
|
||||
private final EbeanServer server;
|
||||
|
||||
@@ -41,11 +42,13 @@ public class MigrationTable {
|
||||
private final ServerConfig serverConfig;
|
||||
private final String envUserName;
|
||||
|
||||
private final Timestamp runOn = new Timestamp(System.currentTimeMillis());
|
||||
|
||||
private final ScriptTransform scriptTransform;
|
||||
|
||||
private final String insertSql;
|
||||
|
||||
private final LinkedHashMap<String,MigrationMetaRow> migrations;
|
||||
private final LinkedHashMap<String, MigrationMetaRow> migrations;
|
||||
|
||||
private MigrationMetaRow lastMigration;
|
||||
|
||||
@@ -60,12 +63,10 @@ public class MigrationTable {
|
||||
SpiServer pluginApi = server.getPluginApi();
|
||||
this.serverConfig = pluginApi.getServerConfig();
|
||||
this.databasePlatform = pluginApi.getDatabasePlatform();
|
||||
|
||||
this.catalog = null;
|
||||
this.schema = null;
|
||||
this.table = migrationConfig.getMetaTable();
|
||||
this.insertSql = MigrationMetaRow.insertSql(table);
|
||||
|
||||
this.scriptTransform = createScriptTransform(migrationConfig);
|
||||
this.envUserName = System.getProperty("user.name");
|
||||
}
|
||||
@@ -96,16 +97,17 @@ public class MigrationTable {
|
||||
}
|
||||
|
||||
ExternalJdbcTransaction t = new ExternalJdbcTransaction(connection);
|
||||
SqlQuery sqlQuery = server.createSqlQuery("select * from "+table+" order by id for update");
|
||||
SqlQuery sqlQuery = server.createSqlQuery("select * from " + table + " order by id for update");
|
||||
List<SqlRow> metaRows = server.findList(sqlQuery, t);
|
||||
|
||||
for (SqlRow row : metaRows) {
|
||||
addMigration(new MigrationMetaRow(row));
|
||||
MigrationMetaRow metaRow = new MigrationMetaRow(row);
|
||||
addMigration(metaRow.getVersion(), metaRow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void createTable(Connection connection) throws IOException {
|
||||
private void createTable(Connection connection) throws IOException, SQLException {
|
||||
|
||||
String script = ScriptTransform.table(table, getCreateTableScript());
|
||||
|
||||
@@ -146,82 +148,95 @@ public class MigrationTable {
|
||||
/**
|
||||
* Return true if the migration ran successfully and false if the migration failed.
|
||||
*/
|
||||
public boolean shouldRun(LocalMigrationResource localVersion, LocalMigrationResource priorVersion) {
|
||||
public boolean shouldRun(LocalMigrationResource localVersion, LocalMigrationResource priorVersion) throws SQLException {
|
||||
|
||||
if (priorVersion != null) {
|
||||
// check priorVersion is installed
|
||||
MigrationMetaRow existing = migrations.get(priorVersion.getVersion().normalised());
|
||||
if (existing == null) {
|
||||
logger.warn("Migration {} requires prior migration {} which has not been run", localVersion.getVersion(), priorVersion.getVersion());
|
||||
if (priorVersion != null && !localVersion.isRepeatable()) {
|
||||
if (!migrationExists(priorVersion)) {
|
||||
logger.error("Migration {} requires prior migration {} which has not been run", localVersion.getVersion(), priorVersion.getVersion());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MigrationMetaRow existing = migrations.get(localVersion.getVersion().normalised());
|
||||
if (existing == null) {
|
||||
runMigration(localVersion, priorVersion);
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// check checksum and return ok, or re-run if repeatable script?
|
||||
existing.getChecksum();
|
||||
return true;
|
||||
}
|
||||
MigrationMetaRow existing = migrations.get(localVersion.key());
|
||||
return runMigration(localVersion, existing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the migration script.
|
||||
*
|
||||
* @param local The local migration resource
|
||||
* @param existing The information for this migration existing in the table
|
||||
*
|
||||
* @return True if the migrations should continue
|
||||
*/
|
||||
private void runMigration(LocalMigrationResource localVersion, LocalMigrationResource prior) {
|
||||
|
||||
logger.debug("run migration "+localVersion.getLocation());
|
||||
|
||||
String script = convertScript(localVersion.getContent());
|
||||
|
||||
MigrationScriptRunner run = new MigrationScriptRunner(connection);
|
||||
run.runScript(false, script, "run migration version: "+localVersion.getVersion());
|
||||
|
||||
private boolean runMigration(LocalMigrationResource local, MigrationMetaRow existing) throws SQLException {
|
||||
|
||||
String script = convertScript(local.getContent());
|
||||
int checksum = Checksum.calculate(script);
|
||||
MigrationMetaRow metaRow = createMetaRow(localVersion, prior, checksum);
|
||||
|
||||
if (existing != null) {
|
||||
|
||||
boolean matchChecksum = (existing.getChecksum() == checksum);
|
||||
|
||||
if (!local.isRepeatable()) {
|
||||
if (!matchChecksum) {
|
||||
logger.error("Checksum mismatch on migration {}", local.getLocation());
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if (matchChecksum) {
|
||||
logger.trace("... skip unchanged repeatable migration {}", local.getLocation());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
runMigration(local, script, checksum);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a migration script as new migration or update on existing repeatable migration.
|
||||
*/
|
||||
private void runMigration(LocalMigrationResource local, String script, int checksum) throws SQLException {
|
||||
|
||||
logger.debug("run migration {}", local.getLocation());
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
MigrationScriptRunner run = new MigrationScriptRunner(connection);
|
||||
run.runScript(false, script, "run migration version: " + local.getVersion());
|
||||
|
||||
long exeMillis = System.currentTimeMillis() - start;
|
||||
// insert new migration row
|
||||
SqlUpdate insert = server.createSqlUpdate(insertSql);
|
||||
MigrationMetaRow metaRow = createMetaRow(local, checksum, exeMillis);
|
||||
metaRow.bindInsert(insert);
|
||||
server.execute(insert, new ExternalJdbcTransaction(connection));
|
||||
|
||||
addMigration(metaRow);
|
||||
addMigration(local.key(), metaRow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the MigrationMetaRow for this migration.
|
||||
*/
|
||||
private MigrationMetaRow createMetaRow(LocalMigrationResource localVersion, LocalMigrationResource prior, int checksum) {
|
||||
private MigrationMetaRow createMetaRow(LocalMigrationResource migration, int checksum, long exeMillis) {
|
||||
|
||||
int nextId = 1;
|
||||
if (lastMigration != null) {
|
||||
nextId = lastMigration.getId() + 1;
|
||||
}
|
||||
|
||||
String runVersion = localVersion.getVersion().normalised();
|
||||
String comment = getMigrationComment(localVersion);
|
||||
String priorVersion = getMigrationPriorVersion(prior);
|
||||
String type = migration.getType();
|
||||
String runVersion = migration.key();
|
||||
String comment = migration.getComment();
|
||||
|
||||
return new MigrationMetaRow(nextId, runVersion, priorVersion, comment, checksum, envUserName);
|
||||
return new MigrationMetaRow(nextId, type, runVersion, comment, checksum, envUserName, runOn, exeMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the prior migration normalised version.
|
||||
* Return true if the migration exists.
|
||||
*/
|
||||
private String getMigrationPriorVersion(LocalMigrationResource prior) {
|
||||
return prior != null ? prior.getVersion().normalised() : "-";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the migration comment.
|
||||
*/
|
||||
private String getMigrationComment(LocalMigrationResource localVersion) {
|
||||
String comment = localVersion.getVersion().getComment();
|
||||
return comment == null || comment.isEmpty() ? "-" : comment;
|
||||
private boolean migrationExists(LocalMigrationResource priorVersion) {
|
||||
return migrations.containsKey(priorVersion.key());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -234,12 +249,11 @@ public class MigrationTable {
|
||||
/**
|
||||
* Register the successfully executed migration (to allow dependant scripts to run).
|
||||
*/
|
||||
private void addMigration(MigrationMetaRow metaRow) {
|
||||
private void addMigration(String key, MigrationMetaRow metaRow) {
|
||||
lastMigration = metaRow;
|
||||
String runVersion = metaRow.getRunVersion();
|
||||
if (runVersion == null) {
|
||||
if (metaRow.getVersion() == null) {
|
||||
throw new IllegalStateException("No runVersion in db migration table row? " + metaRow);
|
||||
}
|
||||
migrations.put(runVersion, metaRow);
|
||||
migrations.put(key, metaRow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,4 @@ public interface SpiServer extends EbeanServer {
|
||||
*/
|
||||
DataSource getDataSource();
|
||||
|
||||
/**
|
||||
* Initialise the query cache for the given bean type.
|
||||
*/
|
||||
boolean initQueryCache(String key);
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* Optimistic concurrency mode used for updates and deletes.
|
||||
@@ -3,7 +3,6 @@ package com.avaje.ebeaninternal.api;
|
||||
import java.sql.PreparedStatement;
|
||||
|
||||
import com.avaje.ebean.SqlQuery;
|
||||
import com.avaje.ebean.SqlQueryListener;
|
||||
|
||||
/**
|
||||
* SQL query - Internal extension to SqlQuery.
|
||||
@@ -20,11 +19,6 @@ public interface SpiSqlQuery extends SqlQuery {
|
||||
*/
|
||||
String getQuery();
|
||||
|
||||
/**
|
||||
* Return the queryListener.
|
||||
*/
|
||||
SqlQueryListener getListener();
|
||||
|
||||
/**
|
||||
* Return the first row to fetch.
|
||||
*/
|
||||
@@ -35,16 +29,6 @@ public interface SpiSqlQuery extends SqlQuery {
|
||||
*/
|
||||
int getMaxRows();
|
||||
|
||||
/**
|
||||
* Return the number of rows after which background fetching occurs.
|
||||
*/
|
||||
int getBackgroundFetchAfter();
|
||||
|
||||
/**
|
||||
* Return the key property for maps.
|
||||
*/
|
||||
String getMapKey();
|
||||
|
||||
/**
|
||||
* Return the query timeout.
|
||||
*/
|
||||
@@ -55,23 +39,9 @@ public interface SpiSqlQuery extends SqlQuery {
|
||||
*/
|
||||
int getBufferFetchSizeHint();
|
||||
|
||||
/**
|
||||
* Return true if this is a future fetch type query.
|
||||
*/
|
||||
boolean isFutureFetch();
|
||||
|
||||
/**
|
||||
* Set to true if this is a future fetch type query.
|
||||
*/
|
||||
void setFutureFetch(boolean futureFetch);
|
||||
|
||||
/**
|
||||
* Set the PreparedStatement for the purposes of supporting cancel.
|
||||
*/
|
||||
void setPreparedStatement(PreparedStatement pstmt);
|
||||
|
||||
/**
|
||||
* Return true if the query has been cancelled.
|
||||
*/
|
||||
boolean isCancelled();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.avaje.ebean.annotation.ConcurrencyMode;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.persist.dml.DmlHandler;
|
||||
import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
|
||||
|
||||
@@ -108,8 +108,8 @@ public class TransactionEvent implements Serializable {
|
||||
/**
|
||||
* Build and return the cache changeSet.
|
||||
*/
|
||||
public CacheChangeSet buildCacheChanges() {
|
||||
CacheChangeSet changeSet = new CacheChangeSet();
|
||||
public CacheChangeSet buildCacheChanges(boolean viewInvalidation) {
|
||||
CacheChangeSet changeSet = new CacheChangeSet(viewInvalidation);
|
||||
if (eventBeans != null) {
|
||||
eventBeans.notifyCache(changeSet);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
|
||||
package com.avaje.ebeaninternal.extraddl.model;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
import javax.xml.bind.annotation.XmlValue;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <simpleContent>
|
||||
* <extension base="<http://www.w3.org/2001/XMLSchema>string">
|
||||
* <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="platforms" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* </extension>
|
||||
* </simpleContent>
|
||||
* </complexType>
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(name = "", propOrder = {
|
||||
"value"
|
||||
})
|
||||
@XmlRootElement(name = "ddl-script")
|
||||
public class DdlScript {
|
||||
|
||||
@XmlValue
|
||||
protected String value;
|
||||
@XmlAttribute(name = "name", required = true)
|
||||
protected String name;
|
||||
@XmlAttribute(name = "platforms")
|
||||
protected String platforms;
|
||||
|
||||
/**
|
||||
* Gets the value of the value property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the value property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the name property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the name property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public void setName(String value) {
|
||||
this.name = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the platforms property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public String getPlatforms() {
|
||||
return platforms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the platforms property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public void setPlatforms(String value) {
|
||||
this.platforms = value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
|
||||
package com.avaje.ebeaninternal.extraddl.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||
* <sequence>
|
||||
* <element ref="{http://ebean-orm.github.io/xml/ns/extraddl}ddl-script" maxOccurs="unbounded"/>
|
||||
* </sequence>
|
||||
* </restriction>
|
||||
* </complexContent>
|
||||
* </complexType>
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(name = "", propOrder = {
|
||||
"ddlScript"
|
||||
})
|
||||
@XmlRootElement(name = "extra-ddl")
|
||||
public class ExtraDdl {
|
||||
|
||||
@XmlElement(name = "ddl-script", required = true)
|
||||
protected List<DdlScript> ddlScript;
|
||||
|
||||
/**
|
||||
* Gets the value of the ddlScript property.
|
||||
*
|
||||
* <p>
|
||||
* This accessor method returns a reference to the live list,
|
||||
* not a snapshot. Therefore any modification you make to the
|
||||
* returned list will be present inside the JAXB object.
|
||||
* This is why there is not a <CODE>set</CODE> method for the ddlScript property.
|
||||
*
|
||||
* <p>
|
||||
* For example, to add a new item, do as follows:
|
||||
* <pre>
|
||||
* getDdlScript().add(newItem);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Objects of the following type(s) are allowed in the list
|
||||
* {@link DdlScript }
|
||||
*
|
||||
*
|
||||
*/
|
||||
public List<DdlScript> getDdlScript() {
|
||||
if (ddlScript == null) {
|
||||
ddlScript = new ArrayList<DdlScript>();
|
||||
}
|
||||
return this.ddlScript;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.avaje.ebeaninternal.extraddl.model;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Read ExtraDdl from an XML document.
|
||||
*/
|
||||
public class ExtraDdlXmlReader {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ExtraDdlXmlReader.class);
|
||||
|
||||
/**
|
||||
* Return the combined extra DDL that should be run given the platform name.
|
||||
*/
|
||||
public static String buildExtra(String platformName) {
|
||||
|
||||
ExtraDdl read = ExtraDdlXmlReader.read("/extra-ddl.xml");
|
||||
if (read == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(300);
|
||||
for (DdlScript script : read.getDdlScript()) {
|
||||
if (matchPlatform(platformName, script.getPlatforms())) {
|
||||
logger.debug("include script {}", script.getName());
|
||||
sb.append(script.getValue()).append("\n");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the script platforms is a match/supported for the given platform.
|
||||
* @param platformName The database platform we are generating/running DDL for
|
||||
* @param platforms The platforms (comma delimited) this script should run for
|
||||
*/
|
||||
public static boolean matchPlatform(String platformName, String platforms) {
|
||||
if (platforms == null || platforms.trim().length() == 0) {
|
||||
return true;
|
||||
}
|
||||
String[] names = platforms.split("[,;]");
|
||||
for (String name : names) {
|
||||
if (name.trim().toLowerCase().contains(platformName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and return a ExtraDdl from an xml document at the given resource path.
|
||||
*/
|
||||
public static ExtraDdl read(String resourcePath) {
|
||||
|
||||
InputStream is = ExtraDdlXmlReader.class.getResourceAsStream(resourcePath);
|
||||
if (is == null) {
|
||||
// we expect this and check for null
|
||||
return null;
|
||||
}
|
||||
return read(is);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and return a ExtraDdl from an xml document.
|
||||
*/
|
||||
public static ExtraDdl read(InputStream is) {
|
||||
|
||||
try {
|
||||
JAXBContext jaxbContext = JAXBContext.newInstance(ExtraDdl.class);
|
||||
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
|
||||
return (ExtraDdl) unmarshaller.unmarshal(is);
|
||||
|
||||
} catch (JAXBException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
package com.avaje.ebeaninternal.extraddl.model;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRegistry;
|
||||
|
||||
|
||||
/**
|
||||
* This object contains factory methods for each
|
||||
* Java content interface and Java element interface
|
||||
* generated in the com.avaje.ebeaninternal.extraddl.model package.
|
||||
* <p>An ObjectFactory allows you to programatically
|
||||
* construct new instances of the Java representation
|
||||
* for XML content. The Java representation of XML
|
||||
* content can consist of schema derived interfaces
|
||||
* and classes representing the binding of schema
|
||||
* type definitions, element declarations and model
|
||||
* groups. Factory methods for each of these are
|
||||
* provided in this class.
|
||||
*
|
||||
*/
|
||||
@XmlRegistry
|
||||
public class ObjectFactory {
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.avaje.ebeaninternal.extraddl.model
|
||||
*
|
||||
*/
|
||||
public ObjectFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link DdlScript }
|
||||
*
|
||||
*/
|
||||
public DdlScript createDdlScript() {
|
||||
return new DdlScript();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link ExtraDdl }
|
||||
*
|
||||
*/
|
||||
public ExtraDdl createExtraDdl() {
|
||||
return new ExtraDdl();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
@javax.xml.bind.annotation.XmlSchema(namespace = "http://ebean-orm.github.io/xml/ns/extraddl", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
|
||||
package com.avaje.ebeaninternal.extraddl.model;
|
||||
@@ -22,9 +22,25 @@ public class CacheChangeSet {
|
||||
private final Map<ManyKey, ManyChange> manyChangeMap = new HashMap<ManyKey, ManyChange>();
|
||||
|
||||
/**
|
||||
* Apply all the changes to the L2 cache.
|
||||
* Set of "base tables" modified used to invalidate entities based on views.
|
||||
*/
|
||||
public void apply() {
|
||||
private final Set<String> viewInvalidation = new HashSet<String>();
|
||||
|
||||
private final boolean viewEntityInvalidation;
|
||||
|
||||
/**
|
||||
* Construct specifying if we also need to process invalidation for entities based on views.
|
||||
*/
|
||||
public CacheChangeSet(boolean viewEntityInvalidation) {
|
||||
this.viewEntityInvalidation = viewEntityInvalidation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the changes to the L2 cache except entity/view invalidation.
|
||||
*
|
||||
* Return the set of table changes to process invalidation for entities based on views.
|
||||
*/
|
||||
public Set<String> apply() {
|
||||
for (BeanDescriptor entry : queryCaches) {
|
||||
entry.queryCacheClear();
|
||||
}
|
||||
@@ -34,6 +50,7 @@ public class CacheChangeSet {
|
||||
for (CacheChange entry : manyChangeMap.values()) {
|
||||
entry.apply();
|
||||
}
|
||||
return viewInvalidation;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,11 +81,23 @@ public class CacheChangeSet {
|
||||
many(desc, manyProperty).addPut(parentId, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* On bean insert register table for view based entity invalidation.
|
||||
*/
|
||||
public void addBeanInsert(String baseTable) {
|
||||
if (viewEntityInvalidation) {
|
||||
viewInvalidation.add(baseTable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a bean from the cache.
|
||||
*/
|
||||
public <T> void addBeanRemove(BeanDescriptor<T> desc, Object id) {
|
||||
entries.add(new CacheChangeBeanRemove(desc, id));
|
||||
if (viewEntityInvalidation) {
|
||||
viewInvalidation.add(desc.getBaseTable());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,6 +105,9 @@ public class CacheChangeSet {
|
||||
*/
|
||||
public <T> void addBeanUpdate(BeanDescriptor<T> desc, Object id, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
entries.add(new CacheChangeBeanUpdate(desc, id, changes, updateNaturalKey, version));
|
||||
if (viewEntityInvalidation) {
|
||||
viewInvalidation.add(desc.getBaseTable());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+6
-5
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.cache;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
@@ -9,20 +10,20 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
public class CachedBeanDataToBean {
|
||||
|
||||
|
||||
public static void load(BeanDescriptor<?> desc, EntityBean bean, CachedBeanData cacheBeanData) {
|
||||
public static void load(BeanDescriptor<?> desc, EntityBean bean, CachedBeanData cacheBeanData, PersistenceContext context) {
|
||||
|
||||
EntityBeanIntercept ebi = bean._ebean_getIntercept();
|
||||
|
||||
BeanProperty idProperty = desc.getIdProperty();
|
||||
if (idProperty != null) {
|
||||
// load the id property
|
||||
loadProperty(bean, cacheBeanData, ebi, idProperty);
|
||||
loadProperty(bean, cacheBeanData, ebi, idProperty, context);
|
||||
}
|
||||
|
||||
// load the non-many properties
|
||||
BeanProperty[] props = desc.propertiesNonMany();
|
||||
for (int i = 0; i < props.length; i++) {
|
||||
loadProperty(bean, cacheBeanData, ebi, props[i]);
|
||||
loadProperty(bean, cacheBeanData, ebi, props[i], context);
|
||||
}
|
||||
|
||||
BeanPropertyAssocMany<?>[] many = desc.propertiesMany();
|
||||
@@ -33,12 +34,12 @@ public class CachedBeanDataToBean {
|
||||
ebi.setLoadedLazy();
|
||||
}
|
||||
|
||||
private static void loadProperty(EntityBean bean, CachedBeanData cacheBeanData, EntityBeanIntercept ebi, BeanProperty prop) {
|
||||
private static void loadProperty(EntityBean bean, CachedBeanData cacheBeanData, EntityBeanIntercept ebi, BeanProperty prop, PersistenceContext context) {
|
||||
|
||||
if (cacheBeanData.isLoaded(prop.getName())) {
|
||||
if (!ebi.isLoadedProperty(prop.getPropertyIndex())) {
|
||||
Object value = cacheBeanData.getData(prop.getName());
|
||||
prop.setCacheDataValue(bean, value);
|
||||
prop.setCacheDataValue(bean, value, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.cache;
|
||||
|
||||
import com.avaje.ebean.annotation.CacheQueryTuning;
|
||||
import com.avaje.ebean.annotation.CacheTuning;
|
||||
import com.avaje.ebean.annotation.CacheBeanTuning;
|
||||
import com.avaje.ebean.cache.ServerCache;
|
||||
import com.avaje.ebean.cache.ServerCacheFactory;
|
||||
import com.avaje.ebean.cache.ServerCacheOptions;
|
||||
@@ -109,9 +109,9 @@ public class DefaultCacheHolder {
|
||||
}
|
||||
|
||||
private ServerCacheOptions getBeanOptions(Class<?> cls) {
|
||||
CacheTuning cacheTuning = cls.getAnnotation(CacheTuning.class);
|
||||
if (cacheTuning != null) {
|
||||
ServerCacheOptions o = new ServerCacheOptions(cacheTuning);
|
||||
CacheBeanTuning tuning = cls.getAnnotation(CacheBeanTuning.class);
|
||||
if (tuning != null) {
|
||||
ServerCacheOptions o = new ServerCacheOptions(tuning);
|
||||
o.applyDefaults(defaultOptions);
|
||||
return o;
|
||||
}
|
||||
|
||||
+1
-31
@@ -107,15 +107,12 @@ public class DefaultServerCache implements ServerCache {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(EbeanServer server) {
|
||||
public void periodicTrim(BackgroundExecutor executor) {
|
||||
|
||||
EvictionRunnable trim = new EvictionRunnable();
|
||||
|
||||
// default to trimming the cache every 60 seconds
|
||||
long trimFreqSecs = (trimFrequency == 0) ? 60 : trimFrequency;
|
||||
|
||||
BackgroundExecutor executor = server.getBackgroundExecutor();
|
||||
executor.executePeriodically(trim, trimFreqSecs, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@@ -176,33 +173,6 @@ public class DefaultServerCache implements ServerCache {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the options controlling the cache.
|
||||
*/
|
||||
@Override
|
||||
public ServerCacheOptions getOptions() {
|
||||
synchronized (monitor) {
|
||||
ServerCacheOptions options = new ServerCacheOptions();
|
||||
options.setMaxIdleSecs(maxIdleSecs);
|
||||
options.setMaxSize(maxSize);
|
||||
options.setMaxSecsToLive(maxSecsToLive);
|
||||
options.setTrimFrequency(trimFrequency);
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the options controlling the cache
|
||||
*/
|
||||
@Override
|
||||
public void setOptions(ServerCacheOptions options) {
|
||||
synchronized (monitor) {
|
||||
maxIdleSecs = options.getMaxIdleSecs();
|
||||
maxSize = options.getMaxSize();
|
||||
maxSecsToLive = options.getMaxSecsToLive();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the cache.
|
||||
*/
|
||||
|
||||
+19
-7
@@ -1,6 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.cache;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.cache.ServerCache;
|
||||
import com.avaje.ebean.cache.ServerCacheFactory;
|
||||
import com.avaje.ebean.cache.ServerCacheOptions;
|
||||
@@ -10,18 +10,30 @@ import com.avaje.ebean.cache.ServerCacheType;
|
||||
/**
|
||||
* Default implementation of ServerCacheFactory.
|
||||
*/
|
||||
public class DefaultServerCacheFactory implements ServerCacheFactory {
|
||||
class DefaultServerCacheFactory implements ServerCacheFactory {
|
||||
|
||||
private EbeanServer ebeanServer;
|
||||
private final BackgroundExecutor executor;
|
||||
|
||||
public void init(EbeanServer ebeanServer) {
|
||||
this.ebeanServer = ebeanServer;
|
||||
/**
|
||||
* Construct when l2 cache is disabled.
|
||||
*/
|
||||
public DefaultServerCacheFactory() {
|
||||
this.executor = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct with executor service.
|
||||
*/
|
||||
public DefaultServerCacheFactory(BackgroundExecutor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public ServerCache createCache(ServerCacheType type, String cacheKey, ServerCacheOptions cacheOptions) {
|
||||
|
||||
ServerCache cache = new DefaultServerCache(cacheKey, cacheOptions);
|
||||
cache.init(ebeanServer);
|
||||
DefaultServerCache cache = new DefaultServerCache(cacheKey, cacheOptions);
|
||||
if (executor != null) {
|
||||
cache.periodicTrim(executor);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
|
||||
-19
@@ -1,12 +1,10 @@
|
||||
package com.avaje.ebeaninternal.server.cache;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.cache.ServerCache;
|
||||
import com.avaje.ebean.cache.ServerCacheFactory;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.cache.ServerCacheOptions;
|
||||
import com.avaje.ebean.cache.ServerCacheType;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
|
||||
|
||||
/**
|
||||
@@ -22,15 +20,10 @@ public class DefaultServerCacheManager implements ServerCacheManager {
|
||||
|
||||
private final DefaultCacheHolder collectionIdsCache;
|
||||
|
||||
private final ServerCacheFactory cacheFactory;
|
||||
|
||||
private SpiEbeanServer ebeanServer;
|
||||
|
||||
/**
|
||||
* Create with a cache factory and default cache options.
|
||||
*/
|
||||
public DefaultServerCacheManager(ServerCacheFactory cacheFactory, ServerCacheOptions defaultBeanOptions, ServerCacheOptions defaultQueryOptions) {
|
||||
this.cacheFactory = cacheFactory;
|
||||
this.beanCache = new DefaultCacheHolder(cacheFactory, defaultBeanOptions);
|
||||
this.queryCache = new DefaultCacheHolder(cacheFactory, defaultQueryOptions);
|
||||
this.naturalKeyCache = new DefaultCacheHolder(cacheFactory, defaultBeanOptions);
|
||||
@@ -44,18 +37,6 @@ public class DefaultServerCacheManager implements ServerCacheManager {
|
||||
this(new DefaultServerCacheFactory(), new ServerCacheOptions(), new ServerCacheOptions());
|
||||
}
|
||||
|
||||
public void init(EbeanServer server) {
|
||||
cacheFactory.init(server);
|
||||
this.ebeanServer = (SpiEbeanServer) server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set bean caching on or off for a given bean type.
|
||||
*/
|
||||
public void setCaching(Class<?> beanType, boolean useCache) {
|
||||
ebeanServer.getBeanDescriptor(beanType).setUseCache(useCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear both the bean cache and the query cache for a
|
||||
* given bean type.
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.avaje.ebeaninternal.server.cache;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.cache.ServerCacheFactory;
|
||||
import com.avaje.ebean.cache.ServerCachePlugin;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
|
||||
/**
|
||||
* Default implementation of ServerCachePlugin.
|
||||
*/
|
||||
public class DefaultServerCachePlugin implements ServerCachePlugin {
|
||||
|
||||
/**
|
||||
* Creates the default ServerCacheFactory.
|
||||
*/
|
||||
@Override
|
||||
public ServerCacheFactory create(ServerConfig config, BackgroundExecutor executor) {
|
||||
return new DefaultServerCacheFactory(executor);
|
||||
}
|
||||
}
|
||||
@@ -1,111 +1,67 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import com.avaje.ebean.annotation.Cache;
|
||||
|
||||
/**
|
||||
* Options for controlling cache behaviour for a given type.
|
||||
*/
|
||||
public class CacheOptions {
|
||||
|
||||
private boolean useCache;
|
||||
|
||||
private boolean readOnly;
|
||||
|
||||
private String naturalKey;
|
||||
|
||||
private int maxIdleSecs;
|
||||
|
||||
private long maxSecsToLive;
|
||||
|
||||
/**
|
||||
* Construct with options.
|
||||
* Instance when no caching is used.
|
||||
*/
|
||||
public CacheOptions() {
|
||||
public static CacheOptions NO_CACHING = new CacheOptions();
|
||||
|
||||
private final boolean enableBeanCache;
|
||||
private final boolean enableQueryCache;
|
||||
private final boolean readOnly;
|
||||
private final String naturalKey;
|
||||
|
||||
/**
|
||||
* Construct for no caching.
|
||||
*/
|
||||
private CacheOptions() {
|
||||
enableBeanCache = false;
|
||||
enableQueryCache = false;
|
||||
readOnly = false;
|
||||
naturalKey = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this should use a cache for lazy loading.
|
||||
* Construct with cache annotation.
|
||||
*/
|
||||
public boolean isUseCache() {
|
||||
return useCache;
|
||||
public CacheOptions(Cache cache, String naturalKey) {
|
||||
enableBeanCache = cache.enableBeanCache();
|
||||
enableQueryCache = cache.enableQueryCache();
|
||||
readOnly = cache.readOnly();
|
||||
this.naturalKey = naturalKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to use the bean cache for the associated type.
|
||||
* Return true if bean caching is enabled.
|
||||
*/
|
||||
public void setUseCache(boolean useCache) {
|
||||
this.useCache = useCache;
|
||||
public boolean isEnableBeanCache() {
|
||||
return enableBeanCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the readOnly default setting.
|
||||
* Return true if query caching is enabled.
|
||||
*/
|
||||
public boolean isEnableQueryCache() {
|
||||
return enableQueryCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if bean cache hits default to read only.
|
||||
*/
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set read Only default setting.
|
||||
*/
|
||||
public void setReadOnly(boolean readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a natural key is set.
|
||||
*/
|
||||
public boolean isUseNaturalKeyCache() {
|
||||
return naturalKey != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the natural key property.
|
||||
* Return the natural key property name.
|
||||
*/
|
||||
public String getNaturalKey() {
|
||||
return naturalKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the natural key property.
|
||||
*/
|
||||
public void setNaturalKey(String naturalKey) {
|
||||
if (naturalKey != null && naturalKey.length() != 0) {
|
||||
this.naturalKey = naturalKey.trim();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the max age of entries in seconds.
|
||||
*/
|
||||
public long getMaxSecsToLive() {
|
||||
return maxSecsToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max age of entries in seconds.
|
||||
*/
|
||||
public void setMaxSecsToLive(long maxSecsToLive) {
|
||||
this.maxSecsToLive = maxSecsToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max idle seconds.
|
||||
*/
|
||||
public void setMaxIdleSecs(int maxIdleSecs) {
|
||||
this.maxIdleSecs = maxIdleSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the max idle seconds.
|
||||
*/
|
||||
public int getMaxIdleSecs() {
|
||||
return maxIdleSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the entry exceeds the maxIdleSecs or maxSecsToLive.
|
||||
*/
|
||||
public boolean isTooOldInMillis(long ageMillis) {
|
||||
long secs = ageMillis / 1000;
|
||||
return (maxIdleSecs > 0 && secs > maxIdleSecs) || (maxSecsToLive > 0 && secs > maxSecsToLive);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -270,15 +270,15 @@ public class DefaultBeanLoader {
|
||||
boolean draft = desc.isDraftInstance(bean);
|
||||
|
||||
if (embeddedOwnerIndex == -1) {
|
||||
if (!draft && SpiQuery.Mode.LAZYLOAD_BEAN.equals(mode) && desc.isBeanCaching()) {
|
||||
// lazy loading and the bean cache is active
|
||||
if (desc.cacheBeanLoad(bean, ebi, id)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (desc.lazyLoadMany(ebi)) {
|
||||
return;
|
||||
}
|
||||
if (!draft && SpiQuery.Mode.LAZYLOAD_BEAN.equals(mode) && desc.isBeanCaching()) {
|
||||
// lazy loading and the bean cache is active
|
||||
if (desc.cacheBeanLoad(bean, ebi, id, pc)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(desc.getBeanType());
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.cache.ServerCacheFactory;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.cache.ServerCacheOptions;
|
||||
import com.avaje.ebean.cache.ServerCachePlugin;
|
||||
import com.avaje.ebean.common.SpiContainer;
|
||||
import com.avaje.ebean.config.ContainerConfig;
|
||||
import com.avaje.ebean.config.PropertyMap;
|
||||
@@ -12,7 +14,7 @@ import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.dbmigration.DbOffline;
|
||||
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.cache.DefaultServerCacheFactory;
|
||||
import com.avaje.ebeaninternal.server.cache.DefaultServerCachePlugin;
|
||||
import com.avaje.ebeaninternal.server.cache.DefaultServerCacheManager;
|
||||
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
|
||||
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
|
||||
@@ -108,19 +110,15 @@ public class DefaultContainer implements SpiContainer {
|
||||
// inform the NamingConvention of the associated DatabasePlatform
|
||||
serverConfig.getNamingConvention().setDatabasePlatform(serverConfig.getDatabasePlatform());
|
||||
|
||||
ServerCacheManager cacheManager = getCacheManager(online, serverConfig);
|
||||
// executor and l2 caching service setup early (used during server construction)
|
||||
SpiBackgroundExecutor executor = createBackgroundExecutor(serverConfig);
|
||||
ServerCacheManager cacheManager = getCacheManager(online, serverConfig, executor);
|
||||
|
||||
SpiBackgroundExecutor bgExecutor = createBackgroundExecutor(serverConfig);
|
||||
|
||||
XmlConfigLoader xmlConfigLoader = new XmlConfigLoader(null);
|
||||
XmlConfig xmlConfig = xmlConfigLoader.load();
|
||||
|
||||
InternalConfiguration c = new InternalConfiguration(xmlConfig, clusterManager, cacheManager, bgExecutor, serverConfig, bootupClasses);
|
||||
XmlConfig xmlConfig = new XmlConfigLoader(null).load();
|
||||
InternalConfiguration c = new InternalConfiguration(xmlConfig, clusterManager, cacheManager, executor, serverConfig, bootupClasses);
|
||||
|
||||
DefaultServer server = new DefaultServer(c, cacheManager);
|
||||
|
||||
cacheManager.init(server);
|
||||
|
||||
// generate and run DDL if required
|
||||
// if there are any other tasks requiring action in their plugins, do them as well
|
||||
if (!DbOffline.isRunningMigration()) {
|
||||
@@ -147,7 +145,7 @@ public class DefaultContainer implements SpiContainer {
|
||||
/**
|
||||
* Create and return the CacheManager.
|
||||
*/
|
||||
private ServerCacheManager getCacheManager(boolean online, ServerConfig serverConfig) {
|
||||
private ServerCacheManager getCacheManager(boolean online, ServerConfig serverConfig, BackgroundExecutor executor) {
|
||||
|
||||
if (!online || serverConfig.isDisableL2Cache()) {
|
||||
// use local only L2 cache implementation as placeholder
|
||||
@@ -171,21 +169,22 @@ public class DefaultContainer implements SpiContainer {
|
||||
queryOptions.setMaxIdleSecs(serverConfig.getQueryCacheMaxIdleTime());
|
||||
queryOptions.setMaxSecsToLive(serverConfig.getQueryCacheMaxTimeToLive());
|
||||
|
||||
ServerCacheFactory cacheFactory = serverConfig.getServerCacheFactory();
|
||||
if (cacheFactory == null) {
|
||||
ServiceLoader<ServerCacheFactory> cacheFactories = ServiceLoader.load(ServerCacheFactory.class);
|
||||
Iterator<ServerCacheFactory> iterator = cacheFactories.iterator();
|
||||
ServerCachePlugin plugin = serverConfig.getServerCachePlugin();
|
||||
if (plugin == null) {
|
||||
ServiceLoader<ServerCachePlugin> cacheFactories = ServiceLoader.load(ServerCachePlugin.class);
|
||||
Iterator<ServerCachePlugin> iterator = cacheFactories.iterator();
|
||||
if (iterator.hasNext()) {
|
||||
// use the cacheFactory (via classpath service loader)
|
||||
cacheFactory = iterator.next();
|
||||
logger.debug("using ServerCacheFactory {}", cacheFactory.getClass());
|
||||
plugin = iterator.next();
|
||||
logger.debug("using ServerCacheFactory {}", plugin.getClass());
|
||||
} else {
|
||||
// use the built in default
|
||||
cacheFactory = new DefaultServerCacheFactory();
|
||||
plugin = new DefaultServerCachePlugin();
|
||||
}
|
||||
}
|
||||
|
||||
return new DefaultServerCacheManager(cacheFactory, beanOptions, queryOptions);
|
||||
ServerCacheFactory factory = plugin.create(serverConfig, executor);
|
||||
return new DefaultServerCacheManager(factory, beanOptions, queryOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,10 +32,10 @@ import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery.Type;
|
||||
import com.avaje.ebeaninternal.api.SpiSqlQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.api.TransactionEventTable;
|
||||
import com.avaje.ebeaninternal.server.autotune.AutoTuneService;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
@@ -50,12 +50,10 @@ import com.avaje.ebeaninternal.server.query.CQueryEngine;
|
||||
import com.avaje.ebeaninternal.server.query.CallableQueryIds;
|
||||
import com.avaje.ebeaninternal.server.query.CallableQueryList;
|
||||
import com.avaje.ebeaninternal.server.query.CallableQueryRowCount;
|
||||
import com.avaje.ebeaninternal.server.query.CallableSqlQueryList;
|
||||
import com.avaje.ebeaninternal.server.query.LimitOffsetPagedList;
|
||||
import com.avaje.ebeaninternal.server.query.QueryFutureIds;
|
||||
import com.avaje.ebeaninternal.server.query.QueryFutureList;
|
||||
import com.avaje.ebeaninternal.server.query.QueryFutureRowCount;
|
||||
import com.avaje.ebeaninternal.server.query.SqlQueryFutureList;
|
||||
import com.avaje.ebeaninternal.server.querydefn.DefaultOrmQuery;
|
||||
import com.avaje.ebeaninternal.server.querydefn.DefaultOrmUpdate;
|
||||
import com.avaje.ebeaninternal.server.querydefn.DefaultRelationalQuery;
|
||||
@@ -64,7 +62,6 @@ 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;
|
||||
@@ -84,7 +81,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
/**
|
||||
* The default server side implementation of EbeanServer.
|
||||
@@ -339,11 +335,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
if (encryptKeyManager != null) {
|
||||
encryptKeyManager.initialise();
|
||||
}
|
||||
List<BeanDescriptor<?>> list = beanDescriptorManager.getBeanDescriptorList();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
list.get(i).cacheInitialise();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1030,7 +1021,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
* Try to get the object out of the persistence context.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T findIdCheckPersistenceContextAndCache(Transaction transaction, SpiQuery<T> query) {
|
||||
private <T> T findIdCheckPersistenceContextAndCache(Transaction transaction, SpiQuery<T> query, Object id) {
|
||||
|
||||
SpiTransaction t = (SpiTransaction) transaction;
|
||||
if (t == null) {
|
||||
@@ -1038,13 +1029,14 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
BeanDescriptor<T> desc = query.getBeanDescriptor();
|
||||
id = desc.convertId(id);
|
||||
|
||||
PersistenceContext pc = null;
|
||||
if (t != null && useTransactionPersistenceContext(query)) {
|
||||
// first look in the transaction scoped persistence context
|
||||
pc = t.getPersistenceContext();
|
||||
if (pc != null) {
|
||||
WithOption o = desc.contextGetWithOption(pc, query.getId());
|
||||
WithOption o = desc.contextGetWithOption(pc, id);
|
||||
if (o != null) {
|
||||
if (o.isDeleted()) {
|
||||
// Bean was previously deleted in the same transaction / persistence context
|
||||
@@ -1061,7 +1053,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
// Hit the L2 bean cache
|
||||
return desc.cacheBeanGet(query, pc);
|
||||
return desc.cacheBeanGet(id, query.isReadOnly(), pc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1087,7 +1079,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
if (SpiQuery.Mode.NORMAL.equals(spiQuery.getMode()) && !spiQuery.isLoadBeanCache()) {
|
||||
// See if we can skip doing the fetch completely by getting the bean from the
|
||||
// persistence context or the bean cache
|
||||
T bean = findIdCheckPersistenceContextAndCache(t, spiQuery);
|
||||
T bean = findIdCheckPersistenceContextAndCache(t, spiQuery, spiQuery.getId());
|
||||
if (bean != null) {
|
||||
return bean;
|
||||
}
|
||||
@@ -1108,19 +1100,21 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
public <T> T findUnique(Query<T> query, Transaction t) {
|
||||
|
||||
// actually a find by Id type of query...
|
||||
// ... perhaps with joins and cache hints
|
||||
Object id = query.getId();
|
||||
if (id != null) {
|
||||
// actually a find by Id query
|
||||
return findId(query, t);
|
||||
}
|
||||
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
BeanDescriptor<T> desc = spiQuery.getBeanDescriptor();
|
||||
|
||||
T bean = desc.cacheNaturalKeyLookup(spiQuery, (SpiTransaction) t);
|
||||
if (bean != null) {
|
||||
return bean;
|
||||
id = desc.cacheNaturalKeyIdLookup(spiQuery);
|
||||
if (id != null) {
|
||||
T bean = findIdCheckPersistenceContextAndCache(t, spiQuery, id);
|
||||
if (bean != null) {
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
// a query that is expected to return either 0 or 1 rows
|
||||
@@ -1389,19 +1383,28 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return extractUnique(list);
|
||||
}
|
||||
|
||||
public SqlFutureList findFutureList(SqlQuery query, Transaction t) {
|
||||
@Override
|
||||
public void findEach(SqlQuery query, QueryEachConsumer<SqlRow> consumer, Transaction transaction) {
|
||||
|
||||
SpiSqlQuery spiQuery = (SpiSqlQuery) query;
|
||||
spiQuery.setFutureFetch(true);
|
||||
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, transaction);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
request.findEach(consumer);
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
}
|
||||
|
||||
Transaction newTxn = createTransaction();
|
||||
CallableSqlQueryList call = new CallableSqlQueryList(this, query, newTxn);
|
||||
@Override
|
||||
public void findEachWhile(SqlQuery query, QueryEachWhileConsumer<SqlRow> consumer, Transaction transaction) {
|
||||
|
||||
FutureTask<List<SqlRow>> futureTask = new FutureTask<List<SqlRow>>(call);
|
||||
|
||||
backgroundExecutor.execute(futureTask);
|
||||
|
||||
return new SqlQueryFutureList(query, futureTask);
|
||||
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, transaction);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
request.findEachWhile(consumer);
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
}
|
||||
|
||||
public List<SqlRow> findList(SqlQuery query, Transaction t) {
|
||||
@@ -1417,31 +1420,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
public Set<SqlRow> findSet(SqlQuery query, Transaction t) {
|
||||
|
||||
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t);
|
||||
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return request.findSet();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
}
|
||||
|
||||
public Map<?, SqlRow> findMap(SqlQuery query, Transaction t) {
|
||||
|
||||
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return request.findMap();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist the bean by either performing an insert or update.
|
||||
*/
|
||||
@@ -1456,7 +1434,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
persister.save(checkEntityBean(bean), t);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void markAsDirty(Object bean) {
|
||||
if (!(bean instanceof EntityBean)) {
|
||||
@@ -2046,16 +2023,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return beanDescriptorManager.getBeanDescriptorByClassName(beanClassName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initQueryCache(String key) {
|
||||
BeanDescriptor<?> desc = getBeanDescriptorById(key);
|
||||
if (desc != null) {
|
||||
desc.queryCacheInit();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Another server in the cluster sent this event so that we can inform local
|
||||
* BeanListeners of inserts updates and deletes that occurred remotely (on
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import com.avaje.ebean.ValuePair;
|
||||
import com.avaje.ebean.annotation.ConcurrencyMode;
|
||||
import com.avaje.ebeaninternal.api.ConcurrencyMode;
|
||||
import com.avaje.ebean.annotation.DocStoreMode;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
@@ -548,7 +548,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
* Create and return a new reference bean matching this beans Id value.
|
||||
*/
|
||||
public T createReference() {
|
||||
return beanDescriptor.createReference(Boolean.FALSE, getBeanId());
|
||||
return beanDescriptor.createReference(Boolean.FALSE, getBeanId(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
|
||||
import com.avaje.ebean.QueryEachConsumer;
|
||||
import com.avaje.ebean.QueryEachWhileConsumer;
|
||||
import com.avaje.ebean.SqlRow;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface RelationalQueryEngine {
|
||||
|
||||
/**
|
||||
* Find a list of beans using relational query.
|
||||
*/
|
||||
Object findMany(RelationalQueryRequest request);
|
||||
/**
|
||||
* Find a list of beans using relational query.
|
||||
*/
|
||||
List<SqlRow> findList(RelationalQueryRequest request);
|
||||
|
||||
/**
|
||||
* Find each query using relational query.
|
||||
*/
|
||||
void findEach(RelationalQueryRequest request, QueryEachConsumer<SqlRow> consumer);
|
||||
|
||||
/**
|
||||
* Find each while query using relational query.
|
||||
*/
|
||||
void findEach(RelationalQueryRequest request, QueryEachWhileConsumer<SqlRow> consumer);
|
||||
|
||||
}
|
||||
@@ -1,114 +1,273 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.QueryEachConsumer;
|
||||
import com.avaje.ebean.QueryEachWhileConsumer;
|
||||
import com.avaje.ebean.SqlQuery;
|
||||
import com.avaje.ebean.SqlRow;
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiSqlQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.lib.util.Str;
|
||||
import com.avaje.ebeaninternal.server.persist.Binder;
|
||||
import com.avaje.ebeaninternal.server.query.DefaultSqlRow;
|
||||
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
|
||||
import com.avaje.ebeaninternal.server.util.BindParamsParser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Wraps the objects involved in executing a SqlQuery.
|
||||
*/
|
||||
public final class RelationalQueryRequest {
|
||||
|
||||
private final SpiSqlQuery query;
|
||||
private static final int GLOBAL_ROW_LIMIT = Integer.valueOf(System.getProperty("ebean.query.globallimit", "1000000"));
|
||||
|
||||
private final RelationalQueryEngine queryEngine;
|
||||
private static final Logger logger = LoggerFactory.getLogger(RelationalQueryRequest.class);
|
||||
|
||||
private final SpiEbeanServer ebeanServer;
|
||||
private final SpiSqlQuery query;
|
||||
|
||||
private SpiTransaction trans;
|
||||
private final RelationalQueryEngine queryEngine;
|
||||
|
||||
private boolean createdTransaction;
|
||||
private final SpiEbeanServer ebeanServer;
|
||||
|
||||
private SpiQuery.Type queryType;
|
||||
private SpiTransaction trans;
|
||||
|
||||
/**
|
||||
* Create the BeanFindRequest.
|
||||
*/
|
||||
public RelationalQueryRequest(SpiEbeanServer server, RelationalQueryEngine engine, SqlQuery q, Transaction t) {
|
||||
this.ebeanServer = server;
|
||||
this.queryEngine = engine;
|
||||
this.query = (SpiSqlQuery) q;
|
||||
this.trans = (SpiTransaction) t;
|
||||
private boolean createdTransaction;
|
||||
|
||||
private String sql;
|
||||
|
||||
private ResultSet resultSet;
|
||||
|
||||
private int rowCount;
|
||||
|
||||
private String bindLog = "";
|
||||
|
||||
private String[] propertyNames;
|
||||
|
||||
private int estimateCapacity;
|
||||
|
||||
private PreparedStatement pstmt;
|
||||
|
||||
/**
|
||||
* Create the BeanFindRequest.
|
||||
*/
|
||||
public RelationalQueryRequest(SpiEbeanServer server, RelationalQueryEngine engine, SqlQuery q, Transaction t) {
|
||||
this.ebeanServer = server;
|
||||
this.queryEngine = engine;
|
||||
this.query = (SpiSqlQuery) q;
|
||||
this.trans = (SpiTransaction) t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a transaction if none currently exists.
|
||||
*/
|
||||
public void initTransIfRequired() {
|
||||
if (trans == null) {
|
||||
trans = ebeanServer.getCurrentServerTransaction();
|
||||
if (trans == null || !trans.isActive()) {
|
||||
// create a local readOnly transaction
|
||||
trans = ebeanServer.createServerTransaction(false, -1);
|
||||
createdTransaction = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* End the transaction if it was locally created.
|
||||
*/
|
||||
public void endTransIfRequired() {
|
||||
if (createdTransaction) {
|
||||
trans.commit();
|
||||
}
|
||||
}
|
||||
|
||||
public void findEach(QueryEachConsumer<SqlRow> consumer) {
|
||||
queryEngine.findEach(this, consumer);
|
||||
}
|
||||
|
||||
public void findEachWhile(QueryEachWhileConsumer<SqlRow> consumer) {
|
||||
queryEngine.findEach(this, consumer);
|
||||
}
|
||||
|
||||
public List<SqlRow> findList() {
|
||||
return queryEngine.findList(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the find that is to be performed.
|
||||
*/
|
||||
public SpiSqlQuery getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public EbeanServer getEbeanServer() {
|
||||
return ebeanServer;
|
||||
}
|
||||
|
||||
public SpiTransaction getTransaction() {
|
||||
return trans;
|
||||
}
|
||||
|
||||
public boolean isLogSql() {
|
||||
return trans.isLogSql();
|
||||
}
|
||||
|
||||
public boolean isLogSummary() {
|
||||
return trans.isLogSummary();
|
||||
}
|
||||
|
||||
private void setResultSet(ResultSet resultSet) throws SQLException {
|
||||
this.resultSet = resultSet;
|
||||
this.propertyNames = getPropertyNames();
|
||||
// calculate the initialCapacity of the Map to reduce rehashing
|
||||
float initCap = (propertyNames.length) / 0.7f;
|
||||
this.estimateCapacity = (int) initCap + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the list of property names.
|
||||
*/
|
||||
private String[] getPropertyNames() throws SQLException {
|
||||
|
||||
ArrayList<String> propNames = new ArrayList<String>();
|
||||
ResultSetMetaData metaData = resultSet.getMetaData();
|
||||
|
||||
int columnsPlusOne = metaData.getColumnCount() + 1;
|
||||
for (int i = 1; i < columnsPlusOne; i++) {
|
||||
propNames.add(metaData.getColumnLabel(i));
|
||||
}
|
||||
return propNames.toArray(new String[propNames.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bindLog for this request.
|
||||
*/
|
||||
public String getBindLog() {
|
||||
return bindLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we can navigate to the next row.
|
||||
*/
|
||||
public boolean next() throws SQLException {
|
||||
rowCount++;
|
||||
return resultSet.next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the underlying resources.
|
||||
*/
|
||||
public void close() {
|
||||
try {
|
||||
if (resultSet != null) {
|
||||
resultSet.close();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.error(null, e);
|
||||
}
|
||||
try {
|
||||
if (pstmt != null) {
|
||||
pstmt.close();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.error(null, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and return the next SqlRow.
|
||||
*/
|
||||
public SqlRow createNewRow(String dbTrueValue) throws SQLException {
|
||||
|
||||
SqlRow sqlRow = new DefaultSqlRow(estimateCapacity, 0.75f, dbTrueValue);
|
||||
|
||||
int index = 0;
|
||||
for (int i = 0; i < propertyNames.length; i++) {
|
||||
index++;
|
||||
Object value = resultSet.getObject(index);
|
||||
sqlRow.set(propertyNames[i], value);
|
||||
}
|
||||
return sqlRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the SQL taking into account named bind parameters.
|
||||
*/
|
||||
private void prepareSql() {
|
||||
|
||||
String sql = query.getQuery();
|
||||
BindParams bindParams = query.getBindParams();
|
||||
if (!bindParams.isEmpty()) {
|
||||
// convert any named parameters if required
|
||||
sql = BindParamsParser.parse(bindParams, sql);
|
||||
}
|
||||
this.sql = sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare and execute the SQL using the Binder.
|
||||
*/
|
||||
public void executeSql(Binder binder) throws SQLException {
|
||||
|
||||
prepareSql();
|
||||
|
||||
Connection conn = trans.getInternalConnection();
|
||||
|
||||
// synchronise for query.cancel() support
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
}
|
||||
if (query.getBufferFetchSizeHint() > 0) {
|
||||
pstmt.setFetchSize(query.getBufferFetchSizeHint());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a transaction if none currently exists.
|
||||
*/
|
||||
public void initTransIfRequired() {
|
||||
if (trans == null) {
|
||||
trans = ebeanServer.getCurrentServerTransaction();
|
||||
if (trans == null || !trans.isActive()) {
|
||||
// create a local readOnly transaction
|
||||
trans = ebeanServer.createServerTransaction(false, -1);
|
||||
createdTransaction = true;
|
||||
}
|
||||
}
|
||||
BindParams bindParams = query.getBindParams();
|
||||
if (!bindParams.isEmpty()) {
|
||||
this.bindLog = binder.bind(bindParams, pstmt);
|
||||
}
|
||||
|
||||
/**
|
||||
* End the transaction if it was locally created.
|
||||
*/
|
||||
public void endTransIfRequired() {
|
||||
if (createdTransaction) {
|
||||
trans.commit();
|
||||
}
|
||||
if (isLogSql()) {
|
||||
String logSql = sql;
|
||||
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
|
||||
logSql = Str.add(logSql, "; --bind(", bindLog, ")");
|
||||
}
|
||||
trans.logSql(logSql);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<SqlRow> findList() {
|
||||
queryType = SpiQuery.Type.LIST;
|
||||
return (List<SqlRow>) queryEngine.findMany(this);
|
||||
}
|
||||
setResultSet(pstmt.executeQuery());
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Set<SqlRow> findSet() {
|
||||
queryType = SpiQuery.Type.SET;
|
||||
return (Set<SqlRow>) queryEngine.findMany(this);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<?, SqlRow> findMap() {
|
||||
queryType = SpiQuery.Type.MAP;
|
||||
return (Map<?, SqlRow>) queryEngine.findMany(this);
|
||||
}
|
||||
/**
|
||||
* Return the maxRows allowed to fetch.
|
||||
*/
|
||||
public int getMaxRows() {
|
||||
return query.getMaxRows() >= 1 ? query.getMaxRows() : GLOBAL_ROW_LIMIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the find that is to be performed.
|
||||
*/
|
||||
public SpiSqlQuery getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type (List, Set or Map) that this fetch returns.
|
||||
*/
|
||||
public SpiQuery.Type getQueryType() {
|
||||
return queryType;
|
||||
}
|
||||
|
||||
public EbeanServer getEbeanServer() {
|
||||
return ebeanServer;
|
||||
}
|
||||
|
||||
public SpiTransaction getTransaction() {
|
||||
return trans;
|
||||
}
|
||||
|
||||
public boolean isLogSql() {
|
||||
return trans.isLogSql();
|
||||
}
|
||||
|
||||
public boolean isLogSummary() {
|
||||
return trans.isLogSummary();
|
||||
}
|
||||
/**
|
||||
* Return the SQL executed for this query.
|
||||
*/
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the rows read.
|
||||
*/
|
||||
public int getRowCount() {
|
||||
return rowCount - 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ class AssocOneHelpRefInherit extends AssocOneHelp {
|
||||
|
||||
// check transaction context to see if it already exists
|
||||
PersistenceContext pc = ctx.getPersistenceContext();
|
||||
BeanDescriptor<?> desc = rowInheritInfo.getBeanDescriptor();
|
||||
BeanDescriptor<?> desc = rowInheritInfo.desc();
|
||||
Object existing = desc.contextGet(pc, id);
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.avaje.ebean.RawSql;
|
||||
import com.avaje.ebean.SqlUpdate;
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.ValuePair;
|
||||
import com.avaje.ebean.annotation.ConcurrencyMode;
|
||||
import com.avaje.ebeaninternal.api.ConcurrencyMode;
|
||||
import com.avaje.ebean.annotation.DocStoreMode;
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
@@ -39,7 +39,6 @@ import com.avaje.ebeaninternal.api.CQueryPlanKey;
|
||||
import com.avaje.ebeaninternal.api.LoadContext;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.api.SpiUpdatePlan;
|
||||
import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD;
|
||||
import com.avaje.ebeaninternal.server.cache.CacheChangeSet;
|
||||
@@ -133,7 +132,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
}
|
||||
|
||||
public enum EntityType {
|
||||
ORM, EMBEDDED, SQL
|
||||
ORM, EMBEDDED, VIEW, SQL
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,6 +178,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
|
||||
private final CompoundUniqueConstraint[] compoundUniqueConstraints;
|
||||
|
||||
private final String[] dependentTables;
|
||||
|
||||
/**
|
||||
* The base database table.
|
||||
*/
|
||||
@@ -446,6 +447,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
this.baseTable = InternString.intern(deploy.getBaseTable());
|
||||
this.baseTableAsOf = deploy.getBaseTableAsOf();
|
||||
this.baseTableVersionsBetween = deploy.getBaseTableVersionsBetween();
|
||||
this.dependentTables = deploy.getDependentTables();
|
||||
this.dbComment = deploy.getDbComment();
|
||||
this.autoTunable = EntityType.ORM.equals(entityType) && (beanFinder == null);
|
||||
|
||||
@@ -720,7 +722,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
propertiesMany[i].initialisePostTarget();
|
||||
}
|
||||
if (inheritInfo != null && !inheritInfo.isRoot()) {
|
||||
docStoreAdapter = (DocStoreBeanAdapter<T>)inheritInfo.getRoot().getBeanDescriptor().docStoreAdapter();
|
||||
docStoreAdapter = (DocStoreBeanAdapter<T>)inheritInfo.getRoot().desc().docStoreAdapter();
|
||||
}
|
||||
docMapping = docStoreAdapter.createDocMapping();
|
||||
docStoreAdapter.registerPaths();
|
||||
@@ -800,13 +802,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
return new BeanChange(getBaseTable(), id, changeType, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the cache once the server has started.
|
||||
*/
|
||||
public void cacheInitialise() {
|
||||
cacheHelp.initialise();
|
||||
}
|
||||
|
||||
public SqlUpdate deleteById(Object id, List<Object> idList, boolean softDelete) {
|
||||
if (id != null) {
|
||||
return deleteById(id, softDelete);
|
||||
@@ -993,7 +988,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
*/
|
||||
public BeanType<?> root() {
|
||||
if (inheritInfo != null && !inheritInfo.isRoot()) {
|
||||
return inheritInfo.getRoot().getBeanDescriptor();
|
||||
return inheritInfo.getRoot().desc();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -1043,13 +1038,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
return draftDirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bean caching on or off.
|
||||
*/
|
||||
public void setUseCache(boolean useCache) {
|
||||
cacheHelp.setUseCache(useCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is currently bean caching for this type of bean.
|
||||
*/
|
||||
@@ -1057,6 +1045,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
return cacheHelp.isBeanCaching();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is query caching for this type of bean.
|
||||
*/
|
||||
public boolean isQueryCaching() {
|
||||
return cacheHelp.isQueryCaching();
|
||||
}
|
||||
|
||||
public boolean isManyPropCaching() {
|
||||
return isBeanCaching();
|
||||
}
|
||||
@@ -1072,10 +1067,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
return cacheHelp.isCacheNotify();
|
||||
}
|
||||
|
||||
public void queryCacheInit() {
|
||||
cacheHelp.queryCacheInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the query cache.
|
||||
*/
|
||||
@@ -1143,22 +1134,22 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
/**
|
||||
* Load the embedded bean (taking into account inheritance).
|
||||
*/
|
||||
public EntityBean cacheEmbeddedBeanLoad(CachedBeanData data) {
|
||||
return cacheHelp.embeddedBeanLoad(data);
|
||||
public EntityBean cacheEmbeddedBeanLoad(CachedBeanData data, PersistenceContext context) {
|
||||
return cacheHelp.embeddedBeanLoad(data, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the embedded bean as the root type.
|
||||
*/
|
||||
EntityBean cacheEmbeddedBeanLoadDirect(CachedBeanData data) {
|
||||
return cacheHelp.embeddedBeanLoadDirect(data);
|
||||
EntityBean cacheEmbeddedBeanLoadDirect(CachedBeanData data, PersistenceContext context) {
|
||||
return cacheHelp.embeddedBeanLoadDirect(data, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the entity bean as the correct bean type.
|
||||
*/
|
||||
EntityBean cacheBeanLoadDirect(Object id, Boolean readOnly, CachedBeanData data) {
|
||||
return cacheHelp.loadBeanDirect(id, readOnly, data);
|
||||
EntityBean cacheBeanLoadDirect(Object id, Boolean readOnly, CachedBeanData data, PersistenceContext context) {
|
||||
return cacheHelp.loadBeanDirect(id, readOnly, data, context);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1185,8 +1176,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
/**
|
||||
* Return a bean from the bean cache (or null).
|
||||
*/
|
||||
public T cacheBeanGet(SpiQuery<T> query, PersistenceContext context) {
|
||||
return cacheHelp.beanCacheGet(query, context);
|
||||
public T cacheBeanGet(Object id, Boolean readOnly, PersistenceContext context) {
|
||||
return cacheHelp.beanCacheGet(id, readOnly, context);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1199,24 +1190,24 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
/**
|
||||
* Returns true if it managed to populate/load the bean from the cache.
|
||||
*/
|
||||
public boolean cacheBeanLoad(EntityBean bean, EntityBeanIntercept ebi, Object id) {
|
||||
return cacheHelp.beanCacheLoad(bean, ebi, id);
|
||||
public boolean cacheBeanLoad(EntityBean bean, EntityBeanIntercept ebi, Object id, PersistenceContext context) {
|
||||
return cacheHelp.beanCacheLoad(bean, ebi, id, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if it managed to populate/load the bean from the cache.
|
||||
*/
|
||||
public boolean cacheBeanLoad(EntityBeanIntercept ebi) {
|
||||
public boolean cacheBeanLoad(EntityBeanIntercept ebi, PersistenceContext context) {
|
||||
EntityBean bean = ebi.getOwner();
|
||||
Object id = getId(bean);
|
||||
return cacheBeanLoad(bean, ebi, id);
|
||||
return cacheBeanLoad(bean, ebi, id, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to hit the cache using the natural key.
|
||||
*/
|
||||
public T cacheNaturalKeyLookup(SpiQuery<T> query, SpiTransaction t) {
|
||||
return cacheHelp.naturalKeyLookup(query, t);
|
||||
public Object cacheNaturalKeyIdLookup(SpiQuery<T> query) {
|
||||
return cacheHelp.naturalKeyIdLookup(query);
|
||||
}
|
||||
|
||||
public void cacheNaturalKeyPut(Object id, Object newKey) {
|
||||
@@ -1594,7 +1585,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
* Create a reference bean based on the id.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T createReference(Boolean readOnly, Object id) {
|
||||
public T createReference(Boolean readOnly, Object id, PersistenceContext pc) {
|
||||
|
||||
if (cacheSharableBeans && !Boolean.FALSE.equals(readOnly)) {
|
||||
CachedBeanData d = cacheHelp.beanCacheGetData(id);
|
||||
@@ -1610,7 +1601,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
}
|
||||
try {
|
||||
EntityBean eb = createEntityBean();
|
||||
convertSetId(id, eb);
|
||||
id = convertSetId(id, eb);
|
||||
|
||||
EntityBeanIntercept ebi = eb._ebean_getIntercept();
|
||||
ebi.setBeanLoader(ebeanServer);
|
||||
@@ -1618,6 +1609,10 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
if (Boolean.TRUE == readOnly) {
|
||||
ebi.setReadOnly(true);
|
||||
}
|
||||
if (pc != null) {
|
||||
contextPut(pc, id, eb);
|
||||
ebi.setPersistenceContext(pc);
|
||||
}
|
||||
|
||||
return (T) eb;
|
||||
|
||||
@@ -1686,7 +1681,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
return unidirectional;
|
||||
}
|
||||
if (inheritInfo != null && !inheritInfo.isRoot()) {
|
||||
return inheritInfo.getParent().getBeanDescriptor().getUnidirectional();
|
||||
return inheritInfo.getParent().desc().getUnidirectional();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1782,11 +1777,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
* Create a reference bean and put it in the persistence context (and return it).
|
||||
*/
|
||||
public Object contextRef(PersistenceContext pc, Boolean readOnly, Object id) {
|
||||
Object ref = createReference(readOnly, id);
|
||||
if (pc != null) {
|
||||
contextPut(pc, id, ref);
|
||||
}
|
||||
return ref;
|
||||
return createReference(readOnly, id, pc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1935,19 +1926,35 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
if (lazyLoadProperty == -1) {
|
||||
return false;
|
||||
}
|
||||
String lazyLoadPropertyName = ebi.getProperty(lazyLoadProperty);
|
||||
BeanProperty lazyLoadBeanProp = getBeanProperty(lazyLoadPropertyName);
|
||||
|
||||
if (inheritInfo != null) {
|
||||
return descOf(ebi.getOwner().getClass()).lazyLoadMany(ebi, lazyLoadProperty);
|
||||
}
|
||||
return lazyLoadMany(ebi, lazyLoadProperty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for lazy loading of many property.
|
||||
*/
|
||||
private boolean lazyLoadMany(EntityBeanIntercept ebi, int lazyLoadProperty) {
|
||||
|
||||
BeanProperty lazyLoadBeanProp = propertiesIndex[lazyLoadProperty];
|
||||
if (lazyLoadBeanProp instanceof BeanPropertyAssocMany<?>) {
|
||||
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>) lazyLoadBeanProp;
|
||||
manyProp.createReference(ebi.getOwner());
|
||||
ebi.setLoadedLazy();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the correct BeanDescriptor based on the bean class type.
|
||||
*/
|
||||
BeanDescriptor<?> descOf(Class<?> type) {
|
||||
return inheritInfo.readType(type).desc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Comparator for local sorting of lists.
|
||||
*
|
||||
@@ -2166,8 +2173,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T createBeanUsingDisc(Object discValue) {
|
||||
InheritInfo type = inheritInfo.getType(discValue.toString());
|
||||
return (T)type.getBeanDescriptor().createBean();
|
||||
return (T) inheritInfo.getType(discValue.toString()).desc().createBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2325,6 +2331,16 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
return dbComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the dependent tables for a view based entity.
|
||||
* <p>
|
||||
* These tables
|
||||
* </p>
|
||||
*/
|
||||
public String[] getDependentTables() {
|
||||
return dependentTables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base table. Only properties mapped to the base table are by
|
||||
* default persisted.
|
||||
@@ -2333,6 +2349,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
return baseTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this type is a base table entity type.
|
||||
*/
|
||||
public boolean isBaseTable() {
|
||||
return baseTable != null && entityType == EntityType.ORM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base table to use given the query temporal mode.
|
||||
*/
|
||||
|
||||
@@ -7,7 +7,6 @@ import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.cache.ServerCache;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD;
|
||||
import com.avaje.ebeaninternal.server.cache.CacheChangeSet;
|
||||
import com.avaje.ebeaninternal.server.cache.CachedBeanData;
|
||||
@@ -57,9 +56,9 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
private final BeanPropertyAssocOne<?>[] propertiesOneImported;
|
||||
private final String naturalKeyProperty;
|
||||
|
||||
private ServerCache beanCache;
|
||||
private ServerCache naturalKeyCache;
|
||||
private volatile ServerCache queryCache;
|
||||
private final ServerCache beanCache;
|
||||
private final ServerCache naturalKeyCache;
|
||||
private final ServerCache queryCache;
|
||||
|
||||
BeanDescriptorCacheHelp(BeanDescriptor<T> desc, ServerCacheManager cacheManager, CacheOptions cacheOptions,
|
||||
boolean cacheSharableBeans, BeanPropertyAssocOne<?>[] propertiesOneImported) {
|
||||
@@ -72,33 +71,30 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
this.cacheSharableBeans = cacheSharableBeans;
|
||||
this.propertiesOneImported = propertiesOneImported;
|
||||
this.naturalKeyProperty = cacheOptions.getNaturalKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the cache once the server has started.
|
||||
*/
|
||||
public void initialise() {
|
||||
if (cacheOptions.isUseNaturalKeyCache()) {
|
||||
this.naturalKeyCache = cacheManager.getNaturalKeyCache(beanType);
|
||||
}
|
||||
if (cacheOptions.isUseCache()) {
|
||||
this.beanCache = cacheManager.getBeanCache(beanType);
|
||||
}
|
||||
}
|
||||
|
||||
public void setUseCache(boolean useCache) {
|
||||
if (useCache) {
|
||||
getBeanCache();
|
||||
if (!cacheOptions.isEnableQueryCache()) {
|
||||
this.queryCache = null;
|
||||
} else {
|
||||
beanCacheClear();
|
||||
beanCache = null;
|
||||
this.queryCache = cacheManager.getQueryCache(beanType);
|
||||
}
|
||||
|
||||
if (cacheOptions.isEnableBeanCache()) {
|
||||
this.beanCache = cacheManager.getBeanCache(beanType);
|
||||
if (cacheOptions.getNaturalKey() != null) {
|
||||
this.naturalKeyCache = cacheManager.getNaturalKeyCache(beanType);
|
||||
} else {
|
||||
this.naturalKeyCache = null;
|
||||
}
|
||||
} else {
|
||||
this.beanCache = null;
|
||||
this.naturalKeyCache = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is currently query caching for this type of bean.
|
||||
*/
|
||||
private boolean isQueryCaching() {
|
||||
boolean isQueryCaching() {
|
||||
return queryCache != null;
|
||||
}
|
||||
|
||||
@@ -129,17 +125,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
return cacheOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the query cache if required
|
||||
* (as some node in the cluster already has it).
|
||||
*/
|
||||
void queryCacheInit() {
|
||||
if (queryCache == null) {
|
||||
queryLog.debug(" init {}", cacheName);
|
||||
queryCache = cacheManager.getQueryCache(beanType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the query cache.
|
||||
*/
|
||||
@@ -167,18 +152,17 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
@SuppressWarnings("unchecked")
|
||||
BeanCollection<T> queryCacheGet(Object id) {
|
||||
if (queryCache == null) {
|
||||
return null;
|
||||
} else {
|
||||
BeanCollection<T> list = (BeanCollection<T>) queryCache.get(id);
|
||||
if (queryLog.isDebugEnabled()) {
|
||||
if (list == null) {
|
||||
queryLog.debug(" GET {}({}) - cache miss", cacheName, id);
|
||||
} else {
|
||||
queryLog.debug(" GET {}({}) - hit", cacheName, id);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
throw new IllegalStateException("No query cache enabled on " + desc + ". Need explicit @Cache(enableQueryCache=true)");
|
||||
}
|
||||
BeanCollection<T> list = (BeanCollection<T>) queryCache.get(id);
|
||||
if (queryLog.isDebugEnabled()) {
|
||||
if (list == null) {
|
||||
queryLog.debug(" GET {}({}) - cache miss", cacheName, id);
|
||||
} else {
|
||||
queryLog.debug(" GET {}({}) - hit", cacheName, id);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,7 +170,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
*/
|
||||
void queryCachePut(Object id, BeanCollection<T> query) {
|
||||
if (queryCache == null) {
|
||||
queryCache = cacheManager.getQueryCache(beanType);
|
||||
throw new IllegalStateException("No query cache enabled on " + desc + ". Need explicit @Cache(enableQueryCache=true)");
|
||||
}
|
||||
if (queryLog.isDebugEnabled()) {
|
||||
queryLog.debug(" PUT {}({})", cacheName, id);
|
||||
@@ -197,8 +181,8 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
|
||||
void manyPropRemove(String propertyName, Object parentId) {
|
||||
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName);
|
||||
if (manyLog.isDebugEnabled()) {
|
||||
manyLog.debug(" REMOVE {}({}).{}", cacheName, parentId, propertyName);
|
||||
if (manyLog.isTraceEnabled()) {
|
||||
manyLog.trace(" REMOVE {}({}).{}", cacheName, parentId, propertyName);
|
||||
}
|
||||
collectionIdsCache.remove(parentId);
|
||||
}
|
||||
@@ -248,12 +232,8 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
bc.checkEmptyLazyLoad();
|
||||
for (int i = 0; i < idList.size(); i++) {
|
||||
Object id = idList.get(i);
|
||||
Object refBean = targetDescriptor.createReference(readOnly, id);
|
||||
EntityBeanIntercept refEbi = ((EntityBean) refBean)._ebean_getIntercept();
|
||||
|
||||
Object refBean = targetDescriptor.createReference(readOnly, id, persistenceContext);
|
||||
many.add(bc, (EntityBean) refBean);
|
||||
targetDescriptor.contextPut(persistenceContext, id, refBean);
|
||||
refEbi.setPersistenceContext(persistenceContext);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -291,46 +271,26 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
/**
|
||||
* Find the bean using the natural key lookup if available.
|
||||
*/
|
||||
T naturalKeyLookup(SpiQuery<T> query, SpiTransaction t) {
|
||||
|
||||
Object naturalKeyIdLookup(SpiQuery<T> query) {
|
||||
|
||||
if (!isNaturalKeyCaching(query.isUseBeanCache())) {
|
||||
// no natural key caching for this query
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// check if it is a find by unique id (using the natural key)
|
||||
NaturalKeyBindParam keyBindParam = query.getNaturalKeyBindParam();
|
||||
if (keyBindParam == null || !isNaturalKey(keyBindParam.getName())) {
|
||||
// query is not appropriate
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// try to lookup the id using the natural key
|
||||
Object id = naturalKeyCache.get(keyBindParam.getValue());
|
||||
if (natLog.isTraceEnabled()) {
|
||||
if (natLog.isTraceEnabled() && id != null) {
|
||||
natLog.trace(" LOOKUP {}({}) - id:{}", cacheName, keyBindParam.getValue(), id);
|
||||
}
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// try looking up into the bean cache using the id
|
||||
T cacheBean = beanCacheGetInternal(id, query.isReadOnly());
|
||||
if (cacheBean != null) {
|
||||
setupContext(cacheBean, getPersistenceContext(t));
|
||||
}
|
||||
return cacheBean;
|
||||
}
|
||||
|
||||
private PersistenceContext getPersistenceContext(SpiTransaction t) {
|
||||
PersistenceContext context = null;
|
||||
if (t == null) {
|
||||
t = desc.getEbeanServer().getCurrentServerTransaction();
|
||||
}
|
||||
if (t != null) {
|
||||
context = t.getPersistenceContext();
|
||||
}
|
||||
return context;
|
||||
return id;
|
||||
}
|
||||
|
||||
private boolean isNaturalKeyCaching(Boolean queryUseCache) {
|
||||
@@ -363,7 +323,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
*/
|
||||
private ServerCache getBeanCache() {
|
||||
if (beanCache == null) {
|
||||
beanCache = cacheManager.getBeanCache(beanType);
|
||||
throw new IllegalStateException("No bean cache enabled for " + desc + ". Add the @Cache annotation.");
|
||||
}
|
||||
return beanCache;
|
||||
}
|
||||
@@ -390,7 +350,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
void beanCachePut(EntityBean bean) {
|
||||
|
||||
if (desc.inheritInfo != null) {
|
||||
desc.inheritInfo.readType(bean.getClass()).getBeanDescriptor().cacheBeanPutDirect(bean);
|
||||
desc.descOf(bean.getClass()).cacheBeanPutDirect(bean);
|
||||
} else {
|
||||
beanCachePutDirect(bean);
|
||||
}
|
||||
@@ -424,9 +384,8 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
return (CachedBeanData) getBeanCache().get(id);
|
||||
}
|
||||
|
||||
T beanCacheGet(SpiQuery<T> query, PersistenceContext context) {
|
||||
Object id = desc.convertId(query.getId());
|
||||
T bean = beanCacheGetInternal(id, query.isReadOnly());
|
||||
T beanCacheGet(Object id, Boolean readOnly, PersistenceContext context) {
|
||||
T bean = beanCacheGetInternal(id, readOnly, context);
|
||||
if (bean != null) {
|
||||
setupContext(bean, context);
|
||||
}
|
||||
@@ -437,7 +396,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Return a bean from the bean cache.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private T beanCacheGetInternal(Object id, Boolean readOnly) {
|
||||
private T beanCacheGetInternal(Object id, Boolean readOnly, PersistenceContext context) {
|
||||
|
||||
CachedBeanData data = (CachedBeanData) getBeanCache().get(id);
|
||||
if (data == null) {
|
||||
@@ -459,19 +418,19 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
}
|
||||
|
||||
return (T)loadBean(id, readOnly, data);
|
||||
return (T)loadBean(id, readOnly, data, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the entity bean taking into account inheritance.
|
||||
*/
|
||||
private EntityBean loadBean(Object id, Boolean readOnly, CachedBeanData data) {
|
||||
private EntityBean loadBean(Object id, Boolean readOnly, CachedBeanData data, PersistenceContext context) {
|
||||
|
||||
String discValue = data.getDiscValue();
|
||||
if (discValue == null) {
|
||||
return loadBeanDirect(id, readOnly, data);
|
||||
return loadBeanDirect(id, readOnly, data, context);
|
||||
} else {
|
||||
return rootDescriptor(discValue).cacheBeanLoadDirect(id, readOnly, data);
|
||||
return rootDescriptor(discValue).cacheBeanLoadDirect(id, readOnly, data, context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,24 +438,32 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Return the root BeanDescriptor for inheritance.
|
||||
*/
|
||||
private BeanDescriptor<?> rootDescriptor(String discValue) {
|
||||
InheritInfo inheritInfo = desc.inheritInfo.readType(discValue);
|
||||
return inheritInfo.getBeanDescriptor();
|
||||
return desc.inheritInfo.readType(discValue).desc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the entity bean from cache data given this is the root bean type.
|
||||
*/
|
||||
EntityBean loadBeanDirect(Object id, Boolean readOnly, CachedBeanData data) {
|
||||
EntityBean loadBeanDirect(Object id, Boolean readOnly, CachedBeanData data, PersistenceContext context) {
|
||||
|
||||
if (context == null) {
|
||||
context = new DefaultPersistenceContext();
|
||||
}
|
||||
|
||||
EntityBean bean = desc.createEntityBean();
|
||||
desc.convertSetId(id, bean);
|
||||
CachedBeanDataToBean.load(desc, bean, data);
|
||||
id = desc.convertSetId(id, bean);
|
||||
CachedBeanDataToBean.load(desc, bean, data, context);
|
||||
|
||||
EntityBeanIntercept ebi = bean._ebean_getIntercept();
|
||||
|
||||
// Not using a loadContext for beans coming out of L2 cache
|
||||
// so that means no batch lazy loading for these beans
|
||||
ebi.setBeanLoader(desc.getEbeanServer());
|
||||
if (Boolean.TRUE.equals(readOnly)) {
|
||||
ebi.setReadOnly(true);
|
||||
}
|
||||
ebi.setPersistenceContext(context);
|
||||
desc.contextPut(context, id, bean);
|
||||
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" GET {}({}) - hit", cacheName, id);
|
||||
@@ -510,22 +477,22 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
/**
|
||||
* Load the embedded bean checking for inheritance.
|
||||
*/
|
||||
EntityBean embeddedBeanLoad(CachedBeanData data) {
|
||||
EntityBean embeddedBeanLoad(CachedBeanData data, PersistenceContext context) {
|
||||
|
||||
String discValue = data.getDiscValue();
|
||||
if (discValue == null) {
|
||||
return embeddedBeanLoadDirect(data);
|
||||
return embeddedBeanLoadDirect(data, context);
|
||||
} else {
|
||||
return rootDescriptor(discValue).cacheEmbeddedBeanLoadDirect(data);
|
||||
return rootDescriptor(discValue).cacheEmbeddedBeanLoadDirect(data, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the embedded bean given this is the bean type.
|
||||
*/
|
||||
EntityBean embeddedBeanLoadDirect(CachedBeanData data) {
|
||||
EntityBean embeddedBeanLoadDirect(CachedBeanData data, PersistenceContext context) {
|
||||
EntityBean bean = desc.createEntityBean();
|
||||
CachedBeanDataToBean.load(desc, bean, data);
|
||||
CachedBeanDataToBean.load(desc, bean, data, context);
|
||||
return bean;
|
||||
}
|
||||
|
||||
@@ -547,7 +514,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
/**
|
||||
* Returns true if it managed to populate/load the bean from the cache.
|
||||
*/
|
||||
boolean beanCacheLoad(EntityBean bean, EntityBeanIntercept ebi, Object id) {
|
||||
boolean beanCacheLoad(EntityBean bean, EntityBeanIntercept ebi, Object id, PersistenceContext context) {
|
||||
|
||||
CachedBeanData cacheData = (CachedBeanData) getBeanCache().get(id);
|
||||
if (cacheData == null) {
|
||||
@@ -559,12 +526,12 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
int lazyLoadProperty = ebi.getLazyLoadPropertyIndex();
|
||||
if (lazyLoadProperty > -1 && !cacheData.isLoaded(ebi.getLazyLoadProperty())) {
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" LOAD {}({}) - cache miss on property", cacheName, id);
|
||||
beanLog.trace(" LOAD {}({}) - cache miss on property({})", cacheName, id, ebi.getLazyLoadProperty());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CachedBeanDataToBean.load(desc, bean, cacheData);
|
||||
CachedBeanDataToBean.load(desc, bean, cacheData, context);
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" LOAD {}({}) - hit", cacheName, id);
|
||||
}
|
||||
@@ -598,6 +565,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
void handleInsert(PersistRequestBean<T> insertRequest, CacheChangeSet changeSet) {
|
||||
queryCacheClear(changeSet);
|
||||
cacheDeleteImported(false, insertRequest.getEntityBean(), changeSet);
|
||||
changeSet.addBeanInsert(desc.getBaseTable());
|
||||
}
|
||||
|
||||
private void cacheDeleteImported(boolean clear, EntityBean entityBean, CacheChangeSet changeSet) {
|
||||
@@ -649,10 +617,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCachedDataTooOld(CachedBeanData existingData) {
|
||||
return cacheOptions.isTooOldInMillis(System.currentTimeMillis() - existingData.getWhenCreated());
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate parts of cache due to SqlUpdate or external modification etc.
|
||||
*/
|
||||
@@ -679,28 +643,21 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
ServerCache cache = getBeanCache();
|
||||
CachedBeanData existingData = (CachedBeanData) cache.get(id);
|
||||
if (existingData != null) {
|
||||
if (isCachedDataTooOld(existingData)) {
|
||||
long currentVersion = existingData.getVersion();
|
||||
if (version > 0 && version < currentVersion) {
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" REMOVE {}({}) - entry too old", cacheName, id);
|
||||
beanLog.debug(" REMOVE {}({}) - version conflict old:{} new:{}", cacheName, id, currentVersion, version);
|
||||
}
|
||||
cache.remove(id);
|
||||
} else {
|
||||
long currentVersion = existingData.getVersion();
|
||||
if (version > 0 && version < currentVersion) {
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" REMOVE {}({}) - version conflict old:{} new:{}", cacheName, id, currentVersion, version);
|
||||
}
|
||||
cache.remove(id);
|
||||
} else {
|
||||
if (version == 0) {
|
||||
version = currentVersion;
|
||||
}
|
||||
CachedBeanData newData = existingData.update(changes, version);
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" UPDATE {}({}) changes:{}", cacheName, id, changes);
|
||||
}
|
||||
cache.put(id, newData);
|
||||
if (version == 0) {
|
||||
version = currentVersion;
|
||||
}
|
||||
CachedBeanData newData = existingData.update(changes, version);
|
||||
if (beanLog.isDebugEnabled()) {
|
||||
beanLog.debug(" UPDATE {}({}) changes:{}", cacheName, id, changes);
|
||||
}
|
||||
cache.put(id, newData);
|
||||
}
|
||||
|
||||
if (updateNaturalKey) {
|
||||
|
||||
@@ -37,8 +37,7 @@ public class BeanDescriptorJsonHelp<T> {
|
||||
String discColumn = localInheritInfo.getDiscriminatorColumn();
|
||||
writeJson.gen().writeStringField(discColumn, discValue);
|
||||
|
||||
BeanDescriptor<?> localDescriptor = localInheritInfo.getBeanDescriptor();
|
||||
localDescriptor.jsonWriteProperties(writeJson, bean);
|
||||
localInheritInfo.desc().jsonWriteProperties(writeJson, bean);
|
||||
}
|
||||
|
||||
writeJson.writeEndObject();
|
||||
@@ -54,13 +53,9 @@ public class BeanDescriptorJsonHelp<T> {
|
||||
|
||||
if (inheritInfo == null) {
|
||||
jsonWriteDirtyProperties(writeJson, bean, dirtyProps);
|
||||
|
||||
} else {
|
||||
InheritInfo localInheritInfo = inheritInfo.readType(bean.getClass());
|
||||
BeanDescriptor<?> localDescriptor = localInheritInfo.getBeanDescriptor();
|
||||
localDescriptor.jsonWriteDirtyProperties(writeJson, bean, dirtyProps);
|
||||
desc.descOf(bean.getClass()).jsonWriteDirtyProperties(writeJson, bean, dirtyProps);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void jsonWriteDirtyProperties(WriteJson writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
|
||||
@@ -120,11 +115,7 @@ public class BeanDescriptorJsonHelp<T> {
|
||||
}
|
||||
|
||||
String discValue = parser.nextTextValue();
|
||||
|
||||
// determine the sub type for this particular json object
|
||||
InheritInfo localInheritInfo = inheritInfo.readType(discValue);
|
||||
BeanDescriptor<?> localDescriptor = localInheritInfo.getBeanDescriptor();
|
||||
return (T) localDescriptor.jsonReadObject(jsonRead, path);
|
||||
return (T) inheritInfo.readType(discValue).desc().jsonReadObject(jsonRead, path);
|
||||
}
|
||||
|
||||
protected T jsonReadObject(ReadJson readJson, String path) throws IOException {
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.Model;
|
||||
import com.avaje.ebean.RawSql;
|
||||
import com.avaje.ebean.RawSqlBuilder;
|
||||
import com.avaje.ebean.annotation.ConcurrencyMode;
|
||||
import com.avaje.ebeaninternal.api.ConcurrencyMode;
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
@@ -124,7 +124,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
private final DocStoreFactory docStoreFactory;
|
||||
|
||||
private int enhancedClassCount;
|
||||
|
||||
|
||||
private final boolean updateChangesOnly;
|
||||
|
||||
private final BootupClasses bootupClasses;
|
||||
@@ -143,6 +143,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
private final Map<String, List<BeanDescriptor<?>>> tableToDescMap = new HashMap<String, List<BeanDescriptor<?>>>();
|
||||
|
||||
private final Map<String, List<BeanDescriptor<?>>> tableToViewDescMap = new HashMap<String, List<BeanDescriptor<?>>>();
|
||||
|
||||
private List<BeanDescriptor<?>> immutableDescriptorList;
|
||||
|
||||
private final DbIdentity dbIdentity;
|
||||
@@ -174,12 +176,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
/**
|
||||
* Map of base tables to 'with history views' used to support 'as of' queries.
|
||||
*/
|
||||
private final Map<String,String> asOfTableMap = new HashMap<String, String>();
|
||||
private final Map<String, String> asOfTableMap = new HashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Map of base tables to 'draft' tables.
|
||||
*/
|
||||
private final Map<String,String> draftTableMap = new HashMap<String, String>();
|
||||
private final Map<String, String> draftTableMap = new HashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Create for a given database dbConfig.
|
||||
@@ -235,7 +237,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
DbHistorySupport historySupport = databasePlatform.getHistorySupport();
|
||||
// with historySupport returns a simple view suffix or the sql2011 as of timestamp suffix
|
||||
return (historySupport == null ) ? serverConfig.getAsOfViewSuffix() : historySupport.getAsOfViewSuffix(serverConfig.getAsOfViewSuffix());
|
||||
return (historySupport == null) ? serverConfig.getAsOfViewSuffix() : historySupport.getAsOfViewSuffix(serverConfig.getAsOfViewSuffix());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,7 +247,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
DbHistorySupport historySupport = databasePlatform.getHistorySupport();
|
||||
// with historySupport returns a simple view suffix or the sql2011 versions between timestamp suffix
|
||||
return (historySupport == null ) ? serverConfig.getAsOfViewSuffix() : historySupport.getVersionsBetweenSuffix(serverConfig.getAsOfViewSuffix());
|
||||
return (historySupport == null) ? serverConfig.getAsOfViewSuffix() : historySupport.getVersionsBetweenSuffix(serverConfig.getAsOfViewSuffix());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -300,14 +302,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
/**
|
||||
* Return the map of base tables to draft tables.
|
||||
*/
|
||||
public Map<String,String> getDraftTableMap() {
|
||||
public Map<String, String> getDraftTableMap() {
|
||||
return draftTableMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy returning the asOfTableMap (which is required by the SQL builders).
|
||||
*/
|
||||
public Map<String,String> deploy() {
|
||||
public Map<String, String> deploy() {
|
||||
|
||||
try {
|
||||
createListeners();
|
||||
@@ -356,10 +358,19 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
*/
|
||||
public void cacheNotify(TransactionEventTable.TableIUD tableIUD) {
|
||||
|
||||
List<BeanDescriptor<?>> list = getBeanDescriptors(tableIUD.getTableName());
|
||||
if (list != null) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
list.get(i).cacheHandleBulkUpdate(tableIUD);
|
||||
String tableName = tableIUD.getTableName().toLowerCase();
|
||||
List<BeanDescriptor<?>> normalBeanTypes = tableToDescMap.get(tableName);
|
||||
if (normalBeanTypes != null) {
|
||||
// 'normal' entity beans based on a "base table"
|
||||
for (int i = 0; i < normalBeanTypes.size(); i++) {
|
||||
normalBeanTypes.get(i).cacheHandleBulkUpdate(tableIUD);
|
||||
}
|
||||
}
|
||||
List<BeanDescriptor<?>> viewBeans = tableToViewDescMap.get(tableName);
|
||||
if (viewBeans != null) {
|
||||
// entity beans based on a "view"
|
||||
for (int i = 0; i < viewBeans.size(); i++) {
|
||||
viewBeans.get(i).cacheHandleBulkUpdate(tableIUD);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -378,6 +389,21 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
return tableToDescMap.get(tableName.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate entity beans based on views via their dependent tables.
|
||||
*/
|
||||
public void processViewInvalidation(Set<String> viewInvalidation) {
|
||||
|
||||
for (String depTable : viewInvalidation) {
|
||||
List<BeanDescriptor<?>> list = tableToViewDescMap.get(depTable.toLowerCase());
|
||||
if (list == null) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
list.get(i).queryCacheClear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a map of table names to BeanDescriptors.
|
||||
* <p>
|
||||
@@ -390,7 +416,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
String baseTable = desc.getBaseTable();
|
||||
if (baseTable != null) {
|
||||
baseTable = baseTable.toLowerCase();
|
||||
|
||||
List<BeanDescriptor<?>> list = tableToDescMap.get(baseTable);
|
||||
if (list == null) {
|
||||
list = new ArrayList<BeanDescriptor<?>>(1);
|
||||
@@ -398,6 +423,22 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
list.add(desc);
|
||||
}
|
||||
if (desc.getEntityType() == EntityType.VIEW && desc.isQueryCaching()) {
|
||||
// build map of tables to view entities dependent on those tables
|
||||
// for the purpose of invalidating appropriate query caches
|
||||
String[] dependentTables = desc.getDependentTables();
|
||||
if (dependentTables != null && dependentTables.length > 0) {
|
||||
for (String depTable : dependentTables) {
|
||||
depTable = depTable.toLowerCase();
|
||||
List<BeanDescriptor<?>> list = tableToViewDescMap.get(depTable);
|
||||
if (list == null) {
|
||||
list = new ArrayList<BeanDescriptor<?>>(1);
|
||||
tableToViewDescMap.put(depTable, list);
|
||||
}
|
||||
list.add(desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,6 +528,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
throw new PersistenceException(msg, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are 'view based entities' using l2 query caching and so need
|
||||
* to be invalidated based on changes to dependent tables.
|
||||
*/
|
||||
public boolean requiresViewEntityCacheInvalidation() {
|
||||
return !tableToViewDescMap.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an immutable list of all the BeanDescriptors.
|
||||
*/
|
||||
@@ -539,7 +588,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* Return the bean deploy info for the given class.
|
||||
*/
|
||||
public <T> DeployBeanInfo<T> getDeploy(Class<T> cls) {
|
||||
return (DeployBeanInfo<T>) deplyInfoMap.get(cls);
|
||||
return (DeployBeanInfo<T>) deplyInfoMap.get(cls);
|
||||
}
|
||||
|
||||
private void registerBeanDescriptor(BeanDescriptor<?> desc) {
|
||||
@@ -651,7 +700,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void readEntityRelationships() {
|
||||
|
||||
// We only perform 'circular' checks etc after we have
|
||||
@@ -667,9 +716,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
// Set inheritance info
|
||||
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
|
||||
setInheritanceInfo(info);
|
||||
setInheritanceInfo(info);
|
||||
}
|
||||
|
||||
|
||||
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
|
||||
registerBeanDescriptor(new BeanDescriptor(this, info.getDescriptor()));
|
||||
}
|
||||
@@ -681,24 +730,24 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* @param info the new inheritance info
|
||||
*/
|
||||
private void setInheritanceInfo(DeployBeanInfo<?> info) {
|
||||
|
||||
for (DeployBeanPropertyAssocOne<?> oneProp : info.getDescriptor().propertiesAssocOne()) {
|
||||
if (!oneProp.isTransient()) {
|
||||
DeployBeanInfo<?> assoc = deplyInfoMap.get(oneProp.getTargetType());
|
||||
if (assoc != null){
|
||||
oneProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (DeployBeanPropertyAssocMany<?> manyProp : info.getDescriptor().propertiesAssocMany()) {
|
||||
if (!manyProp.isTransient()) {
|
||||
DeployBeanInfo<?> assoc = deplyInfoMap.get(manyProp.getTargetType());
|
||||
if (assoc != null){
|
||||
manyProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (DeployBeanPropertyAssocOne<?> oneProp : info.getDescriptor().propertiesAssocOne()) {
|
||||
if (!oneProp.isTransient()) {
|
||||
DeployBeanInfo<?> assoc = deplyInfoMap.get(oneProp.getTargetType());
|
||||
if (assoc != null) {
|
||||
oneProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (DeployBeanPropertyAssocMany<?> manyProp : info.getDescriptor().propertiesAssocMany()) {
|
||||
if (!manyProp.isTransient()) {
|
||||
DeployBeanInfo<?> assoc = deplyInfoMap.get(manyProp.getTargetType());
|
||||
if (assoc != null) {
|
||||
manyProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void secondaryPropsJoins(DeployBeanInfo<?> info) {
|
||||
@@ -845,7 +894,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* into the order_id column on the order_lines table).
|
||||
* </p>
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void makeUnidirectional(DeployBeanInfo<?> info, DeployBeanPropertyAssocMany<?> oneToMany) {
|
||||
|
||||
DeployBeanDescriptor<?> targetDesc = getTargetDescriptor(oneToMany);
|
||||
@@ -1081,7 +1130,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
desc.setUpdateChangesOnly(updateChangesOnly);
|
||||
|
||||
beanLifecycleAdapterFactory.addLifecycleMethods(desc);
|
||||
|
||||
|
||||
// set bean controller, finder and listener
|
||||
setBeanControllerFinderListener(desc);
|
||||
deplyInherit.process(desc);
|
||||
@@ -1103,8 +1152,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
readXml(desc);
|
||||
|
||||
if (!EntityType.ORM.equals(desc.getEntityType())) {
|
||||
// not using base table
|
||||
if (EntityType.SQL == desc.getEntityType()) {
|
||||
desc.setBaseTable(null, null, null);
|
||||
}
|
||||
|
||||
@@ -1373,7 +1421,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
Class<?> beanType = desc.getBeanType();
|
||||
|
||||
BeanPropertiesReader reflectProps = new BeanPropertiesReader(beanType);
|
||||
|
||||
|
||||
BeanPropertyInfo beanReflect = reflectFactory.create(beanType);
|
||||
desc.setProperties(reflectProps.getProperties());
|
||||
|
||||
@@ -1384,7 +1432,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
if (isPersistentField(prop)) {
|
||||
throw new IllegalStateException("Property " + propName + " not found in " + reflectProps + " for type " + beanType);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
final int propertyIndex = pos;
|
||||
prop.setPropertyIndex(propertyIndex);
|
||||
@@ -1459,7 +1507,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
Class<?> beanClass = desc.getBeanType();
|
||||
|
||||
if (!hasEntityBeanInterface(beanClass)) {
|
||||
throw new IllegalStateException("Bean "+beanClass+" is not enhanced?");
|
||||
throw new IllegalStateException("Bean " + beanClass + " is not enhanced?");
|
||||
}
|
||||
|
||||
// the bean already implements EntityBean
|
||||
@@ -1475,7 +1523,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* enhanced or all dynamically subclassed).
|
||||
*/
|
||||
private void checkInheritedClasses(Class<?> beanClass) {
|
||||
|
||||
|
||||
Class<?> superclass = beanClass.getSuperclass();
|
||||
if (Object.class.equals(superclass)) {
|
||||
// we got to the top of the inheritance
|
||||
@@ -1490,9 +1538,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
// ok to stop and treat just the same as Object.class
|
||||
return;
|
||||
}
|
||||
throw new IllegalStateException("Super type "+superclass+" is not enhanced?");
|
||||
throw new IllegalStateException("Super type " + superclass + " is not enhanced?");
|
||||
}
|
||||
|
||||
|
||||
// recursively continue up the inheritance hierarchy
|
||||
checkInheritedClasses(superclass);
|
||||
}
|
||||
@@ -1502,7 +1550,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* If so it is ok for it not to be enhanced.
|
||||
*/
|
||||
private boolean isMappedSuperWithNoProperties(Class<?> beanClass) {
|
||||
|
||||
|
||||
MappedSuperclass annotation = beanClass.getAnnotation(MappedSuperclass.class);
|
||||
if (annotation == null) {
|
||||
return false;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebean.ValuePair;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.config.EncryptKey;
|
||||
import com.avaje.ebean.config.dbplatform.DbEncryptFunction;
|
||||
import com.avaje.ebean.config.dbplatform.DbType;
|
||||
@@ -764,7 +765,7 @@ public class BeanProperty implements ElPropertyValue, Property {
|
||||
* This uses parse() as per the comment in getCacheDataValue().
|
||||
* </p>
|
||||
*/
|
||||
public void setCacheDataValue(EntityBean bean, Object cacheData) {
|
||||
public void setCacheDataValue(EntityBean bean, Object cacheData, PersistenceContext context) {
|
||||
if (cacheData instanceof String) {
|
||||
// parse back from string to support optimisation of java object serialisation
|
||||
cacheData = scalarType.parse((String)cacheData);
|
||||
|
||||
@@ -1022,7 +1022,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
if (isManyToMany()) {
|
||||
if (liveBean == null) {
|
||||
// add new relationship (Map not allowed here)
|
||||
liveVal.addBean(targetDescriptor.createReference(Boolean.FALSE, id));
|
||||
liveVal.addBean(targetDescriptor.createReference(Boolean.FALSE, id, null));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.avaje.ebean.SqlUpdate;
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.ValuePair;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.cache.CacheChangeSet;
|
||||
import com.avaje.ebeaninternal.server.cache.CachedBeanData;
|
||||
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
@@ -399,17 +400,22 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCacheDataValue(EntityBean bean, Object cacheData) {
|
||||
public void setCacheDataValue(EntityBean bean, Object cacheData, PersistenceContext context) {
|
||||
if (cacheData == null) {
|
||||
setValue(bean, null);
|
||||
} else {
|
||||
if (embedded) {
|
||||
setValue(bean, targetDescriptor.cacheEmbeddedBeanLoad((CachedBeanData) cacheData));
|
||||
setValue(bean, targetDescriptor.cacheEmbeddedBeanLoad((CachedBeanData) cacheData, context));
|
||||
} else {
|
||||
if (cacheData instanceof String) {
|
||||
cacheData = targetDescriptor.getIdProperty().scalarType.parse((String)cacheData);
|
||||
}
|
||||
setValue(bean, targetDescriptor.createReference(Boolean.FALSE, cacheData));
|
||||
// cacheData is the id value, maybe already in persistence context
|
||||
Object assocBean = targetDescriptor.contextGet(context, cacheData);
|
||||
if (assocBean == null) {
|
||||
assocBean = targetDescriptor.createReference(Boolean.FALSE, cacheData, context);
|
||||
}
|
||||
setValue(bean, assocBean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ public class InheritInfo {
|
||||
/**
|
||||
* Return the associated BeanDescriptor for this node.
|
||||
*/
|
||||
public BeanDescriptor<?> getBeanDescriptor() {
|
||||
public BeanDescriptor<?> desc() {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@@ -161,10 +161,8 @@ public class InheritInfo {
|
||||
|
||||
for (int i = 0, x = children.size(); i < x; i++) {
|
||||
InheritInfo childInfo = children.get(i);
|
||||
|
||||
// recursively search this child bean descriptor
|
||||
prop = childInfo.getBeanDescriptor().findBeanProperty(propertyName);
|
||||
|
||||
prop = childInfo.desc().findBeanProperty(propertyName);
|
||||
if (prop != null) {
|
||||
return prop;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import com.avaje.ebean.annotation.ConcurrencyMode;
|
||||
import com.avaje.ebean.annotation.Cache;
|
||||
import com.avaje.ebeaninternal.api.ConcurrencyMode;
|
||||
import com.avaje.ebean.annotation.DocStore;
|
||||
import com.avaje.ebean.annotation.DocStoreMode;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
@@ -129,6 +130,8 @@ public class DeployBeanDescriptor<T> {
|
||||
|
||||
private String draftTable;
|
||||
|
||||
private String[] dependentTables;
|
||||
|
||||
private boolean historySupport;
|
||||
|
||||
private boolean readAuditing;
|
||||
@@ -151,7 +154,7 @@ public class DeployBeanDescriptor<T> {
|
||||
private final List<BeanQueryAdapter> queryAdapters = new ArrayList<BeanQueryAdapter>();
|
||||
private final List<BeanPostLoad> postLoaders = new ArrayList<BeanPostLoad>();
|
||||
|
||||
private final CacheOptions cacheOptions = new CacheOptions();
|
||||
private CacheOptions cacheOptions = CacheOptions.NO_CACHING;
|
||||
|
||||
/**
|
||||
* If set overrides the find implementation. Server side only.
|
||||
@@ -440,6 +443,24 @@ public class DeployBeanDescriptor<T> {
|
||||
this.inheritInfo = inheritInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable L2 bean and query caching based on Cache annotation.
|
||||
*/
|
||||
public void setCache(Cache cache) {
|
||||
|
||||
String naturalKey = null;
|
||||
if (cache.naturalKey().length() > 0) {
|
||||
// find the property and mark as natural key property
|
||||
String propName = cache.naturalKey().trim();
|
||||
DeployBeanProperty beanProperty = getBeanProperty(propName);
|
||||
if (beanProperty != null) {
|
||||
beanProperty.setNaturalKey();
|
||||
naturalKey = propName;
|
||||
}
|
||||
}
|
||||
this.cacheOptions = new CacheOptions(cache, naturalKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cache options.
|
||||
*/
|
||||
@@ -582,6 +603,13 @@ public class DeployBeanDescriptor<T> {
|
||||
return draftTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* For view based entity return the dependant tables.
|
||||
*/
|
||||
public String[] getDependentTables() {
|
||||
return dependentTables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base table. Only properties mapped to the base table are by
|
||||
* default persisted.
|
||||
@@ -611,6 +639,15 @@ public class DeployBeanDescriptor<T> {
|
||||
return baseTableFull;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set when entity is based on a view.
|
||||
*/
|
||||
public void setView(String viewName, String[] dependentTables) {
|
||||
this.entityType = EntityType.VIEW;
|
||||
this.dependentTables = this.dependentTables;
|
||||
setBaseTable(new TableName(viewName), "", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the base table. Only properties mapped to the base table are by default persisted.
|
||||
*/
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import com.avaje.ebean.config.TableName;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable;
|
||||
|
||||
/**
|
||||
* Read the annotations for BeanTable.
|
||||
* <p>
|
||||
* Refer to BeanTable but basically determining base table, table alias
|
||||
* and the unique id properties.
|
||||
* </p>
|
||||
*/
|
||||
public class AnnotationBeanTable extends AnnotationBase {
|
||||
|
||||
final DeployBeanTable beanTable;
|
||||
|
||||
public AnnotationBeanTable(DeployUtil util, DeployBeanTable beanTable){
|
||||
super(util);
|
||||
this.beanTable = beanTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the annotations.
|
||||
*/
|
||||
public void parse() {
|
||||
|
||||
TableName tableName = namingConvention.getTableName(beanTable.getBeanType());
|
||||
|
||||
beanTable.setBaseTable(tableName.getQualifiedName());
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,26 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import com.avaje.ebean.annotation.Cache;
|
||||
import com.avaje.ebean.annotation.DbComment;
|
||||
import com.avaje.ebean.annotation.DocStore;
|
||||
import com.avaje.ebean.annotation.Draftable;
|
||||
import com.avaje.ebean.annotation.DraftableElement;
|
||||
import com.avaje.ebean.annotation.History;
|
||||
import com.avaje.ebean.annotation.Index;
|
||||
import com.avaje.ebean.annotation.NamedUpdate;
|
||||
import com.avaje.ebean.annotation.NamedUpdates;
|
||||
import com.avaje.ebean.annotation.ReadAudit;
|
||||
import com.avaje.ebean.annotation.UpdateMode;
|
||||
import com.avaje.ebean.annotation.View;
|
||||
import com.avaje.ebean.config.TableName;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
|
||||
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueConstraint;
|
||||
import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.AttributeOverride;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
@@ -9,29 +30,6 @@ import javax.persistence.NamedQuery;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import com.avaje.ebean.annotation.CacheStrategy;
|
||||
import com.avaje.ebean.annotation.CacheTuning;
|
||||
import com.avaje.ebean.annotation.DocStore;
|
||||
import com.avaje.ebean.annotation.DbComment;
|
||||
import com.avaje.ebean.annotation.Draftable;
|
||||
import com.avaje.ebean.annotation.DraftableElement;
|
||||
import com.avaje.ebean.annotation.EntityConcurrencyMode;
|
||||
import com.avaje.ebean.annotation.History;
|
||||
import com.avaje.ebean.annotation.Index;
|
||||
import com.avaje.ebean.annotation.NamedUpdate;
|
||||
import com.avaje.ebean.annotation.NamedUpdates;
|
||||
import com.avaje.ebean.annotation.ReadAudit;
|
||||
import com.avaje.ebean.annotation.UpdateMode;
|
||||
import com.avaje.ebean.config.TableName;
|
||||
import com.avaje.ebeaninternal.server.core.CacheOptions;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
|
||||
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueConstraint;
|
||||
import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Read the class level deployment annotations.
|
||||
*/
|
||||
@@ -136,13 +134,15 @@ public class AnnotationClass extends AnnotationParser {
|
||||
descriptor.addCompoundUniqueConstraint(new CompoundUniqueConstraint(uc.columnNames()));
|
||||
}
|
||||
|
||||
View view = cls.getAnnotation(View.class);
|
||||
if (view != null) {
|
||||
descriptor.setView(view.name(), view.dependentTables());
|
||||
}
|
||||
Table table = cls.getAnnotation(Table.class);
|
||||
if (table != null) {
|
||||
UniqueConstraint[] uniqueConstraints = table.uniqueConstraints();
|
||||
if (uniqueConstraints != null) {
|
||||
for (UniqueConstraint c : uniqueConstraints) {
|
||||
descriptor.addCompoundUniqueConstraint(new CompoundUniqueConstraint(c.columnNames()));
|
||||
}
|
||||
for (UniqueConstraint c : uniqueConstraints) {
|
||||
descriptor.addCompoundUniqueConstraint(new CompoundUniqueConstraint(c.columnNames()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,39 +200,9 @@ public class AnnotationClass extends AnnotationParser {
|
||||
readNamedUpdate(namedUpdate);
|
||||
}
|
||||
|
||||
CacheStrategy cacheStrategy = cls.getAnnotation(CacheStrategy.class);
|
||||
CacheTuning cacheTuning = cls.getAnnotation(CacheTuning.class);
|
||||
if (cacheStrategy != null || cacheTuning != null) {
|
||||
readCacheStrategy(cacheStrategy, cacheTuning);
|
||||
}
|
||||
|
||||
EntityConcurrencyMode entityConcurrencyMode = cls.getAnnotation(EntityConcurrencyMode.class);
|
||||
if (entityConcurrencyMode != null) {
|
||||
descriptor.setConcurrencyMode(entityConcurrencyMode.value());
|
||||
}
|
||||
}
|
||||
|
||||
private void readCacheStrategy(CacheStrategy cacheStrategy, CacheTuning cacheTuning) {
|
||||
|
||||
if (disableL2Cache) {
|
||||
return;
|
||||
}
|
||||
CacheOptions cacheOptions = descriptor.getCacheOptions();
|
||||
if (cacheTuning != null) {
|
||||
cacheOptions.setMaxSecsToLive(cacheTuning.maxSecsToLive());
|
||||
cacheOptions.setMaxIdleSecs(cacheTuning.maxIdleSecs());
|
||||
}
|
||||
if (cacheStrategy != null) {
|
||||
cacheOptions.setUseCache(cacheStrategy.useBeanCache());
|
||||
cacheOptions.setReadOnly(cacheStrategy.readOnly());
|
||||
if (cacheStrategy.naturalKey().length() > 0) {
|
||||
String propName = cacheStrategy.naturalKey().trim();
|
||||
DeployBeanProperty beanProperty = descriptor.getBeanProperty(propName);
|
||||
if (beanProperty != null) {
|
||||
beanProperty.setNaturalKey();
|
||||
cacheOptions.setNaturalKey(propName);
|
||||
}
|
||||
}
|
||||
Cache cache = cls.getAnnotation(Cache.class);
|
||||
if (cache != null && !disableL2Cache) {
|
||||
descriptor.setCache(cache);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.hitCache && context.desc.cacheBeanLoad(ebi)) {
|
||||
if (context.hitCache && context.desc.cacheBeanLoad(ebi, persistenceContext)) {
|
||||
// successfully hit the L2 cache so don't invoke DB lazy loading
|
||||
list.remove(ebi);
|
||||
return;
|
||||
@@ -185,7 +185,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
|
||||
// check each of the beans in the batch to see if they are in the L2 cache.
|
||||
Iterator<EntityBeanIntercept> iterator = list.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
if (context.desc.cacheBeanLoad(iterator.next())) {
|
||||
if (context.desc.cacheBeanLoad(iterator.next(), persistenceContext)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1352,7 +1352,7 @@ public final class DefaultPersister implements Persister {
|
||||
// convert into a list of reference objects and perform delete by object
|
||||
List<Object> refList = new ArrayList<Object>(childIds.size());
|
||||
for (Object id : childIds) {
|
||||
refList.add(targetDesc.createReference(null, id));
|
||||
refList.add(targetDesc.createReference(null, id, null));
|
||||
}
|
||||
deleteList(refList, t, softDelete);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.avaje.ebeaninternal.server.persist.dml;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.avaje.ebean.annotation.ConcurrencyMode;
|
||||
import com.avaje.ebeaninternal.api.ConcurrencyMode;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.persist.dml;
|
||||
|
||||
import com.avaje.ebean.annotation.ConcurrencyMode;
|
||||
import com.avaje.ebeaninternal.api.ConcurrencyMode;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.api.SpiUpdatePlan;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.persist.dml;
|
||||
|
||||
import com.avaje.ebean.annotation.ConcurrencyMode;
|
||||
import com.avaje.ebeaninternal.api.ConcurrencyMode;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.api.SpiUpdatePlan;
|
||||
import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.query;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.core.RelationalQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.util.BeanCollectionFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Wraps a BeanCollection with helper methods to add beans.
|
||||
* <p>
|
||||
* Helps adding the bean to the underlying set list or map.
|
||||
* </p>
|
||||
*/
|
||||
public final class BeanCollectionWrapper {
|
||||
|
||||
/**
|
||||
* Flag set if this builds a Map rather than a Collection.
|
||||
*/
|
||||
private final boolean isMap;
|
||||
|
||||
/**
|
||||
* A property name used as key for a Map.
|
||||
*/
|
||||
private final String mapKey;
|
||||
|
||||
/**
|
||||
* The actual BeanCollection.
|
||||
*/
|
||||
private final BeanCollection<?> beanCollection;
|
||||
|
||||
/**
|
||||
* Collection type of BeanCollection.
|
||||
*/
|
||||
private final Collection<Object> collection;
|
||||
|
||||
/**
|
||||
* Map type of BeanCollection.
|
||||
*/
|
||||
private final Map<Object, Object> map;
|
||||
|
||||
/**
|
||||
* The associated BeanDescriptor.
|
||||
*/
|
||||
private final BeanDescriptor<?> desc;
|
||||
|
||||
/**
|
||||
* The number of rows added.
|
||||
*/
|
||||
private int rowCount;
|
||||
|
||||
public BeanCollectionWrapper(RelationalQueryRequest request) {
|
||||
|
||||
this.desc = null;
|
||||
SpiQuery.Type queryType = request.getQueryType();
|
||||
this.mapKey = request.getQuery().getMapKey();
|
||||
this.isMap = SpiQuery.Type.MAP.equals(queryType);
|
||||
|
||||
this.beanCollection = createBeanCollection(queryType);
|
||||
this.collection = getCollection(isMap);
|
||||
this.map = getMap(isMap);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private Map<Object, Object> getMap(boolean isMap) {
|
||||
return isMap ? (Map) beanCollection : null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Collection<Object> getCollection(boolean isMap) {
|
||||
return isMap ? null : (Collection<Object>) beanCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying BeanCollection.
|
||||
*/
|
||||
public BeanCollection<?> getBeanCollection() {
|
||||
return beanCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a BeanCollection of the correct type.
|
||||
*/
|
||||
private BeanCollection<?> createBeanCollection(SpiQuery.Type manyType) {
|
||||
return BeanCollectionFactory.create(manyType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this wraps a Map rather than a set or list.
|
||||
*/
|
||||
public boolean isMap() {
|
||||
return isMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of rows added to this wrapper.
|
||||
*/
|
||||
public int size() {
|
||||
return rowCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the bean to the collection held in this wrapper.
|
||||
*/
|
||||
public void add(EntityBean bean) {
|
||||
add(bean, beanCollection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the bean to the collection passed.
|
||||
*
|
||||
* @param bean the bean to add
|
||||
* @param collection the collection or map to add the bean to
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void add(EntityBean bean, Object collection) {
|
||||
if (bean == null) {
|
||||
return;
|
||||
}
|
||||
rowCount++;
|
||||
if (isMap) {
|
||||
Object keyValue;
|
||||
if (mapKey != null) {
|
||||
// use the value for the property
|
||||
keyValue = desc.getValue(bean, mapKey);
|
||||
} else {
|
||||
// use the uniqueId for this
|
||||
keyValue = desc.getId(bean);
|
||||
}
|
||||
|
||||
Map mapColl = (Map) collection;
|
||||
mapColl.put(keyValue, bean);
|
||||
} else {
|
||||
((Collection) collection).add(bean);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifically add to a Collection.
|
||||
*/
|
||||
public void addToCollection(Object bean) {
|
||||
collection.add(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifically add to this as a Map with a known key.
|
||||
*/
|
||||
public void addToMap(Object bean, Object key) {
|
||||
map.put(key, bean);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.query;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.SqlQuery;
|
||||
import com.avaje.ebean.SqlRow;
|
||||
import com.avaje.ebean.Transaction;
|
||||
|
||||
/**
|
||||
* Represent the SQL query findList as a Callable.
|
||||
*/
|
||||
public class CallableSqlQueryList implements Callable<List<SqlRow>> {
|
||||
|
||||
private final SqlQuery query;
|
||||
|
||||
private final EbeanServer server;
|
||||
|
||||
private final Transaction transaction;
|
||||
|
||||
public CallableSqlQueryList(EbeanServer server, SqlQuery query, Transaction t) {
|
||||
this.server = server;
|
||||
this.query = query;
|
||||
this.transaction = t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query returning the resulting list.
|
||||
*/
|
||||
public List<SqlRow> call() throws Exception {
|
||||
try {
|
||||
return server.findList(query, transaction);
|
||||
} finally {
|
||||
transaction.end();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+68
-180
@@ -1,35 +1,23 @@
|
||||
package com.avaje.ebeaninternal.server.query;
|
||||
|
||||
import com.avaje.ebean.SqlQueryListener;
|
||||
import com.avaje.ebean.QueryEachConsumer;
|
||||
import com.avaje.ebean.QueryEachWhileConsumer;
|
||||
import com.avaje.ebean.SqlRow;
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.api.SpiSqlQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.core.Message;
|
||||
import com.avaje.ebeaninternal.server.core.RelationalQueryEngine;
|
||||
import com.avaje.ebeaninternal.server.core.RelationalQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.lib.util.Str;
|
||||
import com.avaje.ebeaninternal.server.persist.Binder;
|
||||
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.util.BindParamsParser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.*;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Perform native sql fetches.
|
||||
*/
|
||||
public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultRelationalQueryEngine.class);
|
||||
|
||||
private static final int GLOBAL_ROW_LIMIT = Integer.valueOf(System.getProperty("ebean.query.globallimit", "1000000"));
|
||||
|
||||
private final Binder binder;
|
||||
|
||||
private final String dbTrueValue;
|
||||
@@ -39,193 +27,93 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
|
||||
this.dbTrueValue = dbTrueValue == null ? "true" : dbTrueValue;
|
||||
}
|
||||
|
||||
public Object findMany(RelationalQueryRequest request) {
|
||||
|
||||
SpiSqlQuery query = request.getQuery();
|
||||
@Override
|
||||
public void findEach(RelationalQueryRequest request, QueryEachWhileConsumer<SqlRow> consumer) {
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
SpiTransaction t = request.getTransaction();
|
||||
Connection conn = t.getInternalConnection();
|
||||
ResultSet rset = null;
|
||||
PreparedStatement pstmt = null;
|
||||
|
||||
String sql = query.getQuery();
|
||||
|
||||
BindParams bindParams = query.getBindParams();
|
||||
|
||||
if (!bindParams.isEmpty()) {
|
||||
// convert any named parameters if required
|
||||
sql = BindParamsParser.parse(bindParams, sql);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
String bindLog = "";
|
||||
String[] propNames;
|
||||
|
||||
synchronized (query) {
|
||||
if (query.isCancelled()) {
|
||||
logger.trace("Query already cancelled");
|
||||
return null;
|
||||
}
|
||||
|
||||
// synchronise for query.cancel() support
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
|
||||
if (query.getTimeout() > 0) {
|
||||
pstmt.setQueryTimeout(query.getTimeout());
|
||||
}
|
||||
if (query.getBufferFetchSizeHint() > 0) {
|
||||
pstmt.setFetchSize(query.getBufferFetchSizeHint());
|
||||
}
|
||||
|
||||
if (!bindParams.isEmpty()) {
|
||||
bindLog = binder.bind(bindParams, pstmt);
|
||||
}
|
||||
|
||||
if (request.isLogSql()) {
|
||||
String logSql = sql;
|
||||
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
|
||||
logSql = Str.add(logSql, "; --bind(", bindLog, ")");
|
||||
}
|
||||
t.logSql(logSql);
|
||||
}
|
||||
|
||||
rset = pstmt.executeQuery();
|
||||
|
||||
propNames = getPropertyNames(rset);
|
||||
}
|
||||
|
||||
// calculate the initialCapacity of the Map to reduce
|
||||
// rehashing for queries with 12+ columns
|
||||
float initCap = (propNames.length) / 0.7f;
|
||||
int estimateCapacity = (int) initCap + 1;
|
||||
|
||||
// determine the maxRows limit
|
||||
int maxRows = GLOBAL_ROW_LIMIT;
|
||||
if (query.getMaxRows() >= 1) {
|
||||
maxRows = query.getMaxRows();
|
||||
}
|
||||
|
||||
int loadRowCount = 0;
|
||||
|
||||
SqlQueryListener listener = query.getListener();
|
||||
|
||||
BeanCollectionWrapper wrapper = new BeanCollectionWrapper(request);
|
||||
boolean isMap = wrapper.isMap();
|
||||
String mapKey = query.getMapKey();
|
||||
|
||||
SqlRow bean = null;
|
||||
|
||||
while (rset.next()) {
|
||||
synchronized (query) {
|
||||
// synchronise for query.cancel() support
|
||||
if (!query.isCancelled()) {
|
||||
bean = readRow(rset, propNames, estimateCapacity);
|
||||
}
|
||||
}
|
||||
if (bean != null) {
|
||||
// bean can be null if query cancelled
|
||||
if (listener != null) {
|
||||
listener.process(bean);
|
||||
|
||||
} else {
|
||||
if (isMap) {
|
||||
Object keyValue = bean.get(mapKey);
|
||||
wrapper.addToMap(bean, keyValue);
|
||||
} else {
|
||||
wrapper.addToCollection(bean);
|
||||
}
|
||||
}
|
||||
|
||||
loadRowCount++;
|
||||
|
||||
if (loadRowCount == maxRows) {
|
||||
// break, as we have hit the max rows to fetch...
|
||||
break;
|
||||
}
|
||||
request.executeSql(binder);
|
||||
while (request.next()) {
|
||||
if (!consumer.accept(readRow(request))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BeanCollection<?> beanColl = wrapper.getBeanCollection();
|
||||
|
||||
if (request.isLogSummary()) {
|
||||
long exeTime = System.currentTimeMillis() - startTime;
|
||||
String msg = "SqlQuery rows[" + loadRowCount + "] time[" + exeTime + "] bind[" + bindLog + "]";
|
||||
t.logSummary(msg);
|
||||
}
|
||||
|
||||
if (query.isCancelled()) {
|
||||
logger.debug("Query was cancelled during execution rows:" + loadRowCount);
|
||||
}
|
||||
|
||||
return beanColl;
|
||||
logSummary(request, startTime);
|
||||
|
||||
} catch (Exception e) {
|
||||
String m = Message.msg("fetch.error", e.getMessage(), sql);
|
||||
throw new PersistenceException(m, e);
|
||||
throw new PersistenceException(Message.msg("fetch.error", e.getMessage(), request.getSql()), e);
|
||||
|
||||
} finally {
|
||||
try {
|
||||
if (rset != null) {
|
||||
rset.close();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.error(null, e);
|
||||
}
|
||||
try {
|
||||
if (pstmt != null) {
|
||||
pstmt.close();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.error(null, e);
|
||||
}
|
||||
request.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the list of property names.
|
||||
*/
|
||||
protected String[] getPropertyNames(ResultSet rset) throws SQLException {
|
||||
@Override
|
||||
public void findEach(RelationalQueryRequest request, QueryEachConsumer<SqlRow> consumer) {
|
||||
|
||||
ArrayList<String> propNames = new ArrayList<String>();
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
ResultSetMetaData rsmd = rset.getMetaData();
|
||||
try {
|
||||
request.executeSql(binder);
|
||||
while (request.next()) {
|
||||
consumer.accept(readRow(request));
|
||||
}
|
||||
logSummary(request, startTime);
|
||||
|
||||
int columnsPlusOne = rsmd.getColumnCount() + 1;
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(Message.msg("fetch.error", e.getMessage(), request.getSql()), e);
|
||||
|
||||
|
||||
for (int i = 1; i < columnsPlusOne; i++) {
|
||||
String columnName = rsmd.getColumnLabel(i);
|
||||
// will convert columnName to lower case
|
||||
propNames.add(columnName);
|
||||
} finally {
|
||||
request.close();
|
||||
}
|
||||
}
|
||||
|
||||
return propNames.toArray(new String[propNames.size()]);
|
||||
public List<SqlRow> findList(RelationalQueryRequest request) {
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
try {
|
||||
request.executeSql(binder);
|
||||
|
||||
int maxRows = request.getMaxRows();
|
||||
|
||||
int loadRowCount = 0;
|
||||
|
||||
List<SqlRow> rows = new ArrayList<SqlRow>();
|
||||
|
||||
while (request.next()) {
|
||||
rows.add(readRow(request));
|
||||
loadRowCount++;
|
||||
if (loadRowCount == maxRows) {
|
||||
// break, as we have hit the max rows to fetch...
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
logSummary(request, startTime);
|
||||
|
||||
return rows;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(Message.msg("fetch.error", e.getMessage(), request.getSql()), e);
|
||||
|
||||
} finally {
|
||||
request.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void logSummary(RelationalQueryRequest request, long startTime) {
|
||||
|
||||
if (request.isLogSummary()) {
|
||||
long exeTime = System.currentTimeMillis() - startTime;
|
||||
request.getTransaction().logSummary("SqlQuery rows[" + request.getRowCount() + "] time[" + exeTime + "] bind[" + request.getBindLog() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the row from the ResultSet and return as a MapBean.
|
||||
*/
|
||||
protected SqlRow readRow(ResultSet rset, String[] propNames, int initialCapacity) throws SQLException {
|
||||
|
||||
// by default a map will rehash on the 12th entry
|
||||
// it will be pretty common to have 12 or more entries so
|
||||
// to reduce rehashing I am trying to estimate a good
|
||||
// initial capacity for the MapBean to use.
|
||||
SqlRow bean = new DefaultSqlRow(initialCapacity, 0.75f, dbTrueValue);
|
||||
|
||||
int index = 0;
|
||||
|
||||
for (int i = 0; i < propNames.length; i++) {
|
||||
index++;
|
||||
Object value = rset.getObject(index);
|
||||
bean.set(propNames[i], value);
|
||||
}
|
||||
|
||||
return bean;
|
||||
|
||||
protected SqlRow readRow(RelationalQueryRequest request) throws SQLException {
|
||||
return request.createNewRow(dbTrueValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.query;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import com.avaje.ebean.SqlFutureList;
|
||||
import com.avaje.ebean.SqlQuery;
|
||||
import com.avaje.ebean.SqlRow;
|
||||
|
||||
/**
|
||||
* Default implementation of SqlFutureList.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class SqlQueryFutureList extends BaseFuture<List<SqlRow>> implements SqlFutureList {
|
||||
|
||||
private final SqlQuery query;
|
||||
|
||||
public SqlQueryFutureList(SqlQuery query, FutureTask<List<SqlRow>> futureTask) {
|
||||
super(futureTask);
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public SqlQuery getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
query.cancel();
|
||||
return super.cancel(mayInterruptIfRunning);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -218,7 +218,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
|
||||
localBean = localInfo.createEntityBean();
|
||||
localType = localInfo.getType();
|
||||
localIdBinder = localInfo.getIdBinder();
|
||||
localDesc = localInfo.getBeanDescriptor();
|
||||
localDesc = localInfo.desc();
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
+15
-105
@@ -1,20 +1,15 @@
|
||||
package com.avaje.ebeaninternal.server.querydefn;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.SqlFutureList;
|
||||
import com.avaje.ebean.SqlQueryListener;
|
||||
import com.avaje.ebean.QueryEachConsumer;
|
||||
import com.avaje.ebean.QueryEachWhileConsumer;
|
||||
import com.avaje.ebean.SqlRow;
|
||||
import com.avaje.ebeaninternal.api.BindParams;
|
||||
import com.avaje.ebeaninternal.api.SpiSqlQuery;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Default implementation of SQuery - SQL Query.
|
||||
*/
|
||||
@@ -24,8 +19,6 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
|
||||
private final transient EbeanServer server;
|
||||
|
||||
private transient SqlQueryListener queryListener;
|
||||
|
||||
private String query;
|
||||
|
||||
private int firstRow;
|
||||
@@ -34,27 +27,13 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
|
||||
private int timeout;
|
||||
|
||||
private boolean futureFetch;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
/**
|
||||
* For the purposes of cancelling the query.
|
||||
*/
|
||||
private transient PreparedStatement pstmt;
|
||||
|
||||
/**
|
||||
* The rows after which the fetch continues in a bg thread.
|
||||
*/
|
||||
private int backgroundFetchAfter;
|
||||
|
||||
private int bufferFetchSizeHint;
|
||||
|
||||
/**
|
||||
* The property used to get the key value for a Map.
|
||||
*/
|
||||
private String mapKey;
|
||||
|
||||
/**
|
||||
* Bind parameters when using the query language.
|
||||
*/
|
||||
@@ -73,26 +52,24 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findEach(QueryEachConsumer<SqlRow> consumer) {
|
||||
server.findEach(this, consumer, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findEachWhile(QueryEachWhileConsumer<SqlRow> consumer) {
|
||||
server.findEachWhile(this, consumer, null);
|
||||
}
|
||||
|
||||
public List<SqlRow> findList() {
|
||||
return server.findList(this, null);
|
||||
}
|
||||
|
||||
public Set<SqlRow> findSet() {
|
||||
return server.findSet(this, null);
|
||||
}
|
||||
|
||||
public Map<?, SqlRow> findMap() {
|
||||
return server.findMap(this, null);
|
||||
}
|
||||
|
||||
public SqlRow findUnique() {
|
||||
return server.findUnique(this, null);
|
||||
}
|
||||
|
||||
public SqlFutureList findFutureList() {
|
||||
return server.findFutureList(this, null);
|
||||
}
|
||||
|
||||
public DefaultRelationalQuery setParameter(int position, Object value) {
|
||||
bindParams.setParameter(position, value);
|
||||
return this;
|
||||
@@ -103,26 +80,6 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the findListener is one has been set.
|
||||
*/
|
||||
public SqlQueryListener getListener() {
|
||||
return queryListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a listener. This is designed for large fetches
|
||||
* where lots are rows are to be processed and instead of
|
||||
* returning all the rows they are processed one at a time.
|
||||
* <p>
|
||||
* Note that the returning List Set or Map will be empty.
|
||||
* </p>
|
||||
*/
|
||||
public DefaultRelationalQuery setListener(SqlQueryListener queryListener) {
|
||||
this.queryListener = queryListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "SqlQuery [" + query + "]";
|
||||
}
|
||||
@@ -145,24 +102,6 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getMapKey() {
|
||||
return mapKey;
|
||||
}
|
||||
|
||||
public DefaultRelationalQuery setMapKey(String mapKey) {
|
||||
this.mapKey = mapKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getBackgroundFetchAfter() {
|
||||
return backgroundFetchAfter;
|
||||
}
|
||||
|
||||
public DefaultRelationalQuery setBackgroundFetchAfter(int backgroundFetchAfter) {
|
||||
this.backgroundFetchAfter = backgroundFetchAfter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
@@ -181,7 +120,6 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public int getBufferFetchSizeHint() {
|
||||
return bufferFetchSizeHint;
|
||||
}
|
||||
@@ -190,38 +128,10 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
return query;
|
||||
}
|
||||
|
||||
public boolean isFutureFetch() {
|
||||
return futureFetch;
|
||||
}
|
||||
|
||||
public void setFutureFetch(boolean futureFetch) {
|
||||
this.futureFetch = futureFetch;
|
||||
}
|
||||
|
||||
public void setPreparedStatement(PreparedStatement pstmt) {
|
||||
synchronized (this) {
|
||||
this.pstmt = pstmt;
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
synchronized (this) {
|
||||
this.cancelled = true;
|
||||
if (pstmt != null) {
|
||||
try {
|
||||
pstmt.cancel();
|
||||
} catch (SQLException e) {
|
||||
String msg = "Error cancelling query";
|
||||
throw new PersistenceException(msg, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
synchronized (this) {
|
||||
return cancelled;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -84,9 +84,9 @@ public final class PostCommitProcessing {
|
||||
/**
|
||||
* Notify the local part of L2 cache.
|
||||
*/
|
||||
void notifyLocalCache() {
|
||||
void notifyLocalCache(boolean viewInvalidation) {
|
||||
processTableEvents(event.getEventTables());
|
||||
cacheChanges = event.buildCacheChanges();
|
||||
cacheChanges = event.buildCacheChanges(viewInvalidation);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,7 +149,7 @@ public final class PostCommitProcessing {
|
||||
return new Runnable() {
|
||||
public void run() {
|
||||
if (cacheChanges != null) {
|
||||
cacheChanges.apply();
|
||||
manager.processViewInvalidation(cacheChanges.apply());
|
||||
}
|
||||
localPersistListenersNotify();
|
||||
notifyCluster();
|
||||
|
||||
@@ -27,6 +27,7 @@ import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
@@ -104,6 +105,8 @@ public class TransactionManager {
|
||||
*/
|
||||
private final ChangeLogListener changeLogListener;
|
||||
|
||||
private final boolean viewInvalidation;
|
||||
|
||||
/**
|
||||
* Create the TransactionManager
|
||||
*/
|
||||
@@ -113,6 +116,7 @@ public class TransactionManager {
|
||||
this.persistBatch = config.getPersistBatch();
|
||||
this.persistBatchOnCascade = config.appliedPersistBatchOnCascade();
|
||||
this.beanDescriptorManager = descMgr;
|
||||
this.viewInvalidation = descMgr.requiresViewEntityCacheInvalidation();
|
||||
this.changeLogPrepare = descMgr.getChangeLogPrepare();
|
||||
this.changeLogListener = descMgr.getChangeLogListener();
|
||||
this.clusterManager = clusterManager;
|
||||
@@ -398,7 +402,7 @@ public class TransactionManager {
|
||||
}
|
||||
|
||||
PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, transaction);
|
||||
postCommit.notifyLocalCache();
|
||||
postCommit.notifyLocalCache(viewInvalidation);
|
||||
backgroundExecutor.execute(postCommit.backgroundNotify());
|
||||
|
||||
for (TransactionEventListener listener : transactionEventListeners) {
|
||||
@@ -423,7 +427,7 @@ public class TransactionManager {
|
||||
event.add(tableEvents);
|
||||
|
||||
PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, event);
|
||||
postCommit.notifyLocalCache();
|
||||
postCommit.notifyLocalCache(viewInvalidation);
|
||||
backgroundExecutor.execute(postCommit.backgroundNotify());
|
||||
}
|
||||
|
||||
@@ -478,4 +482,13 @@ public class TransactionManager {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the query caches for entities based on views.
|
||||
*/
|
||||
public void processViewInvalidation(Set<String> viewInvalidation) {
|
||||
if (!viewInvalidation.isEmpty()) {
|
||||
beanDescriptorManager.processViewInvalidation(viewInvalidation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,27 @@ package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebean.annotation.DbEnumType;
|
||||
import com.avaje.ebean.annotation.DbEnumValue;
|
||||
import com.avaje.ebean.annotation.EnumMapping;
|
||||
import com.avaje.ebean.annotation.EnumValue;
|
||||
import com.avaje.ebean.config.*;
|
||||
import com.avaje.ebean.config.CompoundType;
|
||||
import com.avaje.ebean.config.CompoundTypeProperty;
|
||||
import com.avaje.ebean.config.JsonConfig;
|
||||
import com.avaje.ebean.config.ScalarTypeConverter;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.config.dbplatform.DbType;
|
||||
import com.avaje.ebeaninternal.server.core.BootupClasses;
|
||||
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.*;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.CheckImmutable;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.CheckImmutableResponse;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.ImmutableMeta;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.ImmutableMetaFactory;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.KnownImmutable;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.ReflectionBasedCompoundType;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.ReflectionBasedCompoundTypeProperty;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.ReflectionBasedTypeBuilder;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.joda.time.*;
|
||||
import org.joda.time.DateMidnight;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.LocalDateTime;
|
||||
import org.joda.time.LocalTime;
|
||||
@@ -32,11 +42,29 @@ import java.sql.Date;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
import java.sql.Types;
|
||||
import java.time.*;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.Month;
|
||||
import java.time.MonthDay;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.OffsetTime;
|
||||
import java.time.Period;
|
||||
import java.util.*;
|
||||
import java.time.Year;
|
||||
import java.time.YearMonth;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Currency;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
@@ -516,20 +544,9 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
}
|
||||
}
|
||||
|
||||
// get the mapping information from EnumMapping
|
||||
EnumMapping enumMapping = enumType.getAnnotation(EnumMapping.class);
|
||||
if (enumMapping == null) {
|
||||
// look for EnumValue annotations instead
|
||||
return createEnumScalarType2(enumType);
|
||||
}
|
||||
|
||||
String nameValuePairs = enumMapping.nameValuePairs();
|
||||
boolean integerType = enumMapping.integerType();
|
||||
int dbColumnLength = enumMapping.length();
|
||||
|
||||
Map<String, String> nameValueMap = StringHelper.delimitedToMap(nameValuePairs, ",", "=");
|
||||
|
||||
return createEnumScalarType(enumType, nameValueMap, integerType, dbColumnLength);
|
||||
// look for EnumValue annotations instead
|
||||
return createEnumScalarType2(enumType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -52,12 +52,19 @@ public abstract class ScalarTypeBaseDate<T> extends ScalarTypeBase<T> {
|
||||
|
||||
public String formatValue(T t) {
|
||||
Date date = convertToDate(t);
|
||||
return date.toString();
|
||||
// format all dates into epoch millis
|
||||
long epochMillis = date.getTime();
|
||||
return Long.toString(epochMillis);
|
||||
}
|
||||
|
||||
public T parse(String value) {
|
||||
Date date = Date.valueOf(value);
|
||||
return convertFromDate(date);
|
||||
try {
|
||||
long epochMillis = Long.parseLong(value);
|
||||
return convertFromDate(new Date(epochMillis));
|
||||
} catch (NumberFormatException e) {
|
||||
Date date = Date.valueOf(value);
|
||||
return convertFromDate(date);
|
||||
}
|
||||
}
|
||||
|
||||
public T convertFromMillis(long systemTimeMillis) {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns="http://ebean-orm.github.io/xml/ns/extraddl"
|
||||
targetNamespace="http://ebean-orm.github.io/xml/ns/extraddl" elementFormDefault="qualified">
|
||||
|
||||
<!-- Root level type : extra-ddl -->
|
||||
|
||||
<xsd:element name="extra-ddl">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="ddl-script" minOccurs="1" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="ddl-script">
|
||||
<xsd:complexType>
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="platforms" type="xsd:string"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
</xsd:schema>
|
||||
@@ -1,13 +1,13 @@
|
||||
create table ${table} (
|
||||
id integer not null,
|
||||
status varchar(10) not null,
|
||||
run_version varchar(150) not null,
|
||||
dep_version varchar(150) not null,
|
||||
comment varchar(150),
|
||||
checksum integer not null,
|
||||
mtype varchar(1) not null,
|
||||
mstatus varchar(10) not null,
|
||||
mversion varchar(150) not null,
|
||||
mcomment varchar(150) not null,
|
||||
mchecksum integer not null,
|
||||
run_on timestamp not null,
|
||||
run_by varchar(30) not null,
|
||||
run_ip varchar(30),
|
||||
run_time integer not null,
|
||||
constraint pk_${table} primary key (id)
|
||||
);
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ public class EbeanServerFactory_ServerConfigStart_Test {
|
||||
@Test
|
||||
public void test() throws InterruptedException {
|
||||
|
||||
System.setProperty("ebean.ignoreExtraDdl", "true");
|
||||
|
||||
ServerConfig config = new ServerConfig();
|
||||
config.setName("h2");
|
||||
config.loadFromProperties();
|
||||
|
||||
@@ -2,11 +2,85 @@ package com.avaje.ebean.dbmigration.model;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.StrictAssertions.assertThat;
|
||||
|
||||
public class MigrationVersionTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void sort() {
|
||||
|
||||
List<MigrationVersion> list= new ArrayList<MigrationVersion>();
|
||||
list.add(MigrationVersion.parse("1.1__point"));
|
||||
list.add(MigrationVersion.parse("3.0__three"));
|
||||
list.add(MigrationVersion.parse("1.0__init"));
|
||||
list.add(MigrationVersion.parse("R__beta"));
|
||||
list.add(MigrationVersion.parse("R__alpha"));
|
||||
|
||||
Collections.sort(list);
|
||||
|
||||
assertThat(list.get(0).getComment()).isEqualTo("init");
|
||||
assertThat(list.get(1).getComment()).isEqualTo("point");
|
||||
assertThat(list.get(2).getComment()).isEqualTo("three");
|
||||
assertThat(list.get(3).getComment()).isEqualTo("alpha");
|
||||
assertThat(list.get(4).getComment()).isEqualTo("beta");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_parse_when_repeatable() throws Exception {
|
||||
|
||||
MigrationVersion version = MigrationVersion.parse("R__Foo");
|
||||
assertThat(version.getComment()).isEqualTo("Foo");
|
||||
assertThat(version.normalised()).isEqualTo("R");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_parse_when_repeatable_case() throws Exception {
|
||||
|
||||
MigrationVersion version = MigrationVersion.parse("r__Foo");
|
||||
assertThat(version.isRepeatable()).isTrue();
|
||||
assertThat(version.getComment()).isEqualTo("Foo");
|
||||
assertThat(version.normalised()).isEqualTo("R");
|
||||
assertThat(version.normalised()).isEqualTo("R");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_parse_when_v_prefix() throws Exception {
|
||||
|
||||
MigrationVersion version = MigrationVersion.parse("v1_0__Foo");
|
||||
assertThat(version.isRepeatable()).isFalse();
|
||||
assertThat(version.getComment()).isEqualTo("Foo");
|
||||
assertThat(version.normalised()).isEqualTo("1.0");
|
||||
assertThat(version.asString()).isEqualTo("1_0");
|
||||
assertThat(version.getRaw()).isEqualTo("1_0__Foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void repeatable_compareTo() throws Exception {
|
||||
|
||||
MigrationVersion foo = MigrationVersion.parse("R__Foo");
|
||||
MigrationVersion bar = MigrationVersion.parse("R__Bar");
|
||||
assertThat(foo.compareTo(bar)).isGreaterThan(0);
|
||||
assertThat(bar.compareTo(foo)).isLessThan(0);
|
||||
|
||||
MigrationVersion bar2 = MigrationVersion.parse("R__Bar");
|
||||
assertThat(bar.compareTo(bar2)).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void repeatable_compareTo_when_caseDifferent() throws Exception {
|
||||
|
||||
MigrationVersion none = MigrationVersion.parse("R__");
|
||||
MigrationVersion bar = MigrationVersion.parse("R__Bar");
|
||||
MigrationVersion bar2 = MigrationVersion.parse("R__bar");
|
||||
assertThat(none.compareTo(bar)).isLessThan(0);
|
||||
assertThat(bar.compareTo(bar2)).isLessThan(0);
|
||||
assertThat(none.compareTo(bar2)).isLessThan(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_parse_getComment() throws Exception {
|
||||
@@ -51,10 +125,14 @@ public class MigrationVersionTest {
|
||||
MigrationVersion v0 = MigrationVersion.parse("1.1.1_2__Foo");
|
||||
MigrationVersion v1 = MigrationVersion.parse("1.1.1.2_junk");
|
||||
MigrationVersion v2 = MigrationVersion.parse("1.1_1.2_foo");
|
||||
MigrationVersion v3 = MigrationVersion.parse("1.1_1.2__foo");
|
||||
|
||||
assertThat(v0.compareTo(v1)).isEqualTo(0);
|
||||
assertThat(v1.compareTo(v0)).isEqualTo(0);
|
||||
assertThat(v0.compareTo(v1)).isGreaterThan(0);
|
||||
assertThat(v1.compareTo(v0)).isLessThan(0);
|
||||
assertThat(v1.compareTo(v2)).isEqualTo(0);
|
||||
|
||||
assertThat(v0.compareTo(v3)).isLessThan(0);
|
||||
assertThat(v3.compareTo(v0)).isGreaterThan(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+3
-1
@@ -24,6 +24,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
public class ModelBuild_compoundKeyTest extends BaseTestCase {
|
||||
|
||||
private SpiEbeanServer getServer() {
|
||||
|
||||
System.setProperty("ebean.ignoreExtraDdl", "true");
|
||||
|
||||
ServerConfig config = new ServerConfig();
|
||||
config.setName("h2");
|
||||
config.loadFromProperties();
|
||||
@@ -38,7 +41,6 @@ public class ModelBuild_compoundKeyTest extends BaseTestCase {
|
||||
config.addClass(CKeyAssoc.class);
|
||||
config.addClass(CKeyParentId.class);
|
||||
|
||||
|
||||
return (SpiEbeanServer) EbeanServerFactory.create(config);
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -18,6 +18,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
public class ModelBuild_explicitSequencesTest extends BaseTestCase {
|
||||
|
||||
private SpiEbeanServer getServer(boolean postgres) {
|
||||
|
||||
System.setProperty("ebean.ignoreExtraDdl", "true");
|
||||
|
||||
ServerConfig config = new ServerConfig();
|
||||
config.setName("h2");
|
||||
config.loadFromProperties();
|
||||
|
||||
@@ -20,6 +20,8 @@ public class BeanFindControllerTest extends BaseTestCase {
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
System.setProperty("ebean.ignoreExtraDdl", "true");
|
||||
|
||||
ServerConfig config = new ServerConfig();
|
||||
|
||||
config.setName("h2otherfind");
|
||||
|
||||
@@ -71,6 +71,7 @@ public class BeanPersistControllerTest {
|
||||
|
||||
private EbeanServer getEbeanServer(PersistAdapter persistAdapter) {
|
||||
|
||||
System.setProperty("ebean.ignoreExtraDdl", "true");
|
||||
ServerConfig config = new ServerConfig();
|
||||
|
||||
config.setName("h2ebasicver");
|
||||
|
||||
@@ -47,6 +47,8 @@ public class BeanPostLoadTest extends BaseTestCase {
|
||||
|
||||
private EbeanServer getEbeanServer() {
|
||||
|
||||
System.setProperty("ebean.ignoreExtraDdl", "true");
|
||||
|
||||
ServerConfig config = new ServerConfig();
|
||||
|
||||
config.setName("h2ebasicver");
|
||||
|
||||
@@ -14,10 +14,10 @@ import com.avaje.ebean.meta.MetaInfoManager;
|
||||
import com.avaje.ebean.plugin.SpiServer;
|
||||
import com.avaje.ebean.text.csv.CsvReader;
|
||||
import com.avaje.ebean.text.json.JsonContext;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
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;
|
||||
@@ -505,11 +505,6 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlFutureList findFutureList(SqlQuery query, Transaction transaction) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction, int pageIndex, int pageSize) {
|
||||
return null;
|
||||
@@ -541,13 +536,11 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SqlRow> findSet(SqlQuery query, Transaction transaction) {
|
||||
return null;
|
||||
public void findEach(SqlQuery query, QueryEachConsumer<SqlRow> consumer, Transaction transaction) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<?, SqlRow> findMap(SqlQuery query, Transaction transaction) {
|
||||
return null;
|
||||
public void findEachWhile(SqlQuery query, QueryEachWhileConsumer<SqlRow> consumer, Transaction transaction) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.avaje.ebeaninternal.extraddl.model;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class ExtraDdlXmlReaderTest {
|
||||
|
||||
@Test
|
||||
public void read() throws Exception {
|
||||
|
||||
ExtraDdl read = ExtraDdlXmlReader.read("/extra-ddl.xml");
|
||||
assertNotNull(read);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildExtra_when_h2() {
|
||||
|
||||
String ddl = ExtraDdlXmlReader.buildExtra("h2");
|
||||
|
||||
assertThat(ddl).contains("create or replace view order_agg_vw");
|
||||
assertThat(ddl).contains("-- h2 and postgres script");
|
||||
assertThat(ddl).doesNotContain(" -- oracle only script");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildExtra_when_oracle() {
|
||||
|
||||
String ddl = ExtraDdlXmlReader.buildExtra("oracle");
|
||||
|
||||
assertThat(ddl).contains("create or replace view order_agg_vw");
|
||||
assertThat(ddl).doesNotContain("-- h2 and postgres script");
|
||||
assertThat(ddl).contains(" -- oracle only script");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildExtra_when_mysql() {
|
||||
|
||||
String ddl = ExtraDdlXmlReader.buildExtra("mysql");
|
||||
|
||||
assertThat(ddl).contains("create or replace view order_agg_vw");
|
||||
assertThat(ddl).doesNotContain("-- h2 and postgres script");
|
||||
assertThat(ddl).doesNotContain(" -- oracle only script");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,9 +2,11 @@ package com.avaje.ebeaninternal.server.cache;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
import com.avaje.tests.model.basic.Address;
|
||||
import com.avaje.tests.model.basic.Country;
|
||||
import com.avaje.tests.model.basic.Customer;
|
||||
@@ -50,7 +52,7 @@ public class CacheBeanDataTest extends BaseTestCase {
|
||||
|
||||
Customer newCustomer = new Customer();
|
||||
newCustomer.setId(c.getId());
|
||||
CachedBeanDataToBean.load(desc, (EntityBean) newCustomer, cacheData);
|
||||
CachedBeanDataToBean.load(desc, (EntityBean) newCustomer, cacheData, new DefaultPersistenceContext());
|
||||
|
||||
assertEquals(c.getId(), newCustomer.getId());
|
||||
assertEquals(c.getName(), newCustomer.getName());
|
||||
@@ -91,9 +93,11 @@ public class CacheBeanDataTest extends BaseTestCase {
|
||||
|
||||
CachedBeanData addressCacheData = (CachedBeanData) addressBeanProperty.getCacheDataValue((EntityBean) person);
|
||||
|
||||
PersistenceContext context = new DefaultPersistenceContext();
|
||||
|
||||
EPerson newPersonCheck = new EPerson();
|
||||
newPersonCheck.setId(98989L);
|
||||
addressBeanProperty.setCacheDataValue((EntityBean) newPersonCheck, addressCacheData);
|
||||
addressBeanProperty.setCacheDataValue((EntityBean) newPersonCheck, addressCacheData, context);
|
||||
|
||||
EAddress newAddress = newPersonCheck.getAddress();
|
||||
assertEquals(address.getStreet(), newAddress.getStreet());
|
||||
@@ -105,7 +109,7 @@ public class CacheBeanDataTest extends BaseTestCase {
|
||||
|
||||
assertNotNull(cacheData);
|
||||
|
||||
EPerson newPerson = (EPerson)desc.cacheEmbeddedBeanLoad(cacheData);
|
||||
EPerson newPerson = (EPerson)desc.cacheEmbeddedBeanLoad(cacheData, context);
|
||||
|
||||
assertNotNull(newPerson.getId());
|
||||
assertNotNull(newPerson.getName());
|
||||
|
||||
+8
-1
@@ -4,11 +4,14 @@ import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
import com.avaje.tests.model.basic.Address;
|
||||
import com.avaje.tests.model.basic.Car;
|
||||
import com.avaje.tests.model.basic.Customer;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.sql.Date;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class CachedBeanDataFromBeanTest extends BaseTestCase {
|
||||
@@ -20,9 +23,12 @@ public class CachedBeanDataFromBeanTest extends BaseTestCase {
|
||||
|
||||
BeanDescriptor<Customer> desc = server.getBeanDescriptor(Customer.class);
|
||||
|
||||
Date largeDate = new Date(9223372036825200000L);
|
||||
|
||||
Customer customer = new Customer();
|
||||
customer.setId(42);
|
||||
customer.setName("Rob");
|
||||
customer.setAnniversary(largeDate);
|
||||
|
||||
Address billingAddress = new Address();
|
||||
billingAddress.setId(Short.valueOf("12"));
|
||||
@@ -35,6 +41,7 @@ public class CachedBeanDataFromBeanTest extends BaseTestCase {
|
||||
assertEquals(cacheData.getData("id"), "42");
|
||||
assertEquals(cacheData.getData("name"), "Rob");
|
||||
assertEquals(cacheData.getData("billingAddress"), "12");
|
||||
assertEquals(cacheData.getData("anniversary"), "9223372036825200000");
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +58,7 @@ public class CachedBeanDataFromBeanTest extends BaseTestCase {
|
||||
|
||||
Car newCar = new Car();
|
||||
EntityBean entityBean = (EntityBean)newCar;
|
||||
CachedBeanDataToBean.load(carDesc, entityBean, cacheData);
|
||||
CachedBeanDataToBean.load(carDesc, entityBean, cacheData, new DefaultPersistenceContext());
|
||||
|
||||
assertEquals(newCar.getId(), car.getId());
|
||||
assertEquals(newCar.getDriver(), car.getDriver());
|
||||
|
||||
+3
-2
@@ -4,6 +4,7 @@ import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
import com.avaje.tests.model.basic.Customer;
|
||||
import com.avaje.tests.model.basic.ResetBasicData;
|
||||
import com.avaje.tests.model.basic.TBytesOnly;
|
||||
@@ -82,7 +83,7 @@ public class CachedBeanDataSerializeTest extends BaseTestCase {
|
||||
assertEquals(read.getData(), extract.getData());
|
||||
|
||||
Customer loadCustomer = new Customer();
|
||||
CachedBeanDataToBean.load(desc, (EntityBean)loadCustomer, read);
|
||||
CachedBeanDataToBean.load(desc, (EntityBean)loadCustomer, read, new DefaultPersistenceContext());
|
||||
|
||||
assertEquals(loadCustomer.getVersion(), customer.getVersion());
|
||||
assertEquals(loadCustomer.getId(), customer.getId());
|
||||
@@ -113,7 +114,7 @@ public class CachedBeanDataSerializeTest extends BaseTestCase {
|
||||
assertTrue(Arrays.equals(bean.getContent(), extraContent));
|
||||
|
||||
TBytesOnly loadBean= new TBytesOnly();
|
||||
CachedBeanDataToBean.load(desc, (EntityBean)loadBean, read);
|
||||
CachedBeanDataToBean.load(desc, (EntityBean)loadBean, read, new DefaultPersistenceContext());
|
||||
|
||||
assertEquals(loadBean.getId(), bean.getId());
|
||||
assertTrue(Arrays.equals(loadBean.getContent(), bean.getContent()));
|
||||
|
||||
@@ -18,7 +18,7 @@ public class BeanDescriptorTest extends BaseTestCase {
|
||||
@Test
|
||||
public void createReference() {
|
||||
|
||||
Customer bean = customerDesc.createReference(null, 42);
|
||||
Customer bean = customerDesc.createReference(null, 42, null);
|
||||
assertThat(bean.getId()).isEqualTo(42);
|
||||
assertThat(server().getBeanState(bean).isReadOnly()).isFalse();
|
||||
}
|
||||
@@ -26,14 +26,14 @@ public class BeanDescriptorTest extends BaseTestCase {
|
||||
@Test
|
||||
public void createReference_whenReadOnly() {
|
||||
|
||||
Customer bean = customerDesc.createReference(Boolean.TRUE, 42);
|
||||
Customer bean = customerDesc.createReference(Boolean.TRUE, 42, null);
|
||||
assertThat(server().getBeanState(bean).isReadOnly()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createReference_whenNotReadOnly() {
|
||||
|
||||
Customer bean = customerDesc.createReference(Boolean.FALSE, 42);
|
||||
Customer bean = customerDesc.createReference(Boolean.FALSE, 42, null);
|
||||
assertThat(server().getBeanState(bean).isReadOnly()).isFalse();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ public class BeanDescriptor_registerTest {
|
||||
@Test
|
||||
public void testRegisterDeregister() throws Exception {
|
||||
|
||||
System.setProperty("ebean.ignoreExtraDdl", "true");
|
||||
|
||||
ServerConfig config = new ServerConfig();
|
||||
|
||||
config.setName("h2other");
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.sql.Date;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class ScalarTypeDateTest {
|
||||
|
||||
ScalarTypeDate type = new ScalarTypeDate();
|
||||
|
||||
@Test
|
||||
public void formatParse_PG_DATE_POSITIVE_INFINITY() {
|
||||
|
||||
Date postgresInfinityDate = new Date(9223372036825200000L);
|
||||
|
||||
String format = type.formatValue(postgresInfinityDate);
|
||||
Date parsed = type.parse(format);
|
||||
assertEquals(parsed, postgresInfinityDate);
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,6 @@ public class TunedQueryInfoTest extends BaseTestCase {
|
||||
|
||||
ServerCacheManager serverCacheManager = Ebean.getServer(null).getServerCacheManager();
|
||||
serverCacheManager.clearAll();
|
||||
serverCacheManager.setCaching(Order.class, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -37,6 +37,8 @@ public class MainDbBoolean {
|
||||
*/
|
||||
private EbeanServer createOracleEbeanServer() {
|
||||
|
||||
System.setProperty("ebean.ignoreExtraDdl", "true");
|
||||
|
||||
ServerConfig c = new ServerConfig();
|
||||
c.setName("ora");
|
||||
|
||||
@@ -71,6 +73,8 @@ public class MainDbBoolean {
|
||||
|
||||
private EbeanServer createEbeanServer() {
|
||||
|
||||
System.setProperty("ebean.ignoreExtraDdl", "true");
|
||||
|
||||
ServerConfig c = new ServerConfig();
|
||||
c.setName("pgtest");
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user