Compare commits

..
Author SHA1 Message Date
Robin Bygrave 21b04c244b [maven-release-plugin] prepare release avaje-ebeanorm-4.8.1 2015-07-14 23:28:32 +12:00
Robin Bygrave 957c2def43 bump pom to 4.8.1-SNAPSHOT 2015-07-14 23:24:34 +12:00
Robin Bygrave 35f9cd3821 Bump pom to 4.8.1-SNAPSHOT 2015-07-14 23:18:51 +12:00
Robin Bygrave e95d3304bf #322 - RawSql fetch object with inheritance 2015-07-14 23:09:38 +12:00
rbygrave 0f7c9da5d2 #319 - Beans loaded without an @Id value ... return in NEW state rather than LOADED state (also refer to #317) 2015-07-09 22:18:03 +12:00
rbygrave b4008da70c #317 - RawSql getting error - Caused by: java.sql.SQLException: Column Index out of range, 0 < 1. 2015-07-09 21:44:45 +12:00
rbygrave 34a0f16c74 No effective change - bump mysql-connector-java test dependency version 2015-07-07 23:07:10 +12:00
Rob Bygrave a880109201 Merge pull request #316 from castovoid/master
Resolve NullPointerException on JtaTransacionManager constructor
2015-06-30 21:17:27 +12:00
Rob Bygrave 0607151ac8 Merge pull request #315 from ossd/patch-1
Update ScalarTypeEnumStandard.java
2015-06-30 21:15:47 +12:00
Leone Parise 5c4ad4c01c Resolve NullPointerException on JtaTransacionManager 2015-06-30 01:01:35 -03:00
ossd 3564504e00 Update ScalarTypeEnumStandard.java
Using toString() to calculate the Enum length is wrong. If you overwrite the Enum's toString() the length may be shorter. In this situation, the ddl script will create a to small varchar field for the value and you get an exception on save
2015-06-29 22:36:24 +02:00
rbygrave 49babef495 #314 - ServerCache API - change from int to long for ServerCacheStatistics (hitCount, missCount) and ServerCache remove putIfAbsent() method 2015-06-30 00:56:11 +12:00
rbygrave 9e3362d004 No effective change - tidy 2015-06-26 23:05:40 +12:00
Rob Bygrave 3f0dcdb6d1 Update README.md 2015-06-25 00:12:16 +12:00
rbygrave 481116251d [maven-release-plugin] prepare for next development iteration 2015-06-24 23:50:22 +12:00
rbygrave d490966025 [maven-release-plugin] prepare release avaje-ebeanorm-4.7.3 2015-06-24 23:50:02 +12:00
rbygrave 5bc0b01a6c #308 - Deprecate BeanFinder ... preference now to use BeanFindController 2015-06-24 23:42:35 +12:00
rbygrave 2bd14db8f7 #308 - Add BeanFindController 2015-06-24 23:36:34 +12:00
rbygrave 17a0933860 #308 - Add BeanFindController 2015-06-24 23:12:34 +12:00
rbygrave 5bb8384567 No effective change - format only 2015-06-24 22:44:08 +12:00
rbygrave 29a6bbb6e6 #312 - Ability to remove deregister BeanPersistListeners removed 2015-06-24 22:32:12 +12:00
rbygrave 46a1324f58 [maven-release-plugin] prepare for next development iteration 2015-06-22 00:22:45 +12:00
rbygrave 8f7199b586 [maven-release-plugin] prepare release avaje-ebeanorm-4.7.2 2015-06-22 00:22:23 +12:00
rbygrave d2c3142de4 WIP on Finder 2015-06-22 00:20:01 +12:00
rbygrave 721a50219d #307 - Add back support for JsonValueAdapter and JsonReadOptions into 4.x 2015-06-21 01:46:23 +12:00
Rob Bygrave f0b8270e5e Update README.md 2015-06-18 00:11:04 +12:00
rbygrave 92232c2afb [maven-release-plugin] prepare for next development iteration 2015-06-17 23:18:58 +12:00
rbygrave 5e68b1a25e [maven-release-plugin] prepare release avaje-ebeanorm-4.7.1 2015-06-17 23:18:36 +12:00
rbygrave 041324d177 #310 - Child bean with @Id long id = 0 ... when cascade saved from parent ... tries to update instead of insert resulting in OptimisticLockingException 2015-06-17 23:16:02 +12:00
rbygrave 7d02a42276 #309 - ScalarTypeConverters for Java8 time datatypes not working 2015-06-17 21:13:48 +12:00
rbygrave 2a900165f0 #305 - ENH: Add EbeanServerFactory.createWithContextClassLoader() to support OSGi 2015-06-03 20:47:58 +12:00
rbygrave e7c40e9552 No effective change - update javadoc on annotations 2015-06-01 12:26:59 +12:00
rbygrave 0d92807308 No effective change - change test code to use Long as @Version on BasicDomain 2015-06-01 12:14:20 +12:00
rbygrave 16f7de30c6 No effective change - format code 2015-06-01 12:11:53 +12:00
rbygrave 3d250b0eca No effective change - format code, tidy 2015-05-30 21:42:50 +12:00
rbygrave 8403adac90 #298 - JndiDataSourceLookup assumes java: prefix 2015-05-30 20:54:51 +12:00
rbygrave c006aa3f48 No effective change - inline String messages 2015-05-30 20:54:12 +12:00
rbygrave dcb7054248 #303 - Change beginTransaction() to use REQUIRED scope by default 2015-05-30 20:10:56 +12:00
rbygrave 12833bc918 Bump pom version to 4.7.1-SNAPSHOT 2015-05-30 19:43:15 +12:00
rbygrave 71d805bc17 #302 - Postgres specific - Rename @ColumnHstore to @DbHstore (deprecate @ColumnHstore ) 2015-05-30 19:41:54 +12:00
rbygrave ab8704e2c9 Bump version in README 2015-05-29 21:52:43 +12:00
rbygrave 327e2baae7 No effective change - format only 2015-05-29 21:52:00 +12:00
rbygrave 6e89c551d5 [maven-release-plugin] prepare for next development iteration 2015-05-29 21:17:29 +12:00
138 changed files with 3724 additions and 2046 deletions
+6 -5
View File
@@ -1,9 +1,10 @@
[![Stories in Ready](https://badge.waffle.io/ebean-orm/avaje-ebeanorm.png?label=ready&title=Ready)](https://waffle.io/ebean-orm/avaje-ebeanorm)
* [![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
Ebean ORM
==============
Project Website
----------------
[http://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Website")
@@ -12,7 +13,7 @@ Maven Dependency
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>4.6.2</version>
<version>4.7.3</version>
</dependency>
+2 -2
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>4.6.3</version>
<version>4.8.1</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -152,7 +152,7 @@
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.27</version>
<version>5.1.36</version>
<scope>test</scope>
</dependency>
+4 -1
View File
@@ -315,7 +315,10 @@ public final class Ebean {
}
/**
* Start a new explicit transaction.
* Start a transaction with 'REQUIRED' semantics.
* <p>
* With REQUIRED semantics if an active transaction already exists that transaction will be used.
* </p>
* <p>
* The transaction is stored in a ThreadLocal variable and typically you only
* need to use the returned Transaction <em>IF</em> you wish to do things like
@@ -509,7 +509,10 @@ public interface EbeanServer {
Transaction createTransaction(TxIsolation isolation);
/**
* Start a new explicit transaction putting it into a ThreadLocal.
* Start a transaction with 'REQUIRED' semantics.
* <p>
* With REQUIRED semantics if an active transaction already exists that transaction will be used.
* </p>
* <p>
* The transaction is stored in a ThreadLocal variable and typically you only
* need to use the returned Transaction <em>IF</em> you wish to do things like
@@ -3,6 +3,7 @@ package com.avaje.ebean;
import com.avaje.ebean.common.SpiContainer;
import com.avaje.ebean.config.ContainerConfig;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
import javax.persistence.PersistenceException;
import java.lang.reflect.Constructor;
@@ -75,6 +76,32 @@ public class EbeanServerFactory {
return server;
}
/**
* Create using the ServerConfig additionally specifying a classLoader to use as the context class loader.
*/
public static synchronized EbeanServer createWithContextClassLoader(ServerConfig config, ClassLoader classLoader) {
ClassLoader currentContextLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(classLoader);
try {
return EbeanServerFactory.create(config);
} finally {
// set the currentContextLoader back
Thread.currentThread().setContextClassLoader(currentContextLoader);
}
}
/**
* Shutdown gracefully all EbeanServers cleaning up any resources as required.
* <p>
* This is typically invoked via JVM shutdown hook and not explicitly called.
* </p>
*/
public static synchronized void shutdown() {
ShutdownManager.shutdown();
}
private static EbeanServer createInternal(ServerConfig config) {
@@ -480,6 +480,13 @@ public final class RawSql implements Serializable {
this.queryHashCode = hc;
}
/**
* Return true if the property is mapped.
*/
public boolean contains(String property) {
return this.propertyColumnMap.containsKey(property);
}
/**
* Creates an immutable copy of this ColumnMapping.
*
@@ -55,4 +55,4 @@ public @interface CacheStrategy {
*/
String warmingQuery() default "";
};
}
@@ -44,4 +44,12 @@ public @interface CacheTuning {
* </p>
*/
int maxSecsToLive() default 0;
};
/**
* The frequency (in seconds) that cache trimming should occur.
* <p>
* This is a hint for cache implementations that use background cache trimming.
* </p>
*/
int trimFrequency() default 0;
}
@@ -6,8 +6,10 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* Deprecated - please use @DbHstore instead.
* @see DbHstore
*/
@Deprecated
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ColumnHstore {
@@ -18,14 +18,17 @@ import java.lang.annotation.Target;
* timestamp value after the insert has occurred. You need to fetch the entity
* back to get the inserted timestamp if you want to used it.
* </p>
*
* <pre class="code">
* &#064;Column(insertable = false, updateable = false)
* Timestamp cretimestamp;
* </pre>
*
* <h3>Example:</h3>
* <pre>{@code
*
* @CreatedTimestamp
* Timestamp whenCreated;
*
* }</pre>
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CreatedTimestamp {
};
}
@@ -0,0 +1,26 @@
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;
/**
* Used for mapping a Map<String,String> type property to Postgres HSTORE data type.
* <p>
* The Map property should have keys and values of type String.
* </p>
*
* <h3>Example:</h3>
* <pre>{@code
*
* @DbHstore
* Map<String, String> tags;
*
* }</pre>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DbHstore {
}
@@ -28,4 +28,4 @@ public @interface EmbeddedColumns {
*/
String columns() default "";
};
}
@@ -21,4 +21,4 @@ public @interface Encrypted {
* Used to specify the DB column length.
*/
int dbLength() default 0;
};
}
@@ -87,4 +87,4 @@ public @interface EnumMapping {
* The length of DB column if mapping to string values.
*/
int length() default 0;
};
}
@@ -8,19 +8,21 @@ import java.lang.annotation.Target;
/**
* Enables you to specify a value to use to persist for an enum value.
*
* <pre class="code">
* <pre>{@code
*
* public enum Status {
* &#064;EnumValue(&quot;N&quot;)
*
* @EnumValue("N")
* NEW,
*
* &#064;EnumValue(&quot;A&quot;)
* @EnumValue("A")
* ACTIVE,
*
* &#064;EnumValue(&quot;I&quot;)
* @EnumValue("I")
* INACTIVE,
* }
*
* </pre>
* }</pre>
* <p>
* This is an alternative to using the JPA standard approach or Ebean's
* {@link EnumMapping} annotation.
@@ -43,4 +45,4 @@ public @interface EnumValue {
* </p>
*/
String value();
};
}
@@ -12,17 +12,17 @@ import java.lang.annotation.Target;
@Target(ElementType.FIELD)
public @interface Expose {
/**
* If {@code true}, the field marked with this annotation is written out in the JSON while
* serializing. If {@code false}, the field marked with this annotation is skipped from the
* serialized output. Defaults to {@code true}.
*/
public boolean serialize() default true;
/**
* If {@code true}, the field marked with this annotation is written out in the JSON while
* serializing. If {@code false}, the field marked with this annotation is skipped from the
* serialized output. Defaults to {@code true}.
*/
boolean serialize() default true;
/**
* If {@code true}, the field marked with this annotation is deserialized from the JSON.
* If {@code false}, the field marked with this annotation is skipped during deserialization.
* Defaults to {@code true}.
*/
public boolean deserialize() default true;
/**
* If {@code true}, the field marked with this annotation is deserialized from the JSON.
* If {@code false}, the field marked with this annotation is skipped during deserialization.
* Defaults to {@code true}.
*/
boolean deserialize() default true;
}
@@ -39,30 +39,31 @@ import com.avaje.ebean.Query;
* queries - it needs to be explicitly included.
* </p>
*
* <pre class="code">
* <pre>{@code
*
* // find by Id
* Order o1 = Ebean.find(Order.class)
* .select(&quot;id, totalAmount&quot;)
* .select("id, totalAmount")
* .setId(1).findUnique();
*
* // find list ... using totalAmount in the where clause
* List&lt;Order&gt; list = Ebean.find(Order.class)
* .select(&quot;id, totalAmount&quot;)
* List<Order> list = Ebean.find(Order.class)
* .select("id, totalAmount")
* .where()
* .eq(&quot;status&quot;, Order.Status.NEW)
* .gt(&quot;totalAmount&quot;, 10)
* .eq("status", Order.Status.NEW)
* .gt("totalAmount", 10)
* .findList();
*
* // as a join from customer
* List&lt;Customer&gt; l0 = Ebean.find(Customer.class)
* .select(&quot;id, name&quot;)
* .join(&quot;orders&quot;, &quot;status, totalAmount&quot;)
* List<Customer> l0 = Ebean.find(Customer.class)
* .select("id, name")
* .fetch("orders", "status, totalAmount")
* .where()
* .gt(&quot;id&quot;, 0)
* .gt(&quot;orders.totalAmount&quot;, 10)
* .gt("id", 0)
* .gt("orders.totalAmount", 10)
* .findList();
*
* </pre>
* }</pre>
*/
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@@ -94,10 +95,12 @@ public @interface Formula {
* user.
* </p>
*
* <pre class="code">
* <pre>{@code
*
* join (select user_id, count(*) as topic_count from f_topic group by user_id) as _tc on _tc.user_id = ${ta}.id
* </pre>
*
* }</pre>
*/
String join() default "";
};
}
@@ -14,11 +14,11 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
public @interface Index {
/**
* Name of the index
*
* @return index name
*/
String value() default "";
/**
* Name of the index
*
* @return index name
*/
String value() default "";
}
@@ -34,4 +34,4 @@ public @interface NamedUpdate {
*/
boolean notifyCache() default true;
};
}
@@ -17,4 +17,4 @@ public @interface NamedUpdates {
*/
NamedUpdate[] value();
};
}
@@ -31,4 +31,4 @@ public @interface PrivateOwned {
*/
boolean cascadeRemove() default true;
};
}
@@ -21,4 +21,4 @@ public @interface Sql {
*/
SqlSelect[] select() default { @SqlSelect };
};
}
@@ -295,4 +295,4 @@ public @interface SqlSelect {
* Set this to true to have debug output when Ebean parses the sql-select.
*/
boolean debug() default false;
};
}
@@ -125,4 +125,4 @@ public @interface Transactional {
*/
Class<? extends Throwable>[] noRollbackFor() default {};
};
}
@@ -31,4 +31,4 @@ public @interface UpdateMode {
*/
boolean updateChangesOnly() default true;
};
}
@@ -13,4 +13,4 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
public @interface UpdatedTimestamp {
};
}
@@ -9,26 +9,29 @@ import java.lang.annotation.Target;
* Add an Literal to add to the where clause when a many property (List, Set or
* Map) is loaded or refreshed.
*
* <pre class="code">
* <pre>{@code
*
* // on a OneToMany property...
*
* &#064;OneToMany
* &#064;Where(clause = &quot;deleted='y'&quot;)
* List&lt;Topic&gt; topics;
* </pre>
* @OneToMany
* @Where(clause = "deleted='y'")
* List<Topic> topics;
*
* }</pre>
*
* <p>
* Note that you can include "${ta}" as a place holder for the table alias if
* you need to include the table alias in the clause.
* </p>
*
* <pre class="code">
* <pre>{@code
* // ... including the ${ta} table alias placeholder...
*
* &#064;OneToMany
* &#064;Where(clause = &quot;${ta}.deleted='y'&quot;)
* List&lt;Topic&gt; topics;
* </pre>
* @OneToMany
* @Where(clause = "${ta}.deleted='y'")
* List<Topic> topics;
*
* }</pre>
*
* <p>
* This will be added to the where clause when lazy loading the OneToMany
@@ -48,4 +51,4 @@ public @interface Where {
*/
String clause();
};
}
-6
View File
@@ -48,12 +48,6 @@ public interface ServerCache {
*/
Object put(Object id, Object value);
/**
* Put the value in the cache but only if a matching value is not already in
* the cache.
*/
Object putIfAbsent(Object id, Object value);
/**
* Remove a entry from the cache given its id.
*/
+24 -5
View File
@@ -10,6 +10,7 @@ public class ServerCacheOptions {
private int maxSize;
private int maxIdleSecs;
private int maxSecsToLive;
private int trimFrequency;
/**
* Construct with no set options.
@@ -25,15 +26,17 @@ public class ServerCacheOptions {
this.maxSize = cacheTuning.maxSize();
this.maxIdleSecs = cacheTuning.maxIdleSecs();
this.maxSecsToLive = cacheTuning.maxSecsToLive();
this.trimFrequency = cacheTuning.trimFrequency();
}
/**
* Create merging default options with the deployment specified ones.
*/
public ServerCacheOptions(ServerCacheOptions d) {
this.maxSize = d.getMaxSize();
this.maxIdleSecs = d.getMaxIdleSecs();
this.maxSecsToLive = d.getMaxIdleSecs();
public ServerCacheOptions(ServerCacheOptions defaults) {
this.maxSize = defaults.getMaxSize();
this.maxIdleSecs = defaults.getMaxIdleSecs();
this.maxSecsToLive = defaults.getMaxIdleSecs();
this.trimFrequency = defaults.getTrimFrequency();
}
/**
@@ -50,6 +53,9 @@ public class ServerCacheOptions {
if (maxSecsToLive == 0) {
maxSecsToLive = defaults.getMaxSecsToLive();
}
if (trimFrequency == 0) {
trimFrequency = defaults.getTrimFrequency();
}
}
/**
@@ -61,7 +67,7 @@ public class ServerCacheOptions {
copy.maxSize = maxSize;
copy.maxIdleSecs = maxIdleSecs;
copy.maxSecsToLive = maxSecsToLive;
copy.trimFrequency = trimFrequency;
return copy;
}
@@ -107,4 +113,17 @@ public class ServerCacheOptions {
this.maxSecsToLive = maxSecsToLive;
}
/**
* Return the trim frequency in seconds.
*/
public int getTrimFrequency() {
return trimFrequency;
}
/**
* Set the trim frequency in seconds.
*/
public void setTrimFrequency(int trimFrequency) {
this.trimFrequency = trimFrequency;
}
}
+175 -23
View File
@@ -5,9 +5,9 @@ package com.avaje.ebean.cache;
* <p>
* These can be monitored to review the effectiveness of a particular cache.
* </p>
*
* @author rbygrave
*
* <p>
* Depending on the cache implementation not all the statistics may be collected.
* </p>
*/
public class ServerCacheStatistics {
@@ -17,21 +17,63 @@ public class ServerCacheStatistics {
protected int size;
protected int hitCount;
protected long hitCount;
protected int missCount;
protected long missCount;
protected long insertCount;
protected long updateCount;
protected long removeCount;
protected long clearCount;
protected long evictionRunCount;
protected long evictionRunMicros;
protected long evictByIdle;
protected long evictByTTL;
protected long evictByLRU;
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(cacheName);
sb.append(" maxSize:").append(maxSize);
sb.append(" size:").append(size);
sb.append(" hitRatio:").append(getHitRatio());
sb.append(" hitCount:").append(hitCount);
sb.append(" missCount:").append(missCount);
sb.append(" maxSize:").append(maxSize);
sb.append(" hit:").append(hitCount);
sb.append(" miss:").append(missCount);
sb.append(" insert:").append(insertCount);
sb.append(" update:").append(updateCount);
sb.append(" remove:").append(removeCount);
sb.append(" clear:").append(clearCount);
sb.append(" evictByIdle:").append(evictByIdle);
sb.append(" evictByTTL:").append(evictByTTL);
sb.append(" evictByLRU:").append(evictByLRU);
sb.append(" evictionRunCount:").append(evictionRunCount);
sb.append(" evictionRunMicros:").append(evictionRunMicros);
return sb.toString();
}
/**
* Returns an int from 0 to 100 (percentage) for the hit ratio.
* <p>
* A hit ratio of 100 means every get request against the cache hits an entry.
* </p>
*/
public int getHitRatio() {
long totalCount = hitCount + missCount;
if (totalCount == 0) {
return 0;
} else {
return (int)(hitCount * 100 / totalCount);
}
}
/**
* Return the name of the cache.
*/
@@ -49,28 +91,28 @@ public class ServerCacheStatistics {
/**
* Return the hit count. The number of successful gets.
*/
public int getHitCount() {
public long getHitCount() {
return hitCount;
}
/**
* Set the hit count.
*/
public void setHitCount(int hitCount) {
public void setHitCount(long hitCount) {
this.hitCount = hitCount;
}
/**
* Return the miss count. The number of gets that returned null.
*/
public int getMissCount() {
public long getMissCount() {
return missCount;
}
/**
* Set the miss count.
*/
public void setMissCount(int missCount) {
public void setMissCount(long missCount) {
this.missCount = missCount;
}
@@ -107,18 +149,128 @@ public class ServerCacheStatistics {
}
/**
* Returns an int from 0 to 100 (percentage) for the hit ratio.
* <p>
* A hit ratio of 100 means every get request against the cache hits an entry.
* </p>
* Set the put insert count.
*/
public int getHitRatio() {
int totalCount = hitCount + missCount;
if (totalCount == 0) {
return 0;
} else {
return hitCount * 100 / totalCount;
}
public void setInsertCount(long insertCount) {
this.insertCount = insertCount;
}
/**
* Return the put insert count.
*/
public long getInsertCount() {
return insertCount;
}
/**
* Set the put update count.
*/
public void setUpdateCount(long updateCount) {
this.updateCount = updateCount;
}
/**
* Return the put update count.
*/
public long getUpdateCount() {
return updateCount;
}
/**
* Set the remove count.
*/
public void setRemoveCount(long removeCount) {
this.removeCount = removeCount;
}
/**
* Return the remove count.
*/
public long getRemoveCount() {
return removeCount;
}
/**
* Set the clear count.
*/
public void setClearCount(long clearCount) {
this.clearCount = clearCount;
}
/**
* Return the clear count.
*/
public long getClearCount() {
return clearCount;
}
/**
* Set the eviction run count.
*/
public void setEvictionRunCount(long evictCount) {
this.evictionRunCount = evictCount;
}
/**
* Return the eviction run count.
*/
public long getEvictionRunCount() {
return evictionRunCount;
}
/**
* Set the eviction run time in micros.
*/
public void setEvictionRunMicros(long evictionRunMicros) {
this.evictionRunMicros = evictionRunMicros;
}
/**
* Return the eviction run time in micros.
*/
public long getEvictionRunMicros() {
return evictionRunMicros;
}
/**
* Set the count of entries evicted due to idle time.
*/
public void setEvictByIdle(long evictByIdle) {
this.evictByIdle = evictByIdle;
}
/**
* Return the count of entries evicted due to idle time.
*/
public long getEvictByIdle() {
return evictByIdle;
}
/**
* Set the count of entries evicted due to time to live.
*/
public void setEvictByTTL(long evictByTTL) {
this.evictByTTL = evictByTTL;
}
/**
* Return the count of entries evicted due to time to live.
*/
public long getEvictByTTL() {
return evictByTTL;
}
/**
* Set the count of entries evicted due to time least recently used.
*/
public void setEvictByLRU(long evictByLRU) {
this.evictByLRU = evictByLRU;
}
/**
* Return the count of entries evicted due to time least recently used.
*/
public long getEvictByLRU() {
return evictByLRU;
}
}
@@ -231,6 +231,7 @@ public class ServerConfig {
private boolean uuidStoreAsBinary;
private List<BeanFindController> findControllers = new ArrayList<BeanFindController>();
private List<BeanPersistController> persistControllers = new ArrayList<BeanPersistController>();
private List<BeanPersistListener> persistListeners = new ArrayList<BeanPersistListener>();
private List<BeanQueryAdapter> queryAdapters = new ArrayList<BeanQueryAdapter>();
@@ -1540,6 +1541,14 @@ public class ServerConfig {
persistControllers.add(beanPersistController);
}
public List<BeanFindController> getFindControllers() {
return findControllers;
}
public void setFindControllers(List<BeanFindController> findControllers) {
this.findControllers = findControllers;
}
/**
* Return the BeanPersistController instances.
*/
@@ -1819,8 +1828,7 @@ public class ServerConfig {
String classNames = properties.get("classes", null);
if (classNames == null) {
return null;
return classes;
}
List<Class<?>> classes = new ArrayList<Class<?>>();
@@ -0,0 +1,54 @@
package com.avaje.ebean.event;
import com.avaje.ebean.bean.BeanCollection;
/**
* Used to override the finding implementation for a bean.
* <p>
* For beans that are not in a JDBC data source you can implement this handle
* bean finding. For example, read a log file building each entry as a bean and
* returning that.
* </p>
* <p>
* There are a number of internal BeanFinders in Ebean to return meta data from
* Ebean at runtime such as query execution statistics etc. See the beans in
* com.avaje.ebean.meta and finders in com.avaje.ebean.server.meta.
* </p>
*/
public interface BeanFindController {
/**
* Return true if this BeanPersistController should be registered for events
* on this entity type.
*/
boolean isRegisterFor(Class<?> cls);
/**
* Return true if this controller should intercept and process this find request.
*
* Return false to allow the default behavior to process the request.
*/
boolean isInterceptFind(BeanQueryRequest<?> request);
/**
* Find a bean using its id or unique predicate.
*/
<T> T find(BeanQueryRequest<T> request);
/**
* Return true if this controller should intercept and process this findMany request.
*
* Return false to allow the default behavior to process the request.
*/
boolean isInterceptFindMany(BeanQueryRequest<?> request);
/**
* Return a List, Set or Map for the given find request.
* <p>
* Note the returning object is cast to a List Set or Map so you do need to
* get the return type right.
* </p>
*/
<T> BeanCollection<T> findMany(BeanQueryRequest<T> request);
}
@@ -3,6 +3,8 @@ package com.avaje.ebean.event;
import com.avaje.ebean.bean.BeanCollection;
/**
* Deprecated in favor of using BeanFindController.
*
* Used to override the finding implementation for a bean.
* <p>
* For beans that are not in a JDBC data source you can implement this handle
@@ -14,6 +16,7 @@ import com.avaje.ebean.bean.BeanCollection;
* Ebean at runtime such as query execution statistics etc. See the beans in
* com.avaje.ebean.meta and finders in com.avaje.ebean.server.meta.
* </p>
* @deprecated
*/
public interface BeanFinder<T> {
@@ -21,6 +21,13 @@ public interface JsonContext {
*/
<T> T toBean(Class<T> rootType, String json) throws JsonIOException;
/**
* Convert json string input into a Bean of a specific type additionally using JsonReadOptions.
*
* @throws JsonIOException When IOException occurs
*/
<T> T toBean(Class<T> rootType, String json, JsonReadOptions options) throws JsonIOException;
/**
* Convert json reader input into a Bean of a specific type.
*
@@ -28,6 +35,13 @@ public interface JsonContext {
*/
<T> T toBean(Class<T> rootType, Reader json) throws JsonIOException;
/**
* Convert json reader input into a Bean of a specific type additionally using JsonReadOptions.
*
* @throws JsonIOException When IOException occurs
*/
<T> T toBean(Class<T> rootType, Reader json, JsonReadOptions options) throws JsonIOException;
/**
* Convert json parser input into a Bean of a specific type.
*
@@ -35,6 +49,13 @@ public interface JsonContext {
*/
<T> T toBean(Class<T> cls, JsonParser parser) throws JsonIOException;
/**
* Convert json parser input into a Bean of a specific type additionally using JsonReadOptions..
*
* @throws JsonIOException When IOException occurs
*/
<T> T toBean(Class<T> cls, JsonParser parser, JsonReadOptions options) throws JsonIOException;
/**
* Convert json string input into a list of beans of a specific type.
*
@@ -42,6 +63,13 @@ public interface JsonContext {
*/
<T> List<T> toList(Class<T> rootType, String json) throws JsonIOException;
/**
* Convert json string input into a list of beans of a specific type additionally using JsonReadOptions.
*
* @throws JsonIOException When IOException occurs
*/
<T> List<T> toList(Class<T> rootType, String json, JsonReadOptions options) throws JsonIOException;
/**
* Convert json reader input into a list of beans of a specific type.
*
@@ -49,12 +77,26 @@ public interface JsonContext {
*/
<T> List<T> toList(Class<T> rootType, Reader json) throws JsonIOException;
/**
* Convert json reader input into a list of beans of a specific type additionally using JsonReadOptions.
*
* @throws JsonIOException When IOException occurs
*/
<T> List<T> toList(Class<T> rootType, Reader json, JsonReadOptions options) throws JsonIOException;
/**
* Convert json parser input into a list of beans of a specific type.
*
* @throws JsonIOException When IOException occurs
*/
<T> List<T> toList(Class<T> cls, JsonParser src) throws JsonIOException;
<T> List<T> toList(Class<T> cls, JsonParser json) throws JsonIOException;
/**
* Convert json parser input into a list of beans of a specific type additionally using JsonReadOptions.
*
* @throws JsonIOException When IOException occurs
*/
<T> List<T> toList(Class<T> cls, JsonParser json, JsonReadOptions options) throws JsonIOException;
/**
* Use the genericType to determine if this should be converted into a List or
@@ -0,0 +1,30 @@
package com.avaje.ebean.text.json;
import java.util.Map;
/**
* Provides for custom handling of json content as it is read.
* <p>
* This visit method is called after all the known properties of the bean have
* been processed. Any JSON elements that could not be mapped to known bean
* properties are available in the unmapped Map.
* </p>
*
* @param <T> The type of entity bean
*/
public interface JsonReadBeanVisitor<T> {
/**
* Visit the bean that has just been processed.
* <p>
* This provides a method of customising the bean and processing any custom
* JSON content.
* </p>
*
* @param bean the bean being processed
* @param unmapped Map of any JSON elements that didn't map to known bean properties
*/
void visit(T bean, Map<String, Object> unmapped);
}
@@ -0,0 +1,47 @@
package com.avaje.ebean.text.json;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Provides the ability to customise the reading of JSON content.
* <p>
* You can register JsonReadBeanVisitors to customise the processing of the
* beans as they are processed and handle any custom JSON elements that
* could not be mapped to bean properties.
* </p>
*/
public class JsonReadOptions {
protected Map<String, JsonReadBeanVisitor<?>> visitorMap;
/**
* Default constructor.
*/
public JsonReadOptions() {
this.visitorMap = new LinkedHashMap<String, JsonReadBeanVisitor<?>>();
}
/**
* Return the map of JsonReadBeanVisitor's.
*/
public Map<String, JsonReadBeanVisitor<?>> getVisitorMap() {
return visitorMap;
}
/**
* Register a JsonReadBeanVisitor for the root level.
*/
public JsonReadOptions addRootVisitor(JsonReadBeanVisitor<?> visitor) {
return addVisitor(null, visitor);
}
/**
* Register a JsonReadBeanVisitor for a given path.
*/
public JsonReadOptions addVisitor(String path, JsonReadBeanVisitor<?> visitor) {
visitorMap.put(path, visitor);
return this;
}
}
@@ -11,16 +11,16 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
*/
public interface LoadBeanBuffer {
public int getBatchSize();
int getBatchSize();
public List<EntityBeanIntercept> getBatch();
List<EntityBeanIntercept> getBatch();
public BeanDescriptor<?> getBeanDescriptor();
BeanDescriptor<?> getBeanDescriptor();
public PersistenceContext getPersistenceContext();
PersistenceContext getPersistenceContext();
public String getFullPath();
String getFullPath();
public void configureQuery(SpiQuery<?> query, String lazyLoadProperty);
void configureQuery(SpiQuery<?> query, String lazyLoadProperty);
}
@@ -14,13 +14,13 @@ public interface LoadContext {
/**
* Return the minimum batch size when using QueryIterator with query joins.
*/
public int getSecondaryQueriesMinBatchSize(OrmQueryRequest<?> parentRequest, int defaultQueryBatch);
int getSecondaryQueriesMinBatchSize(OrmQueryRequest<?> parentRequest, int defaultQueryBatch);
/**
* Execute any secondary (+query) queries if there are any defined.
* @param parentRequest the originating query request
*/
public void executeSecondaryQueries(OrmQueryRequest<?> parentRequest);
void executeSecondaryQueries(OrmQueryRequest<?> parentRequest);
/**
* Register any secondary queries (+query or +lazy) with their
@@ -30,17 +30,17 @@ public interface LoadContext {
* defined query for +query and +lazy execution.
* </p>
*/
public void registerSecondaryQueries(SpiQuery<?> query);
void registerSecondaryQueries(SpiQuery<?> query);
/**
* Return the node for a given path which is used by autofetch profiling.
*/
public ObjectGraphNode getObjectGraphNode(String path);
ObjectGraphNode getObjectGraphNode(String path);
/**
* Return the persistence context used by this query and future lazy loading.
*/
public PersistenceContext getPersistenceContext();
PersistenceContext getPersistenceContext();
/**
* Set the persistence context used by this query and future lazy loading.
@@ -48,16 +48,16 @@ public interface LoadContext {
* Used by query iterator when processing large result sets.
* </p>
*/
public void resetPersistenceContext(PersistenceContext persistenceContext);
void resetPersistenceContext(PersistenceContext persistenceContext);
/**
* Register a Bean for lazy loading.
*/
public void register(String path, EntityBeanIntercept ebi);
void register(String path, EntityBeanIntercept ebi);
/**
* Register a collection for lazy loading.
*/
public void register(String path, BeanCollection<?> bc);
void register(String path, BeanCollection<?> bc);
}
@@ -13,20 +13,20 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
*/
public interface LoadManyBuffer {
public int getBatchSize();
int getBatchSize();
public List<BeanCollection<?>> getBatch();
List<BeanCollection<?>> getBatch();
public BeanPropertyAssocMany<?> getBeanProperty();
BeanPropertyAssocMany<?> getBeanProperty();
public ObjectGraphNode getObjectGraphNode();
ObjectGraphNode getObjectGraphNode();
public BeanDescriptor<?> getBeanDescriptor();
BeanDescriptor<?> getBeanDescriptor();
public PersistenceContext getPersistenceContext();
PersistenceContext getPersistenceContext();
public String getFullPath();
String getFullPath();
public void configureQuery(SpiQuery<?> query);
void configureQuery(SpiQuery<?> query);
}
@@ -14,5 +14,5 @@ public interface LoadSecondaryQuery {
/**
* Execute the secondary query with a given batch size.
*/
public void loadSecondaryQuery(OrmQueryRequest<?> parentRequest);
void loadSecondaryQuery(OrmQueryRequest<?> parentRequest);
}
@@ -10,5 +10,5 @@ public interface SpiBackgroundExecutor extends BackgroundExecutor {
/**
* Shutdown any associated thread pools.
*/
public void shutdown();
void shutdown();
}
@@ -4,7 +4,7 @@ import com.avaje.ebean.CallableSql;
public interface SpiCallableSql extends CallableSql {
public BindParams getBindParams();
BindParams getBindParams();
public TransactionEventTable getTransactionEventTable();
TransactionEventTable getTransactionEventTable();
}
@@ -9,6 +9,7 @@ import com.avaje.ebean.config.dbplatform.DatabasePlatform;
* author: Richard Vowles - http://gplus.to/RichardVowles
*/
public interface SpiEbeanPlugin {
/**
* initializes the plugin.
*
@@ -16,7 +17,10 @@ public interface SpiEbeanPlugin {
* @param dbPlatform - the database we are using - this is available from the server, but it is provided for convenience
* @param serverConfig - the configured server information. It allows access to pre-collected information (but not the collector PropertySource, yet)
*/
public void setup(SpiEbeanServer server, DatabasePlatform dbPlatform, ServerConfig serverConfig);
void setup(SpiEbeanServer server, DatabasePlatform dbPlatform, ServerConfig serverConfig);
public void execute(boolean online);
/**
* Execute the plugin (generate DDL for example).
*/
void execute(boolean online);
}
@@ -26,22 +26,22 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
/**
* For internal use, shutdown of the server invoked by JVM Shutdown.
*/
public void shutdownManaged();
void shutdownManaged();
/**
* Return true if query origins should be collected.
*/
public boolean isCollectQueryOrigins();
boolean isCollectQueryOrigins();
/**
* Return the server configuration.
*/
public ServerConfig getServerConfig();
ServerConfig getServerConfig();
/**
* Return the DatabasePlatform for this server.
*/
public DatabasePlatform getDatabasePlatform();
DatabasePlatform getDatabasePlatform();
/**
* Return a JDBC driver specific handler for batching.
@@ -49,7 +49,7 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
* Required for Oracle specific batch handling.
* </p>
*/
public PstmtBatch getPstmtBatch();
PstmtBatch getPstmtBatch();
/**
* Create an object to represent the current CallStack.
@@ -58,47 +58,47 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
* graph costing.
* </p>
*/
public CallStack createCallStack();
CallStack createCallStack();
/**
* Return the PersistenceContextScope to use defined at query or server level.
*/
public PersistenceContextScope getPersistenceContextScope(SpiQuery<?> query);
PersistenceContextScope getPersistenceContextScope(SpiQuery<?> query);
/**
* Return the DDL generator.
*/
public DdlGenerator getDdlGenerator();
DdlGenerator getDdlGenerator();
/**
* Return the AutoFetchListener.
*/
public AutoFetchManager getAutoFetchManager();
AutoFetchManager getAutoFetchManager();
/**
* Clear the query execution statistics.
*/
public void clearQueryStatistics();
void clearQueryStatistics();
/**
* Return all the descriptors.
*/
public List<BeanDescriptor<?>> getBeanDescriptors();
List<BeanDescriptor<?>> getBeanDescriptors();
/**
* Return the BeanDescriptor for a given type of bean.
*/
public <T> BeanDescriptor<T> getBeanDescriptor(Class<T> type);
<T> BeanDescriptor<T> getBeanDescriptor(Class<T> type);
/**
* Return BeanDescriptor using it's unique id.
*/
public BeanDescriptor<?> getBeanDescriptorById(String descriptorId);
BeanDescriptor<?> getBeanDescriptorById(String descriptorId);
/**
* Return BeanDescriptors mapped to this table.
*/
public List<BeanDescriptor<?>> getBeanDescriptors(String tableName);
List<BeanDescriptor<?>> getBeanDescriptors(String tableName);
/**
* Process committed changes from another framework.
@@ -108,7 +108,7 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
* this to maintain its cache and text indexes appropriately.
* </p>
*/
public void externalModification(TransactionEventTable event);
void externalModification(TransactionEventTable event);
/**
* Create a ServerTransaction.
@@ -116,44 +116,44 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
* To specify to use the default transaction isolation use a value of -1.
* </p>
*/
public SpiTransaction createServerTransaction(boolean isExplicit, int isolationLevel);
SpiTransaction createServerTransaction(boolean isExplicit, int isolationLevel);
/**
* Return the current transaction or null if there is no current transaction.
*/
public SpiTransaction getCurrentServerTransaction();
SpiTransaction getCurrentServerTransaction();
/**
* Create a ScopeTrans for a method for the given scope definition.
*/
public ScopeTrans createScopeTrans(TxScope txScope);
ScopeTrans createScopeTrans(TxScope txScope);
/**
* Create a ServerTransaction for query purposes.
*/
public SpiTransaction createQueryTransaction();
SpiTransaction createQueryTransaction();
/**
* An event from another server in the cluster used to notify local
* BeanListeners of remote inserts updates and deletes.
*/
public void remoteTransactionEvent(RemoteTransactionEvent event);
void remoteTransactionEvent(RemoteTransactionEvent event);
/**
* Create a query request object.
*/
public <T> SpiOrmQueryRequest<T> createQueryRequest(BeanDescriptor<T> desc, SpiQuery<T> q,
<T> SpiOrmQueryRequest<T> createQueryRequest(BeanDescriptor<T> desc, SpiQuery<T> q,
Transaction t);
/**
* Compile a query.
*/
public <T> CQuery<T> compileQuery(Query<T> query, Transaction t);
<T> CQuery<T> compileQuery(Query<T> query, Transaction t);
/**
* Return the queryEngine for this server.
*/
public CQueryEngine getQueryEngine();
CQueryEngine getQueryEngine();
/**
* Execute the findId's query but without copying the query.
@@ -162,37 +162,37 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
* the query has finished (if executing in a background thread).
* </p>
*/
public <T> List<Object> findIdsWithCopy(Query<T> query, Transaction t);
<T> List<Object> findIdsWithCopy(Query<T> query, Transaction t);
/**
* Execute the findRowCount query but without copying the query.
*/
public <T> int findRowCountWithCopy(Query<T> query, Transaction t);
<T> int findRowCountWithCopy(Query<T> query, Transaction t);
/**
* Load a batch of Associated One Beans.
*/
public void loadBean(LoadBeanRequest loadRequest);
void loadBean(LoadBeanRequest loadRequest);
/**
* Lazy load a batch of Many's.
*/
public void loadMany(LoadManyRequest loadRequest);
void loadMany(LoadManyRequest loadRequest);
/**
* Return the default batch size for lazy loading.
*/
public int getLazyLoadBatchSize();
int getLazyLoadBatchSize();
/**
* Return true if the type is known as an Entity or Xml type or a List Set or
* Map of known bean types.
*/
public boolean isSupportedType(java.lang.reflect.Type genericType);
boolean isSupportedType(java.lang.reflect.Type genericType);
/**
* Collect query statistics by ObjectGraphNode. Used for Lazy loading reporting.
*/
public void collectQueryStats(ObjectGraphNode objectGraphNode, long loadedBeanCount, long timeMicros);
void collectQueryStats(ObjectGraphNode objectGraphNode, long loadedBeanCount, long timeMicros);
}
@@ -16,7 +16,7 @@ public interface SpiExpression extends Expression {
* Predicates on Many properties require an extra independent join clause.
* </p>
*/
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins);
void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins);
/**
* Calculate a hash value used to identify a query for AutoFetch tuning.
@@ -25,7 +25,7 @@ public interface SpiExpression extends Expression {
* from an AutoFetch perspective and get different tuning.
* </p>
*/
public void queryAutoFetchHash(HashQueryPlanBuilder builder);
void queryAutoFetchHash(HashQueryPlanBuilder builder);
/**
* Calculate a hash value for the expression.
@@ -36,12 +36,12 @@ public interface SpiExpression extends Expression {
* case the query execution plan can be reused.
* </p>
*/
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder);
void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder);
/**
* Return the hash value for the values that will be bound.
*/
public int queryBindHash();
int queryBindHash();
/**
* Add some sql to the query.
@@ -56,7 +56,7 @@ public interface SpiExpression extends Expression {
* @param request
* the associated request.
*/
public void addSql(SpiExpressionRequest request);
void addSql(SpiExpressionRequest request);
/**
* Add the parameter values to be set against query. For each ? place holder
@@ -65,5 +65,5 @@ public interface SpiExpression extends Expression {
* @param request
* the associated request.
*/
public void addBindValues(SpiExpressionRequest request);
void addBindValues(SpiExpressionRequest request);
}
@@ -7,6 +7,6 @@ public interface SpiExpressionFactory extends ExpressionFactory {
/**
* Create another expression factory with a given sub path.
*/
public ExpressionFactory createExpressionFactory();
ExpressionFactory createExpressionFactory();
}
@@ -16,17 +16,17 @@ public interface SpiExpressionList<T> extends ExpressionList<T> {
/**
* Return the underlying list of expressions.
*/
public List<SpiExpression> getUnderlyingList();
List<SpiExpression> getUnderlyingList();
/**
* Return a copy of the ExpressionList with the path trimmed for filterMany() expressions.
*/
public SpiExpressionList<?> trimPath(int prefixTrim);
SpiExpressionList<?> trimPath(int prefixTrim);
/**
* Restore the ExpressionFactory after deserialisation.
*/
public void setExpressionFactory(ExpressionFactory expr);
void setExpressionFactory(ExpressionFactory expr);
/**
* Process "Many" properties populating ManyWhereJoins.
@@ -34,12 +34,12 @@ public interface SpiExpressionList<T> extends ExpressionList<T> {
* Predicates on Many properties require an extra independent join clause.
* </p>
*/
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoins);
void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoins);
/**
* Return true if this list is empty.
*/
public boolean isEmpty();
boolean isEmpty();
/**
* Concatenate the expression sql into a String.
@@ -48,7 +48,7 @@ public interface SpiExpressionList<T> extends ExpressionList<T> {
* with bind parameters.
* </p>
*/
public String buildSql(SpiExpressionRequest request);
String buildSql(SpiExpressionRequest request);
/**
* Combine the expression bind values into a list.
@@ -59,12 +59,12 @@ public interface SpiExpressionList<T> extends ExpressionList<T> {
*
* @return the list of all the bind values in order.
*/
public ArrayList<Object> buildBindValues(SpiExpressionRequest request);
ArrayList<Object> buildBindValues(SpiExpressionRequest request);
/**
* Calculate a hash based on the expressions but excluding the actual bind
* values.
*/
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder);
void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder);
}
@@ -13,45 +13,45 @@ public interface SpiExpressionRequest {
/**
* Parse the logical property name to the deployment name.
*/
public String parseDeploy(String logicalProp);
String parseDeploy(String logicalProp);
/**
* Return the bean descriptor for the root type.
*/
public BeanDescriptor<?> getBeanDescriptor();
BeanDescriptor<?> getBeanDescriptor();
/**
* Return the associated QueryRequest.
*/
public SpiOrmQueryRequest<?> getQueryRequest();
SpiOrmQueryRequest<?> getQueryRequest();
/**
* Append to the expression sql.
*/
public SpiExpressionRequest append(String sql);
SpiExpressionRequest append(String sql);
/**
* Add a bind value to this request.
*/
public void addBindValue(Object bindValue);
void addBindValue(Object bindValue);
/**
* Return the accumulated expression sql for all expressions in this request.
*/
public String getSql();
String getSql();
/**
* Return the ordered list of bind values for all expressions in this request.
*/
public ArrayList<Object> getBindValues();
ArrayList<Object> getBindValues();
/**
* Increments the parameter index and returns that value.
*/
public int nextParameter();
int nextParameter();
/**
* Append a DB Like clause.
*/
public void appendLike();
void appendLike();
}
@@ -10,68 +10,68 @@ import com.avaje.ebean.SqlQueryListener;
*/
public interface SpiSqlQuery extends SqlQuery {
/**
* Return the named or positioned parameters.
*/
public BindParams getBindParams();
/**
* Return the named or positioned parameters.
*/
BindParams getBindParams();
/**
* return the query.
*/
public String getQuery();
/**
* return the query.
*/
String getQuery();
/**
* Return the queryListener.
*/
public SqlQueryListener getListener();
/**
* Return the first row to fetch.
*/
public int getFirstRow();
/**
* Return the maximum number of rows to fetch.
*/
public int getMaxRows();
/**
* Return the number of rows after which background fetching occurs.
*/
public int getBackgroundFetchAfter();
/**
* Return the key property for maps.
*/
public String getMapKey();
/**
* Return the query timeout.
*/
public int getTimeout();
/**
* Return the queryListener.
*/
SqlQueryListener getListener();
/**
* Return the hint for Statement.setFetchSize().
*/
public int getBufferFetchSizeHint();
/**
* Return the first row to fetch.
*/
int getFirstRow();
/**
* Return true if this is a future fetch type query.
*/
public boolean isFutureFetch();
/**
* Return the maximum number of rows to fetch.
*/
int getMaxRows();
/**
* Set to true if this is a future fetch type query.
*/
public void setFutureFetch(boolean futureFetch);
/**
* Return the number of rows after which background fetching occurs.
*/
int getBackgroundFetchAfter();
/**
* Set the PreparedStatement for the purposes of supporting cancel.
*/
public void setPreparedStatement(PreparedStatement pstmt);
/**
* Return true if the query has been cancelled.
*/
public boolean isCancelled();
/**
* Return the key property for maps.
*/
String getMapKey();
/**
* Return the query timeout.
*/
int getTimeout();
/**
* Return the hint for Statement.setFetchSize().
*/
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();
}
@@ -4,7 +4,7 @@ import com.avaje.ebean.SqlUpdate;
public interface SpiSqlUpdate extends SqlUpdate {
public BindParams getBindParams();
BindParams getBindParams();
public void setGeneratedSql(String sql);
void setGeneratedSql(String sql);
}
@@ -20,45 +20,45 @@ public interface SpiTransaction extends Transaction {
/**
* End the transaction when had query only use.
*/
public void endQueryOnly();
void endQueryOnly();
/**
* Return the string prefix with the transactin id and label used in logging.
*/
public String getLogPrefix();
String getLogPrefix();
/**
* Return true if generated SQL and Bind values should be logged to the
* transaction log.
*/
public boolean isLogSql();
boolean isLogSql();
/**
* Return true if summary level events should be logged to the transaction
* log.
*/
public boolean isLogSummary();
boolean isLogSummary();
/**
* Log a message to the SQL logger.
*/
public void logSql(String msg);
void logSql(String msg);
/**
* Log a message to the SUMMARY logger.
*/
public void logSummary(String msg);
void logSummary(String msg);
/**
* Register a "Derived Relationship" (that requires an additional update).
*/
public void registerDerivedRelationship(DerivedRelationshipData assocBean);
void registerDerivedRelationship(DerivedRelationshipData assocBean);
/**
* Return the list of "Derived Relationships" that must be maintained after
* insert.
*/
public List<DerivedRelationshipData> getDerivedRelationship(Object bean);
List<DerivedRelationshipData> getDerivedRelationship(Object bean);
/**
* Add a deleting bean to the registered list.
@@ -66,22 +66,22 @@ public interface SpiTransaction extends Transaction {
* This is to handle bi-directional relationships where both sides Cascade.
* </p>
*/
public void registerDeleteBean(Integer hash);
void registerDeleteBean(Integer hash);
/**
* Unregister the hash of the bean.
*/
public void unregisterDeleteBean(Integer hash);
void unregisterDeleteBean(Integer hash);
/**
* Return true if this is a bean that has already been saved/deleted.
*/
public boolean isRegisteredDeleteBean(Integer hash);
boolean isRegisteredDeleteBean(Integer hash);
/**
* Unregister the persisted bean.
*/
public void unregisterBean(Object bean);
void unregisterBean(Object bean);
/**
* Return true if this is a bean that has already been persisted in the
@@ -91,13 +91,13 @@ public interface SpiTransaction extends Transaction {
* This will register the bean if it is not already.
* </p>
*/
public boolean isRegisteredBean(Object bean);
boolean isRegisteredBean(Object bean);
/**
* Returns a String used to identify the transaction. This id is used for
* Transaction logging.
*/
public String getId();
String getId();
/**
* Return the batchSize specifically set for this transaction or 0.
@@ -105,7 +105,7 @@ public interface SpiTransaction extends Transaction {
* Returning 0 implies to use the system wide default batch size.
* </p>
*/
public int getBatchSize();
int getBatchSize();
/**
* Modify and return the current 'depth' of the transaction.
@@ -119,18 +119,18 @@ public interface SpiTransaction extends Transaction {
* executed first during save.
* </p>
*/
public int depth(int diff);
int depth(int diff);
/**
* Return the current depth.
*/
public int depth();
int depth();
/**
* Return true if this transaction was created explicitly via
* <code>Ebean.beginTransaction()</code>.
*/
public boolean isExplicit();
boolean isExplicit();
/**
* Get the object that holds the event details.
@@ -140,29 +140,29 @@ public interface SpiTransaction extends Transaction {
* around the cluster (if you have a cluster).
* </p>
*/
public TransactionEvent getEvent();
TransactionEvent getEvent();
/**
* Whether persistCascade is on for save and delete.
*/
public boolean isPersistCascade();
boolean isPersistCascade();
/**
* Return true if this request should be batched. Conversely returns false
* if this request should be executed immediately.
*/
public boolean isBatchThisRequest(PersistRequest.Type type);
boolean isBatchThisRequest(PersistRequest.Type type);
/**
* Return the queue used to batch up persist requests.
*/
public BatchControl getBatchControl();
BatchControl getBatchControl();
/**
* Set the queue used to batch up persist requests. There should only be one
* PersistQueue set per transaction.
*/
public void setBatchControl(BatchControl control);
void setBatchControl(BatchControl control);
/**
* Return the persistence context associated with this transaction.
@@ -172,7 +172,7 @@ public interface SpiTransaction extends Transaction {
* behaviour.
* </p>
*/
public PersistenceContext getPersistenceContext();
PersistenceContext getPersistenceContext();
/**
* Set the persistence context to this transaction.
@@ -185,47 +185,46 @@ public interface SpiTransaction extends Transaction {
* PersistenceContext with multiple transactions.
* </p>
*/
public void setPersistenceContext(PersistenceContext context);
void setPersistenceContext(PersistenceContext context);
/**
* Return the underlying Connection for internal use.
* <p>
* If the connection is made public from Transaction and the user code calls
* If the connection is made from Transaction and the user code calls
* that method we can no longer trust the query only status of a
* Transaction.
* </p>
*/
public Connection getInternalConnection();
Connection getInternalConnection();
/**
* Return true if the manyToMany intersection should be persisted for this particular relationship direction.
*/
public boolean isSaveAssocManyIntersection(String intersectionTable, String beanName);
boolean isSaveAssocManyIntersection(String intersectionTable, String beanName);
/**
* Return true if batch mode got escalated for this request (and associated cascades).
*/
public boolean checkBatchEscalationOnCascade(PersistRequestBean<?> request);
boolean checkBatchEscalationOnCascade(PersistRequestBean<?> request);
/**
* If batch mode was turned on for the request then flush the batch.
*/
public void flushBatchOnCascade();
void flushBatchOnCascade();
/**
* Mark the transaction explicitly as not being query only.
*/
public void markNotQueryOnly();
void markNotQueryOnly();
/**
* Potentially escalate batch mode on saving or deleting a collection.
*/
public void checkBatchEscalationOnCollection();
void checkBatchEscalationOnCollection();
/**
* Flush batch if we escalated batch mode on saving or deleting a collection.
*/
public void flushBatchOnCollection();
void flushBatchOnCollection();
}
@@ -2,5 +2,5 @@ package com.avaje.ebeaninternal.api;
public interface SpiTransactionScopeManager {
public void replace(SpiTransaction t);
void replace(SpiTransaction t);
}
@@ -11,66 +11,66 @@ public interface SpiUpdate<T> extends Update<T> {
* The type of the update request.
*/
enum OrmUpdateType {
INSERT{
INSERT {
public String toString() {
return "Insert";
}
},
UPDATE{
UPDATE {
public String toString() {
return "Update";
}
},
DELETE{
},
DELETE {
public String toString() {
return "Delete";
}
},
UNKNOWN{
UNKNOWN {
public String toString() {
return "Unknown";
}
};
}
}
/**
* Return the type of bean being updated.
*/
public Class<?> getBeanType();
Class<?> getBeanType();
/**
* Return the type of this - insert, update or delete.
*/
public OrmUpdateType getOrmUpdateType();
OrmUpdateType getOrmUpdateType();
/**
* Return the name of the table being modified.
*/
public String getBaseTable();
String getBaseTable();
/**
* Return the update statement. This could be either sql or an orm update with bean types and property names.
*/
public String getUpdateStatement();
String getUpdateStatement();
/**
* Return the timeout in seconds.
*/
public int getTimeout();
int getTimeout();
/**
* Return true if the cache should be notified to invalidate objects.
*/
public boolean isNotifyCache();
boolean isNotifyCache();
/**
* Return the bind parameters.
*/
public BindParams getBindParams();
BindParams getBindParams();
/**
* Set the generated sql used.
*/
public void setGeneratedSql(String sql);
void setGeneratedSql(String sql);
}
@@ -16,8 +16,6 @@ import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
* <p>
* The plans are cached by the BeanDescriptors.
* </>
*
* @author rbygrave
*/
public interface SpiUpdatePlan {
@@ -28,48 +26,42 @@ public interface SpiUpdatePlan {
* deployment.
* </p>
*/
public boolean isEmptySetClause();
boolean isEmptySetClause();
/**
* Bind given the request and bean. The bean could be the oldValues bean
* when binding a update or delete where clause with ALL concurrency mode.
*/
public void bindSet(DmlHandler bind, EntityBean bean) throws SQLException;
void bindSet(DmlHandler bind, EntityBean bean) throws SQLException;
/**
* Return the time this plan was created.
*/
public long getTimeCreated();
long getTimeCreated();
/**
* Return the time this plan was last used.
*/
public Long getTimeLastUsed();
Long getTimeLastUsed();
/**
* Return the hash key for this plan.
*/
public Integer getKey();
Integer getKey();
/**
* Return the concurrency mode for this plan.
*/
public ConcurrencyMode getMode();
ConcurrencyMode getMode();
/**
* Return the update SQL statement.
*/
public String getSql();
String getSql();
/**
* Return the set of bindable update properties.
*/
public Bindable getSet();
// /**
// * Return the properties that where changed and should be included in the
// * update statement.
// */
// public Set<String> getProperties();
Bindable getSet();
}
@@ -65,10 +65,10 @@ public class TransactionEvent implements Serializable {
}
/**
* For BeanListeners the requests they are interested in.
* Return the list of PersistRequestBean's for this transaction.
*/
public TransactionEventBeans getEventBeans() {
return eventBeans;
public List<PersistRequestBean<?>> getPersistRequestBeans() {
return (eventBeans == null) ? null : eventBeans.getRequests();
}
public TransactionEventTable getEventTables() {
@@ -1,22 +1,18 @@
package com.avaje.ebeaninternal.server.cache;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.cache.ServerCache;
import com.avaje.ebean.cache.ServerCacheOptions;
import com.avaje.ebean.cache.ServerCacheStatistics;
import com.avaje.ebeaninternal.server.util.LongAdder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* The default cache implementation.
@@ -27,375 +23,392 @@ import org.slf4j.LoggerFactory;
*/
public class DefaultServerCache implements ServerCache {
private static final Logger logger = LoggerFactory.getLogger(DefaultServerCache.class);
protected static final Logger logger = LoggerFactory.getLogger(DefaultServerCache.class);
private static final CacheEntryComparator comparator = new CacheEntryComparator();
private final ConcurrentHashMap<Object, CacheEntry> map = new ConcurrentHashMap<Object, CacheEntry>();
private final AtomicInteger missCount = new AtomicInteger();
private final AtomicInteger removedHitCount = new AtomicInteger();
private final Object monitor = new Object();
private final String name;
private int maxSize;
private long trimFrequency;
private int maxIdleSecs;
private int maxSecsToLive;
public DefaultServerCache(String name, ServerCacheOptions options) {
this(name, options.getMaxSize(), options.getMaxIdleSecs(), options.getMaxSecsToLive());
}
public DefaultServerCache(String name, int maxSize, int maxIdleSecs, int maxSecsToLive) {
this.name = name;
this.maxSize = maxSize;
this.maxIdleSecs = maxIdleSecs;
this.maxSecsToLive = maxSecsToLive;
this.trimFrequency = 60;
}
public void init(EbeanServer server) {
TrimTask trim = new TrimTask();
BackgroundExecutor executor = server.getBackgroundExecutor();
executor.executePeriodically(trim, trimFrequency, TimeUnit.SECONDS);
}
public ServerCacheStatistics getStatistics(boolean reset) {
ServerCacheStatistics s = new ServerCacheStatistics();
s.setCacheName(name);
s.setMaxSize(maxSize);
// these counters won't necessarily be consistent with
// respect to each other as activity can occur while
// they are being calculated
int mc = reset ? missCount.getAndSet(0) : missCount.get();
int hc = getHitCount(reset);
int size = size();
s.setSize(size);
s.setHitCount(hc);
s.setMissCount(mc);
return s;
}
public int getHitRatio() {
int mc = missCount.get();
int hc = getHitCount(false);
int totalCount = hc + mc;
if (totalCount == 0){
return 0;
} else {
return hc * 100 / totalCount;
}
}
private int getHitCount(boolean reset) {
int hc = reset ? removedHitCount.getAndSet(0) : removedHitCount.get();
for (CacheEntry cacheEntry : map.values()) {
hc += cacheEntry.getHitCount(reset);
}
return hc;
}
/**
* Compare by last access time (for LRU eviction).
*/
public static final CompareByLastAccess BY_LAST_ACCESS = new CompareByLastAccess();
public ServerCacheOptions getOptions() {
synchronized (monitor) {
ServerCacheOptions o = new ServerCacheOptions();
o.setMaxIdleSecs(maxIdleSecs);
o.setMaxSize(maxSize);
o.setMaxSecsToLive(maxSecsToLive);
return o;
}
}
public void setOptions(ServerCacheOptions o) {
synchronized (monitor) {
maxIdleSecs = o.getMaxIdleSecs();
maxSize = o.getMaxSize();
maxSecsToLive = o.getMaxSecsToLive();
}
}
/**
* Return the max cache size.
*/
public int getMaxSize() {
return maxSize;
}
/**
* The underlying map (ConcurrentHashMap or similar)
*/
protected final Map<Object, CacheEntry> map;
/**
* Set the max cache size.
*/
public void setMaxSize(int maxSize) {
synchronized (monitor) {
this.maxSize = maxSize;
}
}
// LongAdder is a highly concurrent low latency counter (back ported from Java8)
protected final LongAdder missCount = new LongAdder();
protected final LongAdder hitCount = new LongAdder();
protected final LongAdder insertCount = new LongAdder();
protected final LongAdder updateCount = new LongAdder();
protected final LongAdder removeCount = new LongAdder();
protected final LongAdder clearCount = new LongAdder();
/**
* Return the max idle time.
*/
public long getMaxIdleSecs() {
return maxIdleSecs;
}
protected final LongAdder evictByIdle = new LongAdder();
protected final LongAdder evictByTTL = new LongAdder();
protected final LongAdder evictByLRU = new LongAdder();
protected final LongAdder evictCount = new LongAdder();
protected final LongAdder evictMicros = new LongAdder();
/**
* Set the max idle time.
*/
public void setMaxIdleSecs(int maxIdleSecs) {
synchronized (monitor) {
this.maxIdleSecs = maxIdleSecs;
}
}
protected final Object monitor = new Object();
/**
* Return the maximum time to live.
*/
public long getMaxSecsToLive() {
return maxSecsToLive;
}
protected final String name;
/**
* Set the maximum time to live.
*/
public void setMaxSecsToLive(int maxSecsToLive) {
synchronized (monitor) {
this.maxSecsToLive = maxSecsToLive;
}
}
/**
* Return the name of the cache.
*/
public String getName() {
return name;
}
protected int maxSize;
/**
* Clear the cache.
*/
public void clear() {
map.clear();
}
protected int trimFrequency;
/**
* Return a value from the cache.
*/
public Object get(Object key) {
CacheEntry entry = map.get(key);
if (entry == null){
missCount.incrementAndGet();
return null;
} else {
// get value incrementing last
// access time and hitCount
return entry.getValue();
}
}
protected int maxIdleSecs;
/**
* Put a value into the cache.
*/
public Object put(Object key, Object value) {
// put new entry with create time
CacheEntry entry = map.put(key, new CacheEntry(key, value));
if (entry == null){
return null;
} else {
int removedHits = entry.getHitCount(true);
removedHitCount.addAndGet(removedHits);
return entry.getValue();
}
}
protected int maxSecsToLive;
/**
* Put a value into the cache but only if absent.
*/
public Object putIfAbsent(Object key, Object value) {
CacheEntry entry = map.putIfAbsent(key, new CacheEntry(key, value));
if (entry == null){
return null;
} else {
return entry.getValue();
}
}
/**
* Construct using a ConcurrentHashMap and cache options.
*/
public DefaultServerCache(String name, ServerCacheOptions options) {
this(name, new ConcurrentHashMap<Object, CacheEntry>(), options);
}
/**
* Remove an entry from the cache.
*/
public Object remove(Object key) {
CacheEntry entry = map.remove(key);
if (entry == null){
return null;
} else {
int removedHits = entry.getHitCount(true);
removedHitCount.addAndGet(removedHits);
return entry.getValue();
}
}
/**
* Construct passing in name, map and base eviction controls as ServerCacheOptions.
*/
public DefaultServerCache(String name, Map<Object, CacheEntry> map, ServerCacheOptions options) {
this(name, map, options.getMaxSize(), options.getMaxIdleSecs(), options.getMaxSecsToLive(), options.getTrimFrequency());
}
/**
* Return the number of elements in the cache.
*/
public int size() {
return map.size();
}
/**
* Construct passing in name, map and base eviction controls.
*/
public DefaultServerCache(String name, Map<Object, CacheEntry> map, int maxSize, int maxIdleSecs, int maxSecsToLive, int trimFrequency) {
this.name = name;
this.map = map;
this.maxSize = maxSize;
this.maxIdleSecs = maxIdleSecs;
this.maxSecsToLive = maxSecsToLive;
this.trimFrequency = trimFrequency;
}
/**
* The task used to periodically trim the cache.
*/
private class TrimTask implements Runnable {
@Override
public void init(EbeanServer server) {
public void run() {
EvictionRunnable trim = new EvictionRunnable();
long startTime = System.currentTimeMillis();
if (logger.isTraceEnabled()){
logger.trace("trimming cache " + name);
}
int trimmedByIdle = 0;
int trimmedByTTL = 0;
int trimmedByLRU = 0;
// default to trimming the cache every 60 seconds
long trimFreqSecs = (trimFrequency == 0) ? 60 : trimFrequency;
boolean trimMaxSize = maxSize > 0 && maxSize < size();
BackgroundExecutor executor = server.getBackgroundExecutor();
executor.executePeriodically(trim, trimFreqSecs, TimeUnit.SECONDS);
}
ArrayList<CacheEntry> activeList = new ArrayList<CacheEntry>();
@Override
public ServerCacheStatistics getStatistics(boolean reset) {
long idleExpire = System.currentTimeMillis() - (maxIdleSecs*1000);
long ttlExpire = System.currentTimeMillis() - (maxSecsToLive*1000);
ServerCacheStatistics cacheStats = new ServerCacheStatistics();
cacheStats.setCacheName(name);
cacheStats.setMaxSize(maxSize);
Iterator<CacheEntry> it = map.values().iterator();
while (it.hasNext()) {
CacheEntry cacheEntry = it.next();
if (maxIdleSecs > 0 && idleExpire > cacheEntry.getLastAccessTime()) {
it.remove();
trimmedByIdle++;
// these counters won't necessarily be consistent with
// respect to each other as activity can occur while
// they are being calculated here but they should be good enough
// and we don't want to reduce concurrent use to make them consistent
long clear = reset ? clearCount.sumThenReset() : clearCount.sum();
long remove = reset ? removeCount.sumThenReset() : removeCount.sum();
long update = reset ? updateCount.sumThenReset() : updateCount.sum();
long insert = reset ? insertCount.sumThenReset() : insertCount.sum();
long miss = reset ? missCount.sumThenReset() : missCount.sum();
long hit = reset ? hitCount.sumThenReset() : hitCount.sum();
} else if (maxSecsToLive > 0 && ttlExpire > cacheEntry.getCreateTime()) {
it.remove();
trimmedByTTL++;
long evict = reset ? evictCount.sumThenReset() : evictCount.sum();
long evictTime = reset ? evictMicros.sumThenReset() : evictMicros.sum();
long evictIdle = reset ? evictByIdle.sumThenReset() : evictByIdle.sum();
long evictTTL = reset ? evictByTTL.sumThenReset() : evictByTTL.sum();
long evictLRU = reset ? evictByLRU.sumThenReset() : evictByLRU.sum();
} else if (trimMaxSize) {
activeList.add(cacheEntry);
}
}
int size = size();
if (trimMaxSize) {
trimmedByLRU = activeList.size() - maxSize;
cacheStats.setSize(size);
cacheStats.setHitCount(hit);
cacheStats.setMissCount(miss);
cacheStats.setInsertCount(insert);
cacheStats.setUpdateCount(update);
cacheStats.setRemoveCount(remove);
cacheStats.setClearCount(clear);
if (trimmedByLRU > 0) {
// sort into last access time ascending
Collections.sort(activeList, comparator);
for (int i = maxSize; i < activeList.size(); i++) {
// remove if still in the cache
map.remove(activeList.get(i).getKey());
}
}
}
long exeTime = System.currentTimeMillis() - startTime;
if (logger.isDebugEnabled()){
logger.debug("Executed trim of cache " + name + " in ["+exeTime
+"]millis idle[" + trimmedByIdle + "] timeToLive["
+ trimmedByTTL + "] accessTime["
+ trimmedByLRU + "]");
}
cacheStats.setEvictionRunCount(evict);
cacheStats.setEvictionRunMicros(evictTime);
cacheStats.setEvictByIdle(evictIdle);
cacheStats.setEvictByTTL(evictTTL);
cacheStats.setEvictByLRU(evictLRU);
}
return cacheStats;
}
}
@Override
public int getHitRatio() {
/**
* Comparator for sorting by last access time.
*/
private static class CacheEntryComparator implements Comparator<CacheEntry>, Serializable {
long mc = missCount.sum();
long hc = hitCount.sum();
private static final long serialVersionUID = 1L;
long totalCount = hc + mc;
if (totalCount == 0) {
return 0;
} else {
return (int) (hc * 100 / totalCount);
}
}
public int compare(CacheEntry o1, CacheEntry o2) {
return o1.getLastAccessLong().compareTo(o2.getLastAccessLong());
}
}
/**
* Wraps the values to additionally hold createTime and lastAccessTime.
*/
public static class CacheEntry {
/**
* 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;
}
}
private final Object key;
private final Object value;
private final long createTime;
private final AtomicInteger hitCount = new AtomicInteger();
private Long lastAccessTime;
/**
* Set the options controlling the cache
*/
@Override
public void setOptions(ServerCacheOptions options) {
synchronized (monitor) {
maxIdleSecs = options.getMaxIdleSecs();
maxSize = options.getMaxSize();
maxSecsToLive = options.getMaxSecsToLive();
}
}
public CacheEntry(Object key, Object value) {
this.key = key;
this.value = value;
this.createTime = System.currentTimeMillis();
this.lastAccessTime = Long.valueOf(createTime);
}
/**
* Return the name of the cache.
*/
public String getName() {
return name;
}
public Object getKey() {
return key;
}
/**
* Clear the cache.
*/
@Override
public void clear() {
clearCount.increment();
map.clear();
}
public Object getValue() {
// object assignment is atomic
hitCount.incrementAndGet();
this.lastAccessTime = Long.valueOf(System.currentTimeMillis());
return value;
}
/**
* Return a value from the cache.
*/
@Override
public Object get(Object key) {
public long getCreateTime() {
return createTime;
}
CacheEntry entry = map.get(key);
if (entry == null) {
missCount.increment();
return null;
public long getLastAccessTime() {
return lastAccessTime.longValue();
}
} else {
// Important that hitCount.increment() MUST be low latency under concurrent
// use hence must use LongAdder or better here
hitCount.increment();
return entry.getValue();
}
}
public Long getLastAccessLong() {
return lastAccessTime;
}
/**
* Put a value into the cache.
*/
@Override
public Object put(Object key, Object value) {
CacheEntry entry = map.put(key, new CacheEntry(key, value));
if (entry == null) {
insertCount.increment();
return null;
} else {
updateCount.increment();
return entry.getValue();
}
}
public int getHitCount(boolean reset) {
if (reset){
return hitCount.getAndSet(0);
/**
* Remove an entry from the cache.
*/
@Override
public Object remove(Object key) {
CacheEntry entry = map.remove(key);
if (entry == null) {
return null;
} else {
removeCount.increment();
return entry.getValue();
}
}
/**
* Return the number of elements in the cache.
*/
@Override
public int size() {
return map.size();
}
/**
* Return the size to trim to based on the max size.
* <p>
* This returns 90% of the max size.
* </p>
*/
protected int getTrimSize() {
return (maxSize * 90 / 100);
}
/**
* Run the eviction based on Idle time, Time to live and LRU last access.
*/
public void runEviction() {
long trimForMaxSize;
if (maxSize == 0) {
trimForMaxSize = 0;
} else {
trimForMaxSize = size() - maxSize;
}
if (maxIdleSecs == 0 && maxSecsToLive == 0 && trimForMaxSize < 0) {
// nothing to trim on this cache
return;
}
long startNanos = System.nanoTime();
long trimmedByIdle = 0;
long trimmedByTTL = 0;
long trimmedByLRU = 0;
ArrayList<CacheEntry> activeList = new ArrayList<CacheEntry>();
long idleExpire = System.currentTimeMillis() - (maxIdleSecs * 1000);
long ttlExpire = System.currentTimeMillis() - (maxSecsToLive * 1000);
Iterator<CacheEntry> it = map.values().iterator();
while (it.hasNext()) {
CacheEntry cacheEntry = it.next();
if (maxIdleSecs > 0 && idleExpire > cacheEntry.getLastAccessTime()) {
it.remove();
trimmedByIdle++;
} else if (maxSecsToLive > 0 && ttlExpire > cacheEntry.getCreateTime()) {
it.remove();
trimmedByTTL++;
} else if (trimForMaxSize > 0) {
activeList.add(cacheEntry);
}
}
if (trimForMaxSize > 0) {
trimmedByLRU = activeList.size() - maxSize;
if (trimmedByLRU > 0) {
// sort into last access time ascending
Collections.sort(activeList, BY_LAST_ACCESS);
int trimSize = getTrimSize();
for (int i = trimSize; i < activeList.size(); i++) {
// remove if still in the cache
map.remove(activeList.get(i).getKey());
}
}
}
long exeNanos = System.nanoTime() - startNanos;
long exeMicros = TimeUnit.MICROSECONDS.convert(exeNanos, TimeUnit.NANOSECONDS);
// increment the eviction statistics
evictMicros.add(exeMicros);
evictCount.increment();
evictByIdle.add(trimmedByIdle);
evictByTTL.add(trimmedByTTL);
evictByLRU.add(trimmedByLRU);
if (logger.isDebugEnabled()) {
logger.debug("Executed trim of cache {} in [{}]millis idle[{}] timeToLive[{}] accessTime[{}]"
, name, exeMicros, trimmedByIdle, trimmedByTTL, trimmedByLRU);
}
}
/**
* Runnable that calls the eviction routine.
*/
public class EvictionRunnable implements Runnable {
@Override
public void run() {
runEviction();
}
}
/**
* Comparator for sorting by last access time.
*/
public static class CompareByLastAccess implements Comparator<CacheEntry>, Serializable {
private static final long serialVersionUID = 1L;
public int compare(CacheEntry entry1, CacheEntry entry2) {
return Long.compare(entry1.getLastAccessTime(), entry2.getLastAccessTime());
}
}
/**
* Wraps the value to additionally hold createTime and lastAccessTime and hit counter.
*/
public static class CacheEntry {
private final Object key;
private final Object value;
private final long createTime;
private long lastAccessTime;
public CacheEntry(Object key, Object value) {
this.key = key;
this.value = value;
this.createTime = System.currentTimeMillis();
this.lastAccessTime = createTime;
}
/**
* Return the entry key.
*/
public Object getKey() {
return key;
}
/**
* Return the entry value.
*/
public Object getValue() {
// long assignment should be atomic these days (Ref Cliff Click)
lastAccessTime = System.currentTimeMillis();
return value;
}
/**
* Return the time the entry was created.
*/
public long getCreateTime() {
return createTime;
}
/**
* Return the time the entry was last accessed.
*/
public long getLastAccessTime() {
return lastAccessTime;
}
}
} else {
return hitCount.get();
}
}
public int getHitCount() {
return hitCount.get();
}
}
}
@@ -9,18 +9,13 @@ import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.Table;
import com.avaje.ebean.event.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.config.CompoundType;
import com.avaje.ebean.config.ScalarTypeConverter;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistListener;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.event.ServerConfigStartup;
import com.avaje.ebean.event.TransactionEventListener;
import com.avaje.ebeaninternal.server.type.ScalarType;
import com.avaje.ebeaninternal.server.util.ClassPathSearchMatcher;
@@ -47,15 +42,15 @@ public class BootupClasses implements ClassPathSearchMatcher {
private ArrayList<Class<?>> transactionEventListenerList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> beanFinderList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> beanListenerList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> beanFindControllerList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> beanQueryAdapterList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> beanListenerList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> serverConfigStartupList = new ArrayList<Class<?>>();
private ArrayList<ServerConfigStartup> serverConfigStartupInstances = new ArrayList<ServerConfigStartup>();
private List<BeanFindController> findControllerInstances = new ArrayList<BeanFindController>();
private List<BeanPersistController> persistControllerInstances = new ArrayList<BeanPersistController>();
private List<BeanPersistListener> persistListenerInstances = new ArrayList<BeanPersistListener>();
private List<BeanQueryAdapter> queryAdapterInstances = new ArrayList<BeanQueryAdapter>();
@@ -112,6 +107,19 @@ public class BootupClasses implements ClassPathSearchMatcher {
}
}
/**
* Add BeanFindController instances.
*/
public void addFindControllers(List<BeanFindController> findControllers) {
if (findControllers != null) {
for (BeanFindController c : findControllers) {
this.findControllerInstances.add(c);
// don't automatically instantiate
this.beanFindControllerList.remove(c.getClass());
}
}
}
/**
* Add TransactionEventListeners instances.
*/
@@ -161,6 +169,22 @@ public class BootupClasses implements ClassPathSearchMatcher {
return queryAdapterInstances;
}
public List<BeanFindController> getBeanFindControllers() {
// add class registered BeanFindController to the
// list of created instances
for (Class<?> cls : beanFindControllerList) {
try {
BeanFindController newInstance = (BeanFindController) cls.newInstance();
findControllerInstances.add(newInstance);
} catch (Exception e) {
String msg = "Error creating BeanPersistController " + cls;
logger.error(msg, e);
}
}
return findControllerInstances;
}
public List<BeanPersistListener> getBeanPersistListeners() {
// add class registered BeanPersistController to the
// already created instances
@@ -212,63 +236,63 @@ public class BootupClasses implements ClassPathSearchMatcher {
/**
* Return the list of Embeddable classes.
*/
public ArrayList<Class<?>> getEmbeddables() {
public List<Class<?>> getEmbeddables() {
return embeddableList;
}
/**
* Return the list of entity classes.
*/
public ArrayList<Class<?>> getEntities() {
public List<Class<?>> getEntities() {
return entityList;
}
/**
* Return the list of ScalarTypes found.
*/
public ArrayList<Class<?>> getScalarTypes() {
public List<Class<?>> getScalarTypes() {
return scalarTypeList;
}
/**
* Return the list of ScalarConverters found.
*/
public ArrayList<Class<?>> getScalarConverters() {
public List<Class<?>> getScalarConverters() {
return scalarConverterList;
}
/**
* Return the list of ScalarConverters found.
*/
public ArrayList<Class<?>> getCompoundTypes() {
public List<Class<?>> getCompoundTypes() {
return compoundTypeList;
}
/**
* Return the list of BeanControllers found.
*/
public ArrayList<Class<?>> getBeanControllers() {
public List<Class<?>> getBeanControllers() {
return beanControllerList;
}
/**
* Return the list of TransactionEventListeners found
*/
public ArrayList<Class<?>> getTransactionEventListenerList() {
public List<Class<?>> getTransactionEventListenerList() {
return transactionEventListenerList;
}
/**
* Return the list of BeanFinders found.
*/
public ArrayList<Class<?>> getBeanFinders() {
public List<Class<?>> getBeanFinders() {
return beanFinderList;
}
/**
* Return the list of BeanListeners found.
*/
public ArrayList<Class<?>> getBeanListeners() {
public List<Class<?>> getBeanListeners() {
return beanListenerList;
}
@@ -332,6 +356,11 @@ public class BootupClasses implements ClassPathSearchMatcher {
interesting = true;
}
if (BeanFindController.class.isAssignableFrom(cls)) {
beanFindControllerList.add(cls);
interesting = true;
}
if (BeanPersistListener.class.isAssignableFrom(cls)) {
beanListenerList.add(cls);
interesting = true;
@@ -80,7 +80,9 @@ public class DatabasePlatformFactory {
if (dbName.equals("mysql")) {
return new MySqlPlatform();
}
if (dbName.equals("h2")) {
return new H2Platform();
}
if (dbName.equals("sqlite")) {
return new SQLitePlatform();
}
@@ -219,7 +219,16 @@ public class DefaultContainer implements SpiContainer {
ServerCacheFactory cacheFactory = serverConfig.getServerCacheFactory();
if (cacheFactory == null) {
cacheFactory = new DefaultServerCacheFactory();
ServiceLoader<ServerCacheFactory> cacheFactories = ServiceLoader.load(ServerCacheFactory.class);
Iterator<ServerCacheFactory> iterator = cacheFactories.iterator();
if (iterator.hasNext()) {
// use the cacheFactory (via classpath service loader)
cacheFactory = iterator.next();
logger.debug("using ServerCacheFactory {}", cacheFactory.getClass());
} else {
// use the built in default
cacheFactory = new DefaultServerCacheFactory();
}
}
return new DefaultServerCacheManager(cacheFactory, beanOptions, queryOptions);
@@ -233,6 +242,7 @@ public class DefaultContainer implements SpiContainer {
BootupClasses bootupClasses = getBootupClasses1(serverConfig);
bootupClasses.addPersistControllers(serverConfig.getPersistControllers());
bootupClasses.addFindControllers(serverConfig.getFindControllers());
bootupClasses.addTransactionEventListeners(serverConfig.getTransactionEventListeners());
bootupClasses.addPersistListeners(serverConfig.getPersistListeners());
bootupClasses.addQueryAdapters(serverConfig.getQueryAdapters());
@@ -300,8 +310,7 @@ public class DefaultContainer implements SpiContainer {
if (config.getDataSourceJndiName() != null) {
ds = jndiDataSourceFactory.lookup(config.getDataSourceJndiName());
if (ds == null) {
String m = "JNDI lookup for DataSource " + config.getDataSourceJndiName() + " returned null.";
throw new PersistenceException(m);
throw new PersistenceException("JNDI lookup for DataSource " + config.getDataSourceJndiName() + " returned null.");
} else {
return ds;
}
@@ -309,14 +318,13 @@ public class DefaultContainer implements SpiContainer {
DataSourceConfig dsConfig = config.getDataSourceConfig();
if (dsConfig == null) {
String m = "No DataSourceConfig definded for " + config.getName();
String m = "No DataSourceConfig defined for " + config.getName();
throw new PersistenceException(m);
}
if (dsConfig.isOffline()) {
if (config.getDatabasePlatformName() == null) {
String m = "You MUST specify a DatabasePlatformName on ServerConfig when offline";
throw new PersistenceException(m);
throw new PersistenceException("You MUST specify a DatabasePlatformName on ServerConfig when offline");
}
return null;
}
@@ -198,19 +198,20 @@ public final class DefaultServer implements SpiEbeanServer {
this.beanLoader = new DefaultBeanLoader(this);
this.jsonContext = config.createJsonContext(this);
loadAndInitializePlugins(config);
// load normal plugins late and call setup on all
loadAndInitializePlugins(config.getServerConfig());
// Register with the JVM Shutdown hook
ShutdownManager.registerEbeanServer(this);
}
protected void loadAndInitializePlugins(InternalConfiguration config) {
protected void loadAndInitializePlugins(ServerConfig config) {
List<SpiEbeanPlugin> spiPlugins = new ArrayList<SpiEbeanPlugin>();
for (SpiEbeanPlugin plugin : ServiceLoader.load(SpiEbeanPlugin.class)) {
spiPlugins.add(plugin);
plugin.setup(this, this.getDatabasePlatform(), config.getServerConfig());
plugin.setup(this, this.getDatabasePlatform(), config);
if (plugin instanceof DdlGenerator) {
// backwards compatible
@@ -222,18 +223,24 @@ public final class DefaultServer implements SpiEbeanServer {
// ServiceLoader not finding ddlGenerator (typically OSGi)
ddlGenerator = new DdlGenerator();
spiPlugins.add(ddlGenerator);
ddlGenerator.setup(this, this.getDatabasePlatform(), config.getServerConfig());
ddlGenerator.setup(this, this.getDatabasePlatform(), config);
}
ebeanPlugins = Collections.unmodifiableList(spiPlugins);
}
/**
* Return the list of registered plugins.
*/
public List<SpiEbeanPlugin> getSpiEbeanPlugins() {
return ebeanPlugins;
}
/**
* Execute all the plugins with an online flag indicating the DB is up or not.
*/
public void executePlugins(boolean online) {
for(SpiEbeanPlugin plugin : ebeanPlugins) {
for (SpiEbeanPlugin plugin : ebeanPlugins) {
plugin.execute(online);
}
}
@@ -756,16 +763,16 @@ public final class DefaultServer implements SpiEbeanServer {
}
/**
* Start a transaction.
* Start a transaction with 'REQUIRED' semantics.
* <p>
* If a transaction already exists that transaction will be used.
* </p>
* <p>
* Note that the transaction is stored in a ThreadLocal variable.
* </p>
*/
public Transaction beginTransaction() {
// start an explicit transaction
SpiTransaction t = transactionManager.createTransaction(true, -1);
transactionScopeManager.set(t);
return t;
return beginTransaction(TxScope.required());
}
public Transaction beginTransaction(TxScope scope) {
@@ -182,7 +182,7 @@ public class InternalConfiguration {
}
public OrmQueryEngine createOrmQueryEngine() {
return new DefaultOrmQueryEngine(beanDescriptorManager, cQueryEngine);
return new DefaultOrmQueryEngine(cQueryEngine);
}
public Persister createPersister(SpiEbeanServer server) {
@@ -11,34 +11,27 @@ import javax.sql.DataSource;
*/
public class JndiDataSourceLookup {
private static final String DEFAULT_PREFIX = "java:comp/env/jdbc/";
public JndiDataSourceLookup() {
}
public JndiDataSourceLookup() {
}
/**
* Return the DataSource by JNDI lookup.
* <p>
* If name is null the 'default' dataSource is returned.
* </p>
*/
public DataSource lookup(String jndiName) {
/**
* Return the DataSource by JNDI lookup.
* <p>
* If name is null the 'default' dataSource is returned.
* </p>
*/
public DataSource lookup(String jndiName) {
try {
if (!jndiName.startsWith("java:")){
jndiName = DEFAULT_PREFIX + jndiName;
}
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup(jndiName);
if (ds == null) {
throw new PersistenceException("JNDI DataSource [" + jndiName + "] not found?");
}
return ds;
try {
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup(jndiName);
if (ds == null) {
throw new PersistenceException("JNDI DataSource [" + jndiName + "] not found?");
}
return ds;
} catch (NamingException ex) {
throw new PersistenceException(ex);
}
}
} catch (NamingException ex) {
throw new PersistenceException(ex);
}
}
}
@@ -9,7 +9,7 @@ import javax.persistence.PersistenceException;
import com.avaje.ebean.*;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebean.event.BeanFindController;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.HashQuery;
@@ -40,7 +40,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
private final SpiQuery<T> query;
private final BeanFinder<T> finder;
private final BeanFindController finder;
private final Boolean readOnly;
@@ -315,7 +315,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
/**
* Return a bean specific finder if one has been set.
*/
public BeanFinder<T> getBeanFinder() {
public BeanFindController getBeanFinder() {
return finder;
}
@@ -10,16 +10,12 @@ import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.config.EncryptKey;
import com.avaje.ebean.config.dbplatform.IdGenerator;
import com.avaje.ebean.config.dbplatform.IdType;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistListener;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.event.*;
import com.avaje.ebean.meta.MetaBeanInfo;
import com.avaje.ebean.meta.MetaQueryPlanStatistic;
import com.avaje.ebeaninternal.api.*;
import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD;
import com.avaje.ebeaninternal.server.cache.CachedBeanData;
import com.avaje.ebeaninternal.server.cache.CachedManyIds;
import com.avaje.ebeaninternal.server.core.CacheOptions;
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
import com.avaje.ebeaninternal.server.core.InternString;
@@ -28,17 +24,18 @@ import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyLists;
import com.avaje.ebeaninternal.server.el.*;
import com.avaje.ebeaninternal.server.persist.DmlUtil;
import com.avaje.ebeaninternal.server.query.CQueryPlan;
import com.avaje.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
import com.avaje.ebeaninternal.server.query.SplitName;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import com.avaje.ebeaninternal.server.text.json.ReadJson;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.type.TypeManager;
import com.avaje.ebeaninternal.util.SortByClause;
import com.avaje.ebeaninternal.util.SortByClause.Property;
import com.avaje.ebeaninternal.util.SortByClauseParser;
import com.fasterxml.jackson.core.JsonParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -153,7 +150,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
/**
* If set overrides the find implementation. Server side only.
*/
private final BeanFinder<T> beanFinder;
private final BeanFindController beanFinder;
/**
* The table joins for this bean.
@@ -634,7 +631,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
}
public boolean calculateUseCache(Boolean queryUseCache) {
return (queryUseCache != null) ? queryUseCache.booleanValue() : isBeanCaching();
return (queryUseCache != null) ? queryUseCache : isBeanCaching();
}
/**
@@ -1514,7 +1511,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
/**
* Return the beanFinder. Usually null unless overriding the finder.
*/
public BeanFinder<T> getBeanFinder() {
public BeanFindController getBeanFinder() {
return beanFinder;
}
@@ -1525,17 +1522,35 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return queryAdapter;
}
/**
* De-register the BeanPersistListener.
*/
public void deregister(BeanPersistListener listener) {
// volatile read...
BeanPersistListener currentListener = persistListener;
if (currentListener != null) {
if (currentListener instanceof ChainedBeanPersistListener) {
// remove it from the existing chain
persistListener = ((ChainedBeanPersistListener) currentListener).deregister(listener);
} else if (currentListener.equals(listener)) {
persistListener = null;
}
}
}
/**
* De-register the BeanPersistController.
*/
public void deregister(BeanPersistController controller) {
// volatile read...
BeanPersistController c = persistController;
if (c != null) {
if (c instanceof ChainedBeanPersistController) {
BeanPersistController currentController = persistController;
if (currentController != null) {
if (currentController instanceof ChainedBeanPersistController) {
// remove it from the existing chain
persistController = ((ChainedBeanPersistController) c).deregister(controller);
} else if (c.equals(controller)) {
persistController = ((ChainedBeanPersistController) currentController).deregister(controller);
} else if (currentController.equals(controller)) {
persistController = null;
}
}
@@ -1549,16 +1564,16 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
if (newPersistListener.isRegisterFor(beanType)) {
// volatile read...
BeanPersistListener currListener = persistListener;
if (currListener == null) {
BeanPersistListener currentListener = persistListener;
if (currentListener == null) {
persistListener = newPersistListener;
} else {
if (currListener instanceof ChainedBeanPersistListener) {
if (currentListener instanceof ChainedBeanPersistListener) {
// add it to the existing chain
persistListener = ((ChainedBeanPersistListener) currListener).register(newPersistListener);
persistListener = ((ChainedBeanPersistListener) currentListener).register(newPersistListener);
} else {
// build new chain of the 2
persistListener = new ChainedBeanPersistListener(currListener, newPersistListener);
persistListener = new ChainedBeanPersistListener(currentListener, newPersistListener);
}
}
}
@@ -1571,16 +1586,16 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
if (newController.isRegisterFor(beanType)) {
// volatile read...
BeanPersistController c = persistController;
if (c == null) {
BeanPersistController currentController = persistController;
if (currentController == null) {
persistController = newController;
} else {
if (c instanceof ChainedBeanPersistController) {
if (currentController instanceof ChainedBeanPersistController) {
// add it to the existing chain
persistController = ((ChainedBeanPersistController) c).register(newController);
persistController = ((ChainedBeanPersistController) currentController).register(newController);
} else {
// build new chain of the 2
persistController = new ChainedBeanPersistController(c, newController);
persistController = new ChainedBeanPersistController(currentController, newController);
}
}
}
@@ -1714,7 +1729,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
// not using Id generator so just base on isLoaded()
return !ebi.isLoaded();
}
if (!hasIdProperty(ebi)) {
if (!hasIdValue(ebi.getOwner())) {
// No Id property means it must be an insert
return true;
}
@@ -1729,9 +1744,9 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
public boolean hasIdPropertyOnly(EntityBeanIntercept ebi) {
return ebi.hasIdOnly(idPropertyIndex);
}
public boolean hasIdProperty(EntityBeanIntercept ebi) {
return idPropertyIndex > -1 && ebi.isLoadedProperty(idPropertyIndex);
public boolean hasIdValue(EntityBean bean) {
return (idProperty != null && !DmlUtil.isNullOrZero(idProperty.getValue(bean)));
}
public boolean hasVersionProperty(EntityBeanIntercept ebi) {
@@ -1893,11 +1908,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
jsonHelp.jsonWriteProperties(writeJson, bean);
}
public T jsonRead(JsonParser parser, String path) throws IOException {
return jsonHelp.jsonRead(parser, path);
public T jsonRead(ReadJson jsonRead, String path) throws IOException {
return jsonHelp.jsonRead(jsonRead, path);
}
protected T jsonReadObject(JsonParser parser, String path) throws IOException {
return jsonHelp.jsonReadObject(parser, path);
protected T jsonReadObject(ReadJson jsonRead, String path) throws IOException {
return jsonHelp.jsonReadObject(jsonRead, path);
}
}
@@ -303,7 +303,7 @@ public final class BeanDescriptorCacheHelp<T> {
// try looking up into the bean cache using the id
T cacheBean = beanCacheGetInternal(id, query.isReadOnly());
if (cacheBean != null) {
setupContext(cacheBean, query, getPersistenceContext(t));
setupContext(cacheBean, getPersistenceContext(t));
}
return cacheBean;
}
@@ -320,7 +320,7 @@ public final class BeanDescriptorCacheHelp<T> {
}
private boolean isNaturalKeyCaching(Boolean queryUseCache) {
return naturalKeyCache != null && (queryUseCache == null || queryUseCache.booleanValue());
return naturalKeyCache != null && (queryUseCache == null || queryUseCache);
}
private boolean isNaturalKey(String propName) {
@@ -332,7 +332,7 @@ public final class BeanDescriptorCacheHelp<T> {
/**
* For a bean built from the cache this sets up its persistence context for future lazy loading etc.
*/
private void setupContext(Object bean, SpiQuery<T> query, PersistenceContext context) {
private void setupContext(Object bean, PersistenceContext context) {
if (context == null) {
context = new DefaultPersistenceContext();
}
@@ -408,7 +408,7 @@ public final class BeanDescriptorCacheHelp<T> {
public T beanCacheGet(SpiQuery<T> query, PersistenceContext context) {
T bean = beanCacheGetInternal(query.getId(), query.isReadOnly());
if (bean != null) {
setupContext(bean, query, context);
setupContext(bean, context);
}
return bean;
}
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.text.json.EJson;
import com.avaje.ebeaninternal.server.text.json.ReadJson;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
import com.avaje.ebeaninternal.server.text.json.WriteJson.WriteBean;
import com.fasterxml.jackson.core.JsonParseException;
@@ -9,6 +10,8 @@ import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
public class BeanDescriptorJsonHelp<T> {
@@ -50,8 +53,9 @@ public class BeanDescriptorJsonHelp<T> {
@SuppressWarnings("unchecked")
public T jsonRead(JsonParser parser, String path) throws IOException {
public T jsonRead(ReadJson jsonRead, String path) throws IOException {
JsonParser parser = jsonRead.getParser();
if (parser.getCurrentToken() == JsonToken.START_OBJECT) {
// start object token read by Jackson already
} else {
@@ -66,7 +70,7 @@ public class BeanDescriptorJsonHelp<T> {
}
if (desc.inheritInfo == null) {
return jsonReadObject(parser, path);
return jsonReadObject(jsonRead, path);
}
// check for the discriminator value to determine the correct sub type
@@ -83,8 +87,8 @@ public class BeanDescriptorJsonHelp<T> {
BeanProperty property = desc.getBeanProperty(propName);
if (property != null) {
EntityBean bean = desc.createEntityBean();
property.jsonRead(parser, bean);
return jsonReadProperties(parser, bean);
property.jsonRead(jsonRead, bean);
return jsonReadProperties(jsonRead, bean, path);
}
String msg = "Error reading inheritance discriminator, expected property ["+discColumn+"] but got [" + propName + "] ?";
throw new JsonParseException(msg, parser.getCurrentLocation());
@@ -95,29 +99,39 @@ public class BeanDescriptorJsonHelp<T> {
// determine the sub type for this particular json object
InheritInfo localInheritInfo = inheritInfo.readType(discValue);
BeanDescriptor<?> localDescriptor = localInheritInfo.getBeanDescriptor();
return (T) localDescriptor.jsonReadObject(parser, path);
return (T) localDescriptor.jsonReadObject(jsonRead, path);
}
protected T jsonReadObject(JsonParser parser, String path) throws IOException {
protected T jsonReadObject(ReadJson readJson, String path) throws IOException {
EntityBean bean = desc.createEntityBean();
return jsonReadProperties(parser, bean);
return jsonReadProperties(readJson, bean, path);
}
@SuppressWarnings("unchecked")
protected T jsonReadProperties(JsonParser parser, EntityBean bean) throws IOException {
protected T jsonReadProperties(ReadJson readJson, EntityBean bean, String path) throws IOException {
if (path != null) {
readJson.pushPath(path);
}
// unmapped properties, send to JsonReadBeanVisitor later
Map<String,Object> unmappedProperties = null;
do {
JsonParser parser = readJson.getParser();
JsonToken event = parser.nextToken();
if (JsonToken.FIELD_NAME == event) {
String key = parser.getCurrentName();
BeanProperty p = desc.getBeanProperty(key);
if (p != null) {
p.jsonRead(parser, bean);
p.jsonRead(readJson, bean);
} else {
// unknown property ... read and ignore
EJson.parse(parser);
// read an unmapped property
if (unmappedProperties == null) {
unmappedProperties = new LinkedHashMap<String, Object>();
}
unmappedProperties.put(key, EJson.parse(parser));
}
} else if (JsonToken.END_OBJECT == event) {
@@ -128,6 +142,13 @@ public class BeanDescriptorJsonHelp<T> {
}
} while (true);
// visit JsonReadBeanVisitor (if registered for this path)
readJson.beanVisitor(bean, unmappedProperties);
if (path != null) {
readJson.popPath();
}
return (T)bean;
}
@@ -14,7 +14,6 @@ import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.DbIdentity;
import com.avaje.ebean.config.dbplatform.IdGenerator;
import com.avaje.ebean.config.dbplatform.IdType;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.TransactionEventTable;
import com.avaje.ebeaninternal.server.core.*;
@@ -161,8 +160,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
this.persistControllerManager = new PersistControllerManager(bootupClasses);
this.persistListenerManager = new PersistListenerManager(bootupClasses);
this.beanQueryAdapterManager = new BeanQueryAdapterManager(bootupClasses);
this.beanFinderManager = new DefaultBeanFinderManager();
this.beanFinderManager = new BeanFinderManager(bootupClasses);
this.reflectFactory = createReflectionFactory();
this.transientProperties = new TransientProperties();
@@ -407,7 +405,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
int qa = beanQueryAdapterManager.getRegisterCount();
int cc = persistControllerManager.getRegisterCount();
int lc = persistListenerManager.getRegisterCount();
int fc = beanFinderManager.createBeanFinders(bootupClasses.getBeanFinders());
int fc = beanFinderManager.getRegisterCount();
logger.debug("BeanPersistControllers[" + cc + "] BeanFinders[" + fc + "] BeanPersistListeners[" + lc + "] BeanQueryAdapters[" + qa + "]");
}
@@ -435,7 +433,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void readEmbeddedDeployment() {
ArrayList<Class<?>> embeddedClasses = bootupClasses.getEmbeddables();
List<Class<?>> embeddedClasses = bootupClasses.getEmbeddables();
for (int i = 0; i < embeddedClasses.size(); i++) {
Class<?> cls = embeddedClasses.get(i);
if (logger.isTraceEnabled()) {
@@ -456,7 +454,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void readEntityDeploymentInitial() {
ArrayList<Class<?>> entityClasses = bootupClasses.getEntities();
List<Class<?>> entityClasses = bootupClasses.getEntities();
for (Class<?> entityClass : entityClasses) {
DeployBeanInfo<?> info = createDeployBeanInfo(entityClass);
@@ -935,17 +933,10 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private <T> void setBeanControllerFinderListener(DeployBeanDescriptor<T> descriptor) {
Class<T> beanType = descriptor.getBeanType();
persistControllerManager.addPersistControllers(descriptor);
persistListenerManager.addPersistListeners(descriptor);
beanQueryAdapterManager.addQueryAdapter(descriptor);
BeanFinder<T> beanFinder = beanFinderManager.getBeanFinder(beanType);
if (beanFinder != null) {
descriptor.setBeanFinder(beanFinder);
logger.debug("BeanFinder on[" + descriptor.getFullName() + "] " + beanFinder.getClass().getName());
}
beanFinderManager.addFindControllers(descriptor);
}
/**
@@ -0,0 +1,45 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.event.BeanFindController;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebean.event.BeanQueryRequest;
/**
* Wraps the old BeanFinder to make it implement BeanFindController.
*/
public class BeanFinderAdapter implements BeanFindController {
final BeanFinder beanFinder;
public BeanFinderAdapter(BeanFinder beanFinder) {
this.beanFinder = beanFinder;
}
@Override
public boolean isRegisterFor(Class<?> cls) {
return false;
}
@Override
public boolean isInterceptFind(BeanQueryRequest<?> request) {
// always intercept to support old behavior
return true;
}
@Override
public <T> T find(BeanQueryRequest<T> request) {
return (T)beanFinder.find(request);
}
@Override
public <T> BeanCollection<T> findMany(BeanQueryRequest<T> request) {
return beanFinder.findMany(request);
}
@Override
public boolean isInterceptFindMany(BeanQueryRequest<?> request) {
// always intercept to support old behavior
return true;
}
}
@@ -1,26 +1,85 @@
package com.avaje.ebeaninternal.server.deploy;
import java.util.List;
import com.avaje.ebean.event.BeanFindController;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebeaninternal.server.core.BootupClasses;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Factory for controlling the construction of BeanFinders.
* Default implementation for BeanFinderFactory.
*/
public interface BeanFinderManager {
/**
* Return the number of beans with a registered finder.
*/
public int getRegisterCount();
public class BeanFinderManager {
/**
* Create the appropriate BeanController.
*/
public int createBeanFinders(List<Class<?>> finderClassList);
/**
* Return the BeanController for a given entity type.
*/
public <T> BeanFinder<T> getBeanFinder(Class<T> entityType);
final Logger logger = LoggerFactory.getLogger(BeanFinderManager.class);
/**
* Register of BeanFinder instances.
*/
final Map<Class<?>, BeanFinder<?>> registerFor = new HashMap<Class<?>, BeanFinder<?>>();
private final List<BeanFindController> list;
public BeanFinderManager(BootupClasses bootupClasses) {
list = bootupClasses.getBeanFindControllers();
List<Class<?>> beanFinders = bootupClasses.getBeanFinders();
for (Class<?> cls : beanFinders) {
Class<?> entityType = getEntityClass(cls);
try {
BeanFinder<?> beanFinder = (BeanFinder<?>) cls.newInstance();
registerFor.put(entityType, beanFinder);
} catch (Exception ex) {
throw new PersistenceException(ex);
}
}
}
public int getRegisterCount() {
return registerFor.size() + list.size();
}
/**
* Return the BeanPersistController for a given entity type.
*/
public void addFindControllers(DeployBeanDescriptor<?> deployDesc) {
for (int i = 0; i < list.size(); i++) {
BeanFindController c = list.get(i);
if (c.isRegisterFor(deployDesc.getBeanType())) {
logger.debug("BeanFindController on[" + deployDesc.getFullName() + "] " + c.getClass().getName());
deployDesc.setBeanFinder(c);
}
}
// support the deprecated BeanFinder
BeanFinder beanFinder = registerFor.get(deployDesc.getBeanType());
if (beanFinder != null) {
deployDesc.setBeanFinder(new BeanFinderAdapter(beanFinder));
logger.debug("BeanFinder on[" + deployDesc.getFullName() + "] " + beanFinder.getClass().getName());
}
}
/**
* Find the entity class given the controller class.
* <p>
* This uses reflection to find the generics parameter type.
* </p>
*/
private Class<?> getEntityClass(Class<?> controller) {
Class<?> cls = ParamTypeUtil.findParamType(controller, BeanFinder.class);
if (cls == null) {
String msg = "Could not determine the entity class (generics parameter type) from " + controller + " using reflection.";
throw new PersistenceException(msg);
}
return cls;
}
}
@@ -17,10 +17,10 @@ import com.avaje.ebeaninternal.server.properties.BeanPropertyGetter;
import com.avaje.ebeaninternal.server.properties.BeanPropertySetter;
import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
import com.avaje.ebeaninternal.server.text.json.ReadJson;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.type.ScalarType;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import javax.persistence.PersistenceException;
@@ -533,7 +533,7 @@ public class BeanProperty implements ElPropertyValue {
sqlBeanLoad.load(this);
}
public void buildSelectExpressionChain(String prefix, List<String> selectChain) {
public void buildRawSqlSelectChain(String prefix, List<String> selectChain) {
if (prefix == null) {
selectChain.add(name);
} else {
@@ -1076,7 +1076,7 @@ public class BeanProperty implements ElPropertyValue {
}
}
public void jsonRead(JsonParser ctx, EntityBean bean) throws IOException {
public void jsonRead(ReadJson ctx, EntityBean bean) throws IOException {
if (!jsonDeserialize) {
return;
}
@@ -1086,7 +1086,7 @@ public class BeanProperty implements ElPropertyValue {
setValue(bean, null);
} else {
// expect to read non-null json value
Object objValue = scalarType.jsonRead(ctx, event);
Object objValue = scalarType.jsonRead(ctx.getParser(), event);
setValue(bean, objValue);
}
}
@@ -13,8 +13,8 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
import com.avaje.ebeaninternal.server.text.json.ReadJson;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
import com.fasterxml.jackson.core.JsonParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -225,7 +225,8 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
return createElPropertyValue(propName, remainder, chain, propertyDeploy);
}
public void buildSelectExpressionChain(String prefix, List<String> selectChain) {
@Override
public void buildRawSqlSelectChain(String prefix, List<String> selectChain) {
// do not add to the selectChain at the top level of the Many bean
}
@@ -878,7 +879,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
}
}
public void jsonRead(JsonParser parser, EntityBean parentBean) throws IOException {
jsonHelp.jsonRead(parser, parentBean);
public void jsonRead(ReadJson readJson, EntityBean parentBean) throws IOException {
jsonHelp.jsonRead(readJson, parentBean);
}
}
@@ -5,6 +5,7 @@ import java.io.IOException;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.text.json.ReadJson;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
@@ -17,12 +18,13 @@ public class BeanPropertyAssocManyJsonHelp {
this.many = many;
}
public void jsonRead(JsonParser parser, EntityBean parentBean) throws IOException {
public void jsonRead(ReadJson readJson, EntityBean parentBean) throws IOException {
if (!this.many.jsonDeserialize) {
return;
}
JsonParser parser = readJson.getParser();
JsonToken event = parser.nextToken();
if (JsonToken.VALUE_NULL == event) {
return;
@@ -34,7 +36,7 @@ public class BeanPropertyAssocManyJsonHelp {
BeanCollection<?> collection = many.createEmpty(parentBean);
BeanCollectionAdd add = many.getBeanCollectionAdd(collection, null);
do {
EntityBean detailBean = (EntityBean) many.targetDescriptor.jsonRead(parser, many.name);
EntityBean detailBean = (EntityBean) many.targetDescriptor.jsonRead(readJson, many.name);
if (detailBean == null) {
// read the entire array
break;
@@ -1,13 +1,5 @@
package com.avaje.ebeaninternal.server.deploy;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.persistence.PersistenceException;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.Query;
import com.avaje.ebean.SqlUpdate;
@@ -25,8 +17,15 @@ import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.query.SplitName;
import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
import com.avaje.ebeaninternal.server.text.json.ReadJson;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
import com.fasterxml.jackson.core.JsonParser;
import javax.persistence.PersistenceException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Property mapped to a joined bean.
@@ -273,17 +272,27 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
return embeddedProps;
}
public void buildSelectExpressionChain(String prefix, List<String> selectChain) {
@Override
public void buildRawSqlSelectChain(String prefix, List<String> selectChain) {
prefix = SplitName.add(prefix, name);
if (!embedded){
targetIdBinder.buildSelectExpressionChain(prefix, selectChain);
InheritInfo inheritInfo = targetDescriptor.getInheritInfo();
if (inheritInfo != null) {
// expect the discriminator column to be included in order
// to determine the inheritance type so we add it to the
// selectChain (so that it takes a position in the resultSet)
String discriminatorColumn = inheritInfo.getDiscriminatorColumn();
String discProperty = prefix + "." + discriminatorColumn;
selectChain.add(discProperty);
}
targetIdBinder.buildRawSqlSelectChain(prefix, selectChain);
} else {
for (int i = 0; i < embeddedProps.length; i++) {
embeddedProps[i].buildSelectExpressionChain(prefix, selectChain);
}
embeddedProps[i].buildRawSqlSelectChain(prefix, selectChain);
}
}
}
@@ -843,9 +852,9 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
@Override
public void jsonRead(JsonParser parser, EntityBean bean) throws IOException {
public void jsonRead(ReadJson readJson, EntityBean bean) throws IOException {
if (jsonDeserialize && targetDescriptor != null) {
T assocBean = targetDescriptor.jsonRead(parser, name);
T assocBean = targetDescriptor.jsonRead(readJson, name);
setValue(bean, assocBean);
}
}
@@ -13,11 +13,11 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound;
import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
import com.avaje.ebeaninternal.server.text.json.ReadJson;
import com.avaje.ebeaninternal.server.text.json.WriteJson;
import com.avaje.ebeaninternal.server.type.CtCompoundProperty;
import com.avaje.ebeaninternal.server.type.CtCompoundPropertyElAdapter;
import com.avaje.ebeaninternal.server.type.CtCompoundType;
import com.fasterxml.jackson.core.JsonParser;
/**
* Property mapped to an Immutable Compound Value Object.
@@ -177,18 +177,17 @@ public class BeanPropertyCompound extends BeanProperty {
}
}
public void jsonRead(JsonParser ctx, EntityBean bean) throws IOException {
@Override
public void jsonRead(ReadJson readJson, EntityBean bean) throws IOException {
if (!jsonDeserialize) {
return;
}
Object value = EJson.parse(ctx);
if (value == null) {
Map<String, Object> map = EJson.parseObject(readJson.getParser());
if (map == null) {
setValue(bean, null);
} else {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) value;
Object objValue = compoundType.jsonConvert(map);
setValue(bean, objValue);
}
@@ -9,6 +9,11 @@ import java.util.Set;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistRequest;
/**
* Chains multiple BeanPersistController's together.
*
* Used when multiple BeanPersistController register for the same bean type.
*/
public class ChainedBeanPersistController implements BeanPersistController {
private static final Sorter SORTER = new Sorter();
@@ -35,7 +40,6 @@ public class ChainedBeanPersistController implements BeanPersistController {
/**
* Construct given the list of BeanPersistController's.
* @param list
*/
public ChainedBeanPersistController(List<BeanPersistController> list) {
this.list = list;
@@ -43,7 +47,14 @@ public class ChainedBeanPersistController implements BeanPersistController {
Arrays.sort(c, SORTER);
this.chain = c;
}
/**
* Return the size of the chain.
*/
protected int size() {
return chain.length;
}
/**
* Register a new BeanPersistController and return the resulting chain.
*/
@@ -22,6 +22,13 @@ public class ChainedBeanPersistListener implements BeanPersistListener {
this(addList(c1, c2));
}
/**
* Return the size of the chain.
*/
protected int size() {
return chain.length;
}
@Override
public boolean isRegisterFor(Class<?> cls) {
// never called
@@ -40,7 +47,6 @@ public class ChainedBeanPersistListener implements BeanPersistListener {
/**
* Construct given the list of BeanPersistController's.
* @param list
*/
public ChainedBeanPersistListener(List<BeanPersistListener> list) {
this.list = list;
@@ -1,61 +0,0 @@
package com.avaje.ebeaninternal.server.deploy;
import java.util.HashMap;
import java.util.List;
import javax.persistence.PersistenceException;
import com.avaje.ebean.event.BeanFinder;
/**
* Default implementation for BeanFinderFactory.
*/
public class DefaultBeanFinderManager implements BeanFinderManager {
HashMap<Class<?>, BeanFinder<?>> registerFor = new HashMap<Class<?>, BeanFinder<?>>();
public int createBeanFinders(List<Class<?>> finderClassList) {
for (Class<?> cls : finderClassList) {
Class<?> entityType = getEntityClass(cls);
try {
BeanFinder<?> beanFinder = (BeanFinder<?>) cls.newInstance();
registerFor.put(entityType, beanFinder);
} catch (Exception ex) {
throw new PersistenceException(ex);
}
}
return registerFor.size();
}
public int getRegisterCount() {
return registerFor.size();
}
/**
* Return the BeanFinder for a given entity type.
*/
@SuppressWarnings("unchecked")
public <T> BeanFinder<T> getBeanFinder(Class<T> entityType) {
return (BeanFinder<T>)registerFor.get(entityType);
}
/**
* Find the entity class given the controller class.
* <p>
* This uses reflection to find the generics parameter type.
* </p>
*/
private Class<?> getEntityClass(Class<?> controller){
Class<?> cls = ParamTypeUtil.findParamType(controller, BeanFinder.class);
if (cls == null){
String msg = "Could not determine the entity class (generics parameter type) from "+controller+" using reflection.";
throw new PersistenceException(msg);
}
return cls;
}
}
@@ -13,31 +13,31 @@ import org.slf4j.LoggerFactory;
*/
public class PersistControllerManager {
private static final Logger logger = LoggerFactory.getLogger(PersistControllerManager.class);
private static final Logger logger = LoggerFactory.getLogger(PersistControllerManager.class);
private final List<BeanPersistController> list;
public PersistControllerManager(BootupClasses bootupClasses){
list = bootupClasses.getBeanPersistControllers();
private final List<BeanPersistController> list;
public PersistControllerManager(BootupClasses bootupClasses) {
list = bootupClasses.getBeanPersistControllers();
}
public int getRegisterCount() {
return list.size();
}
/**
* Return the BeanPersistController for a given entity type.
*/
public void addPersistControllers(DeployBeanDescriptor<?> deployDesc) {
for (int i = 0; i < list.size(); i++) {
BeanPersistController c = list.get(i);
if (c.isRegisterFor(deployDesc.getBeanType())) {
logger.debug("BeanPersistController on[" + deployDesc.getFullName() + "] " + c.getClass().getName());
deployDesc.addPersistController(c);
}
}
public int getRegisterCount() {
return list.size();
}
/**
* Return the BeanPersistController for a given entity type.
*/
public void addPersistControllers(DeployBeanDescriptor<?> deployDesc){
for (int i = 0; i < list.size(); i++) {
BeanPersistController c = list.get(i);
if (c.isRegisterFor(deployDesc.getBeanType())){
logger.debug("BeanPersistController on[" + deployDesc.getFullName() + "] " + c.getClass().getName());
deployDesc.addPersistController(c);
}
}
}
}
}
@@ -1,13 +1,6 @@
package com.avaje.ebeaninternal.server.deploy.id;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
@@ -15,6 +8,12 @@ import com.avaje.ebeaninternal.server.deploy.DbReadContext;
import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
import com.avaje.ebeaninternal.server.type.DataBind;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
/**
* Binds id values to prepared statements.
*/
@@ -135,7 +134,7 @@ public interface IdBinder {
*/
public String getIdInValueExprDelete(int size);
public void buildSelectExpressionChain(String prefix, List<String> selectChain);
public void buildRawSqlSelectChain(String prefix, List<String> selectChain);
/**
* Read the id value from the result set and set it to the bean also returning
@@ -1,11 +1,5 @@
package com.avaje.ebeaninternal.server.deploy.id;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
@@ -17,6 +11,12 @@ import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
import com.avaje.ebeaninternal.server.query.SplitName;
import com.avaje.ebeaninternal.server.type.DataBind;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
/**
* Bind an Id that is an Embedded bean.
*/
@@ -115,12 +115,12 @@ public final class IdBinderEmbedded implements IdBinder {
return embIdProperty.getName();
}
public void buildSelectExpressionChain(String prefix, List<String> selectChain) {
public void buildRawSqlSelectChain(String prefix, List<String> selectChain) {
prefix = SplitName.add(prefix, embIdProperty.getName());
for (int i = 0; i < props.length; i++) {
props[i].buildSelectExpressionChain(prefix, selectChain);
props[i].buildRawSqlSelectChain(prefix, selectChain);
}
}
@@ -1,13 +1,6 @@
package com.avaje.ebeaninternal.server.deploy.id;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
@@ -15,6 +8,12 @@ import com.avaje.ebeaninternal.server.deploy.DbReadContext;
import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
import com.avaje.ebeaninternal.server.type.DataBind;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
/**
* For beans with no id properties AKA report type beans.
*/
@@ -38,7 +37,7 @@ public final class IdBinderEmpty implements IdBinder {
return pathPrefix;
}
public void buildSelectExpressionChain(String prefix, List<String> selectChain) {
public void buildRawSqlSelectChain(String prefix, List<String> selectChain) {
}
public int getPropertyCount() {
@@ -1,13 +1,6 @@
package com.avaje.ebeaninternal.server.deploy.id;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
import com.avaje.ebeaninternal.server.core.InternString;
@@ -17,6 +10,12 @@ import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.type.ScalarType;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
/**
* Bind an Id where the Id is made of a single property (not embedded).
*/
@@ -59,9 +58,9 @@ public final class IdBinderSimple implements IdBinder {
return sb.toString();
}
public void buildSelectExpressionChain(String prefix, List<String> selectChain) {
public void buildRawSqlSelectChain(String prefix, List<String> selectChain) {
idProperty.buildSelectExpressionChain(prefix, selectChain);
idProperty.buildRawSqlSelectChain(prefix, selectChain);
}
/**
@@ -4,14 +4,10 @@ import com.avaje.ebean.annotation.ConcurrencyMode;
import com.avaje.ebean.config.TableName;
import com.avaje.ebean.config.dbplatform.IdGenerator;
import com.avaje.ebean.config.dbplatform.IdType;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistListener;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.event.*;
import com.avaje.ebeaninternal.server.core.CacheOptions;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import com.avaje.ebeaninternal.server.deploy.*;
import com.avaje.ebeaninternal.server.properties.BeanPropertyInfo;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
@@ -109,7 +105,7 @@ public class DeployBeanDescriptor<T> {
/**
* If set overrides the find implementation. Server side only.
*/
private BeanFinder<T> beanFinder;
private BeanFindController beanFinder;
/**
* The table joins for this bean. Server side only.
@@ -338,7 +334,7 @@ public class DeployBeanDescriptor<T> {
/**
* Return the beanFinder. Usually null unless overriding the finder.
*/
public BeanFinder<T> getBeanFinder() {
public BeanFindController getBeanFinder() {
return beanFinder;
}
@@ -346,7 +342,7 @@ public class DeployBeanDescriptor<T> {
* Set the BeanFinder to use for beans of this type. This is set to override
* the finding from the default.
*/
public void setBeanFinder(BeanFinder<T> beanFinder) {
public void setBeanFinder(BeanFindController beanFinder) {
this.beanFinder = beanFinder;
}
@@ -146,6 +146,9 @@ public class AnnotationFields extends AnnotationParser {
if (get(prop, ColumnHstore.class) != null) {
util.setDbHstore(prop);
}
if (get(prop, DbHstore.class) != null) {
util.setDbHstore(prop);
}
Formula formula = get(prop, Formula.class);
if (formula != null) {
@@ -1,6 +1,7 @@
package com.avaje.ebeaninternal.server.deploy.parse;
import com.avaje.ebean.annotation.ColumnHstore;
import com.avaje.ebean.annotation.DbHstore;
import com.avaje.ebean.annotation.DbJson;
import com.avaje.ebean.annotation.DbJsonB;
import com.avaje.ebeaninternal.server.deploy.DetermineManyType;
@@ -292,6 +293,7 @@ public class DeployCreateProperties {
private boolean isMappedType(Field field) {
return (field.getAnnotation(DbJson.class) != null)
|| (field.getAnnotation(DbJsonB.class) != null)
|| (field.getAnnotation(DbHstore.class) != null)
|| (field.getAnnotation(ColumnHstore.class) != null);
}
File diff suppressed because it is too large Load Diff
@@ -265,7 +265,7 @@ public class CQueryBuilder implements Constants {
propertyName = SplitName.parent(propertyName);
} else if (beanProperty instanceof BeanPropertyAssocOne<?>) {
String msg = "Column [" + column.getDbColumn() + "] mapped to complex Property[" + propertyName + "]";
msg += ". It should be mapped to a simple property (proably the Id property). ";
msg += ". It should be mapped to a simple property (probably the Id property). ";
throw new PersistenceException(msg);
}
if (propertyName != null) {
@@ -284,8 +284,16 @@ public class CQueryBuilder implements Constants {
detail.getChunk(path, true).setDefaultProperties(null, props);
}
// check if @Id property included in RawSql
boolean rawNoId = true;
BeanProperty idProperty = descriptor.getIdProperty();
if (idProperty != null && columnMapping.contains(idProperty.getName())) {
// contains the @Id property for the root level bean
rawNoId = false;
}
// build SqlTree based on OrmQueryDetail of the RawSql
return new SqlTreeBuilder(request, predicates, detail).build();
return new SqlTreeBuilder(request, predicates, detail, rawNoId).build();
}
private SqlLimitResponse buildSql(String selectClause, OrmQueryRequest<?> request, CQueryPredicates predicates, SqlTree select) {
@@ -31,9 +31,11 @@ public class CQueryPlanRawSql extends CQueryPlan {
private int[] createIndexPositions(OrmQueryRequest<?> request, SqlTree sqlTree) {
List<String> chain = sqlTree.buildSelectExpressionChain();
List<String> chain = sqlTree.buildRawSqlSelectChain();
ColumnMapping columnMapping = request.getQuery().getRawSql().getColumnMapping();
// if the top level bean has inheritance expect first column
// to be the discriminator type column (and use offset 1)
InheritInfo inheritInfo = request.getBeanDescriptor().getInheritInfo();
boolean addDiscriminator = inheritInfo != null;
int offset = addDiscriminator ? 1 : 0;
@@ -43,12 +45,25 @@ public class CQueryPlanRawSql extends CQueryPlan {
// discriminator column must always be first in the query
indexPositions[0] = 1;
}
// set the resultSet index positions for the property expressions
for (int i = 0; i < chain.size(); i++) {
String expr = chain.get(i);
int indexPos = 1 + columnMapping.getIndexPosition(expr);
indexPositions[i + offset] = indexPos;
}
// check and handle the case where a discriminator column for
// an associated bean is in the raw SQL but is mapped columnIgnore
for (int i = 0; i <indexPositions.length ; i++) {
if (indexPositions[i] == 0) {
if (i < indexPositions.length) {
// expect discriminator column to immediately proceed id column
indexPositions[i] = indexPositions[i+1] - 1;
}
}
}
return indexPositions;
}
}
@@ -1,18 +1,17 @@
package com.avaje.ebeaninternal.server.query;
import java.util.Collection;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebean.event.BeanFindController;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.OrmQueryEngine;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import java.util.Collection;
/**
* Main Finder implementation.
@@ -27,7 +26,7 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine {
/**
* Create the Finder.
*/
public DefaultOrmQueryEngine(BeanDescriptorManager descMgr, CQueryEngine queryEngine) {
public DefaultOrmQueryEngine(CQueryEngine queryEngine) {
this.queryEngine = queryEngine;
}
@@ -70,11 +69,11 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine {
flushJdbcBatchOnQuery(request);
BeanFinder<T> finder = request.getBeanFinder();
BeanFindController finder = request.getBeanFinder();
BeanCollection<T> result;
if (finder != null) {
// this bean type has its own specific finder
if (finder != null && finder.isInterceptFindMany(request)) {
// intercept this request
result = finder.findMany(request);
} else {
result = queryEngine.findMany(request);
@@ -106,10 +105,10 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine {
flushJdbcBatchOnQuery(request);
BeanFinder<T> finder = request.getBeanFinder();
BeanFindController finder = request.getBeanFinder();
T result;
if (finder != null) {
if (finder != null && finder.isInterceptFind(request)) {
result = finder.find(request);
} else {
result = queryEngine.find(request);
@@ -1,13 +1,13 @@
package com.avaje.ebeaninternal.server.query;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* Represents the SELECT clause part of the SQL query.
*/
@@ -83,9 +83,9 @@ public class SqlTree {
/**
* Build a select expression chain for RawSql.
*/
public List<String> buildSelectExpressionChain() {
public List<String> buildRawSqlSelectChain() {
ArrayList<String> list = new ArrayList<String>();
rootNode.buildSelectExpressionChain(list);
rootNode.buildRawSqlSelectChain(list);
return list;
}
@@ -66,15 +66,21 @@ public class SqlTreeBuilder {
private final boolean rawSql;
/**
* rawNoId true if the RawSql does not include the @Id property
*/
private final boolean rawNoId;
private SqlTreeNode rootNode;
/**
* Construct for RawSql query.
*/
public SqlTreeBuilder(OrmQueryRequest<?> request, CQueryPredicates predicates, OrmQueryDetail queryDetail) {
public SqlTreeBuilder(OrmQueryRequest<?> request, CQueryPredicates predicates, OrmQueryDetail queryDetail, boolean rawNoId) {
this.rawSql = true;
this.desc = request.getBeanDescriptor();
this.rawNoId = rawNoId;
this.query = null;
this.subQuery = false;
this.queryDetail = queryDetail;
@@ -95,6 +101,7 @@ public class SqlTreeBuilder {
OrmQueryRequest<?> request, CQueryPredicates predicates) {
this.rawSql = false;
this.rawNoId = false;
this.desc = request.getBeanDescriptor();
this.query = request.getQuery();
@@ -257,7 +264,7 @@ public class SqlTreeBuilder {
// Optional many property for lazy loading query
BeanPropertyAssocMany<?> lazyLoadMany = (query == null) ? null : query.getLazyLoadForParentsProperty();
boolean withId = !subQuery && (query == null || !query.isDistinct());
boolean withId = !rawNoId && !subQuery && (query == null || !query.isDistinct());
return new SqlTreeNodeRoot(desc, props, myList, withId, includeJoin, lazyLoadMany);
} else if (prop instanceof BeanPropertyAssocMany<?>) {
@@ -1,17 +1,20 @@
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import java.util.List;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
import java.sql.SQLException;
import java.util.List;
public interface SqlTreeNode {
String COMMA = ", ";
void buildSelectExpressionChain(List<String> selectChain);
/**
* Build the select chain for a RawSql query.
*/
void buildRawSqlSelectChain(List<String> selectChain);
/**
* Append the required column information to the SELECT part of the sql
@@ -137,18 +137,18 @@ public class SqlTreeNodeBean implements SqlTreeNode {
}
}
public void buildSelectExpressionChain(List<String> selectChain) {
public void buildRawSqlSelectChain(List<String> selectChain) {
if (readId) {
idBinder.buildSelectExpressionChain(prefix, selectChain);
idBinder.buildRawSqlSelectChain(prefix, selectChain);
}
for (int i = 0, x = properties.length; i < x; i++) {
properties[i].buildSelectExpressionChain(prefix, selectChain);
properties[i].buildRawSqlSelectChain(prefix, selectChain);
}
// recursively continue reading...
for (int i = 0; i < children.length; i++) {
// read each child... and let them set their
// values back to this localBean
children[i].buildSelectExpressionChain(selectChain);
children[i].buildRawSqlSelectChain(selectChain);
}
}
@@ -276,7 +276,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
if (Mode.LAZYLOAD_BEAN.equals(queryMode)) {
// Lazy Load does not reset the dirty state
ebi.setLoadedLazy();
} else {
} else if (readId) {
// normal bean loading
ebi.setLoaded();
}
@@ -1,9 +1,5 @@
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
@@ -11,6 +7,10 @@ import com.avaje.ebeaninternal.server.deploy.DbReadContext;
import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* The purpose is to add an extra join to the query.
* <p>
@@ -35,7 +35,8 @@ public class SqlTreeNodeExtraJoin implements SqlTreeNode {
this.manyJoin = assocBeanProperty instanceof BeanPropertyAssocMany<?>;
}
public void buildSelectExpressionChain(List<String> selectChain) {
@Override
public void buildRawSqlSelectChain(List<String> selectChain) {
// nothing to add
}
@@ -49,7 +50,6 @@ public class SqlTreeNodeExtraJoin implements SqlTreeNode {
return manyJoin;
}
public String getName() {
return prefix;
}
@@ -1,9 +1,5 @@
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
@@ -12,6 +8,9 @@ import com.avaje.ebeaninternal.server.deploy.DbReadContext;
import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
import java.sql.SQLException;
import java.util.List;
/**
* Join to Many (or child of a many) to support where clause predicates on many properties.
*/
@@ -76,7 +75,7 @@ public class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
}
}
public void buildSelectExpressionChain(List<String> selectChain) {
public void buildRawSqlSelectChain(List<String> selectChain) {
// nothing to add
}
@@ -1,11 +1,8 @@
package com.avaje.ebeaninternal.server.text.json;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.text.json.EJson;
import com.avaje.ebean.text.json.*;
import com.avaje.ebean.text.PathProperties;
import com.avaje.ebean.text.json.JsonContext;
import com.avaje.ebean.text.json.JsonIOException;
import com.avaje.ebean.text.json.JsonWriteOptions;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.util.ParamTypeHelper;
@@ -56,15 +53,29 @@ public class DJsonContext implements JsonContext {
return toBean(cls, new StringReader(json));
}
@Override
public <T> T toBean(Class<T> cls, String json, JsonReadOptions options) throws JsonIOException {
return toBean(cls, new StringReader(json), options);
}
public <T> T toBean(Class<T> cls, Reader jsonReader) throws JsonIOException {
return toBean(cls, createParser(jsonReader));
}
public <T> T toBean(Class<T> cls, JsonParser parser) throws JsonIOException {
public <T> T toBean(Class<T> cls, Reader jsonReader, JsonReadOptions options) throws JsonIOException {
return toBean(cls, createParser(jsonReader), options);
}
public <T> T toBean(Class<T> cls, JsonParser parser) throws JsonIOException {
return toBean(cls, parser, null);
}
public <T> T toBean(Class<T> cls, JsonParser parser, JsonReadOptions options) throws JsonIOException {
ReadJson readJson = new ReadJson(parser, options);
try {
BeanDescriptor<T> d = getDescriptor(cls);
return d.jsonRead(parser, null);
return d.jsonRead(readJson, null);
} catch (IOException e) {
throw new JsonIOException(e);
}
@@ -74,13 +85,26 @@ public class DJsonContext implements JsonContext {
return toList(cls, new StringReader(json));
}
@Override
public <T> List<T> toList(Class<T> cls, String json, JsonReadOptions options) throws JsonIOException {
return toList(cls, new StringReader(json), options);
}
public <T> List<T> toList(Class<T> cls, Reader jsonReader) throws JsonIOException {
return toList(cls, createParser(jsonReader));
}
public <T> List<T> toList(Class<T> cls, JsonParser src) throws JsonIOException {
public <T> List<T> toList(Class<T> cls, Reader jsonReader, JsonReadOptions options) throws JsonIOException {
return toList(cls, createParser(jsonReader), options);
}
public <T> List<T> toList(Class<T> cls, JsonParser src) throws JsonIOException {
return toList(cls, src, null);
}
public <T> List<T> toList(Class<T> cls, JsonParser src, JsonReadOptions options) throws JsonIOException {
ReadJson readJson = new ReadJson(src, options);
try {
BeanDescriptor<T> d = getDescriptor(cls);
@@ -92,7 +116,7 @@ public class DJsonContext implements JsonContext {
}
do {
T bean = d.jsonRead(src, null);
T bean = d.jsonRead(readJson, null);
if (bean == null) {
break;
} else {
@@ -0,0 +1,87 @@
package com.avaje.ebeaninternal.server.text.json;
import com.avaje.ebean.text.json.JsonReadBeanVisitor;
import com.avaje.ebean.text.json.JsonReadOptions;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import java.io.IOException;
import java.util.Map;
/**
* Context for JSON read processing.
*/
public class ReadJson {
/**
* Jackson parser.
*/
final JsonParser parser;
/**
* Stack of the path - used to find the appropriate JsonReadBeanVisitor.
*/
final PathStack pathStack;
/**
* Map of the JsonReadBeanVisitor keyed by path.
*/
final Map<String, JsonReadBeanVisitor<?>> visitorMap;
/**
* Construct with parser and readOptions.
*/
public ReadJson(JsonParser parser, JsonReadOptions readOptions) {
this.parser = parser;
// only create visitorMap, pathStack if needed ...
this.visitorMap = (readOptions == null) ? null : readOptions.getVisitorMap();
this.pathStack = (visitorMap == null) ? null : new PathStack();
}
/**
* Return the JsonParser.
*/
public JsonParser getParser() {
return parser;
}
/**
* Return the next JsonToken from the underlying parser.
*/
public JsonToken nextToken() throws IOException {
return parser.nextToken();
}
/**
* Push the path onto the stack (traversing a 1-M or M-1 etc)
*/
public void pushPath(String path) {
if (pathStack != null) {
pathStack.pushPathKey(path);
}
}
/**
* Pop the path stack.
*/
public void popPath() {
if (pathStack != null) {
pathStack.pop();
}
}
/**
* If there is a JsonReadBeanVisitor registered to the current path then
* call it's visit method with the bean and unmappedProperties.
*/
@SuppressWarnings(value = "unchecked")
public void beanVisitor(Object bean, Map<String, Object> unmappedProperties) {
if (visitorMap != null) {
JsonReadBeanVisitor visitor = visitorMap.get(pathStack.peekWithNull());
if (visitor != null) {
visitor.visit(bean, unmappedProperties);
}
}
}
}
@@ -7,39 +7,39 @@ import com.avaje.ebeaninternal.api.SpiTransaction;
*/
public class DefaultTransactionScopeManager extends TransactionScopeManager {
public DefaultTransactionScopeManager(TransactionManager transactionManager){
super(transactionManager);
}
public void commit() {
DefaultTransactionThreadLocal.commit(serverName);
}
public DefaultTransactionScopeManager(TransactionManager transactionManager) {
super(transactionManager);
}
public void end() {
DefaultTransactionThreadLocal.end(serverName);
}
public void commit() {
DefaultTransactionThreadLocal.commit(serverName);
}
public SpiTransaction get() {
SpiTransaction t = DefaultTransactionThreadLocal.get(serverName);
if (t == null || !t.isActive()){
return null;
} else {
return t;
}
}
public void end() {
DefaultTransactionThreadLocal.end(serverName);
}
public void replace(SpiTransaction trans) {
DefaultTransactionThreadLocal.replace(serverName, trans);
}
public SpiTransaction get() {
SpiTransaction t = DefaultTransactionThreadLocal.get(serverName);
if (t == null || !t.isActive()) {
return null;
} else {
return t;
}
}
public void rollback() {
DefaultTransactionThreadLocal.rollback(serverName);
}
public void replace(SpiTransaction trans) {
DefaultTransactionThreadLocal.replace(serverName, trans);
}
public void rollback() {
DefaultTransactionThreadLocal.rollback(serverName);
}
public void set(SpiTransaction trans) {
DefaultTransactionThreadLocal.set(serverName, trans);
}
public void set(SpiTransaction trans) {
DefaultTransactionThreadLocal.set(serverName, trans);
}
}
@@ -14,63 +14,63 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
*/
public final class DeleteByIdMap {
private final Map<String,BeanPersistIds> beanMap = new LinkedHashMap<String, BeanPersistIds>();
public String toString() {
return beanMap.toString();
}
private final Map<String, BeanPersistIds> beanMap = new LinkedHashMap<String, BeanPersistIds>();
public void notifyCache() {
for (BeanPersistIds deleteIds : beanMap.values()) {
BeanDescriptor<?> d = deleteIds.getBeanDescriptor();
List<Serializable> idValues = deleteIds.getDeleteIds();
if (idValues != null){
d.queryCacheClear();
for (int i = 0; i < idValues.size(); i++) {
d.cacheBeanRemove(idValues.get(i));
}
}
}
}
public boolean isEmpty() {
return beanMap.isEmpty();
}
public String toString() {
return beanMap.toString();
}
public Collection<BeanPersistIds> values() {
return beanMap.values();
}
/**
* Add a Insert Update or Delete payload.
*/
public void add(BeanDescriptor<?> desc, Object id) {
BeanPersistIds r = getPersistIds(desc);
r.addId(PersistRequest.Type.DELETE, (Serializable)id);
}
/**
* Add a List of Insert Update or Delete Id's.
*/
public void addList(BeanDescriptor<?> desc, List<Object> idList) {
BeanPersistIds r = getPersistIds(desc);
for (int i = 0; i < idList.size(); i++) {
r.addId(PersistRequest.Type.DELETE, (Serializable) idList.get(i));
public void notifyCache() {
for (BeanPersistIds deleteIds : beanMap.values()) {
BeanDescriptor<?> d = deleteIds.getBeanDescriptor();
List<Serializable> idValues = deleteIds.getDeleteIds();
if (idValues != null) {
d.queryCacheClear();
for (int i = 0; i < idValues.size(); i++) {
d.cacheBeanRemove(idValues.get(i));
}
}
}
private BeanPersistIds getPersistIds(BeanDescriptor<?> desc) {
String beanType = desc.getFullName();
BeanPersistIds r = beanMap.get(beanType);
if (r == null){
r = new BeanPersistIds(desc);
beanMap.put(beanType, r);
}
return r;
}
public boolean isEmpty() {
return beanMap.isEmpty();
}
public Collection<BeanPersistIds> values() {
return beanMap.values();
}
/**
* Add a Insert Update or Delete payload.
*/
public void add(BeanDescriptor<?> desc, Object id) {
BeanPersistIds r = getPersistIds(desc);
r.addId(PersistRequest.Type.DELETE, (Serializable) id);
}
/**
* Add a List of Insert Update or Delete Id's.
*/
public void addList(BeanDescriptor<?> desc, List<Object> idList) {
BeanPersistIds r = getPersistIds(desc);
for (int i = 0; i < idList.size(); i++) {
r.addId(PersistRequest.Type.DELETE, (Serializable) idList.get(i));
}
}
private BeanPersistIds getPersistIds(BeanDescriptor<?> desc) {
String beanType = desc.getFullName();
BeanPersistIds r = beanMap.get(beanType);
if (r == null) {
r = new BeanPersistIds(desc);
beanMap.put(beanType, r);
}
return r;
}
}
@@ -115,7 +115,7 @@ public class JdbcTransaction implements SpiTransaction {
/**
* Set to true if the connection has autoCommit=true initially.
*/
protected final boolean autoCommit;
protected boolean autoCommit;
protected IdentityHashMap<Object, Object> persistingBeans;
@@ -146,11 +146,13 @@ public class JdbcTransaction implements SpiTransaction {
this.batchOnCascadeMode = manager == null ? PersistBatch.NONE : manager.getPersistBatchOnCascade();
this.onQueryOnly = manager == null ? OnQueryOnly.ROLLBACK : manager.getOnQueryOnly();
this.persistenceContext = new DefaultPersistenceContext();
this.autoCommit = connection.getAutoCommit();
if (this.autoCommit) {
connection.setAutoCommit(false);
if (connection != null) {
this.autoCommit = connection.getAutoCommit();
if (this.autoCommit) {
connection.setAutoCommit(false);
}
}
} catch (Exception e) {
throw new PersistenceException(e);
}

Some files were not shown because too many files have changed in this diff Show More