Compare commits

...
108 Commits
Author SHA1 Message Date
Rob Bygrave 9c74520a4b [maven-release-plugin] prepare release ebean-8.8.1 2016-11-04 19:19:02 +13:00
Rob Bygrave c911c710d5 Bump pom to 8.8.1-SNAPSHOT 2016-11-04 01:17:08 +13:00
Rob Bygrave f08dd01035 #836 - ENH: Add support for @DbArray even for H2 db 2016-11-04 01:16:32 +13:00
Rob Bygrave a1b4af2716 Refactor - no effective change - remove un-required callback from PersistExecute to PersistRequestBean 2016-11-03 23:42:43 +13:00
Rob Bygrave 8c500c5836 #846 - Add soft delete related callbacks 2016-11-03 23:27:39 +13:00
Rob Bygrave c1d6cc7b75 Refactor - no real change - tighter access on SqlTreeNodeBean 2016-11-03 23:18:14 +13:00
Rob Bygrave d6fe16ffda Change permissions on travis scripts 2016-11-03 23:14:14 +13:00
Rob Bygrave d0230491e5 #99 - ENH (419) : Simplify aggregation queries - part 2 2016-11-03 23:06:54 +13:00
Rob Bygrave 3526f71f0a #99 - ENH (419) : Simplify aggregation queries 2016-11-02 22:10:22 +13:00
Rob Bygrave 18f943ab2b [maven-release-plugin] prepare for next development iteration 2016-10-31 21:59:39 +13:00
Rob Bygrave 03ff53c7af [maven-release-plugin] prepare release ebean-8.7.1 2016-10-31 21:59:30 +13:00
Rob Bygrave c20d111ea2 Bump version to 8.7.1-SNAPSHOT 2016-10-31 21:58:43 +13:00
Rob Bygrave 920fa73d11 #837 - ENH: Support using @HistoryExclude on normal properties (other than @ManyToMany) 2016-10-31 21:57:16 +13:00
Rob Bygrave cd62686337 #845 - Fix EQL parsing to support the 'inverse' form - e.g. where 'Rob' eq firstName (where the path is on the RHS rather than LHS) 2016-10-31 21:32:29 +13:00
Rob Bygrave e9ad459fa5 #843 - Change ServerConfig name to default to "db" (prior default was null) and defaultServer to true 2016-10-31 20:40:26 +13:00
Rob Bygrave ea5e9fa75a #842 - For @DbJson map the type Map<String, ?> (using the wildcard) ... the same as Map<String, Object> 2016-10-31 20:15:23 +13:00
Rob BygraveandGitHub d44460e180 Merge pull request #841 from FOCONIS/bugfix/softdelete-calls-wrong-lifecycle-method
Bugfix/softdelete calls wrong lifecycle method
2016-10-27 10:17:29 +13:00
Roland Praml 0e5ebf3fea FIX: softDeleting an entity calls now preUpdate/postUpdate 2016-10-26 15:47:54 +02:00
Roland Praml 42d35f8ec6 FIX: removed trailing commas in logs 2016-10-26 15:45:10 +02:00
Rob Bygrave 7f52e466e0 [maven-release-plugin] prepare for next development iteration 2016-10-25 22:23:13 +13:00
Rob Bygrave 861a8a4ee5 [maven-release-plugin] prepare release ebean-8.6.1 2016-10-25 22:23:04 +13:00
Rob Bygrave abe2f76d5c Bump pom to 8.6.1-SNAPSHOT 2016-10-25 22:22:16 +13:00
Rob Bygrave d0f75a940c #239 - Support Geometric types of Postgres, Also adding LineString, MultiLineString and MultiPoint 2016-10-25 22:19:33 +13:00
Rob Bygrave 44ad78b5e0 #239 - Support Geometric types of Postgres 2016-10-21 02:45:06 +13:00
Rob Bygrave 967b4de784 #239 - Support Geometric types of Postgres 2016-10-21 02:42:25 +13:00
Rob Bygrave ea45201905 [maven-release-plugin] prepare for next development iteration 2016-10-17 20:58:19 +13:00
Rob Bygrave 938f4ec9c2 [maven-release-plugin] prepare release ebean-8.5.1 2016-10-17 20:58:10 +13:00
Rob Bygrave 99bd2972d0 Bump pom version to 8.5.1-SNAPSHOT 2016-10-17 20:57:26 +13:00
Rob Bygrave deac291951 #835 - A query that includes a fetch() of a many that is @SoftDelete where all the rows are soft deleted returns incorrect results 2016-10-17 20:52:18 +13:00
Rob Bygrave 3161e24677 #835 - Tests for - A query that includes a fetch() of a many that is @SoftDelete where all the rows are soft deleted returns incorrect results 2016-10-17 20:51:22 +13:00
Rob Bygrave 37804bd083 Modify tests to avoid MySql keyword (mod) for column names 2016-10-17 20:50:48 +13:00
Rob Bygrave 0a2a987695 Modify tests to avoid Oracle keywords for column names 2016-10-15 17:03:32 +13:00
Rob Bygrave 4a085cb819 #834 - Oracle DDL for drop index and drop constraints incorrectly includes "if exits" 2016-10-15 17:02:44 +13:00
Rob Bygrave 67469ef4d3 No effective change - change Line endings to CR (from CRLF) 2016-10-15 14:39:20 +13:00
Rob Bygrave c3f43d503f #832 - Refactor: Change generated SQL to use "left join" rather than "left outer join" - change tests 2016-10-15 14:37:36 +13:00
Rob Bygrave 9158f46bb3 #832 - Refactor: Change generated SQL to use "left join" rather than "left outer join" 2016-10-15 14:36:11 +13:00
Rob Bygrave b3bfa88eb5 [maven-release-plugin] prepare for next development iteration 2016-10-15 01:14:29 +13:00
Rob Bygrave 185dec9c02 [maven-release-plugin] prepare release ebean-8.4.2 2016-10-15 01:14:18 +13:00
Rob Bygrave 20634233ca Update tests only - handle MySql specifics (boolean literal, lack of order by nulls last) 2016-10-15 00:55:09 +13:00
Rob Bygrave 7c9dda586e No effective change - format only 2016-10-14 23:49:00 +13:00
Rob BygraveandGitHub 3ca0f7b776 Merge pull request #828 from FOCONIS/feature/postconstruct
Feature/postconstruct
2016-10-14 23:44:45 +13:00
Rob Bygrave 85caca48de No effective change - add tests for naming convention on m2m columns 2016-10-14 23:29:50 +13:00
Rob Bygrave ff85a51994 No effective change - format only 2016-10-14 23:29:27 +13:00
Rob BygraveandGitHub 6dc3038d0d Merge pull request #830 from lvffilho/master
Add NamingConvention support to ManyToMany columns
2016-10-14 23:27:32 +13:00
Rob BygraveandGitHub c0fcb77f3d Merge pull request #829 from kdazzle/patch-1
Remove outdated findPageList(int, int) javadocs
2016-10-14 23:17:42 +13:00
Rob Bygrave da5d5296b6 #831 - EQL query language parser needs to throw exception on invalid query ... "find ..." - remove unhelpful tests 2016-10-14 21:09:23 +13:00
Rob Bygrave f04ab28c3f #831 - EQL query language parser needs to throw exception on invalid query ... "find ..." 2016-10-14 21:08:23 +13:00
Rob Bygrave d6cd41f8d2 #831 - EQL query language parser needs to throw exception on invalid query ... "find ..." - tests 2016-10-14 21:07:31 +13:00
Rob Bygrave 504a209a78 Merge branch 'master' of https://github.com/ebean-orm/ebean 2016-10-14 20:24:13 +13:00
Rob Bygrave 9f98b5333d Update tests - for asserts trim column alias from generated SQL if necessary (to support generating without column alias) 2016-10-14 20:23:36 +13:00
Eddie Mc Greal 2a600adbcf Added tests for EQL - syntactically incorrect und named parameter on LHS of comparison operator 2016-10-12 19:42:31 +02:00
Luiz Filho 9ce6007330 Add NamingConvention support to ManyToMany columns 2016-10-07 16:46:40 -03:00
Kyle ValadeandGitHub 7780dd6272 Remove outdated findPageList(int, int) javadocs
The class no longer supports that type of call
2016-10-07 12:26:06 -04:00
Roland Praml b01ddea781 FIX: fixed non-deterministic failing TestHistoryInsert testcase 2016-10-05 21:51:42 +02:00
Roland Praml b848587cd3 ADD: Testcases for PostConstruct 2016-10-05 21:51:04 +02:00
Roland Praml fe7e929a99 Refactor: Removed duplicate code from BootupClasses - use generics. 2016-10-05 21:50:22 +02:00
Roland Praml 3ee0b0224e ENH: Support for javax.annotation.PostConstruct. BeanPostConstructListener will be informed when an entity-bean is constructed/created to perform dependency-injection (e.g. Spring-Autowire) 2016-10-05 21:48:11 +02:00
Roland Praml 169eba4296 No effective code change - fixed commets 2016-10-05 21:39:44 +02:00
Robin Bygrave 91d3279061 [maven-release-plugin] prepare for next development iteration 2016-10-03 23:40:32 +13:00
Robin Bygrave b4b7c631da [maven-release-plugin] prepare release ebean-8.4.1 2016-10-03 23:40:22 +13:00
Robin Bygrave 4f98ca5d91 #821 - CQueryIteratorSimple not working with consecutive calls to hasNext() - ie. without matching calls to next() 2016-10-03 23:36:03 +13:00
Robin Bygrave 014a9aef6f #826 - Remove deprecated methods - migrate findRowCount() to findCount() & findFutureRowCount() to findFutureCount() 2016-10-03 22:31:33 +13:00
Robin Bygrave f21c33df7b Bump pom to 8.4.1-SNAPSHOT 2016-10-03 22:16:52 +13:00
Robin Bygrave f1eb494e0d #824 - Refactor com.avaje.ebean.config.dbplatform DbType & DbPlatformType - handle logical and plaform ddl conversion 2016-10-03 22:04:37 +13:00
Robin Bygrave d26203db1f #824 - Refactor com.avaje.ebean.config.dbplatform DbType & DbPlatformType - support custom DbType mapping options 2016-10-03 20:52:53 +13:00
Robin Bygrave abfe58fb06 #824 - Refactor com.avaje.ebean.config.dbplatform DbType & DbPlatformType - no effective change, tidy. 2016-09-23 00:27:39 +12:00
Robin Bygrave d0ad5f6c48 #824 - Refactor com.avaje.ebean.config.dbplatform DbType & DbPlatformType 2016-09-23 00:17:51 +12:00
Robin Bygrave bc4a246f33 [maven-release-plugin] prepare for next development iteration 2016-09-22 22:25:14 +12:00
Robin Bygrave 61f590944b [maven-release-plugin] prepare release ebean-8.3.1 2016-09-22 22:25:05 +12:00
Robin Bygrave 223e53416d Bump pom version to 8.3.1-SNAPSHOT 2016-09-22 22:24:17 +12:00
Robin Bygrave a3143b1cf6 #822 - EbeanServer.getBeanId(entity) does not trigger jdbc batch flush - when bean inserted with jdbc batch mode 2016-09-22 21:25:22 +12:00
Robin Bygrave bd3e7c8035 #823 - Remove deprecated API from PagedList 2016-09-22 21:02:28 +12:00
Robin Bygrave b753ce3a73 #820 - #718 Removed PagedList.getPageIndex() - Can it be added back? 2016-09-22 20:57:17 +12:00
Robin Bygrave a0f2ebe448 [maven-release-plugin] prepare for next development iteration 2016-09-01 21:08:23 +12:00
Robin Bygrave 7e7810602e [maven-release-plugin] prepare release ebean-8.2.3 2016-09-01 21:08:09 +12:00
Robin Bygrave d218fff374 #819 - Fix for bug in @ManyToOne relations with enabled soft-deletes 2016-08-31 23:03:00 +12:00
Rob BygraveandGitHub a0e927d353 Merge pull request #819 from FOCONIS/test-softdelete
ADD: testcase for bug in @ManyToOne relations with enabled soft-deletes
2016-08-31 22:05:46 +12:00
Robin Bygrave 51a58d4993 No effective change - remove whitespace in test 2016-08-31 20:57:24 +12:00
Roland Praml b9f0ee7ac0 ADD: testcase for https://groups.google.com/forum/#!topic/ebean/WKbGmROoIRA 2016-08-31 09:48:11 +02:00
Roland Praml 4f3b461f8e FIX: TestHistoryInsert / TestNewTypes failing nondeterministic 2016-08-30 09:14:22 +02:00
Roland Praml adfe5f0305 FIX: Bean did not get dirty when modified with ExpressionPath.pathSet 2016-08-30 09:13:44 +02:00
Robin Bygrave 1796c8b9bc #817 - SqlRow getBoolean() does not treat numeric 1 value as true (with MySql and other DB's without native boolean type) - Fix test for PG 2016-08-29 21:50:14 +12:00
Robin Bygrave 4bad4358f0 Merge branch 'master' of https://github.com/ebean-orm/ebean 2016-08-29 21:06:55 +12:00
Robin Bygrave 92665ebc3b Refactor - tidy of DefaultSqlRow - no logical change 2016-08-29 21:06:31 +12:00
Robin Bygrave 59670d0303 #817 - SqlRow getBoolean() does not treat numeric 1 value as true (with MySql and other DB's without native boolean type) 2016-08-29 20:54:49 +12:00
Robin Bygrave 244275fd72 #816 - Error saving circular relationships (when generated Id values) 2016-08-25 22:44:22 +12:00
Robin Bygrave d1e2176a5a [maven-release-plugin] prepare for next development iteration 2016-08-24 22:53:15 +12:00
Robin Bygrave 43968e70b8 [maven-release-plugin] prepare release ebean-8.2.2 2016-08-24 22:53:02 +12:00
Robin Bygrave 0aa3bd3c97 Update README 2016-08-24 22:52:02 +12:00
Robin Bygrave 2d85a9a20c Update README 2016-08-24 22:45:14 +12:00
Robin Bygrave 157dfcfeb6 #815 - ENH: When loading ebean-autotune.xml also look in resources (if not found as file) 2016-08-24 22:19:46 +12:00
Robin Bygrave 2b7c204072 No effective change - adjust tests for deleting OneToOne - Tire to Wheel 2016-08-24 21:32:10 +12:00
Robin Bygrave be731feb6b No effective change - adjust tests for H2 error vs Postgres truncate with oversized varchar input 2016-08-23 20:01:11 +12:00
Robin Bygrave da15d0b2c6 No effective change - format javadoc 2016-08-23 20:00:07 +12:00
Rob BygraveandGitHub 9c5fd496c1 Merge pull request #814 from FOCONIS/master
ENHANCEMENT: multiple indices & meta-annotation support similar to spring-framework
2016-08-23 19:43:04 +12:00
Roland Praml 5075ea32b4 Merge branch 'master' of github.com:FOCONIS/avaje-ebeanorm 2016-08-23 09:27:28 +02:00
Roland Praml f5c8d8896f Used @Repeatable feature from JDK8 for @Indices 2016-08-23 09:27:12 +02:00
Roland Praml 32fdff9f11 Used @Repeatable feature from JDK8 for @Indices 2016-08-22 10:00:13 +02:00
Roland Praml 87f6964995 Ignored tests that make problems with Postgres 2016-08-22 09:34:45 +02:00
Roland Praml b45226dd41 Merge remote-tracking branch 'ebean/master' 2016-08-16 11:06:47 +02:00
Roland Praml 992c40d362 no effective code change - fix indentions 2016-08-16 11:06:01 +02:00
Rob BygraveandGitHub 9144d60948 Merge pull request #813 from zhangshifeng/fix-artifactId
Update README.md and pom.xml for artifactId change
2016-08-13 11:23:04 +12:00
zhangshifeng 6396143d49 Update README.md and pom.xml for artifactId change 2016-08-12 17:20:29 +08:00
Robin Bygrave ce045ca0fe No effective change - remove unused methods (from ImportedId) 2016-08-11 20:22:35 +12:00
Robin Bygrave d722d34072 [maven-release-plugin] prepare for next development iteration 2016-08-05 13:53:23 +12:00
Roland Praml 84f32c2ace ENH: support for multiple indices added 2016-07-30 16:06:01 +02:00
Roland Praml 85288a0652 Added a testcase that checks if meta-annotations are recognized 2016-07-30 13:13:54 +02:00
Roland Praml 3183287833 ENH: meta-annotation support similar to spring.
See http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-meta-annotations for detailed explanation
2016-07-30 12:44:14 +02:00
308 changed files with 8561 additions and 5890 deletions
+5 -5
View File
@@ -6,12 +6,12 @@ Goto [https://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Web
## Maven cental links:
[Maven central - avaje-ebeanorm](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.avaje.ebeanorm%22%20AND%20a%3A%22avaje-ebeanorm%22 "maven central ebeanorm")
[Maven central - ebean](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.avaje.ebean%22%20AND%20a%3A%22ebean%22 "maven central ebean")
[Maven central - all related projects](http://search.maven.org/#search%7Cga%7C1%7Cavaje-ebeanorm "maven central ebeanorm")
[Maven central - all related projects](http://search.maven.org/#search%7Cga%7C1%7Cebean "maven central all related projects")
## Current versions
* [![Maven Central : avaje-ebeanorm](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm) - avaje-ebeanorm
* [![Maven Central : avaje-ebeanorm-agent](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-agent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-agent) - avaje-ebeanorm-agent
* [![Maven Central : avaje-ebeanorm-agent](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-mavenenhancer/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebeanorm/avaje-ebeanorm-mavenenhancer) - avaje-ebeanorm-mavenenhancer
* [![Maven Central : ebean](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean) - ebean
* [![Maven Central : ebean-agent](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean-agent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean-agent) - ebean-agent
* [![Maven Central : ebean-maven-plugin](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean-maven-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.avaje.ebean/ebean-maven-plugin) - ebean-maven-plugin
+3 -3
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebean</groupId>
<artifactId>ebean</artifactId>
<version>8.2.1</version>
<version>8.8.1</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -36,8 +36,8 @@
</developers>
<scm>
<developerConnection>scm:git:https://github.com/ebean-orm/avaje-ebeanorm.git</developerConnection>
<tag>ebean-8.2.1</tag>
<developerConnection>scm:git:https://github.com/ebean-orm/ebean.git</developerConnection>
<tag>ebean-8.8.1</tag>
</scm>
<dependencies>
@@ -1,6 +1,7 @@
package com.avaje.ebean;
import com.avaje.ebean.RawSql.Sql;
import com.avaje.ebeaninternal.server.querydefn.SimpleTextParser;
/**
* Parses sql-select queries to try and determine the location where WHERE and
@@ -8,13 +9,13 @@ import com.avaje.ebean.RawSql.Sql;
*/
class DRawSqlParser {
public static final String $_AND_HAVING = "${andHaving}";
private static final String $_AND_HAVING = "${andHaving}";
public static final String $_HAVING = "${having}";
private static final String $_HAVING = "${having}";
public static final String $_AND_WHERE = "${andWhere}";
private static final String $_AND_WHERE = "${andWhere}";
public static final String $_WHERE = "${where}";
private static final String $_WHERE = "${where}";
private final SimpleTextParser textParser;
+4 -19
View File
@@ -167,8 +167,9 @@ public interface EbeanServer {
/**
* Create a new instance of T that is an EntityBean.
* <p>
* Generally not expected to be useful (now dynamic subclassing support was removed in
* favour of always using enhancement).
* Useful if you use BeanPostConstructListeners or &#64;PostConstruct Annotations.
* In this case you should not use "new Bean...()". Making all bean construtors protected
* could be a good idea here.
* </p>
*/
<T> T createEntityBean(Class<T> type);
@@ -746,14 +747,6 @@ public interface EbeanServer {
*/
<T> int findCount(Query<T> query, Transaction transaction);
/**
* Deprecated in favor of findCount()
*
* Return the number of 'top level' or 'root' entities this query should return.
* @deprecated
*/
<T> int findRowCount(Query<T> query, Transaction transaction);
/**
* Return the Id values of the query as a List.
*
@@ -892,18 +885,10 @@ public interface EbeanServer {
* @param query the query to execute the row count on
* @param transaction the transaction (can be null).
* @return a Future object for the row count query
* @see com.avaje.ebean.Query#findFutureRowCount()
* @see com.avaje.ebean.Query#findFutureCount()
*/
<T> FutureRowCount<T> findFutureCount(Query<T> query, Transaction transaction);
/**
* Deprecated in favor of findFutureCount().
*
* Execute find row count query in a background thread.
* @deprecated
*/
<T> FutureRowCount<T> findFutureRowCount(Query<T> query, Transaction transaction);
/**
* Execute find Id's query in a background thread.
* <p>
@@ -185,13 +185,6 @@ public interface ExpressionList<T> {
*/
int findCount();
/**
* Deprecated in favor of findCount().
*
* @deprecated
*/
int findRowCount();
/**
* Execute the query returning a set.
*
@@ -265,13 +258,6 @@ public interface ExpressionList<T> {
*/
FutureRowCount<T> findFutureCount();
/**
* Deprecated in favor of findFutureCount().
*
* @deprecated
*/
FutureRowCount<T> findFutureRowCount();
/**
* Execute find Id's query in a background thread.
* <p>
+8 -45
View File
@@ -44,30 +44,6 @@ import java.util.concurrent.Future;
*
* }</pre>
*
* <h4>Example: typical use including total row count</h4>
* <pre>{@code
*
* // We want to find the first 100 new orders
* // ... 0 means first page
* // ... page size is 100
*
* PagedList<Order> pagedList
* = ebeanServer.find(Order.class)
* .where().eq("status", Order.Status.NEW)
* .order().asc("id")
* .findPagedList(0, 100);
*
* // Optional: initiate the loading of the total
* // row count in a background thread
* pagedList.loadRowCount();
*
* // fetch and return the list in the foreground thread
* List<Order> orders = pagedList.getList();
*
* // get the total row count (from the future)
* int totalRowCount = pagedList.getTotalRowCount();
*
* }</pre>
*
* <h4>Example: No total row count required</h4>
* <pre>{@code
@@ -112,13 +88,6 @@ public interface PagedList<T> {
*/
void loadCount();
/**
* Deprecated in favor of loadCount().
*
* @deprecated
*/
void loadRowCount();
/**
* Return the Future row count. You might get this if you wish to cancel the total row count query
* or specify a timeout for the row count query.
@@ -147,13 +116,6 @@ public interface PagedList<T> {
*/
Future<Integer> getFutureCount();
/**
* Deprecated in favor of getFutureCount().
*
* @deprecated
*/
Future<Integer> getFutureRowCount();
/**
* Return the list of entities for this page.
*/
@@ -186,13 +148,6 @@ public interface PagedList<T> {
*/
int getTotalCount();
/**
* Deprecated in favor of getTotalCount().
*
* @deprecated
*/
int getTotalRowCount();
/**
* Return the total number of pages based on the page size and total row count.
* <p>
@@ -207,6 +162,14 @@ public interface PagedList<T> {
*/
int getPageSize();
/**
* Return the index position of this page (Zero based).
* <p>
* This is a calculated value based on firstRow/maxRows.
* </p>
*/
int getPageIndex();
/**
* Return true if there is a next page.
* <p>
-24
View File
@@ -845,15 +845,6 @@ public interface Query<T> {
*/
int findCount();
/**
* Deprecated in favor of findCount().
* <p>
* Return the count of entities this query should return.
*
* @deprecated
*/
int findRowCount();
/**
* Execute find row count query in a background thread.
* <p>
@@ -866,21 +857,6 @@ public interface Query<T> {
*/
FutureRowCount<T> findFutureCount();
/**
* Deprecated in favor of findFutureCount().
* <p>
* Execute find row count query in a background thread.
* <p>
* This returns a Future object which can be used to cancel, check the
* execution status (isDone etc) and get the value (with or without a
* timeout).
* </p>
*
* @return a Future object for the row count query
* @deprecated
*/
FutureRowCount<T> findFutureRowCount();
/**
* Execute find Id's query in a background thread.
* <p>
@@ -1,167 +0,0 @@
package com.avaje.ebean;
class SimpleTextParser {
private final String oql;
private final char[] chars;
private final int eof;
private int pos;
private String word;
private String lowerWord;
SimpleTextParser(String oql) {
this.oql = oql;
this.chars = oql.toCharArray();
this.eof = oql.length();
}
public int getPos() {
return pos;
}
public String getWord() {
return word;
}
public String peekNextWord() {
int origPos = pos;
String nw = nextWordInternal();
pos = origPos;
return nw;
}
/**
* Match the current and the next word.
*/
public boolean isMatch(String lowerMatch, String nextWordMatch) {
if (isMatch(lowerMatch)) {
String nw = peekNextWord();
if (nw != null) {
nw = nw.toLowerCase();
return nw.equals(nextWordMatch);
}
}
return false;
}
public boolean isFinished() {
return word == null;
}
public int findWordLower(String lowerMatch, int afterPos) {
this.pos = afterPos;
return findWordLower(lowerMatch);
}
public int findWordLower(String lowerMatch) {
do {
if (nextWord() == null) {
return -1;
}
if (lowerMatch.equals(lowerWord)) {
return pos - lowerWord.length();
}
} while (true);
}
/**
* Match the current word.
*/
public boolean isMatch(String lowerMatch) {
return lowerMatch.equals(lowerWord);
}
public String nextWord() {
word = nextWordInternal();
if (word != null) {
lowerWord = word.toLowerCase();
}
return word;
}
private String nextWordInternal() {
trimLeadingWhitespace();
if (pos >= eof) {
return null;
}
int start = pos;
if (chars[pos] == '(') {
moveToClose();
} else {
moveToEndOfWord();
}
return oql.substring(start, pos);
}
private void moveToClose() {
pos++;
int openParenthesisCount = 0;
for (; pos < eof; pos++) {
char c = chars[pos];
if (c == '(') {
// count nested parenthesis
openParenthesisCount++;
} else if (c == ')') {
if (openParenthesisCount > 0) {
// still in nested parenthesis
--openParenthesisCount;
} else {
// we have found the end
pos++;
return;
}
}
}
}
private void moveToEndOfWord() {
char c = chars[pos];
boolean isOperator = isOperator(c);
for (; pos < eof; pos++) {
c = chars[pos];
if (isWordTerminator(c, isOperator)) {
return;
}
}
}
private boolean isWordTerminator(char c, boolean isOperator) {
if (Character.isWhitespace(c)) {
return true;
}
if (isOperator(c)) {
return !isOperator;
}
return c == '(' || isOperator;
}
private boolean isOperator(char c) {
switch (c) {
case '<':
return true;
case '>':
return true;
case '=':
return true;
case '!':
return true;
default:
return false;
}
}
private void trimLeadingWhitespace() {
for (; pos < eof; pos++) {
char c = chars[pos];
if (!Character.isWhitespace(c)) {
break;
}
}
}
}
@@ -57,7 +57,7 @@ package com.avaje.ebean;
* where id in (
* select t0.id c0
* from o_customer t0
* left outer join o_address t1 on t1.id = t0.billing_address_id
* left join o_address t1 on t1.id = t0.billing_address_id
* where t0.status = ?
* and t1.country_code = ?
* and t0.id > ? )
@@ -0,0 +1,50 @@
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Specify a property to be an aggregation formula.
* <p>
* The aggregation formula should be a sum, count, avg, min or max.
* By default aggregation properties are treated as transient and not
* included in a query. To populate the aggregation property it must be
* explicitly included in the select().
* </p>
*
* <h3>Example:</h3>
* <pre>{@code
*
* @Aggregation("count(details)")
* Long totalCount;
*
* @Aggregation("sum(details.quantity*details.unitPrice)")
* Long totalAmount;
*
* }</pre>
*
* <h3>Example query</h3>
* <pre>{@code
*
* List<TEventOne> list = Ebean.find(TEventOne.class)
* .select("name, totalCount, totalUnits, totalAmount")
* .where()
* .startsWith("logs.description", "a")
* .having()
* .ge("count", 1)
* .orderBy().asc("name")
* .findList();
*
* }</pre>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Aggregation {
/**
* Aggregation formula using sum, count, avg, min, max.
*/
String value();
}
@@ -82,7 +82,7 @@ public @interface Formula {
* as count, sum etc.
* </p>
* <p>
* The join string should start with either "left outer join" or "join".
* The join string should start with either "left join" or "join".
* </p>
*
* <p>
@@ -1,17 +1,19 @@
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* An annotation for declaring an index on a single column.
* An annotation for declaring an index.
*
* @author rvbiljouw
*/
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Indices.class)
public @interface Index {
/**
@@ -27,6 +29,9 @@ public @interface Index {
/**
* When placed on the class (rather than field) you can specify the columns
* to include in the index in order.
*
* Wnen placed on a field, and columnNames are specified, the field-column has to be included.
* You can use "${fa}" for alias
*/
String[] columnNames() default {};
@@ -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;
/**
* An annotation for declaring multiple indices at class or field level.
*
* @author Roland Praml, FOCONIS AG
*/
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Indices {
/**
* Array with {@link Index} definitions.
*/
Index[] value() default {};
}
@@ -91,7 +91,7 @@ public class ClassLoadConfig {
/**
* Return true if the given class is present.
*/
protected boolean isPresent(String className) {
public boolean isPresent(String className) {
try {
forName(className);
return true;
@@ -0,0 +1,54 @@
package com.avaje.ebean.config;
import com.avaje.ebean.config.dbplatform.DbType;
/**
* Custom mappings for DB types that override the default.
*
* @see ServerConfig#addCustomMapping(DbType, String)
*/
public class CustomDbTypeMapping {
protected final DbType type;
protected final String columnDefinition;
protected final Platform platform;
/**
* Create a mapping.
*/
public CustomDbTypeMapping(DbType type, String columnDefinition, Platform platform) {
this.type = type;
this.columnDefinition = columnDefinition;
this.platform = platform;
}
/**
* Create a mapping that should apply to all the database platforms.
*/
public CustomDbTypeMapping(DbType type, String columnDefinition) {
this(type, columnDefinition, null);
}
/**
* Return the DB type the mapping applies to.
*/
public DbType getType() {
return type;
}
/**
* Return the DB column definition to use.
*/
public String getColumnDefinition() {
return columnDefinition;
}
/**
* Return the platform this mapping should apply to. Null means it applied to all platforms.
*/
public Platform getPlatform() {
return platform;
}
}
@@ -1,7 +1,6 @@
package com.avaje.ebean.config;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.config.dbplatform.DbPlatformName;
import com.avaje.ebean.dbmigration.DbMigration;
import org.avaje.dbmigration.MigrationConfig;
import org.avaje.dbmigration.MigrationRunner;
@@ -20,7 +19,7 @@ public class DbMigrationConfig {
/**
* The database platform to generate migration DDL for.
*/
protected DbPlatformName platform;
protected Platform platform;
/**
* Set to true if the DB migration should be generated on server start.
@@ -112,14 +111,14 @@ public class DbMigrationConfig {
* We typically need to explicitly specify this as migration can often be generated
* when running against H2.
*/
public DbPlatformName getPlatform() {
public Platform getPlatform() {
return platform;
}
/**
* Set the DB platform to generate migration DDL for.
*/
public void setPlatform(DbPlatformName platform) {
public void setPlatform(Platform platform) {
this.platform = platform;
}
@@ -366,7 +365,7 @@ public class DbMigrationConfig {
includeGeneratedFileComment = properties.getBoolean("migration.includeGeneratedFileComment", includeGeneratedFileComment);
generatePendingDrop = properties.get("migration.generatePendingDrop", generatePendingDrop);
platform = properties.getEnum(DbPlatformName.class, "migration.platform", platform);
platform = properties.getEnum(Platform.class, "migration.platform", platform);
generate = properties.getBoolean("migration.generate", generate);
version = properties.get("migration.version", version);
@@ -1,9 +1,9 @@
package com.avaje.ebean.config.dbplatform;
package com.avaje.ebean.config;
/**
* Built in supported platforms.
*/
public enum DbPlatformName {
public enum Platform {
/**
* Generic base platform configured via properties or code.
@@ -7,9 +7,11 @@ import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.cache.ServerCachePlugin;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.DbEncrypt;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebean.event.BeanFindController;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistListener;
import com.avaje.ebean.event.BeanPostConstructListener;
import com.avaje.ebean.event.BeanPostLoad;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.event.BulkTableEventListener;
@@ -73,7 +75,7 @@ public class ServerConfig {
/**
* The EbeanServer name.
*/
private String name;
private String name = "db";
/**
* Typically configuration type objects that are passed by this ServerConfig
@@ -83,6 +85,8 @@ public class ServerConfig {
private ContainerConfig containerConfig;
private List<CustomDbTypeMapping> customDbTypeMappings = new ArrayList<CustomDbTypeMapping>();
/**
* The underlying properties that were used during configuration.
*/
@@ -101,13 +105,18 @@ public class ServerConfig {
/**
* Set to true if this is the default/primary server.
*/
private boolean defaultServer;
private boolean defaultServer = true;
/**
* Set this to true to disable class path search.
*/
private boolean disableClasspathSearch;
/**
* The Geometry SRID value (default 4326).
*/
private int geometrySRID = 4326;
/**
* List of interesting classes such as entities, embedded, ScalarTypes,
* Listeners, Finders, Controllers etc.
@@ -326,6 +335,7 @@ public class ServerConfig {
private List<BeanFindController> findControllers = new ArrayList<BeanFindController>();
private List<BeanPersistController> persistControllers = new ArrayList<BeanPersistController>();
private List<BeanPostLoad> postLoaders = new ArrayList<BeanPostLoad>();
private List<BeanPostConstructListener> postConstructListeners = new ArrayList<BeanPostConstructListener>();
private List<BeanPersistListener> persistListeners = new ArrayList<BeanPersistListener>();
private List<BeanQueryAdapter> queryAdapters = new ArrayList<BeanQueryAdapter>();
private List<BulkTableEventListener> bulkTableEventListeners = new ArrayList<BulkTableEventListener>();
@@ -553,7 +563,7 @@ public class ServerConfig {
}
/**
* Set true if this EbeanServer should be registered as the "default" server
* Set false if you do not want this EbeanServer to be registered as the "default" server
* with the Ebean singleton.
* <p>
* This is only used when {@link #setRegister(boolean)} is also true.
@@ -871,6 +881,20 @@ public class ServerConfig {
this.migrationConfig = migrationConfig;
}
/**
* Return the Geometry SRID.
*/
public int getGeometrySRID() {
return geometrySRID;
}
/**
* Set the Geometry SRID.
*/
public void setGeometrySRID(int geometrySRID) {
this.geometrySRID = geometrySRID;
}
/**
* Return the time zone to use when reading/writing Timestamps via JDBC.
* <p>
@@ -1437,7 +1461,7 @@ public class ServerConfig {
* <p>
* You can also set this in ebean.proprerties:
* </p>
*
* <p>
* <pre>{@code
* # set via ebean.properties
* ebean.encryptKeyManager=com.avaje.tests.basic.encrypt.BasicEncyptKeyManager
@@ -1770,7 +1794,7 @@ public class ServerConfig {
* queries that normally hit L2 bean cache automatically will not do so after a write/persist
* on the transaction.
* </p>
*
* <p>
* <pre>{@code
*
* // assume Customer has L2 bean caching enabled ...
@@ -1932,6 +1956,54 @@ public class ServerConfig {
this.resourceDirectory = resourceDirectory;
}
/**
* Add a custom type mapping.
* <p>
* <pre>{@code
*
* // set the default mapping for BigDecimal.class/decimal
* serverConfig.addCustomMapping(DbType.DECIMAL, "decimal(18,6)");
*
* // set the default mapping for String.class/varchar but only for Postgres
* serverConfig.addCustomMapping(DbType.VARCHAR, "text", Platform.POSTGRES);
*
* }</pre>
*
* @param type The DB type this mapping should apply to
* @param columnDefinition The column definition that should be used
* @param platform Optionally specify the platform this mapping should apply to.
*/
public void addCustomMapping(DbType type, String columnDefinition, Platform platform) {
customDbTypeMappings.add(new CustomDbTypeMapping(type, columnDefinition, platform));
}
/**
* Add a custom type mapping that applies to all platforms.
* <p>
* <pre>{@code
*
* // set the default mapping for BigDecimal/decimal
* serverConfig.addCustomMapping(DbType.DECIMAL, "decimal(18,6)");
*
* // set the default mapping for String/varchar
* serverConfig.addCustomMapping(DbType.VARCHAR, "text");
*
* }</pre>
*
* @param type The DB type this mapping should apply to
* @param columnDefinition The column definition that should be used
*/
public void addCustomMapping(DbType type, String columnDefinition) {
customDbTypeMappings.add(new CustomDbTypeMapping(type, columnDefinition));
}
/**
* Return the list of custom type mappings.
*/
public List<CustomDbTypeMapping> getCustomTypeMappings() {
return customDbTypeMappings;
}
/**
* Register a BeanQueryAdapter instance.
* <p>
@@ -2004,6 +2076,16 @@ public class ServerConfig {
postLoaders.add(postLoad);
}
/**
* Register a BeanPostConstructListener instance.
* <p>
* Note alternatively you can use {@link #setPostConstructListeners(List)} to set
* all the BeanPostConstructListener instances.
* </p>
*/
public void add(BeanPostConstructListener listener) {
postConstructListeners.add(listener);
}
/**
* Return the list of BeanFindController instances.
*/
@@ -2032,6 +2114,19 @@ public class ServerConfig {
this.postLoaders = postLoaders;
}
/**
* Return the list of BeanPostLoader instances.
*/
public List<BeanPostConstructListener> getPostConstructListeners() {
return postConstructListeners;
}
/**
* Set the list of BeanPostLoader instances.
*/
public void setPostConstructListeners(List<BeanPostConstructListener> listeners) {
this.postConstructListeners = listeners;
}
/**
* Return the BeanPersistController instances.
*/
@@ -2287,6 +2382,7 @@ public class ServerConfig {
}
loadDocStoreSettings(p);
geometrySRID = p.getInt("geometrySRID", geometrySRID);
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
explicitTransactionBeginMode = p.getBoolean("explicitTransactionBeginMode", explicitTransactionBeginMode);
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
@@ -24,9 +24,9 @@ public class DB2Platform extends DatabasePlatform {
this.dbIdentity.setSupportsSequence(true);
booleanDbType = Types.BOOLEAN;
dbTypeMap.put(Types.REAL, new DbType("real"));
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
dbTypeMap.put(Types.DECIMAL, new DbType("decimal", 15));
dbTypeMap.put(DbType.REAL, new DbPlatformType("real"));
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("decimal", 15));
}
/**
@@ -2,7 +2,9 @@ package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.Query;
import com.avaje.ebean.config.CustomDbTypeMapping;
import com.avaje.ebean.config.PersistBatch;
import com.avaje.ebean.config.Platform;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
@@ -73,7 +75,7 @@ public class DatabasePlatform {
/**
* Mapping of JDBC to Database types.
*/
protected DbTypeMap dbTypeMap = new DbTypeMap();
protected DbPlatformTypeMapping dbTypeMap = new DbPlatformTypeMapping();
/**
* Default values for DB columns.
@@ -180,6 +182,26 @@ public class DatabasePlatform {
*/
public void configure(ServerConfig serverConfig) {
dbTypeMap.config(nativeUuidType, serverConfig.getDbUuid());
for (CustomDbTypeMapping mapping : serverConfig.getCustomTypeMappings()) {
if (platformMatch(mapping.getPlatform())) {
dbTypeMap.put(mapping.getType(), parse(mapping.getColumnDefinition()));
}
}
}
private DbPlatformType parse(String columnDefinition) {
return DbPlatformType.parse(columnDefinition);
}
private boolean platformMatch(Platform platform) {
return platform == null || platform.name().equalsIgnoreCase(name);
}
/**
* Return true if this matches the given platform.
*/
public boolean isPlatform(Platform platform) {
return platform.name().equalsIgnoreCase(name);
}
/**
@@ -309,7 +331,7 @@ public class DatabasePlatform {
*
* @return the db type map
*/
public DbTypeMap getDbTypeMap() {
public DbPlatformTypeMapping getDbTypeMap() {
return dbTypeMap;
}
@@ -0,0 +1,158 @@
package com.avaje.ebean.config.dbplatform;
/**
* Represents a DB type with name, length, precision, and scale.
* <p>
* The length is for VARCHAR types and precision/scale for DECIMAL types.
* </p>
*/
public class DbPlatformType implements ExtraDbTypes {
/**
* The data type name (VARCHAR, INTEGER ...)
*/
private final String name;
/**
* The default length or precision.
*/
private final int defaultLength;
/**
* The default scale (decimal).
*/
private final int defaultScale;
/**
* Set to true if the type should never have a length or scale.
*/
private final boolean canHaveLength;
/**
* Parse a type definition into a DbPlatformType.
* <p>
* e.g. "decimal(18,6)"
* e.g. "text"
* </p>
*/
public static DbPlatformType parse(String columnDefinition) {
return DbPlatformTypeParser.parse(columnDefinition);
}
/**
* Construct with no length or scale.
*/
public DbPlatformType(String name) {
this(name, 0, 0);
}
/**
* Construct with a given length.
*/
public DbPlatformType(String name, int defaultLength) {
this(name, defaultLength, 0);
}
/**
* Construct for Decimal with precision and scale.
*/
public DbPlatformType(String name, int defaultPrecision, int defaultScale) {
this.name = name;
this.defaultLength = defaultPrecision;
this.defaultScale = defaultScale;
this.canHaveLength = true;
}
/**
* Use with canHaveLength=false for types that should never have a length.
*
* @param name
* the type name
* @param canHaveLength
* set this to false for type that should never have a length
*/
public DbPlatformType(String name, boolean canHaveLength) {
this.name = name;
this.defaultLength = 0;
this.defaultScale = 0;
this.canHaveLength = canHaveLength;
}
/**
* Return the type name.
*/
public String getName() {
return name;
}
/**
* Return the default length/precision.
*/
public int getDefaultLength() {
return defaultLength;
}
/**
* Return the default scale.
*/
public int getDefaultScale() {
return defaultScale;
}
/**
* Return the type for a specific property that incorporates the name, length,
* precision and scale.
* <p>
* The deployLength and deployScale are for the property we are rendering the
* DB type for.
* </p>
*
* @param deployLength
* the length or precision defined by deployment on a specific
* property.
* @param deployScale
* the scale defined by deployment on a specific property.
*/
public String renderType(int deployLength, int deployScale) {
return renderType(deployLength, deployScale, true);
}
/**
* Render the type defining strict mode.
* <p>
* If strict mode if OFF then this will render with a scale value even if
* that is not strictly supported. The reason for supporting this is to enable
* use to use types like jsonb(200) as a "logical" type that maps to JSONB for
* Postgres and VARCHAR(200) for other databases.
* </p>
*/
public String renderType(int deployLength, int deployScale, boolean strict) {
StringBuilder sb = new StringBuilder();
sb.append(name);
if (canHaveLength || !strict) {
// see if there is a precision/scale to add (or not)
int len = deployLength != 0 ? deployLength : defaultLength;
if (len > 0) {
sb.append("(");
sb.append(len);
int scale = deployScale != 0 ? deployScale : defaultScale;
if (scale > 0) {
sb.append(",");
sb.append(scale);
}
sb.append(")");
}
}
return sb.toString();
}
/**
* Create a copy of the type with a new default length.
*/
public DbPlatformType withLength(int defaultLength) {
return new DbPlatformType(name, defaultLength);
}
}
@@ -0,0 +1,53 @@
package com.avaje.ebean.config.dbplatform;
import java.util.HashMap;
import java.util.Map;
/**
* Helper to reverse lookup a DbType given the name or JDBC int value.
*/
class DbPlatformTypeLookup {
/**
* A map to lookup the type by name.
*/
private Map<String, DbType> nameLookup = new HashMap<String, DbType>();
/**
* A map to lookup the type by JDBC int value.
*/
private Map<Integer, DbType> idLookup = new HashMap<Integer, DbType>();
DbPlatformTypeLookup() {
addAll();
}
/**
* Return the DbType for the given name.
*/
DbType byName(String name) {
return nameLookup.get(name.trim().toUpperCase());
}
/**
* Return the DbType for the given name.
*/
DbType byId(int jdbcId) {
return idLookup.get(jdbcId);
}
private void addAll() {
// Extra mapping for Float and Varchar2
add("FLOAT", DbType.REAL);
add("VARCHAR2", DbType.VARCHAR);
for (DbType type : DbType.values()) {
add(type.name(), type);
}
}
private void add(String name, DbType dbType) {
nameLookup.put(name, dbType);
idLookup.put(dbType.id(), dbType);
}
}
@@ -0,0 +1,194 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.config.ServerConfig;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;
/**
* Used to map bean property types to DB specific types for DDL generation.
*/
public class DbPlatformTypeMapping {
private static DbPlatformTypeLookup lookup = new DbPlatformTypeLookup();
private static final DbPlatformType UUID_NATIVE = new DbPlatformType("uuid", false);
private static final DbPlatformType UUID_PLACEHOLDER = new DbPlatformType("uuidPlaceholder");
private static final DbPlatformType JSON_CLOB_PLACEHOLDER = new DbPlatformType("jsonClobPlaceholder");
private static final DbPlatformType JSON_BLOB_PLACEHOLDER = new DbPlatformType("jsonBlobPlaceholder");
private static final DbPlatformType JSON_VARCHAR_PLACEHOLDER = new DbPlatformType("jsonVarcharPlaceholder");
private static final DbPlatformType POINT = new DbPlatformType("point");
private static final DbPlatformType POLYGON = new DbPlatformType("polygon");
private static final DbPlatformType LINESTRING = new DbPlatformType("linestring");
private static final DbPlatformType MULTIPOINT = new DbPlatformType("multipoint");
private static final DbPlatformType MULTILINESTRING = new DbPlatformType("multilinestring");
private static final DbPlatformType MULTIPOLYGON = new DbPlatformType("multipolygon");
private final Map<DbType, DbPlatformType> typeMap = new HashMap<DbType, DbPlatformType>();
/**
* Return the DbTypeMap with standard (not platform specific) types.
* <p>
* This has some extended JSON types (JSON, JSONB, JSONVarchar, JSONClob, JSONBlob).
* These types get translated to specific database platform types during DDL generation.
*/
public static DbPlatformTypeMapping logicalTypes() {
return new DbPlatformTypeMapping(true);
}
public DbPlatformTypeMapping() {
loadDefaults(false);
}
private DbPlatformTypeMapping(boolean logicalTypes) {
loadDefaults(logicalTypes);
}
/**
* Load the standard types. These can be overridden by DB specific platform.
*/
private void loadDefaults(boolean logicalTypes) {
put(DbType.BOOLEAN);
put(DbType.BIT);
put(DbType.INTEGER);
put(DbType.BIGINT);
put(DbType.DOUBLE);
put(DbType.SMALLINT);
put(DbType.TINYINT);
put(DbType.BLOB);
put(DbType.CLOB);
put(DbType.ARRAY);
put(DbType.DATE);
put(DbType.TIME);
put(DbType.TIMESTAMP);
put(DbType.LONGVARBINARY);
put(DbType.LONGVARCHAR);
// most commonly real maps to db float
put(DbType.REAL, new DbPlatformType("float"));
put(DbType.POINT, POINT);
put(DbType.POLYGON, POLYGON);
put(DbType.LINESTRING, LINESTRING);
put(DbType.MULTIPOINT, MULTIPOINT);
put(DbType.MULTILINESTRING, MULTILINESTRING);
put(DbType.MULTIPOLYGON, MULTIPOLYGON);
if (logicalTypes) {
// keep it logical for 2 layer DDL generation
put(DbType.VARCHAR, new DbPlatformType("varchar"));
put(DbType.DECIMAL, new DbPlatformType("decimal"));
put(DbType.VARBINARY, new DbPlatformType("varbinary"));
put(DbType.BINARY, new DbPlatformType("binary"));
put(DbType.CHAR, new DbPlatformType("char"));
put(DbType.HSTORE, new DbPlatformType("hstore", false));
put(DbType.JSON, new DbPlatformType("json", false));
put(DbType.JSONB, new DbPlatformType("jsonb", false));
put(DbType.JSONCLOB, new DbPlatformType("jsonclob"));
put(DbType.JSONBLOB, new DbPlatformType("jsonblob"));
put(DbType.JSONVARCHAR, new DbPlatformType("jsonvarchar", 1000));
put(DbType.UUID, UUID_NATIVE);
} else {
put(DbType.VARCHAR, new DbPlatformType("varchar", 255));
put(DbType.DECIMAL, new DbPlatformType("decimal", 38));
put(DbType.VARBINARY, new DbPlatformType("varbinary", 255));
put(DbType.BINARY, new DbPlatformType("binary", 255));
put(DbType.CHAR, new DbPlatformType("char", 1));
put(DbType.JSON, JSON_CLOB_PLACEHOLDER); // Postgres maps this to JSON
put(DbType.JSONB, JSON_CLOB_PLACEHOLDER); // Postgres maps this to JSONB
put(DbType.JSONCLOB, JSON_CLOB_PLACEHOLDER);
put(DbType.JSONBLOB, JSON_BLOB_PLACEHOLDER);
put(DbType.JSONVARCHAR, JSON_VARCHAR_PLACEHOLDER);
put(DbType.UUID, UUID_PLACEHOLDER);
}
}
/**
* Lookup the platform specific DbType given the standard sql type name.
*/
public DbPlatformType lookup(String name, boolean withScale) {
DbType type = lookup.byName(name);
if (type == null) {
throw new IllegalArgumentException("Unknown type [" + name + "] - not standard sql type");
}
// handle JSON types mapped to clob, blob and varchar
switch (type) {
case JSONBLOB:
return get(DbType.BLOB);
case JSONCLOB:
return get(DbType.CLOB);
case JSONVARCHAR:
return get(DbType.VARCHAR);
case JSON:
return getJsonType(DbType.JSON, withScale);
case JSONB:
return getJsonType(DbType.JSONB, withScale);
default:
return get(type);
}
}
private DbPlatformType getJsonType(DbType type, boolean withScale) {
DbPlatformType dbType = get(type);
if (dbType == JSON_CLOB_PLACEHOLDER) {
// if we have scale that implies this maps to varchar
return withScale ? get(DbType.VARCHAR) : get(DbType.CLOB);
}
if (dbType == JSON_BLOB_PLACEHOLDER) {
return get(DbType.BLOB);
}
if (dbType == JSON_VARCHAR_PLACEHOLDER) {
return get(DbType.VARCHAR);
}
// Postgres has specific type
return get(type);
}
/**
* Override the type for a given JDBC type.
*/
private void put(DbType type) {
typeMap.put(type, type.createPlatformType());
}
/**
* Override the type for a given JDBC type.
*/
public void put(DbType type, DbPlatformType platformType) {
typeMap.put(type, platformType);
}
/**
* Return the type for a given jdbc type.
*/
public DbPlatformType get(int jdbcType) {
return get(lookup.byId(jdbcType));
}
/**
* Return the type for a given jdbc type.
*/
public DbPlatformType get(DbType dbType) {
return typeMap.get(dbType);
}
/**
* Map the UUID appropriately based on native DB support and ServerConfig.DbUuid.
*/
public void config(boolean nativeUuidType, ServerConfig.DbUuid dbUuid) {
if (nativeUuidType && dbUuid.useNativeType()) {
put(DbType.UUID, UUID_NATIVE);
} else if (dbUuid.useBinary()) {
put(DbType.UUID, get(DbType.BINARY).withLength(16));
} else {
put(DbType.UUID, get(DbType.VARCHAR).withLength(40));
}
}
}
@@ -0,0 +1,40 @@
package com.avaje.ebean.config.dbplatform;
/**
* Parse raw column definitions into DbPlatformType like "decimal(18,6)" and "varchar(20) and "json".
*/
class DbPlatformTypeParser {
/**
* Parse the column definition and return the DbPlatformType.
*/
static DbPlatformType parse(String columnDefinition) {
columnDefinition = columnDefinition.trim();
int openPos = columnDefinition.indexOf('(');
if (openPos == -1) {
return new DbPlatformType(columnDefinition, false);
}
int closePos = columnDefinition.indexOf(')', openPos);
if (closePos == -1) {
return new DbPlatformType(columnDefinition, false);
}
try {
int commaPos = columnDefinition.indexOf(',', openPos);
if (commaPos > -1) {
String type = columnDefinition.substring(0, openPos);
int scale = Integer.parseInt(columnDefinition.substring(openPos + 1, commaPos));
int precision = Integer.parseInt(columnDefinition.substring(commaPos + 1, closePos));
return new DbPlatformType(type, scale, precision);
} else {
String type = columnDefinition.substring(0, openPos);
int scale = Integer.parseInt(columnDefinition.substring(openPos + 1, closePos));
return new DbPlatformType(type, scale);
}
} catch (RuntimeException e) {
return new DbPlatformType(columnDefinition, false);
}
}
}
@@ -1,161 +1,72 @@
package com.avaje.ebean.config.dbplatform;
import java.sql.Types;
/**
* Represents a DB type with name, length, precision, and scale.
* The known DB types that are mapped.
* <p>
* The length is for VARCHAR types and precision/scale for DECIMAL types.
* This includes extra types such as UUID, JSON, JSONB and HSTORE.
* </p>
*/
public class DbType {
public enum DbType {
/**
* DB native UUID type (H2 and Postgres).
*/
public static final int UUID = 5010;
BOOLEAN(Types.BOOLEAN),
BIT(Types.BIT),
INTEGER(Types.INTEGER),
BIGINT(Types.BIGINT),
SMALLINT(Types.SMALLINT),
TINYINT(Types.TINYINT),
REAL(Types.REAL),
//FLOAT(Types.FLOAT),
DOUBLE(Types.DOUBLE),
DECIMAL(Types.DECIMAL),
VARCHAR(Types.VARCHAR),
CHAR(Types.CHAR),
BLOB(Types.BLOB),
CLOB(Types.CLOB),
LONGVARBINARY(Types.LONGVARBINARY),
LONGVARCHAR(Types.LONGVARCHAR),
VARBINARY(Types.VARBINARY),
BINARY(Types.BINARY),
DATE(Types.DATE),
TIME(Types.TIME),
TIMESTAMP(Types.TIMESTAMP),
/**
* Type to map Map content to Postgres HSTORE.
*/
public static final int HSTORE = 5000;
ARRAY(Types.ARRAY),
/**
* Type to map JSON content to Clob or Postgres JSON type.
*/
public static final int JSON = 5001;
UUID(ExtraDbTypes.UUID),
/**
* Type to map JSON content to Clob or Postgres JSONB type.
*/
public static final int JSONB = 5002;
POINT(ExtraDbTypes.POINT),
POLYGON(ExtraDbTypes.POLYGON),
LINESTRING(ExtraDbTypes.LINESTRING),
MULTIPOINT(ExtraDbTypes.MULTIPOINT),
MULTILINESTRING(ExtraDbTypes.MULTILINESTRING),
MULTIPOLYGON(ExtraDbTypes.MULTIPOLYGON),
/**
* Type to map JSON content to VARCHAR.
*/
public static final int JSONVarchar = 5003;
HSTORE(ExtraDbTypes.HSTORE),
JSON(ExtraDbTypes.JSON),
JSONB(ExtraDbTypes.JSONB),
JSONCLOB(ExtraDbTypes.JSONClob),
JSONBLOB(ExtraDbTypes.JSONBlob),
JSONVARCHAR(ExtraDbTypes.JSONVarchar);
/**
* Type to map JSON content to Clob.
*/
public static final int JSONClob = 5004;
private final int id;
/**
* Type to map JSON content to Blob.
*/
public static final int JSONBlob = 5005;
/**
* The data type name (VARCHAR, INTEGER ...)
*/
private final String name;
/**
* The default length or precision.
*/
private final int defaultLength;
/**
* The default scale (decimal).
*/
private final int defaultScale;
/**
* Set to true if the type should never have a length or scale.
*/
private final boolean canHaveLength;
/**
* Construct with no length or scale.
*/
public DbType(String name) {
this(name, 0, 0);
DbType(int id) {
this.id = id;
}
/**
* Construct with a given length.
* Return the JDBC java.sql.Types value.
*/
public DbType(String name, int defaultLength) {
this(name, defaultLength, 0);
public int id() {
return id;
}
/**
* Construct for Decimal with precision and scale.
* Create a platform type without scale or precision.
*/
public DbType(String name, int defaultPrecision, int defaultScale) {
this.name = name;
this.defaultLength = defaultPrecision;
this.defaultScale = defaultScale;
this.canHaveLength = true;
}
/**
* Use with canHaveLength=false for types that should never have a length.
*
* @param name
* the type name
* @param canHaveLength
* set this to false for type that should never have a length
*/
public DbType(String name, boolean canHaveLength) {
this.name = name;
this.defaultLength = 0;
this.defaultScale = 0;
this.canHaveLength = canHaveLength;
}
/**
* Return the type for a specific property that incorporates the name, length,
* precision and scale.
* <p>
* The deployLength and deployScale are for the property we are rendering the
* DB type for.
* </p>
*
* @param deployLength
* the length or precision defined by deployment on a specific
* property.
* @param deployScale
* the scale defined by deployment on a specific property.
*/
public String renderType(int deployLength, int deployScale) {
return renderType(deployLength, deployScale, true);
}
/**
* Render the type defining strict mode.
* <p>
* If strict mode if OFF then this will render with a scale value even if
* that is not strictly supported. The reason for supporting this is to enable
* use to use types like jsonb(200) as a "logical" type that maps to JSONB for
* Postgres and VARCHAR(200) for other databases.
* </p>
*/
public String renderType(int deployLength, int deployScale, boolean strict) {
StringBuilder sb = new StringBuilder();
sb.append(name);
if (canHaveLength || !strict) {
// see if there is a precision/scale to add (or not)
int len = deployLength != 0 ? deployLength : defaultLength;
if (len > 0) {
sb.append("(");
sb.append(len);
int scale = deployScale != 0 ? deployScale : defaultScale;
if (scale > 0) {
sb.append(",");
sb.append(scale);
}
sb.append(")");
}
}
return sb.toString();
}
/**
* Create a copy of the type with a new default length.
*/
public DbType withLength(int defaultLength) {
return new DbType(name, defaultLength);
public DbPlatformType createPlatformType() {
return new DbPlatformType(name().toLowerCase());
}
}
@@ -1,210 +0,0 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.config.ServerConfig;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;
/**
* Used to map bean property types to DB specific types for DDL generation.
*/
public class DbTypeMap {
private static final DbType UUID_NATIVE = new DbType("uuid", false);
private static final DbType UUID_PLACEHOLDER = new DbType("uuidPlaceholder");
private static final DbType JSON_CLOB_PLACEHOLDER = new DbType("jsonClobPlaceholder");
private static final DbType JSON_BLOB_PLACEHOLDER = new DbType("jsonBlobPlaceholder");
private static final DbType JSON_VARCHAR_PLACEHOLDER = new DbType("jsonVarcharPlaceholder");
/**
* A map to reverse lookup the type by name.
* <p>
* Used when converting from logical types to platform types which we
* want to do with 2 phase DDL generation.
*/
static Map<String, Integer> lookup = new HashMap<String, Integer>();
static {
lookup.put("BOOLEAN", Types.BOOLEAN);
lookup.put("BIT", Types.BIT);
lookup.put("INTEGER", Types.INTEGER);
lookup.put("BIGINT", Types.BIGINT);
lookup.put("REAL", Types.REAL);
// Float is most common REAL mapping to have that as well
lookup.put("FLOAT", Types.REAL);
lookup.put("DOUBLE", Types.DOUBLE);
lookup.put("SMALLINT", Types.SMALLINT);
lookup.put("TINYINT", Types.TINYINT);
lookup.put("DECIMAL", Types.DECIMAL);
lookup.put("VARCHAR", Types.VARCHAR);
// VARCHAR2 - extra for Oracle specific column definition
lookup.put("VARCHAR2", Types.VARCHAR);
lookup.put("CHAR", Types.CHAR);
lookup.put("BLOB", Types.BLOB);
lookup.put("CLOB", Types.CLOB);
lookup.put("LONGVARBINARY", Types.LONGVARBINARY);
lookup.put("LONGVARCHAR", Types.LONGVARCHAR);
lookup.put("VARBINARY", Types.VARBINARY);
lookup.put("BINARY", Types.BINARY);
lookup.put("DATE", Types.DATE);
lookup.put("TIME", Types.TIME);
lookup.put("TIMESTAMP", Types.TIMESTAMP);
lookup.put("ARRAY", Types.ARRAY);
lookup.put("UUID", DbType.UUID);
// Not standard java.sql.Types
// logical JSON storage types
lookup.put("JSON", DbType.JSON);
lookup.put("JSONB", DbType.JSONB);
lookup.put("JSONCLOB", DbType.JSONClob);
lookup.put("JSONBLOB", DbType.JSONBlob);
lookup.put("JSONVARCHAR", DbType.JSONVarchar);
}
private final Map<Integer, DbType> typeMap = new HashMap<Integer, DbType>();
/**
* Return the DbTypeMap with standard (not platform specific) types.
* <p>
* This has some extended JSON types (JSON, JSONB, JSONVarchar, JSONClob, JSONBlob).
* These types get translated to specific database platform types during DDL generation.
*/
public static DbTypeMap logicalTypes() {
return new DbTypeMap(true);
}
public DbTypeMap() {
loadDefaults(false);
}
private DbTypeMap(boolean logicalTypes) {
loadDefaults(logicalTypes);
}
/**
* Load the standard types. These can be overridden by DB specific platform.
*/
private void loadDefaults(boolean logicalTypes) {
put(Types.BOOLEAN, new DbType("boolean"));
put(Types.BIT, new DbType("bit"));
put(Types.INTEGER, new DbType("integer"));
put(Types.BIGINT, new DbType("bigint"));
put(Types.REAL, new DbType("float"));
put(Types.DOUBLE, new DbType("double"));
put(Types.SMALLINT, new DbType("smallint"));
put(Types.TINYINT, new DbType("tinyint"));
put(Types.DECIMAL, new DbType("decimal", 38));
put(Types.VARCHAR, new DbType("varchar", 255));
put(Types.CHAR, new DbType("char", 1));
put(Types.BLOB, new DbType("blob"));
put(Types.CLOB, new DbType("clob"));
put(Types.ARRAY, new DbType("array"));
if (logicalTypes) {
// keep it logical for 2 layer DDL generation
put(DbType.HSTORE, new DbType("hstore", false));
put(DbType.JSON, new DbType("json", false));
put(DbType.JSONB, new DbType("jsonb", false));
put(DbType.JSONClob, new DbType("jsonclob"));
put(DbType.JSONBlob, new DbType("jsonblob"));
put(DbType.JSONVarchar, new DbType("jsonvarchar", 1000));
put(DbType.UUID, UUID_NATIVE);
} else {
put(DbType.JSON, JSON_CLOB_PLACEHOLDER); // Postgres maps this to JSON
put(DbType.JSONB, JSON_CLOB_PLACEHOLDER); // Postgres maps this to JSONB
put(DbType.JSONClob, JSON_CLOB_PLACEHOLDER);
put(DbType.JSONBlob, JSON_BLOB_PLACEHOLDER);
put(DbType.JSONVarchar, JSON_VARCHAR_PLACEHOLDER);
put(DbType.UUID, UUID_PLACEHOLDER);
}
put(Types.LONGVARBINARY, new DbType("longvarbinary"));
put(Types.LONGVARCHAR, new DbType("lonvarchar"));
put(Types.VARBINARY, new DbType("varbinary", 255));
put(Types.BINARY, new DbType("binary", 255));
put(Types.DATE, new DbType("date"));
put(Types.TIME, new DbType("time"));
put(Types.TIMESTAMP, new DbType("timestamp"));
}
/**
* Lookup the platform specific DbType given the standard sql type name.
*/
public DbType lookup(String name, boolean withScale) {
name = name.trim().toUpperCase();
Integer typeKey = lookup.get(name);
if (typeKey == null) {
throw new IllegalArgumentException("Unknown type [" + name + "] - not standard sql type");
}
// handle JSON types mapped to clob, blob and varchar
switch (typeKey) {
case DbType.JSONBlob:
return get(Types.BLOB);
case DbType.JSONClob:
return get(Types.CLOB);
case DbType.JSONVarchar:
return get(Types.VARCHAR);
case DbType.JSON:
return getJsonType(DbType.JSON, withScale);
case DbType.JSONB:
return getJsonType(DbType.JSONB, withScale);
default:
return get(typeKey);
}
}
private DbType getJsonType(int type, boolean withScale) {
DbType dbType = get(type);
if (dbType == JSON_CLOB_PLACEHOLDER) {
// if we have scale that implies this maps to varchar
return withScale ? get(Types.VARCHAR) : get(Types.CLOB);
}
if (dbType == JSON_BLOB_PLACEHOLDER) {
return get(Types.BLOB);
}
if (dbType == JSON_VARCHAR_PLACEHOLDER) {
return get(Types.VARCHAR);
}
// Postgres has specific type
return get(type);
}
/**
* Override the type for a given JDBC type.
*/
public void put(int jdbcType, DbType dbType) {
typeMap.put(jdbcType, dbType);
}
/**
* Return the type for a given jdbc type.
*/
public DbType get(int jdbcType) {
return typeMap.get(jdbcType);
}
/**
* Map the UUID appropriately based on native DB support and ServerConfig.DbUuid.
*/
public void config(boolean nativeUuidType, ServerConfig.DbUuid dbUuid) {
if (nativeUuidType && dbUuid.useNativeType()) {
put(DbType.UUID, UUID_NATIVE);
} else if (dbUuid.useBinary()) {
put(DbType.UUID, get(Types.BINARY).withLength(16));
} else {
put(DbType.UUID, get(Types.VARCHAR).withLength(40));
}
}
}
@@ -0,0 +1,73 @@
package com.avaje.ebean.config.dbplatform;
/**
* Integer codes for the extra types beyond java.sql.Types.
*/
public interface ExtraDbTypes {
/**
* DB native UUID type (H2 and Postgres).
*/
int UUID = 5010;
/**
* Type to map Map content to Postgres HSTORE.
*/
int HSTORE = 5000;
/**
* Type to map JSON content to Clob or Postgres JSON type.
*/
int JSON = 5001;
/**
* Type to map JSON content to Clob or Postgres JSONB type.
*/
int JSONB = 5002;
/**
* Type to map JSON content to VARCHAR.
*/
int JSONVarchar = 5003;
/**
* Type to map JSON content to Clob.
*/
int JSONClob = 5004;
/**
* Type to map JSON content to Blob.
*/
int JSONBlob = 5005;
/**
* Geo Point
*/
int POINT = 6000;
/**
* Geo Polygon
*/
int POLYGON = 6001;
/**
* Geo Point
*/
int LINESTRING = 6002;
/**
* Geo MultiPolygon
*/
int MULTIPOINT = 6005;
/**
* Geo MultiPolygon
*/
int MULTIPOLYGON = 6006;
/**
* Geo MultiPolygon
*/
int MULTILINESTRING = 6007;
}
@@ -4,7 +4,6 @@ import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.HsqldbDdl;
import javax.sql.DataSource;
import java.sql.Types;
/**
* H2 specific platform.
@@ -22,7 +21,7 @@ public class HsqldbPlatform extends DatabasePlatform {
this.dbIdentity.setSupportsSequence(true);
this.dbIdentity.setSupportsIdentity(true);
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
}
@Override
@@ -2,8 +2,6 @@ package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.config.PersistBatch;
import java.sql.Types;
/**
* Microsoft SQL Server 2000 specific platform.
* <p>
@@ -29,22 +27,22 @@ public class MsSqlServer2000Platform extends DatabasePlatform {
this.openQuote = "[";
this.closeQuote = "]";
dbTypeMap.put(Types.BOOLEAN, new DbType("bit default 0"));
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("bit default 0"));
dbTypeMap.put(Types.BIGINT, new DbType("numeric", 19));
dbTypeMap.put(Types.REAL, new DbType("float(16)"));
dbTypeMap.put(Types.DOUBLE, new DbType("float(32)"));
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
dbTypeMap.put(Types.DECIMAL, new DbType("numeric", 28));
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("numeric", 19));
dbTypeMap.put(DbType.REAL, new DbPlatformType("float(16)"));
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("float(32)"));
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("numeric", 28));
dbTypeMap.put(Types.BLOB, new DbType("image"));
dbTypeMap.put(Types.CLOB, new DbType("text"));
dbTypeMap.put(Types.LONGVARBINARY, new DbType("image"));
dbTypeMap.put(Types.LONGVARCHAR, new DbType("text"));
dbTypeMap.put(DbType.BLOB, new DbPlatformType("image"));
dbTypeMap.put(DbType.CLOB, new DbPlatformType("text"));
dbTypeMap.put(DbType.LONGVARBINARY, new DbPlatformType("image"));
dbTypeMap.put(DbType.LONGVARCHAR, new DbPlatformType("text"));
dbTypeMap.put(Types.DATE, new DbType("datetime"));
dbTypeMap.put(Types.TIME, new DbType("datetime"));
dbTypeMap.put(Types.TIMESTAMP, new DbType("datetime"));
dbTypeMap.put(DbType.DATE, new DbPlatformType("datetime"));
dbTypeMap.put(DbType.TIME, new DbPlatformType("datetime"));
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime"));
}
@@ -3,8 +3,6 @@ package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.config.PersistBatch;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.MsSqlServerDdl;
import java.sql.Types;
/**
* Microsoft SQL Server 2005 specific platform.
* <p>
@@ -34,23 +32,23 @@ public class MsSqlServer2005Platform extends DatabasePlatform {
this.openQuote = "[";
this.closeQuote = "]";
dbTypeMap.put(Types.BOOLEAN, new DbType("bit default 0"));
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("bit default 0"));
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
dbTypeMap.put(Types.BIGINT, new DbType("numeric", 19));
dbTypeMap.put(Types.REAL, new DbType("float(16)"));
dbTypeMap.put(Types.DOUBLE, new DbType("float(32)"));
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
dbTypeMap.put(Types.DECIMAL, new DbType("numeric", 28));
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("numeric", 19));
dbTypeMap.put(DbType.REAL, new DbPlatformType("float(16)"));
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("float(32)"));
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("numeric", 28));
dbTypeMap.put(Types.BLOB, new DbType("image"));
dbTypeMap.put(Types.CLOB, new DbType("text"));
dbTypeMap.put(Types.LONGVARBINARY, new DbType("image"));
dbTypeMap.put(Types.LONGVARCHAR, new DbType("text"));
dbTypeMap.put(DbType.BLOB, new DbPlatformType("image"));
dbTypeMap.put(DbType.CLOB, new DbPlatformType("text"));
dbTypeMap.put(DbType.LONGVARBINARY, new DbPlatformType("image"));
dbTypeMap.put(DbType.LONGVARCHAR, new DbPlatformType("text"));
dbTypeMap.put(Types.DATE, new DbType("date"));
dbTypeMap.put(Types.TIME, new DbType("time"));
dbTypeMap.put(Types.TIMESTAMP, new DbType("datetime2"));
dbTypeMap.put(DbType.DATE, new DbPlatformType("date"));
dbTypeMap.put(DbType.TIME, new DbPlatformType("time"));
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime2"));
}
@@ -7,7 +7,7 @@ package com.avaje.ebean.config.dbplatform;
* If no deployment length is defined longblob is used.
* </p>
*/
public class MySqlBlob extends DbType {
public class MySqlBlob extends DbPlatformType {
private static final int POWER_2_16 = 65536;
private static final int POWER_2_24 = 16777216;
@@ -7,7 +7,7 @@ package com.avaje.ebean.config.dbplatform;
* If no deployment length is defined longtext is used.
* </p>
*/
public class MySqlClob extends DbType {
public class MySqlClob extends DbPlatformType {
private static final int POWER_2_16 = 65536;
private static final int POWER_2_24 = 16777216;
@@ -39,13 +39,13 @@ public class MySqlPlatform extends DatabasePlatform {
this.forwardOnlyHintOnFindIterate = true;
this.booleanDbType = Types.BIT;
dbTypeMap.put(Types.BIT, new DbType("tinyint(1) default 0"));
dbTypeMap.put(Types.BOOLEAN, new DbType("tinyint(1) default 0"));
dbTypeMap.put(Types.TIMESTAMP, new DbType("datetime(6)"));
dbTypeMap.put(Types.CLOB, new MySqlClob());
dbTypeMap.put(Types.BLOB, new MySqlBlob());
dbTypeMap.put(Types.BINARY, new DbType("binary", 255));
dbTypeMap.put(Types.VARBINARY, new DbType("varbinary", 255));
dbTypeMap.put(DbType.BIT, new DbPlatformType("tinyint(1) default 0"));
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("tinyint(1) default 0"));
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime(6)"));
dbTypeMap.put(DbType.CLOB, new MySqlClob());
dbTypeMap.put(DbType.BLOB, new MySqlBlob());
dbTypeMap.put(DbType.BINARY, new DbPlatformType("binary", 255));
dbTypeMap.put(DbType.VARBINARY, new DbPlatformType("varbinary", 255));
}
/**
@@ -34,24 +34,24 @@ public class OraclePlatform extends DatabasePlatform {
this.closeQuote = "\"";
booleanDbType = Types.INTEGER;
dbTypeMap.put(Types.BOOLEAN, new DbType("number(1) default 0"));
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("number(1) default 0"));
dbTypeMap.put(Types.INTEGER, new DbType("number", 10));
dbTypeMap.put(Types.BIGINT, new DbType("number", 19));
dbTypeMap.put(Types.REAL, new DbType("number", 19, 4));
dbTypeMap.put(Types.DOUBLE, new DbType("number", 19, 4));
dbTypeMap.put(Types.SMALLINT, new DbType("number", 5));
dbTypeMap.put(Types.TINYINT, new DbType("number", 3));
dbTypeMap.put(Types.DECIMAL, new DbType("number", 38));
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("number", 10));
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("number", 19));
dbTypeMap.put(DbType.REAL, new DbPlatformType("number", 19, 4));
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("number", 19, 4));
dbTypeMap.put(DbType.SMALLINT, new DbPlatformType("number", 5));
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("number", 3));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("number", 38));
dbTypeMap.put(Types.VARCHAR, new DbType("varchar2", 255));
dbTypeMap.put(DbType.VARCHAR, new DbPlatformType("varchar2", 255));
dbTypeMap.put(Types.LONGVARBINARY, new DbType("blob"));
dbTypeMap.put(Types.LONGVARCHAR, new DbType("clob"));
dbTypeMap.put(Types.VARBINARY, new DbType("raw", 255));
dbTypeMap.put(Types.BINARY, new DbType("raw", 255));
dbTypeMap.put(DbType.LONGVARBINARY, new DbPlatformType("blob"));
dbTypeMap.put(DbType.LONGVARCHAR, new DbPlatformType("clob"));
dbTypeMap.put(DbType.VARBINARY, new DbPlatformType("raw", 255));
dbTypeMap.put(DbType.BINARY, new DbPlatformType("raw", 255));
dbTypeMap.put(Types.TIME, new DbType("timestamp"));
dbTypeMap.put(DbType.TIME, new DbPlatformType("timestamp"));
}
@Override
@@ -41,26 +41,26 @@ public class PostgresPlatform extends DatabasePlatform {
this.openQuote = "\"";
this.closeQuote = "\"";
DbType dbTypeText = new DbType("text");
DbType dbBytea = new DbType("bytea", false);
DbPlatformType dbTypeText = new DbPlatformType("text");
DbPlatformType dbBytea = new DbPlatformType("bytea", false);
dbTypeMap.put(DbType.HSTORE, new DbType("hstore", false));
dbTypeMap.put(DbType.JSON, new DbType("json", false));
dbTypeMap.put(DbType.JSONB, new DbType("jsonb", false));
dbTypeMap.put(DbType.HSTORE, new DbPlatformType("hstore", false));
dbTypeMap.put(DbType.JSON, new DbPlatformType("json", false));
dbTypeMap.put(DbType.JSONB, new DbPlatformType("jsonb", false));
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
dbTypeMap.put(Types.DECIMAL, new DbType("decimal", 38));
dbTypeMap.put(Types.TIMESTAMP, new DbType("timestamptz"));
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("float"));
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("decimal", 38));
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("timestamptz"));
dbTypeMap.put(Types.BINARY, dbBytea);
dbTypeMap.put(Types.VARBINARY, dbBytea);
dbTypeMap.put(DbType.BINARY, dbBytea);
dbTypeMap.put(DbType.VARBINARY, dbBytea);
dbTypeMap.put(Types.BLOB, dbBytea);
dbTypeMap.put(Types.CLOB, dbTypeText);
dbTypeMap.put(Types.LONGVARBINARY, dbBytea);
dbTypeMap.put(Types.LONGVARCHAR, dbTypeText);
dbTypeMap.put(DbType.BLOB, dbBytea);
dbTypeMap.put(DbType.CLOB, dbTypeText);
dbTypeMap.put(DbType.LONGVARBINARY, dbBytea);
dbTypeMap.put(DbType.LONGVARCHAR, dbTypeText);
}
@Override
@@ -71,9 +71,23 @@ public class PostgresPlatform extends DatabasePlatform {
String tsType = properties.getProperty("ebean.postgres.timestamp");
if (tsType != null) {
// set timestamp type to "timestamp" without time zone
dbTypeMap.put(Types.TIMESTAMP, new DbType(tsType));
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType(tsType));
}
}
addGeoTypes(serverConfig.getGeometrySRID());
}
private void addGeoTypes(int srid) {
dbTypeMap.put(DbType.POINT, geoType("point",srid));
dbTypeMap.put(DbType.POLYGON, geoType("polygon",srid));
dbTypeMap.put(DbType.LINESTRING, geoType("linestring",srid));
dbTypeMap.put(DbType.MULTIPOINT, geoType("multipoint",srid));
dbTypeMap.put(DbType.MULTILINESTRING, geoType("multilinestring",srid));
dbTypeMap.put(DbType.MULTIPOLYGON, geoType("multipolygon",srid));
}
private DbPlatformType geoType(String type, int srid) {
return new DbPlatformType("geometry("+type+","+srid+")");
}
/**
@@ -18,10 +18,10 @@ public class SQLitePlatform extends DatabasePlatform {
this.booleanDbType = Types.INTEGER;
dbTypeMap.put(Types.BIT, new DbType("int default 0"));
dbTypeMap.put(Types.BOOLEAN, new DbType("int default 0"));
dbTypeMap.put(Types.BIGINT, new DbType("integer"));
dbTypeMap.put(Types.SMALLINT, new DbType("integer"));
dbTypeMap.put(DbType.BIT, new DbPlatformType("int default 0"));
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("int default 0"));
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("integer"));
dbTypeMap.put(DbType.SMALLINT, new DbPlatformType("integer"));
}
@@ -1,7 +1,5 @@
package com.avaje.ebean.config.dbplatform;
import java.sql.Types;
/**
* Sybase SQL Anywhere specific platform.
* <p>
@@ -23,17 +21,17 @@ public class SqlAnywherePlatform extends DatabasePlatform {
this.dbIdentity.setSelectLastInsertedIdTemplate("select @@IDENTITY as X");
this.dbIdentity.setSupportsIdentity(true);
dbTypeMap.put(Types.BOOLEAN, new DbType("bit default 0"));
dbTypeMap.put(Types.BIGINT, new DbType("numeric", 19));
dbTypeMap.put(Types.REAL, new DbType("float(16)"));
dbTypeMap.put(Types.DOUBLE, new DbType("float(32)"));
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
dbTypeMap.put(Types.DECIMAL, new DbType("numeric", 28));
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("bit default 0"));
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("numeric", 19));
dbTypeMap.put(DbType.REAL, new DbPlatformType("float(16)"));
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("float(32)"));
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("numeric", 28));
dbTypeMap.put(Types.BLOB, new DbType("binary(4500)"));
dbTypeMap.put(Types.CLOB, new DbType("long varchar"));
dbTypeMap.put(Types.LONGVARBINARY, new DbType("long binary"));
dbTypeMap.put(Types.LONGVARCHAR, new DbType("long varchar"));
dbTypeMap.put(DbType.BLOB, new DbPlatformType("binary(4500)"));
dbTypeMap.put(DbType.CLOB, new DbPlatformType("long varchar"));
dbTypeMap.put(DbType.LONGVARBINARY, new DbPlatformType("long binary"));
dbTypeMap.put(DbType.LONGVARCHAR, new DbPlatformType("long varchar"));
}
@@ -7,7 +7,7 @@ import com.avaje.ebean.config.DbMigrationConfig;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DB2Platform;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.DbPlatformName;
import com.avaje.ebean.config.Platform;
import com.avaje.ebean.config.dbplatform.H2Platform;
import com.avaje.ebean.config.dbplatform.MsSqlServer2005Platform;
import com.avaje.ebean.config.dbplatform.MySqlPlatform;
@@ -139,7 +139,7 @@ public class DbMigration {
* If not set this defaults to the platform of the default server.
* </p>
*/
public void setPlatform(DbPlatformName platform) {
public void setPlatform(Platform platform) {
setPlatform(getPlatform(platform));
}
@@ -163,7 +163,7 @@ public class DbMigration {
* from the migration (e.g. generate migration sql for MySql, Postgres and Oracle).
* </p>
*/
public void addPlatform(DbPlatformName platform, String prefix) {
public void addPlatform(Platform platform, String prefix) {
platforms.add(new Pair(getPlatform(platform), prefix));
}
@@ -515,7 +515,7 @@ public class DbMigration {
/**
* Return the DatabasePlatform given the platform key.
*/
protected DatabasePlatform getPlatform(DbPlatformName platform) {
protected DatabasePlatform getPlatform(Platform platform) {
switch (platform) {
case H2:
return new H2Platform();
@@ -1,6 +1,6 @@
package com.avaje.ebean.dbmigration;
import com.avaje.ebean.config.dbplatform.DbPlatformName;
import com.avaje.ebean.config.Platform;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -19,7 +19,7 @@ public class DbOffline {
/**
* Set the platform to use when creating the next EbeanServer instance.
*/
public static void setPlatform(DbPlatformName dbPlatform) {
public static void setPlatform(Platform dbPlatform) {
System.setProperty(KEY, dbPlatform.name());
}
@@ -41,7 +41,7 @@ public class DbOffline {
* Bring up the next EbeanServer instance using the H2 platform.
*/
public static void asH2() {
setPlatform(DbPlatformName.H2);
setPlatform(Platform.H2);
}
/**
@@ -11,6 +11,8 @@ public class Oracle10Ddl extends PlatformDdl {
super(platform);
this.dropTableIfExists = "drop table ";
this.dropSequenceIfExists = "drop sequence ";
this.dropConstraintIfExists = "drop constraint";
this.dropIndexIfExists = "drop index ";
this.dropTableCascade = " cascade constraints purge";
this.foreignKeyRestrict = "";
this.alterColumn = "modify";
@@ -110,7 +110,6 @@ public class PlatformDdl {
* Set configuration options.
*/
public void configure(ServerConfig serverConfig) {
platform.configure(serverConfig);
historyDdl.configure(serverConfig, this);
naming = serverConfig.getConstraintNaming();
}
@@ -1,7 +1,7 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform.util;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebean.config.dbplatform.DbPlatformType;
import com.avaje.ebean.config.dbplatform.DbPlatformTypeMapping;
/**
* Converts a logical column definition into platform specific one.
@@ -10,12 +10,12 @@ import com.avaje.ebean.config.dbplatform.DbTypeMap;
*/
public class PlatformTypeConverter {
protected final DbTypeMap platformTypes;
protected final DbPlatformTypeMapping platformTypes;
/**
* Construct with the platform specific types.
*/
public PlatformTypeConverter(DbTypeMap platformTypes) {
public PlatformTypeConverter(DbPlatformTypeMapping platformTypes) {
this.platformTypes = platformTypes;
}
@@ -47,7 +47,7 @@ public class PlatformTypeConverter {
String suffix = close + 1 < columnDefinition.length() ? columnDefinition.substring(close + 1) : "";
String type = columnDefinition.substring(0, open);
try {
DbType dbType = platformTypes.lookup(type, true);
DbPlatformType dbType = platformTypes.lookup(type, true);
int comma = columnDefinition.indexOf(',', open);
if (comma > -1) {
// scale and precision - decimal(10,4)
@@ -73,7 +73,7 @@ public class PlatformTypeConverter {
protected String convertNoScale(String columnDefinition) {
try {
DbType dbType = platformTypes.lookup(columnDefinition, false);
DbPlatformType dbType = platformTypes.lookup(columnDefinition, false);
return dbType.renderType(0, 0);
} catch (IllegalArgumentException e) {
@@ -1,6 +1,6 @@
package com.avaje.ebean.dbmigration.model.build;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebean.config.dbplatform.DbPlatformType;
import com.avaje.ebean.config.dbplatform.IdType;
import com.avaje.ebean.dbmigration.migration.IdentityType;
import com.avaje.ebean.dbmigration.model.MColumn;
@@ -52,7 +52,7 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
if (inheritInfo != null && inheritInfo.isRoot()) {
// add the discriminator column
String discColumn = inheritInfo.getDiscriminatorColumn();
DbType dbType = ctx.getDbTypeMap().get(inheritInfo.getDiscriminatorType());
DbPlatformType dbType = ctx.getDbTypeMap().get(inheritInfo.getDiscriminatorType());
String discDbType = dbType.renderType(inheritInfo.getDiscriminatorLength(), 0);
table.addColumn(new MColumn(discColumn, discDbType, true));
@@ -1,8 +1,8 @@
package com.avaje.ebean.dbmigration.model.build;
import com.avaje.ebean.config.DbConstraintNaming;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebean.config.dbplatform.DbPlatformType;
import com.avaje.ebean.config.dbplatform.DbPlatformTypeMapping;
import com.avaje.ebean.dbmigration.model.MColumn;
import com.avaje.ebean.dbmigration.model.MTable;
import com.avaje.ebean.dbmigration.model.ModelContainer;
@@ -21,7 +21,7 @@ public class ModelBuildContext {
* Use platform agnostic logical types. These types are converted to
* platform specific types in the DDL generation.
*/
private final DbTypeMap dbTypeMap = DbTypeMap.logicalTypes();
private final DbPlatformTypeMapping dbTypeMap = DbPlatformTypeMapping.logicalTypes();
private final ModelContainer model;
@@ -106,7 +106,7 @@ public class ModelBuildContext {
* Return the map used to determine the DB specific type
* for a given bean property.
*/
public DbTypeMap getDbTypeMap() {
public DbPlatformTypeMapping getDbTypeMap() {
return dbTypeMap;
}
@@ -115,14 +115,14 @@ public class ModelBuildContext {
* Render the DB type for this property given the strict mode.
*/
public String getColumnDefn(BeanProperty p, boolean strict) {
DbType dbType = getDbType(p);
DbPlatformType dbType = getDbType(p);
if (dbType == null) {
throw new IllegalStateException("Unknown DbType mapping for " + p.getFullBeanName());
}
return p.renderDbType(dbType, strict);
}
private DbType getDbType(BeanProperty p) {
private DbPlatformType getDbType(BeanProperty p) {
if (p.isDbEncrypted()) {
return dbTypeMap.get(p.getDbEncryptedType());
@@ -236,6 +236,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
col.setDefaultValue(p.getDbColumnDefault());
col.setComment(p.getDbComment());
col.setDraftOnly(p.isDraftOnly());
col.setHistoryExclude(p.isExcludedFromHistory());
if (p.isId()) {
col.setPrimaryKey(true);
@@ -66,7 +66,7 @@ public class VisitAllUsing {
BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient();
for (int i = 0; i < propertiesNonTransient.length; i++) {
BeanProperty p = propertiesNonTransient[i];
if (!p.isFormula() && !p.isSecondaryTable()) {
if (p.isDDLColumn()) {
visit(propertyVisitor, p);
}
}
@@ -13,8 +13,7 @@ import java.util.Set;
public abstract class AbstractBeanPersistListener implements BeanPersistListener {
/**
* Notified that a bean has been inserted locally. Return true if you want the
* cluster to be notified of the event.
* Notified that a bean has been inserted.
*
* @param bean The bean that was inserted.
*/
@@ -23,8 +22,7 @@ public abstract class AbstractBeanPersistListener implements BeanPersistListener
}
/**
* Notified that a bean has been updated locally. Return true if you want the
* cluster to be notified of the event.
* Notified that a bean has been updated.
*
* @param bean The bean that was updated.
* @param updatedProperties The properties that were modified by this update.
@@ -34,8 +32,7 @@ public abstract class AbstractBeanPersistListener implements BeanPersistListener
}
/**
* Notified that a bean has been deleted locally. Return true if you want the
* cluster to be notified of the event.
* Notified that a bean has been deleted.
*
* @param bean The bean that was deleted.
*/
@@ -43,4 +40,12 @@ public abstract class AbstractBeanPersistListener implements BeanPersistListener
public void deleted(Object bean) {
}
/**
* Notified that a bean has been soft deleted.
*
* @param bean The bean that was deleted.
*/
@Override
public void softDeleted(Object bean) {
}
}
@@ -46,6 +46,13 @@ public abstract class BeanPersistAdapter implements BeanPersistController {
return true;
}
/**
* Returns true indicating normal processing should continue.
*/
public boolean preSoftDelete(BeanPersistRequest<?> request) {
return true;
}
/**
* Does nothing by default.
*/
@@ -64,4 +71,10 @@ public abstract class BeanPersistAdapter implements BeanPersistController {
public void postUpdate(BeanPersistRequest<?> request) {
}
/**
* Does nothing by default.
*/
public void postSoftDelete(BeanPersistRequest<?> request) {
}
}
@@ -89,6 +89,12 @@ public interface BeanPersistController {
*/
boolean preDelete(BeanPersistRequest<?> request);
/**
* Prior to a soft delete perform some action. Return true if you want the
* default functionality to continue.
*/
boolean preSoftDelete(BeanPersistRequest<?> request);
/**
* Called after the insert was performed.
*/
@@ -104,4 +110,8 @@ public interface BeanPersistController {
*/
void postDelete(BeanPersistRequest<?> request);
/**
* Called after the soft delete was performed.
*/
void postSoftDelete(BeanPersistRequest<?> request);
}
@@ -67,4 +67,12 @@ public interface BeanPersistListener {
*/
void deleted(Object bean);
/**
* Notified that a bean has been soft deleted.
*
* @param bean
* The bean that was soft deleted.
*/
void softDeleted(Object bean);
}
@@ -0,0 +1,41 @@
package com.avaje.ebean.event;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
/**
* Fired after a bean is constructed, but not yet loaded from database.
* <p>
* Note: You MUST NOT set any default values, as in a following step,
* properties will get unload. Use {@link BeanPostLoad} instead.
*
* it's intended to do some dependency-injection here.
* If you plan to use this feature you should use {@link EbeanServer#createEntityBean(Class)}
* to create new beans.
* </p>
*/
public interface BeanPostConstructListener {
/**
* Return true if this BeanPostConstructListener instance should be registered
* for post construct on this entity type.
*/
boolean isRegisterFor(Class<?> cls);
/**
* Called immediately after construction. Perform DI here.
*/
void autowire(Object bean);
/**
* Called after every &#64;PostConstruct annotated method of the bean is executed
*/
void postConstruct(Object bean);
/**
* Called after {@link EbeanServer#createEntityBean(Class)}. Only for new beans.
* intended to set default values here.
*/
void postCreate(Object bean);
}
@@ -0,0 +1,17 @@
package com.avaje.ebean.plugin;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.server.type.ScalarType;
import java.util.List;
/**
* A factory that provides extra types to Ebean.
*/
public interface ExtraTypeFactory {
/**
* Provide extra types to Ebean.
*/
List<? extends ScalarType> createTypes(ServerConfig config, Object objectMapper);
}
@@ -1,27 +0,0 @@
package com.avaje.ebeaninternal.api;
public final class DerivedRelationshipData {
private final Object assocBean;
private final String logicalName;
private final Object bean;
public DerivedRelationshipData(Object assocBean, String logicalName, Object bean) {
this.assocBean = assocBean;
this.logicalName = logicalName;
this.bean = bean;
}
public Object getAssocBean() {
return assocBean;
}
public String getLogicalName() {
return logicalName;
}
public Object getBean() {
return bean;
}
}
@@ -169,8 +169,13 @@ public class LoadBeanRequest extends LoadRequest {
EntityBeanIntercept ebi = batch.get(i);
Object id = desc.getId(ebi.getOwner());
if (!loadedIds.contains(id)) {
logger.info("Lazy loading unsuccessful for type:" + desc.getName() + " id:" + id + " - expecting when bean has been deleted");
ebi.setLazyLoadFailure(id);
if (desc.isSoftDelete()) {
// assume this is logically deleted (hence not found)
desc.setSoftDeleteValue(ebi.getOwner());
} else {
logger.info("Lazy loading unsuccessful for type:" + desc.getName() + " id:" + id + " - expecting when bean has been deleted");
ebi.setLazyLoadFailure(id);
}
}
}
}
@@ -25,6 +25,8 @@ public class ManyWhereJoins implements Serializable {
private boolean formulaWithJoin;
private boolean aggregation;
/**
* 'Mode' indicating that joins added while this is true are required to be outer joins.
*/
@@ -93,10 +95,10 @@ public class ManyWhereJoins implements Serializable {
}
/**
* Return true if there are no extra many where joins.
* Return true if this is an aggregation query or if there are no extra many where joins.
*/
public boolean isEmpty() {
return joins.isEmpty();
public boolean requireSqlDistinct() {
return !aggregation && !joins.isEmpty();
}
/**
@@ -146,4 +148,18 @@ public class ManyWhereJoins implements Serializable {
return formulaProperties.toString();
}
/**
* Mark this as part of an aggregation query (so using group by clause).
*/
public void setAggregation() {
aggregation = true;
}
/**
* Ensure we have the join required to support the aggregation properties.
*/
public void addAggregationJoin(String property) {
this.aggregation = true;
joins.put(property, new PropertyJoin(property, SqlJoinType.INNER));
}
}
@@ -8,13 +8,13 @@ import com.avaje.ebean.event.changelog.BeanChange;
import com.avaje.ebean.event.changelog.ChangeSet;
import com.avaje.ebeaninternal.server.core.PersistRequest;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.core.PersistDeferredRelationship;
import com.avaje.ebeaninternal.server.persist.BatchControl;
import javax.persistence.PersistenceException;
import javax.persistence.RollbackException;
import java.io.IOException;
import java.sql.Connection;
import java.util.List;
/**
* Wrapper of a ScopeTrans request and it's underlying transaction.
@@ -145,13 +145,8 @@ public class ScopedTransaction implements SpiTransaction {
}
@Override
public void registerDerivedRelationship(DerivedRelationshipData assocBean) {
transaction.registerDerivedRelationship(assocBean);
}
@Override
public List<DerivedRelationshipData> getDerivedRelationship(Object bean) {
return transaction.getDerivedRelationship(bean);
public void registerDeferred(PersistDeferredRelationship derived) {
transaction.registerDeferred(derived);
}
@Override
@@ -7,10 +7,10 @@ import com.avaje.ebean.event.changelog.BeanChange;
import com.avaje.ebean.event.changelog.ChangeSet;
import com.avaje.ebeaninternal.server.core.PersistRequest;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.core.PersistDeferredRelationship;
import com.avaje.ebeaninternal.server.persist.BatchControl;
import java.sql.Connection;
import java.util.List;
/**
* Extends Transaction with additional API required on server.
@@ -48,15 +48,9 @@ public interface SpiTransaction extends Transaction {
void logSummary(String msg);
/**
* Register a "Derived Relationship" (that requires an additional update).
* Register a "Deferred Relationship" that requires an additional update later.
*/
void registerDerivedRelationship(DerivedRelationshipData assocBean);
/**
* Return the list of "Derived Relationships" that must be maintained after
* insert.
*/
List<DerivedRelationshipData> getDerivedRelationship(Object bean);
void registerDeferred(PersistDeferredRelationship derived);
/**
* Add a deleting bean to the registered list.
@@ -19,8 +19,7 @@ public class AutoTuneXmlReader {
/**
* Read and return a Profiling from an xml file.
*/
public Autotune read(File file) {
public static Autotune read(File file) {
try {
return readFile(file);
} catch (IOException e) {
@@ -28,7 +27,7 @@ public class AutoTuneXmlReader {
}
}
protected Autotune readFile(File file) throws IOException {
protected static Autotune readFile(File file) throws IOException {
if (!file.exists()) {
return new Autotune();
}
@@ -43,8 +42,7 @@ public class AutoTuneXmlReader {
/**
* Read and return a Profiling from an xml document.
*/
public Autotune read(InputStream is) {
public static Autotune read(InputStream is) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Autotune.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
@@ -12,6 +12,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;
/**
@@ -101,19 +102,26 @@ public class DefaultAutoTuneService implements AutoTuneService {
*/
private void loadTuningFile() {
File file = new File(tuningFile);
if (!file.exists()) {
logger.warn("AutoTune file {} not found - no initial automatic query tuning", file.getAbsolutePath());
if (file.exists()) {
loadAutoTuneProfiling(AutoTuneXmlReader.read(file));
} else {
AutoTuneXmlReader reader = new AutoTuneXmlReader();
Autotune profiling = reader.read(file);
logger.info("AutoTune loading {} tuning entries", profiling.getOrigin().size());
for (Origin origin : profiling.getOrigin()) {
queryTuner.put(origin);
// look for autotune as a resource
InputStream stream = getClass().getResourceAsStream("/" + tuningFile);
if (stream != null) {
loadAutoTuneProfiling(AutoTuneXmlReader.read(stream));
} else {
logger.warn("AutoTune file {} not found - no initial automatic query tuning", tuningFile);
}
}
}
private void loadAutoTuneProfiling(Autotune profiling) {
logger.info("AutoTune loading {} tuning entries", profiling.getOrigin().size());
for (Origin origin : profiling.getOrigin()) {
queryTuner.put(origin);
}
}
/**
* Collect profiling, check for new/diff to existing tuning and apply changes.
*/
@@ -5,6 +5,7 @@ import com.avaje.ebean.annotation.ChangeLogInsertMode;
import com.avaje.ebean.event.BeanPersistRequest;
import com.avaje.ebean.event.changelog.ChangeLogFilter;
import com.avaje.ebean.event.changelog.ChangeLogRegister;
import com.avaje.ebeaninternal.server.deploy.parse.AnnotationBase;
import java.util.HashSet;
import java.util.Set;
@@ -52,14 +53,7 @@ public class DefaultChangeLogRegister implements ChangeLogRegister {
* Find and return the ChangeLog annotation in the inheritance hierarchy.
*/
private ChangeLog getChangeLog(Class<?> beanType) {
ChangeLog changeLog = beanType.getAnnotation(ChangeLog.class);
if (changeLog != null) {
return changeLog;
}
if (Object.class.equals(beanType.getSuperclass())) {
return null;
}
return getChangeLog(beanType.getSuperclass());
return AnnotationBase.findAnnotation(beanType, ChangeLog.class);
}
/**
@@ -150,6 +150,9 @@ public final class BasicTypeConverter implements Serializable {
if (value instanceof Boolean) {
return (Boolean) value;
}
if (value instanceof Number) {
return ((Number)value).intValue() == 1;
}
String s = value.toString();
return s.equalsIgnoreCase(dbTrueValue);
}
@@ -194,6 +194,7 @@ public class DefaultContainer implements SpiContainer {
bootup.addIdGenerators(serverConfig.getIdGenerators());
bootup.addPersistControllers(serverConfig.getPersistControllers());
bootup.addPostLoaders(serverConfig.getPostLoaders());
bootup.addPostConstructListeners(serverConfig.getPostConstructListeners());
bootup.addFindControllers(serverConfig.getFindControllers());
bootup.addPersistListeners(serverConfig.getPersistListeners());
bootup.addQueryAdapters(serverConfig.getQueryAdapters());
@@ -523,7 +523,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@SuppressWarnings("unchecked")
public <T> T createEntityBean(Class<T> type) {
BeanDescriptor<T> desc = getBeanDescriptor(type);
return (T) desc.createEntityBean();
return (T) desc.createEntityBean(true);
}
/**
@@ -0,0 +1,53 @@
package com.avaje.ebeaninternal.server.core;
import com.avaje.ebean.SqlUpdate;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
/**
* Deferred update of a relationship where an Id value is not initially available
* so instead we execute this later as a SqlUpdate statement.
*/
public class PersistDeferredRelationship {
private final SpiEbeanServer ebeanServer;
private final BeanDescriptor<?> beanDescriptor;
private final EntityBean assocBean;
private final ImportedId importedId;
private final EntityBean bean;
public PersistDeferredRelationship(SpiEbeanServer ebeanServer, BeanDescriptor<?> beanDescriptor, EntityBean assocBean, ImportedId importedId, EntityBean bean) {
this.ebeanServer = ebeanServer;
this.beanDescriptor = beanDescriptor;
this.assocBean = assocBean;
this.importedId = importedId;
this.bean = bean;
}
/**
* Build and execute a SqlUpdate to set the importId value (as it will be available now).
* <p>
* This is executed later (deferred) until after JDBC batch flush or prior to commit.
* </p>
*/
public void execute(SpiTransaction transaction) {
String sql = beanDescriptor.getUpdateImportedIdSql(importedId);
SqlUpdate sqlUpdate = ebeanServer.createSqlUpdate(sql);
// bind the set clause for the importedId
int pos = importedId.bind(1, sqlUpdate, assocBean);
// bind the where clause for the bean
Object[] idValues = beanDescriptor.getIdBinder().getIdValues(bean);
for (int j = 0; j < idValues.length; j++) {
sqlUpdate.setParameter(pos + j, idValues[j]);
}
ebeanServer.execute(sqlUpdate, transaction);
}
}
@@ -1,16 +1,15 @@
package com.avaje.ebeaninternal.server.core;
import com.avaje.ebean.ValuePair;
import com.avaje.ebean.bean.PreGetterCallback;
import com.avaje.ebeaninternal.api.ConcurrencyMode;
import com.avaje.ebean.annotation.DocStoreMode;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.PreGetterCallback;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistListener;
import com.avaje.ebean.event.BeanPersistRequest;
import com.avaje.ebean.event.changelog.BeanChange;
import com.avaje.ebeaninternal.api.DerivedRelationshipData;
import com.avaje.ebeaninternal.api.ConcurrencyMode;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.api.TransactionEvent;
@@ -19,12 +18,13 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanManager;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
import com.avaje.ebeaninternal.server.persist.BatchControl;
import com.avaje.ebeaninternal.server.persist.PersistExecute;
import com.avaje.ebeaninternal.server.transaction.BeanPersistIdMap;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdate;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateContext;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdates;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdate;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
@@ -180,7 +180,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
/**
* Return the document store event that should be used for this request.
*
* <p>
* Used to check if the Transaction has set the mode to IGNORE when doing large batch inserts that we
* don't want to send to the doc store.
*/
@@ -373,6 +373,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
beanDescriptor.docStoreInsert(idValue, this, txn);
break;
case UPDATE:
case SOFT_DELETE:
beanDescriptor.docStoreUpdate(idValue, this, txn);
break;
case DELETE:
@@ -392,6 +393,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
docStoreUpdates.queueIndex(beanDescriptor.getDocStoreQueueId(), idValue);
break;
case UPDATE:
case SOFT_DELETE:
docStoreUpdates.queueIndex(beanDescriptor.getDocStoreQueueId(), idValue);
break;
case DELETE:
@@ -422,6 +424,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
beanPersistListener.deleted(bean);
break;
case SOFT_DELETE:
beanPersistListener.softDeleted(bean);
break;
default:
}
}
@@ -641,7 +647,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
switch (type) {
case INSERT:
persistExecute.executeInsertBean(this);
executeInsert();
return -1;
case UPDATE:
@@ -649,16 +655,16 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
// store the updated properties for sending later
updatedProperties = getUpdatedProperties();
}
persistExecute.executeUpdateBean(this);
executeUpdate();
return -1;
case SOFT_DELETE:
prepareForSoftDelete();
persistExecute.executeUpdateBean(this);
executeSoftDelete();
return -1;
case DELETE:
return persistExecute.executeDeleteBean(this);
return executeDelete();
default:
throw new RuntimeException("Invalid type " + type);
@@ -789,8 +795,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
case UPDATE:
controller.postUpdate(this);
break;
case DELETE:
case SOFT_DELETE:
controller.postSoftDelete(this);
break;
case DELETE:
controller.postDelete(this);
break;
default:
@@ -874,8 +882,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
}
public List<DerivedRelationshipData> getDerivedRelationships() {
return transaction.getDerivedRelationship(bean);
/**
* Register the derived relationships to get executed later (on JDBC batch flush or commit).
*/
public void deferredRelationship(EntityBean assocBean, ImportedId importedId, EntityBean bean) {
transaction.registerDeferred(new PersistDeferredRelationship(ebeanServer, beanDescriptor, assocBean, importedId, bean));
}
private void postInsert() {
@@ -1040,4 +1051,32 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
public long getVersion() {
return version;
}
public void executeInsert() {
if (controller == null || controller.preInsert(this)) {
beanManager.getBeanPersister().insert(this);
}
}
public void executeUpdate() {
if (controller == null || controller.preUpdate(this)) {
postControllerPrepareUpdate();
beanManager.getBeanPersister().update(this);
}
}
public void executeSoftDelete() {
if (controller == null || controller.preSoftDelete(this)) {
postControllerPrepareUpdate();
beanManager.getBeanPersister().update(this);
}
}
public int executeDelete() {
if (controller == null || controller.preDelete(this)) {
return beanManager.getBeanPersister().delete(this);
}
// delete handled by the BeanController so return 0
return 0;
}
}
@@ -7,6 +7,7 @@ import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.event.BeanFindController;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistListener;
import com.avaje.ebean.event.BeanPostConstructListener;
import com.avaje.ebean.event.BeanPostLoad;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.event.ServerConfigStartup;
@@ -41,38 +42,48 @@ public class BootupClasses implements ClassFilter {
private final List<Class<?>> entityList = new ArrayList<Class<?>>();
private final List<Class<?>> scalarTypeList = new ArrayList<Class<?>>();
private final List<Class<? extends ScalarType<?>>> scalarTypeList = new ArrayList<Class<? extends ScalarType<?>>>();
private final List<Class<?>> scalarConverterList = new ArrayList<Class<?>>();
private final List<Class<? extends ScalarTypeConverter<?, ?>>> scalarConverterList = new ArrayList<Class<? extends ScalarTypeConverter<?, ?>>>();
private final List<Class<?>> compoundTypeList = new ArrayList<Class<?>>();
private final List<Class<? extends CompoundType<?>>> compoundTypeList = new ArrayList<Class<? extends CompoundType<?>>>();
private final List<Class<?>> idGeneratorList = new ArrayList<Class<?>>();
private final List<Class<?>> beanControllerList = new ArrayList<Class<?>>();
// The following objects are instantiiated on first request
// there is always a candidate list, that holds the class and an
// instance list, that holds the instance. Once a class is instantiiated
// (or added) it will get removed from the candidate list
private final List<Class<? extends IdGenerator>> idGeneratorCandidates = new ArrayList<Class<? extends IdGenerator>>();
private final List<Class<?>> beanPostLoadList = new ArrayList<Class<?>>();
private final List<Class<? extends BeanPersistController>> beanPersistControllerCandidates = new ArrayList<Class<? extends BeanPersistController>>();
private final List<Class<?>> beanFindControllerList = new ArrayList<Class<?>>();
private final List<Class<?>> beanQueryAdapterList = new ArrayList<Class<?>>();
private final List<Class<? extends BeanPostLoad>> beanPostLoadCandidates = new ArrayList<Class<? extends BeanPostLoad>>();
private final List<Class<?>> beanListenerList = new ArrayList<Class<?>>();
private final List<Class<? extends BeanPostConstructListener>> beanPostConstructListenerCandidates = new ArrayList<Class<? extends BeanPostConstructListener>>();
private final List<Class<?>> serverConfigStartupList = new ArrayList<Class<?>>();
private final List<ServerConfigStartup> serverConfigStartupInstances = new ArrayList<ServerConfigStartup>();
private final List<Class<? extends BeanFindController>> beanFindControllerCandidates = new ArrayList<Class<? extends BeanFindController>>();
private final List<Class<? extends BeanPersistListener>> beanPersistListenerCandidates = new ArrayList<Class<? extends BeanPersistListener>>();
private final List<Class<? extends BeanQueryAdapter>> beanQueryAdapterCandidates = new ArrayList<Class<? extends BeanQueryAdapter>>();
private final List<Class<? extends ServerConfigStartup>> serverConfigStartupCandidates = new ArrayList<Class<? extends ServerConfigStartup>>();
private final List<IdGenerator> idGeneratorInstances = new ArrayList<IdGenerator>();
private final List<BeanFindController> findControllerInstances = new ArrayList<BeanFindController>();
private final List<BeanPersistController> persistControllerInstances = new ArrayList<BeanPersistController>();
private final List<BeanPersistController> beanPersistControllerInstances = new ArrayList<BeanPersistController>();
private final List<BeanPostLoad> beanPostLoadInstances = new ArrayList<BeanPostLoad>();
private final List<BeanPersistListener> persistListenerInstances = new ArrayList<BeanPersistListener>();
private final List<BeanQueryAdapter> queryAdapterInstances = new ArrayList<BeanQueryAdapter>();
private final List<BeanPostConstructListener> beanPostConstructListenerInstances = new ArrayList<BeanPostConstructListener>();
private final List<BeanFindController> beanFindControllerInstances = new ArrayList<BeanFindController>();
private final List<BeanPersistListener> beanPersistListenerInstances = new ArrayList<BeanPersistListener>();
private final List<BeanQueryAdapter> beanQueryAdapterInstances = new ArrayList<BeanQueryAdapter>();
private final List<ServerConfigStartup> serverConfigStartupInstances = new ArrayList<ServerConfigStartup>();
private Class<?> changeLogPrepareClass;
private Class<?> changeLogListenerClass;
private Class<?> changeLogRegisterClass;
private Class<?> readAuditPrepareClass;
private Class<?> readAuditLoggerClass;
// single objects
private Class<? extends ChangeLogPrepare> changeLogPrepareClass;
private Class<? extends ChangeLogListener> changeLogListenerClass;
private Class<? extends ChangeLogRegister> changeLogRegisterClass;
private Class<? extends ReadAuditPrepare> readAuditPrepareClass;
private Class<? extends ReadAuditLogger> readAuditLoggerClass;
private ChangeLogPrepare changeLogPrepare;
private ChangeLogListener changeLogListener;
@@ -96,7 +107,7 @@ public class BootupClasses implements ClassFilter {
*/
public void runServerConfigStartup(ServerConfig serverConfig) {
for (Class<?> cls : serverConfigStartupList) {
for (Class<?> cls : serverConfigStartupCandidates) {
try {
ServerConfigStartup newInstance = (ServerConfigStartup) cls.newInstance();
newInstance.onStart(serverConfig);
@@ -116,113 +127,90 @@ public class BootupClasses implements ClassFilter {
}
/**
* Add IdGenerator instances (registered explicitly with the ServerConfig).
* Adds the list <code>toAdd</code> to <code>instances</code> and removes any pending
* candiate, to prevent duplicate instantiiation.
*/
public void addIdGenerators(List<IdGenerator> idGenerators) {
if (idGenerators != null) {
for (IdGenerator c : idGenerators) {
this.idGeneratorInstances.add(c);
private <T> void add(List<T> toAdd, List<T> instances, List<Class<? extends T>> candidates) {
if (toAdd != null) {
for (T obj : toAdd) {
instances.add(obj);
// don't automatically instantiate
this.idGeneratorList.remove(c.getClass());
candidates.remove(obj.getClass());
}
}
}
public void addQueryAdapters(List<BeanQueryAdapter> queryAdapterInstances) {
if (queryAdapterInstances != null) {
for (BeanQueryAdapter a : queryAdapterInstances) {
this.queryAdapterInstances.add(a);
// don't automatically instantiate
this.beanQueryAdapterList.remove(a.getClass());
}
}
/**
* Add IdGenerator instances (registered explicitly with the ServerConfig).
*/
public void addIdGenerators(List<IdGenerator> idGenerators) {
add(idGenerators, idGeneratorInstances, idGeneratorCandidates);
}
/**
* Add BeanPersistController instances.
*/
public void addPersistControllers(List<BeanPersistController> beanControllerInstances) {
if (beanControllerInstances != null) {
for (BeanPersistController c : beanControllerInstances) {
this.persistControllerInstances.add(c);
// don't automatically instantiate
this.beanControllerList.remove(c.getClass());
}
}
public void addPersistControllers(List<BeanPersistController> beanControllers) {
add(beanControllers, beanPersistControllerInstances, beanPersistControllerCandidates);
}
/**
* Add BeanPostLoad instances.
*/
public void addPostLoaders(List<BeanPostLoad> postLoadInstances) {
if (postLoadInstances != null) {
for (BeanPostLoad c : postLoadInstances) {
this.beanPostLoadInstances.add(c);
// don't automatically instantiate
this.beanPostLoadList.remove(c.getClass());
}
}
public void addPostLoaders(List<BeanPostLoad> postLoaders) {
add(postLoaders, beanPostLoadInstances, beanPostLoadCandidates);
}
/**
* Add BeanPostConstructListener instances.
*/
public void addPostConstructListeners(List<BeanPostConstructListener> postConstructListener) {
add(postConstructListener, beanPostConstructListenerInstances, beanPostConstructListenerCandidates);
}
/**
* Add BeanFindController instances.
*/
public void addFindControllers(List<BeanFindController> findControllers) {
if (findControllers != null) {
for (BeanFindController c : findControllers) {
this.findControllerInstances.add(c);
// don't automatically instantiate
this.beanFindControllerList.remove(c.getClass());
}
}
add(findControllers, beanFindControllerInstances, beanFindControllerCandidates);
}
public void addPersistListeners(List<BeanPersistListener> listenerInstances) {
if (listenerInstances != null) {
for (BeanPersistListener l : listenerInstances) {
this.persistListenerInstances.add(l);
// don't automatically instantiate
this.beanListenerList.remove(l.getClass());
}
}
add(listenerInstances, beanPersistListenerInstances, beanPersistListenerCandidates);
}
public void addQueryAdapters(List<BeanQueryAdapter> queryAdapters) {
add(queryAdapters, beanQueryAdapterInstances, beanQueryAdapterCandidates);
}
public void addServerConfigStartup(List<ServerConfigStartup> startupInstances) {
if (startupInstances != null) {
for (ServerConfigStartup l : startupInstances) {
this.serverConfigStartupInstances.add(l);
// don't automatically instantiate
this.serverConfigStartupList.remove(l.getClass());
}
}
add(startupInstances, serverConfigStartupInstances, serverConfigStartupCandidates);
}
public void addChangeLogInstances(ServerConfig serverConfig) {
readAuditPrepare = serverConfig.getReadAuditPrepare();
readAuditLogger = serverConfig.getReadAuditLogger();
if (readAuditPrepare == null && readAuditPrepareClass != null) {
readAuditPrepare = (ReadAuditPrepare)create(readAuditPrepareClass, false);
}
if (readAuditLogger == null && readAuditLoggerClass != null) {
readAuditLogger = (ReadAuditLogger)create(readAuditLoggerClass, false);
}
changeLogPrepare = serverConfig.getChangeLogPrepare();
changeLogListener = serverConfig.getChangeLogListener();
changeLogRegister = serverConfig.getChangeLogRegister();
changeLogPrepare = serverConfig.getChangeLogPrepare();
// if not already set create the implementations found
// via classpath scanning
if (readAuditPrepare == null && readAuditPrepareClass != null) {
readAuditPrepare = create(readAuditPrepareClass, false);
}
if (readAuditLogger == null && readAuditLoggerClass != null) {
readAuditLogger = create(readAuditLoggerClass, false);
}
if (changeLogPrepare == null && changeLogPrepareClass != null) {
changeLogPrepare = (ChangeLogPrepare)create(changeLogPrepareClass, false);
changeLogPrepare = create(changeLogPrepareClass, false);
}
if (changeLogListener == null && changeLogListenerClass != null) {
changeLogListener = (ChangeLogListener)create(changeLogListenerClass, false);
changeLogListener = create(changeLogListenerClass, false);
}
if (changeLogRegister == null && changeLogRegisterClass != null) {
changeLogRegister = (ChangeLogRegister)create(changeLogRegisterClass, false);
changeLogRegister = create(changeLogRegisterClass, false);
}
}
@@ -233,10 +221,10 @@ public class BootupClasses implements ClassFilter {
* <p>
* Use logOnException = true to log the error and carry on.
*/
private Object create(Class<?> cls, boolean logOnException) {
private <T> T create(Class<T> cls, boolean logOnException) {
try {
// instantiate via found class
Constructor constructor = cls.getConstructor();
Constructor<T> constructor = cls.getConstructor();
return constructor.newInstance();
} catch (NoSuchMethodException e) {
@@ -258,13 +246,18 @@ public class BootupClasses implements ClassFilter {
/**
* Create the instance if it has a default constructor and add it to the list of instances.
* It clears the list of classes afterwards, so that each class in the given list is
* instantiated only once
*/
@SuppressWarnings(value = "unchecked")
private <T> void createAdd(Class<?> cls, List<T> instances) {
Object newInstance = create(cls, true);
if (newInstance != null) {
instances.add((T)newInstance);
private <T> List<T> createAdd(List<T> instances, List<Class<? extends T>> candidates) {
for (Class<? extends T> cls : candidates) {
T newInstance = create(cls, true);
if (newInstance != null) {
instances.add(newInstance);
}
}
candidates.clear(); // important, clear class list!
return instances;
}
public ChangeLogPrepare getChangeLogPrepare() {
@@ -287,51 +280,32 @@ public class BootupClasses implements ClassFilter {
return readAuditLogger;
}
public List<BeanQueryAdapter> getBeanQueryAdapters() {
// add class registered BeanQueryAdapter to the already created instances
for (Class<?> cls : beanQueryAdapterList) {
createAdd(cls, queryAdapterInstances);
}
return queryAdapterInstances;
}
public List<BeanFindController> getBeanFindControllers() {
// add class registered BeanFindController to the list of created instances
for (Class<?> cls : beanFindControllerList) {
createAdd(cls, findControllerInstances);
}
return findControllerInstances;
}
public List<BeanPersistListener> getBeanPersistListeners() {
// add class registered BeanPersistController to the already created instances
for (Class<?> cls : beanListenerList) {
createAdd(cls, persistListenerInstances);
}
return persistListenerInstances;
public List<IdGenerator> getIdGenerators() {
return createAdd(idGeneratorInstances, idGeneratorCandidates);
}
public List<BeanPersistController> getBeanPersistControllers() {
// add class registered BeanPersistController to the already created instances
for (Class<?> cls : beanControllerList) {
createAdd(cls, persistControllerInstances);
}
return persistControllerInstances;
return createAdd(beanPersistControllerInstances, beanPersistControllerCandidates);
}
public List<BeanPostLoad> getBeanPostLoaders() {
// add class registered BeanPostLoad to the already created instances
for (Class<?> cls : beanPostLoadList) {
createAdd(cls, beanPostLoadInstances);
}
return beanPostLoadInstances;
return createAdd(beanPostLoadInstances, beanPostLoadCandidates);
}
public List<IdGenerator> getIdGenerators() {
for (Class<?> cls : idGeneratorList) {
createAdd(cls, idGeneratorInstances);
}
return idGeneratorInstances;
public List<BeanPostConstructListener> getBeanPostConstructoListeners() {
return createAdd(beanPostConstructListenerInstances, beanPostConstructListenerCandidates);
}
public List<BeanFindController> getBeanFindControllers() {
return createAdd(beanFindControllerInstances, beanFindControllerCandidates);
}
public List<BeanPersistListener> getBeanPersistListeners() {
return createAdd(beanPersistListenerInstances, beanPersistListenerCandidates);
}
public List<BeanQueryAdapter> getBeanQueryAdapters() {
return createAdd(beanQueryAdapterInstances, beanQueryAdapterCandidates);
}
/**
@@ -351,21 +325,21 @@ public class BootupClasses implements ClassFilter {
/**
* Return the list of ScalarTypes found.
*/
public List<Class<?>> getScalarTypes() {
public List<Class<? extends ScalarType<?>>> getScalarTypes() {
return scalarTypeList;
}
/**
* Return the list of ScalarConverters found.
*/
public List<Class<?>> getScalarConverters() {
public List<Class<? extends ScalarTypeConverter<?, ?>>> getScalarConverters() {
return scalarConverterList;
}
/**
* Return the list of ScalarConverters found.
*/
public List<Class<?>> getCompoundTypes() {
public List<Class<? extends CompoundType<?>>> getCompoundTypes() {
return compoundTypeList;
}
@@ -391,6 +365,7 @@ public class BootupClasses implements ClassFilter {
* This includes ScalarType, BeanController, BeanFinder and BeanListener.
* </p>
*/
@SuppressWarnings("unchecked")
private boolean isInterestingInterface(Class<?> cls) {
if (Modifier.isAbstract(cls.getModifiers())) {
@@ -400,77 +375,87 @@ public class BootupClasses implements ClassFilter {
}
boolean interesting = false;
if (IdGenerator.class.isAssignableFrom(cls)) {
idGeneratorList.add(cls);
interesting = true;
}
if (BeanPersistController.class.isAssignableFrom(cls)) {
beanControllerList.add(cls);
interesting = true;
}
if (BeanPostLoad.class.isAssignableFrom(cls)) {
beanPostLoadList.add(cls);
interesting = true;
}
// Types
if (ScalarType.class.isAssignableFrom(cls)) {
scalarTypeList.add(cls);
scalarTypeList.add((Class<? extends ScalarType<?>>) cls);
interesting = true;
}
if (ScalarTypeConverter.class.isAssignableFrom(cls)) {
scalarConverterList.add(cls);
scalarConverterList.add((Class<? extends ScalarTypeConverter<?, ?>>) cls);
interesting = true;
}
if (CompoundType.class.isAssignableFrom(cls)) {
compoundTypeList.add(cls);
compoundTypeList.add((Class<? extends CompoundType<?>>) cls);
interesting = true;
}
if (IdGenerator.class.isAssignableFrom(cls)) {
idGeneratorCandidates.add((Class<? extends IdGenerator>) cls);
interesting = true;
}
// "Candidates"
if (BeanPersistController.class.isAssignableFrom(cls)) {
beanPersistControllerCandidates.add((Class<? extends BeanPersistController>) cls);
interesting = true;
}
if (BeanPostLoad.class.isAssignableFrom(cls)) {
beanPostLoadCandidates.add((Class<? extends BeanPostLoad>) cls);
interesting = true;
}
if (BeanPostConstructListener.class.isAssignableFrom(cls)) {
beanPostConstructListenerCandidates.add((Class<? extends BeanPostConstructListener>) cls);
interesting = true;
}
if (BeanFindController.class.isAssignableFrom(cls)) {
beanFindControllerList.add(cls);
beanFindControllerCandidates.add((Class<? extends BeanFindController>) cls);
interesting = true;
}
if (BeanPersistListener.class.isAssignableFrom(cls)) {
beanListenerList.add(cls);
beanPersistListenerCandidates.add((Class<? extends BeanPersistListener>) cls);
interesting = true;
}
if (BeanQueryAdapter.class.isAssignableFrom(cls)) {
beanQueryAdapterList.add(cls);
beanQueryAdapterCandidates.add((Class<? extends BeanQueryAdapter>) cls);
interesting = true;
}
if (ServerConfigStartup.class.isAssignableFrom(cls)) {
serverConfigStartupList.add(cls);
serverConfigStartupCandidates.add((Class<? extends ServerConfigStartup>) cls);
interesting = true;
}
// single instances
// TODO: What should happen, if there is already an other
// changeLogListener assigned? (Last wins? / Exception?)
if (ChangeLogListener.class.isAssignableFrom(cls)) {
changeLogListenerClass = cls;
changeLogListenerClass = (Class<? extends ChangeLogListener>) cls;
interesting = true;
}
if (ChangeLogRegister.class.isAssignableFrom(cls)) {
changeLogRegisterClass = cls;
changeLogRegisterClass = (Class<? extends ChangeLogRegister>) cls;
interesting = true;
}
if (ChangeLogPrepare.class.isAssignableFrom(cls)) {
changeLogPrepareClass = cls;
changeLogPrepareClass = (Class<? extends ChangeLogPrepare>) cls;
interesting = true;
}
if (ReadAuditPrepare.class.isAssignableFrom(cls)) {
readAuditPrepareClass = cls;
readAuditPrepareClass = (Class<? extends ReadAuditPrepare>) cls;
interesting = true;
}
if (ReadAuditLogger.class.isAssignableFrom(cls)) {
readAuditLoggerClass = cls;
readAuditLoggerClass = (Class<? extends ReadAuditLogger>) cls;
interesting = true;
}
@@ -20,6 +20,7 @@ import com.avaje.ebean.config.dbplatform.PlatformIdGenerator;
import com.avaje.ebean.event.BeanFindController;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistListener;
import com.avaje.ebean.event.BeanPostConstructListener;
import com.avaje.ebean.event.BeanPostLoad;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.event.changelog.BeanChange;
@@ -51,6 +52,7 @@ import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.core.PersistRequest;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyLists;
import com.avaje.ebeaninternal.server.el.ElComparator;
@@ -241,6 +243,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
private volatile BeanPersistController persistController;
private final BeanPostLoad beanPostLoad;
private final BeanPostConstructListener beanPostConstructListener;
/**
* Listens for post commit insert update and delete events.
@@ -423,6 +426,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
this.beanFinder = deploy.getBeanFinder();
this.persistController = deploy.getPersistController();
this.persistListener = deploy.getPersistListener();
this.beanPostConstructListener = deploy.getPostConstructListener();
this.beanPostLoad = deploy.getPostLoad();
this.queryAdapter = deploy.getQueryAdapter();
this.changeLogFilter = deploy.getChangeLogFilter();
@@ -1408,6 +1412,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
updatePlanCache.put(key, plan);
}
/**
* Return a Sql update statement to set the importedId value (deferred execution).
*/
public String getUpdateImportedIdSql(ImportedId prop) {
return "update " + baseTable + " set " + prop.importedIdClause() + " where " + idBinder.getBindIdSql(null);
}
/**
* Return true if updates should only include changed properties. Otherwise
* all loaded properties are included in the update.
@@ -1550,17 +1561,26 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
@Override
@SuppressWarnings("unchecked")
public T createBean() {
return (T) createEntityBean();
return (T) createEntityBean(true);
}
/**
* Creates a new EntityBean.
* The parameter <code>isNew</code> controls either this is a new bean (then
* {@link BeanPostConstructListener#postCreate(Object)} will be invoked) or
* a reference (then {@link BeanPostLoad#postLoad(Object)} will be invoked
* on first access (lazy load) or immediately (eager load)
*/
@SuppressWarnings("unchecked")
public EntityBean createEntityBean() {
public EntityBean createEntityBean(boolean isNew) {
try {
EntityBean bean = (EntityBean) prototypeEntityBean._ebean_newInstance();
if (beanPostConstructListener != null) {
beanPostConstructListener.autowire(bean); // calls all registered listeners
beanPostConstructListener.postConstruct(bean); // calls first the @PostConstruct method and then the listeners
}
if (unloadProperties.length > 0) {
// 'unload' any properties initialised in the default constructor
EntityBeanIntercept ebi = bean._ebean_getIntercept();
@@ -1568,12 +1588,23 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
ebi.setPropertyUnloaded(unloadProperties[i]);
}
}
if (beanPostConstructListener != null && isNew) {
beanPostConstructListener.postCreate(bean);
// if bean is not new, postLoad will be executed later in the bean's lifecycle
}
return bean;
} catch (Exception ex) {
throw new PersistenceException(ex);
}
}
/**
* Creates a new entitybean without invoking {@link BeanPostConstructListener#postCreate(Object)}
*/
public EntityBean createEntityBean() {
return createEntityBean(false);
}
/**
* Create a reference bean based on the id.
@@ -1806,7 +1837,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
* properties that make up the unique id.
*/
public Object getId(EntityBean bean) {
return (idProperty == null) ? null : idProperty.getValue(bean);
return (idProperty == null) ? null : idProperty.getValueIntercept(bean);
}
@Override
@@ -106,6 +106,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final PostLoadManager postLoadManager;
private final PostConstructManager postConstructManager;
private final BeanFinderManager beanFinderManager;
private final PersistListenerManager persistListenerManager;
@@ -218,6 +220,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
this.beanLifecycleAdapterFactory = new BeanLifecycleAdapterFactory();
this.persistControllerManager = new PersistControllerManager(bootupClasses);
this.postLoadManager = new PostLoadManager(bootupClasses);
this.postConstructManager = new PostConstructManager(bootupClasses);
this.persistListenerManager = new PersistListenerManager(bootupClasses);
this.beanQueryAdapterManager = new BeanQueryAdapterManager(bootupClasses);
this.beanFinderManager = new BeanFinderManager(bootupClasses);
@@ -377,13 +380,13 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
try {
entityClass = Class.forName(entityClassName, false, classLoader);
} catch (Exception e) {
logger.error("Could not load entity bean class "+entityClassName+" for ebean.xml entry");
logger.error("Could not load entity bean class " + entityClassName + " for ebean.xml entry");
return;
}
DeployBeanInfo<?> info = deployInfoMap.get(entityClass);
if (info == null) {
logger.error("No entity bean for ebean.xml entry "+entityClassName);
logger.error("No entity bean for ebean.xml entry " + entityClassName);
} else {
for (XmRawSql sql : entityDeploy.getRawSql()) {
@@ -623,10 +626,11 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
int qa = beanQueryAdapterManager.getRegisterCount();
int cc = persistControllerManager.getRegisterCount();
int pl = postLoadManager.getRegisterCount();
int pc = postConstructManager.getRegisterCount();
int lc = persistListenerManager.getRegisterCount();
int fc = beanFinderManager.getRegisterCount();
logger.debug("BeanPersistControllers[" + cc + "] BeanFinders[" + fc + "] BeanPersistListeners[" + lc + "] BeanQueryAdapters[" + qa + "] BeanPostLoaders[" + pl + "]");
logger.debug("BeanPersistControllers[" + cc + "] BeanFinders[" + fc + "] BeanPersistListeners[" + lc + "] BeanQueryAdapters[" + qa + "] BeanPostLoaders[" + pl + "] BeanPostConstructors[" + pc + "]");
}
private void logStatus() {
@@ -1142,6 +1146,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
persistControllerManager.addPersistControllers(descriptor);
postLoadManager.addPostLoad(descriptor);
postConstructManager.addPostConstructListeners(descriptor);
persistListenerManager.addPersistListeners(descriptor);
beanQueryAdapterManager.addQueryAdapter(descriptor);
beanFinderManager.addFindControllers(descriptor);
@@ -1345,6 +1350,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
prop.setPropertyIndex(propertyIndex);
prop.setGetter(beanReflect.getGetter(propertyIndex));
prop.setSetter(beanReflect.getSetter(propertyIndex));
if (prop.isAggregation()) {
prop.setAggregationPrefix(DetermineAggPath.manyPath(prop.getAggregation(), desc));
}
}
}
}
@@ -1457,7 +1465,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
* If so it is ok for it not to be enhanced.
*/
private boolean isMappedSuperWithNoProperties(Class<?> beanClass) {
// Attention: do not use AnnotationBase.findAnnotation(cls,...) here.
MappedSuperclass annotation = beanClass.getAnnotation(MappedSuperclass.class);
if (annotation == null) {
return false;
@@ -35,6 +35,11 @@ public final class BeanFkeyProperty implements ElPropertyValue {
return "prefix:" + prefix + " name:" + name + " dbColumn:" + dbColumn + " ph:" + placeHolder;
}
@Override
public boolean isAggregation() {
return false;
}
public int getDeployOrder() {
return deployOrder;
}
@@ -5,6 +5,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.persistence.PersistenceException;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
@@ -16,6 +17,7 @@ import javax.persistence.PreUpdate;
import com.avaje.ebean.event.BeanPersistAdapter;
import com.avaje.ebean.event.BeanPersistRequest;
import com.avaje.ebean.event.BeanPostConstructListener;
import com.avaje.ebean.event.BeanPostLoad;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
@@ -50,6 +52,10 @@ public class BeanLifecycleAdapterFactory {
// has postLoad methods
deployDesc.addPostLoad(new PostLoadAdapter(methodHolder.postLoads));
}
if (!methodHolder.postConstructs.isEmpty()) {
// has postConstruct methods
deployDesc.addPostConstructListener(new PostConstructAdapter(methodHolder.postConstructs));
}
}
/**
@@ -65,6 +71,7 @@ public class BeanLifecycleAdapterFactory {
private final List<Method> preDeletes = new ArrayList<Method>();
private final List<Method> postDeletes = new ArrayList<Method>();
private final List<Method> postLoads = new ArrayList<Method>();
private final List<Method> postConstructs = new ArrayList<Method>();
/**
* Has one of the pre or post insert update delete annotated methods.
@@ -107,6 +114,9 @@ public class BeanLifecycleAdapterFactory {
if (method.isAnnotationPresent(PostLoad.class)) {
postLoads.add(method);
}
if (method.isAnnotationPresent(PostConstruct.class)) {
postConstructs.add(method);
}
}
}
@@ -241,4 +251,49 @@ public class BeanLifecycleAdapterFactory {
}
}
}
/**
* PostConstructAdapter using reflection to invoke lifecycle methods.
*/
private static class PostConstructAdapter implements BeanPostConstructListener {
private final Method[] postConstructMethods;
private PostConstructAdapter(List<Method> postConstructMethods) {
this.postConstructMethods = toArray(postConstructMethods);
}
@Override
public boolean isRegisterFor(Class<?> cls) {
// Not used
return false;
}
private void invoke(Method method, Object bean) {
try {
method.invoke(bean);
} catch (InvocationTargetException e) {
throw new PersistenceException("Error invoking lifecycle method", e);
} catch (IllegalAccessException e) {
throw new PersistenceException("Error invoking lifecycle method", e);
}
}
@Override
public void postConstruct(Object bean) {
for (int i = 0; i < postConstructMethods.length; i++) {
invoke(postConstructMethods[i], bean);
}
}
@Override
public void autowire(Object bean) {
// autowire is done by global PostConstructListener only
}
@Override
public void postCreate(Object bean) {
// postCreate is done by global PostConstructListener only
}
}
}
@@ -5,7 +5,7 @@ import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.config.EncryptKey;
import com.avaje.ebean.config.dbplatform.DbEncryptFunction;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebean.config.dbplatform.DbPlatformType;
import com.avaje.ebean.plugin.Property;
import com.avaje.ebean.text.StringParser;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
@@ -158,6 +158,7 @@ public class BeanProperty implements ElPropertyValue, Property {
*/
final String dbColumn;
final String elPrefix;
final String elPlaceHolder;
final String elPlaceHolderEncrypted;
@@ -171,6 +172,8 @@ public class BeanProperty implements ElPropertyValue, Property {
*/
final String sqlFormulaJoin;
final String aggregation;
final boolean formula;
/**
@@ -311,6 +314,7 @@ public class BeanProperty implements ElPropertyValue, Property {
this.dbColumn = tableAliasIntern(descriptor, deploy.getDbColumn(), false, null);
this.dbComment = deploy.getDbComment();
this.aggregation = deploy.getAggregation();
this.sqlFormulaJoin = InternString.intern(deploy.getSqlFormulaJoin());
this.sqlFormulaSelect = InternString.intern(deploy.getSqlFormulaSelect());
this.formula = sqlFormulaSelect != null;
@@ -324,6 +328,7 @@ public class BeanProperty implements ElPropertyValue, Property {
this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(), false, null);
this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(), dbEncrypted, dbColumn);
this.elPrefix = deploy.getElPrefix();
this.softDelete = deploy.isSoftDelete();
if (softDelete) {
@@ -370,6 +375,7 @@ public class BeanProperty implements ElPropertyValue, Property {
this.sqlFormulaJoin = null;
this.sqlFormulaSelect = null;
this.formula = false;
this.aggregation = null;
this.excludedFromHistory = source.excludedFromHistory;
this.draft = source.draft;
@@ -421,6 +427,7 @@ public class BeanProperty implements ElPropertyValue, Property {
this.field = source.getField();
this.docOptions = source.docOptions;
this.elPrefix = override.replace(source.elPrefix, source.dbColumn);
this.elPlaceHolder = override.replace(source.elPlaceHolder, source.dbColumn);
this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn);
@@ -474,6 +481,13 @@ public class BeanProperty implements ElPropertyValue, Property {
return true;
}
/**
* Return true if this property should have a DB Column created in DDL.
*/
public boolean isDDLColumn() {
return !formula && !secondaryTable && (aggregation == null);
}
/**
* Return true if this property is based on a formula.
*/
@@ -537,8 +551,16 @@ public class BeanProperty implements ElPropertyValue, Property {
return secondaryTableJoinPrefix;
}
public boolean isAggregation() {
return aggregation != null;
}
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
if (formula) {
if (aggregation != null) {
ctx.appendRawColumn(aggregation);
} else if (formula) {
ctx.appendFormulaSelect(sqlFormulaSelect);
} else if (!isTransient && !ignoreDraftOnlyProperty(ctx.isDraftQuery())) {
@@ -798,7 +820,7 @@ public class BeanProperty implements ElPropertyValue, Property {
if (bean != null) {
Object logicalVal = convertToLogicalType(value);
setValue((EntityBean) bean, logicalVal);
setValueIntercept((EntityBean) bean, logicalVal);
}
}
@@ -843,7 +865,7 @@ public class BeanProperty implements ElPropertyValue, Property {
}
public boolean containsMany() {
return false;
return aggregation != null;
}
@Override
@@ -893,7 +915,7 @@ public class BeanProperty implements ElPropertyValue, Property {
}
public String getElPrefix() {
return secondaryTableJoinPrefix;
return elPrefix;
}
/**
@@ -972,7 +994,7 @@ public class BeanProperty implements ElPropertyValue, Property {
/**
* Return the DB column type definition.
*/
public String renderDbType(DbType dbType, boolean strict) {
public String renderDbType(DbPlatformType dbType, boolean strict) {
if (dbColumnDefn != null) {
return dbColumnDefn;
}
@@ -122,6 +122,13 @@ public class ChainedBeanPersistController implements BeanPersistController {
}
}
@Override
public void postSoftDelete(BeanPersistRequest<?> request) {
for (int i = 0; i < chain.length; i++) {
chain[i].postSoftDelete(request);
}
}
@Override
public boolean preDelete(BeanPersistRequest<?> request) {
for (int i = 0; i < chain.length; i++) {
@@ -132,6 +139,16 @@ public class ChainedBeanPersistController implements BeanPersistController {
return true;
}
@Override
public boolean preSoftDelete(BeanPersistRequest<?> request) {
for (int i = 0; i < chain.length; i++) {
if (!chain[i].preSoftDelete(request)) {
return false;
}
}
return true;
}
@Override
public boolean preInsert(BeanPersistRequest<?> request) {
for (int i = 0; i < chain.length; i++) {
@@ -16,9 +16,9 @@ public class ChainedBeanPersistListener implements BeanPersistListener {
private final BeanPersistListener[] chain;
/**
* Construct adding 2 BeanPersistController's.
* Construct adding 2 BeanPersistListener's.
*/
public ChainedBeanPersistListener(BeanPersistListener c1, BeanPersistListener c2) {
ChainedBeanPersistListener(BeanPersistListener c1, BeanPersistListener c2) {
this(addList(c1, c2));
}
@@ -46,7 +46,7 @@ public class ChainedBeanPersistListener implements BeanPersistListener {
}
/**
* Construct given the list of BeanPersistController's.
* Construct given the list of BeanPersistListener's.
*/
public ChainedBeanPersistListener(List<BeanPersistListener> list) {
this.list = list;
@@ -54,7 +54,7 @@ public class ChainedBeanPersistListener implements BeanPersistListener {
}
/**
* Register a new BeanPersistController and return the resulting chain.
* Register a new BeanPersistListener and return the resulting chain.
*/
public ChainedBeanPersistListener register(BeanPersistListener c) {
if (list.contains(c)){
@@ -69,7 +69,7 @@ public class ChainedBeanPersistListener implements BeanPersistListener {
}
/**
* De-register a BeanPersistController and return the resulting chain.
* De-register a BeanPersistListener and return the resulting chain.
*/
public ChainedBeanPersistListener deregister(BeanPersistListener c) {
if (!list.contains(c)){
@@ -89,6 +89,12 @@ public class ChainedBeanPersistListener implements BeanPersistListener {
}
}
public void softDeleted(Object bean) {
for (int i = 0; i < chain.length; i++) {
chain[i].softDeleted(bean);
}
}
public void inserted(Object bean) {
for (int i = 0; i < chain.length; i++) {
chain[i].inserted(bean);
@@ -0,0 +1,91 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.event.BeanPostConstructListener;
import java.util.ArrayList;
import java.util.List;
/**
* Handles multiple BeanPostLoad's for a given entity type.
*/
public class ChainedBeanPostConstructListener implements BeanPostConstructListener {
private final List<BeanPostConstructListener> list;
private final BeanPostConstructListener[] chain;
/**
* Construct given the list of BeanPostCreate's.
*/
public ChainedBeanPostConstructListener(List<BeanPostConstructListener> list) {
this.list = list;
this.chain = list.toArray(new BeanPostConstructListener[list.size()]);
}
/**
* Register a new BeanPostCreate and return the resulting chain.
*/
public ChainedBeanPostConstructListener register(BeanPostConstructListener c) {
if (list.contains(c)) {
return this;
} else {
List<BeanPostConstructListener> newList = new ArrayList<BeanPostConstructListener>();
newList.addAll(list);
newList.add(c);
return new ChainedBeanPostConstructListener(newList);
}
}
/**
* De-register a BeanPostCreate and return the resulting chain.
*/
public BeanPostConstructListener deregister(BeanPostConstructListener c) {
if (!list.contains(c)) {
return this;
} else {
ArrayList<BeanPostConstructListener> newList = new ArrayList<BeanPostConstructListener>();
newList.addAll(list);
newList.remove(c);
return new ChainedBeanPostConstructListener(newList);
}
}
/**
* Return the size of the chain.
*/
protected int size() {
return chain.length;
}
@Override
public boolean isRegisterFor(Class<?> cls) {
// never called
return false;
}
/**
* Fire postLoad on all registered BeanPostCreate implementations.
*/
@Override
public void postConstruct(Object bean) {
for (int i = 0; i < chain.length; i++) {
chain[i].postConstruct(bean);
}
}
@Override
public void autowire(Object bean) {
for (int i = 0; i < chain.length; i++) {
chain[i].autowire(bean);
}
}
@Override
public void postCreate(Object bean) {
for (int i = 0; i < chain.length; i++) {
chain[i].postCreate(bean);
}
}
}
@@ -15,7 +15,7 @@ public class ChainedBeanPostLoad implements BeanPostLoad {
private final BeanPostLoad[] chain;
/**
* Construct given the list of BeanPersistController's.
* Construct given the list of BeanPostLoad's.
*/
public ChainedBeanPostLoad(List<BeanPostLoad> list) {
this.list = list;
@@ -23,7 +23,7 @@ public class ChainedBeanPostLoad implements BeanPostLoad {
}
/**
* Register a new BeanPersistController and return the resulting chain.
* Register a new BeanPostLoad and return the resulting chain.
*/
public ChainedBeanPostLoad register(BeanPostLoad c) {
if (list.contains(c)){
@@ -38,7 +38,7 @@ public class ChainedBeanPostLoad implements BeanPostLoad {
}
/**
* De-register a BeanPersistController and return the resulting chain.
* De-register a BeanPostLoad and return the resulting chain.
*/
public ChainedBeanPostLoad deregister(BeanPostLoad c) {
if (!list.contains(c)){
@@ -125,4 +125,8 @@ public interface DbSqlContext {
*/
boolean isDraftQuery();
/**
* Start group by clause.
*/
void startGroupBy();
}
@@ -0,0 +1,96 @@
package com.avaje.ebeaninternal.server.deploy;
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;
class DetermineAggPath {
/**
* Return the many path for the given aggregation formula.
*/
static String manyPath(String aggregation, DeployBeanDescriptor<?> desc) {
DetermineAggPath.Path path = paths(aggregation);
return path.getManyPath(0, desc);
}
static Path paths(String aggregation) {
String aggPath = path(aggregation);
return new Path(aggPath.split("\\."), aggregation);
}
/**
* Parse and return the full path for the aggregation.
*/
static String path(String aggregation) {
// aggregations always have a form of sum(), avg(), max(), count() etc
// so find the first open bracket
int start = aggregation.indexOf('(');
if (start == -1) {
throw new IllegalArgumentException("Aggregation formula ["+aggregation+"] is expected to have a '(' ?");
}
for (int i = start + 1; i< aggregation.length(); i++) {
char ch = aggregation.charAt(i);
if (!isNamePart(ch)) {
return aggregation.substring(start + 1, i);
}
}
throw new IllegalArgumentException("Could not find path in aggregation formula ["+aggregation+"]");
}
private static boolean isNamePart(char ch) {
return ch == '.' || Character.isJavaIdentifierPart(ch);
}
/**
* Helper class holding aggregation path segments.
*/
static class Path {
final String aggregation;
final String[] paths;
Path(String[] paths, String aggregation) {
this.paths = paths;
this.aggregation = aggregation;
}
int length() {
return paths.length;
}
String path(int pos) {
if (pos == 0) {
return paths[0];
} else {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < pos; i++) {
if (i > 0) {
sb.append(".");
}
sb.append(paths[i]);
}
return sb.toString();
}
}
String getManyPath(int pos, DeployBeanDescriptor<?> desc) {
String path = paths[pos];
DeployBeanProperty details = desc.getBeanProperty(path);
if (details instanceof DeployBeanPropertyAssocMany<?>) {
return path(pos);
} else if (details instanceof DeployBeanPropertyAssocOne<?>) {
DeployBeanPropertyAssocOne<?> one = (DeployBeanPropertyAssocOne<?>)details;
DeployBeanDescriptor<?> targetDesc = one.getTargetDeploy();
return getManyPath(pos + 1, targetDesc);
}
throw new IllegalArgumentException("Can not find path to many in aggregation formula ["+aggregation+"]");
}
}
}
@@ -0,0 +1,42 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.event.BeanPostConstructListener;
import com.avaje.ebeaninternal.server.core.bootup.BootupClasses;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* Default implementation for creating BeanControllers.
*/
public class PostConstructManager {
private static final Logger logger = LoggerFactory.getLogger(PostConstructManager.class);
private final List<BeanPostConstructListener> list;
public PostConstructManager(BootupClasses bootupClasses) {
this.list = bootupClasses.getBeanPostConstructoListeners();
}
public int getRegisterCount() {
return list.size();
}
/**
* Register BeanPostLoad listeners for a given entity type.
*/
public void addPostConstructListeners(DeployBeanDescriptor<?> deployDesc) {
for (int i = 0; i < list.size(); i++) {
BeanPostConstructListener c = list.get(i);
if (c.isRegisterFor(deployDesc.getBeanType())) {
logger.debug("BeanPostLoad on[" + deployDesc.getFullName() + "] " + c.getClass().getName());
deployDesc.addPostConstructListener(c);
}
}
}
}
@@ -120,7 +120,7 @@ public final class TableJoin {
}
/**
* Return the type of join. LEFT OUTER JOIN etc.
* Return the type of join. LEFT JOIN etc.
*/
public SqlJoinType getType() {
return type;
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.deploy.id;
import java.sql.SQLException;
import com.avaje.ebean.SqlUpdate;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
@@ -22,11 +23,6 @@ public interface ImportedId {
*/
boolean isScalar();
/**
* Return the logical property name.
*/
String getLogicalName();
/**
* For scalar id return the related single db column.
* <p>
@@ -46,16 +42,16 @@ public interface ImportedId {
*/
void dmlAppend(GenerateDmlRequest request);
/**
* Append to the DML statement to the where clause.
*/
void dmlWhere(GenerateDmlRequest request, EntityBean bean);
/**
* Bind the value from the bean.
*/
Object bind(BindableRequest request, EntityBean bean) throws SQLException;
/**
* Bind the imported Id value to the SqlUpdate.
*/
int bind(int position, SqlUpdate update, EntityBean bean);
/**
* For inserting into ManyToMany intersection.
*/
@@ -65,4 +61,9 @@ public interface ImportedId {
* Used to derive a missing concatenated key from multiple imported keys.
*/
BeanProperty findMatchImport(String matchDbColumn);
/**
* Return the set importedId clause.
*/
String importedIdClause();
}
@@ -4,6 +4,7 @@ import java.sql.SQLException;
import javax.persistence.PersistenceException;
import com.avaje.ebean.SqlUpdate;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.BeanFkeyProperty;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
@@ -46,11 +47,6 @@ public class ImportedIdEmbedded implements ImportedId {
return false;
}
public String getLogicalName() {
return owner.getName() + "." + foreignAssocOne.getName();
}
public String getDbColumn() {
return null;
}
@@ -68,32 +64,32 @@ public class ImportedIdEmbedded implements ImportedId {
}
}
public void dmlWhere(GenerateDmlRequest request, EntityBean bean) {
@Override
public String importedIdClause() {
Object embeddedId = null;
if (bean != null) {
embeddedId = foreignAssocOne.getValue(bean);
}
if (embeddedId == null) {
for (int i = 0; i < imported.length; i++) {
if (imported[i].owner.isDbUpdatable()) {
request.appendColumnIsNull(imported[i].localDbColumn);
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < imported.length; i++) {
if (i > 0) {
sb.append(", ");
}
} else {
EntityBean embedded = (EntityBean) embeddedId;
for (int i = 0; i < imported.length; i++) {
if (imported[i].owner.isDbUpdatable()) {
Object value = imported[i].foreignProperty.getValue(embedded);
if (value == null) {
request.appendColumnIsNull(imported[i].localDbColumn);
} else {
request.appendColumn(imported[i].localDbColumn);
}
}
sb.append(imported[i].localDbColumn).append(" = ?");
}
return sb.toString();
}
@Override
public int bind(int position, SqlUpdate update, EntityBean bean) {
int pos = position;
EntityBean embedded = (EntityBean) foreignAssocOne.getValue(bean);
for (int i = 0; i < imported.length; i++) {
if (imported[i].owner.isUpdateable()) {
Object scalarValue = imported[i].foreignProperty.getValue(embedded);
update.setParameter(pos++, scalarValue);
}
}
return pos;
}
public Object bind(BindableRequest request, EntityBean bean) throws SQLException {
@@ -7,6 +7,7 @@ import java.util.List;
import javax.persistence.PersistenceException;
import com.avaje.ebean.SqlUpdate;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.deploy.BeanFkeyProperty;
@@ -82,10 +83,6 @@ public final class ImportedIdSimple implements ImportedId, Comparable<ImportedId
return true;
}
public String getLogicalName() {
return logicalName;
}
public String getDbColumn() {
return localDbColumn;
}
@@ -114,19 +111,16 @@ public final class ImportedIdSimple implements ImportedId, Comparable<ImportedId
request.appendColumn(localDbColumn);
}
public void dmlWhere(GenerateDmlRequest request, EntityBean bean) {
@Override
public String importedIdClause() {
return localDbColumn + " = ?";
}
if (owner.isDbUpdatable()) {
Object value = null;
if (bean != null) {
value = getIdValue(bean);
}
if (value == null) {
request.appendColumnIsNull(localDbColumn);
} else {
request.appendColumn(localDbColumn);
}
}
@Override
public int bind(int position, SqlUpdate update, EntityBean bean) {
Object value = getIdValue(bean);
update.setParameter(position, value);
return ++position;
}
public Object bind(BindableRequest request, EntityBean bean) throws SQLException {
@@ -11,6 +11,7 @@ import com.avaje.ebean.config.dbplatform.PlatformIdGenerator;
import com.avaje.ebean.event.BeanFindController;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistListener;
import com.avaje.ebean.event.BeanPostConstructListener;
import com.avaje.ebean.event.BeanPostLoad;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.event.changelog.ChangeLogFilter;
@@ -22,6 +23,7 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistController;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistListener;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanPostConstructListener;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanPostLoad;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanQueryAdapter;
import com.avaje.ebeaninternal.server.deploy.IndexDefinition;
@@ -155,6 +157,7 @@ public class DeployBeanDescriptor<T> {
private final List<BeanPersistListener> persistListeners = new ArrayList<BeanPersistListener>();
private final List<BeanQueryAdapter> queryAdapters = new ArrayList<BeanQueryAdapter>();
private final List<BeanPostLoad> postLoaders = new ArrayList<BeanPostLoad>();
private final List<BeanPostConstructListener> postConstructListeners = new ArrayList<BeanPostConstructListener>();
private CacheOptions cacheOptions = CacheOptions.NO_CACHING;
@@ -175,8 +178,6 @@ public class DeployBeanDescriptor<T> {
private String name;
private boolean processedRawSqlExtend;
private ChangeLogFilter changeLogFilter;
private String dbComment;
@@ -533,6 +534,19 @@ public class DeployBeanDescriptor<T> {
}
}
/**
* Return the BeanPostCreate(could be a chain of them, 1 or null).
*/
public BeanPostConstructListener getPostConstructListener() {
if (postConstructListeners.isEmpty()) {
return null;
} else if (postConstructListeners.size() == 1) {
return postConstructListeners.get(0);
} else {
return new ChainedBeanPostConstructListener(postConstructListeners);
}
}
public void addPersistController(BeanPersistController controller) {
persistControllers.add(controller);
}
@@ -549,6 +563,10 @@ public class DeployBeanDescriptor<T> {
postLoaders.add(postLoad);
}
public void addPostConstructListener(BeanPostConstructListener postConstructListener) {
postConstructListeners.add(postConstructListener);
}
public String getDraftTable() {
return draftTable;
}
@@ -18,6 +18,7 @@ import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.DeployDocPropertyOptions;
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import com.avaje.ebeaninternal.server.deploy.parse.AnnotationBase;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.properties.BeanPropertyGetter;
import com.avaje.ebeaninternal.server.properties.BeanPropertySetter;
@@ -159,6 +160,9 @@ public class DeployBeanProperty {
*/
private String dbColumn;
private String aggregationPrefix;
private String aggregation;
private String sqlFormulaSelect;
private String sqlFormulaJoin;
@@ -233,29 +237,29 @@ public class DeployBeanProperty {
if (field == null) {
return 0;
}
if (field.getAnnotation(Id.class) != null) {
if (AnnotationBase.findAnnotation(field, Id.class) != null) {
return ID_ORDER;
} else if (field.getAnnotation(EmbeddedId.class) != null) {
} else if (AnnotationBase.findAnnotation(field, EmbeddedId.class) != null) {
return ID_ORDER;
} else if (undirectionalShadow) {
return UNIDIRECTIONAL_ORDER;
} else if (isAuditProperty()) {
return AUDITCOLUMN_ORDER;
} else if (field.getAnnotation(Version.class) != null) {
} else if (AnnotationBase.findAnnotation(field, Version.class) != null) {
return VERSIONCOLUMN_ORDER;
} else if (field.getAnnotation(SoftDelete.class) != null) {
} else if (AnnotationBase.findAnnotation(field, SoftDelete.class) != null) {
return VERSIONCOLUMN_ORDER;
}
return 0;
}
private boolean isAuditProperty() {
return (field.getAnnotation(WhenCreated.class) != null
|| field.getAnnotation(WhenModified.class) != null
|| field.getAnnotation(WhoModified.class) != null
|| field.getAnnotation(WhoCreated.class) != null
|| field.getAnnotation(UpdatedTimestamp.class) != null
|| field.getAnnotation(CreatedTimestamp.class) != null);
return (AnnotationBase.findAnnotation(field, WhenCreated.class) != null
|| AnnotationBase.findAnnotation(field, WhenModified.class) != null
|| AnnotationBase.findAnnotation(field, WhoModified.class) != null
|| AnnotationBase.findAnnotation(field, WhoCreated.class) != null
|| AnnotationBase.findAnnotation(field, UpdatedTimestamp.class) != null
|| AnnotationBase.findAnnotation(field, CreatedTimestamp.class) != null);
}
public String getFullBeanName() {
@@ -584,8 +588,42 @@ public class DeployBeanProperty {
this.dbUpdateable = false;
}
public boolean isAggregation() {
return aggregation != null;
}
public String getAggregation() {
return aggregation;
}
public void setAggregation(String aggregation) {
this.aggregation = aggregation;
this.dbRead = true;
this.dbInsertable = false;
this.dbUpdateable = false;
}
/**
* Set the path to the aggregation.
*/
public void setAggregationPrefix(String aggregationPrefix) {
this.aggregationPrefix = aggregationPrefix;
this.aggregation = aggregation.replace(aggregationPrefix, "u1");
}
public String getElPrefix() {
if (aggregation != null) {
return aggregationPrefix;
} else {
return secondaryTableJoinPrefix;
}
}
public String getElPlaceHolder() {
if (sqlFormulaSelect != null) {
if (aggregation != null) {
return aggregation;
} else if (sqlFormulaSelect != null) {
return sqlFormulaSelect;
} else {
if (secondaryTableJoinPrefix != null) {
@@ -603,6 +641,9 @@ public class DeployBeanProperty {
if (sqlFormulaSelect != null) {
return sqlFormulaSelect;
}
if (aggregation != null) {
return aggregation;
}
return dbColumn;
}
@@ -21,7 +21,7 @@ public class DeployTableJoin {
private String table;
/**
* The type of join. LEFT OUTER etc.
* The type of join. LEFT JOIN etc.
*/
private SqlJoinType type = SqlJoinType.INNER;
@@ -29,294 +29,293 @@ import com.avaje.ebeaninternal.server.query.SqlJoinType;
*/
public class AnnotationAssocManys extends AnnotationParser {
private final BeanDescriptorManager factory;
private final BeanDescriptorManager factory;
/**
* Create with the DeployInfo.
*/
public AnnotationAssocManys(DeployBeanInfo<?> info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) {
super(info, javaxValidationAnnotations);
this.factory = factory;
}
/**
* Create with the DeployInfo.
*/
public AnnotationAssocManys(DeployBeanInfo<?> info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) {
super(info, javaxValidationAnnotations);
this.factory = factory;
}
/**
* Parse the annotations.
*/
public void parse() {
for (DeployBeanProperty prop : descriptor.propertiesAll()) {
if (prop instanceof DeployBeanPropertyAssocMany<?>) {
read((DeployBeanPropertyAssocMany<?>) prop);
}
}
}
/**
* Parse the annotations.
*/
public void parse() {
for (DeployBeanProperty prop : descriptor.propertiesAll()) {
if (prop instanceof DeployBeanPropertyAssocMany<?>) {
read((DeployBeanPropertyAssocMany<?>) prop);
}
}
}
private void read(DeployBeanPropertyAssocMany<?> prop) {
private void read(DeployBeanPropertyAssocMany<?> prop) {
OneToMany oneToMany = get(prop, OneToMany.class);
if (oneToMany != null) {
readToOne(oneToMany, prop);
PrivateOwned privateOwned = get(prop, PrivateOwned.class);
if (privateOwned != null){
prop.setModifyListenMode(ModifyListenMode.REMOVALS);
prop.getCascadeInfo().setDelete(privateOwned.cascadeRemove());
}
}
ManyToMany manyToMany = get(prop, ManyToMany.class);
if (manyToMany != null) {
readToMany(manyToMany, prop);
}
OneToMany oneToMany = get(prop, OneToMany.class);
if (oneToMany != null) {
readToOne(oneToMany, prop);
PrivateOwned privateOwned = get(prop, PrivateOwned.class);
if (privateOwned != null) {
prop.setModifyListenMode(ModifyListenMode.REMOVALS);
prop.getCascadeInfo().setDelete(privateOwned.cascadeRemove());
}
}
ManyToMany manyToMany = get(prop, ManyToMany.class);
if (manyToMany != null) {
readToMany(manyToMany, prop);
}
if (get(prop, HistoryExclude.class) != null) {
prop.setExcludedFromHistory();
}
OrderBy orderBy = get(prop, OrderBy.class);
if (orderBy != null) {
prop.setFetchOrderBy(orderBy.value());
}
MapKey mapKey = get(prop, MapKey.class);
if (mapKey != null) {
prop.setMapKey(mapKey.name());
}
Where where = get(prop, Where.class);
if (where != null) {
prop.setExtraWhere(where.clause());
}
// check for manually defined joins
BeanTable beanTable = prop.getBeanTable();
JoinColumn joinColumn = get(prop, JoinColumn.class);
if (joinColumn != null) {
prop.getTableJoin().addJoinColumn(true, joinColumn, beanTable);
}
JoinColumns joinColumns = get(prop, JoinColumns.class);
if (joinColumns != null) {
prop.getTableJoin().addJoinColumn(true, joinColumns.value(), beanTable);
}
JoinTable joinTable = get(prop, JoinTable.class);
if (joinTable != null) {
if (prop.isManyToMany()){
// expected this
readJoinTable(joinTable, prop);
} else {
// OneToMany in theory
prop.getTableJoin().addJoinColumn(true, joinTable.joinColumns(), beanTable);
}
}
if (prop.getMappedBy() != null){
// the join is derived by reversing the join information
// from the mapped by property.
// Refer BeanDescriptorManager.readEntityRelationships()
return;
}
if (prop.isManyToMany()){
manyToManyDefaultJoins(prop);
return;
}
if (!prop.getTableJoin().hasJoinColumns() && beanTable != null){
// use naming convention to define join (based on the bean name for this side of relationship)
// A unidirectional OneToMany or OneToMany with no mappedBy property
NamingConvention nc = factory.getNamingConvention();
String fkeyPrefix = null;
if (nc.isUseForeignKeyPrefix()){
fkeyPrefix = nc.getColumnFromProperty(descriptor.getBeanType(), descriptor.getName());
}
// Use the owning bean table to define the join
BeanTable owningBeanTable = factory.getBeanTable(descriptor.getBeanType());
owningBeanTable.createJoinColumn(fkeyPrefix, prop.getTableJoin(), false);
}
}
/**
* Define the joins for a ManyToMany relationship.
* <p>
* This includes joins to the intersection table and from the intersection table
* to the other side of the ManyToMany.
* </p>
*/
private void readJoinTable(JoinTable joinTable, DeployBeanPropertyAssocMany<?> prop) {
String intTableName = getFullTableName(joinTable);
// set the intersection table
DeployTableJoin intJoin = new DeployTableJoin();
intJoin.setTable(intTableName);
// add the source to intersection join columns
intJoin.addJoinColumn(true, joinTable.joinColumns(), prop.getBeanTable());
// set the intersection to dest table join columns
DeployTableJoin destJoin = prop.getTableJoin();
destJoin.addJoinColumn(false, joinTable.inverseJoinColumns(), prop.getBeanTable());
intJoin.setType(SqlJoinType.OUTER);
// reverse join from dest back to intersection
DeployTableJoin inverseDest = destJoin.createInverse(intTableName);
prop.setIntersectionJoin(intJoin);
prop.setInverseJoin(inverseDest);
}
/**
* Return the full table name
*/
private String getFullTableName(JoinTable joinTable) {
StringBuilder sb = new StringBuilder();
if (!StringHelper.isNull(joinTable.catalog())){
sb.append(joinTable.catalog()).append(".");
}
if (!StringHelper.isNull(joinTable.schema())){
sb.append(joinTable.schema()).append(".");
}
sb.append(joinTable.name());
return sb.toString();
}
/**
* Define intersection table and foreign key columns for ManyToMany.
* <p>
* Some of these (maybe all) have been already defined via @JoinTable
* and @JoinColumns etc.
* </p>
*/
private void manyToManyDefaultJoins(DeployBeanPropertyAssocMany<?> prop) {
String intTableName = null;
DeployTableJoin intJoin = prop.getIntersectionJoin();
if (intJoin == null){
intJoin = new DeployTableJoin();
prop.setIntersectionJoin(intJoin);
} else {
// intersection table already defined (by @JoinTable)
intTableName = intJoin.getTable();
}
BeanTable localTable = factory.getBeanTable(descriptor.getBeanType());
BeanTable otherTable = factory.getBeanTable(prop.getTargetType());
final String localTableName = localTable.getUnqualifiedBaseTable();
final String otherTableName = otherTable.getUnqualifiedBaseTable();
if (intTableName == null){
// define intersection table name
intTableName = getM2MJoinTableName(localTable, otherTable);
intJoin.setTable(intTableName);
intJoin.setType(SqlJoinType.OUTER);
}
DeployTableJoin destJoin = prop.getTableJoin();
if (intJoin.hasJoinColumns() && destJoin.hasJoinColumns()){
// already defined the foreign key columns etc
return;
}
if (!intJoin.hasJoinColumns()){
// define foreign key columns
BeanProperty[] localIds = localTable.getIdProperties();
for (int i = 0; i < localIds.length; i++) {
// add the source to intersection join columns
String fkCol = localTableName+"_"+localIds[i].getDbColumn();
intJoin.addJoinColumn(new DeployTableJoinColumn(localIds[i].getDbColumn(), fkCol));
}
}
if (!destJoin.hasJoinColumns()){
// define inverse foreign key columns
BeanProperty[] otherIds = otherTable.getIdProperties();
for (int i = 0; i < otherIds.length; i++) {
// set the intersection to dest table join columns
final String fkCol = otherTableName+"_"+otherIds[i].getDbColumn();
destJoin.addJoinColumn(new DeployTableJoinColumn(fkCol, otherIds[i].getDbColumn()));
}
}
// reverse join from dest back to intersection
DeployTableJoin inverseDest = destJoin.createInverse(intTableName);
prop.setInverseJoin(inverseDest);
}
private String errorMsgMissingBeanTable(Class<?> type, String from) {
return "Error with association to ["+type+"] from ["+from+"]. Is "+type+" registered?";
if (orderBy != null) {
prop.setFetchOrderBy(orderBy.value());
}
private void readToMany(ManyToMany propAnn, DeployBeanPropertyAssocMany<?> manyProp) {
MapKey mapKey = get(prop, MapKey.class);
if (mapKey != null) {
prop.setMapKey(mapKey.name());
}
manyProp.setMappedBy(propAnn.mappedBy());
manyProp.setFetchType(propAnn.fetch());
setCascadeTypes(propAnn.cascade(), manyProp.getCascadeInfo());
Where where = get(prop, Where.class);
if (where != null) {
prop.setExtraWhere(where.clause());
}
Class<?> targetType = propAnn.targetEntity();
if (targetType.equals(void.class)) {
// via reflection of generics type
targetType = manyProp.getTargetType();
} else {
manyProp.setTargetType(targetType);
}
// check for manually defined joins
BeanTable beanTable = prop.getBeanTable();
JoinColumn joinColumn = get(prop, JoinColumn.class);
if (joinColumn != null) {
prop.getTableJoin().addJoinColumn(true, joinColumn, beanTable);
}
// find the other many table (not intersection)
BeanTable assoc = factory.getBeanTable(targetType);
if (assoc == null) {
String msg = errorMsgMissingBeanTable(targetType, manyProp.getFullBeanName());
throw new RuntimeException(msg);
}
JoinColumns joinColumns = get(prop, JoinColumns.class);
if (joinColumns != null) {
prop.getTableJoin().addJoinColumn(true, joinColumns.value(), beanTable);
}
manyProp.setManyToMany();
manyProp.setModifyListenMode(ModifyListenMode.ALL);
manyProp.setBeanTable(assoc);
manyProp.getTableJoin().setType(SqlJoinType.OUTER);
}
JoinTable joinTable = get(prop, JoinTable.class);
if (joinTable != null) {
if (prop.isManyToMany()) {
// expected this
readJoinTable(joinTable, prop);
private void readToOne(OneToMany propAnn, DeployBeanPropertyAssocMany<?> manyProp) {
} else {
// OneToMany in theory
prop.getTableJoin().addJoinColumn(true, joinTable.joinColumns(), beanTable);
}
}
manyProp.setMappedBy(propAnn.mappedBy());
manyProp.setFetchType(propAnn.fetch());
if (prop.getMappedBy() != null) {
// the join is derived by reversing the join information
// from the mapped by property.
// Refer BeanDescriptorManager.readEntityRelationships()
return;
}
setCascadeTypes(propAnn.cascade(), manyProp.getCascadeInfo());
if (prop.isManyToMany()) {
manyToManyDefaultJoins(prop);
return;
}
Class<?> targetType = propAnn.targetEntity();
if (targetType.equals(void.class)) {
// via reflection of generics type
targetType = manyProp.getTargetType();
} else {
manyProp.setTargetType(targetType);
}
BeanTable assoc = factory.getBeanTable(targetType);
if (assoc == null) {
String msg = errorMsgMissingBeanTable(targetType, manyProp.getFullBeanName());
throw new RuntimeException(msg);
}
if (!prop.getTableJoin().hasJoinColumns() && beanTable != null) {
manyProp.setBeanTable(assoc);
manyProp.getTableJoin().setType(SqlJoinType.OUTER);
}
// use naming convention to define join (based on the bean name for this side of relationship)
// A unidirectional OneToMany or OneToMany with no mappedBy property
private String getM2MJoinTableName(BeanTable lhsTable, BeanTable rhsTable){
TableName lhs = new TableName(lhsTable.getBaseTable());
TableName rhs = new TableName(rhsTable.getBaseTable());
TableName joinTable = namingConvention.getM2MJoinTableName(lhs, rhs);
return joinTable.getQualifiedName();
}
NamingConvention nc = factory.getNamingConvention();
String fkeyPrefix = null;
if (nc.isUseForeignKeyPrefix()) {
fkeyPrefix = nc.getColumnFromProperty(descriptor.getBeanType(), descriptor.getName());
}
// Use the owning bean table to define the join
BeanTable owningBeanTable = factory.getBeanTable(descriptor.getBeanType());
owningBeanTable.createJoinColumn(fkeyPrefix, prop.getTableJoin(), false);
}
}
/**
* Define the joins for a ManyToMany relationship.
* <p>
* This includes joins to the intersection table and from the intersection table
* to the other side of the ManyToMany.
* </p>
*/
private void readJoinTable(JoinTable joinTable, DeployBeanPropertyAssocMany<?> prop) {
String intTableName = getFullTableName(joinTable);
// set the intersection table
DeployTableJoin intJoin = new DeployTableJoin();
intJoin.setTable(intTableName);
// add the source to intersection join columns
intJoin.addJoinColumn(true, joinTable.joinColumns(), prop.getBeanTable());
// set the intersection to dest table join columns
DeployTableJoin destJoin = prop.getTableJoin();
destJoin.addJoinColumn(false, joinTable.inverseJoinColumns(), prop.getBeanTable());
intJoin.setType(SqlJoinType.OUTER);
// reverse join from dest back to intersection
DeployTableJoin inverseDest = destJoin.createInverse(intTableName);
prop.setIntersectionJoin(intJoin);
prop.setInverseJoin(inverseDest);
}
/**
* Return the full table name
*/
private String getFullTableName(JoinTable joinTable) {
StringBuilder sb = new StringBuilder();
if (!StringHelper.isNull(joinTable.catalog())) {
sb.append(joinTable.catalog()).append(".");
}
if (!StringHelper.isNull(joinTable.schema())) {
sb.append(joinTable.schema()).append(".");
}
sb.append(joinTable.name());
return sb.toString();
}
/**
* Define intersection table and foreign key columns for ManyToMany.
* <p>
* Some of these (maybe all) have been already defined via @JoinTable
* and @JoinColumns etc.
* </p>
*/
private void manyToManyDefaultJoins(DeployBeanPropertyAssocMany<?> prop) {
String intTableName = null;
DeployTableJoin intJoin = prop.getIntersectionJoin();
if (intJoin == null) {
intJoin = new DeployTableJoin();
prop.setIntersectionJoin(intJoin);
} else {
// intersection table already defined (by @JoinTable)
intTableName = intJoin.getTable();
}
BeanTable localTable = factory.getBeanTable(descriptor.getBeanType());
BeanTable otherTable = factory.getBeanTable(prop.getTargetType());
final String localTableName = localTable.getUnqualifiedBaseTable();
final String otherTableName = otherTable.getUnqualifiedBaseTable();
if (intTableName == null) {
// define intersection table name
intTableName = getM2MJoinTableName(localTable, otherTable);
intJoin.setTable(intTableName);
intJoin.setType(SqlJoinType.OUTER);
}
DeployTableJoin destJoin = prop.getTableJoin();
if (intJoin.hasJoinColumns() && destJoin.hasJoinColumns()) {
// already defined the foreign key columns etc
return;
}
if (!intJoin.hasJoinColumns()) {
// define foreign key columns
BeanProperty[] localIds = localTable.getIdProperties();
for (int i = 0; i < localIds.length; i++) {
// add the source to intersection join columns
String fkCol = localTableName + "_" + localIds[i].getDbColumn();
intJoin.addJoinColumn(new DeployTableJoinColumn(localIds[i].getDbColumn(), namingConvention.getColumnFromProperty(null, fkCol)));
}
}
if (!destJoin.hasJoinColumns()) {
// define inverse foreign key columns
BeanProperty[] otherIds = otherTable.getIdProperties();
for (int i = 0; i < otherIds.length; i++) {
// set the intersection to dest table join columns
final String fkCol = otherTableName + "_" + otherIds[i].getDbColumn();
destJoin.addJoinColumn(new DeployTableJoinColumn(namingConvention.getColumnFromProperty(null, fkCol), otherIds[i].getDbColumn()));
}
}
// reverse join from dest back to intersection
DeployTableJoin inverseDest = destJoin.createInverse(intTableName);
prop.setInverseJoin(inverseDest);
}
private String errorMsgMissingBeanTable(Class<?> type, String from) {
return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered?";
}
private void readToMany(ManyToMany propAnn, DeployBeanPropertyAssocMany<?> manyProp) {
manyProp.setMappedBy(propAnn.mappedBy());
manyProp.setFetchType(propAnn.fetch());
setCascadeTypes(propAnn.cascade(), manyProp.getCascadeInfo());
Class<?> targetType = propAnn.targetEntity();
if (targetType.equals(void.class)) {
// via reflection of generics type
targetType = manyProp.getTargetType();
} else {
manyProp.setTargetType(targetType);
}
// find the other many table (not intersection)
BeanTable assoc = factory.getBeanTable(targetType);
if (assoc == null) {
String msg = errorMsgMissingBeanTable(targetType, manyProp.getFullBeanName());
throw new RuntimeException(msg);
}
manyProp.setManyToMany();
manyProp.setModifyListenMode(ModifyListenMode.ALL);
manyProp.setBeanTable(assoc);
manyProp.getTableJoin().setType(SqlJoinType.OUTER);
}
private void readToOne(OneToMany propAnn, DeployBeanPropertyAssocMany<?> manyProp) {
manyProp.setMappedBy(propAnn.mappedBy());
manyProp.setFetchType(propAnn.fetch());
setCascadeTypes(propAnn.cascade(), manyProp.getCascadeInfo());
Class<?> targetType = propAnn.targetEntity();
if (targetType.equals(void.class)) {
// via reflection of generics type
targetType = manyProp.getTargetType();
} else {
manyProp.setTargetType(targetType);
}
BeanTable assoc = factory.getBeanTable(targetType);
if (assoc == null) {
String msg = errorMsgMissingBeanTable(targetType, manyProp.getFullBeanName());
throw new RuntimeException(msg);
}
manyProp.setBeanTable(assoc);
manyProp.getTableJoin().setType(SqlJoinType.OUTER);
}
private String getM2MJoinTableName(BeanTable lhsTable, BeanTable rhsTable) {
TableName lhs = new TableName(lhsTable.getBaseTable());
TableName rhs = new TableName(rhsTable.getBaseTable());
TableName joinTable = namingConvention.getM2MJoinTableName(lhs, rhs);
return joinTable.getQualifiedName();
}
}
@@ -1,13 +1,16 @@
package com.avaje.ebeaninternal.server.deploy.parse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import com.avaje.ebean.config.NamingConvention;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
/**
* Provides some base methods for processing deployment annotations.
*/
@@ -46,12 +49,12 @@ public abstract class AnnotationBase {
T a = null;
Field field = prop.getField();
if (field != null) {
a = field.getAnnotation(annClass);
a = findAnnotation(field, annClass);
}
if (a == null) {
Method m = prop.getReadMethod();
if (m != null) {
a = m.getAnnotation(annClass);
a = findAnnotation(m, annClass);
}
}
return a;
@@ -66,10 +69,90 @@ public abstract class AnnotationBase {
protected <T extends Annotation> T find(DeployBeanProperty prop, Class<T> annClass) {
T a = get(prop, annClass);
if (a == null) {
a = prop.getOwningType().getAnnotation(annClass);
a = findAnnotation(prop.getOwningType(), annClass);
}
return a;
}
// this code is taken from the spring framework to find annotations recursively
/**
* Determine if the supplied {@link Annotation} is defined in the core JDK {@code java.lang.annotation} package.
*/
public static boolean isInJavaLangAnnotationPackage(Annotation annotation) {
return annotation.annotationType().getName().startsWith("java.lang.annotation");
}
/**
* Find a single {@link Annotation} of {@code annotationType} on the supplied {@link AnnotatedElement}.
* <p>
* Meta-annotations will be searched if the annotation is not <em>directly present</em> on the supplied element.
* <p>
* <strong>Warning</strong>: this method operates generically on annotated elements. In other words, this method
* does not execute specialized search algorithms for classes or methods. It only traverses through Annotations!
*/
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
if (annotationType == null) {
return null;
}
// check if directly present, if not, start recursive traversal
A ann = annotatedElement.getAnnotation(annotationType);
if (ann != null) {
return ann;
} else {
return findAnnotation(annotatedElement, annotationType, new HashSet<Annotation>());
}
}
/**
* Find a single {@link Annotation} of {@code annotationType} on the supplied class.
* <p>Meta-annotations will be searched if the annotation is not directly present on
* the supplied element.
* <p><strong>Note</strong>: this method searches for annotations at class & superClass(es)!
*/
public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) {
if (annotationType == null) {
return null;
}
// check if directly present, if not, start recursive traversal
A ann = clazz.getAnnotation(annotationType);
if (ann != null) {
return ann;
} else {
while (clazz != null && clazz != Object.class) {
ann = findAnnotation(clazz, annotationType, new HashSet<Annotation>());
if (ann != null) {
return ann;
}
// not present at this class - traverse to superclass
clazz = clazz.getSuperclass();
}
return null;
}
}
/**
* Perform the search algorithm avoiding endless recursion by tracking which
* annotations have already been visited.
*/
@SuppressWarnings("unchecked")
private static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType, Set<Annotation> visited) {
Annotation[] anns = annotatedElement.getDeclaredAnnotations();
for (Annotation ann : anns) {
if (ann.annotationType() == annotationType) {
return (A) ann;
}
}
for (Annotation ann : anns) {
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
A annotation = findAnnotation(ann.annotationType(), annotationType, visited);
if (annotation != null) {
return annotation;
}
}
}
return null;
}
}
@@ -7,6 +7,7 @@ import com.avaje.ebean.annotation.Draftable;
import com.avaje.ebean.annotation.DraftableElement;
import com.avaje.ebean.annotation.History;
import com.avaje.ebean.annotation.Index;
import com.avaje.ebean.annotation.Indices;
import com.avaje.ebean.annotation.ReadAudit;
import com.avaje.ebean.annotation.UpdateMode;
import com.avaje.ebean.annotation.View;
@@ -66,7 +67,7 @@ public class AnnotationClass extends AnnotationParser {
public void parseAttributeOverride() {
Class<?> cls = descriptor.getBeanType();
AttributeOverride override = cls.getAnnotation(AttributeOverride.class);
AttributeOverride override = AnnotationBase.findAnnotation(cls,AttributeOverride.class);
if (override != null) {
String propertyName = override.name();
Column column = override.column();
@@ -103,7 +104,7 @@ public class AnnotationClass extends AnnotationParser {
private void read(Class<?> cls) {
Entity entity = cls.getAnnotation(Entity.class);
Entity entity = AnnotationBase.findAnnotation(cls,Entity.class);
if (entity != null) {
if (entity.name().equals("")) {
descriptor.setName(cls.getSimpleName());
@@ -112,27 +113,34 @@ public class AnnotationClass extends AnnotationParser {
}
}
Embeddable embeddable = cls.getAnnotation(Embeddable.class);
Embeddable embeddable = AnnotationBase.findAnnotation(cls,Embeddable.class);
if (embeddable != null) {
descriptor.setEntityType(EntityType.EMBEDDED);
descriptor.setName("Embeddable:" + cls.getSimpleName());
}
Index index = cls.getAnnotation(Index.class);
Indices indices = AnnotationBase.findAnnotation(cls, Indices.class);
if (indices != null) {
for (Index index: indices.value()) {
descriptor.addIndex(new IndexDefinition(index.columnNames(), index.name(), index.unique()));
}
}
Index index = AnnotationBase.findAnnotation(cls,Index.class);
if (index != null) {
descriptor.addIndex(new IndexDefinition(index.columnNames(), index.name(), index.unique()));
}
UniqueConstraint uc = cls.getAnnotation(UniqueConstraint.class);
UniqueConstraint uc = AnnotationBase.findAnnotation(cls,UniqueConstraint.class);
if (uc != null) {
descriptor.addIndex(new IndexDefinition(uc.columnNames()));
}
View view = cls.getAnnotation(View.class);
View view = AnnotationBase.findAnnotation(cls,View.class);
if (view != null) {
descriptor.setView(view.name(), view.dependentTables());
}
Table table = cls.getAnnotation(Table.class);
Table table = AnnotationBase.findAnnotation(cls,Table.class);
if (table != null) {
UniqueConstraint[] uniqueConstraints = table.uniqueConstraints();
for (UniqueConstraint c : uniqueConstraints) {
@@ -140,54 +148,54 @@ public class AnnotationClass extends AnnotationParser {
}
}
Draftable draftable = cls.getAnnotation(Draftable.class);
Draftable draftable = AnnotationBase.findAnnotation(cls,Draftable.class);
if (draftable != null) {
descriptor.setDraftable();
}
DraftableElement draftableElement = cls.getAnnotation(DraftableElement.class);
DraftableElement draftableElement = AnnotationBase.findAnnotation(cls,DraftableElement.class);
if (draftableElement != null) {
descriptor.setDraftableElement();
}
ReadAudit readAudit = cls.getAnnotation(ReadAudit.class);
ReadAudit readAudit = AnnotationBase.findAnnotation(cls,ReadAudit.class);
if (readAudit != null) {
descriptor.setReadAuditing();
}
History history = cls.getAnnotation(History.class);
History history = AnnotationBase.findAnnotation(cls,History.class);
if (history != null) {
descriptor.setHistorySupport();
}
DbComment comment = cls.getAnnotation(DbComment.class);
DbComment comment = AnnotationBase.findAnnotation(cls,DbComment.class);
if (comment != null) {
descriptor.setDbComment(comment.value());
}
DocStore docStore = cls.getAnnotation(DocStore.class);
DocStore docStore = AnnotationBase.findAnnotation(cls,DocStore.class);
if (docStore != null) {
descriptor.readDocStore(docStore);
}
UpdateMode updateMode = cls.getAnnotation(UpdateMode.class);
UpdateMode updateMode = AnnotationBase.findAnnotation(cls,UpdateMode.class);
if (updateMode != null) {
descriptor.setUpdateChangesOnly(updateMode.updateChangesOnly());
}
Cache cache = cls.getAnnotation(Cache.class);
Cache cache = AnnotationBase.findAnnotation(cls,Cache.class);
if (cache != null && !disableL2Cache) {
descriptor.setCache(cache);
}
NamedQueries namedQueries = cls.getAnnotation(NamedQueries.class);
NamedQueries namedQueries = AnnotationBase.findAnnotation(cls,NamedQueries.class);
if (namedQueries != null) {
for (NamedQuery namedQuery : namedQueries.value()) {
descriptor.addNamedQuery(namedQuery.name(), namedQuery.query());
}
}
NamedQuery namedQuery = cls.getAnnotation(NamedQuery.class);
NamedQuery namedQuery = AnnotationBase.findAnnotation(cls,NamedQuery.class);
if (namedQuery != null) {
descriptor.addNamedQuery(namedQuery.name(), namedQuery.query());
}
@@ -212,6 +212,11 @@ public class AnnotationFields extends AnnotationParser {
prop.setSqlFormula(formula.select(), formula.join());
}
Aggregation aggregation = get(prop, Aggregation.class);
if (aggregation != null) {
prop.setAggregation(aggregation.value());
}
Version version = get(prop, Version.class);
if (version != null) {
// explicitly specify a version column
@@ -310,15 +315,46 @@ public class AnnotationFields extends AnnotationParser {
}
}
Indices indices = get(prop, Indices.class);
if (indices != null) {
for (Index index: indices.value()) {
addIndex(prop, index);
}
}
Index index = get(prop, Index.class);
if (index != null) {
if (hasRelationshipItem(prop)) {
throw new RuntimeException("Can't use Index on foreign key relationships.");
}
descriptor.addIndex(new IndexDefinition(prop.getDbColumn(), index.name(), index.unique()));
addIndex(prop, index);
}
}
private void addIndex(DeployBeanProperty prop, Index index) {
String[] columnNames;
if (index.columnNames() == null || index.columnNames().length == 0) {
columnNames = new String[] {prop.getDbColumn()};
} else {
columnNames = new String[index.columnNames().length];
int i=0;
int found = 0;
for (String colName : index.columnNames()) {
if (colName.equals("${fa}") || colName.equals(prop.getDbColumn())) {
columnNames[i++] = prop.getDbColumn();
found++;
} else {
columnNames[i++] = colName;
}
}
if (found != 1) {
throw new RuntimeException("DB-columname has to be specified exactly one time in columnNames.");
}
}
if (columnNames.length == 1 && hasRelationshipItem(prop)) {
throw new RuntimeException("Can't use Index on foreign key relationships.");
}
descriptor.addIndex(new IndexDefinition(columnNames, index.name(), index.unique()));
}
private void readJsonAnnotations(DeployBeanProperty prop) {
if (jacksonAnnotationsPresent) {
com.fasterxml.jackson.annotation.JsonIgnore jsonIgnore = get(prop, com.fasterxml.jackson.annotation.JsonIgnore.class);
@@ -14,7 +14,7 @@ public class AnnotationSql extends AnnotationParser {
public void parse() {
Class<?> cls = descriptor.getBeanType();
Sql sql = cls.getAnnotation(Sql.class);
Sql sql = AnnotationBase.findAnnotation(cls,Sql.class);
if (sql != null) {
descriptor.setEntityType(BeanDescriptor.EntityType.SQL);
}
@@ -223,7 +223,7 @@ public class DeployCreateProperties {
Class<?> propertyType = field.getType();
ManyToOne manyToOne = field.getAnnotation(ManyToOne.class);
ManyToOne manyToOne = AnnotationBase.findAnnotation(field,ManyToOne.class);
if (manyToOne != null){
Class<?> tt = manyToOne.targetEntity();
if (!tt.equals(void.class)){
@@ -240,7 +240,7 @@ public class DeployCreateProperties {
// List, Set or Map based object
Class<?> targetType = determineTargetType(field);
if (targetType == null) {
Transient transAnnotation = field.getAnnotation(Transient.class);
Transient transAnnotation = AnnotationBase.findAnnotation(field,Transient.class);
if (transAnnotation != null) {
// not supporting this field (generic type used)
return null;
@@ -298,15 +298,15 @@ public class DeployCreateProperties {
* Return true if the field has one of the special mappings.
*/
private boolean isSpecialScalarType(Field field) {
return (field.getAnnotation(DbJson.class) != null)
|| (field.getAnnotation(DbJsonB.class) != null)
|| (field.getAnnotation(DbArray.class) != null)
|| (field.getAnnotation(DbHstore.class) != null);
return (AnnotationBase.findAnnotation(field,DbJson.class) != null)
|| (AnnotationBase.findAnnotation(field,DbJsonB.class) != null)
|| (AnnotationBase.findAnnotation(field,DbArray.class) != null)
|| (AnnotationBase.findAnnotation(field,DbHstore.class) != null);
}
private boolean isTransientField(Field field) {
Transient t = field.getAnnotation(Transient.class);
Transient t = AnnotationBase.findAnnotation(field,Transient.class);
return (t != null);
}
@@ -111,11 +111,11 @@ public class DeployInherit {
info.setParent(parent);
}
Inheritance ia = cls.getAnnotation(Inheritance.class);
Inheritance ia = AnnotationBase.findAnnotation(cls,Inheritance.class);
if (ia != null) {
ia.strategy();
}
DiscriminatorColumn da = cls.getAnnotation(DiscriminatorColumn.class);
DiscriminatorColumn da = AnnotationBase.findAnnotation(cls,DiscriminatorColumn.class);
if (da != null) {
// lowercase the discriminator column for RawSql and JSON
info.setDiscriminatorColumn(da.name().toLowerCase());
@@ -128,7 +128,7 @@ public class DeployInherit {
info.setDiscriminatorLength(da.length());
}
DiscriminatorValue dv = cls.getAnnotation(DiscriminatorValue.class);
DiscriminatorValue dv = AnnotationBase.findAnnotation(cls,DiscriminatorValue.class);
if (dv != null) {
info.setDiscriminatorValue(dv.value());
}
@@ -149,7 +149,7 @@ public class DeployInherit {
if (cls.equals(Object.class)) {
return false;
}
Annotation a = cls.getAnnotation(Inheritance.class);
Annotation a = AnnotationBase.findAnnotation(cls,Inheritance.class);
if (a != null) {
return true;
}
@@ -18,7 +18,7 @@ import com.avaje.ebean.config.NamingConvention;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.TableName;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebean.config.dbplatform.DbPlatformType;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound;
import com.avaje.ebeaninternal.server.type.DataEncryptSupport;
@@ -254,7 +254,7 @@ public class DeployUtil {
}
public void setDbJsonBType(DeployBeanProperty prop, DbJsonB dbJsonB) {
setDbJsonType(prop, DbType.JSONB, dbJsonB.length());
setDbJsonType(prop, DbPlatformType.JSONB, dbJsonB.length());
}
private void setDbJsonType(DeployBeanProperty prop, int dbType, int dbLength) {
@@ -280,9 +280,9 @@ public class DeployUtil {
switch (dbJsonType) {
case JSON:
return DbType.JSON;
return DbPlatformType.JSON;
case JSONB:
return DbType.JSONB;
return DbPlatformType.JSONB;
case VARCHAR:
return Types.VARCHAR;
case CLOB:
@@ -290,7 +290,7 @@ public class DeployUtil {
case BLOB:
return Types.BLOB;
default:
return DbType.JSON;
return DbPlatformType.JSON;
}
}
@@ -83,6 +83,11 @@ public class ElPropertyChain implements ElPropertyValue {
return "expr:" + expression + " chain:" + Arrays.toString(chain);
}
@Override
public boolean isAggregation() {
return false;
}
private String getElPlaceHolder(String prefix, ElPropertyValue lastElPropertyValue, boolean encrypted) {
if (prefix == null) {
return lastElPropertyValue.getElPlaceholder(encrypted);
@@ -69,4 +69,9 @@ public interface ElPropertyDeploy {
* Return the underlying bean property.
*/
BeanProperty getBeanProperty();
/**
* Return true if this is an aggregation property.
*/
boolean isAggregation();
}
@@ -72,6 +72,9 @@ public abstract class AbstractExpression implements SpiExpression {
if (elProp.containsMany()) {
// for findRowCount we join to a many property
manyWhereJoin.add(elProp);
if (elProp.isAggregation()) {
manyWhereJoin.setAggregation();
}
}
}
}
@@ -323,11 +323,6 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return query.findFutureCount();
}
@Override
public FutureRowCount<T> findFutureRowCount() {
return findFutureCount();
}
@Override
public FutureList<T> findFutureList() {
return query.findFutureList();
@@ -343,11 +338,6 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return query.findCount();
}
@Override
public int findRowCount() {
return findCount();
}
@Override
public <A> List<A> findIds() {
return query.findIds();
@@ -53,11 +53,6 @@ public class FilterExpressionList<T> extends DefaultExpressionList<T> {
return rootQuery.findFutureCount();
}
@Override
public FutureRowCount<T> findFutureRowCount() {
return findFutureCount();
}
@Override
public List<T> findList() {
return rootQuery.findList();
@@ -73,11 +68,6 @@ public class FilterExpressionList<T> extends DefaultExpressionList<T> {
return rootQuery.findCount();
}
@Override
public int findRowCount() {
return findCount();
}
@Override
public Set<T> findSet() {
return rootQuery.findSet();
@@ -362,11 +362,6 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
return exprList.findFutureCount();
}
@Override
public FutureRowCount<T> findFutureRowCount() {
return findFutureCount();
}
@Override
public <A> List<A> findIds() {
return exprList.findIds();
@@ -409,12 +404,7 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public int findCount() {
return exprList.findRowCount();
}
@Override
public int findRowCount() {
return findCount();
return exprList.findCount();
}
@Override
@@ -287,15 +287,38 @@ class EqlAdapter<T> extends EQLBaseListener {
if (childCount < 3) {
throw new IllegalStateException("expecting 3 children for comparison? " + ctx);
}
String path = getLeftHandSidePath(ctx);
String operator = child(ctx, 1);
EqlOperator op = operatorMapping.get(operator);
if (op == null) {
throw new IllegalStateException("No operator found for " + operator);
}
String path = getLeftHandSidePath(ctx);
String rhs = child(ctx, 2);
if (path.equals(rhs)) {
// the 'value operator path' form
// invert the operator and use LHS as RHS
op = invert(op);
rhs = child(ctx, 0);
}
// RHS is Path, Literal or Named input parameter
helper.addExpression(path, op, child(ctx, 2));
helper.addExpression(path, op, rhs);
}
private EqlOperator invert(EqlOperator op) {
switch (op) {
// no change
case EQ : return EqlOperator.EQ;
case IEQ : return EqlOperator.IEQ;
case NE : return EqlOperator.NE;
// invert
case LT : return EqlOperator.GT;
case LTE : return EqlOperator.GTE;
case GT : return EqlOperator.LT;
case GTE : return EqlOperator.LTE;
default:
throw new IllegalStateException("Can not invert operator "+op);
}
}

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