Compare commits

...
Author SHA1 Message Date
Robin Bygrave 1c6592a6ec [maven-release-plugin] prepare release avaje-ebeanorm-6.2.1 2015-07-27 23:03:27 +12:00
Robin Bygrave 554c5a7bfc No effective change - format 2015-07-27 23:02:33 +12:00
Robin Bygrave 7a32c50894 Bump pom to 6.2.1-SNAPSHOT 2015-07-27 23:00:12 +12:00
Robin Bygrave 3fa8f04a4c #358 - ENH: Add ServerConfig updateAllPropertiesInBatch with a default of true 2015-07-27 22:59:18 +12:00
Robin Bygrave 962d7f601d #356 - ENH: Change to Binder logging such that the value in the bind log is the converted/bound value 2015-07-27 20:59:40 +12:00
Robin Bygrave f2aa2b5b8b No effective change - format and tidy 2015-07-27 20:47:23 +12:00
Robin Bygrave 87b485cfa4 #355 - ENH: Add notIn(...) expressions. Just to make life easier in those cases 2015-07-27 20:39:34 +12:00
Robin Bygrave 5a55b5235d No effective change - tidy 2015-07-27 15:56:06 +12:00
Robin Bygrave 97adb76eac No effective change - remove unused methods 2015-07-27 15:54:20 +12:00
Robin Bygrave d0d5ab474d No effective change - remove unused import 2015-07-27 15:42:57 +12:00
Robin Bygrave d396f09b3a #296 - ENH: Add to transaction setUpdateAllLoadedProperties() - force update all properties api 2015-07-24 21:56:11 +12:00
Robin Bygrave 3baa5bca46 #352 - Remove deprecated API - ValuePair getValue1() getValue2() ... use getNewValue() getOldValue() 2015-07-24 20:15:13 +12:00
Robin Bygrave c00484026c #351 - ENH: Add diff to Version beans returned from findVersions() 2015-07-24 20:12:26 +12:00
Robin Bygrave 71798afadd #350 - ENH: Add @History findVersions() query - add default ordering to lower sys period desc 2015-07-24 16:52:58 +12:00
Robin Bygrave 1ec2f74df9 #350 - ENH: Add @History findVersions() query ... returning historic versions for a given bean using history view 2015-07-24 16:20:15 +12:00
Robin Bygrave 61a0651439 No effective change - code format 2015-07-24 12:33:35 +12:00
Robin Bygrave eae06059c9 #349 - ENH: Add @WhoCreated @WhoModified ... with CurrentUserProvider interface 2015-07-24 12:33:04 +12:00
Robin Bygrave a1ff9034b6 No effective change - format 2015-07-24 12:09:04 +12:00
Robin Bygrave 1085a8d608 #348 - ENH: Add @WhenCreated and @WhenModified ... as alias to existing @CreatedTimestamp and @UpdatedTimstamp 2015-07-24 10:50:35 +12:00
Robin Bygrave 3a547bf487 #347 - @History support with query.asOf(Timestamp) - javadoc update 2015-07-24 10:24:28 +12:00
Robin Bygrave 82489636d5 #347 - @History support with query.asOf(Timestamp) 2015-07-24 10:18:17 +12:00
Robin Bygrave 3cafb37a73 [maven-release-plugin] prepare for next development iteration 2015-07-20 20:09:05 +12:00
Robin Bygrave b4c7cf529d [maven-release-plugin] prepare release avaje-ebeanorm-6.1.1 2015-07-20 20:08:42 +12:00
Robin Bygrave ba5c67f936 #346 - ENH: Add @Nullable annotation on find by id and findUnique() methods 2015-07-20 20:06:55 +12:00
Robin Bygrave 2bd7995ca1 Bump pom to 6.1.1-SNAPSHOT 2015-07-20 12:55:33 +12:00
Robin Bygrave cc8c860709 Internal DefaultOrmQuery cleanup : timeout can default to 0 (rather than -1) 2015-07-20 12:51:24 +12:00
Robin Bygrave 7d25e31289 No effective change - unused imports 2015-07-20 12:48:08 +12:00
Robin Bygrave 936e56835c Internal SpiQuery : remove unused lazyLoadForParentsIds 2015-07-20 12:47:09 +12:00
Robin Bygrave 0d848e9311 Internal SpiQuery : remove unused context additions 2015-07-20 12:44:26 +12:00
Robin Bygrave b794b6c77e Internal SpiQuery : remove unused convertWhereNaturalKeyToId() 2015-07-20 12:40:08 +12:00
Robin Bygrave f45d240682 #345 - Expose Query.getId() and Query.getBeanType() methods ... (previously on SpiQuery) 2015-07-20 12:24:56 +12:00
Robin Bygrave 1a6f68edd3 No effective change - javadoc 2015-07-20 11:55:24 +12:00
Robin Bygrave 81683c5d2a No effective change - javadoc 2015-07-20 11:50:38 +12:00
Robin Bygrave 2b535d59b8 No effective change - javadoc 2015-07-20 11:41:23 +12:00
Robin Bygrave ae0bbaaaf8 #344 - Remove deprecated annotation @ColumnHstore ... migrate to @DbHstore 2015-07-20 11:38:40 +12:00
Robin Bygrave aaca20f9c3 #343 - Remove deprecated interface BeanFinder<T> ... migrate to BeanFindController 2015-07-20 11:34:05 +12:00
Robin Bygrave 245cbb0ea1 #342 - Remove deprecated method - JsonContext createJsonContext() ... migrate to json() 2015-07-20 11:27:00 +12:00
Robin Bygrave dbe984c668 #341 - Remove deprecated method - insert(Collection<?> beans); ... migrate to insertAll() 2015-07-20 11:22:16 +12:00
Robin Bygrave 89088f9480 #340 - Remove deprecated method - update(Collection<?> beans) ... migrate to updateAll() 2015-07-20 11:18:51 +12:00
Robin Bygrave 80253c4e07 #339 - Remove deprecated method - save(Collection<?> beans, Transaction transaction) ... migrate to saveAll() 2015-07-20 11:15:10 +12:00
Robin Bygrave f96ce11c73 #338 - Remove deprecated method - save(Iterator<?> it, Transaction transaction) ... please change to iterate yourself and save. 2015-07-20 11:11:45 +12:00
Robin Bygrave b8f8cb5ae7 #337 - Remove deprecated method - delete(Class<?> beanType, Collection<?> ids) ... migrate to deleteAll() 2015-07-20 11:07:52 +12:00
Robin Bygrave b8a2aac95a #336 - Remove deprecated method - delete(Iterator<?> it, Transaction transaction) 2015-07-20 10:59:52 +12:00
Robin Bygrave 7b090a6346 #335 - Remove deprecated method - delete(Collection<?> beans) ... migrate to deleteAll() 2015-07-20 10:54:18 +12:00
Robin Bygrave b10c68489f #334 - Remove deprecated method - delete(Iterator<?> it) ... change to iterate yourself and delete() 2015-07-20 10:47:51 +12:00
Robin Bygrave 2dbb2d9911 #333 - Remove deprecated method - save(Iterator<?> it) ... change to iterate yourself and save() 2015-07-20 10:43:37 +12:00
Robin Bygrave b3b4c72a77 #332 - Remove deprecated method - save(Collection<?> beans) ... migrate to saveAll(beans) 2015-07-20 10:39:15 +12:00
Robin Bygrave 1c040e4583 No effective change - code format 2015-07-20 10:24:10 +12:00
Robin Bygrave 9677e585b6 #331 - Remove deprecated method - EbeanServer.findVisit() ... in favor of findEachWhile() or findEach() 2015-07-20 10:22:50 +12:00
Robin Bygrave c53806224f [maven-release-plugin] prepare for next development iteration 2015-07-20 09:53:59 +12:00
Robin Bygrave 6fd5927c64 [maven-release-plugin] prepare release avaje-ebeanorm-5.1.1 2015-07-20 09:53:35 +12:00
Robin Bygrave 920635c6df #326 - API Addition - saveAll(), deleteAll() etc 2015-07-20 09:51:36 +12:00
Robin Bygrave fb1c29b57c Bump pom to 5.1.1-SNAPSHOT 2015-07-20 09:46:31 +12:00
Robin Bygrave 89bed8be67 #326 - API Addition - saveAll(), deleteAll() etc 2015-07-20 09:42:05 +12:00
Robin Bygrave 776e48a501 [maven-release-plugin] prepare for next development iteration 2015-07-20 09:06:01 +12:00
Robin Bygrave 0666a72df7 [maven-release-plugin] prepare release avaje-ebeanorm-4.9.1 2015-07-20 09:05:34 +12:00
Robin Bygrave 55000a3515 No effective change - rename parameter ctx -> parser 2015-07-20 09:01:54 +12:00
Robin Bygrave 8307e259ba bump pom to 4.9.1-SNAPSHOT 2015-07-20 08:50:54 +12:00
Robin Bygrave 95b7574d04 No effective change - format 2015-07-20 08:49:31 +12:00
Robin Bygrave f6466d3211 #280 - ENH: Add ServerConfig.setJsonInclude(...) ALL, NON_NULL, NON_EMPTY 2015-07-20 08:45:46 +12:00
Robin Bygrave 160e4d7dd1 #318 - JSON - toBean error at Transient (with support for #327, #328, #329) 2015-07-19 21:41:20 +12:00
Robin Bygrave a00980c61f #329 - JSON - Remove @Deprecated from JsonWriteOptions ... to support ObjectMapper which can be used for serialize/deserialize of @Transient properties 2015-07-19 21:38:27 +12:00
Robin Bygrave 72d2fcb59e #330 - JSON - Add ebean @JsonIgnore ... with serialize() default false and deserialize() default false 2015-07-19 21:37:37 +12:00
Robin Bygrave 0f503b2a2f No effective change - format code 2015-07-19 09:35:27 +12:00
Robin Bygrave a79e2e786a #324 - on effective change, sync internal name from primaryServer to defaultServer 2015-07-19 09:20:03 +12:00
Robin Bygrave c9ffa2673f #324 - ENH: Add Ebean.getDefaultServer() 2015-07-18 07:46:53 +12:00
Robin Bygrave 0e0fa37f0d #324 - ENH: Add Ebean.getDefaultServer() 2015-07-18 07:46:42 +12:00
Robin Bygrave f7bbd5b3bb No effective change - code format (fix variable names) 2015-07-18 07:35:19 +12:00
Robin Bygrave ba614cd287 No effective change - code format 2015-07-18 07:34:56 +12:00
Robin Bygrave 7d028f3318 [maven-release-plugin] prepare for next development iteration 2015-07-14 23:28:55 +12:00
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
221 changed files with 7928 additions and 5855 deletions
+1
View File
@@ -1,5 +1,6 @@
*.autofetch
*.sql
*.orig
.classpath
.project
.settings/
+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.7.1</version>
<version>4.7.3</version>
</dependency>
+8 -2
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>4.7.3</version>
<version>6.2.1</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -43,6 +43,12 @@
<dependencies>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>13.0</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
@@ -152,7 +158,7 @@
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.27</version>
<version>5.1.36</version>
<scope>test</scope>
</dependency>
+96 -109
View File
@@ -2,7 +2,6 @@ package com.avaje.ebean;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -10,6 +9,7 @@ import java.util.concurrent.ConcurrentHashMap;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -101,7 +101,7 @@ import com.avaje.ebean.text.json.JsonContext;
* specific database.
* </p>
*
* <pre> {@code
* <pre>{@code
*
* // Get access to the Human Resources EbeanServer/Database
* EbeanServer hrDb = Ebean.getServer("hr");
@@ -146,9 +146,9 @@ public final class Ebean {
private final Object monitor = new Object();
/**
* The 'default/primary' EbeanServer.
* The 'default' EbeanServer.
*/
private EbeanServer primaryServer;
private EbeanServer defaultServer;
private ServerManager() {
@@ -162,10 +162,10 @@ public final class Ebean {
} else {
// look to see if there is a default server defined
String primaryName = PrimaryServer.getPrimaryServerName();
logger.debug("primaryName:" + primaryName);
if (primaryName != null && primaryName.trim().length() > 0) {
primaryServer = getWithCreate(primaryName.trim());
String defaultName = PrimaryServer.getDefaultServerName();
logger.debug("defaultName:" + defaultName);
if (defaultName != null && defaultName.trim().length() > 0) {
defaultServer = getWithCreate(defaultName.trim());
}
}
} catch (RuntimeException e) {
@@ -174,19 +174,19 @@ public final class Ebean {
}
}
private EbeanServer getPrimaryServer() {
if (primaryServer == null) {
private EbeanServer getDefaultServer() {
if (defaultServer == null) {
String msg = "The default EbeanServer has not been defined?";
msg += " This is normally set via the ebean.datasource.default property.";
msg += " Otherwise it should be registered programatically via registerServer()";
throw new PersistenceException(msg);
}
return primaryServer;
return defaultServer;
}
private EbeanServer get(String name) {
if (name == null || name.length() == 0) {
return primaryServer;
return defaultServer;
}
// non-synchronized read
EbeanServer server = concMap.get(name);
@@ -221,12 +221,12 @@ public final class Ebean {
registerWithName(server.getName(), server, isPrimaryServer);
}
private void registerWithName(String name, EbeanServer server, boolean isPrimaryServer) {
private void registerWithName(String name, EbeanServer server, boolean isDefaultServer) {
synchronized (monitor) {
concMap.put(name, server);
syncMap.put(name, server);
if (isPrimaryServer) {
primaryServer = server;
if (isDefaultServer) {
defaultServer = server;
}
}
}
@@ -254,12 +254,22 @@ public final class Ebean {
* }</pre>
*
* @param name
* the name of the server, use null for the 'default server'
* the name of the server, can use null for the 'default server'
*/
public static EbeanServer getServer(String name) {
return serverMgr.get(name);
}
/**
* Returns the default EbeanServer.
* <p>
* This is equivalent to <code>Ebean.getServer(null);</code>
* </p>
*/
public static EbeanServer getDefaultServer() {
return serverMgr.getDefaultServer();
}
/**
* Return the ExpressionFactory from the default server.
* <p>
@@ -278,23 +288,23 @@ public final class Ebean {
* </p>
*/
public static ExpressionFactory getExpressionFactory() {
return serverMgr.getPrimaryServer().getExpressionFactory();
return serverMgr.getDefaultServer().getExpressionFactory();
}
/**
* Register the server with this Ebean singleton. Specify if the registered
* server is the primary/default server.
*/
public static void register(EbeanServer server, boolean isPrimaryServer) {
serverMgr.register(server, isPrimaryServer);
public static void register(EbeanServer server, boolean defaultServer) {
serverMgr.register(server, defaultServer);
}
/**
* Backdoor for registering a mock implementation of EbeanServer as the default server.
*/
protected static EbeanServer mock(String name, EbeanServer server, boolean isPrimaryServer) {
EbeanServer originalPrimaryServer = serverMgr.primaryServer;
serverMgr.registerWithName(name, server, isPrimaryServer);
protected static EbeanServer mock(String name, EbeanServer server, boolean defaultServer) {
EbeanServer originalPrimaryServer = serverMgr.defaultServer;
serverMgr.registerWithName(name, server, defaultServer);
return originalPrimaryServer;
}
@@ -311,7 +321,7 @@ public final class Ebean {
* </p>
*/
public static Object nextId(Class<?> beanType) {
return serverMgr.getPrimaryServer().nextId(beanType);
return serverMgr.getDefaultServer().nextId(beanType);
}
/**
@@ -359,7 +369,7 @@ public final class Ebean {
* </p>
*/
public static Transaction beginTransaction() {
return serverMgr.getPrimaryServer().beginTransaction();
return serverMgr.getDefaultServer().beginTransaction();
}
/**
@@ -370,7 +380,7 @@ public final class Ebean {
*
*/
public static Transaction beginTransaction(TxIsolation isolation) {
return serverMgr.getPrimaryServer().beginTransaction(isolation);
return serverMgr.getDefaultServer().beginTransaction(isolation);
}
/**
@@ -425,7 +435,7 @@ public final class Ebean {
* }</pre>
*/
public static Transaction beginTransaction(TxScope scope){
return serverMgr.getPrimaryServer().beginTransaction(scope);
return serverMgr.getDefaultServer().beginTransaction(scope);
}
/**
@@ -433,7 +443,7 @@ public final class Ebean {
* in scope.
*/
public static Transaction currentTransaction() {
return serverMgr.getPrimaryServer().currentTransaction();
return serverMgr.getDefaultServer().currentTransaction();
}
/**
@@ -446,21 +456,21 @@ public final class Ebean {
* @throws PersistenceException if there is no currently active transaction
*/
public static void register(TransactionCallback transactionCallback) throws PersistenceException {
serverMgr.getPrimaryServer().register(transactionCallback);
serverMgr.getDefaultServer().register(transactionCallback);
}
/**
* Commit the current transaction.
*/
public static void commitTransaction() {
serverMgr.getPrimaryServer().commitTransaction();
serverMgr.getDefaultServer().commitTransaction();
}
/**
* Rollback the current transaction.
*/
public static void rollbackTransaction() {
serverMgr.getPrimaryServer().rollbackTransaction();
serverMgr.getDefaultServer().rollbackTransaction();
}
/**
@@ -489,7 +499,7 @@ public final class Ebean {
* }</pre>
*/
public static void endTransaction() {
serverMgr.getPrimaryServer().endTransaction();
serverMgr.getDefaultServer().endTransaction();
}
/**
@@ -500,7 +510,7 @@ public final class Ebean {
* </p>
*/
public static Map<String, ValuePair> diff(Object a, Object b) {
return serverMgr.getPrimaryServer().diff(a, b);
return serverMgr.getDefaultServer().diff(a, b);
}
/**
@@ -523,7 +533,6 @@ public final class Ebean {
* public class Order { ...
*
* @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
* @JoinColumn(name="order_id")
* List<OrderDetail> details;
* ...
* }
@@ -537,7 +546,7 @@ public final class Ebean {
* </p>
*/
public static void save(Object bean) throws OptimisticLockException {
serverMgr.getPrimaryServer().save(bean);
serverMgr.getDefaultServer().save(bean);
}
/**
@@ -545,14 +554,14 @@ public final class Ebean {
* want to explicitly insert it.
*/
public static void insert(Object bean) {
serverMgr.getPrimaryServer().insert(bean);
serverMgr.getDefaultServer().insert(bean);
}
/**
* Insert a collection of beans.
*/
public static void insert(Collection<?> beans) {
serverMgr.getPrimaryServer().insert(beans);
public static void insertAll(Collection<?> beans) {
serverMgr.getDefaultServer().insertAll(beans);
}
/**
@@ -576,7 +585,7 @@ public final class Ebean {
* }</pre>
*/
public static void markAsDirty(Object bean) throws OptimisticLockException {
serverMgr.getPrimaryServer().markAsDirty(bean);
serverMgr.getDefaultServer().markAsDirty(bean);
}
/**
@@ -616,28 +625,21 @@ public final class Ebean {
* @see ServerConfig#setUpdateChangesOnly(boolean)
*/
public static void update(Object bean) throws OptimisticLockException {
serverMgr.getPrimaryServer().update(bean);
serverMgr.getDefaultServer().update(bean);
}
/**
* Update the beans in the collection.
*/
public static void update(Collection<?> beans) throws OptimisticLockException {
serverMgr.getPrimaryServer().update(beans);
}
/**
* Save all the beans from an Iterator.
*/
public static int save(Iterator<?> iterator) throws OptimisticLockException {
return serverMgr.getPrimaryServer().save(iterator);
public static void updateAll(Collection<?> beans) throws OptimisticLockException {
serverMgr.getDefaultServer().updateAll(beans);
}
/**
* Save all the beans from a Collection.
*/
public static int save(Collection<?> beans) throws OptimisticLockException {
return serverMgr.getPrimaryServer().save(beans);
public static int saveAll(Collection<?> beans) throws OptimisticLockException {
return serverMgr.getDefaultServer().saveAll(beans);
}
/**
@@ -651,7 +653,7 @@ public final class Ebean {
* @return the number of associations deleted (from the intersection table).
*/
public static int deleteManyToManyAssociations(Object ownerBean, String propertyName) {
return serverMgr.getPrimaryServer().deleteManyToManyAssociations(ownerBean, propertyName);
return serverMgr.getDefaultServer().deleteManyToManyAssociations(ownerBean, propertyName);
}
/**
@@ -669,7 +671,7 @@ public final class Ebean {
* </p>
*/
public static void saveManyToManyAssociations(Object ownerBean, String propertyName) {
serverMgr.getPrimaryServer().saveManyToManyAssociations(ownerBean, propertyName);
serverMgr.getDefaultServer().saveManyToManyAssociations(ownerBean, propertyName);
}
/**
@@ -689,7 +691,7 @@ public final class Ebean {
* the property we want to save
*/
public static void saveAssociation(Object ownerBean, String propertyName) {
serverMgr.getPrimaryServer().saveAssociation(ownerBean, propertyName);
serverMgr.getDefaultServer().saveAssociation(ownerBean, propertyName);
}
/**
@@ -700,35 +702,28 @@ public final class Ebean {
* </p>
*/
public static void delete(Object bean) throws OptimisticLockException {
serverMgr.getPrimaryServer().delete(bean);
serverMgr.getDefaultServer().delete(bean);
}
/**
* Delete the bean given its type and id.
*/
public static int delete(Class<?> beanType, Object id) {
return serverMgr.getPrimaryServer().delete(beanType, id);
return serverMgr.getDefaultServer().delete(beanType, id);
}
/**
* Delete several beans given their type and id values.
*/
public static void delete(Class<?> beanType, Collection<?> ids) {
serverMgr.getPrimaryServer().delete(beanType, ids);
public static void deleteAll(Class<?> beanType, Collection<?> ids) {
serverMgr.getDefaultServer().deleteAll(beanType, ids);
}
/**
* Delete all the beans from an Iterator.
* Delete all the beans in the Collection.
*/
public static int delete(Iterator<?> it) throws OptimisticLockException {
return serverMgr.getPrimaryServer().delete(it);
}
/**
* Delete all the beans from a Collection.
*/
public static int delete(Collection<?> c) throws OptimisticLockException {
return delete(c.iterator());
public static int deleteAll(Collection<?> beans) throws OptimisticLockException {
return serverMgr.getDefaultServer().deleteAll(beans);
}
/**
@@ -739,7 +734,7 @@ public final class Ebean {
* </p>
*/
public static void refresh(Object bean) {
serverMgr.getPrimaryServer().refresh(bean);
serverMgr.getDefaultServer().refresh(bean);
}
/**
@@ -760,7 +755,7 @@ public final class Ebean {
* the property name of the List Set or Map to refresh.
*/
public static void refreshMany(Object bean, String manyPropertyName) {
serverMgr.getPrimaryServer().refreshMany(bean, manyPropertyName);
serverMgr.getDefaultServer().refreshMany(bean, manyPropertyName);
}
/**
@@ -788,7 +783,7 @@ public final class Ebean {
* the id value
*/
public static <T> T getReference(Class<T> beanType, Object id) {
return serverMgr.getPrimaryServer().getReference(beanType, id);
return serverMgr.getDefaultServer().getReference(beanType, id);
}
/**
@@ -834,7 +829,7 @@ public final class Ebean {
* the properties to sort the list by
*/
public static <T> void sort(List<T> list, String sortByClause) {
serverMgr.getPrimaryServer().sort(list, sortByClause);
serverMgr.getDefaultServer().sort(list, sortByClause);
}
/**
@@ -883,8 +878,9 @@ public final class Ebean {
* @param id
* the id value
*/
@Nullable
public static <T> T find(Class<T> beanType, Object id) {
return serverMgr.getPrimaryServer().find(beanType, id);
return serverMgr.getDefaultServer().find(beanType, id);
}
/**
@@ -896,7 +892,7 @@ public final class Ebean {
* </p>
*/
public static SqlQuery createSqlQuery(String sql) {
return serverMgr.getPrimaryServer().createSqlQuery(sql);
return serverMgr.getDefaultServer().createSqlQuery(sql);
}
/**
@@ -909,7 +905,7 @@ public final class Ebean {
* the name of the query
*/
public static SqlQuery createNamedSqlQuery(String namedQuery) {
return serverMgr.getPrimaryServer().createNamedSqlQuery(namedQuery);
return serverMgr.getDefaultServer().createNamedSqlQuery(namedQuery);
}
/**
@@ -927,7 +923,7 @@ public final class Ebean {
* </p>
*/
public static SqlUpdate createSqlUpdate(String sql) {
return serverMgr.getPrimaryServer().createSqlUpdate(sql);
return serverMgr.getDefaultServer().createSqlUpdate(sql);
}
/**
@@ -936,7 +932,7 @@ public final class Ebean {
* @see CallableSql
*/
public static CallableSql createCallableSql(String sql) {
return serverMgr.getPrimaryServer().createCallableSql(sql);
return serverMgr.getDefaultServer().createCallableSql(sql);
}
/**
@@ -959,7 +955,7 @@ public final class Ebean {
* }</pre>
*/
public static SqlUpdate createNamedSqlUpdate(String namedQuery) {
return serverMgr.getPrimaryServer().createNamedSqlUpdate(namedQuery);
return serverMgr.getDefaultServer().createNamedSqlUpdate(namedQuery);
}
/**
@@ -987,7 +983,7 @@ public final class Ebean {
*/
public static <T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery) {
return serverMgr.getPrimaryServer().createNamedQuery(beanType, namedQuery);
return serverMgr.getDefaultServer().createNamedQuery(beanType, namedQuery);
}
/**
@@ -1015,7 +1011,7 @@ public final class Ebean {
* the object query
*/
public static <T> Query<T> createQuery(Class<T> beanType, String query) {
return serverMgr.getPrimaryServer().createQuery(beanType, query);
return serverMgr.getDefaultServer().createQuery(beanType, query);
}
/**
@@ -1074,7 +1070,7 @@ public final class Ebean {
*/
public static <T> Update<T> createNamedUpdate(Class<T> beanType, String namedUpdate) {
return serverMgr.getPrimaryServer().createNamedUpdate(beanType, namedUpdate);
return serverMgr.getDefaultServer().createNamedUpdate(beanType, namedUpdate);
}
/**
@@ -1108,7 +1104,7 @@ public final class Ebean {
*/
public static <T> Update<T> createUpdate(Class<T> beanType, String ormUpdate) {
return serverMgr.getPrimaryServer().createUpdate(beanType, ormUpdate);
return serverMgr.getDefaultServer().createUpdate(beanType, ormUpdate);
}
/**
@@ -1116,7 +1112,7 @@ public final class Ebean {
*/
public static <T> CsvReader<T> createCsvReader(Class<T> beanType) {
return serverMgr.getPrimaryServer().createCsvReader(beanType);
return serverMgr.getDefaultServer().createCsvReader(beanType);
}
/**
@@ -1173,7 +1169,7 @@ public final class Ebean {
*/
public static <T> Query<T> createQuery(Class<T> beanType) {
return serverMgr.getPrimaryServer().createQuery(beanType);
return serverMgr.getDefaultServer().createQuery(beanType);
}
/**
@@ -1190,7 +1186,7 @@ public final class Ebean {
*/
public static <T> Query<T> find(Class<T> beanType) {
return serverMgr.getPrimaryServer().find(beanType);
return serverMgr.getDefaultServer().find(beanType);
}
/**
@@ -1204,7 +1200,7 @@ public final class Ebean {
* </p>
*/
public static <T> Filter<T> filter(Class<T> beanType) {
return serverMgr.getPrimaryServer().filter(beanType);
return serverMgr.getDefaultServer().filter(beanType);
}
/**
@@ -1251,7 +1247,7 @@ public final class Ebean {
* @see Ebean#execute(CallableSql)
*/
public static int execute(SqlUpdate sqlUpdate) {
return serverMgr.getPrimaryServer().execute(sqlUpdate);
return serverMgr.getDefaultServer().execute(sqlUpdate);
}
/**
@@ -1280,7 +1276,7 @@ public final class Ebean {
* @see Ebean#execute(SqlUpdate)
*/
public static int execute(CallableSql callableSql) {
return serverMgr.getPrimaryServer().execute(callableSql);
return serverMgr.getDefaultServer().execute(callableSql);
}
/**
@@ -1305,7 +1301,7 @@ public final class Ebean {
* }</pre>
*/
public static void execute(TxScope scope, TxRunnable r) {
serverMgr.getPrimaryServer().execute(scope, r);
serverMgr.getDefaultServer().execute(scope, r);
}
/**
@@ -1333,7 +1329,7 @@ public final class Ebean {
* }</pre>
*/
public static void execute(TxRunnable r) {
serverMgr.getPrimaryServer().execute(r);
serverMgr.getDefaultServer().execute(r);
}
/**
@@ -1360,7 +1356,7 @@ public final class Ebean {
*
*/
public static <T> T execute(TxScope scope, TxCallable<T> c) {
return serverMgr.getPrimaryServer().execute(scope, c);
return serverMgr.getDefaultServer().execute(scope, c);
}
/**
@@ -1394,7 +1390,7 @@ public final class Ebean {
* }</pre>
*/
public static <T> T execute(TxCallable<T> c) {
return serverMgr.getPrimaryServer().execute(c);
return serverMgr.getDefaultServer().execute(c);
}
/**
@@ -1431,10 +1427,9 @@ public final class Ebean {
* @param deletes
* true if rows on the table where deleted
*/
public static void externalModification(String tableName, boolean inserts, boolean updates,
boolean deletes) {
public static void externalModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
serverMgr.getPrimaryServer().externalModification(tableName, inserts, updates, deletes);
serverMgr.getDefaultServer().externalModification(tableName, inserts, updates, deletes);
}
/**
@@ -1444,7 +1439,7 @@ public final class Ebean {
* </p>
*/
public static BeanState getBeanState(Object bean) {
return serverMgr.getPrimaryServer().getBeanState(bean);
return serverMgr.getDefaultServer().getBeanState(bean);
}
/**
@@ -1452,7 +1447,7 @@ public final class Ebean {
*
*/
public static ServerCacheManager getServerCacheManager() {
return serverMgr.getPrimaryServer().getServerCacheManager();
return serverMgr.getDefaultServer().getServerCacheManager();
}
/**
@@ -1460,7 +1455,7 @@ public final class Ebean {
* queries.
*/
public static BackgroundExecutor getBackgroundExecutor() {
return serverMgr.getPrimaryServer().getBackgroundExecutor();
return serverMgr.getDefaultServer().getBackgroundExecutor();
}
/**
@@ -1471,7 +1466,7 @@ public final class Ebean {
* </p>
*/
public static void runCacheWarming() {
serverMgr.getPrimaryServer().runCacheWarming();
serverMgr.getDefaultServer().runCacheWarming();
}
/**
@@ -1483,22 +1478,14 @@ public final class Ebean {
*/
public static void runCacheWarming(Class<?> beanType) {
serverMgr.getPrimaryServer().runCacheWarming(beanType);
serverMgr.getDefaultServer().runCacheWarming(beanType);
}
/**
* Return the JsonContext for reading/writing JSON.
*/
public static JsonContext json() {
return serverMgr.getPrimaryServer().json();
}
/**
* Return the JsonContext for reading/writing JSON.
* @deprecated Please use #json instead.
*/
public static JsonContext createJsonContext() {
return json();
return serverMgr.getDefaultServer().json();
}
}
+39 -66
View File
@@ -1,7 +1,6 @@
package com.avaje.ebean;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -15,6 +14,7 @@ import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.meta.MetaInfoManager;
import com.avaje.ebean.text.csv.CsvReader;
import com.avaje.ebean.text.json.JsonContext;
import org.jetbrains.annotations.Nullable;
/**
* Provides the API for fetching and saving beans to a particular DataSource.
@@ -148,7 +148,7 @@ public interface EbeanServer {
* difference comparison.
* </p>
*/
Map<String, ValuePair> diff(Object a, Object b);
Map<String, ValuePair> diff(Object newBean, Object oldBean);
/**
* Create a new instance of T that is an EntityBean.
@@ -905,17 +905,13 @@ public interface EbeanServer {
<T> void findEachWhile(Query<T> query, QueryEachWhileConsumer<T> consumer, Transaction transaction);
/**
* Deprecated in favor of #findEachWhile which is functionally exactly the same
* but has a much better name.
* Return versions of a @History entity bean.
* <p>
* Execute the query visiting the results. This is similar to findIterate in
* that not all the result beans need to be held in memory at the same time
* and as such is go for processing large queries.
* Generally this query is expected to be a find by id or unique predicates query.
* It will execute the query against the history returning the versions of the bean.
* </p>
*
* @deprecated
*/
<T> void findVisit(Query<T> query, QueryResultVisitor<T> visitor, Transaction transaction);
<T> List<Version<T>> findVersions(Query<T> query, Transaction transaction);
/**
* Execute a query returning a list of beans.
@@ -1109,6 +1105,7 @@ public interface EbeanServer {
*
* @see Query#findUnique()
*/
@Nullable
<T> T findUnique(Query<T> query, Transaction transaction);
/**
@@ -1185,6 +1182,7 @@ public interface EbeanServer {
*
* @see SqlQuery#findUnique()
*/
@Nullable
SqlRow findUnique(SqlQuery query, Transaction transaction);
/**
@@ -1222,15 +1220,10 @@ public interface EbeanServer {
*/
void save(Object bean) throws OptimisticLockException;
/**
* Save all the beans in the iterator.
*/
int save(Iterator<?> it) throws OptimisticLockException;
/**
* Save all the beans in the collection.
*/
int save(Collection<?> beans) throws OptimisticLockException;
int saveAll(Collection<?> beans) throws OptimisticLockException;
/**
* Delete the bean.
@@ -1241,15 +1234,10 @@ public interface EbeanServer {
*/
void delete(Object bean) throws OptimisticLockException;
/**
* Delete all the beans from an Iterator.
*/
int delete(Iterator<?> it) throws OptimisticLockException;
/**
* Delete all the beans in the collection.
*/
int delete(Collection<?> c) throws OptimisticLockException;
int deleteAll(Collection<?> beans) throws OptimisticLockException;
/**
* Delete the bean given its type and id.
@@ -1264,13 +1252,18 @@ public interface EbeanServer {
/**
* Delete several beans given their type and id values.
*/
void delete(Class<?> beanType, Collection<?> ids);
void deleteAll(Class<?> beanType, Collection<?> ids);
/**
* Delete several beans given their type and id values with an explicit
* transaction.
*/
void delete(Class<?> beanType, Collection<?> ids, Transaction transaction);
void deleteAll(Class<?> beanType, Collection<?> ids, Transaction transaction);
/**
* Delete the bean with an explicit transaction.
*/
void delete(Object bean, Transaction transaction) throws OptimisticLockException;
/**
* Execute a Sql Update Delete or Insert statement. This returns the number of
@@ -1328,7 +1321,7 @@ public interface EbeanServer {
* Execute a ORM insert update or delete statement with an explicit
* transaction.
*/
int execute(Update<?> update, Transaction t);
int execute(Update<?> update, Transaction transaction);
/**
* For making calls to stored procedures.
@@ -1400,27 +1393,22 @@ public interface EbeanServer {
* the type of entity bean to find
* @param beanType
* the type of entity bean to find
* @param uid
* @param id
* the bean id value
* @param transaction
* the transaction to use (can be null)
*/
<T> T find(Class<T> beanType, Object uid, Transaction transaction);
<T> T find(Class<T> beanType, Object id, Transaction transaction);
/**
* Insert or update a bean with an explicit transaction.
*/
void save(Object bean, Transaction transaction) throws OptimisticLockException;
/**
* Save all the beans in the iterator with an explicit transaction.
*/
int save(Iterator<?> it, Transaction transaction) throws OptimisticLockException;
/**
* Save all the beans in the collection with an explicit transaction.
*/
int save(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
int saveAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
/**
* Marks the entity bean as dirty.
@@ -1485,7 +1473,7 @@ public interface EbeanServer {
/**
* Update a bean additionally specifying a transaction.
*/
void update(Object bean, Transaction t) throws OptimisticLockException;
void update(Object bean, Transaction transaction) throws OptimisticLockException;
/**
* Update a bean additionally specifying a transaction and the deleteMissingChildren setting.
@@ -1505,13 +1493,13 @@ public interface EbeanServer {
* Update a collection of beans. If there is no current transaction one is created and used to
* update all the beans in the collection.
*/
void update(Collection<?> beans) throws OptimisticLockException;
void updateAll(Collection<?> beans) throws OptimisticLockException;
/**
* Update a collection of beans with an explicit transaction.
*/
void update(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
void updateAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
/**
* Insert the bean.
* <p>
@@ -1525,18 +1513,18 @@ public interface EbeanServer {
/**
* Insert the bean with a transaction.
*/
void insert(Object bean, Transaction t);
void insert(Object bean, Transaction transaction);
/**
* Insert a collection of beans. If there is no current transaction one is created and used to
* insert all the beans in the collection.
*/
void insert(Collection<?> beans);
void insertAll(Collection<?> beans);
/**
* Insert a collection of beans with an explicit transaction.
*/
void insert(Collection<?> beans, Transaction t);
void insertAll(Collection<?> beans, Transaction transaction);
/**
* Delete the associations (from the intersection table) of a ManyToMany given
@@ -1545,7 +1533,7 @@ public interface EbeanServer {
* Typically these deletions occur automatically when persisting a ManyToMany
* collection and this provides a way to invoke those deletions directly.
* </p>
*
*
* @return the number of associations deleted (from the intersection table).
*/
int deleteManyToManyAssociations(Object ownerBean, String propertyName);
@@ -1560,10 +1548,10 @@ public interface EbeanServer {
* Typically these deletions occur automatically when persisting a ManyToMany
* collection and this provides a way to invoke those deletions directly.
* </p>
*
*
* @return the number of associations deleted (from the intersection table).
*/
int deleteManyToManyAssociations(Object ownerBean, String propertyName, Transaction t);
int deleteManyToManyAssociations(Object ownerBean, String propertyName, Transaction transaction);
/**
* Save the associations of a ManyToMany given the owner bean and the
@@ -1585,7 +1573,7 @@ public interface EbeanServer {
* way to invoke those insertions directly.
* </p>
*/
void saveManyToManyAssociations(Object ownerBean, String propertyName, Transaction t);
void saveManyToManyAssociations(Object ownerBean, String propertyName, Transaction transaction);
/**
* Save the associated collection or bean given the property name.
@@ -1622,27 +1610,18 @@ public interface EbeanServer {
* @param propertyName
* the property we want to save
*/
void saveAssociation(Object ownerBean, String propertyName, Transaction t);
void saveAssociation(Object ownerBean, String propertyName, Transaction transaction);
/**
* Delete the bean with an explicit transaction.
*/
void delete(Object bean, Transaction t) throws OptimisticLockException;
/**
* Delete all the beans from an iterator.
*/
int delete(Iterator<?> it, Transaction t) throws OptimisticLockException;
/**
* Execute explicitly passing a transaction.
*/
int execute(SqlUpdate updSql, Transaction t);
int execute(SqlUpdate updSql, Transaction transaction);
/**
* Execute explicitly passing a transaction.
*/
int execute(CallableSql callableSql, Transaction t);
int execute(CallableSql callableSql, Transaction transaction);
/**
* Execute a TxRunnable in a Transaction with an explicit scope.
@@ -1665,7 +1644,7 @@ public interface EbeanServer {
*
* }</pre>
*/
void execute(TxScope scope, TxRunnable r);
void execute(TxScope scope, TxRunnable runnable);
/**
* Execute a TxRunnable in a Transaction with the default scope.
@@ -1691,7 +1670,7 @@ public interface EbeanServer {
*
* }</pre>
*/
void execute(TxRunnable r);
void execute(TxRunnable runnable);
/**
* Execute a TxCallable in a Transaction with an explicit scope.
@@ -1715,7 +1694,7 @@ public interface EbeanServer {
*
* }</pre>
*/
<T> T execute(TxScope scope, TxCallable<T> c);
<T> T execute(TxScope scope, TxCallable<T> callable);
/**
* Execute a TxCallable in a Transaction with the default scope.
@@ -1747,7 +1726,7 @@ public interface EbeanServer {
*
* }</pre>
*/
<T> T execute(TxCallable<T> c);
<T> T execute(TxCallable<T> callable);
/**
* Return the manager of the server cache ("L2" cache).
@@ -1777,12 +1756,6 @@ public interface EbeanServer {
*/
void runCacheWarming(Class<?> beanType);
/**
* Return the JsonContext for reading/writing JSON.
* @deprecated Please use #json instead.
*/
JsonContext createJsonContext();
/**
* Return the JsonContext for reading/writing JSON.
* <p>
@@ -169,7 +169,22 @@ public interface ExpressionFactory {
* In - property has a value in the collection of values.
*/
Expression in(String propertyName, Collection<?> values);
/**
* Not In - property has a value in the array of values.
*/
Expression notIn(String propertyName, Object[] values);
/**
* Not In - property has a value in the collection of values.
*/
Expression notIn(String propertyName, Collection<?> values);
/**
* Not In - using a subQuery.
*/
Expression notIn(String propertyName, Query<?> subQuery);
/**
* Exists expression
*/
@@ -1,8 +1,10 @@
package com.avaje.ebean;
import com.avaje.ebean.text.PathProperties;
import org.jetbrains.annotations.Nullable;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -94,6 +96,17 @@ public interface ExpressionList<T> extends Serializable {
*/
Query<T> apply(PathProperties pathProperties);
/**
* Perform an 'As of' query using history tables to return the object graph
* as of a time in the past.
* <p>
* To perform this query the DB must have underlying history tables.
* </p>
*
* @param asOf the date time in the past at which you want to view the data
*/
Query<T> asOf(Timestamp asOf);
/**
* Execute the query iterating over the results.
*
@@ -116,14 +129,6 @@ public interface ExpressionList<T> extends Serializable {
*/
void findEachWhile(QueryEachWhileConsumer<T> consumer);
/**
* Deprecated in favor of #findEachWhile which is functionally exactly the same
* but has a much better name.
*
* @deprecated
*/
void findVisit(QueryResultVisitor<T> visitor);
/**
* Execute the query returning a list.
*
@@ -170,6 +175,7 @@ public interface ExpressionList<T> extends Serializable {
*
* @see Query#findUnique()
*/
@Nullable
T findUnique();
/**
@@ -229,6 +235,15 @@ public interface ExpressionList<T> extends Serializable {
*/
PagedList<T> findPagedList(int pageIndex, int pageSize);
/**
* Return versions of a @History entity bean.
* <p>
* Generally this query is expected to be a find by id or unique predicates query.
* It will execute the query against the history returning the versions of the bean.
* </p>
*/
List<Version<T>> findVersions();
/**
* Add some filter predicate expressions to the many property.
*/
@@ -473,7 +488,22 @@ public interface ExpressionList<T> extends Serializable {
* In - property has a value in the collection of values.
*/
ExpressionList<T> in(String propertyName, Collection<?> values);
/**
* Not In - property has a value in the array of values.
*/
ExpressionList<T> notIn(String propertyName, Object... values);
/**
* Not In - property has a value in the collection of values.
*/
ExpressionList<T> notIn(String propertyName, Collection<?> values);
/**
* Not In - using a subQuery.
*/
ExpressionList<T> notIn(String propertyName, Query<?> subQuery);
/**
* Exists expression
*/
+36 -36
View File
@@ -22,57 +22,57 @@ import java.io.Serializable;
* efficient loaded as 2 SQL queries.
* </p>
*
* <pre class="code">
* <pre>{@code
* // Normal fetch join results in a single SQL query
* List&lt;Order&gt; list = Ebean.find(Order.class).fetch(&quot;details&quot;).findList();
* List<Order> list = Ebean.find(Order.class).fetch("details").findList();
*
* // Find Orders join details using a single SQL query
* </pre>
* }</pre>
* <p>
* Example: Using a "query join" instead of a "fetch join" we instead use 2 SQL
* queries
* </p>
*
* <pre class="code">
* <pre>{@code
* // This will use 2 SQL queries to build this object graph
* List&lt;Order&gt; list =
* List<Order> list =
* Ebean.find(Order.class)
* .fetch(&quot;details&quot;, new FetchConfig().query())
* .fetch("details", new FetchConfig().query())
* .findList();
*
* // query 1) find order
* // query 2) find orderDetails where order.id in (?,?...) // first 100 order id's
* </pre>
* }</pre>
* <p>
* Example: Using 2 "query joins"
* </p>
*
* <pre class="code">
* <pre>{@code
* // This will use 3 SQL queries to build this object graph
* List&lt;Order&gt; list =
* List<Order> list =
* Ebean.find(Order.class)
* .fetch(&quot;details&quot;, new FetchConfig().query())
* .fetch(&quot;customer&quot;, new FetchConfig().queryFirst(5))
* .fetch("details", new FetchConfig().query())
* .fetch("customer", new FetchConfig().queryFirst(5))
* .findList();
*
* // query 1) find order
* // query 2) find orderDetails where order.id in (?,?...) // first 100 order id's
* // query 3) find customer where id in (?,?,?,?,?) // first 5 customers
* </pre>
* }</pre>
* <p>
* Example: Using "query joins" and partial objects
* </p>
*
* <pre class="code">
* <pre>{@code
* // This will use 3 SQL queries to build this object graph
* List&lt;Order&gt; list =
* List<Order> list =
* Ebean.find(Order.class)
* .select(&quot;status, shipDate&quot;)
* .fetch(&quot;details&quot;, &quot;quantity, price&quot;, new FetchConfig().query())
* .fetch(&quot;details.product&quot;, &quot;sku, name&quot;)
* .fetch(&quot;customer&quot;, &quot;name&quot;, new FetchConfig().queryFirst(5))
* .fetch(&quot;customer.contacts&quot;)
* .fetch(&quot;customer.shippingAddress&quot;)
* .select("status, shipDate")
* .fetch("details", "quantity, price", new FetchConfig().query())
* .fetch("details.product", "sku, name")
* .fetch("customer", "name", new FetchConfig().queryFirst(5))
* .fetch("customer.contacts")
* .fetch("customer.shippingAddress")
* .findList();
*
* // query 1) find order (status, shipDate)
@@ -81,22 +81,22 @@ import java.io.Serializable;
* // query 3) find customer (name) fetch contacts (*) fetch shippingAddress (*)
* // where id in (?,?,?,?,?)
*
* // Note: the fetch of &quot;details.product&quot; is automatically included into the
* // fetch of &quot;details&quot;
* // Note: the fetch of "details.product" is automatically included into the
* // fetch of "details"
* //
* // Note: the fetch of &quot;customer.contacts&quot; and &quot;customer.shippingAddress&quot;
* // are automatically included in the fetch of &quot;customer&quot;
* </pre>
* // Note: the fetch of "customer.contacts" and "customer.shippingAddress"
* // are automatically included in the fetch of "customer"
* }</pre>
* <p>
* You can use query() and lazy together on a single join. The query is executed
* immediately and the lazy defines the batch size to use for further lazy
* loading (if lazy loading is invoked).
* </p>
*
* <pre class="code">
* List&lt;Order&gt; list =
* <pre>{@code
* List<Order> list =
* Ebean.find(Order.class)
* .fetch(&quot;customer&quot;, new FetchConfig().query(10).lazy(5))
* .fetch("customer", new FetchConfig().query(10).lazy(5))
* .findList();
*
* // query 1) find order
@@ -104,7 +104,7 @@ import java.io.Serializable;
* // .. then if lazy loading of customers is invoked
* // .. use a batch size of 5 to load the customers
*
* </pre>
* }</pre>
*
* <p>
* Example of controlling the lazy loading query:
@@ -114,12 +114,12 @@ import java.io.Serializable;
* case.
* </p>
*
* <pre class="code">
* List&lt;Order&gt; list = Ebean.find(Order.class)
* .fetch(&quot;customer&quot;,&quot;name&quot;, new FetchConfig().lazy(5))
* .fetch(&quot;customer.contacts&quot;,&quotcontactName, phone, email&quot)
* .fetch(&quot;customer.shippingAddress&quot;)
* .where().eq(&quot;status&quot;,Order.Status.NEW)
* <pre>{@code
* List<Order> list = Ebean.find(Order.class)
* .fetch("customer","name", new FetchConfig().lazy(5))
* .fetch("customer.contacts",&quotcontactName, phone, email&quot)
* .fetch("customer.shippingAddress")
* .where().eq("status",Order.Status.NEW)
* .findList();
*
* // query 1) find order where status = Order.Status.NEW
@@ -132,7 +132,7 @@ import java.io.Serializable;
* fetch customer.shippingAddress (*)
* where id in (?,?,?,?,?)
*
* </pre>
* }</pre>
*
* @author mario
* @author rbygrave
+8 -8
View File
@@ -2,14 +2,13 @@ package com.avaje.ebean;
import com.avaje.ebean.text.PathProperties;
import com.avaje.ebean.util.ClassUtil;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import org.jetbrains.annotations.Nullable;
import javax.persistence.MappedSuperclass;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* A MappedSuperclass base class that provides convenience methods for inserting, updating and
@@ -152,7 +151,7 @@ public abstract class Model {
* }</pre>
*/
public static EbeanServer db() {
return Ebean.getServer(null);
return Ebean.getDefaultServer();
}
/**
@@ -451,8 +450,8 @@ public abstract class Model {
* Construct passing the class literal type of the entity type.
*/
protected Find(String serverName, Class<T> type) {
this.type = type;
this.serverName = serverName;
this.type = type;
}
/**
@@ -514,6 +513,7 @@ public abstract class Model {
* <p>
* Equivalent to {@link EbeanServer#find(Class, Object)}
*/
@Nullable
public T byId(I id) {
return db().find(type, id);
}
@@ -34,7 +34,7 @@ class PrimaryServer {
/**
* Return the default server name.
*/
static synchronized String getPrimaryServerName() {
static synchronized String getDefaultServerName() {
getProperties();
return defaultServerName;
}
+38 -16
View File
@@ -1,8 +1,10 @@
package com.avaje.ebean;
import com.avaje.ebean.text.PathProperties;
import org.jetbrains.annotations.Nullable;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -284,6 +286,17 @@ public interface Query<T> extends Serializable {
*/
Query<T> setRawSql(RawSql rawSql);
/**
* Perform an 'As of' query using history tables to return the object graph
* as of a time in the past.
* <p>
* To perform this query the DB must have underlying history tables.
* </p>
*
* @param asOf the date time in the past at which you want to view the data
*/
Query<T> asOf(Timestamp asOf);
/**
* Cancel the query execution if supported by the underlying database and
* driver.
@@ -483,7 +496,7 @@ public interface Query<T> extends Serializable {
*
* }</pre>
*/
Query<T> fetch(String path, FetchConfig joinConfig);
Query<T> fetch(String path, FetchConfig fetchConfig);
/**
* Apply the path properties replacing the select and fetch clauses.
@@ -520,19 +533,7 @@ public interface Query<T> extends Serializable {
*/
QueryIterator<T> findIterate();
/**
* This is deprecated in favor of #findEachWhile.
* <p>
* This is functionally exactly the same as #findEachWhile. It is
* replaced by findEachWhile because the method name is much better.
* </p>
*
* @param visitor
* the visitor used to process the queried beans.
*
* @deprecated
*/
void findVisit(QueryResultVisitor<T> visitor);
/**
* Execute the query processing the beans one at a time.
@@ -716,8 +717,18 @@ public interface Query<T> extends Serializable {
* ...
* }</pre>
*/
@Nullable
T findUnique();
/**
* Return versions of a @History entity bean.
* <p>
* Generally this query is expected to be a find by id or unique predicates query.
* It will execute the query against the history returning the versions of the bean.
* </p>
*/
List<Version<T>> findVersions();
/**
* Return the count of entities this query should return.
* <p>
@@ -878,6 +889,11 @@ public interface Query<T> extends Serializable {
*/
Query<T> setId(Object id);
/**
* Return the Id value.
*/
Object getId();
/**
* Add additional clause(s) to the where clause.
* <p>
@@ -1123,8 +1139,8 @@ public interface Query<T> extends Serializable {
/**
* Set the first row to return for this query.
*
* @param firstRow
*
* @param firstRow the first row to include in the query result.
*/
Query<T> setFirstRow(int firstRow);
@@ -1235,4 +1251,10 @@ public interface Query<T> extends Serializable {
* Set root table alias.
*/
Query<T> alias(String alias);
/**
* Return the type of beans being queried.
*/
Class<T> getBeanType();
}
@@ -1,49 +0,0 @@
package com.avaje.ebean;
/**
* Used to process a query result one bean at a time via a callback to this
* visitor.
* <p>
* If you wish to stop further processing return false from the accept method.
* </p>
* <p>
* Unlike findList() and findSet() using a QueryResultVisitor does not require
* all the beans in the query result to be held in memory at once. This makes
* QueryResultVisitor useful for processing large queries.
* </p>
*
* <pre class="code">
*
* Query&lt;Customer&gt; query = server.find(Customer.class)
* .fetch(&quot;contacts&quot;, new FetchConfig().query(2))
* .where().gt(&quot;id&quot;, 0)
* .orderBy(&quot;id&quot;)
* .setMaxRows(2);
*
* query.findVisit(new QueryResultVisitor&lt;Customer&gt;() {
*
* public boolean accept(Customer customer) {
* // do something with customer
* System.out.println(&quot;-- visit &quot; + customer);
* return true;
* }
* });
* </pre>
*
* @author rbygrave
*
* @param <T>
* the type of entity bean being queried.
*/
public interface QueryResultVisitor<T> {
/**
* Process the bean and return true if you want to continue processing more
* beans. Return false if you want to stop processing further.
*
* @param bean
* the entity bean to process
* @return true to continue processing or false to stop.
*/
boolean accept(T bean);
}
@@ -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.
*
@@ -97,6 +97,17 @@ public interface Transaction extends Closeable {
*/
void setPersistCascade(boolean persistCascade);
/**
* Set to true when you want all loaded properties to be included in the update
* (rather than just the changed properties).
* <p>
* You might set this when using JDBC batch in order to get multiple updates
* with slightly different sets of changed properties into the same statement
* and hence better JDBC batch performance.
* </p>
*/
void setUpdateAllLoadedProperties(boolean updateAllLoadedProperties);
/**
* Turn on or off statement batching. Statement batching can be transparent
* for drivers and databases that support getGeneratedKeys. Otherwise you may
+18 -11
View File
@@ -5,10 +5,19 @@ package com.avaje.ebean;
*/
public class ValuePair {
private final Object newValue;
protected Object newValue;
private final Object oldValue;
protected Object oldValue;
/**
* Default constructor for JSON tools.
*/
public ValuePair() {
}
/**
* Construct with the pair of new and old values.
*/
public ValuePair(Object newValue, Object oldValue) {
this.newValue = newValue;
this.oldValue = oldValue;
@@ -27,21 +36,19 @@ public class ValuePair {
public Object getOldValue() {
return oldValue;
}
/**
* Return the new value.
* Set the new value.
*/
@Deprecated
public Object getValue1() {
return newValue;
public void setNewValue(Object newValue) {
this.newValue = newValue;
}
/**
* Return the old value.
* Set the old value.
*/
@Deprecated
public Object getValue2() {
return oldValue;
public void setOldValue(Object oldValue) {
this.oldValue = oldValue;
}
public String toString() {
+101
View File
@@ -0,0 +1,101 @@
package com.avaje.ebean;
import java.sql.Timestamp;
import java.util.Map;
/**
* Wraps a version of a @History bean.
*/
public class Version<T> {
/**
* The version of the bean.
*/
protected T bean;
/**
* The effective start date time of this version.
*/
protected Timestamp start;
/**
* The effective end date time of this version.
*/
protected Timestamp end;
/**
* The map of changed properties.
*/
protected Map<String, ValuePair> diff;
/**
* Construct with bean and an effective date time range.
*/
public Version(T bean, Timestamp start, Timestamp end) {
this.bean = bean;
this.start = start;
this.end = end;
}
/**
* Default constructor - useful for JSON tools such as Jackson.
*/
public Version() {
}
/**
* Return the bean instance for this version.
*/
public T getBean() {
return bean;
}
/**
* Set the bean instance for this version.
*/
public void setBean(T bean) {
this.bean = bean;
}
/**
* Return the effective start date time of this version.
*/
public Timestamp getStart() {
return start;
}
/**
* Set the effective start date time of this version.
*/
public void setStart(Timestamp start) {
this.start = start;
}
/**
* Return the effective end date time of this version.
*/
public Timestamp getEnd() {
return end;
}
/**
* Set the effective end date time of this version.
*/
public void setEnd(Timestamp end) {
this.end = end;
}
/**
* Set the map of differences from this bean to the prior version.
*/
public void setDiff(Map<String, ValuePair> diff) {
this.diff = diff;
}
/**
* Return the map of differences from this bean to the prior version.
*/
public Map<String, ValuePair> getDiff() {
return diff;
}
}
@@ -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;
}
@@ -1,17 +0,0 @@
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Deprecated - please use @DbHstore instead.
* @see DbHstore
*/
@Deprecated
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ColumnHstore {
}
@@ -0,0 +1,19 @@
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;
/**
* Marks an entity bean as having history support.
* <p>
* In Postgres for example this means there is an associated history table which is
* typically automatically populated via database triggers.
* </p>
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface History {
}
@@ -0,0 +1,25 @@
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;
/**
* Marks a property as being excluded from history.
* <p>
* This means the property values are not maintained in the history table. Typically this
* would be placed on relatively large properties (Clobs, Blobs, large varchar columns etc)
* that are considered not interesting enough to maintain history on excluding them reduces
* underlying database costs.
* </p>
* <p>
* When placed on a ManyToMany this means that the intersection table does not have history
* support.
* </p>
*/
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface HistoryExclude {
}
@@ -0,0 +1,32 @@
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;
/**
* Similar to Jackson JsonIgnore but provides the option to just ignore serialize or deserialize.
* <p>
* This provides the same features as Expose but from the opposite perspective which is probably
* more common and more familiar to Jackson users.
* </p>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JsonIgnore {
/**
* 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 false}.
*/
boolean serialize() default false;
/**
* 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 false}.
*/
boolean deserialize() default false;
}
@@ -0,0 +1,38 @@
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* For a timestamp property that is set to the datetime when the entity is
* created/inserted.
* <p>
* This is effectively an alias for @CreatedTimestamp and added to hint
* towards a better naming convention (WhenCreated, WhenModified).
* </p>
* <p>
* An alternative to using this annotation would be to use insertable=false,
* updateable=false with @Column and have the DB insert the current time
* (default value on the DB column is SYSTIME etc).
* </p>
* <p>
* The downside to this approach is that the inserted entity does not have the
* 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>
*
* <h3>Example:</h3>
* <pre>{@code
*
* @WhenCreated
* Timestamp whenCreated;
*
* }</pre>
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface WhenCreated {
}
@@ -0,0 +1,20 @@
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* For a timestamp property that is set to the datetime when the entity was last modified.
* <p>
* This is effectively an alias for @UpdatedTimestamp and added to hint
* towards a better naming convention (WhenCreated, WhenModified).
* </p>
* </p>
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface WhenModified {
}
@@ -0,0 +1,29 @@
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;
/**
* Mapped onto a entity bean property that represents the user id of
* who created the entity>
* <p>
* To use this annotation you need to implement CurrentUserProvider.
* The type of the bean property should match the type returned by
* CurrentUserProvider.
*</p>
* <h3>Example:</h3>
* <pre>{@code
*
* @WhoCreated
* String whoCreated;
*
* }</pre>
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface WhoCreated {
}
@@ -0,0 +1,29 @@
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;
/**
* Mapped onto a entity bean property that represents the user id of
* who last modified the entity>
* <p>
* To use this annotation you need to implement CurrentUserProvider.
* The type of the bean property should match the type returned by
* CurrentUserProvider.
* </p>
*
* <h3>Example:</h3>
* <pre>{@code
*
* @WhoModified
* String whoModified;
*
* }</pre>
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface WhoModified {
}
@@ -608,7 +608,7 @@ public final class EntityBeanIntercept implements Serializable {
} else if (embeddedDirty != null && embeddedDirty[i]) {
// an embedded property has been changed - recurse
EntityBean embeddedBean = (EntityBean)owner._ebean_getField(i);
embeddedBean._ebean_getIntercept().addDirtyPropertyValues(dirtyValues, getProperty(i)+".");
embeddedBean._ebean_getIntercept().addDirtyPropertyValues(dirtyValues, getProperty(i) + ".");
}
}
}
@@ -638,6 +638,20 @@ public final class EntityBeanIntercept implements Serializable {
return hash;
}
/**
* Return a loaded property hash.
*/
public int getLoadedPropertyHash() {
int hash = 37;
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
if (isLoadedProperty(i)) {
hash = hash * 31 + (i+1);
}
}
return hash;
}
/**
* Return the set of property names for changed properties.
*/
-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;
}
}
@@ -0,0 +1,16 @@
package com.avaje.ebean.config;
/**
* Provides the current user in order to support 'Who created', 'Who modified' and other audit features.
*/
public interface CurrentUserProvider {
/**
* Return the current user id.
* <p>
* The type returned should match the type of the properties annotated
* with @WhoCreated and @WhoModified. These are typically String, Long or UUID.
* </p>
*/
Object currentUser();
}
@@ -26,4 +26,22 @@ public abstract class JsonConfig {
ISO8601
}
public enum Include {
/**
* Include all values including null and empty collections.
*/
ALL,
/**
* Exclude null values (include empty collections).
*/
NON_NULL,
/**
* Exclude null values and empty collections.
*/
NON_EMPTY
}
}
@@ -113,6 +113,12 @@ public class ServerConfig {
*/
private String classPathReaderClassName;
/**
* This is used to populate @WhoCreated, @WhoModified and
* support other audit features (who executed a query etc).
*/
private CurrentUserProvider currentUserProvider;
/**
* Config controlling the autofetch behaviour.
*/
@@ -123,6 +129,12 @@ public class ServerConfig {
*/
private JsonConfig.DateTime jsonDateTime = JsonConfig.DateTime.MILLIS;
/**
* For writing JSON specify if null values or empty collections should be exluded.
* By default all values are included.
*/
private JsonConfig.Include jsonInclude = JsonConfig.Include.ALL;
/**
* The database platform name. Used to imply a DatabasePlatform to use.
*/
@@ -138,6 +150,18 @@ public class ServerConfig {
*/
private int databaseSequenceBatchSize = 20;
/**
* Suffix appended to the base table to derive the view that contains the union
* of the base table and the history table in order to support asOf queries.
*/
private String asOfViewSuffix = "_with_history";
/**
* Column used to support history and 'As of' queries. This column is a timestamp range
* or equivalent.
*/
private String asOfSysPeriod = "sys_period";
/**
* Use for transaction scoped batch mode.
*/
@@ -219,7 +243,12 @@ public class ServerConfig {
* Behaviour of update to include on the change properties.
*/
private boolean updateChangesOnly = true;
/**
* Behaviour of updates in JDBC batch to by default include all properties.
*/
private boolean updateAllPropertiesInBatch = true;
/**
* Default behaviour for updates when cascade save on a O2M or M2M to delete any missing children.
*/
@@ -293,6 +322,7 @@ public class ServerConfig {
private int queryCacheMaxIdleTime = 600;
private int queryCacheMaxTimeToLive = 60*60*6;
private Object objectMapper;
private boolean diffFlatMode;
/**
* Construct a Server Configuration for programmatically creating an EbeanServer.
@@ -304,7 +334,7 @@ public class ServerConfig {
/**
* Return the Jackson JsonFactory to use.
* <p>
* If not set a default implmentation will be used.
* If not set a default implementation will be used.
*/
public JsonFactory getJsonFactory() {
return jsonFactory;
@@ -313,7 +343,7 @@ public class ServerConfig {
/**
* Set the Jackson JsonFactory to use.
* <p>
* If not set a default implmentation will be used.
* If not set a default implementation will be used.
*/
public void setJsonFactory(JsonFactory jsonFactory) {
this.jsonFactory = jsonFactory;
@@ -333,6 +363,23 @@ public class ServerConfig {
this.jsonDateTime = jsonDateTime;
}
/**
* Return the JSON include mode used when writing JSON.
*/
public JsonConfig.Include getJsonInclude() {
return jsonInclude;
}
/**
* Set the JSON include mode used when writing JSON.
* <p>
* Set to NON_NULL or NON_EMPTY to suppress nulls or null & empty collections respectively.
* </p>
*/
public void setJsonInclude(JsonConfig.Include jsonInclude) {
this.jsonInclude = jsonInclude;
}
/**
* Return the name of the EbeanServer.
*/
@@ -411,6 +458,22 @@ public class ServerConfig {
this.defaultServer = defaultServer;
}
/**
* Return the CurrentUserProvider. This is used to populate @WhoCreated, @WhoModified and
* support other audit features (who executed a query etc).
*/
public CurrentUserProvider getCurrentUserProvider() {
return currentUserProvider;
}
/**
* Set the CurrentUserProvider. This is used to populate @WhoCreated, @WhoModified and
* support other audit features (who executed a query etc).
*/
public void setCurrentUserProvider(CurrentUserProvider currentUserProvider) {
this.currentUserProvider = currentUserProvider;
}
/**
* Return the PersistBatch mode to use by default at the transaction level.
* <p>
@@ -556,6 +619,38 @@ public class ServerConfig {
this.databaseSequenceBatchSize = databaseSequenceBatchSize;
}
/**
* Return the suffix appended to the base table to derive the view that contains the union
* of the base table and the history table in order to support asOf queries.
*/
public String getAsOfViewSuffix() {
return asOfViewSuffix;
}
/**
* Set the suffix appended to the base table to derive the view that contains the union
* of the base table and the history table in order to support asOf queries.
*/
public void setAsOfViewSuffix(String asOfViewSuffix) {
this.asOfViewSuffix = asOfViewSuffix;
}
/**
* Return the database column used to support history and 'As of' queries. This column is a timestamp range
* or equivalent.
*/
public String getAsOfSysPeriod() {
return asOfSysPeriod;
}
/**
* Set the database column used to support history and 'As of' queries. This column is a timestamp range
* or equivalent.
*/
public void setAsOfSysPeriod(String asOfSysPeriod) {
this.asOfSysPeriod = asOfSysPeriod;
}
/**
* Return true if we are running in a JTA Transaction manager.
*/
@@ -1426,7 +1521,26 @@ public class ServerConfig {
public void setUpdateChangesOnly(boolean updateChangesOnly) {
this.updateChangesOnly = updateChangesOnly;
}
/**
* Returns true if updates in JDBC batch default to include all properties by default.
*/
public boolean isUpdateAllPropertiesInBatch() {
return updateAllPropertiesInBatch;
}
/**
* Set to false if by default updates in JDBC batch should not include all properties.
* <p>
* This mode can be explicitly set per transaction.
* </p>
*
* @see com.avaje.ebean.Transaction#setUpdateAllLoadedProperties(boolean)
*/
public void setUpdateAllPropertiesInBatch(boolean updateAllPropertiesInBatch) {
this.updateAllPropertiesInBatch = updateAllPropertiesInBatch;
}
/**
* Return true if updates by default delete missing children when cascading save to a OneToMany or
* ManyToMany. When not set this defaults to true.
@@ -1745,6 +1859,7 @@ public class ServerConfig {
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
currentUserProvider = createInstance(p, CurrentUserProvider.class, "currentUserProvider");
disableClasspathSearch = p.getBoolean("disableClasspathSearch", disableClasspathSearch);
databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform");
encryptKeyManager = createInstance(p, EncryptKeyManager.class, "encryptKeyManager");
@@ -1769,6 +1884,7 @@ public class ServerConfig {
collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", collectQueryStatsByNode);
collectQueryOrigins = p.getBoolean("collectQueryOrigins", collectQueryOrigins);
updateAllPropertiesInBatch = p.getBoolean("updateAllPropertiesInBatch", updateAllPropertiesInBatch);
updateChangesOnly = p.getBoolean("updateChangesOnly", updateChangesOnly);
boolean defaultDeleteMissingChildren = p.getBoolean("defaultDeleteMissingChildren", updatesDeleteMissingChildren);
@@ -1784,8 +1900,11 @@ public class ServerConfig {
int batchSize = p.getInt("batch.size", persistBatchSize);
persistBatchSize = p.getInt("persistBatchSize", batchSize);
persistenceContextScope = PersistenceContextScope.valueOf(p.get("persistenceContextScope","TRANSACTION"));
persistenceContextScope = PersistenceContextScope.valueOf(p.get("persistenceContextScope", "TRANSACTION"));
diffFlatMode = p.getBoolean("diffFlatMode", diffFlatMode);
asOfViewSuffix = p.get("asOfViewSuffix", asOfViewSuffix);
asOfSysPeriod = p.get("asOfSysPeriod", asOfSysPeriod);
dataSourceJndiName = p.get("dataSourceJndiName", dataSourceJndiName);
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize);
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
@@ -1797,6 +1916,7 @@ public class ServerConfig {
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", lazyLoadBatchSize);
queryBatchSize = p.getInt("queryBatchSize", queryBatchSize);
jsonInclude = p.getEnum(JsonConfig.Include.class, "jsonInclude", jsonInclude);
String jsonDateTimeFormat = p.get("jsonDateTime", null);
if (jsonDateTimeFormat != null) {
jsonDateTime = JsonConfig.DateTime.valueOf(jsonDateTimeFormat);
@@ -1874,11 +1994,39 @@ public class ServerConfig {
return databasePlatform.isDisallowBatchOnCascade() ? PersistBatch.NONE : persistBatchOnCascade;
}
/**
* Return the Jackson ObjectMapper.
* <p>
* Note that this is not strongly typed as Jackson ObjectMapper is an optional dependency.
* </p>
*/
public Object getObjectMapper() {
return objectMapper;
}
/**
* Set the Jackson ObjectMapper.
* <p>
* Note that this is not strongly typed as Jackson ObjectMapper is an optional dependency.
* </p>
*/
public void setObjectMapper(Object objectMapper) {
this.objectMapper = objectMapper;
}
/**
* Return true if diff should return flat properties with dot notation rather than
* embedded beans or reference beans (when the associated bean id is different).
*/
public boolean isDiffFlatMode() {
return diffFlatMode;
}
/**
* Set to true if diff should return flat properties with dot notation rather than
* embedded beans or reference beans (when the associated bean id is different).
*/
public void setDiffFlatMode(boolean diffFlatMode) {
this.diffFlatMode = diffFlatMode;
}
}
@@ -80,6 +80,11 @@ public class DatabasePlatform {
*/
protected DbIdentity dbIdentity = new DbIdentity();
/**
* The history support for this database platform.
*/
protected DbHistorySupport historySupport;
/**
* The JDBC type to map booleans to (by default).
*/
@@ -215,6 +220,20 @@ public class DatabasePlatform {
this.dbEncrypt = dbEncrypt;
}
/**
* Return the history support for this database platform.
*/
public DbHistorySupport getHistorySupport() {
return historySupport;
}
/**
* Set the history support for this database platform.
*/
public void setHistorySupport(DbHistorySupport historySupport) {
this.historySupport = historySupport;
}
/**
* Return the mapping of JDBC to DB types.
*
@@ -0,0 +1,33 @@
package com.avaje.ebean.config.dbplatform;
/**
* History support for the database platform.
*/
public interface DbHistorySupport {
/**
* Return the 'as of' predicate added for the given table alias.
*
* @param tableAlias The table alias this predicate is added for
* @param sysPeriod The name of the 'sys_period' column used for effective date time range.
* @return The predicate containing a single ? bind parameter which will be bound to the 'as at' timestamp value
*/
String getAsOfPredicate(String tableAlias, String sysPeriod);
/**
* Return the column for the system period lower bound that will be included in findVersions() queries.
*
* @param tableAlias the table alias which will typically be 't0'
* @param sysPeriod the name of the sys_period column
*/
String getSysPeriodLower(String tableAlias, String sysPeriod);
/**
* Return the column for the system period upper bound that will be included in findVersions() queries.
*
* @param tableAlias the table alias which will typically be 't0'
* @param sysPeriod the name of the sys_period column
*/
String getSysPeriodUpper(String tableAlias, String sysPeriod);
}
@@ -21,6 +21,7 @@ public class Postgres8Platform extends DatabasePlatform {
this.clobDbType = Types.VARCHAR;
this.dbEncrypt = new PostgresDbEncrypt();
this.historySupport = new PostgresHistorySupport();
this.dbIdentity.setSupportsGetGeneratedKeys(false);
this.dbIdentity.setIdType(IdType.SEQUENCE);
@@ -47,15 +48,13 @@ public class Postgres8Platform extends DatabasePlatform {
dbDdlSyntax.setDropTableCascade("cascade");
dbDdlSyntax.setDropIfExists("if exists");
}
/**
* Create a Postgres specific sequence IdGenerator.
*/
@Override
public IdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds,
String seqName, int batchSize) {
public IdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
return new PostgresSequenceIdGenerator(be, ds, seqName, batchSize);
}
@@ -0,0 +1,33 @@
package com.avaje.ebean.config.dbplatform;
/**
* Postgres support for history features.
*/
public class PostgresHistorySupport implements DbHistorySupport {
/**
* Build and return the 'as of' predicate for a given table alias.
* <p>
* Each @History entity involved in the query has this predicate added using the related table alias.
* </p>
*/
@Override
public String getAsOfPredicate(String asOfTableAlias, String asOfSysPeriod) {
// for Postgres we are using the 'timestamp with timezone range' data type
// as our sys_period column so hence the predicate below
StringBuilder sb = new StringBuilder(40);
sb.append(asOfTableAlias).append(".").append(asOfSysPeriod).append(" @> ?::timestamptz");
return sb.toString();
}
@Override
public String getSysPeriodLower(String tableAlias, String sysPeriod) {
return "lower("+tableAlias+"."+sysPeriod+")";
}
@Override
public String getSysPeriodUpper(String tableAlias, String sysPeriod) {
return "upper("+tableAlias+"."+sysPeriod+")";
}
}
@@ -27,6 +27,7 @@ public class PostgresPlatform extends DatabasePlatform {
this.clobDbType = Types.VARCHAR;
this.dbEncrypt = new PostgresDbEncrypt();
this.historySupport = new PostgresHistorySupport();
// Use Identity and getGeneratedKeys
this.dbIdentity.setIdType(IdType.IDENTITY);
@@ -57,15 +58,14 @@ public class PostgresPlatform extends DatabasePlatform {
dbDdlSyntax.setDropTableCascade("cascade");
dbDdlSyntax.setDropIfExists("if exists");
}
/**
* Create a Postgres specific sequence IdGenerator.
*/
@Override
public IdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds,
String seqName, int batchSize) {
public IdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
return new PostgresSequenceIdGenerator(be, ds, seqName, batchSize);
}
@@ -1,37 +0,0 @@
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
* 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>
* @deprecated
*/
public interface BeanFinder<T> {
/**
* Find a bean using its id or unique predicate.
*/
T find(BeanQueryRequest<T> 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>
*/
BeanCollection<T> findMany(BeanQueryRequest<T> request);
}
@@ -163,8 +163,6 @@ public interface JsonContext {
/**
* Deprecated in favour of using PathProperties by itself.
* Write json to the JsonGenerator using the JsonWriteOptions.
*
* @deprecated
*/
void toJson(Object value, JsonGenerator generator, JsonWriteOptions options) throws JsonIOException;
@@ -173,7 +171,6 @@ public interface JsonContext {
* With additional options.
*
* @throws JsonIOException When IOException occurs
* @deprecated
*/
void toJson(Object value, Writer writer, JsonWriteOptions options) throws JsonIOException;
@@ -182,7 +179,6 @@ public interface JsonContext {
* Convert a bean or collection to json string.
*
* @throws JsonIOException When IOException occurs
* @deprecated
*/
String toJson(Object value, JsonWriteOptions options) throws JsonIOException;
@@ -15,6 +15,8 @@ public class JsonReadOptions {
protected Map<String, JsonReadBeanVisitor<?>> visitorMap;
protected Object objectMapper;
/**
* Default constructor.
*/
@@ -44,4 +46,17 @@ public class JsonReadOptions {
return this;
}
/**
* Return the Jackson ObjectMapper to use (if not wanted to use the objectMapper set on the ServerConfig).
*/
public Object getObjectMapper() {
return objectMapper;
}
/**
* Set the Jackson ObjectMapper to use (if not wanted to use the objectMapper set on the ServerConfig).
*/
public void setObjectMapper(Object objectMapper) {
this.objectMapper = objectMapper;
}
}
@@ -1,21 +1,23 @@
package com.avaje.ebean.text.json;
import com.avaje.ebean.config.JsonConfig;
import com.avaje.ebean.text.PathProperties;
/**
* Deprecated in favour of just using PathProperties.
*
* Provides options for customising the JSON write process.
* <p>
* You can explicitly state which properties to include in the JSON output for
* the root level and each path.
* </p>
* @deprecated
*/
public class JsonWriteOptions {
protected PathProperties pathProperties;
protected Object objectMapper;
protected JsonConfig.Include include;
/**
* Parse and return a PathProperties from nested string format like
* (a,b,c(d,e),f(g)) where "c" is a path containing "d" and "e" and "f" is a
@@ -51,4 +53,35 @@ public class JsonWriteOptions {
return pathProperties;
}
/**
* Return the include mode for this request.
*/
public JsonConfig.Include getInclude() {
return include;
}
/**
* Set the include mode for this request.
*/
public void setInclude(JsonConfig.Include include) {
this.include = include;
}
/**
* Return the jackson object mapper to use.
* <p/>
* If null the ObjectMapper from serverConfig will be used.
*/
public Object getObjectMapper() {
return objectMapper;
}
/**
* Set the jackson object mapper to use.
* <p/>
* If null the ObjectMapper from serverConfig will be used.
*/
public void setObjectMapper(Object objectMapper) {
this.objectMapper = objectMapper;
}
}
@@ -6,88 +6,108 @@ import org.slf4j.LoggerFactory;
/**
* Helper to find classes taking into account the context class loader.
*
* @author rbygrave
*/
public class ClassUtil {
private static final Logger logger = LoggerFactory.getLogger(ClassUtil.class);
private static boolean preferContext = true;
private static final Logger logger = LoggerFactory.getLogger(ClassUtil.class);
/**
* Load a class taking into account a context class loader (if present).
*/
public static Class<?> forName(String name) throws ClassNotFoundException {
return forName(name, null);
}
/**
* Load a class taking into account a context class loader (if present).
*/
public static Class<?> forName(String name, Class<?> caller) throws ClassNotFoundException {
if (caller == null){
caller = ClassUtil.class;
}
ClassLoadContext ctx = ClassLoadContext.of(caller, preferContext);
return ctx.forName(name);
}
private static boolean preferContext = true;
public static ClassLoader getClassLoader(Class<?> caller, boolean preferContext) {
if (caller == null){
caller = ClassUtil.class;
}
ClassLoadContext ctx = ClassLoadContext.of(caller, preferContext);
ClassLoader classLoader = ctx.getDefault(preferContext);
if (ctx.isAmbiguous()){
logger.info("Ambigous ClassLoader (Context vs Caller) chosen "+classLoader);
}
return classLoader;
}
/**
* Load a class taking into account a context class loader (if present).
*/
public static Class<?> forName(String name) throws ClassNotFoundException {
return forName(name, null);
}
/**
* Return true if the given class is present.
*/
public static boolean isPresent(String className) {
return isPresent(className, null);
}
/**
* Return true if the given class is present.
*/
public static boolean isPresent(String className, Class<?> caller) {
try {
forName(className, caller);
return true;
} catch (Throwable ex) {
// Class or one of its dependencies is not present...
return false;
}
}
/**
* Load a class taking into account a context class loader (if present).
*/
public static Class<?> forName(String name, Class<?> caller) throws ClassNotFoundException {
/**
* Return a new instance of the class using the default constructor.
*/
public static Object newInstance(String className) {
return newInstance(className,null);
if (caller == null) {
caller = ClassUtil.class;
}
/**
* Return a new instance of the class using the default constructor.
*/
public static Object newInstance(String className, Class<?> caller) {
try {
Class<?> cls = forName(className, caller);
return cls.newInstance();
} catch (Exception e){
String msg = "Error constructing "+className;
throw new IllegalArgumentException(msg, e);
}
ClassLoadContext ctx = ClassLoadContext.of(caller, preferContext);
return ctx.forName(name);
}
public static ClassLoader getClassLoader(Class<?> caller, boolean preferContext) {
if (caller == null) {
caller = ClassUtil.class;
}
ClassLoadContext ctx = ClassLoadContext.of(caller, preferContext);
ClassLoader classLoader = ctx.getDefault(preferContext);
if (ctx.isAmbiguous()) {
logger.info("Ambigous ClassLoader (Context vs Caller) chosen " + classLoader);
}
return classLoader;
}
/**
* Return true if javax validation annotations like Size and NotNull are present.
*/
public static boolean isJavaxValidationAnnotationsPresent() {
return isPresent("javax.validation.constraints.NotNull", null);
}
/**
* Return true if Jackson annotations like JsonIgnore are present.
*/
public static boolean isJacksonAnnotationsPresent() {
return isPresent("com.fasterxml.jackson.annotation.JsonIgnore", null);
}
/**
* Return true if Jackson ObjectMapper is present.
*/
public static boolean isJacksonObjectMapperPresent() {
return isPresent("com.fasterxml.jackson.databind.ObjectMapper", null);
}
/**
* Return true if the given class is present.
*/
public static boolean isPresent(String className) {
return isPresent(className, null);
}
/**
* Return true if the given class is present.
*/
public static boolean isPresent(String className, Class<?> caller) {
try {
forName(className, caller);
return true;
} catch (Throwable ex) {
// Class or one of its dependencies is not present...
return false;
}
}
/**
* Return a new instance of the class using the default constructor.
*/
public static Object newInstance(String className) {
return newInstance(className, null);
}
/**
* Return a new instance of the class using the default constructor.
*/
public static Object newInstance(String className, Class<?> caller) {
try {
Class<?> cls = forName(className, caller);
return cls.newInstance();
} catch (Exception e) {
String msg = "Error constructing " + className;
throw new IllegalArgumentException(msg, e);
}
}
}
@@ -152,6 +152,16 @@ public class ScopedTransaction implements SpiTransaction {
transaction.setPersistCascade(persistCascade);
}
@Override
public void setUpdateAllLoadedProperties(boolean updateAllLoaded) {
transaction.setUpdateAllLoadedProperties(updateAllLoaded);
}
@Override
public Boolean isUpdateAllLoadedProperties() {
return transaction.isUpdateAllLoadedProperties();
}
@Override
public void setBatchMode(boolean useBatch) {
transaction.setBatchMode(useBatch);
@@ -32,7 +32,12 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
* Return true if query origins should be collected.
*/
boolean isCollectQueryOrigins();
/**
* Return true if updates in JDBC batch should include all columns if unspecified on the transaction.
*/
boolean isUpdateAllPropertiesInBatch();
/**
* Return the server configuration.
*/
@@ -1,15 +1,12 @@
package com.avaje.ebeaninternal.api;
import java.util.ArrayList;
import java.util.List;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.PersistenceContextScope;
import com.avaje.ebean.Query;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebean.bean.CallStack;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.event.BeanQueryRequest;
@@ -22,6 +19,9 @@ import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
import java.sql.Timestamp;
import java.util.List;
/**
* Object Relational query - Internal extension to Query object.
*/
@@ -87,6 +87,30 @@ public interface SpiQuery<T> extends Query<T> {
SUBQUERY
}
enum TemporalMode {
/**
* Query runs against current data (normal).
*/
CURRENT,
/**
* Query runs potentially returning many versions of the same bean.
*/
VERSIONS,
/**
* Query runs 'As Of' a given date time.
*/
AS_OF;
/**
* Return the mode of the query of if null return CURRENT mode.
*/
public static TemporalMode of(SpiQuery<?> query) {
return (query != null) ? query.getTemporalMode() : TemporalMode.CURRENT;
}
}
/**
* Return the PersistenceContextScope that this query should use.
* <p>
@@ -116,6 +140,31 @@ public interface SpiQuery<T> extends Query<T> {
*/
Mode getMode();
/**
* Return the Temporal mode for the query.
*/
TemporalMode getTemporalMode();
/**
* Return true if this is a 'As Of' query.
*/
boolean isAsOfQuery();
/**
* Return the asOf Timestamp which the query should run as.
*/
Timestamp getAsOf();
/**
* Add a table alias for a @History entity involved in a 'As Of' query.
*/
void addAsOfTableAlias(String tableAlias);
/**
* Return the list of table alias involved in a 'As Of' query that have @History support.
*/
List<String> getAsOfTableAlias();
/**
* Return a listener that wants to be notified when the bean collection is
* first used.
@@ -148,6 +197,11 @@ public interface SpiQuery<T> extends Query<T> {
*/
SpiQuery<T> copy();
/**
* Return a copy of the query attaching to a different EbeanServer.
*/
SpiQuery<T> copy(EbeanServer server);
/**
* Return the type of query (List, Set, Map, Bean, rowCount etc).
*/
@@ -171,18 +225,13 @@ public interface SpiQuery<T> extends Query<T> {
/**
* This becomes a lazy loading query for a many relationship.
*/
void setLazyLoadForParents(List<Object> parentIds, BeanPropertyAssocMany<?> many);
void setLazyLoadForParents(BeanPropertyAssocMany<?> many);
/**
* Return the lazy loading 'many' property.
*/
BeanPropertyAssocMany<?> getLazyLoadForParentsProperty();
/**
* Return the list of parent Id's for lazy loading.
*/
List<Object> getLazyLoadForParentIds();
/**
* Set the load mode (+lazy or +query) and the load description.
*/
@@ -203,11 +252,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
ManyWhereJoins getManyWhereJoins();
/**
* Convert this natural key query into a find by id query.
*/
void convertWhereNaturalKeyToId(Object idValue);
/**
* Return a Natural Key bind parameter if supported by this query.
*/
@@ -341,11 +385,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
String getLazyLoadProperty();
/**
* Return the lazy load path.
*/
String getLazyLoadManyPath();
/**
* Used to hook back a lazy loading query to the original query (query
* point).
@@ -476,27 +515,11 @@ public interface SpiQuery<T> extends Query<T> {
*/
Boolean isReadOnly();
/**
* Adds this bean to the persistence context prior to executing the query.
*/
void contextAdd(EntityBean bean);
/**
* Return the type of beans queries.
*/
Class<T> getBeanType();
/**
* Return the query timeout.
*/
int getTimeout();
/**
* Return the objects that should be added to the persistence context prior
* to executing the query.
*/
ArrayList<EntityBean> getContextAdditions();
/**
* Return the bind parameters.
*/
@@ -580,11 +603,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
String getRawWhereClause();
/**
* Return the Id value.
*/
Object getId();
/**
* Set the generated sql for debug purposes.
*/
@@ -99,6 +99,12 @@ public interface SpiTransaction extends Transaction {
*/
String getId();
/**
* Return true if this transaction has updateAllLoadedProperties set.
* If null is returned the server default is used (set on ServerConfig).
*/
Boolean isUpdateAllLoadedProperties();
/**
* Return the batchSize specifically set for this transaction or 0.
* <p>
@@ -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();
}
}
}
@@ -41,7 +41,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
private ArrayList<Class<?>> transactionEventListenerList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> beanFinderList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> beanFindControllerList = new ArrayList<Class<?>>();
private ArrayList<Class<?>> beanQueryAdapterList = new ArrayList<Class<?>>();
@@ -282,13 +281,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
return transactionEventListenerList;
}
/**
* Return the list of BeanFinders found.
*/
public List<Class<?>> getBeanFinders() {
return beanFinderList;
}
/**
* Return the list of BeanListeners found.
*/
@@ -351,11 +343,6 @@ public class BootupClasses implements ClassPathSearchMatcher {
interesting = true;
}
if (BeanFinder.class.isAssignableFrom(cls)) {
beanFinderList.add(cls);
interesting = true;
}
if (BeanFindController.class.isAssignableFrom(cls)) {
beanFindControllerList.add(cls);
interesting = true;
@@ -121,7 +121,7 @@ public class DefaultBeanLoader {
query.where().raw(ew);
}
query.setLazyLoadForParents(idList, many);
query.setLazyLoadForParents(many);
many.addWhereParentIdIn(query, idList);
query.setPersistenceContext(pc);
@@ -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);
@@ -38,6 +38,7 @@ import org.slf4j.LoggerFactory;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@@ -87,7 +88,7 @@ public final class DefaultServer implements SpiEbeanServer {
private final BeanDescriptorManager beanDescriptorManager;
private final DiffHelp diffHelp = new DiffHelp();
private final DiffHelp diffHelp;
private final AutoFetchManager autoFetchManager;
@@ -147,6 +148,8 @@ public final class DefaultServer implements SpiEbeanServer {
*/
private List<SpiEbeanPlugin> ebeanPlugins;
private final boolean updateAllPropertiesInBatch;
private final boolean collectQueryOrigins;
private final boolean collectQueryStatsByNode;
@@ -170,6 +173,7 @@ public final class DefaultServer implements SpiEbeanServer {
this.backgroundExecutor = config.getBackgroundExecutor();
this.serverName = serverConfig.getName();
this.diffHelp = new DiffHelp(serverConfig.isDiffFlatMode());
this.lazyLoadBatchSize = serverConfig.getLazyLoadBatchSize();
this.queryBatchSize = serverConfig.getQueryBatchSize();
this.cqueryEngine = config.getCQueryEngine();
@@ -180,6 +184,7 @@ public final class DefaultServer implements SpiEbeanServer {
this.beanDescriptorManager = config.getBeanDescriptorManager();
beanDescriptorManager.setEbeanServer(this);
this.updateAllPropertiesInBatch = serverConfig.isUpdateAllPropertiesInBatch();
this.collectQueryOrigins = serverConfig.isCollectQueryOrigins();
this.collectQueryStatsByNode = serverConfig.isCollectQueryStatsByNode();
this.maxCallStack = serverConfig.getMaxCallStack();
@@ -250,6 +255,11 @@ public final class DefaultServer implements SpiEbeanServer {
return collectQueryOrigins;
}
@Override
public boolean isUpdateAllPropertiesInBatch() {
return updateAllPropertiesInBatch;
}
public int getLazyLoadBatchSize() {
return lazyLoadBatchSize;
}
@@ -1162,16 +1172,15 @@ public final class DefaultServer implements SpiEbeanServer {
public <T> T findUnique(Query<T> query, Transaction t) {
// actually a find by Id type of query...
// ... perhaps with joins and cache hints?
SpiQuery<T> q = (SpiQuery<T>) query;
Object id = q.getId();
// ... perhaps with joins and cache hints
Object id = query.getId();
if (id != null) {
return findId(query, t);
}
BeanDescriptor<T> desc = beanDescriptorManager.getBeanDescriptor(q.getBeanType());
BeanDescriptor<T> desc = beanDescriptorManager.getBeanDescriptor(query.getBeanType());
T bean = desc.cacheNaturalKeyLookup(q, (SpiTransaction)t);
T bean = desc.cacheNaturalKeyLookup((SpiQuery<T>)query, (SpiTransaction) t);
if (bean != null) {
return bean;
}
@@ -1323,15 +1332,6 @@ public final class DefaultServer implements SpiEbeanServer {
return new LimitOffsetPagedList<T>(this, (SpiQuery<T>)query, pageIndex, pageSize);
}
public <T> void findVisit(Query<T> query, QueryResultVisitor<T> visitor, Transaction t) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
request.initTransIfRequired();
request.findVisit(visitor);
// no try finally - findVisit guarantee's cleanup of the transaction if required
}
public <T> void findEach(Query<T> query, QueryEachConsumer<T> consumer, Transaction t) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
@@ -1364,6 +1364,19 @@ public final class DefaultServer implements SpiEbeanServer {
}
}
@Override
public <T> List<Version<T>> findVersions(Query<T> query, Transaction transaction) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, transaction);
try {
request.initTransIfRequired();
return request.findVersions();
} finally {
request.endTransIfRequired();
}
}
@SuppressWarnings("unchecked")
public <T> List<T> findList(Query<T> query, Transaction t) {
@@ -1499,17 +1512,15 @@ public final class DefaultServer implements SpiEbeanServer {
persister.update(checkEntityBean(bean), t, deleteMissingChildren);
}
/**
* Update all beans in the collection.
*/
public void update(Collection<?> beans) {
update(beans, null);
@Override
public void updateAll(Collection<?> beans) throws OptimisticLockException {
updateAll(beans, null);
}
/**
* Update all beans in the collection with an explicit transaction.
*/
public void update(Collection<?> beans, Transaction t) {
public void updateAll(Collection<?> beans, Transaction t) {
if (beans == null || beans.isEmpty()) {
// Nothing to update?
@@ -1547,14 +1558,14 @@ public final class DefaultServer implements SpiEbeanServer {
/**
* Insert all beans in the collection.
*/
public void insert(Collection<?> beans) {
insert(beans, null);
public void insertAll(Collection<?> beans) {
insertAll(beans, null);
}
/**
* Insert all beans in the collection with a transaction.
*/
public void insert(Collection<?> beans, Transaction t) {
public void insertAll(Collection<?> beans, Transaction t) {
if (beans == null || beans.isEmpty()) {
// Nothing to insert?
@@ -1669,34 +1680,20 @@ public final class DefaultServer implements SpiEbeanServer {
}
}
/**
* Perform an update or insert on each bean in the iterator. Returns the
* number of beans that where saved.
*/
public int save(Iterator<?> it) {
return save(it, null);
@Override
public int saveAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException {
return saveAllInternal(beans.iterator(), transaction);
}
/**
* Perform an update or insert on each bean in the collection. Returns the
* number of beans that where saved.
*/
public int save(Collection<?> c) {
return save(c.iterator(), null);
@Override
public int saveAll(Collection<?> beans) throws OptimisticLockException {
return saveAllInternal(beans.iterator(), null);
}
/**
* Perform an update or insert on each bean in the collection. Returns the
* number of beans that where saved.
*/
public int save(Collection<?> c, Transaction t) {
return save(c.iterator(), t);
}
/**
* Save all beans in the iterator with an explicit transaction.
*/
public int save(Iterator<?> it, Transaction t) {
public int saveAllInternal(Iterator<?> it, Transaction t) {
TransWrapper wrap = initTransIfRequired(t);
try {
@@ -1739,11 +1736,13 @@ public final class DefaultServer implements SpiEbeanServer {
}
}
public void delete(Class<?> beanType, Collection<?> ids) {
delete(beanType, ids, null);
@Override
public void deleteAll(Class<?> beanType, Collection<?> ids) {
deleteAll(beanType, ids, null);
}
public void delete(Class<?> beanType, Collection<?> ids, Transaction t) {
@Override
public void deleteAll(Class<?> beanType, Collection<?> ids, Transaction t) {
TransWrapper wrap = initTransIfRequired(t);
try {
@@ -1772,24 +1771,18 @@ public final class DefaultServer implements SpiEbeanServer {
persister.delete(checkEntityBean(bean), t);
}
/**
* Delete all the beans in the iterator.
*/
public int delete(Iterator<?> it) {
return delete(it, null);
}
/**
* Delete all the beans in the collection.
*/
public int delete(Collection<?> c) {
return delete(c.iterator(), null);
@Override
public int deleteAll(Collection<?> beans) {
return deleteAllInternal(beans.iterator(), null);
}
/**
* Delete all the beans in the iterator with an explicit transaction.
*/
public int delete(Iterator<?> it, Transaction t) {
private int deleteAllInternal(Iterator<?> it, Transaction t) {
TransWrapper wrap = initTransIfRequired(t);
@@ -2002,12 +1995,6 @@ public final class DefaultServer implements SpiEbeanServer {
// immutable thread safe so return shared instance
return jsonContext;
}
@Override
public JsonContext createJsonContext() {
return json();
}
@Override
public void collectQueryStats(ObjectGraphNode node, long loadedBeanCount, long timeMicros) {
@@ -18,7 +18,12 @@ import com.avaje.ebeaninternal.util.ValueUtil;
*/
public class DiffHelp {
private final boolean flatMode;
public DiffHelp(boolean flatMode) {
this.flatMode = flatMode;
}
/**
* Return a map of the differences between a and b.
* <p>
@@ -29,45 +34,45 @@ public class DiffHelp {
* This intentionally does not include as OneToMany or ManyToMany properties.
* </p>
*/
public Map<String, ValuePair> diff(Object a, Object b, BeanDescriptor<?> desc) {
public Map<String, ValuePair> diff(Object newBean, Object oldBean, BeanDescriptor<?> desc) {
if (a instanceof EntityBean == false) {
throw new IllegalArgumentException("First bean expected to be an enhanced EntityBean? bean:"+a);
if (!(newBean instanceof EntityBean)) {
throw new IllegalArgumentException("First bean expected to be an enhanced EntityBean? bean:"+newBean);
}
if (b != null) {
if (b instanceof EntityBean == false) {
throw new IllegalArgumentException("Second bean expected to be an enhanced EntityBean? bean:"+b);
if (oldBean != null) {
if (!(oldBean instanceof EntityBean)) {
throw new IllegalArgumentException("Second bean expected to be an enhanced EntityBean? bean:"+oldBean);
}
if (!a.getClass().isAssignableFrom(b.getClass())) {
if (!newBean.getClass().isAssignableFrom(oldBean.getClass())) {
throw new IllegalArgumentException("Second bean not assignable to the first bean?");
}
}
if (b == null) {
return ((EntityBean) a)._ebean_getIntercept().getDirtyValues();
if (oldBean == null) {
return ((EntityBean) newBean)._ebean_getIntercept().getDirtyValues();
}
Map<String, ValuePair> map = new LinkedHashMap<String, ValuePair>();
diff(null, map, (EntityBean)a, (EntityBean)b, desc);
diff(null, map, (EntityBean) newBean, (EntityBean) oldBean, desc);
return map;
}
public void diff(String prefix, Map<String, ValuePair> map, EntityBean first, EntityBean sec, BeanDescriptor<?> desc) {
public void diff(String prefix, Map<String, ValuePair> map, EntityBean newBean, EntityBean oldBean, BeanDescriptor<?> desc) {
// check the simple properties
BeanProperty[] base = desc.propertiesBaseScalar();
for (int i = 0; i < base.length; i++) {
Object aval = base[i].getValue(first);
Object bval = base[i].getValue(sec);
if (!ValueUtil.areEqual(aval, bval)) {
Object newVal = (newBean == null) ? null : base[i].getValue(newBean);
Object oldVal = (oldBean == null) ? null : base[i].getValue(oldBean);
if (!ValueUtil.areEqual(newVal, oldVal)) {
String propName = (prefix == null) ? base[i].getName() : prefix + base[i].getName();
map.put(propName, new ValuePair(aval, bval));
map.put(propName, new ValuePair(newVal, oldVal));
}
}
diffAssocOne(prefix, first, sec, desc, map);
diffEmbedded(prefix, first, sec, desc, map);
diffAssocOne(prefix, newBean, oldBean, desc, map);
diffEmbedded(prefix, newBean, oldBean, desc, map);
}
/**
@@ -77,24 +82,29 @@ public class DiffHelp {
* determined to be different as is added to the map.
* </p>
*/
private void diffEmbedded(String prefix, EntityBean a, EntityBean b, BeanDescriptor<?> desc, Map<String, ValuePair> map) {
private void diffEmbedded(String prefix, EntityBean newBean, EntityBean oldBean, BeanDescriptor<?> desc, Map<String, ValuePair> map) {
BeanPropertyAssocOne<?>[] emb = desc.propertiesEmbedded();
for (int i = 0; i < emb.length; i++) {
EntityBean aval = (EntityBean)emb[i].getValue(a);
EntityBean bval = (EntityBean)emb[i].getValue(b);
EntityBean newVal = (EntityBean)emb[i].getValue(newBean);
EntityBean oldVal = (EntityBean)emb[i].getValue(oldBean);
if (!isBothNull(aval, bval)) {
if (!isBothNull(newVal, oldVal)) {
String propName = (prefix == null) ? emb[i].getName() : prefix + emb[i].getName();
if (isDiffNull(aval, bval)) {
if (isDiffNull(newVal, oldVal)) {
// one of the embedded beans is null
map.put(propName, new ValuePair(aval, bval));
if (flatMode) {
BeanDescriptor<?> embDesc = emb[i].getTargetDescriptor();
diff(emb[i].getName()+".", map, newVal, oldVal, embDesc);
} else {
map.put(propName, new ValuePair(newVal, oldVal));
}
} else {
// recursively diff into the embedded bean
BeanDescriptor<?> embDesc = emb[i].getTargetDescriptor();
diff(emb[i].getName()+".", map, aval, bval, embDesc);
diff(emb[i].getName()+".", map, newVal, oldVal, embDesc);
}
}
}
@@ -104,45 +114,43 @@ public class DiffHelp {
* If the properties are different by null OR if the id value is different,
* then add the Assoc One bean to the map.
*/
private void diffAssocOne(String prefix, EntityBean a, EntityBean b, BeanDescriptor<?> desc, Map<String, ValuePair> map) {
private void diffAssocOne(String prefix, EntityBean newBean, EntityBean oldBean, BeanDescriptor<?> desc, Map<String, ValuePair> map) {
BeanPropertyAssocOne<?>[] ones = desc.propertiesOne();
for (int i = 0; i < ones.length; i++) {
Object aval = ones[i].getValue(a);
Object bval = ones[i].getValue(b);
Object newVal = ones[i].getValue(newBean);
Object oldVal = ones[i].getValue(oldBean);
if (!isBothNull(aval, bval)) {
String propName = (prefix == null) ? ones[i].getName() : prefix + ones[i].getName();
if (isDiffNull(aval, bval)) {
// one of them is/was null
map.put(propName, new ValuePair(aval, bval));
if (!isBothNull(newVal, oldVal)) {
BeanDescriptor<?> oneDesc = ones[i].getTargetDescriptor();
Object newId = (newVal == null) ? null : oneDesc.getId((EntityBean)newVal);
Object oldId = (oldVal == null) ? null : oneDesc.getId((EntityBean)oldVal);
} else {
// check to see if the Id properties
// are different
BeanDescriptor<?> oneDesc = ones[i].getTargetDescriptor();
Object aOneId = oneDesc.getId((EntityBean)aval);
Object bOneId = oneDesc.getId((EntityBean)bval);
if (!ValueUtil.areEqual(newId, oldId)) {
String propName = (prefix == null) ? ones[i].getName() : prefix + ones[i].getName();
// the ids are different
if (flatMode) {
String idName = oneDesc.getIdProperty().getName();
map.put(propName + "." + idName, new ValuePair(newId, oldId));
if (!ValueUtil.areEqual(aOneId, bOneId)) {
// the ids are different
map.put(propName, new ValuePair(aval, bval));
}
}
}
} else {
map.put(propName, new ValuePair(newVal, oldVal));
}
}
}
}
}
private boolean isBothNull(Object aval, Object bval) {
return aval == null && bval == null;
private boolean isBothNull(Object newVal, Object oldVal) {
return newVal == null && oldVal == null;
}
private boolean isDiffNull(Object aval, Object bval) {
if (aval == null) {
return bval != null;
private boolean isDiffNull(Object newVal, Object oldVal) {
if (newVal == null) {
return oldVal != null;
} else {
return bval == null;
return oldVal == null;
}
}
}
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.core;
import javax.sql.DataSource;
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,6 +42,8 @@ import com.avaje.ebeaninternal.server.type.DefaultTypeManager;
import com.avaje.ebeaninternal.server.type.TypeManager;
import com.fasterxml.jackson.core.JsonFactory;
import java.util.Map;
/**
* Used to extend the ServerConfig with additional objects used to configure and
* construct an EbeanServer.
@@ -90,7 +93,7 @@ public class InternalConfiguration {
private final XmlConfig xmlConfig;
private final JsonFactory jsonFactory;
public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager,
ServerCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor,
ServerConfig serverConfig, BootupClasses bootupClasses, PstmtBatch pstmtBatch) {
@@ -116,11 +119,11 @@ public class InternalConfiguration {
this.deployUtil = new DeployUtil(typeManager, serverConfig);
this.beanDescriptorManager = new BeanDescriptorManager(this);
beanDescriptorManager.deploy();
Map<String, String> asOfTableMapping = beanDescriptorManager.deploy();
this.transactionManager = createTransactionManager();
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder);
this.cQueryEngine = new CQueryEngine(serverConfig.getDatabasePlatform(), binder, asOfTableMapping, serverConfig.getAsOfSysPeriod());
ExternalTransactionManager externalTransactionManager = serverConfig.getExternalTransactionManager();
if (externalTransactionManager == null && serverConfig.isUseJtaTransactionManager()) {
@@ -265,4 +268,7 @@ public class InternalConfiguration {
return backgroundExecutor;
}
public GeneratedPropertyFactory getGeneratedPropertyFactory() {
return new GeneratedPropertyFactory(serverConfig.getCurrentUserProvider());
}
}
@@ -1,38 +1,42 @@
package com.avaje.ebeaninternal.server.core;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.Version;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebeaninternal.api.BeanIdList;
import java.util.List;
/**
* The Object Relational query execution API.
*/
public interface OrmQueryEngine {
/**
* Execute the 'find by id' query returning a single bean.
*/
public <T> T findId(OrmQueryRequest<T> request);
/**
* Execute the 'find by id' query returning a single bean.
*/
<T> T findId(OrmQueryRequest<T> request);
/**
* Execute the findList, findSet, findMap query returning an appropriate BeanCollection.
*/
public <T> BeanCollection<T> findMany(OrmQueryRequest<T> request);
/**
* Execute the findList, findSet, findMap query returning an appropriate BeanCollection.
*/
<T> BeanCollection<T> findMany(OrmQueryRequest<T> request);
/**
* Execute the query using a QueryIterator.
*/
public <T> QueryIterator<T> findIterate(OrmQueryRequest<T> request);
/**
* Execute the row count query.
*/
public <T> int findRowCount(OrmQueryRequest<T> request);
/**
* Execute the find id's query.
*/
public <T> BeanIdList findIds(OrmQueryRequest<T> request);
<T> List<Version<T>> findVersions(OrmQueryRequest<T> request);
/**
* Execute the query using a QueryIterator.
*/
<T> QueryIterator<T> findIterate(OrmQueryRequest<T> request);
/**
* Execute the row count query.
*/
<T> int findRowCount(OrmQueryRequest<T> request);
/**
* Execute the find id's query.
*/
<T> BeanIdList findIds(OrmQueryRequest<T> request);
}
@@ -259,19 +259,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
}
}
public void findVisit(QueryResultVisitor<T> visitor) {
QueryIterator<T> it = queryEngine.findIterate(this);
try {
while (it.hasNext()) {
if (!visitor.accept(it.next())) {
break;
}
}
} finally {
it.close();
}
}
public QueryIterator<T> findIterate() {
return queryEngine.findIterate(this);
}
@@ -284,6 +271,10 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
return (List<T>) queryEngine.findMany(this);
}
public List<Version<T>> findVersions() {
return queryEngine.findVersions(this);
}
/**
* Execute the query as findSet.
*/
@@ -117,6 +117,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
*/
private boolean batchOnCascadeSet;
/**
* Set for updates to determine if all loaded properties are included in the update.
*/
private boolean requestUpdateAllLoadedProps;
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
PersistExecute persistExecute, PersistRequest.Type type, boolean saveRecurse) {
@@ -544,6 +549,9 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
// if bean persisted again then should result in an update
intercept.setLoaded();
if (isInsert()) {
postInsert();
}
addEvent();
@@ -641,17 +649,21 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
/**
* Test if the property value has changed and if so include it in the update.
* Return true if the property should be included in the update.
*/
public boolean isAddToUpdate(BeanProperty prop) {
return intercept.isDirtyProperty(prop.getPropertyIndex());
if (requestUpdateAllLoadedProps) {
return intercept.isLoadedProperty(prop.getPropertyIndex());
} else {
return intercept.isDirtyProperty(prop.getPropertyIndex());
}
}
public List<DerivedRelationshipData> getDerivedRelationships() {
return transaction.getDerivedRelationship(bean);
}
public void postInsert() {
private void postInsert() {
// mark all properties as loaded after an insert to support immediate update
int len = intercept.getPropertyLength();
for (int i = 0; i < len; i++) {
@@ -706,4 +718,24 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
return updatedManysOnly;
}
/**
* Determine if all loaded properties should be used for an update.
* <p>
* Takes into account transaction setting and JDBC batch.
* </p>
*/
public boolean determineUpdateAllLoadedProperties() {
Boolean txnUpdateAll = transaction.isUpdateAllLoadedProperties();
if (txnUpdateAll != null) {
// use the setting explicitly set on the transaction
requestUpdateAllLoadedProps = txnUpdateAll;
} else {
// if using batch use the server default setting
requestUpdateAllLoadedProps = isBatchThisRequest() && ebeanServer.isUpdateAllPropertiesInBatch();
}
return requestUpdateAllLoadedProps;
}
}
@@ -1,115 +1,110 @@
package com.avaje.ebeaninternal.server.core;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.avaje.ebean.QueryEachConsumer;
import com.avaje.ebean.QueryEachWhileConsumer;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.QueryResultVisitor;
import com.avaje.ebean.Version;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Defines the ORM query request api.
*/
public interface SpiOrmQueryRequest<T> {
/**
* Return the query.
*/
public SpiQuery<T> getQuery();
/**
* Return the query.
*/
SpiQuery<T> getQuery();
/**
* Return the associated BeanDescriptor.
*/
public BeanDescriptor<?> getBeanDescriptor();
/**
* Return the associated BeanDescriptor.
*/
BeanDescriptor<?> getBeanDescriptor();
/**
* This will create a local (readOnly) transaction if no current transaction
* exists.
* <p>
* A transaction may have been passed in explicitly or currently be active
* in the thread local. If not, then a readOnly transaction is created to
* execute this query.
* </p>
*/
public void initTransIfRequired();
/**
* This will create a local (readOnly) transaction if no current transaction
* exists.
* <p>
* A transaction may have been passed in explicitly or currently be active
* in the thread local. If not, then a readOnly transaction is created to
* execute this query.
* </p>
*/
void initTransIfRequired();
/**
* Will end a locally created transaction.
* <p>
* It ends the transaction by using a rollback() as the transaction is known
* to be readOnly.
* </p>
*/
public void endTransIfRequired();
/**
* Will end a locally created transaction.
* <p>
* It ends the transaction by using a rollback() as the transaction is known
* to be readOnly.
* </p>
*/
void endTransIfRequired();
/**
* Execute the query as findById.
*/
public Object findId();
/**
* Execute the query as findById.
*/
Object findId();
/**
* Execute the find row count query.
*/
public int findRowCount();
/**
* Execute the find row count query.
*/
int findRowCount();
/**
* Execute the find ids query.
*/
public List<Object> findIds();
/**
* Execute the find returning a QueryIterator and visitor pattern.
*/
public void findVisit(QueryResultVisitor<T> visitor);
/**
* Execute the find ids query.
*/
List<Object> findIds();
/**
* Execute the find returning a QueryIterator and visitor pattern.
*/
public void findEach(QueryEachConsumer<T> consumer);
void findEach(QueryEachConsumer<T> consumer);
/**
* Execute the find returning a QueryIterator and visitor pattern.
*/
public void findEachWhile(QueryEachWhileConsumer<T> consumer);
void findEachWhile(QueryEachWhileConsumer<T> consumer);
/**
* Execute the find returning a QueryIterator.
*/
public QueryIterator<T> findIterate();
/**
* Execute the query as findList.
*/
public List<T> findList();
* Execute the find returning a QueryIterator.
*/
QueryIterator<T> findIterate();
/**
* Execute the query as findSet.
*/
public Set<?> findSet();
/**
* Execute the finVersions() query.
*/
List<Version<T>> findVersions();
/**
* Execute the query as findMap.
*/
public Map<?, ?> findMap();
/**
* Execute the query as findList.
*/
List<T> findList();
/**
* Try to get the object out of the persistence context.
*/
//public T getFromPersistenceContextOrCache();
/**
* Execute the query as findSet.
*/
Set<?> findSet();
/**
* Try to get the query result from the query cache.
*/
public BeanCollection<T> getFromQueryCache();
/**
* Execute the query as findMap.
*/
Map<?, ?> findMap();
/**
* Return the Database platform like clause.
*/
public String getDBLikeClause();
/**
* Try to get the query result from the query cache.
*/
BeanCollection<T> getFromQueryCache();
/**
* Return the Database platform like clause.
*/
String getDBLikeClause();
}
@@ -112,6 +112,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
* The base database table.
*/
private final String baseTable;
private final String baseTableAsOf;
private final boolean historySupport;
/**
* Map of BeanProperty Linked so as to preserve order.
@@ -332,8 +334,9 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
this.updateChangesOnly = deploy.isUpdateChangesOnly();
this.compoundUniqueConstraints = deploy.getCompoundUniqueConstraints();
this.historySupport = deploy.isHistorySupport();
this.baseTable = InternString.intern(deploy.getBaseTable());
this.baseTableAsOf = deploy.getBaseTableAsOf();
this.autoFetchTunable = EntityType.ORM.equals(entityType) && (beanFinder == null);
// helper object used to derive lists of properties
@@ -496,13 +499,19 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
* These properties need to be initialised prior to the association properties
* as they are used to get the imported and exported properties.
* </p>
* @param withHistoryTables map populated if @History is supported on this entity bean
*/
public void initialiseId() {
public void initialiseId(Map<String, String> withHistoryTables) {
if (logger.isTraceEnabled()) {
logger.trace("BeanDescriptor initialise " + fullName);
}
if (historySupport) {
// add mapping (used to swap out baseTable for asOf queries)
withHistoryTables.put(baseTable, baseTableAsOf);
}
if (inheritInfo != null) {
inheritInfo.setDescriptor(this);
}
@@ -522,8 +531,25 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
/**
* Initialise the exported and imported parts for associated properties.
*
* @param asOfTableMap the map of base tables to associated 'with history' tables
* @param asOfViewSuffix the suffix added to the table name to derive the 'with history' view name
*/
public void initialiseOther() {
public void initialiseOther(Map<String, String> asOfTableMap, String asOfViewSuffix) {
if (historySupport) {
// history support on this bean so check all associated intersection tables
// and if they are not excluded register the associated 'with history' table
for (int i = 0; i < propertiesManyToMany.length; i++) {
if (!propertiesManyToMany[i].isExcludedFromHistory()) {
// this intersection table has history support so also register
// it into the asOfTableMap
TableJoin intersectionTableJoin = propertiesManyToMany[i].getIntersectionTableJoin();
String intersectionTableName = intersectionTableJoin.getTable();
asOfTableMap.put(intersectionTableName, intersectionTableName + asOfViewSuffix);
}
}
}
if (!isEmbedded()) {
// initialise all the non-id properties
@@ -631,7 +657,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
}
public boolean calculateUseCache(Boolean queryUseCache) {
return (queryUseCache != null) ? queryUseCache.booleanValue() : isBeanCaching();
return (queryUseCache != null) ? queryUseCache : isBeanCaching();
}
/**
@@ -761,7 +787,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
}
public void cacheBeanPut(T bean) {
cacheBeanPutData((EntityBean)bean);
cacheBeanPutData((EntityBean) bean);
}
/**
@@ -1628,6 +1654,24 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return baseTable;
}
/**
* Return the base table to use given the query temporal mode.
*/
public String getBaseTable(SpiQuery.TemporalMode mode) {
switch (mode) {
case VERSIONS: return baseTableAsOf;
case AS_OF: return baseTableAsOf;
default: return baseTable;
}
}
/**
* Return true if this entity bean has history support.
*/
public boolean isHistorySupport() {
return historySupport;
}
/**
* Return the identity generation type.
*/
@@ -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;
}
@@ -10,6 +10,7 @@ import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.EncryptKey;
import com.avaje.ebean.config.EncryptKeyManager;
import com.avaje.ebean.config.NamingConvention;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.DbIdentity;
import com.avaje.ebean.config.dbplatform.IdGenerator;
@@ -51,7 +52,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private static final BeanDescComparator beanDescComparator = new BeanDescComparator();
private final ReadAnnotations readAnnotations = new ReadAnnotations();
private final ReadAnnotations readAnnotations;
private final TransientProperties transientProperties;
@@ -127,34 +128,45 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final boolean eagerFetchLobs;
private final String asOfViewSuffix;
/**
* Map of base tables to 'with history views' used to support 'as of' queries.
*/
private final Map<String,String> asOfTableMap = new HashMap<String, String>();
/**
* Create for a given database dbConfig.
*/
public BeanDescriptorManager(InternalConfiguration config) {
this.serverName = InternString.intern(config.getServerConfig().getName());
ServerConfig serverConfig = config.getServerConfig();
this.serverName = InternString.intern(serverConfig.getName());
this.cacheManager = config.getCacheManager();
this.xmlConfig = config.getXmlConfig();
this.dbSequenceBatchSize = config.getServerConfig().getDatabaseSequenceBatchSize();
this.dbSequenceBatchSize = serverConfig.getDatabaseSequenceBatchSize();
this.backgroundExecutor = config.getBackgroundExecutor();
this.dataSource = config.getServerConfig().getDataSource();
this.encryptKeyManager = config.getServerConfig().getEncryptKeyManager();
this.databasePlatform = config.getServerConfig().getDatabasePlatform();
this.dataSource = serverConfig.getDataSource();
this.encryptKeyManager = serverConfig.getEncryptKeyManager();
this.databasePlatform = serverConfig.getDatabasePlatform();
this.idBinderFactory = new IdBinderFactory(databasePlatform.isIdInExpandedForm());
this.eagerFetchLobs = config.getServerConfig().isEagerFetchLobs();
this.eagerFetchLobs = serverConfig.isEagerFetchLobs();
this.asOfViewSuffix = serverConfig.getAsOfViewSuffix();
this.readAnnotations = new ReadAnnotations(config.getGeneratedPropertyFactory(), asOfViewSuffix);
this.bootupClasses = config.getBootupClasses();
this.createProperties = config.getDeployCreateProperties();
this.typeManager = config.getTypeManager();
this.namingConvention = config.getServerConfig().getNamingConvention();
this.namingConvention = serverConfig.getNamingConvention();
this.dbIdentity = config.getDatabasePlatform().getDbIdentity();
this.deplyInherit = config.getDeployInherit();
this.deployOrmXml = config.getDeployOrmXml();
this.deployUtil = config.getDeployUtil();
this.beanManagerFactory = new BeanManagerFactory(config.getServerConfig(), config.getDatabasePlatform());
this.beanManagerFactory = new BeanManagerFactory(serverConfig, config.getDatabasePlatform());
this.updateChangesOnly = config.getServerConfig().isUpdateChangesOnly();
this.updateChangesOnly = serverConfig.isUpdateChangesOnly();
this.beanLifecycleAdapterFactory = new BeanLifecycleAdapterFactory();
this.persistControllerManager = new PersistControllerManager(bootupClasses);
@@ -205,7 +217,10 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
return idBinderFactory.createIdBinder(idProperty);
}
public void deploy() {
/**
* Deploy returning the asOfTableMap (which is required by the SQL builders).
*/
public Map<String,String> deploy() {
try {
createListeners();
@@ -237,6 +252,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
deplyInfoMap.clear();
deplyInfoMap = null;
return asOfTableMap;
} catch (RuntimeException e) {
String msg = "Error in deployment";
logger.error(msg, e);
@@ -324,7 +342,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
// first (as they are needed to initialise the
// associated properties in the second pass).
for (BeanDescriptor<?> d : descMap.values()) {
d.initialiseId();
d.initialiseId(asOfTableMap);
}
// PASS 2:
@@ -336,7 +354,10 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
// PASS 3:
// now initialise all the associated properties
for (BeanDescriptor<?> d : descMap.values()) {
d.initialiseOther();
// also look for intersection tables with
// associated history support and register them
// into the asOfTableMap
d.initialiseOther(asOfTableMap, asOfViewSuffix);
}
// create BeanManager for each non-embedded entity bean
@@ -973,7 +994,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
if (!EntityType.ORM.equals(desc.getEntityType())) {
// not using base table
desc.setBaseTable(null);
desc.setBaseTable(null, null);
}
// mark transient properties
@@ -1,45 +0,0 @@
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,16 +1,12 @@
package com.avaje.ebeaninternal.server.deploy;
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;
/**
* Default implementation for BeanFinderFactory.
@@ -19,31 +15,14 @@ public class BeanFinderManager {
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 list.size();
}
/**
@@ -58,28 +37,6 @@ public class BeanFinderManager {
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;
}
}
@@ -2,7 +2,6 @@ package com.avaje.ebeaninternal.server.deploy;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.avaje.ebean.EbeanServer;
@@ -141,11 +140,13 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
list = (List<?>) collection;
}
ctx.writeStartArray(name);
for (int j = 0; j < list.size(); j++) {
targetDescriptor.jsonWrite(ctx, (EntityBean) list.get(j));
if (!list.isEmpty() || ctx.isIncludeEmpty()) {
ctx.writeStartArray(name);
for (int j = 0; j < list.size(); j++) {
targetDescriptor.jsonWrite(ctx, (EntityBean) list.get(j));
}
ctx.writeEndArray();
}
ctx.writeEndArray();
}
}
@@ -177,12 +177,14 @@ public final class BeanMapHelp<T> implements BeanCollectionHelp<T> {
map = (Map<?, ?>) collection;
}
ctx.writeStartArray(name);
for (Entry<?, ?> entry : map.entrySet()) {
//FIXME: json write map key ...
targetDescriptor.jsonWrite(ctx, (EntityBean) entry.getValue());
if (!map.isEmpty() || ctx.isIncludeEmpty()) {
ctx.writeStartArray(name);
for (Entry<?, ?> entry : map.entrySet()) {
//FIXME: json write map key ...
targetDescriptor.jsonWrite(ctx, (EntityBean) entry.getValue());
}
ctx.writeEndArray();
}
ctx.writeEndArray();
}
}
@@ -22,6 +22,8 @@ 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.JsonToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import java.io.DataInput;
@@ -39,6 +41,8 @@ import java.util.Map;
*/
public class BeanProperty implements ElPropertyValue {
private static final Logger logger = LoggerFactory.getLogger(BeanProperty.class);
/**
* Flag to mark this at part of the unique id.
*/
@@ -169,6 +173,8 @@ public class BeanProperty implements ElPropertyValue {
*/
final int dbType;
final boolean excludedFromHistory;
/**
* The default value to insert if null.
*/
@@ -252,6 +258,7 @@ public class BeanProperty implements ElPropertyValue {
this.dbRead = deploy.isDbRead();
this.dbInsertable = deploy.isDbInsertable();
this.dbUpdatable = deploy.isDbUpdateable();
this.excludedFromHistory = deploy.isExcludedFromHistory();
this.secondaryTable = deploy.isSecondaryTable();
if (secondaryTable) {
@@ -299,8 +306,8 @@ public class BeanProperty implements ElPropertyValue {
this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), false, null);
this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), dbEncrypted, dbColumn);
this.jsonSerialize = deploy.isExposeSerialize();
this.jsonDeserialize = deploy.isExposeDeserialize();
this.jsonSerialize = deploy.isJsonSerialize();
this.jsonDeserialize = deploy.isJsonDeserialize();
}
private String tableAliasIntern(BeanDescriptor<?> descriptor, String s, boolean dbEncrypted, String dbColumn) {
@@ -337,6 +344,7 @@ public class BeanProperty implements ElPropertyValue {
this.sqlFormulaSelect = InternString.intern(override.getSqlFormulaSelect());
this.formula = sqlFormulaSelect != null;
this.excludedFromHistory = source.excludedFromHistory;
this.fetchEager = source.fetchEager;
this.unidirectionalShadow = source.unidirectionalShadow;
this.discriminator = source.discriminator;
@@ -533,7 +541,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 {
@@ -1000,6 +1008,13 @@ public class BeanProperty implements ElPropertyValue {
return dbEncryptedType;
}
/**
* Return true if this property is excluded from history.
*/
public boolean isExcludedFromHistory() {
return excludedFromHistory;
}
/**
* Return true if this property should be included in an Insert.
*/
@@ -1070,24 +1085,42 @@ public class BeanProperty implements ElPropertyValue {
}
Object value = getValueIntercept(bean);
if (value == null) {
writeJson.writeNull(name);
writeJson.writeNullField(name);
} else {
scalarType.jsonWrite(writeJson.gen(), name, value);
if (scalarType != null) {
scalarType.jsonWrite(writeJson, name, value);
} else {
writeJson.writeValueUsingObjectMapper(name, value);
}
}
}
public void jsonRead(ReadJson ctx, EntityBean bean) throws IOException {
if (!jsonDeserialize) {
return;
}
JsonToken event = ctx.nextToken();
if (JsonToken.VALUE_NULL == event) {
setValue(bean, null);
if (jsonDeserialize) {
setValue(bean, null);
}
} else {
// expect to read non-null json value
Object objValue = scalarType.jsonRead(ctx.getParser(), event);
setValue(bean, objValue);
Object objValue;
if (scalarType != null) {
objValue = scalarType.jsonRead(ctx.getParser(), event);
} else {
try {
objValue = ctx.readValueUsingObjectMapper(propertyType);
} catch (IOException e) {
// change in behavior for #318
objValue = null;
String msg = "Error trying to use Jackson ObjectMapper to read transient property "
+ getFullBeanName() +" - consider marking this property with @JsonIgnore";
logger.error(msg, e);
}
}
if (jsonDeserialize) {
setValue(bean, objValue);
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -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
}
@@ -872,7 +873,14 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
if (help != null) {
help.jsonWrite(ctx, name, value, include != null);
} else {
ctx.toJson(name, (Collection<?>) value);
if (isTransient) {
ctx.writeValueUsingObjectMapper(name, value);
} else {
Collection<?> collection = (Collection<?>)value;
if (!collection.isEmpty() || ctx.isIncludeEmpty()) {
ctx.toJson(name, collection);
}
}
}
ctx.popParentBeanMany();
}
@@ -1,23 +1,42 @@
package com.avaje.ebeaninternal.server.deploy;
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.api.ClassUtil;
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;
import java.io.IOException;
/**
* Help BeanPropertyAssocMany with JSON processing.
*/
public class BeanPropertyAssocManyJsonHelp {
/**
* The associated many property.
*/
private final BeanPropertyAssocMany<?> many;
/**
* Helper used to read json for transient 'many' properties.
*/
private final BeanPropertyAssocManyJsonTransient jsonTransient;
/**
* Construct for the owning many property.
*/
public BeanPropertyAssocManyJsonHelp(BeanPropertyAssocMany<?> many) {
this.many = many;
this.jsonTransient = !ClassUtil.isJacksonObjectMapperPresent() ? null : new BeanPropertyAssocManyJsonTransient();
}
/**
* Read the JSON for this property.
*/
public void jsonRead(ReadJson readJson, EntityBean parentBean) throws IOException {
if (!this.many.jsonDeserialize) {
@@ -33,6 +52,11 @@ public class BeanPropertyAssocManyJsonHelp {
throw new JsonParseException("Unexpected token " + event + " - expecting start_array ", parser.getCurrentLocation());
}
if (many.isTransient()) {
jsonReadTransientUsingObjectMapper(readJson, parentBean);
return;
}
BeanCollection<?> collection = many.createEmpty(parentBean);
BeanCollectionAdd add = many.getBeanCollectionAdd(collection, null);
do {
@@ -51,4 +75,15 @@ public class BeanPropertyAssocManyJsonHelp {
many.setValue(parentBean, collection);
}
/**
* Read a Transient property using Jackson ObjectMapper.
*/
private void jsonReadTransientUsingObjectMapper(ReadJson readJson, EntityBean parentBean) throws IOException {
if (jsonTransient == null) {
throw new IllegalStateException("Jackson ObjectMapper is required to read this Transient property "+many.getFullBeanName());
}
jsonTransient.jsonReadUsingObjectMapper(many, readJson, parentBean);
}
}
@@ -0,0 +1,44 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.text.json.ReadJson;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import java.io.IOException;
import java.util.LinkedHashMap;
/**
* Helper used to read transient many properties using Jackson ObjectMapper.
*/
public class BeanPropertyAssocManyJsonTransient {
/**
* Use Jackson ObjectMapper to read the transient 'many' property.
*/
public void jsonReadUsingObjectMapper(BeanPropertyAssocMany<?> many, ReadJson readJson, EntityBean parentBean) throws IOException {
ObjectMapper mapper = readJson.getObjectMapper();
ManyType manyType = many.getManyType();
Object value;
if (manyType.isMap()) {
// read map using Jackson object mapper with unknown key type
TypeFactory typeFactory = mapper.getTypeFactory();
JavaType target = typeFactory.constructType(many.getTargetType());
MapType jacksonType = typeFactory.constructMapType(LinkedHashMap.class, typeFactory.unknownType(), target);
value = mapper.readValue(readJson.getParser(), jacksonType);
} else {
// read list or set using Jackson object mapper
CollectionType jacksonType = mapper.getTypeFactory().constructCollectionType(manyType.getCollectionType(), many.getTargetType());
value = mapper.readValue(readJson.getParser(), jacksonType);
}
many.setValue(parentBean, value);
}
}
File diff suppressed because it is too large Load Diff
@@ -171,7 +171,7 @@ public class BeanPropertyCompound extends BeanProperty {
}
Object value = getValueIntercept(bean);
if (value == null) {
ctx.writeNull(name);
ctx.writeNullField(name);
} else {
compoundType.jsonWrite(ctx, value, name);
}
@@ -139,11 +139,13 @@ public final class BeanSetHelp<T> implements BeanCollectionHelp<T> {
set = (Set<?>) collection;
}
ctx.writeStartArray(name);
Iterator<?> it = set.iterator();
while (it.hasNext()) {
targetDescriptor.jsonWrite(ctx, (EntityBean) it.next());
if (!set.isEmpty() || ctx.isIncludeEmpty()) {
ctx.writeStartArray(name);
Iterator<?> it = set.iterator();
while (it.hasNext()) {
targetDescriptor.jsonWrite(ctx, (EntityBean) it.next());
}
ctx.writeEndArray();
}
ctx.writeEndArray();
}
}
@@ -10,108 +10,114 @@ public interface DbSqlContext {
/**
* Add a join to the sql query.
*/
public void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2, String inheritance);
void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2, String inheritance);
public void pushSecondaryTableAlias(String alias);
void pushSecondaryTableAlias(String alias);
/**
* Push the current table alias onto the stack.
*/
public void pushTableAlias(String tableAlias);
void pushTableAlias(String tableAlias);
/**
* Pop the current table alias from the stack.
*/
public void popTableAlias();
void popTableAlias();
/**
* Add an encrypted property which will require additional binding.
*/
public void addEncryptedProp(BeanProperty prop);
void addEncryptedProp(BeanProperty prop);
/**
* Return a list of encrypted properties which require additional binding.
*/
public BeanProperty[] getEncryptedProps();
BeanProperty[] getEncryptedProps();
/**
* Append a char directly to the SQL buffer.
*/
public DbSqlContext append(char s);
DbSqlContext append(char s);
/**
* Append a string directly to the SQL buffer.
*/
public DbSqlContext append(String s);
DbSqlContext append(String s);
/**
* Peek the current table alias.
*/
public String peekTableAlias();
String peekTableAlias();
/**
* Add a raw column to the sql.
*/
public void appendRawColumn(String rawcolumnWithTableAlias);
void appendRawColumn(String rawcolumnWithTableAlias);
/**
* Append a column with an explicit table alias.
*/
public void appendColumn(String tableAlias, String column);
void appendColumn(String tableAlias, String column);
/**
* Append a column with the current table alias.
*/
public void appendColumn(String column);
void appendColumn(String column);
/**
* Append a Sql Formula select. This converts the "${ta}" keyword to the
* current table alias.
*/
public void appendFormulaSelect(String sqlFormulaSelect);
void appendFormulaSelect(String sqlFormulaSelect);
/**
* Append a Sql Formula join. This converts the "${ta}" keyword to the current
* table alias.
*/
public void appendFormulaJoin(String sqlFormulaJoin, SqlJoinType joinType);
void appendFormulaJoin(String sqlFormulaJoin, SqlJoinType joinType);
/**
* Return the current content length.
*/
public int length();
int length();
/**
* Return the current context of the sql context.
*/
public String getContent();
String getContent();
/**
* Return the current join node.
*/
public String peekJoin();
String peekJoin();
/**
* Push a join node onto the stack.
*/
public void pushJoin(String prefix);
void pushJoin(String prefix);
/**
* Pop a join node off the stack.
*/
public void popJoin();
void popJoin();
/**
* Return a table alias without many where clause joins. Typically this is for
* the select clause (fetch joins).
*/
public String getTableAlias(String prefix);
String getTableAlias(String prefix);
/**
* Return a table alias that takes into account many where joins.
*/
public String getTableAliasManyWhere(String prefix);
String getTableAliasManyWhere(String prefix);
public String getRelativePrefix(String propName);
String getRelativePrefix(String propName);
/**
* Append the lower and upper bound columns into the select clause
* for findVersions() queries.
*/
void appendHistorySysPeriod();
}
@@ -2,58 +2,76 @@ package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebeaninternal.api.SpiQuery;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* Represents the type of a OneToMany or ManyToMany property.
*/
public class ManyType {
public static final ManyType JAVA_LIST = new ManyType(Underlying.LIST);
public static final ManyType JAVA_SET = new ManyType(Underlying.SET);
public static final ManyType JAVA_MAP = new ManyType(Underlying.MAP);
public enum Underlying {
LIST,
SET,
MAP
}
private final SpiQuery.Type queryType;
private final Underlying underlying;
public static final ManyType JAVA_LIST = new ManyType(Underlying.LIST);
public static final ManyType JAVA_SET = new ManyType(Underlying.SET);
public static final ManyType JAVA_MAP = new ManyType(Underlying.MAP);
public ManyType(Underlying underlying) {
this.underlying = underlying;
switch (underlying) {
case LIST:
queryType = SpiQuery.Type.LIST;
break;
case SET:
queryType = SpiQuery.Type.SET;
break;
public enum Underlying {
default:
queryType = SpiQuery.Type.MAP;
break;
}
}
LIST(List.class),
SET(Set.class),
MAP(null);
public boolean isMap() {
return Underlying.MAP.equals(underlying);
Class<? extends Collection> type;
Underlying(Class<? extends Collection> type) {
this.type = type;
}
/**
* Return the matching Query type.
*/
public SpiQuery.Type getQueryType() {
return queryType;
}
private final SpiQuery.Type queryType;
private final Underlying underlying;
public ManyType(Underlying underlying) {
this.underlying = underlying;
switch (underlying) {
case LIST:
queryType = SpiQuery.Type.LIST;
break;
case SET:
queryType = SpiQuery.Type.SET;
break;
default:
queryType = SpiQuery.Type.MAP;
break;
}
/**
* Return the underlying type.
*/
public Underlying getUnderlying() {
return underlying;
}
}
public boolean isMap() {
return Underlying.MAP.equals(underlying);
}
/**
* Return the matching Query type.
*/
public SpiQuery.Type getQueryType() {
return queryType;
}
/**
* Return the underlying type.
*/
public Underlying getUnderlying() {
return underlying;
}
/**
* Returns List.class or Set.class and null for Map.
* Not intended to be called for maps.
*/
public Class<? extends Collection> getCollectionType() {
return underlying.type;
}
}
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.deploy.generatedproperty;
import java.math.BigDecimal;
import java.util.HashSet;
import com.avaje.ebean.config.CurrentUserProvider;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
/**
@@ -10,19 +11,27 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
*/
public class GeneratedPropertyFactory {
CounterFactory counterFactory;
private final CounterFactory counterFactory = new CounterFactory();;
InsertTimestampFactory insertFactory;
private final InsertTimestampFactory insertFactory = new InsertTimestampFactory();
UpdateTimestampFactory updateFactory;
private final UpdateTimestampFactory updateFactory = new UpdateTimestampFactory();
HashSet<String> numberTypes = new HashSet<String>();
private final HashSet<String> numberTypes = new HashSet<String>();
public GeneratedPropertyFactory() {
counterFactory = new CounterFactory();
insertFactory = new InsertTimestampFactory();
updateFactory = new UpdateTimestampFactory();
private final GeneratedWhoModified generatedWhoModified;
private final GeneratedWhoCreated generatedWhoCreated;
public GeneratedPropertyFactory(CurrentUserProvider currentUserProvider) {
if (currentUserProvider != null) {
generatedWhoCreated = new GeneratedWhoCreated(currentUserProvider);
generatedWhoModified = new GeneratedWhoModified(currentUserProvider);
} else {
generatedWhoCreated = null;
generatedWhoModified = null;
}
numberTypes.add(Integer.class.getName());
numberTypes.add(int.class.getName());
@@ -62,4 +71,18 @@ public class GeneratedPropertyFactory {
updateFactory.setUpdateTimestamp(property);
}
public void setWhoCreated(DeployBeanProperty property) {
if (generatedWhoCreated == null) {
throw new IllegalStateException("No CurrentUserProvider has been set so @WhoCreated is not supported");
}
property.setGeneratedProperty(generatedWhoCreated);
}
public void setWhoModified(DeployBeanProperty property) {
if (generatedWhoModified == null) {
throw new IllegalStateException("No CurrentUserProvider has been set so @WhoModified is not supported");
}
property.setGeneratedProperty(generatedWhoModified);
}
}
@@ -0,0 +1,47 @@
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.config.CurrentUserProvider;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
/**
* Used to populate @WhoCreated bean properties.
*/
public class GeneratedWhoCreated implements GeneratedProperty {
final CurrentUserProvider currentUserProvider;
public GeneratedWhoCreated(CurrentUserProvider currentUserProvider) {
this.currentUserProvider = currentUserProvider;
}
@Override
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
return currentUserProvider.currentUser();
}
@Override
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
return null;
}
@Override
public boolean includeInUpdate() {
return false;
}
@Override
public boolean includeInAllUpdates() {
return false;
}
@Override
public boolean includeInInsert() {
return true;
}
@Override
public boolean isDDLNotNullable() {
return true;
}
}
@@ -0,0 +1,47 @@
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.config.CurrentUserProvider;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
/**
* Used to populate @WhoModified bean properties.
*/
public class GeneratedWhoModified implements GeneratedProperty {
final CurrentUserProvider currentUserProvider;
public GeneratedWhoModified(CurrentUserProvider currentUserProvider) {
this.currentUserProvider = currentUserProvider;
}
@Override
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
return currentUserProvider.currentUser();
}
@Override
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
return currentUserProvider.currentUser();
}
@Override
public boolean includeInUpdate() {
return true;
}
@Override
public boolean includeInAllUpdates() {
return true;
}
@Override
public boolean includeInInsert() {
return true;
}
@Override
public boolean isDDLNotNullable() {
return true;
}
}
@@ -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.
*/
@@ -23,22 +22,22 @@ public interface IdBinder {
/**
* Initialise the binder.
*/
public void initialise();
void initialise();
/**
* Return true if this is a compound key and must use expanded and or form.
*/
public boolean isIdInExpandedForm();
boolean isIdInExpandedForm();
/**
* Write the Id value to binary DataOuput.
*/
public void writeData(DataOutput dataOutput, Object idValue) throws IOException;
void writeData(DataOutput dataOutput, Object idValue) throws IOException;
/**
* Read the Id value from the binary DataInput.
*/
public Object readData(DataInput dataInput) throws IOException;
Object readData(DataInput dataInput) throws IOException;
/**
* Return the name(s) of the Id property(s). Comma delimited if there is more
@@ -47,36 +46,36 @@ public interface IdBinder {
* This can be used to include in a query.
* </p>
*/
public String getIdProperty();
String getIdProperty();
/**
* Return the Id BeanProperty.
*/
public BeanProperty getBeanProperty();
BeanProperty getBeanProperty();
/**
* Find a BeanProperty that is mapped to the database column.
*/
public BeanProperty findBeanProperty(String dbColumnName);
BeanProperty findBeanProperty(String dbColumnName);
/**
* Return the number of scalar properties for this id.
*/
public int getPropertyCount();
int getPropertyCount();
/**
* Return false if the id is a simple scalar and false if it is embedded or
* concatenated.
*/
public boolean isComplexId();
boolean isComplexId();
/**
* Return the default order by that may need to be used if the query includes
* a many property.
*/
public String getDefaultOrderBy();
String getDefaultOrderBy();
public String getOrderBy(String pathPrefix, boolean ascending);
String getOrderBy(String pathPrefix, boolean ascending);
/**
* Return the values as an array of scalar bindable values.
@@ -88,12 +87,12 @@ public interface IdBinder {
* Added primarily for Query.addWhere().add(Expr.idEq()) support.
* </p>
*/
public Object[] getBindValues(Object idValue);
Object[] getBindValues(Object idValue);
/**
* Return the id values for a given bean.
*/
public Object[] getIdValues(EntityBean bean);
Object[] getIdValues(EntityBean bean);
/**
* Build a string of the logical expressions.
@@ -101,68 +100,68 @@ public interface IdBinder {
* Typically used to build a id = ? string.
* </p>
*/
public String getAssocOneIdExpr(String prefix, String operator);
String getAssocOneIdExpr(String prefix, String operator);
/**
* Return the logical id in expression taking into account embedded id's.
*/
public String getAssocIdInExpr(String prefix);
String getAssocIdInExpr(String prefix);
/**
* Binds an id value to a prepared statement.
*/
public void bindId(DataBind dataBind, Object value) throws SQLException;
void bindId(DataBind dataBind, Object value) throws SQLException;
/**
* Bind the id value to a SqlUpdate statement.
*/
public void bindId(DefaultSqlUpdate sqlUpdate, Object value);
void bindId(DefaultSqlUpdate sqlUpdate, Object value);
public void addIdInBindValue(SpiExpressionRequest request, Object value);
void addIdInBindValue(SpiExpressionRequest request, Object value);
/**
* Return the sql for binding the id using an IN clause.
*/
public String getBindIdInSql(String baseTableAlias);
String getBindIdInSql(String baseTableAlias);
/**
* Return the binding expression (like "?" or "(?,?)")for the Id.
*/
public String getIdInValueExpr(int size);
String getIdInValueExpr(int size);
/**
* Same as getIdInValueExpr but for delete by id.
*/
public String getIdInValueExprDelete(int size);
String getIdInValueExprDelete(int size);
public void buildSelectExpressionChain(String prefix, List<String> selectChain);
void buildRawSqlSelectChain(String prefix, List<String> selectChain);
/**
* Read the id value from the result set and set it to the bean also returning
* it.
*/
public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException;
Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException;
/**
* Ignore the appropriate number of scalar properties for this id.
*/
public void loadIgnore(DbReadContext ctx);
void loadIgnore(DbReadContext ctx);
/**
* Read the id value from the result set and return it.
*/
public Object read(DbReadContext ctx) throws SQLException;
Object read(DbReadContext ctx) throws SQLException;
/**
* Append to the select clause.
*/
public void appendSelect(DbSqlContext ctx, boolean subQuery);
void appendSelect(DbSqlContext ctx, boolean subQuery);
/**
* Return the sql for binding the id to. This includes table alias and columns
* that make up the id.
*/
public String getBindIdSql(String baseTableAlias);
String getBindIdSql(String baseTableAlias);
/**
* Cast or convert the Id value if necessary and optionally set it.
@@ -175,6 +174,6 @@ public interface IdBinder {
* If the bean is not null, then the value is set to the bean.
* </p>
*/
public Object convertSetId(Object idValue, EntityBean bean);
Object convertSetId(Object idValue, EntityBean bean);
}
@@ -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);
}
/**
@@ -87,6 +87,11 @@ public class DeployBeanDescriptor<T> {
* The base database table.
*/
private String baseTable;
private String baseTableAsOf;
private boolean historySupport;
private TableName baseTableFull;
private String[] properties;
@@ -135,6 +140,20 @@ public class DeployBeanDescriptor<T> {
return Modifier.isAbstract(beanType.getModifiers());
}
/**
* Set to true for @History entity beans that have history.
*/
public void setHistorySupport(boolean historySupport) {
this.historySupport = historySupport;
}
/**
* Return true if this is an @History entity bean.
*/
public boolean isHistorySupport() {
return historySupport;
}
public boolean isScalaObject() {
Class<?>[] interfaces = beanType.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
@@ -405,6 +424,10 @@ public class DeployBeanDescriptor<T> {
return baseTable;
}
public String getBaseTableAsOf() {
return baseTableAsOf;
}
/**
* Return the base table with full structure.
*/
@@ -413,12 +436,12 @@ public class DeployBeanDescriptor<T> {
}
/**
* Set the base table. Only properties mapped to the base table are by default
* persisted.
* Set the base table. Only properties mapped to the base table are by default persisted.
*/
public void setBaseTable(TableName baseTableFull) {
public void setBaseTable(TableName baseTableFull, String asOfSuffix) {
this.baseTableFull = baseTableFull;
this.baseTable = baseTableFull == null ? null : baseTableFull.getQualifiedName();
this.baseTableAsOf = baseTable + asOfSuffix;
}
public void sortProperties() {
File diff suppressed because it is too large Load Diff
@@ -8,6 +8,7 @@ import javax.persistence.MapKey;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import com.avaje.ebean.annotation.HistoryExclude;
import com.avaje.ebean.annotation.PrivateOwned;
import com.avaje.ebean.annotation.Where;
import com.avaje.ebean.bean.BeanCollection.ModifyListenMode;
@@ -33,8 +34,8 @@ public class AnnotationAssocManys extends AnnotationParser {
/**
* Create with the DeployInfo.
*/
public AnnotationAssocManys(DeployBeanInfo<?> info, BeanDescriptorManager factory) {
super(info);
public AnnotationAssocManys(DeployBeanInfo<?> info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) {
super(info, javaxValidationAnnotations);
this.factory = factory;
}
@@ -65,7 +66,11 @@ public class AnnotationAssocManys extends AnnotationParser {
readToMany(manyToMany, prop);
}
OrderBy orderBy = get(prop, OrderBy.class);
if (get(prop, HistoryExclude.class) != null) {
prop.setExcludedFromHistory(true);
}
OrderBy orderBy = get(prop, OrderBy.class);
if (orderBy != null) {
prop.setFetchOrderBy(orderBy.value());
}
@@ -28,186 +28,186 @@ import com.avaje.ebeaninternal.server.query.SqlJoinType;
*/
public class AnnotationAssocOnes extends AnnotationParser {
private final BeanDescriptorManager factory;
private final BeanDescriptorManager factory;
/**
* Create with the deploy Info.
*/
public AnnotationAssocOnes(DeployBeanInfo<?> info, BeanDescriptorManager factory) {
super(info);
this.factory = factory;
/**
* Create with the deploy Info.
*/
public AnnotationAssocOnes(DeployBeanInfo<?> info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) {
super(info, javaxValidationAnnotations);
this.factory = factory;
}
/**
* Parse the annotation.
*/
public void parse() {
for (DeployBeanProperty prop : descriptor.propertiesAll()) {
if (prop instanceof DeployBeanPropertyAssocOne<?>) {
readAssocOne((DeployBeanPropertyAssocOne<?>) prop);
}
}
}
private void readAssocOne(DeployBeanPropertyAssocOne<?> prop) {
ManyToOne manyToOne = get(prop, ManyToOne.class);
if (manyToOne != null) {
readManyToOne(manyToOne, prop);
}
OneToOne oneToOne = get(prop, OneToOne.class);
if (oneToOne != null) {
readOneToOne(oneToOne, prop);
}
Embedded embedded = get(prop, Embedded.class);
if (embedded != null) {
readEmbedded(embedded, prop);
}
EmbeddedId emId = get(prop, EmbeddedId.class);
if (emId != null) {
prop.setEmbedded(true);
prop.setId(true);
prop.setNullable(false);
}
Column column = get(prop, Column.class);
if (column != null && !isEmpty(column.name())) {
// have this in for AssocOnes used on
// Sql based beans...
prop.setDbColumn(column.name());
}
/**
* Parse the annotation.
*/
public void parse() {
for (DeployBeanProperty prop : descriptor.propertiesAll()) {
if (prop instanceof DeployBeanPropertyAssocOne<?>) {
readAssocOne((DeployBeanPropertyAssocOne<?>) prop);
}
}
// May as well check for Id. Makes sense to me.
Id id = get(prop, Id.class);
if (id != null) {
prop.setEmbedded(true);
prop.setId(true);
prop.setNullable(false);
}
private void readAssocOne(DeployBeanPropertyAssocOne<?> prop) {
ManyToOne manyToOne = get(prop, ManyToOne.class);
if (manyToOne != null) {
readManyToOne(manyToOne, prop);
}
OneToOne oneToOne = get(prop, OneToOne.class);
if (oneToOne != null) {
readOneToOne(oneToOne, prop);
}
Embedded embedded = get(prop, Embedded.class);
if (embedded != null) {
readEmbedded(embedded, prop);
}
EmbeddedId emId = get(prop, EmbeddedId.class);
if (emId != null) {
prop.setEmbedded(true);
prop.setId(true);
prop.setNullable(false);
}
Column column = get(prop, Column.class);
if (column != null && !isEmpty(column.name())) {
// have this in for AssocOnes used on
// Sql based beans...
prop.setDbColumn(column.name());
}
// May as well check for Id. Makes sense to me.
Id id = get(prop, Id.class);
if (id != null) {
prop.setEmbedded(true);
prop.setId(true);
prop.setNullable(false);
}
Where where = get(prop, Where.class);
if (where != null) {
// not expecting this to be used on assoc one properties
prop.setExtraWhere(where.clause());
}
if (validationAnnotations) {
NotNull notNull = get(prop, NotNull.class);
if (notNull != null) {
prop.setNullable(false);
// overrides optional attribute of ManyToOne etc
prop.getTableJoin().setType(SqlJoinType.INNER);
}
}
// check for manually defined joins
BeanTable beanTable = prop.getBeanTable();
JoinColumn joinColumn = get(prop, JoinColumn.class);
if (joinColumn != null) {
prop.getTableJoin().addJoinColumn(false, joinColumn, beanTable);
if (!joinColumn.updatable()) {
prop.setDbUpdateable(false);
}
if (!joinColumn.nullable()) {
prop.setNullable(false);
}
}
JoinColumns joinColumns = get(prop, JoinColumns.class);
if (joinColumns != null) {
prop.getTableJoin().addJoinColumn(false, joinColumns.value(), beanTable);
}
JoinTable joinTable = get(prop, JoinTable.class);
if (joinTable != null) {
prop.getTableJoin().addJoinColumn(false, joinTable.joinColumns(), beanTable);
}
info.setBeanJoinType(prop, prop.isNullable());
if (!prop.getTableJoin().hasJoinColumns() && beanTable != null) {
if (prop.getMappedBy() != null) {
// the join is derived by reversing the join information
// from the mapped by property.
// Refer BeanDescriptorManager.readEntityRelationships()
} else {
// use naming convention to define join.
NamingConvention nc = factory.getNamingConvention();
String fkeyPrefix = null;
if (nc.isUseForeignKeyPrefix()){
fkeyPrefix = nc.getColumnFromProperty(beanType, prop.getName());
}
beanTable.createJoinColumn(fkeyPrefix, prop.getTableJoin(), true);
}
}
Where where = get(prop, Where.class);
if (where != null) {
// not expecting this to be used on assoc one properties
prop.setExtraWhere(where.clause());
}
private String errorMsgMissingBeanTable(Class<?> type, String from) {
return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered?";
if (validationAnnotations) {
NotNull notNull = get(prop, NotNull.class);
if (notNull != null) {
prop.setNullable(false);
// overrides optional attribute of ManyToOne etc
prop.getTableJoin().setType(SqlJoinType.INNER);
}
}
private void readManyToOne(ManyToOne propAnn, DeployBeanProperty prop) {
DeployBeanPropertyAssocOne<?> beanProp = (DeployBeanPropertyAssocOne<?>) prop;
setCascadeTypes(propAnn.cascade(), beanProp.getCascadeInfo());
BeanTable assoc = factory.getBeanTable(beanProp.getPropertyType());
if (assoc == null) {
String msg = errorMsgMissingBeanTable(beanProp.getPropertyType(), prop.getFullBeanName());
throw new RuntimeException(msg);
}
beanProp.setBeanTable(assoc);
beanProp.setDbInsertable(true);
beanProp.setDbUpdateable(true);
beanProp.setNullable(propAnn.optional());
beanProp.setFetchType(propAnn.fetch());
// check for manually defined joins
BeanTable beanTable = prop.getBeanTable();
JoinColumn joinColumn = get(prop, JoinColumn.class);
if (joinColumn != null) {
prop.getTableJoin().addJoinColumn(false, joinColumn, beanTable);
if (!joinColumn.updatable()) {
prop.setDbUpdateable(false);
}
if (!joinColumn.nullable()) {
prop.setNullable(false);
}
}
private void readOneToOne(OneToOne propAnn, DeployBeanPropertyAssocOne<?> prop) {
prop.setOneToOne(true);
prop.setDbInsertable(true);
prop.setDbUpdateable(true);
prop.setNullable(propAnn.optional());
prop.setFetchType(propAnn.fetch());
prop.setMappedBy(propAnn.mappedBy());
if (!"".equals(propAnn.mappedBy())) {
prop.setOneToOneExported(true);
}
setCascadeTypes(propAnn.cascade(), prop.getCascadeInfo());
BeanTable assoc = factory.getBeanTable(prop.getPropertyType());
if (assoc == null) {
String msg = errorMsgMissingBeanTable(prop.getPropertyType(), prop.getFullBeanName());
throw new RuntimeException(msg);
}
prop.setBeanTable(assoc);
JoinColumns joinColumns = get(prop, JoinColumns.class);
if (joinColumns != null) {
prop.getTableJoin().addJoinColumn(false, joinColumns.value(), beanTable);
}
private void readEmbedded(Embedded propAnn, DeployBeanPropertyAssocOne<?> prop) {
JoinTable joinTable = get(prop, JoinTable.class);
if (joinTable != null) {
prop.getTableJoin().addJoinColumn(false, joinTable.joinColumns(), beanTable);
}
prop.setEmbedded(true);
prop.setDbInsertable(true);
prop.setDbUpdateable(true);
info.setBeanJoinType(prop, prop.isNullable());
EmbeddedColumns columns = get(prop, EmbeddedColumns.class);
if (columns != null) {
if (!prop.getTableJoin().hasJoinColumns() && beanTable != null) {
// convert into a Map
String propColumns = columns.columns();
Map<String, String> propMap = StringHelper.delimitedToMap(propColumns, ",", "=");
if (prop.getMappedBy() != null) {
// the join is derived by reversing the join information
// from the mapped by property.
// Refer BeanDescriptorManager.readEntityRelationships()
prop.getDeployEmbedded().putAll(propMap);
} else {
// use naming convention to define join.
NamingConvention nc = factory.getNamingConvention();
String fkeyPrefix = null;
if (nc.isUseForeignKeyPrefix()) {
fkeyPrefix = nc.getColumnFromProperty(beanType, prop.getName());
}
readEmbeddedAttributeOverrides(prop);
beanTable.createJoinColumn(fkeyPrefix, prop.getTableJoin(), true);
}
}
}
private String errorMsgMissingBeanTable(Class<?> type, String from) {
return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered?";
}
private void readManyToOne(ManyToOne propAnn, DeployBeanProperty prop) {
DeployBeanPropertyAssocOne<?> beanProp = (DeployBeanPropertyAssocOne<?>) prop;
setCascadeTypes(propAnn.cascade(), beanProp.getCascadeInfo());
BeanTable assoc = factory.getBeanTable(beanProp.getPropertyType());
if (assoc == null) {
String msg = errorMsgMissingBeanTable(beanProp.getPropertyType(), prop.getFullBeanName());
throw new RuntimeException(msg);
}
beanProp.setBeanTable(assoc);
beanProp.setDbInsertable(true);
beanProp.setDbUpdateable(true);
beanProp.setNullable(propAnn.optional());
beanProp.setFetchType(propAnn.fetch());
}
private void readOneToOne(OneToOne propAnn, DeployBeanPropertyAssocOne<?> prop) {
prop.setOneToOne(true);
prop.setDbInsertable(true);
prop.setDbUpdateable(true);
prop.setNullable(propAnn.optional());
prop.setFetchType(propAnn.fetch());
prop.setMappedBy(propAnn.mappedBy());
if (!"".equals(propAnn.mappedBy())) {
prop.setOneToOneExported(true);
}
setCascadeTypes(propAnn.cascade(), prop.getCascadeInfo());
BeanTable assoc = factory.getBeanTable(prop.getPropertyType());
if (assoc == null) {
String msg = errorMsgMissingBeanTable(prop.getPropertyType(), prop.getFullBeanName());
throw new RuntimeException(msg);
}
prop.setBeanTable(assoc);
}
private void readEmbedded(Embedded propAnn, DeployBeanPropertyAssocOne<?> prop) {
prop.setEmbedded(true);
prop.setDbInsertable(true);
prop.setDbUpdateable(true);
EmbeddedColumns columns = get(prop, EmbeddedColumns.class);
if (columns != null) {
// convert into a Map
String propColumns = columns.columns();
Map<String, String> propMap = StringHelper.delimitedToMap(propColumns, ",", "=");
prop.getDeployEmbedded().putAll(propMap);
}
readEmbeddedAttributeOverrides(prop);
}
}
@@ -13,66 +13,66 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
*/
public abstract class AnnotationBase {
protected final DatabasePlatform databasePlatform;
protected final NamingConvention namingConvention;
protected final DeployUtil util;
protected AnnotationBase(DeployUtil util) {
this.util = util;
this.databasePlatform = util.getDbPlatform();
this.namingConvention = util.getNamingConvention();
}
/**
* read the deployment annotations.
*/
public abstract void parse();
/**
* Checks string is null or empty .
*/
protected boolean isEmpty(String s) {
if (s == null || s.trim().length() == 0) {
return true;
}
return false;
}
/**
* Return the annotation for the property.
* <p>
* Looks first at the field and then at the getter method.
* </p>
*/
protected <T extends Annotation> T get(DeployBeanProperty prop, Class<T> annClass) {
T a = null;
Field field = prop.getField();
if (field != null){
a = field.getAnnotation(annClass);
}
if (a == null) {
Method m = prop.getReadMethod();
if (m != null) {
a = m.getAnnotation(annClass);
}
}
return a;
protected final DatabasePlatform databasePlatform;
protected final NamingConvention namingConvention;
protected final DeployUtil util;
protected AnnotationBase(DeployUtil util) {
this.util = util;
this.databasePlatform = util.getDbPlatform();
this.namingConvention = util.getNamingConvention();
}
/**
* read the deployment annotations.
*/
public abstract void parse();
/**
* Checks string is null or empty .
*/
protected boolean isEmpty(String s) {
if (s == null || s.trim().length() == 0) {
return true;
}
/**
* Return the annotation for the property.
* <p>
* Looks first at the field and then at the getter method. then at class level.
* </p>
*/
protected <T extends Annotation> T find(DeployBeanProperty prop, Class<T> annClass) {
T a = get(prop, annClass);
if (a == null) {
a = prop.getOwningType().getAnnotation(annClass);
}
return a;
}
return false;
}
/**
* Return the annotation for the property.
* <p>
* Looks first at the field and then at the getter method.
* </p>
*/
protected <T extends Annotation> T get(DeployBeanProperty prop, Class<T> annClass) {
T a = null;
Field field = prop.getField();
if (field != null) {
a = field.getAnnotation(annClass);
}
if (a == null) {
Method m = prop.getReadMethod();
if (m != null) {
a = m.getAnnotation(annClass);
}
}
return a;
}
/**
* Return the annotation for the property.
* <p>
* Looks first at the field and then at the getter method. then at class level.
* </p>
*/
protected <T extends Annotation> T find(DeployBeanProperty prop, Class<T> annClass) {
T a = get(prop, annClass);
if (a == null) {
a = prop.getOwningType().getAnnotation(annClass);
}
return a;
}
}
@@ -10,6 +10,7 @@ import javax.persistence.UniqueConstraint;
import com.avaje.ebean.annotation.CacheStrategy;
import com.avaje.ebean.annotation.CacheTuning;
import com.avaje.ebean.annotation.EntityConcurrencyMode;
import com.avaje.ebean.annotation.History;
import com.avaje.ebean.annotation.NamedUpdate;
import com.avaje.ebean.annotation.NamedUpdates;
import com.avaje.ebean.annotation.UpdateMode;
@@ -26,8 +27,11 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
*/
public class AnnotationClass extends AnnotationParser {
public AnnotationClass(DeployBeanInfo<?> info) {
super(info);
private final String asOfViewSuffix;
public AnnotationClass(DeployBeanInfo<?> info, boolean validationAnnotations, String asOfViewSuffix) {
super(info, validationAnnotations);
this.asOfViewSuffix = asOfViewSuffix;
}
/**
@@ -48,7 +52,7 @@ public class AnnotationClass extends AnnotationParser {
// default the TableName using NamingConvention.
TableName tableName = namingConvention.getTableName(descriptor.getBeanType());
descriptor.setBaseTable(tableName);
descriptor.setBaseTable(tableName, asOfViewSuffix);
}
}
@@ -84,6 +88,11 @@ public class AnnotationClass extends AnnotationParser {
}
}
History history = cls.getAnnotation(History.class);
if (history != null) {
descriptor.setHistorySupport(true);
}
UpdateMode updateMode = cls.getAnnotation(UpdateMode.class);
if (updateMode != null) {
descriptor.setUpdateChangesOnly(updateMode.updateChangesOnly());
@@ -13,7 +13,12 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound;
import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.type.*;
import com.avaje.ebeaninternal.server.type.CtCompoundType;
import com.avaje.ebeaninternal.server.type.DataEncryptSupport;
import com.avaje.ebeaninternal.server.type.ScalarType;
import com.avaje.ebeaninternal.server.type.ScalarTypeBytesBase;
import com.avaje.ebeaninternal.server.type.ScalarTypeBytesEncrypted;
import com.avaje.ebeaninternal.server.type.ScalarTypeEncryptedWrapper;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
@@ -27,15 +32,25 @@ import java.util.UUID;
*/
public class AnnotationFields extends AnnotationParser {
/**
* If present read Jackson JsonIgnore.
*/
private final boolean jacksonAnnotationsPresent;
private final GeneratedPropertyFactory generatedPropFactory;
/**
* By default we lazy load Lob properties.
*/
private FetchType defaultLobFetchType = FetchType.LAZY;
private GeneratedPropertyFactory generatedPropFactory = new GeneratedPropertyFactory();
public AnnotationFields(GeneratedPropertyFactory generatedPropFactory, DeployBeanInfo<?> info,
boolean javaxValidationAnnotations, boolean jacksonAnnotationsPresent, boolean eagerFetchLobs) {
public AnnotationFields(DeployBeanInfo<?> info, boolean eagerFetchLobs) {
super(info);
super(info, javaxValidationAnnotations);
this.jacksonAnnotationsPresent = jacksonAnnotationsPresent;
this.generatedPropFactory = generatedPropFactory;
if (eagerFetchLobs) {
defaultLobFetchType = FetchType.EAGER;
@@ -101,10 +116,25 @@ public class AnnotationFields extends AnnotationParser {
if (column != null) {
readColumn(column, prop);
}
if (jacksonAnnotationsPresent) {
com.fasterxml.jackson.annotation.JsonIgnore jsonIgnore = get(prop, com.fasterxml.jackson.annotation.JsonIgnore.class);
if (jsonIgnore != null) {
prop.setJsonSerialize(!jsonIgnore.value());
prop.setJsonDeserialize(!jsonIgnore.value());
}
}
Expose expose = get(prop, Expose.class);
if (expose != null) {
prop.setExposeSerialize(expose.serialize());
prop.setExposeDeserialize(expose.deserialize());
prop.setJsonSerialize(expose.serialize());
prop.setJsonDeserialize(expose.deserialize());
}
JsonIgnore jsonIgnore = get(prop, JsonIgnore.class);
if (jsonIgnore != null) {
prop.setJsonSerialize(jsonIgnore.serialize());
prop.setJsonDeserialize(jsonIgnore.deserialize());
}
if (prop.getDbColumn() == null) {
@@ -143,9 +173,7 @@ public class AnnotationFields extends AnnotationParser {
util.setDbJsonBType(prop);
}
}
if (get(prop, ColumnHstore.class) != null) {
util.setDbHstore(prop);
}
if (get(prop, DbHstore.class) != null) {
util.setDbHstore(prop);
}
@@ -173,16 +201,25 @@ public class AnnotationFields extends AnnotationParser {
prop.setFetchType(defaultLobFetchType);
}
CreatedTimestamp ct = get(prop, CreatedTimestamp.class);
if (ct != null) {
if (get(prop, WhenCreated.class) != null || get(prop, CreatedTimestamp.class) != null) {
generatedPropFactory.setInsertTimestamp(prop);
}
UpdatedTimestamp ut = get(prop, UpdatedTimestamp.class);
if (ut != null) {
if (get(prop, WhenModified.class) != null || get(prop, UpdatedTimestamp.class) != null) {
generatedPropFactory.setUpdateTimestamp(prop);
}
if (get(prop, WhoCreated.class) != null) {
generatedPropFactory.setWhoCreated(prop);
}
if (get(prop, WhoModified.class) != null) {
generatedPropFactory.setWhoModified(prop);
}
if (get(prop, HistoryExclude.class) != null) {
prop.setExcludedFromHistory(true);
}
if (validationAnnotations) {
NotNull notNull = get(prop, NotNull.class);
if (notNull != null && isNotNullOnAllValidationGroups(notNull.groups())) {
@@ -21,22 +21,14 @@ public abstract class AnnotationParser extends AnnotationBase {
protected final Class<?> beanType;
protected boolean validationAnnotations;
protected final boolean validationAnnotations;
public AnnotationParser(DeployBeanInfo<?> info) {
public AnnotationParser(DeployBeanInfo<?> info, boolean validationAnnotations) {
super(info.getUtil());
this.validationAnnotations = validationAnnotations;
this.info = info;
this.beanType = info.getDescriptor().getBeanType();
this.descriptor = info.getDescriptor();
try {
Class.forName("javax.validation.constraints.NotNull");
validationAnnotations = true;
} catch (ClassNotFoundException e) {
// javax.validation not in the classpath so don't
// check for NotNull and Size
validationAnnotations = false;
}
}
/**
@@ -9,8 +9,8 @@ import com.avaje.ebeaninternal.server.deploy.DRawSqlMeta;
*/
public class AnnotationSql extends AnnotationParser {
public AnnotationSql(DeployBeanInfo<?> info) {
super(info);
public AnnotationSql(DeployBeanInfo<?> info, boolean javaxValidationAnnotations) {
super(info, javaxValidationAnnotations);
}
public void parse() {
@@ -1,12 +1,16 @@
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;
import com.avaje.ebeaninternal.server.deploy.ManyType;
import com.avaje.ebeaninternal.server.deploy.meta.*;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertySimpleCollection;
import com.avaje.ebeaninternal.server.type.CtCompoundType;
import com.avaje.ebeaninternal.server.type.ScalarType;
import com.avaje.ebeaninternal.server.type.TypeManager;
@@ -17,7 +21,11 @@ import org.slf4j.LoggerFactory;
import javax.persistence.ManyToOne;
import javax.persistence.PersistenceException;
import javax.persistence.Transient;
import java.lang.reflect.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* Create the properties for a bean.
@@ -259,7 +267,8 @@ public class DeployCreateProperties {
}
if (isTransientField(field)) {
return null;
// return with no ScalarType (still support JSON features)
return new DeployBeanProperty(desc, propertyType, null, null);
}
try {
CheckImmutableResponse checkImmutable = typeManager.checkImmutable(propertyType);
@@ -293,8 +302,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);
|| (field.getAnnotation(DbHstore.class) != null);
}
private boolean isTransientField(Field field) {
@@ -173,19 +173,27 @@ public class DeployUtil {
// Note that Temporal types already have dbType
// set via annotations
Class<?> propType = property.getPropertyType();
ScalarType<?> scalarType = typeManager.getScalarType(propType, property.getDbType());
if (scalarType != null) {
return scalarType;
}
try {
ScalarType<?> scalarType = typeManager.getScalarType(propType, property.getDbType());
if (scalarType != null) {
return scalarType;
}
String msg = property.getFullBeanName() + " has no ScalarType - type[" + propType.getName() + "]";
if (!property.isTransient()) {
throw new PersistenceException(msg);
String msg = property.getFullBeanName() + " has no ScalarType - type[" + propType.getName() + "]";
if (!property.isTransient()) {
throw new PersistenceException(msg);
} else {
// this is ok...
logger.trace("... transient property " + msg);
return null;
} else {
// this is ok...
logger.trace("... transient property " + msg);
return null;
}
} catch (IllegalArgumentException e) {
if (property.isTransient()) {
// expected for transient properties with unknown/non-mapped types
return null;
}
throw e;
}
}
@@ -1,6 +1,8 @@
package com.avaje.ebeaninternal.server.deploy.parse;
import com.avaje.ebeaninternal.api.ClassUtil;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
/**
@@ -8,51 +10,76 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
*/
public class ReadAnnotations {
/**
* Read the initial non-relationship annotations included Id and EmbeddedId.
* <p>
* We then have enough to create BeanTables which are used in readAssociations
* to resolve the relationships etc.
* </p>
*/
public void readInitial(DeployBeanInfo<?> info, boolean eagerFetchLobs){
/**
* Creates appropriate generated properties - WhenXXX, WhoXXX, Version etc.
*/
private final GeneratedPropertyFactory generatedPropFactory;
try {
new AnnotationClass(info).parse();
new AnnotationFields(info, eagerFetchLobs).parse();
} catch (RuntimeException e){
String msg = "Error reading annotations for "+info;
throw new RuntimeException(msg, e);
}
}
/**
* Typically _with_history and when appended to the base table derives the name of
* the view that unions the base table with the history table to support asOf queries.
*/
private final String asOfViewSuffix;
/**
* Read and process the associated relationship annotations.
* <p>
* These can only be processed after the BeanTables have been created
* </p>
* <p>
* This uses the factory as a call back to get the BeanTable for a given
* associated bean.
* </p>
*/
public void readAssociations(DeployBeanInfo<?> info, BeanDescriptorManager factory){
try {
new AnnotationAssocOnes(info, factory).parse();
new AnnotationAssocManys(info, factory).parse();
// read the Sql annotations last because they may be
// dependent on field level annotations
new AnnotationSql(info).parse();
} catch (RuntimeException e){
String msg = "Error reading annotations for "+info;
throw new RuntimeException(msg, e);
}
/**
* True if the javax validation annotations are present in the classpath.
*/
private final boolean javaxValidationAnnotations;
/**
* True if the jackson annotations are present in the classpath.
*/
private final boolean jacksonAnnotations;
public ReadAnnotations(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix) {
this.generatedPropFactory = generatedPropFactory;
this.asOfViewSuffix = asOfViewSuffix;
this.javaxValidationAnnotations = ClassUtil.isJavaxValidationAnnotationsPresent();
this.jacksonAnnotations = ClassUtil.isJacksonAnnotationsPresent();
}
/**
* Read the initial non-relationship annotations included Id and EmbeddedId.
* <p>
* We then have enough to create BeanTables which are used in readAssociations
* to resolve the relationships etc.
* </p>
*/
public void readInitial(DeployBeanInfo<?> info, boolean eagerFetchLobs) {
try {
new AnnotationClass(info, javaxValidationAnnotations, asOfViewSuffix).parse();
new AnnotationFields(generatedPropFactory, info, javaxValidationAnnotations, jacksonAnnotations, eagerFetchLobs).parse();
} catch (RuntimeException e) {
String msg = "Error reading annotations for " + info;
throw new RuntimeException(msg, e);
}
}
/**
* Read and process the associated relationship annotations.
* <p>
* These can only be processed after the BeanTables have been created
* </p>
* <p>
* This uses the factory as a call back to get the BeanTable for a given
* associated bean.
* </p>
*/
public void readAssociations(DeployBeanInfo<?> info, BeanDescriptorManager factory) {
try {
new AnnotationAssocOnes(info, javaxValidationAnnotations, factory).parse();
new AnnotationAssocManys(info, javaxValidationAnnotations, factory).parse();
// read the Sql annotations last because they may be
// dependent on field level annotations
new AnnotationSql(info, javaxValidationAnnotations).parse();
} catch (RuntimeException e) {
throw new RuntimeException("Error reading annotations for " + info, e);
}
}
}
@@ -225,23 +225,44 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
* In - property has a value in the array of values.
*/
public Expression in(String propertyName, Object[] values) {
return new InExpression(propertyName, values);
return new InExpression(propertyName, values, false);
}
/**
* In - using a subQuery.
*/
public Expression in(String propertyName, Query<?> subQuery) {
return new InQueryExpression(propertyName, (SpiQuery<?>) subQuery);
return new InQueryExpression(propertyName, (SpiQuery<?>) subQuery, false);
}
/**
* In - property has a value in the collection of values.
*/
public Expression in(String propertyName, Collection<?> values) {
return new InExpression(propertyName, values);
return new InExpression(propertyName, values, false);
}
/**
* In - property has a value in the array of values.
*/
public Expression notIn(String propertyName, Object[] values) {
return new InExpression(propertyName, values, true);
}
/**
* Not In - property has a value in the collection of values.
*/
public Expression notIn(String propertyName, Collection<?> values) {
return new InExpression(propertyName, values, true);
}
/**
* In - using a subQuery.
*/
public Expression notIn(String propertyName, Query<?> subQuery) {
return new InQueryExpression(propertyName, (SpiQuery<?>) subQuery, true);
}
/**
* Exists subquery
*/
@@ -12,16 +12,20 @@ class InExpression extends AbstractExpression {
private static final long serialVersionUID = 3150665801693551260L;
private final boolean not;
private final Object[] values;
InExpression(String propertyName, Collection<?> coll) {
InExpression(String propertyName, Collection<?> coll, boolean not) {
super(propertyName);
values = coll.toArray(new Object[coll.size()]);
this.values = coll.toArray(new Object[coll.size()]);
this.not = not;
}
InExpression(String propertyName, Object[] array) {
InExpression(String propertyName, Object[] array, boolean not) {
super(propertyName);
this.values = array;
this.not = not;
}
public void addBindValues(SpiExpressionRequest request) {
@@ -50,8 +54,10 @@ class InExpression extends AbstractExpression {
public void addSql(SpiExpressionRequest request) {
if (values.length == 0) {
// 'no match' for in empty collection
request.append("1=0");
if (!not) {
// 'no match' for in empty collection
request.append("1=0");
}
return;
}
@@ -69,6 +75,9 @@ class InExpression extends AbstractExpression {
} else {
request.append(propertyName);
if (not) {
request.append(" not");
}
request.append(" in (?");
for (int i = 1; i < values.length; i++) {
request.append(", ").append("?");
@@ -82,7 +91,7 @@ class InExpression extends AbstractExpression {
* Based on the number of values in the in clause.
*/
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
builder.add(InExpression.class).add(propName).add(values.length);
builder.add(InExpression.class).add(propName).add(values.length).add(not);
builder.bind(values.length);
}
@@ -18,18 +18,20 @@ class InQueryExpression extends AbstractExpression {
private static final long serialVersionUID = 666990277309851644L;
private final boolean not;
private final SpiQuery<?> subQuery;
private transient CQuery<?> compiledSubQuery;
public InQueryExpression(String propertyName, SpiQuery<?> subQuery) {
public InQueryExpression(String propertyName, SpiQuery<?> subQuery, boolean not) {
super(propertyName);
this.subQuery = subQuery;
this.not = not;
}
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
builder.add(InQueryExpression.class).add(propName);
builder.add(InQueryExpression.class).add(propName).add(not);
subQuery.queryAutofetchHash(builder);
}
@@ -61,9 +63,11 @@ class InQueryExpression extends AbstractExpression {
subSelect = subSelect.replace('\n', ' ');
String propertyName = getPropertyName();
request.append(" (");
request.append(propertyName);
request.append(") in (");
request.append(" (").append(propertyName).append(")");
if (not) {
request.append(" not");
}
request.append(" in (");
request.append(subSelect);
request.append(") ");
}
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.expression;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -217,6 +218,16 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
throw new RuntimeException("filterMany not allowed on Junction expression list");
}
@Override
public Query<T> asOf(Timestamp asOf) {
return exprList.asOf(asOf);
}
@Override
public List<Version<T>> findVersions() {
return exprList.findVersions();
}
@Override
public Query<T> apply(PathProperties pathProperties) {
return exprList.apply(pathProperties);
@@ -252,11 +263,6 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
exprList.findEachWhile(consumer);
}
@Override
public void findVisit(QueryResultVisitor<T> visitor) {
exprList.findVisit(visitor);
}
@Override
public QueryIterator<T> findIterate() {
return exprList.findIterate();
@@ -361,7 +367,22 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
public ExpressionList<T> in(String propertyName, com.avaje.ebean.Query<?> subQuery) {
return exprList.in(propertyName, subQuery);
}
@Override
public ExpressionList<T> notIn(String propertyName, Collection<?> values) {
return exprList.notIn(propertyName, values);
}
@Override
public ExpressionList<T> notIn(String propertyName, Object... values) {
return exprList.notIn(propertyName, values);
}
@Override
public ExpressionList<T> notIn(String propertyName, com.avaje.ebean.Query<?> subQuery) {
return exprList.notIn(propertyName, subQuery);
}
@Override
public ExpressionList<T> exists(Query<?> subQuery) {
return exprList.exists(subQuery);
@@ -49,6 +49,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
if (parent.isReadOnly() != null) {
query.setReadOnly(parent.isReadOnly());
}
query.asOf(parent.getAsOf());
query.setParentNode(objectGraphNode);
query.setLazyLoadProperty(lazyLoadProperty);
@@ -13,6 +13,7 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -30,7 +31,8 @@ public class DLoadContext implements LoadContext {
private final Map<String, DLoadManyContext> manyMap = new HashMap<String, DLoadManyContext>();
private final DLoadBeanContext rootBeanContext;
private final Timestamp asOf;
private final Boolean readOnly;
private final boolean excludeBeanCache;
private final int defaultBatchSize;
@@ -56,6 +58,7 @@ public class DLoadContext implements LoadContext {
this.rootDescriptor = request.getBeanDescriptor();
SpiQuery<?> query = request.getQuery();
this.asOf = query.getAsOf();
this.readOnly = query.isReadOnly();
this.excludeBeanCache = Boolean.FALSE.equals(query.isUseBeanCache());
this.useAutofetchManager = query.getAutoFetchManager() != null;
@@ -211,8 +214,12 @@ public class DLoadContext implements LoadContext {
protected Boolean isReadOnly() {
return readOnly;
}
public PersistenceContext getPersistenceContext() {
protected Timestamp getAsOf() {
return asOf;
}
public PersistenceContext getPersistenceContext() {
return persistenceContext;
}
@@ -60,6 +60,8 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
if (parent.isReadOnly() != null){
query.setReadOnly(parent.isReadOnly());
}
query.asOf(parent.getAsOf());
query.setParentNode(objectGraphNode);
if (queryProps != null){

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