Compare commits

...
Author SHA1 Message Date
Robin Bygrave b4d49161af [maven-release-plugin] prepare release avaje-ebeanorm-7.9.1 2016-05-02 16:35:04 +12:00
Robin Bygrave 8a68e72305 No effective change - modify test, add l2 cache await 2016-05-02 16:32:08 +12:00
Robin Bygrave 07fe1cde6b #689 - Refactor DB Migration to support "V" prefix 2016-05-02 16:25:27 +12:00
Robin Bygrave 9da5ce01a7 #686 - Trouble with non-standard java.sql.Date values caching in 7.8.1. 2016-04-29 22:22:38 +12:00
Robin Bygrave 5bcfce9cd4 #684 - Refactor - rename @CacheStrategy to @Cache ... require explicit enableQueryCache=true for query cache use 2016-04-29 21:52:50 +12:00
Robin Bygrave a6d276d4d3 #683 - Refactor - rename @CacheTuning to @CacheBeanTuning ... for consistency 2016-04-29 09:48:36 +12:00
Robin Bygrave 5b100e6d37 #682 - Remove deprecated Model.Finder constructors that take Id type ... migrate to ones that don't 2016-04-29 09:28:43 +12:00
Robin Bygrave bf75af85c1 #681 - Remove SqlQuery.cancel() ... migrate to SqlQuery.findEachWhile() and stop while iterating 2016-04-29 09:05:36 +12:00
Robin Bygrave 5cf2d3c39a #680 - Remove deprecated SqlQuery.findFutureList() 2016-04-29 08:49:07 +12:00
Robin Bygrave 840c5f8992 #679 - Remove SqlQuery.setListener() ... migrate to SqlQuery.findEach() or SqlQuery.findEachWhile() 2016-04-29 08:43:30 +12:00
Robin Bygrave f867cd4c01 #678 - Remove SqlQuery.findSet() and SqlQuery.findMap() ... migrate to use SlqQuery.findList() 2016-04-29 08:37:52 +12:00
Robin Bygrave ad17da5b36 No effective change - format 2016-04-28 22:22:04 +12:00
Robin Bygrave 9350155c00 #677 - REFACTOR: Remove unused SqlQuery.setBackgroundFetchAfter() 2016-04-28 22:18:14 +12:00
Robin Bygrave 8a84eaf7a7 #675 - ENH: Add SqlQuery.findEach() and SqlQuery.findEachWhile() methods ... to replace setListener() 2016-04-28 22:15:41 +12:00
Robin Bygrave f0fab77ff7 #670 - Remove @EntityConcurrencyMode ... this is now rather redundant since 4.x changes 2016-04-28 20:26:17 +12:00
Robin Bygrave 41aace9dc4 #669 - Remove @EnumMapping ... migrate to use @DbEnumValue or @EnumValue 2016-04-28 20:22:37 +12:00
Robin Bygrave 7c9d752c3d Merge branch 'master' of github.com:ebean-orm/avaje-ebeanorm 2016-04-28 20:19:22 +12:00
Robin Bygrave 81531b6cda #671 - ENH: Add support for @View and extra-ddl.xml to nicely support entity beans based on views 2016-04-28 20:19:05 +12:00
Rob Bygrave b70f3574d9 Update ISSUE_TEMPLATE.md
Add "some java code" placeholder
2016-04-28 13:22:31 +12:00
Rob Bygrave be214273cc Update README.md 2016-04-28 13:05:17 +12:00
Robin Bygrave 69c2810fbe No effective change - add test for SqlQuery listener 2016-04-28 09:54:17 +12:00
Robin Bygrave 82da81116e Add github issue template 2016-04-28 09:40:05 +12:00
Robin Bygrave 7d5e135f56 [maven-release-plugin] prepare for next development iteration 2016-04-27 08:41:26 +12:00
138 changed files with 2504 additions and 2181 deletions
+9
View File
@@ -0,0 +1,9 @@
## Expected behavior
## Actual behavior
### Steps to reproduce
```java
// some java code
```
+10 -7
View File
@@ -1,14 +1,17 @@
* [![Maven Central : avaje-ebeanorm](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm) - avaje-ebeanorm
* [![Maven Central : avaje-ebeanorm-agent](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-agent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-agent) - avaje-ebeanorm-agent
* [![Maven Central : avaje-ebeanorm-agent](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-mavenenhancer/badge.svg)](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
* [![Maven Central : avaje-ebeanorm](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm) - avaje-ebeanorm
* [![Maven Central : avaje-ebeanorm-agent](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-agent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-agent) - avaje-ebeanorm-agent
* [![Maven Central : avaje-ebeanorm-agent](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-mavenenhancer/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-mavenenhancer) - avaje-ebeanorm-mavenenhancer
+2 -2
View File
@@ -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>
+9 -42
View File
@@ -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.
-23
View File
@@ -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&lt;SqlRow&gt; list = futureList.get();
* System.out.println("list:"+list);
* }
*
* </pre>
*
* @author rob
*
*/
public interface SqlFutureList extends Future<List<SqlRow>> {
SqlQuery getQuery();
}
+25 -58
View File
@@ -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 = &quot;select id, name from customer where name like :name and status_code = :status&quot;;
* String sql = "select id, name from customer where name like :name and status_code = :status";
*
* SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
* sqlQuery.setParameter(&quot;name&quot;, &quot;Acme%&quot;);
* sqlQuery.setParameter(&quot;status&quot;, &quot;ACTIVE&quot;);
* SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
* sqlQuery.setParameter("name", "Acme%");
* sqlQuery.setParameter("status", "ACTIVE");
*
* // execute the query returning a List of MapBean objects
* List&lt;SqlRow&gt; 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(&quot;my.large.query&quot;);
*
* // 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);
}
@@ -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>
*/
@@ -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">
* ...
* &#064;EnumMapping(nameValuePairs=&quot;NEW=N, ACTIVE=A, INACTIVE=I&quot;)
* 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 {};
}
-23
View File
@@ -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
View File
@@ -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,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>
* &lt;complexType>
* &lt;simpleContent>
* &lt;extension base="&lt;http://www.w3.org/2001/XMLSchema>string">
* &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="platforms" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/extension>
* &lt;/simpleContent>
* &lt;/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>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element ref="{http://ebean-orm.github.io/xml/ns/extraddl}ddl-script" maxOccurs="unbounded"/>
* &lt;/sequence>
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/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());
}
}
/**
@@ -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;
}
@@ -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.
*/
@@ -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;
}
@@ -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.
@@ -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();
}
}
}
@@ -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 {
@@ -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) {
+26
View File
@@ -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
@@ -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);
}
@@ -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());
@@ -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());
@@ -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