Compare commits

..
Author SHA1 Message Date
Robin Bygrave 2bac7bf84d [maven-release-plugin] prepare release avaje-ebeanorm-7.20.1 2016-07-25 23:36:58 +12:00
Robin Bygrave 499862438d Bump pom to 7.20.1-SNAPSHOT 2016-07-25 23:36:12 +12:00
Robin Bygrave 4ad0b7cf66 #786 - Bump avaje-dbmigration to 1.2.1 ... which uses autoCommit true when running drop-all ddl 2016-07-25 23:34:17 +12:00
Robin Bygrave 6108ef484b #782 - History Many-To-Many Lazy-Loads fail 2016-07-25 23:31:02 +12:00
Robin Bygrave b763f4c875 #783 - HistoryExclude Many-to-Many Query-Generation Error 2016-07-25 23:29:33 +12:00
Robin Bygrave ca7285b2ad #783 - tests for ... HistoryExclude Many-to-Many Query-Generation Error 2016-07-25 23:28:45 +12:00
Robin Bygrave 75b639bc15 #782 - tests for ... History Many-To-Many Lazy-Loads fail 2016-07-25 23:22:33 +12:00
Robin Bygrave 1a7c4dac3f #1 - Change DdlGenerator.runScript() so that it uses connection.setAutoCommit(true) for drop ddl - when expectErrors is true 2016-07-25 23:03:00 +12:00
Robin Bygrave 6828fa1786 #784 - DDL DB Migration - Add @ManyToOne does not generate Foreign key and index ddl statements in migration script 2016-07-25 20:44:15 +12:00
Robin Bygrave ba7e729ae1 #785 - Bump avaje-classpath-scanner-api dependency to 2.2 for Windows fixes to classpath-scanner 2016-07-25 11:39:41 +12:00
Rob BygraveandGitHub c3fba72a24 Merge pull request #781 from FOCONIS/master
FIX: PathTests - on Windows
2016-07-25 11:14:23 +12:00
Robin Bygrave ff401cfc72 [maven-release-plugin] prepare for next development iteration 2016-07-20 00:03:20 +12:00
Robin Bygrave 7902276296 [maven-release-plugin] prepare release avaje-ebeanorm-7.19.1 2016-07-20 00:02:59 +12:00
Robin Bygrave 95f7a85aa1 Bump version to 7.19.1-SNAPSHOT 2016-07-20 00:02:08 +12:00
Roland Praml cf9eb4ed97 FIX: PathTests - they fail on windows machine, because the OS translates "/tmp" to "c:\tmp". bumped classpath-canner to current API version 2016-07-19 13:25:53 +02:00
Robin Bygrave b5cb04a6ed #780 - Remove ServerConfig registerJmxMBeans ... as it is no longer used 2016-07-18 09:22:22 +12:00
Robin Bygrave 5a9193f732 #779 - Refactor - Tidy internals of DefaultOrmQuery, remove unused String query property. 2016-07-15 23:51:27 +12:00
Robin Bygrave 55584f09d5 #778 - ENH: Add support for loading named queries from ebean.xml 2016-07-15 23:33:09 +12:00
Robin Bygrave 05eb0bfc55 #777 - ENH: Add support for @NamedQuery and @NamedQueries 2016-07-15 23:15:18 +12:00
Robin Bygrave cf13119b49 #776 - Remove ServerConfig.cacheWarmingDelay ... as it is no longer used 2016-07-15 20:51:22 +12:00
Robin Bygrave e0eff149b5 #775 - Change DefaultBackgroundExecutor to use JDK cached thread pool (rather than configure min/max size etc) 2016-07-15 20:48:41 +12:00
Robin Bygrave 474c2d5595 EQL - Add / expose via EbeanServer.createQuery(type, eql) method 2016-07-13 16:40:52 +12:00
Robin Bygrave e58cec8bdd EQL - parsing order by clause 2016-07-13 15:28:06 +12:00
Robin Bygrave 9dd82c0dd7 EQL - parsing limit offset clause 2016-07-13 14:30:12 +12:00
Robin Bygrave 49d3cc91e4 EQL - parsing select clause with distinct option 2016-07-13 14:07:59 +12:00
Robin Bygrave a8c2fc2ed5 EQL - parsing select clause 2016-07-13 13:55:56 +12:00
Robin Bygrave 45d7e3cafd EQL - parsing fetch clause with lazy/query option 2016-07-13 13:04:38 +12:00
Robin Bygrave be6754bd06 EQL - parsing fetch clause 2016-07-13 11:01:44 +12:00
Robin Bygrave 83c16ad1c6 EQL - parsing IN expressions with named parameters 2016-07-13 10:12:15 +12:00
Robin Bygrave 3f8b389999 EQL - parsing BETWEEN property expressions 2016-07-13 09:48:58 +12:00
Robin Bygrave b4b6a13e75 EQL - parsing BETWEEN expressions 2016-07-13 09:30:00 +12:00
Robin Bygrave 3fad4aae2f EQL - parsing IN expressions 2016-07-12 23:29:34 +12:00
Robin Bygrave ceab95e6c2 EQL - Simplify nested query where expressions after parsing 2016-07-12 22:51:02 +12:00
Robin Bygrave 84b37fb014 EQL Named parameter support for Like and ieq 2016-07-12 21:56:43 +12:00
Robin Bygrave 1a1c432c2a Initial add of SpiNamedParam 2016-07-12 20:45:33 +12:00
Robin Bygrave 86faa947af No effective change - tidy tests 2016-07-12 16:22:18 +12:00
Robin Bygrave 6b54b6ef46 Refactor BeanPropertyAssocOne findByParentIds use of raw expression 2016-07-12 16:11:51 +12:00
Robin Bygrave d8dfd66af3 Refactor BeanPropertyAssocMany findByParentIds use of raw expression 2016-07-12 16:01:32 +12:00
Robin Bygrave 675c51b8c1 Add IEQ support 2016-07-12 15:22:04 +12:00
Robin Bygrave 3cd7ecaa4a Merge branch 'wip/eql1' into wip/eql2 2016-07-12 15:05:00 +12:00
Robin Bygrave 1b322955ee [maven-release-plugin] prepare for next development iteration 2016-07-11 21:31:44 +12:00
Robin Bygrave 8115c5e230 [maven-release-plugin] prepare release avaje-ebeanorm-7.18.1 2016-07-11 21:31:25 +12:00
Robin Bygrave ad180c5531 No effective change - reduce test logging 2016-07-11 21:27:34 +12:00
Robin Bygrave b454f46dd4 Bump pom to 7.18.1-SNAPSHOT 2016-07-11 21:27:06 +12:00
Robin Bygrave 8a853b28f3 #772 - DB Migration DDL - alter table add column ... needs type conversion when using multiple platforms. e.g. varchar(20) ... convert to varchar2(20) for Oracle 2016-07-11 21:25:58 +12:00
Robin Bygrave a1be7ba5dd #771 - DB Migration DDL when using multiple platforms ... destination into sub directories rather than file suffix 2016-07-11 21:17:12 +12:00
Robin Bygrave 3914516793 #770 - DB Migration DDL generation run offline when using multiple platforms 2016-07-11 20:20:26 +12:00
Robin Bygrave 8c5854f77c #770 - DB Migration DDL generation run offline when using multiple platforms 2016-07-11 20:14:29 +12:00
Robin Bygrave 0bc5b94eae #770 - DB Migration DDL generation run offline when using multiple platforms 2016-07-11 20:08:26 +12:00
Robin Bygrave 12fa287f35 #769 - DB Migration DDL generation for UUID type when generating multiple platforms 2016-07-11 19:54:50 +12:00
Robin Bygrave 2991cb9ad1 #768 - Query.setDisableLazyLoading(true) ... not disabling lazy loading on @ManyToOne reference bean 2016-07-11 15:09:59 +12:00
Robin Bygrave 3b7f5496a8 #767 - Query.setDisableLazyLoading(true) ... not disabling lazy loading on @OneToMany 2016-07-11 12:28:31 +12:00
Robin Bygrave 4be703ff6e #767 - Query.setDisableLazyLoading(true) ... not disabling lazy loading on @OneToMany 2016-07-11 11:58:02 +12:00
Robin Bygrave d2c5765ac1 No effective change - tidy comments 2016-07-11 11:24:57 +12:00
Robin Bygrave aaf256b9be #766 - RawSql should automatically map column alias with underscore to camelCase ... so item_total should automatically map to bean property itemTotal 2016-07-08 23:44:11 +12:00
Robin Bygrave c7c6410cd9 #765 - ENH: Add ebean.xml with support for named RawSql, also adds EbeanServer.createNamedQuery() 2016-07-08 22:35:04 +12:00
Robin Bygrave 2c3c23725a No effective change - reformat code 2016-07-08 15:37:26 +12:00
Robin Bygrave 92069c0319 #764 - NPE on BeanDescriptorManager.processViewInvalidation(BeanDescriptorManager.java:385) 2016-07-08 15:34:43 +12:00
Robin Bygrave 630ad66ff1 #763 - Internal Code cleanup - remove redundant null checking etc 2016-07-08 15:01:39 +12:00
Robin Bygrave b732b390c7 [maven-release-plugin] prepare for next development iteration 2016-07-07 21:51:15 +12:00
Robin Bygrave 8c62fcdcb2 [maven-release-plugin] prepare release avaje-ebeanorm-7.17.1 2016-07-07 21:50:53 +12:00
Robin Bygrave e458f577fe No effective change - reduce logging for BeanDescriptorManager entity count message from INFO to DEBUG 2016-07-07 21:49:43 +12:00
Robin Bygrave 23434bab8c Bump pom to 7.17.1-SNAPSHOT 2016-07-07 21:45:40 +12:00
Robin Bygrave c47b5a1595 #762 - Change H2 Platform to IDENTITY (from SEQUENCE) with version 1.4.192 2016-07-07 21:43:15 +12:00
Robin Bygrave 411846347b #762 - Change H2 Platform to IDENTITY (from SEQUENCE) with version 1.4.192 2016-07-07 21:33:19 +12:00
Robin Bygrave 3722d19424 #761 - Change default for persistBatchOnCascade to be platform specific rather than NONE 2016-07-07 21:19:08 +12:00
Robin Bygrave 04a7310e5d [maven-release-plugin] prepare for next development iteration 2016-07-06 12:27:45 +12:00
Robin Bygrave 3490e90f03 [maven-release-plugin] prepare release avaje-ebeanorm-7.16.3 2016-07-06 12:27:26 +12:00
Robin Bygrave d89817a5c5 #760 - Change @Transactional(batchSize=100) ... to imply batch mode (remove need to also specify batch=PersistBatch.ALL) 2016-07-06 12:26:18 +12:00
Robin Bygrave db1ba5f20e #759 - Use default value of 100 for JDBC buffer fetchSize for findEach() / findEachWhile() - Add global defaults ServerConfig jdbcFetchSizeFindEach, jdbcFetchSizeFindList 2016-07-04 21:56:33 +12:00
Robin Bygrave 4f9a9a5094 No effective change - remove unused method 2016-06-30 14:45:06 +12:00
Robin Bygrave 1c6ac1289d #758 - DDL - @Index(unique=true) on field/property not creating unique index 2016-06-30 13:59:38 +12:00
Robin Bygrave b96be84106 Merge branch 'master' of github.com:ebean-orm/avaje-ebeanorm 2016-06-30 05:38:32 +12:00
Robin Bygrave 3c40293183 #757 - Reduce logging of Commit message of org.avaje.ebean.TXN from INFO to DEBUG 2016-06-30 05:38:16 +12:00
Rob BygraveandGitHub a9a6031986 Merge pull request #755 from MatthewMerrill/patch-1
Javadoc typo in SqlUpdate
2016-06-28 09:32:27 +12:00
Robin Bygrave b82085da42 [maven-release-plugin] prepare for next development iteration 2016-06-28 07:13:26 +12:00
Robin Bygrave 9a17a7752c [maven-release-plugin] prepare release avaje-ebeanorm-7.16.2 2016-06-28 07:13:06 +12:00
Robin Bygrave 92474c2b24 #754 - Logging output for bind log of UpdateQuery does not include bind values for set clause ... 2016-06-28 07:10:59 +12:00
Robin Bygrave 9a00b9df0e #753 - @History "as of" query errors when executed a second time - invalid bind parameter 2016-06-28 07:04:39 +12:00
Robin Bygrave 697f67c14a #753 - @History "as of" query errors when executed a second time - invalid bind parameter 2016-06-28 06:46:16 +12:00
Matthew MerrillandGitHub 2c34bc1636 Example code typo
Change "there where" to "there were" and added space between modify count and next word.
2016-06-27 09:07:45 -07:00
Robin Bygrave 1f9ccf1c8b [maven-release-plugin] prepare for next development iteration 2016-06-23 21:39:03 +12:00
Robin Bygrave a1f10b8704 [maven-release-plugin] prepare release avaje-ebeanorm-7.16.1 2016-06-23 21:38:42 +12:00
Robin Bygrave 3c21950b91 Bump pom to 7.16.1-SNAPSHOT 2016-06-23 21:36:57 +12:00
Robin Bygrave 3b45308ac7 Merge branch 'DevFactory-release/collection-isempty-1' 2016-06-23 13:40:23 +12:00
Robin Bygrave 00e69e1b5d No effective change - remove extra whitespace 2016-06-23 13:39:34 +12:00
Robin Bygrave 11b3f7a813 Merge branch 'release/collection-isempty-1' of https://github.com/DevFactory/avaje-ebeanorm into DevFactory-release/collection-isempty-1
Conflicts:
	src/main/java/com/avaje/ebeaninternal/server/query/DefaultDbSqlContext.java
2016-06-23 13:24:58 +12:00
Robin Bygrave 2185c8a886 #752 - Refactor: SQL generation for @History support with Postgres, MySql (views/triggers based) such that it more closely aligns with SQL 2011 based SQL 2016-06-23 11:33:01 +12:00
Robin Bygrave f8bda05b37 #751 - @History with @ManyToMany ... intersection table not getting associated history support 2016-06-23 11:05:16 +12:00
Artyom Melnikov 0a3b819991 Fixing squid:S1155 - Collection.isEmpty() should be used to test for emptiness 2016-06-22 13:43:15 +03:00
Rob BygraveandGitHub 5c8b1eb3bb Merge pull request #748 from yuqiyi/master
fix DeployBeanDescriptor.java of the method setView
2016-06-22 22:21:52 +12:00
yuqiyi 824f6dcc7e fix DeployBeanDescriptor.java of the method setView 2016-06-22 18:11:37 +08:00
Robin Bygrave 06e23b85ad #747 - @History findVersionsBetween query with Oracle has incorrect ordering 2016-06-22 20:16:35 +12:00
Robin Bygrave 09219c759b [maven-release-plugin] prepare for next development iteration 2016-06-20 22:57:36 +12:00
Robin Bygrave a954f8e9e1 EQL - initial 2016-06-01 17:27:35 +12:00
256 changed files with 9835 additions and 1785 deletions
+12 -6
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>7.15.1</version>
<version>7.20.1</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -37,7 +37,7 @@
<scm>
<developerConnection>scm:git:https://github.com/ebean-orm/avaje-ebeanorm.git</developerConnection>
<tag>avaje-ebeanorm-7.15.1</tag>
<tag>avaje-ebeanorm-7.20.1</tag>
</scm>
<dependencies>
@@ -69,19 +69,19 @@
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-classpath-scanner-api</artifactId>
<version>2.1</version>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-classpath-scanner</artifactId>
<version>2.1.1</version>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-dbmigration</artifactId>
<version>1.1.1</version>
<version>1.2.1</version>
</dependency>
<dependency>
@@ -90,6 +90,12 @@
<version>[1.7.1,1.7.99)</version>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.5.3</version>
</dependency>
<!-- Jackson core used internally by Ebean -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
@@ -177,7 +183,7 @@
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.189</version>
<version>1.4.192</version>
<scope>provided</scope>
</dependency>
@@ -38,6 +38,11 @@ public interface BeanState {
*/
void setDisableLazyLoad(boolean disableLazyLoading);
/**
* Return true if the bean has lazy loading disabled.
*/
boolean isDisableLazyLoad();
/**
* Set the loaded state of the property given it's name.
*
@@ -49,7 +49,7 @@ final class DRawSqlColumnsParser {
if (split.length > 1) {
ArrayList<String> tmp = new ArrayList<String>(split.length);
for (int i = 0; i < split.length; i++) {
if (split[i].trim().length() > 0) {
if (!split[i].trim().isEmpty()) {
tmp.add(split[i].trim());
}
}
+59 -35
View File
@@ -161,7 +161,7 @@ public final class Ebean {
// look to see if there is a default server defined
String defaultName = PrimaryServer.getDefaultServerName();
logger.debug("defaultName:" + defaultName);
if (defaultName != null && defaultName.trim().length() > 0) {
if (defaultName != null && !defaultName.trim().isEmpty()) {
defaultServer = getWithCreate(defaultName.trim());
}
}
@@ -182,7 +182,7 @@ public final class Ebean {
}
private EbeanServer get(String name) {
if (name == null || name.length() == 0) {
if (name == null || name.isEmpty()) {
return defaultServer;
}
// non-synchronized read
@@ -945,6 +945,21 @@ public final class Ebean {
return serverMgr.getDefaultServer().createCsvReader(beanType);
}
/**
* Create a named query.
* <p>
* For RawSql the named query is expected to be in ebean.xml.
* </p>
*
* @param beanType The type of entity bean
* @param namedQuery The name of the query
* @param <T> The type of entity bean
* @return The query
*/
public static <T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery) {
return serverMgr.getDefaultServer().createNamedQuery(beanType, namedQuery);
}
/**
* Create a query for a type of entity bean.
* <p>
@@ -960,39 +975,7 @@ public final class Ebean {
* {@link Query#findSet()} etc will execute against the same EbeanServer from
* which is was created.
* </p>
*
* <pre>{@code
* // Find order 2 additionally fetching the customer, details and details.product
* // name.
*
* Order order = Ebean.find(Order.class)
* .fetch("customer")
* .fetch("details")
* .fetch("detail.product", "name")
* .setId(2)
* .findUnique();
*
* // Find order 2 additionally fetching the customer, details and details.product
* // name.
* // Note: same query as above but using the query language
* // Note: using a named query would be preferred practice
*
* String oql = "find order fetch customer fetch details fetch details.product (name) where id = :orderId ";
*
* Query<Order> query = Ebean.find(Order.class);
* query.setQuery(oql);
* query.setParameter("orderId", 2);
*
* Order order = query.findUnique();
*
* // Using a named query
* Query<Order> query = Ebean.find(Order.class, "with.details");
* query.setParameter("orderId", 2);
*
* Order order = query.findUnique();
*
* }</pre>
*
*
* @param beanType
* the class of entity to be fetched
* @return A ORM Query object for this beanType
@@ -1002,6 +985,47 @@ public final class Ebean {
return serverMgr.getDefaultServer().createQuery(beanType);
}
/**
* Parse the Ebean query language statement returning the query which can then
* be modified (add expressions, change order by clause, change maxRows, change
* fetch and select paths etc).
*
* <h3>Example</h3>
*
* <pre>{@code
*
*
* // Find order additionally fetching the customer, details and details.product name.
*
* String eql = "fetch customer fetch details fetch details.product (name) where id = :orderId ";
*
* Query<Order> query = Ebean.createQuery(Order.class, eql);
* query.setParameter("orderId", 2);
*
* Order order = query.findUnique();
*
* // This is the same as:
*
* Order order = Ebean.find(Order.class)
* .fetch("customer")
* .fetch("details")
* .fetch("detail.product", "name")
* .setId(2)
* .findUnique();
*
* }</pre>
*
* @param beanType The type of bean to fetch
* @param eql The Ebean query
* @param <T> The type of the entity bean
*
* @return The query with expressions defined as per the parsed query statement
*/
public static <T> Query<T> createQuery(Class<T> beanType, String eql) {
return serverMgr.getDefaultServer().createQuery(beanType, eql);
}
/**
* Create a query for a type of entity bean.
* <p>
@@ -199,6 +199,19 @@ public interface EbeanServer {
*/
<T> UpdateQuery<T> update(Class<T> beanType);
/**
* Create a named query.
* <p>
* For RawSql the named query is expected to be in ebean.xml.
* </p>
*
* @param beanType The type of entity bean
* @param namedQuery The name of the query
* @param <T> The type of entity bean
* @return The query
*/
<T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery);
/**
* Create a query for an entity bean and synonym for {@link #find(Class)}.
*
@@ -206,6 +219,43 @@ public interface EbeanServer {
*/
<T> Query<T> createQuery(Class<T> beanType);
/**
* Parse the Ebean query language statement returning the query which can then
* be modified (add expressions, change order by clause, change maxRows, change
* fetch and select paths etc).
*
* <h3>Example</h3>
*
* <pre>{@code
*
* // Find order additionally fetching the customer, details and details.product name.
*
* String eql = "fetch customer fetch details fetch details.product (name) where id = :orderId ";
*
* Query<Order> query = Ebean.createQuery(Order.class, eql);
* query.setParameter("orderId", 2);
*
* Order order = query.findUnique();
*
* // This is the same as:
*
* Order order = Ebean.find(Order.class)
* .fetch("customer")
* .fetch("details")
* .fetch("detail.product", "name")
* .setId(2)
* .findUnique();
*
* }</pre>
*
* @param beanType The type of bean to fetch
* @param eql The Ebean query
* @param <T> The type of the entity bean
*
* @return The query with expressions defined as per the parsed query statement
*/
<T> Query<T> createQuery(Class<T> beanType, String eql);
/**
* Create a query for a type of entity bean.
* <p>
@@ -135,6 +135,11 @@ public interface ExpressionFactory {
*/
Expression ieq(String propertyName, String value);
/**
* Case Insensitive Equal To that allows for named parameter use.
*/
Expression ieqObject(String propertyName, Object value);
/**
* Between - property between the two given values.
*/
@@ -192,6 +197,11 @@ public interface ExpressionFactory {
*/
ExampleExpression exampleLike(Object example, boolean caseInsensitive, LikeType likeType);
/**
* Like with support for named parameters.
*/
Expression like(String propertyName, Object value, boolean caseInsensitive, LikeType likeType);
/**
* Like - property like value where the value contains the SQL wild card
* characters % (percentage) and _ (underscore).
+21 -11
View File
@@ -59,11 +59,11 @@ package com.avaje.ebean;
* .and()
* .startsWith("name", "r")
* .eq("anniversary", onAfter)
* .endJunction()
* .endAnd()
* .and()
* .eq("status", Customer.Status.ACTIVE)
* .gt("id", 0)
* .endJunction()
* .endAnd()
* .order().asc("name");
*
* q.findList();
@@ -84,39 +84,41 @@ public interface Junction<T> extends Expression, ExpressionList<T> {
/**
* AND group.
*/
AND(" and ", ""),
AND(" and ", "", false),
/**
* OR group.
*/
OR(" or ", ""),
OR(" or ", "", false),
/**
* NOT group.
*/
NOT(" and ", "not "),
NOT(" and ", "not ", false),
/**
* Text search AND group.
*/
MUST("must", ""),
MUST("must", "", true),
/**
* Text search NOT group.
*/
MUST_NOT("must_not", ""),
MUST_NOT("must_not", "", true),
/**
* Text search OR group.
*/
SHOULD("should", "");
SHOULD("should", "", true);
String prefix;
String literal;
private String prefix;
private String literal;
private boolean text;
Type(String literal, String prefix) {
Type(String literal, String prefix, boolean text) {
this.literal = literal;
this.prefix = prefix;
this.text = text;
}
/**
@@ -132,6 +134,14 @@ public interface Junction<T> extends Expression, ExpressionList<T> {
public String prefix() {
return prefix;
}
/**
* Return true if this is a text type.
*/
public boolean isText() {
return text;
}
}
}
+10 -2
View File
@@ -148,6 +148,13 @@ public final class OrderBy<T> implements Serializable {
return copy;
}
/**
* Add to the order by by parsing a raw expression.
*/
public void add(String rawExpression) {
parse(rawExpression);
}
/**
* Add a property to the order by.
*/
@@ -206,8 +213,9 @@ public final class OrderBy<T> implements Serializable {
* order by clause and replace.
* </p>
*/
public void clear() {
public OrderBy<T> clear() {
list.clear();
return this;
}
/**
@@ -400,6 +408,6 @@ public final class OrderBy<T> implements Serializable {
}
private boolean isEmptyString(String s) {
return s == null || s.length() == 0;
return s == null || s.isEmpty();
}
}
@@ -78,6 +78,6 @@ class PrimaryServer {
* Return true if the string is null or empty.
*/
private static boolean isEmpty(String value) {
return value == null || value.trim().length() == 0;
return value == null || value.trim().isEmpty();
}
}
+2 -2
View File
@@ -606,9 +606,9 @@ public final class RawSql implements Serializable {
}
}
private static String derivePropertyName(String dbAlias, String dbColumn) {
protected static String derivePropertyName(String dbAlias, String dbColumn) {
if (dbAlias != null) {
return dbAlias;
return CamelCaseHelper.toCamelFromUnderscore(dbAlias);
}
int dotPos = dbColumn.indexOf('.');
if (dotPos > -1) {
+2 -2
View File
@@ -26,7 +26,7 @@ package com.avaje.ebean;
*
* int modifiedCount = Ebean.execute(update);
*
* String msg = &quot;There where &quot; + modifiedCount + &quot;rows updated&quot;
* String msg = &quot;There were &quot; + modifiedCount + &quot; rows updated&quot;
* </pre>
*
* @see Update
@@ -143,4 +143,4 @@ public interface SqlUpdate {
*/
SqlUpdate setNullParameter(String name, int jdbcType);
}
}
+39 -22
View File
@@ -16,7 +16,7 @@ import java.util.ArrayList;
* This object is used internally with the enhancement of a method with
* Transactional annotation.
* </p>
*
*
* @see TxCallable
* @see TxRunnable
* @see Ebean#execute(TxScope, TxCallable)
@@ -44,27 +44,6 @@ public final class TxScope {
ArrayList<Class<? extends Throwable>> noRollbackFor;
/**
* Return true if PersistBatch has been set.
*/
public boolean isBatchSet() {
return batch != null && batch != PersistBatch.INHERIT;
}
/**
* Return true if batch on cascade has been set.
*/
public boolean isBatchOnCascadeSet() {
return batchOnCascade != null && batchOnCascade != PersistBatch.INHERIT;
}
/**
* Return true if batch size has been set.
*/
public boolean isBatchSizeSet() {
return batchSize > 0;
}
/**
* Helper method to create a TxScope with REQUIRES.
*/
@@ -130,6 +109,44 @@ public final class TxScope {
+ "] rollbackFor[" + rollbackFor + "] noRollbackFor[" + noRollbackFor + "]";
}
/**
* Return true if PersistBatch has been set.
*/
public boolean isBatchSet() {
return batch != null && batch != PersistBatch.INHERIT;
}
/**
* Return true if batch on cascade has been set.
*/
public boolean isBatchOnCascadeSet() {
return batchOnCascade != null && batchOnCascade != PersistBatch.INHERIT;
}
/**
* Return true if batch size has been set.
*/
public boolean isBatchSizeSet() {
return batchSize > 0;
}
/**
* Check for batchSize being set without batch mode and use this to imply PersistBatch.ALL.
*/
public void checkBatchMode() {
if (batchSize > 0 && notSet(batch) && notSet(batchOnCascade)) {
// Use setting the batchSize as implying PersistBatch.ALL for @Transactional
batch = PersistBatch.ALL;
}
}
/**
* Return true if the mode is considered not set.
*/
private boolean notSet(PersistBatch batchMode) {
return batchMode == null || batchMode == PersistBatch.INHERIT;
}
/**
* Return the transaction type.
*/
@@ -31,6 +31,11 @@ public interface BeanCollection<E> extends Serializable {
ALL
}
/**
* Set the disableLazyLoad state.
*/
void setDisableLazyLoad(boolean disableLazyLoad);
/**
* Load bean from another collection.
*/
@@ -20,6 +20,8 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
protected boolean readOnly;
protected boolean disableLazyLoad;
/**
* The EbeanServer this is associated with. (used for lazy fetch).
*/
@@ -84,6 +86,11 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
this.filterMany = filterMany;
}
@Override
public void setDisableLazyLoad(boolean disableLazyLoad) {
this.disableLazyLoad = disableLazyLoad;
}
protected void lazyLoadCollection(boolean onlyIds) {
if (loader == null) {
loader = (BeanCollectionLoader) Ebean.getServer(ebeanServerName);
@@ -102,7 +102,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
private void initClear() {
synchronized (this) {
if (list == null) {
if (modifyListening) {
if (!disableLazyLoad && modifyListening) {
lazyLoadCollection(true);
} else {
list = new ArrayList<E>();
@@ -114,7 +114,11 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
private void init() {
synchronized (this) {
if (list == null) {
lazyLoadCollection(false);
if (disableLazyLoad) {
list = new ArrayList<E>();
} else {
lazyLoadCollection(false);
}
}
}
}
@@ -274,7 +274,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
* Checks string is null or empty .
*/
protected boolean isEmpty(String s) {
return s == null || s.trim().length() == 0;
return s == null || s.trim().isEmpty();
}
/**
@@ -163,6 +163,17 @@ public class ServerConfig {
*/
private int databaseSequenceBatchSize = 20;
/**
* JDBC fetchSize hint when using findList. Defaults to 0 leaving it up to the JDBC driver.
*/
private int jdbcFetchSizeFindList;
/**
* JDBC fetchSize hint when using findEach/findEachWhile. Defaults to 100. Note that this does
* not apply to MySql as that gets special treatment (forward only etc).
*/
private int jdbcFetchSizeFindEach = 100;
/**
* Suffix appended to the base table to derive the view that contains the union
* of the base table and the history table in order to support asOf queries.
@@ -187,9 +198,11 @@ public class ServerConfig {
private PersistBatch persistBatch = PersistBatch.NONE;
/**
* Use for per request batch mode.
* Use for cascade persist JDBC batch mode. INHERIT means use the platform default
* which is ALL except for SQL Server where it is NONE (as getGeneratedKeys isn't
* supported on SQL Server with JDBC batch).
*/
private PersistBatch persistBatchOnCascade = PersistBatch.NONE;
private PersistBatch persistBatchOnCascade = PersistBatch.INHERIT;
private int persistBatchSize = 20;
@@ -307,7 +320,7 @@ public class ServerConfig {
/**
* Setting to indicate if UUID should be stored as binary(16) or varchar(40) or native DB type (for H2 and Postgres).
*/
private DbUuid dbUuid = DbUuid.AUTO;
private DbUuid dbUuid = DbUuid.AUTO_VARCHAR;
private List<IdGenerator> idGenerators = new ArrayList<IdGenerator>();
@@ -366,19 +379,13 @@ public class ServerConfig {
private boolean transactionRollbackOnChecked = true;
private boolean registerJmxMBeans = true;
// configuration for the background executor service (thread pool)
private int backgroundExecutorSchedulePoolSize = 1;
private int backgroundExecutorCorePoolSize = 1;
private int backgroundExecutorMaxPoolSize = 8;
private int backgroundExecutorIdleSecs = 60;
private int backgroundExecutorShutdownSecs = 30;
// defaults for the L2 bean caching
private int cacheWarmingDelay = 30;
private int cacheMaxSize = 10000;
private int cacheMaxIdleTime = 600;
private int cacheMaxTimeToLive = 60 * 60 * 6;
@@ -396,7 +403,7 @@ public class ServerConfig {
private boolean expressionEqualsWithNullAsNoop;
/**
* Set to true to use native ILIKE expression (if support by datasbase platform / like Postgres).
* Set to true to use native ILIKE expression (if support by database platform / like Postgres).
*/
private boolean expressionNativeIlike;
@@ -719,6 +726,34 @@ public class ServerConfig {
this.databaseSequenceBatchSize = databaseSequenceBatchSize;
}
/**
* Return the default JDBC fetchSize hint for findList queries.
*/
public int getJdbcFetchSizeFindList() {
return jdbcFetchSizeFindList;
}
/**
* Set the default JDBC fetchSize hint for findList queries.
*/
public void setJdbcFetchSizeFindList(int jdbcFetchSizeFindList) {
this.jdbcFetchSizeFindList = jdbcFetchSizeFindList;
}
/**
* Return the default JDBC fetchSize hint for findEach/findEachWhile queries.
*/
public int getJdbcFetchSizeFindEach() {
return jdbcFetchSizeFindEach;
}
/**
* Set the default JDBC fetchSize hint for findEach/findEachWhile queries.
*/
public void setJdbcFetchSizeFindEach(int jdbcFetchSizeFindEach) {
this.jdbcFetchSizeFindEach = jdbcFetchSizeFindEach;
}
/**
* Return the ChangeLogPrepare.
* <p>
@@ -1001,20 +1036,6 @@ public class ServerConfig {
this.transactionRollbackOnChecked = transactionRollbackOnChecked;
}
/**
* Return true if the server should register JMX MBeans.
*/
public boolean isRegisterJmxMBeans() {
return registerJmxMBeans;
}
/**
* Set if the server should register JMX MBeans.
*/
public void setRegisterJmxMBeans(boolean registerJmxMBeans) {
this.registerJmxMBeans = registerJmxMBeans;
}
/**
* Return the Background executor schedule pool size. Defaults to 1.
*/
@@ -1029,48 +1050,6 @@ public class ServerConfig {
this.backgroundExecutorSchedulePoolSize = backgroundExecutorSchedulePoolSize;
}
/**
* Return the Background executor core pool size.
*/
public int getBackgroundExecutorCorePoolSize() {
return backgroundExecutorCorePoolSize;
}
/**
* Set the Background executor core pool size.
*/
public void setBackgroundExecutorCorePoolSize(int backgroundExecutorCorePoolSize) {
this.backgroundExecutorCorePoolSize = backgroundExecutorCorePoolSize;
}
/**
* Return the Background executor max pool size.
*/
public int getBackgroundExecutorMaxPoolSize() {
return backgroundExecutorMaxPoolSize;
}
/**
* Set the Background executor max pool size.
*/
public void setBackgroundExecutorMaxPoolSize(int backgroundExecutorMaxPoolSize) {
this.backgroundExecutorMaxPoolSize = backgroundExecutorMaxPoolSize;
}
/**
* Return the Background executor idle seconds.
*/
public int getBackgroundExecutorIdleSecs() {
return backgroundExecutorIdleSecs;
}
/**
* Set the Background executor idle seconds.
*/
public void setBackgroundExecutorIdleSecs(int backgroundExecutorIdleSecs) {
this.backgroundExecutorIdleSecs = backgroundExecutorIdleSecs;
}
/**
* Return the Background executor shutdown seconds. This is the time allowed for the pool to shutdown nicely
* before it is forced shutdown.
@@ -1087,20 +1066,6 @@ public class ServerConfig {
this.backgroundExecutorShutdownSecs = backgroundExecutorShutdownSecs;
}
/**
* Return the cache warming delay in seconds.
*/
public int getCacheWarmingDelay() {
return cacheWarmingDelay;
}
/**
* Set the cache warming delay in seconds.
*/
public void setCacheWarmingDelay(int cacheWarmingDelay) {
this.cacheWarmingDelay = cacheWarmingDelay;
}
/**
* Return the L2 cache default max size.
*/
@@ -2358,6 +2323,8 @@ public class ServerConfig {
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
backgroundExecutorSchedulePoolSize = p.getInt("backgroundExecutorSchedulePoolSize", backgroundExecutorSchedulePoolSize);
backgroundExecutorShutdownSecs = p.getInt("backgroundExecutorShutdownSecs", backgroundExecutorShutdownSecs);
disableClasspathSearch = p.getBoolean("disableClasspathSearch", disableClasspathSearch);
currentUserProvider = createInstance(p, CurrentUserProvider.class, "currentUserProvider", currentUserProvider);
databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform", databasePlatform);
@@ -2367,7 +2334,6 @@ public class ServerConfig {
dbEncrypt = createInstance(p, DbEncrypt.class, "dbEncrypt", dbEncrypt);
serverCachePlugin = createInstance(p, ServerCachePlugin.class, "serverCachePlugin", serverCachePlugin);
serverCacheManager = createInstance(p, ServerCacheManager.class, "serverCacheManager", serverCacheManager);
cacheWarmingDelay = p.getInt("cacheWarmingDelay", cacheWarmingDelay);
if (packages != null) {
String packagesProp = p.get("search.packages", p.get("packages", null));
@@ -2405,6 +2371,8 @@ public class ServerConfig {
asOfSysPeriod = p.get("asOfSysPeriod", asOfSysPeriod);
historyTableSuffix = p.get("historyTableSuffix", historyTableSuffix);
dataSourceJndiName = p.get("dataSourceJndiName", dataSourceJndiName);
jdbcFetchSizeFindEach = p.getInt("jdbcFetchSizeFindEach", jdbcFetchSizeFindEach);
jdbcFetchSizeFindList = p.getInt("jdbcFetchSizeFindList", jdbcFetchSizeFindList);
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize);
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
@@ -2460,7 +2428,7 @@ public class ServerConfig {
String[] split = classNames.split("[ ,;]");
for (int i = 0; i < split.length; i++) {
String cn = split[i].trim();
if (cn.length() > 0 && !"class".equalsIgnoreCase(cn)) {
if (!cn.isEmpty() && !"class".equalsIgnoreCase(cn)) {
try {
classes.add(Class.forName(cn));
} catch (ClassNotFoundException e) {
@@ -2489,13 +2457,14 @@ public class ServerConfig {
/**
* Return the PersistBatch mode to use for 'batchOnCascade' taking into account if the database
* platform supports getGeneratedKeys in batch mode.
* <p>
* Used to effectively turn off batchOnCascade for SQL Server - still allows explicit batch mode.
* </p>
*/
public PersistBatch appliedPersistBatchOnCascade() {
return databasePlatform.isDisallowBatchOnCascade() ? PersistBatch.NONE : persistBatchOnCascade;
if (persistBatchOnCascade == PersistBatch.INHERIT) {
// use the platform default (ALL except SQL Server which has NONE)
return databasePlatform.getPersistBatchOnCascade();
}
return persistBatchOnCascade;
}
/**
@@ -2571,18 +2540,45 @@ public class ServerConfig {
public enum DbUuid {
/**
* Store using native UUID in H2 and Postgres.
* Store using native UUID in H2 and Postgres and otherwise fallback to VARCHAR(40).
*/
AUTO,
AUTO_VARCHAR(true, false),
/**
* Store using DB VARCHAR.
* Store using native UUID in H2 and Postgres and otherwise fallback to BINARY(16).
*/
VARCHAR,
AUTO_BINARY(true, true),
/**
* Store using DB BINARY.
* Store using DB VARCHAR(40).
*/
BINARY
VARCHAR(false, false),
/**
* Store using DB BINARY(16).
*/
BINARY(false, true);
boolean nativeType;
boolean binary;
DbUuid(boolean nativeType, boolean binary) {
this.nativeType = nativeType;
this.binary = binary;
}
/**
* Return true if native UUID type is preferred.
*/
public boolean useNativeType() {
return nativeType;
}
/**
* Return true if BINARY(16) storage is preferred over VARCHAR(40).
*/
public boolean useBinary() {
return binary;
}
}
}
@@ -146,6 +146,6 @@ public final class TableName {
* @return true, if is valid
*/
public boolean isValid() {
return name != null && name.length() > 0;
return name != null && !name.isEmpty();
}
}
@@ -2,6 +2,7 @@ package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.Query;
import com.avaje.ebean.config.PersistBatch;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
@@ -155,11 +156,9 @@ public class DatabasePlatform {
protected boolean forwardOnlyHintOnFindIterate;
/**
* Flag set for SQL Server due to lack of support of getGeneratedKeys in
* batch mode (meaning for batch inserts you should explicitly turn off
* getGeneratedKeys - joy).
* By default we use JDBC batch when cascading (except for SQL Server).
*/
protected boolean disallowBatchOnCascade;
protected PersistBatch persistBatchOnCascade = PersistBatch.ALL;
protected PlatformDdl platformDdl;
@@ -183,8 +182,11 @@ public class DatabasePlatform {
public DatabasePlatform() {
}
public void configure(Properties properties) {
// by default do nothing
/**
* Configure UUID Storage etc based on ServerConfig settings.
*/
public void configure(ServerConfig serverConfig) {
dbTypeMap.config(nativeUuidType, serverConfig.getDbUuid());
}
/**
@@ -492,7 +494,7 @@ public class DatabasePlatform {
*/
public String convertQuotedIdentifiers(String dbName) {
// Ignore null values e.g. schema name or catalog
if (dbName != null && dbName.length() > 0) {
if (dbName != null && !dbName.isEmpty()) {
if (dbName.charAt(0) == BACK_TICK) {
if (dbName.charAt(dbName.length() - 1) == BACK_TICK) {
@@ -542,14 +544,10 @@ public class DatabasePlatform {
}
/**
* Return true if the persistBatchOnCascade setting should be ignored.
* <p>
* This is primarily for SQL Server which does not support getGeneratedKeys with jdbc batch mode
* so can't really be transparently used.
* </p>
* Return the platform default JDBC batch mode for persist cascade.
*/
public boolean isDisallowBatchOnCascade() {
return disallowBatchOnCascade;
public PersistBatch getPersistBatchOnCascade() {
return persistBatchOnCascade;
}
/**
@@ -6,11 +6,13 @@ package com.avaje.ebean.config.dbplatform;
public interface DbHistorySupport {
/**
* Return true if the 'As of' predicate is part of the from clause
* (more standard sql2011). So true for Oracle total recall and false
* for Postgres and MySql (where we use views and history tables).
* Return true if the implementation is SQL2011 standards based.
* <p>
* Non standards based means we need to add additional predicates into the
* JOIN ON clause and add an additional predicate for the base table.
* </p>
*/
boolean isBindWithFromClause();
boolean isStandardsBased();
/**
* Return the number of columns bound in a 'As Of' predicate.
@@ -5,11 +5,8 @@ package com.avaje.ebean.config.dbplatform;
*/
public abstract class DbStandardHistorySupport implements DbHistorySupport {
/**
* Return true as with sql2011 the 'as of timestamp' clause included in from or join clause.
*/
@Override
public boolean isBindWithFromClause() {
public boolean isStandardsBased() {
return true;
}
@@ -152,4 +152,10 @@ public class DbType {
return sb.toString();
}
/**
* Create a copy of the type with a new default length.
*/
public DbType withLength(int defaultLength) {
return new DbType(name, defaultLength);
}
}
@@ -1,5 +1,7 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.config.ServerConfig;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;
@@ -9,6 +11,8 @@ import java.util.Map;
*/
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");
@@ -66,7 +70,7 @@ public class DbTypeMap {
/**
* 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.
*/
@@ -104,8 +108,6 @@ public class DbTypeMap {
put(Types.BLOB, new DbType("blob"));
put(Types.CLOB, new DbType("clob"));
// DB native UUID support (H2 and Postgres)
put(DbType.UUID, new DbType("uuid"));
put(Types.ARRAY, new DbType("array"));
if (logicalTypes) {
@@ -116,6 +118,7 @@ public class DbTypeMap {
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
@@ -123,6 +126,7 @@ public class DbTypeMap {
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"));
@@ -133,7 +137,6 @@ public class DbTypeMap {
put(Types.DATE, new DbType("date"));
put(Types.TIME, new DbType("time"));
put(Types.TIMESTAMP, new DbType("timestamp"));
}
/**
@@ -191,4 +194,17 @@ public class DbTypeMap {
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));
}
}
}
@@ -9,11 +9,8 @@ package com.avaje.ebean.config.dbplatform;
*/
public abstract class DbViewHistorySupport implements DbHistorySupport {
/**
* Return false for view based implementations where we append extra 'as of' predicates to the end.
*/
@Override
public boolean isBindWithFromClause() {
public boolean isStandardsBased() {
return false;
}
@@ -9,9 +9,7 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* H2 database trigger used to populate history tables to support the @History feature.
@@ -54,7 +52,6 @@ public class H2HistoryTrigger implements Trigger {
insertSql.append("insert into ").append(tableName).append(HISTORY_SUFFIX).append(" (");
int count = 0;
List<String> columns = new ArrayList<String>();
while (rs.next()) {
if (++count > 1) {
insertSql.append(",");
@@ -66,7 +63,6 @@ public class H2HistoryTrigger implements Trigger {
this.effectEndPosition = count - 1;
}
insertSql.append(columnName);
columns.add(columnName);
}
insertSql.append(") values (");
for (int i = 0; i < count; i++) {
@@ -1,9 +1,11 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.H2Ddl;
import javax.sql.DataSource;
import java.util.Properties;
/**
* H2 specific platform.
@@ -19,9 +21,7 @@ public class H2Platform extends DatabasePlatform {
this.nativeUuidType = true;
this.dbDefaultValue.setNow("now()");
// only support getGeneratedKeys with non-batch JDBC
// so generally use SEQUENCE instead of IDENTITY for H2
this.dbIdentity.setIdType(IdType.SEQUENCE);
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
this.dbIdentity.setSupportsIdentity(true);
@@ -34,6 +34,18 @@ public class H2Platform extends DatabasePlatform {
// so no changes to dbTypeMap required
}
@Override
public void configure(ServerConfig serverConfig) {
super.configure(serverConfig);
Properties properties = serverConfig.getProperties();
if (properties != null) {
String idType = properties.getProperty("ebean.h2.idtype");
if (idType != null) {
this.dbIdentity.setIdType(IdType.valueOf(idType));
}
}
}
/**
* Return a H2 specific sequence IdGenerator that supports batch fetching
* sequence values.
@@ -1,5 +1,7 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.config.PersistBatch;
import java.sql.Types;
/**
@@ -18,6 +20,7 @@ public class MsSqlServer2000Platform extends DatabasePlatform {
public MsSqlServer2000Platform() {
super();
this.name = "mssqlserver2000";
this.persistBatchOnCascade = PersistBatch.NONE;
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(false);
this.dbIdentity.setSelectLastInsertedIdTemplate("select @@IDENTITY as X");
@@ -1,5 +1,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;
@@ -21,7 +22,7 @@ public class MsSqlServer2005Platform extends DatabasePlatform {
this.name = "mssqlserver2005";
// effectively disable persistBatchOnCascade mode for SQL Server
// due to lack of support for getGeneratedKeys in batch mode
this.disallowBatchOnCascade = true;
this.persistBatchOnCascade = PersistBatch.NONE;
this.idInExpandedForm = true;
this.selectCountWithAlias = true;
this.sqlLimiter = new MsSqlServer2005SqlLimiter();
@@ -64,12 +64,15 @@ public class PostgresPlatform extends DatabasePlatform {
}
@Override
public void configure(Properties properties) {
super.configure(properties);
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));
public void configure(ServerConfig serverConfig) {
super.configure(serverConfig);
Properties properties = serverConfig.getProperties();
if (properties != null) {
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));
}
}
}
@@ -164,9 +164,6 @@ public class DbMigration {
* </p>
*/
public void addPlatform(DbPlatformName platform, String prefix) {
if (!prefix.endsWith("-")) {
prefix += "-";
}
platforms.add(new Pair(getPlatform(platform), prefix));
}
@@ -204,13 +201,20 @@ public class DbMigration {
// use this flag to stop other plugins like full DDL generation
if (!online) {
DbOffline.setRunningMigration();
DbOffline.setGenerateMigration();
if (databasePlatform == null || !platforms.isEmpty()) {
// for multiple platform generation set the general platform
// to H2 so that it runs offline without DB connection
setPlatform(DbPlatformName.H2);
}
}
setDefaults();
try {
Request request = createRequest();
generateExtraDdl(request);
if (platforms.isEmpty()) {
generateExtraDdl(request.migrationDir, databasePlatform);
}
String pendingVersion = generatePendingDrop();
if (pendingVersion != null) {
@@ -234,15 +238,15 @@ public class DbMigration {
* migration runner.
* </p>
*/
private void generateExtraDdl(Request request) throws IOException {
private void generateExtraDdl(File migrationDir, DatabasePlatform dbPlatform) throws IOException {
if (databasePlatform != null) {
if (dbPlatform != null) {
ExtraDdl extraDdl = ExtraDdlXmlReader.read("/extra-ddl.xml");
if (extraDdl != null) {
List<DdlScript> ddlScript = extraDdl.getDdlScript();
for (DdlScript script : ddlScript) {
if (ExtraDdlXmlReader.matchPlatform(databasePlatform.getName(), script.getPlatforms())) {
writeExtraDdl(request, script);
if (ExtraDdlXmlReader.matchPlatform(dbPlatform.getName(), script.getPlatforms())) {
writeExtraDdl(migrationDir, script);
}
}
}
@@ -252,13 +256,13 @@ public class DbMigration {
/**
* Write (or override) the "repeatable" migration script.
*/
private void writeExtraDdl(Request request, DdlScript script) throws IOException {
private void writeExtraDdl(File migrationDir, DdlScript script) throws IOException {
String fullName = repeatableMigrationName(script.getName());
logger.info("writing repeatable script {}", fullName);
File file = new File(request.migrationDir, fullName);
File file = new File(migrationDir, fullName);
FileWriter writer = new FileWriter(file);
writer.write(script.getValue());
writer.flush();
@@ -363,14 +367,16 @@ public class DbMigration {
logger.warn("migration already exists, not generating DDL");
} else {
if (databasePlatform != null) {
if (!platforms.isEmpty()) {
writeExtraPlatformDdl(fullVersion, request.currentModel, dbMigration, request.migrationDir);
} else if (databasePlatform != null) {
// writer needs the current model to provide table/column details for
// history ddl generation (triggers, history tables etc)
DdlWrite write = new DdlWrite(new MConfiguration(), request.current);
PlatformDdlWriter writer = createDdlWriter(databasePlatform, "");
writer.processMigration(dbMigration, write, request.migrationDir, fullVersion);
}
writeExtraPlatformDdl(fullVersion, request.currentModel, dbMigration, request.migrationDir);
}
}
@@ -426,7 +432,10 @@ public class DbMigration {
for (Pair pair : platforms) {
DdlWrite platformBuffer = new DdlWrite(new MConfiguration(), currentModel.read());
PlatformDdlWriter platformWriter = createDdlWriter(pair);
platformWriter.processMigration(dbMigration, platformBuffer, writePath, fullVersion);
File subPath = platformWriter.subPath(writePath, pair.prefix);
platformWriter.processMigration(dbMigration, platformBuffer, subPath, fullVersion);
generateExtraDdl(subPath, pair.platform);
}
}
@@ -14,7 +14,7 @@ public class DbOffline {
private static final String KEY = "ebean.dboffline";
private static boolean runningMigration;
private static boolean generateMigration;
/**
* Set the platform to use when creating the next EbeanServer instance.
@@ -55,23 +55,23 @@ public class DbOffline {
* Return true if the migration is running. This typically means don't run the
* plugins like full DDL generation.
*/
public static boolean isRunningMigration() {
return runningMigration;
public static boolean isGenerateMigration() {
return generateMigration;
}
/**
* Called when the migration is running is order to stop other plugins
* like the full DDL generation from executing.
*/
public static void setRunningMigration() {
runningMigration = true;
public static void setGenerateMigration() {
generateMigration = true;
}
/**
* Reset the offline platform and runningMigration flag.
*/
public static void reset() {
runningMigration = false;
generateMigration = false;
System.clearProperty(KEY);
logger.debug("reset");
}
@@ -96,7 +96,13 @@ public class DdlGenerator {
Transaction transaction = server.createTransaction();
Connection connection = transaction.getConnection();
try {
if (expectErrors) {
connection.setAutoCommit(true);
}
int count = runner.runAll(content, connection);
if (expectErrors) {
connection.setAutoCommit(false);
}
transaction.commit();
return count;
@@ -583,6 +583,12 @@ public class BaseTableDdl implements TableDdl {
}
}
for (Column column : columns) {
if (hasValue(column.getReferences())) {
writeForeignKey(writer, tableName, column);
}
}
// add a bit of whitespace
writer.apply().end();
}
@@ -831,9 +837,10 @@ public class BaseTableDdl implements TableDdl {
protected void alterTableAddColumn(DdlBuffer buffer, String tableName, Column column, boolean onHistoryTable) throws IOException {
String convertedType = platformDdl.convert(column.getType(), false);
buffer.append("alter table ").append(tableName)
.append(" add column ").append(column.getName())
.append(" ").append(column.getType());
.append(" ").append(convertedType);
if (!onHistoryTable) {
if (isTrue(column.isNotnull())) {
@@ -26,6 +26,8 @@ import java.util.List;
*/
public class PlatformDdl {
protected final DatabasePlatform platform;
protected PlatformHistoryDdl historyDdl = new NoHistorySupportDdl();
/**
@@ -98,6 +100,7 @@ public class PlatformDdl {
protected final DbDefaultValue dbDefaultValue;
public PlatformDdl(DatabasePlatform platform) {
this.platform = platform;
this.dbIdentity = platform.getDbIdentity();
this.dbDefaultValue = platform.getDbDefaultValue();
this.typeConverter = new PlatformTypeConverter(platform.getDbTypeMap());
@@ -107,6 +110,7 @@ public class PlatformDdl {
* Set configuration options.
*/
public void configure(ServerConfig serverConfig) {
platform.configure(serverConfig);
historyDdl.configure(serverConfig, this);
naming = serverConfig.getConstraintNaming();
}
@@ -11,7 +11,6 @@ import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import java.io.IOException;
import java.util.List;
/**
* Reads EbeanServer bean descriptors to build the current model.
@@ -24,6 +23,8 @@ public class CurrentModel {
private final DbConstraintNaming.MaxLength maxLength;
private final boolean platformTypes;
private ModelContainer model;
private ChangeSet changeSet;
@@ -31,32 +32,31 @@ public class CurrentModel {
private DdlWrite write;
/**
* Construct with a given EbeanServer instance.
* Construct with a given EbeanServer instance for DDL create all generation, not migration.
*/
public CurrentModel(SpiEbeanServer server) {
this(server, server.getServerConfig().getConstraintNaming());
this(server, server.getServerConfig().getConstraintNaming(), true);
}
/**
* Construct with a given EbeanServer, platformDdl and constraintNaming convention.
* <p>
* Note the EbeanServer is just used to read the BeanDescriptors and platformDdl supplies
* the platform specific handling on
* Note the EbeanServer is just used to read the BeanDescriptors and platformDdl supplies
* the platform specific handling on
* </p>
*/
public CurrentModel(SpiEbeanServer server, DbConstraintNaming constraintNaming) {
this(server, constraintNaming, false);
}
private CurrentModel(SpiEbeanServer server, DbConstraintNaming constraintNaming, boolean platformTypes) {
this.server = server;
this.constraintNaming = constraintNaming;
this.maxLength = maxLength(server, constraintNaming);
this.platformTypes = platformTypes;
}
public CurrentModel(SpiEbeanServer server, DbConstraintNaming constraintNaming, int maxConstraintLength) {
this.server = server;
this.constraintNaming = constraintNaming;
this.maxLength = new DefaultConstraintMaxLength(maxConstraintLength);
}
private DbConstraintNaming.MaxLength maxLength(SpiEbeanServer server, DbConstraintNaming naming) {
private static DbConstraintNaming.MaxLength maxLength(SpiEbeanServer server, DbConstraintNaming naming) {
if (naming.getMaxLength() != null) {
return naming.getMaxLength();
@@ -73,7 +73,7 @@ public class CurrentModel {
if (model == null) {
model = new ModelContainer();
ModelBuildContext context = new ModelBuildContext(model, constraintNaming, maxLength);
ModelBuildContext context = new ModelBuildContext(model, constraintNaming, maxLength, platformTypes);
ModelBuildBeanVisitor visitor = new ModelBuildBeanVisitor(context);
VisitAllUsing visit = new VisitAllUsing(visitor, server);
visit.visitAllBeans();
@@ -104,7 +104,7 @@ public class MTable {
/**
* Compound unique constraints.
*/
private List<MCompoundUniqueConstraint> compoundUniqueConstraints = new ArrayList<MCompoundUniqueConstraint>();
private List<MCompoundUniqueConstraint> uniqueConstraints = new ArrayList<MCompoundUniqueConstraint>();
/**
* Compound foreign keys.
@@ -216,7 +216,7 @@ public class MTable {
createTable.getForeignKey().add(compoundKey.createForeignKey());
}
for (MCompoundUniqueConstraint constraint : compoundUniqueConstraints) {
for (MCompoundUniqueConstraint constraint : uniqueConstraints) {
UniqueConstraint uq = new UniqueConstraint();
uq.setName(constraint.getName());
String[] columns = constraint.getColumns();
@@ -404,8 +404,8 @@ public class MTable {
return columns;
}
public List<MCompoundUniqueConstraint> getCompoundUniqueConstraints() {
return compoundUniqueConstraints;
public List<MCompoundUniqueConstraint> getUniqueConstraints() {
return uniqueConstraints;
}
public List<MCompoundForeignKey> getCompoundKeys() {
@@ -476,21 +476,21 @@ public class MTable {
}
/**
* Add a compound unique constraint.
* Add a unique constraint.
*/
public void addCompoundUniqueConstraint(String[] columns, boolean oneToOne, String constraintName) {
compoundUniqueConstraints.add(new MCompoundUniqueConstraint(columns, oneToOne, constraintName));
public void addUniqueConstraint(String[] columns, boolean oneToOne, String constraintName) {
uniqueConstraints.add(new MCompoundUniqueConstraint(columns, oneToOne, constraintName));
}
/**
* Add a compound unique constraint.
* Add a unique constraint.
*/
public void addCompoundUniqueConstraint(List<MColumn> columns, boolean oneToOne, String constraintName) {
public void addUniqueConstraint(List<MColumn> columns, boolean oneToOne, String constraintName) {
String[] cols = new String[columns.size()];
for (int i = 0; i < columns.size(); i++) {
cols[i] = columns.get(i).getName();
}
addCompoundUniqueConstraint(cols, oneToOne, constraintName);
addUniqueConstraint(cols, oneToOne, constraintName);
}
/**
@@ -9,6 +9,8 @@ import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
import com.avaje.ebean.dbmigration.migration.ChangeSet;
import com.avaje.ebean.dbmigration.migration.ChangeSetType;
import com.avaje.ebean.dbmigration.migration.Migration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileWriter;
@@ -21,6 +23,8 @@ import java.util.List;
*/
public class PlatformDdlWriter {
private static final Logger logger = LoggerFactory.getLogger(PlatformDdlWriter.class);
private final ServerConfig serverConfig;
private final DatabasePlatform platform;
@@ -67,7 +71,7 @@ public class PlatformDdlWriter {
protected void writePlatformDdl(DdlWrite write, File resourcePath, String fullVersion) throws IOException {
if (!write.isApplyEmpty()) {
FileWriter applyWriter = createWriter(resourcePath, fullVersion, "", config.getApplySuffix());
FileWriter applyWriter = createWriter(resourcePath, fullVersion, config.getApplySuffix());
try {
writeApplyDdl(applyWriter, write);
applyWriter.flush();
@@ -77,28 +81,12 @@ public class PlatformDdlWriter {
}
}
protected FileWriter createWriter(File path, String fullVersion, String subPath, String suffix) throws IOException {
protected FileWriter createWriter(File path, String fullVersion, String suffix) throws IOException {
String fileName = fullVersion;
if (!platformPrefix.isEmpty()) {
fileName += "-"+platformPrefix;
}
if (subPath != null && !subPath.isEmpty()) {
path = subPath(path, subPath);
}
fileName += suffix;
File applyFile = new File(path, fileName);
File applyFile = new File(path, fullVersion + suffix);
return new FileWriter(applyFile);
}
protected File subPath(File path, String suffix) {
File subPath = new File(path, suffix);
if (!subPath.exists()) {
subPath.mkdirs();
}
return subPath;
}
/**
* Write the 'Apply' DDL buffers to the writer.
*/
@@ -127,4 +115,17 @@ public class PlatformDdlWriter {
return platform.createDdlHandler(serverConfig);
}
/**
* Return a sub directory (for multi-platform ddl generation).
*/
public File subPath(File path, String suffix) {
File subPath = new File(path, suffix);
if (!subPath.exists()) {
if (!subPath.mkdirs()) {
logger.error("failed to create directories for " + subPath.getAbsolutePath());
}
}
return subPath;
}
}
@@ -29,10 +29,13 @@ public class ModelBuildContext {
private final DbConstraintNaming.MaxLength maxLength;
public ModelBuildContext(ModelContainer model, DbConstraintNaming naming, DbConstraintNaming.MaxLength maxLength) {
private final boolean platformTypes;
public ModelBuildContext(ModelContainer model, DbConstraintNaming naming, DbConstraintNaming.MaxLength maxLength, boolean platformTypes) {
this.model = model;
this.constraintNaming = naming;
this.maxLength = maxLength;
this.platformTypes = platformTypes;
}
/**
@@ -132,7 +135,7 @@ public class ModelBuildContext {
}
// can be the logical JSON types (JSON, JSONB, JSONClob, JSONBlob, JSONVarchar)
int dbType = p.getDbType();
int dbType = p.getDbType(platformTypes);
if (dbType == 0) {
throw new RuntimeException("No scalarType defined for " + p.getFullBeanName());
}
@@ -85,6 +85,11 @@ public class ModelBuildIntersectionTable {
String tableName = intersectionTableJoin.getTable();
MTable table = new MTable(tableName);
if (!manyProp.isExcludedFromHistory()) {
if (localDesc.isHistorySupport()) {
table.setWithHistory(true);
}
}
table.setPkName(ctx.primaryKeyName(tableName));
TableJoinColumn[] columns = intersectionTableJoin.columns();
@@ -10,7 +10,7 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueConstraint;
import com.avaje.ebeaninternal.server.deploy.IndexDefinition;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.deploy.TableJoinColumn;
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
@@ -45,30 +45,30 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
this.ctx = ctx;
this.table = table;
this.beanDescriptor = beanDescriptor;
addCompoundUniqueConstraint(beanDescriptor.getCompoundUniqueConstraints());
addIndexes(beanDescriptor.getIndexDefinitions());
}
/**
* Add unique constraints defined via JPA UniqueConstraint annotations.
*/
private void addCompoundUniqueConstraint(CompoundUniqueConstraint[] constraints) {
private void addIndexes(IndexDefinition[] indexes) {
if (constraints != null) {
for (int i = 0; i < constraints.length; i++) {
CompoundUniqueConstraint constraint = constraints[i];
String[] columns = constraint.getColumns();
if (indexes != null) {
for (int i = 0; i < indexes.length; i++) {
IndexDefinition index = indexes[i];
String[] columns = index.getColumns();
indexSet.add(columns);
if (constraint.isUnique()) {
String uqName = constraint.getName();
if (index.isUnique()) {
String uqName = index.getName();
if (uqName == null || uqName.trim().isEmpty()) {
uqName = determineUniqueConstraintName(columns);
}
table.addCompoundUniqueConstraint(columns, false, uqName);
table.addUniqueConstraint(columns, false, uqName);
} else {
// 'just' an index (not a unique constraint)
String idxName = constraint.getName();
String idxName = index.getName();
if (idxName == null || idxName.trim().isEmpty()) {
idxName = determineIndexName(columns);
}
@@ -216,7 +216,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
} else {
String uqName = determineUniqueConstraintName(p.getName());
table.addCompoundUniqueConstraint(modelColumns, true, uqName);
table.addUniqueConstraint(modelColumns, true, uqName);
indexSetAdd(modelColumns);
}
}
@@ -260,15 +260,6 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
col.setCheckConstraintName(determineCheckConstraintName(col.getName()));
}
String indexName = p.getIndexName();
if (indexName != null) {
// single column non-unique index
if (indexName.trim().isEmpty()) {
indexName = determineIndexName(col.getName());
}
ctx.addIndex(indexName, table.getName(), p.getDbColumn());
}
lastColumn = col;
table.addColumn(col);
}
@@ -109,7 +109,7 @@ public class PathProperties implements FetchPath {
String path = entry.getKey();
String props = entry.getValue().getPropertiesAsString();
if (path == null || path.length() == 0) {
if (path == null || path.isEmpty()) {
query.select(props);
} else {
query.fetch(path, props);
@@ -108,7 +108,7 @@ class PathPropertiesParser {
private void addCurrentProperty() {
String w = currentWord();
if (w.length() > 0) {
if (!w.isEmpty()) {
currentPathProps.addProperty(w);
}
}
@@ -23,7 +23,7 @@ public final class TimeStringParser implements StringParser {
*/
@SuppressWarnings("deprecation")
public Object parse(String value) {
if (value == null || value.trim().length() == 0) {
if (value == null || value.trim().isEmpty()) {
return null;
}
@@ -12,9 +12,12 @@ public class CamelCaseHelper {
*/
public static String toCamelFromUnderscore(String underscore) {
StringBuilder result = new StringBuilder();
String[] vals = underscore.split("_");
if (vals.length == 1) {
return underscore;
}
StringBuilder result = new StringBuilder();
for (int i = 0; i < vals.length; i++) {
String lower = vals[i].toLowerCase();
if (i > 0) {
@@ -123,7 +123,7 @@ public class StringHelper {
String listDelimiter, String nameValueSeparator) {
HashMap<String, String> params = new HashMap<String, String>();
if ((allNameValuePairs == null) || (allNameValuePairs.length() == 0)) {
if ((allNameValuePairs == null) || (allNameValuePairs.isEmpty())) {
return params;
}
// trim off any leading listDelimiter...
@@ -155,7 +155,7 @@ public class StringHelper {
* Return true if the value is null or an empty string.
*/
public static boolean isNull(String value) {
return value == null || value.trim().length() == 0;
return value == null || value.trim().isEmpty();
}
/**
@@ -189,7 +189,7 @@ public class StringHelper {
// there is a key without a value?
String key = allNameValuePairs.substring(pos, delimPos);
key = key.trim();
if (key.length() > 0) {
if (!key.isEmpty()) {
map.put(key, null);
}
return getKeyValue(map, delimPos + 1, allNameValuePairs, listDelimiter,
@@ -251,7 +251,7 @@ public class StringHelper {
if (endPos == -1) {
if (startPos <= str.length()) {
String lastValue = str.substring(startPos, str.length());
if (keepEmpties || lastValue.length() != 0) {
if (keepEmpties || !lastValue.isEmpty()) {
list.add(lastValue);
}
}
@@ -260,7 +260,7 @@ public class StringHelper {
} else {
// get the delimited value... add it..
String value = str.substring(startPos, endPos);
if (keepEmpties || value.length() != 0) {
if (keepEmpties || !value.isEmpty()) {
list.add(value);
}
// recursively search as we are not at the end yet...
@@ -398,7 +398,7 @@ public class StringHelper {
int additionalSize, int startPos, int endPos) {
if (source == null) {
return source;
return null;
}
char match0 = match.charAt(0);
@@ -13,7 +13,12 @@ import java.io.IOException;
*/
public interface SpiExpression extends Expression {
/**
/**
* Simplify nested expressions if possible.
*/
void simplify();
/**
* Write the expression as an elastic search expression.
*/
void writeDocQuery(DocQueryContext context) throws IOException;
@@ -0,0 +1,6 @@
package com.avaje.ebeaninternal.api;
public interface SpiNamedParam {
Object getValue();
}
@@ -223,14 +223,24 @@ public interface SpiQuery<T> extends Query<T> {
Timestamp getAsOf();
/**
* Add a table alias for a @History entity involved in a 'As Of' query.
* Return true if the base table is using history.
*/
void addAsOfTableAlias(String tableAlias);
boolean isAsOfBaseTable();
/**
* Return the list of table alias involved in a 'As Of' query that have @History support.
* Set when the base table is using history.
*/
List<String> getAsOfTableAlias();
void setAsOfBaseTable();
/**
* Increment the counter of tables used in 'As Of' query.
*/
void incrementAsOfTableCount();
/**
* Return the table alias used for the base table.
*/
int getAsOfTableCount();
void addSoftDeletePredicate(String softDeletePredicate);
@@ -296,6 +306,16 @@ public interface SpiQuery<T> extends Query<T> {
*/
void setLoadDescription(String loadMode, String loadDescription);
/**
* Check that the named parameters have had their values set.
*/
void checkNamedParameters();
/**
* Create a named parameter placeholder.
*/
SpiNamedParam createNamedParameter(String parameterName);
/**
* Return the joins required to support predicates on the many properties.
*/
@@ -542,12 +562,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
BindParams getBindParams();
/**
* Get the orm query as a String. Only available if the query was built from
* a string.
*/
String getQuery();
/**
* Replace the query detail. This is used by the AutoTune feature to as a
* fast way to set the query properties and joins.
@@ -564,6 +578,11 @@ public interface SpiQuery<T> extends Query<T> {
*/
boolean tuneFetchProperties(OrmQueryDetail detail);
/**
* If this is a RawSql based entity set the default RawSql if not set.
*/
void setDefaultRawSqlIfRequired();
/**
* Set to true if this query has been tuned by autoTune.
*/
@@ -625,6 +644,11 @@ public interface SpiQuery<T> extends Query<T> {
*/
void setGeneratedSql(String generatedSql);
/**
* Set the JDBC fetchSize buffer hint if not explicitly set.
*/
void setDefaultFetchBuffer(int fetchSize);
/**
* Return the hint for Statement.setFetchSize().
*/
@@ -681,4 +705,8 @@ public interface SpiQuery<T> extends Query<T> {
*/
OrmUpdateProperties getUpdateProperties();
/**
* Simplify nested expression lists where possible.
*/
void simplifyExpressions();
}
@@ -40,7 +40,7 @@ public class ExtraDdlXmlReader {
* @param platforms The platforms (comma delimited) this script should run for
*/
public static boolean matchPlatform(String platformName, String platforms) {
if (platforms == null || platforms.trim().length() == 0) {
if (platforms == null || platforms.trim().isEmpty()) {
return true;
}
String[] names = platforms.split("[,;]");
@@ -45,9 +45,8 @@ public class ProfileOriginNodeUsage {
if (path != null) {
ElPropertyValue elGetValue = rootDesc.getElGetValue(path);
if (elGetValue == null) {
desc = null;
logger.warn("AutoTune: Can't find join for path[" + path + "] for " + rootDesc.getName());
return;
} else {
BeanProperty beanProperty = elGetValue.getBeanProperty();
if (beanProperty instanceof BeanPropertyAssoc<?>) {
@@ -1,35 +1,25 @@
package com.avaje.ebeaninternal.server.core;
import java.util.concurrent.TimeUnit;
import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
import com.avaje.ebeaninternal.server.lib.DaemonExecutorService;
import com.avaje.ebeaninternal.server.lib.DaemonScheduleThreadPool;
import com.avaje.ebeaninternal.server.lib.DaemonThreadPool;
import java.util.concurrent.TimeUnit;
/**
* The default implementation of the BackgroundExecutor.
*/
public class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
private final DaemonThreadPool pool;
private final DaemonScheduleThreadPool schedulePool;
private final DaemonExecutorService pool;
/**
* Construct the default implementation of BackgroundExecutor.
*
* @param corePoolSize
* the core size of the thread pool.
* @param maximumPoolSize
* the maximum pool size before jobs are queued
* @param keepAliveSecs
* the time in seconds idle threads are keep alive
* @param shutdownWaitSeconds
* the time in seconds allowed for the pool to shutdown nicely.
* After this the pool is forced to shutdown.
*/
public DefaultBackgroundExecutor(int schedulePoolSize, int corePoolSize, int maximumPoolSize, long keepAliveSecs,int shutdownWaitSeconds, String namePrefix) {
this.pool = new DaemonThreadPool(corePoolSize, maximumPoolSize, keepAliveSecs, shutdownWaitSeconds, namePrefix);
public DefaultBackgroundExecutor(int schedulePoolSize, int shutdownWaitSeconds, String namePrefix) {
this.pool = new DaemonExecutorService(shutdownWaitSeconds, namePrefix);
this.schedulePool = new DaemonScheduleThreadPool(schedulePoolSize, shutdownWaitSeconds, namePrefix+"-periodic-");
}
@@ -14,14 +14,14 @@ import java.util.Set;
*/
public class DefaultBeanState implements BeanState {
private final EntityBean entityBean;
private final EntityBeanIntercept intercept;
public DefaultBeanState(EntityBean entityBean){
this.entityBean = entityBean;
this.intercept = entityBean._ebean_getIntercept();
}
private final EntityBean entityBean;
private final EntityBeanIntercept intercept;
public DefaultBeanState(EntityBean entityBean) {
this.entityBean = entityBean;
this.intercept = entityBean._ebean_getIntercept();
}
public void setPropertyLoaded(String propertyName, boolean loaded) {
intercept.setPropertyLoaded(propertyName, loaded);
@@ -31,52 +31,57 @@ public class DefaultBeanState implements BeanState {
return intercept.isReference();
}
public boolean isNew() {
return intercept.isNew();
}
public boolean isNewOrDirty() {
return intercept.isNewOrDirty();
}
public boolean isDirty() {
return intercept.isDirty();
}
public Set<String> getLoadedProps() {
return intercept.getLoadedPropertyNames();
}
public Set<String> getChangedProps() {
return intercept.getDirtyPropertyNames();
public boolean isNew() {
return intercept.isNew();
}
public Map<String,ValuePair> getDirtyValues() {
public boolean isNewOrDirty() {
return intercept.isNewOrDirty();
}
public boolean isDirty() {
return intercept.isDirty();
}
public Set<String> getLoadedProps() {
return intercept.getLoadedPropertyNames();
}
public Set<String> getChangedProps() {
return intercept.getDirtyPropertyNames();
}
public Map<String, ValuePair> getDirtyValues() {
return intercept.getDirtyValues();
}
public boolean isReadOnly() {
return intercept.isReadOnly();
}
public void setReadOnly(boolean readOnly){
intercept.setReadOnly(readOnly);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
entityBean.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
entityBean.removePropertyChangeListener(listener);
}
public void setLoaded() {
intercept.setLoaded();
}
public boolean isReadOnly() {
return intercept.isReadOnly();
}
public void setReadOnly(boolean readOnly) {
intercept.setReadOnly(readOnly);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
entityBean.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
entityBean.removePropertyChangeListener(listener);
}
public void setLoaded() {
intercept.setLoaded();
}
@Override
public void setDisableLazyLoad(boolean disableLazyLoading) {
intercept.setDisableLazyLoad(disableLazyLoading);
}
@Override
public boolean isDisableLazyLoad() {
return intercept.isDisableLazyLoad();
}
}
@@ -78,14 +78,10 @@ public class DefaultContainer implements SpiContainer {
private SpiBackgroundExecutor createBackgroundExecutor(ServerConfig serverConfig) {
String namePrefix = "ebean-" + serverConfig.getName();
int schedulePoolSize = serverConfig.getBackgroundExecutorSchedulePoolSize();
int corePoolSize = serverConfig.getBackgroundExecutorCorePoolSize();
int maxPoolSize = serverConfig.getBackgroundExecutorMaxPoolSize();
int idleSecs = serverConfig.getBackgroundExecutorIdleSecs();
int shutdownSecs = serverConfig.getBackgroundExecutorShutdownSecs();
return new DefaultBackgroundExecutor(schedulePoolSize, corePoolSize, maxPoolSize, idleSecs, shutdownSecs, namePrefix);
return new DefaultBackgroundExecutor(schedulePoolSize, shutdownSecs, namePrefix);
}
/**
@@ -122,22 +118,20 @@ public class DefaultContainer implements SpiContainer {
// generate and run DDL if required
// if there are any other tasks requiring action in their plugins, do them as well
if (!DbOffline.isRunningMigration()) {
if (!DbOffline.isGenerateMigration()) {
server.executePlugins(online);
}
// initialise prior to registering with clusterManager
server.initialise();
if (online) {
if (clusterManager.isClustering()) {
// register the server once it has been created
clusterManager.registerServer(server);
// initialise prior to registering with clusterManager
server.initialise();
if (online) {
if (clusterManager.isClustering()) {
// register the server once it has been created
clusterManager.registerServer(server);
}
}
// start any services after registering with clusterManager
server.start();
}
// start any services after registering with clusterManager
server.start();
DbOffline.reset();
return server;
}
@@ -244,7 +238,7 @@ public class DefaultContainer implements SpiContainer {
if (dbPlatform == null) {
DatabasePlatformFactory factory = new DatabasePlatformFactory();
DatabasePlatform db = factory.create(config);
db.configure(config.getProperties());
db.configure(config);
config.setDatabasePlatform(db);
logger.info("DatabasePlatform name:" + config.getName() + " platform:" + db.getName());
}
@@ -40,6 +40,7 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.el.ElFilter;
import com.avaje.ebeaninternal.server.grammer.EqlParser;
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
import com.avaje.ebeaninternal.server.query.CQuery;
import com.avaje.ebeaninternal.server.query.CQueryEngine;
@@ -557,7 +558,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
InheritInfo inheritInfo = desc.getInheritInfo();
if (inheritInfo == null) {
return (T)desc.contextRef(pc, null, id);
return (T)desc.contextRef(pc, null, false, id);
}
BeanProperty idProp = desc.getIdProperty();
@@ -683,6 +684,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
if (txScope == null) {
// create a TxScope with default settings
txScope = new TxScope();
} else {
// check for implied batch mode via setting batchSize
txScope.checkBatchMode();
}
SpiTransaction suspended = null;
@@ -884,6 +888,32 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return createQuery(beanType);
}
@Override
public <T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery) {
BeanDescriptor<T> desc = getBeanDescriptor(beanType);
if (desc == null) {
throw new PersistenceException(beanType.getName() + " is NOT an Entity Bean registered with this server?");
}
String named = desc.getNamedQuery(namedQuery);
if (named != null) {
return createQuery(beanType, named);
}
RawSql rawSql = desc.getNamedRawSql(namedQuery);
if (rawSql != null) {
DefaultOrmQuery<T> query = createQuery(beanType);
query.setRawSql(rawSql);
return query;
}
throw new PersistenceException("No named query called " + namedQuery + " for bean:" + beanType.getName());
}
@Override
public <T> Query<T> createQuery(Class<T> beanType, String eql) {
DefaultOrmQuery<T> query = createQuery(beanType);
EqlParser.parse(eql, query);
return query;
}
public <T> DefaultOrmQuery<T> createQuery(Class<T> beanType) {
BeanDescriptor<T> desc = getBeanDescriptor(beanType);
if (desc == null) {
@@ -936,12 +966,14 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
spiQuery.setType(type);
spiQuery.checkNamedParameters();
return createQueryRequest(spiQuery, t);
}
private <T> SpiOrmQueryRequest<T> createQueryRequest(SpiQuery<T> query, Transaction t) {
query.setDefaultRawSqlIfRequired();
if (query.isAutoTunable() && !autoTuneService.tuneQuery(query)) {
// use deployment FetchType.LAZY/EAGER annotations
// to define the 'default' select clause
@@ -137,7 +137,7 @@ public class InternalConfiguration {
this.dataTimeZone = initDataTimeZone();
this.binder = getBinder(typeManager, databasePlatform, dataTimeZone);
this.cQueryEngine = new CQueryEngine(databasePlatform, binder, asOfTableMapping, serverConfig.getAsOfSysPeriod(), draftTableMap);
this.cQueryEngine = new CQueryEngine(serverConfig, databasePlatform, binder, asOfTableMapping, draftTableMap);
}
/**
@@ -235,7 +235,7 @@ public class InternalConfiguration {
if (historySupport == null) {
return new Binder(typeManager, 0, false, jsonHandler, dataTimeZone);
}
return new Binder(typeManager, historySupport.getBindCount(), historySupport.isBindWithFromClause(), jsonHandler, dataTimeZone);
return new Binder(typeManager, historySupport.getBindCount(), historySupport.isStandardsBased(), jsonHandler, dataTimeZone);
}
/**
@@ -513,4 +513,18 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
public boolean isAuditReads() {
return !query.isDisableReadAudit() && beanDescriptor.isReadAuditing();
}
/**
* Return the base table alias for this query.
*/
public String getBaseTableAlias() {
return query.getAlias() == null ? beanDescriptor.getBaseTableAlias() : query.getAlias();
}
/**
* Set the JDBC buffer fetchSize hint if not set explicitly.
*/
public void setDefaultFetchBuffer(int fetchSize) {
query.setDefaultFetchBuffer(fetchSize);
}
}
@@ -548,7 +548,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* Create and return a new reference bean matching this beans Id value.
*/
public T createReference() {
return beanDescriptor.createReference(Boolean.FALSE, getBeanId(), null);
return beanDescriptor.createReference(Boolean.FALSE, false, getBeanId(), null);
}
/**
@@ -46,9 +46,11 @@ abstract class AssocOneHelp {
return existing;
}
Object ref = target.contextRef(pc, ctx.isReadOnly(), id);
EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept();
ctx.register(property.name, ebi);
boolean disableLazyLoading = ctx.isDisableLazyLoading();
Object ref = target.contextRef(pc, ctx.isReadOnly(), disableLazyLoading, id);
if (!disableLazyLoading) {
ctx.register(property.name, ((EntityBean) ref)._ebean_getIntercept());
}
return ref;
}
@@ -53,9 +53,11 @@ class AssocOneHelpRefInherit extends AssocOneHelp {
}
// for inheritance hierarchy create the correct type for this row...
Object ref = desc.contextRef(pc, ctx.isReadOnly(), id);
EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept();
ctx.register(property.name, ebi);
boolean disableLazyLoading = ctx.isDisableLazyLoading();
Object ref = desc.contextRef(pc, ctx.isReadOnly(), disableLazyLoading, id);
if (disableLazyLoading) {
ctx.register(property.name, ((EntityBean) ref)._ebean_getIntercept());
}
return ref;
}
@@ -7,7 +7,6 @@ import com.avaje.ebean.RawSql;
import com.avaje.ebean.SqlUpdate;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.ValuePair;
import com.avaje.ebeaninternal.api.ConcurrencyMode;
import com.avaje.ebean.annotation.DocStoreMode;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.EntityBean;
@@ -16,8 +15,8 @@ import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.bean.PersistenceContextUtil;
import com.avaje.ebean.config.EncryptKey;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.PlatformIdGenerator;
import com.avaje.ebean.config.dbplatform.IdType;
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;
@@ -36,6 +35,7 @@ import com.avaje.ebean.plugin.BeanType;
import com.avaje.ebean.plugin.ExpressionPath;
import com.avaje.ebean.plugin.Property;
import com.avaje.ebeaninternal.api.CQueryPlanKey;
import com.avaje.ebeaninternal.api.ConcurrencyMode;
import com.avaje.ebeaninternal.api.LoadContext;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
@@ -109,6 +109,10 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
private final ConcurrentHashMap<String, ElComparator<T>> comparatorCache = new ConcurrentHashMap<String, ElComparator<T>>();
private final Map<String, RawSql> namedRawSql;
private final Map<String, String> namedQuery;
public void merge(EntityBean bean, EntityBean existing) {
EntityBeanIntercept fromEbi = bean._ebean_getIntercept();
@@ -176,7 +180,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
*/
private final ConcurrencyMode concurrencyMode;
private final CompoundUniqueConstraint[] compoundUniqueConstraints;
private final IndexDefinition[] indexDefinitions;
private final String[] dependentTables;
@@ -412,6 +416,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
this.rootBeanType = PersistenceContextUtil.root(beanType);
this.prototypeEntityBean = createPrototypeEntityBean(beanType);
this.namedQuery = deploy.getNamedQuery();
this.namedRawSql = deploy.getNamedRawSql();
this.inheritInfo = deploy.getInheritInfo();
this.beanFinder = deploy.getBeanFinder();
@@ -431,7 +437,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
this.selectLastInsertedId = deploy.getSelectLastInsertedId();
this.concurrencyMode = deploy.getConcurrencyMode();
this.updateChangesOnly = deploy.isUpdateChangesOnly();
this.compoundUniqueConstraints = deploy.getCompoundUniqueConstraints();
this.indexDefinitions = deploy.getIndexDefinitions();
this.readAuditing = deploy.isReadAuditing();
this.draftable = deploy.isDraftable();
@@ -443,7 +449,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
this.baseTableVersionsBetween = deploy.getBaseTableVersionsBetween();
this.dependentTables = deploy.getDependentTables();
this.dbComment = deploy.getDbComment();
this.autoTunable = EntityType.ORM.equals(entityType) && (beanFinder == null);
this.autoTunable = EntityType.ORM == entityType && (beanFinder == null);
// helper object used to derive lists of properties
DeployBeanPropertyLists listHelper = new DeployBeanPropertyLists(owner, this, deploy);
@@ -985,6 +991,20 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
return this;
}
/**
* Return the named ORM query.
*/
public String getNamedQuery(String name) {
return namedQuery.get(name);
}
/**
* Return the named RawSql query.
*/
public RawSql getNamedRawSql(String named) {
return namedRawSql.get(named);
}
/**
* Return the type of DocStoreMode that should occur for this type of persist request
* given the transactions requested mode.
@@ -1559,9 +1579,9 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
* Create a reference bean based on the id.
*/
@SuppressWarnings("unchecked")
public T createReference(Boolean readOnly, Object id, PersistenceContext pc) {
public T createReference(Boolean readOnly, boolean disableLazyLoad, Object id, PersistenceContext pc) {
if (cacheSharableBeans && !Boolean.FALSE.equals(readOnly)) {
if (cacheSharableBeans && !disableLazyLoad && !Boolean.FALSE.equals(readOnly)) {
CachedBeanData d = cacheHelp.beanCacheGetData(id);
if (d != null) {
Object shareableBean = d.getSharableBean();
@@ -1578,7 +1598,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
id = convertSetId(id, eb);
EntityBeanIntercept ebi = eb._ebean_getIntercept();
ebi.setBeanLoader(ebeanServer);
if (disableLazyLoad) {
ebi.setDisableLazyLoad(true);
} else {
ebi.setBeanLoader(ebeanServer);
}
ebi.setReference(idPropertyIndex);
if (Boolean.TRUE == readOnly) {
ebi.setReadOnly(true);
@@ -1750,8 +1774,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
/**
* Create a reference bean and put it in the persistence context (and return it).
*/
public Object contextRef(PersistenceContext pc, Boolean readOnly, Object id) {
return createReference(readOnly, id, pc);
public Object contextRef(PersistenceContext pc, Boolean readOnly, boolean disableLazyLoad, Object id) {
return createReference(readOnly, disableLazyLoad, id, pc);
}
/**
@@ -2169,14 +2193,14 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
* Return true if this is an embedded bean.
*/
public boolean isEmbedded() {
return EntityType.EMBEDDED.equals(entityType);
return EntityType.EMBEDDED == entityType;
}
/**
* Return the compound unique constraints.
*/
public CompoundUniqueConstraint[] getCompoundUniqueConstraints() {
return compoundUniqueConstraints;
public IndexDefinition[] getIndexDefinitions() {
return indexDefinitions;
}
/**
@@ -2295,15 +2319,10 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
/**
* Returns true if this bean is based on a table (or possibly view) and
* returns false if this bean is based on a raw sql select statement.
* <p>
* When false querying this bean is based on a supplied sql select statement
* placed in the orm xml file (as opposed to Ebean generated sql).
* </p>
* Returns true if this bean is based on RawSql.
*/
public boolean isSqlSelectBased() {
return EntityType.SQL.equals(entityType);
public boolean isRawSqlBased() {
return EntityType.SQL == entityType;
}
/**
@@ -266,7 +266,7 @@ final class BeanDescriptorCacheHelp<T> {
bc.checkEmptyLazyLoad();
for (int i = 0; i < idList.size(); i++) {
Object id = idList.get(i);
Object refBean = targetDescriptor.createReference(readOnly, id, persistenceContext);
Object refBean = targetDescriptor.createReference(readOnly, false, id, persistenceContext);
many.add(bc, (EntityBean) refBean);
}
return true;
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.Model;
import com.avaje.ebean.RawSqlBuilder;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.cache.ServerCacheManager;
@@ -22,10 +23,10 @@ import com.avaje.ebean.plugin.BeanType;
import com.avaje.ebeaninternal.api.ConcurrencyMode;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.TransactionEventTable;
import com.avaje.ebeaninternal.server.core.bootup.BootupClasses;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.core.InternalConfiguration;
import com.avaje.ebeaninternal.server.core.Message;
import com.avaje.ebeaninternal.server.core.bootup.BootupClasses;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.deploy.id.IdBinderEmbedded;
@@ -47,6 +48,13 @@ import com.avaje.ebeaninternal.server.properties.BeanPropertiesReader;
import com.avaje.ebeaninternal.server.properties.BeanPropertyInfo;
import com.avaje.ebeaninternal.server.properties.BeanPropertyInfoFactory;
import com.avaje.ebeaninternal.server.properties.EnhanceBeanPropertyInfoFactory;
import com.avaje.ebeaninternal.xmlmapping.XmlMappingReader;
import com.avaje.ebeaninternal.xmlmapping.model.XmAliasMapping;
import com.avaje.ebeaninternal.xmlmapping.model.XmColumnMapping;
import com.avaje.ebeaninternal.xmlmapping.model.XmEbean;
import com.avaje.ebeaninternal.xmlmapping.model.XmEntity;
import com.avaje.ebeaninternal.xmlmapping.model.XmNamedQuery;
import com.avaje.ebeaninternal.xmlmapping.model.XmRawSql;
import com.avaje.ebeanservice.docstore.api.DocStoreBeanAdapter;
import com.avaje.ebeanservice.docstore.api.DocStoreFactory;
import org.slf4j.Logger;
@@ -56,12 +64,16 @@ import javax.persistence.MappedSuperclass;
import javax.persistence.PersistenceException;
import javax.persistence.Transient;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -116,7 +128,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final DocStoreFactory docStoreFactory;
private int enhancedClassCount;
private int entityBeanCount;
private final boolean updateChangesOnly;
@@ -124,7 +136,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final String serverName;
private Map<Class<?>, DeployBeanInfo<?>> deplyInfoMap = new HashMap<Class<?>, DeployBeanInfo<?>>();
private Map<Class<?>, DeployBeanInfo<?>> deployInfoMap = new HashMap<Class<?>, DeployBeanInfo<?>>();
private final Map<Class<?>, BeanTable> beanTableMap = new HashMap<Class<?>, BeanTable>();
@@ -301,6 +313,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
try {
createListeners();
readEntityDeploymentInitial();
readXmlMapping();
readEmbeddedDeployment();
readEntityBeanTable();
readEntityDeploymentAssociations();
@@ -319,8 +332,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
logStatus();
deplyInfoMap.clear();
deplyInfoMap = null;
deployInfoMap.clear();
deployInfoMap = null;
return asOfTableMap;
@@ -330,6 +343,66 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
}
private void readXmlMapping() {
try {
ClassLoader classLoader = serverConfig.getClassLoadConfig().getClassLoader();
Enumeration<URL> resources = classLoader.getResources("ebean.xml");
List<XmEbean> mappings = new ArrayList<XmEbean>();
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
InputStream is = url.openStream();
mappings.add(XmlMappingReader.read(is));
is.close();
}
for (XmEbean mapping : mappings) {
List<XmEntity> entityDeploy = mapping.getEntity();
for (XmEntity deploy : entityDeploy) {
readEntityMapping(classLoader, deploy);
}
}
} catch (IOException e) {
throw new RuntimeException("Error reading ebean.xml", e);
}
}
private void readEntityMapping(ClassLoader classLoader, XmEntity entityDeploy) {
String entityClassName = entityDeploy.getClazz();
Class<?> entityClass;
try {
entityClass = Class.forName(entityClassName, false, classLoader);
} catch (Exception e) {
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);
} else {
for (XmRawSql sql : entityDeploy.getRawSql()) {
RawSqlBuilder builder = RawSqlBuilder.parse(sql.getQuery().getValue());
for (XmColumnMapping columnMapping : sql.getColumnMapping()) {
builder.columnMapping(columnMapping.getColumn(), columnMapping.getProperty());
}
for (XmAliasMapping aliasMapping : sql.getAliasMapping()) {
builder.tableAliasMapping(aliasMapping.getAlias(), aliasMapping.getProperty());
}
info.addRawSql(sql.getName(), builder.create());
}
for (XmNamedQuery namedQuery : entityDeploy.getNamedQuery()) {
info.addNamedQuery(namedQuery.getName(), namedQuery.getQuery().getValue());
}
}
}
/**
* Return the Encrypt key given the table and column name.
*/
@@ -381,7 +454,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
for (String depTable : viewInvalidation) {
List<BeanDescriptor<?>> list = tableToViewDescMap.get(depTable.toLowerCase());
if (list == null) {
if (list != null) {
for (int i = 0; i < list.size(); i++) {
list.get(i).queryCacheClear();
}
@@ -557,7 +630,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
private void logStatus() {
logger.info("Entities enhanced[" + enhancedClassCount + "]");
logger.debug("Entities[{}]", entityBeanCount);
}
private <T> BeanDescriptor<T> createEmbedded(Class<T> beanClass) {
@@ -569,7 +642,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
* Return the bean deploy info for the given class.
*/
public <T> DeployBeanInfo<T> getDeploy(Class<T> cls) {
return (DeployBeanInfo<T>) deplyInfoMap.get(cls);
return (DeployBeanInfo<T>) deployInfoMap.get(cls);
}
private void registerBeanDescriptor(BeanDescriptor<?> desc) {
@@ -601,12 +674,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
for (Class<?> entityClass : bootupClasses.getEntities()) {
DeployBeanInfo<?> info = createDeployBeanInfo(entityClass);
deplyInfoMap.put(entityClass, info);
deployInfoMap.put(entityClass, info);
}
for (Class<?> entityClass : bootupClasses.getEmbeddables()) {
DeployBeanInfo<?> info = createDeployBeanInfo(entityClass);
readDeployAssociations(info);
deplyInfoMap.put(entityClass, info);
deployInfoMap.put(entityClass, info);
}
}
@@ -618,7 +691,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void readEntityBeanTable() {
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
BeanTable beanTable = createBeanTable(info);
beanTableMap.put(beanTable.getBeanType(), beanTable);
}
@@ -632,18 +705,18 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void readEntityDeploymentAssociations() {
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
readDeployAssociations(info);
}
}
private void readInheritedIdGenerators() {
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
DeployBeanDescriptor<?> descriptor = info.getDescriptor();
InheritInfo inheritInfo = descriptor.getInheritInfo();
if (inheritInfo != null && !inheritInfo.isRoot()) {
DeployBeanInfo<?> rootBeanInfo = deplyInfoMap.get(inheritInfo.getRoot().getType());
DeployBeanInfo<?> rootBeanInfo = deployInfoMap.get(inheritInfo.getRoot().getType());
PlatformIdGenerator rootIdGen = rootBeanInfo.getDescriptor().getIdGenerator();
if (rootIdGen != null) {
descriptor.setIdGenerator(rootIdGen);
@@ -668,20 +741,20 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
// We only perform 'circular' checks etc after we have
// all the DeployBeanDescriptors created and in the map.
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
checkMappedBy(info);
}
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
secondaryPropsJoins(info);
}
// Set inheritance info
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
setInheritanceInfo(info);
}
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
registerBeanDescriptor(new BeanDescriptor(this, info.getDescriptor()));
}
}
@@ -695,7 +768,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
for (DeployBeanPropertyAssocOne<?> oneProp : info.getDescriptor().propertiesAssocOne()) {
if (!oneProp.isTransient()) {
DeployBeanInfo<?> assoc = deplyInfoMap.get(oneProp.getTargetType());
DeployBeanInfo<?> assoc = deployInfoMap.get(oneProp.getTargetType());
if (assoc != null) {
oneProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
}
@@ -704,7 +777,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
for (DeployBeanPropertyAssocMany<?> manyProp : info.getDescriptor().propertiesAssocMany()) {
if (!manyProp.isTransient()) {
DeployBeanInfo<?> assoc = deplyInfoMap.get(manyProp.getTargetType());
DeployBeanInfo<?> assoc = deployInfoMap.get(manyProp.getTargetType());
if (assoc != null) {
manyProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
}
@@ -763,7 +836,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private DeployBeanDescriptor<?> getTargetDescriptor(DeployBeanPropertyAssoc<?> prop) {
Class<?> targetType = prop.getTargetType();
DeployBeanInfo<?> info = deplyInfoMap.get(targetType);
DeployBeanInfo<?> info = deployInfoMap.get(targetType);
if (info == null) {
String msg = "Can not find descriptor [" + targetType + "] for " + prop.getFullBeanName();
throw new PersistenceException(msg);
@@ -1141,7 +1214,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
// already assigned (So custom or UUID)
return;
}
if (desc.propertiesId().size() == 0) {
if (desc.propertiesId().isEmpty()) {
// bean doesn't have an Id property
if (desc.isBaseTableType() && desc.getBeanFinder() == null) {
// expecting an id property
@@ -1348,7 +1421,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
checkInheritedClasses(beanClass);
if (!beanClass.getName().startsWith("com.avaje.ebean.meta")) {
enhancedClassCount++;
entityBeanCount++;
}
}
@@ -27,6 +27,7 @@ import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.type.ScalarType;
import com.avaje.ebeaninternal.server.type.ScalarTypeBoolean;
import com.avaje.ebeaninternal.server.type.ScalarTypeEnum;
import com.avaje.ebeaninternal.server.type.ScalarTypeLogicalType;
import com.avaje.ebeaninternal.util.ValueUtil;
import com.avaje.ebeanservice.docstore.api.mapping.DocMappingBuilder;
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyMapping;
@@ -255,10 +256,6 @@ public class BeanProperty implements ElPropertyValue, Property {
final String softDeleteDbPredicate;
final boolean indexed;
final String indexName;
public BeanProperty(DeployBeanProperty deploy) {
this(null, deploy);
}
@@ -268,8 +265,6 @@ public class BeanProperty implements ElPropertyValue, Property {
this.descriptor = descriptor;
this.name = InternString.intern(deploy.getName());
this.propertyIndex = deploy.getPropertyIndex();
this.indexed = deploy.isIndexed();
this.indexName = deploy.getIndexName();
this.unidirectionalShadow = deploy.isUndirectionalShadow();
this.discriminator = deploy.isDiscriminator();
this.localEncrypted = deploy.isLocalEncrypted();
@@ -370,9 +365,6 @@ public class BeanProperty implements ElPropertyValue, Property {
this.descriptor = source.descriptor;
this.name = InternString.intern(source.getName());
this.propertyIndex = source.propertyIndex;
this.indexed = source.isIndexed();
this.indexName = source.getIndexName();
this.dbColumn = InternString.intern(override.getDbColumn());
// override with sqlFormula not currently supported
this.sqlFormulaJoin = null;
@@ -422,7 +414,7 @@ public class BeanProperty implements ElPropertyValue, Property {
this.generatedProperty = source.getGeneratedProperty();
this.getter = source.getter;
this.setter = source.setter;
this.dbType = source.getDbType();
this.dbType = source.getDbType(true);
this.scalarType = source.scalarType;
this.lob = isLobType(dbType);
this.propertyType = source.getPropertyType();
@@ -631,14 +623,6 @@ public class BeanProperty implements ElPropertyValue, Property {
return this;
}
public boolean isIndexed() {
return indexed;
}
public String getIndexName() {
return indexName;
}
/**
* Return true if this object is part of an inheritance hierarchy.
*/
@@ -1104,9 +1088,14 @@ public class BeanProperty implements ElPropertyValue, Property {
/**
* Return the database jdbc data type this is mapped to.
*
* @param platformTypes Set as false when we want logical platform agnostic types.
*/
public int getDbType() {
return dbType;
public int getDbType(boolean platformTypes) {
if (platformTypes || !(scalarType instanceof ScalarTypeLogicalType)) {
return dbType;
}
return ((ScalarTypeLogicalType)scalarType).getLogicalType();
}
/**
@@ -1184,6 +1173,13 @@ public class BeanProperty implements ElPropertyValue, Property {
return excludedFromHistory;
}
/**
* Return true if this is a ManyToMany with history support (on the intersection table).
*/
public boolean isManyToManyWithHistory() {
return !excludedFromHistory && descriptor.isHistorySupport();
}
/**
* Return true if this property only exists on the draft table.
*/
@@ -21,6 +21,7 @@ import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import java.util.ArrayList;
import java.util.List;
/**
* Abstract base for properties mapped to an associated bean, list, set or map.
@@ -41,6 +42,11 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
String targetIdProperty;
/**
* Derived list of exported property and matching foreignKey
*/
protected ExportedProperty[] exportedProperties;
/**
* Persist settings.
*/
@@ -322,7 +328,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
BeanProperty idProp = target.getIdProperty();
BeanProperty[] others = target.propertiesBaseScalar();
if (descriptor.isSqlSelectBased()) {
if (descriptor.isRawSqlBased()) {
String dbColumn = owner.getDbColumn();
return new ImportedIdSimple(owner, dbColumn, idProp, 0);
}
@@ -385,4 +391,18 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
+ " Perhaps an error in a @JoinColumn";
throw new PersistenceException(msg);
}
protected void bindWhereParentId(List<Object> bindValues, Object parentId) {
if (exportedProperties.length == 1) {
bindValues.add(parentId);
} else {
EntityBean parent = (EntityBean) parentId;
for (int i = 0; i < exportedProperties.length; i++) {
Object embVal = exportedProperties[i].getValue(parent);
bindValues.add(embVal);
}
}
}
}
@@ -85,11 +85,6 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
private BeanProperty mapKeyProperty;
/**
* Derived list of exported property and matching foreignKey
*/
private ExportedProperty[] exportedProperties;
private String exportedPropertyBindProto = "?";
/**
@@ -299,23 +294,25 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
/**
* Find the Id's of detail beans given a parent Id or list of parent Id's.
*/
public List<Object> findIdsByParentId(Object parentId, List<Object> parentIdist, Transaction t, ArrayList<Object> excludeDetailIds) {
public List<Object> findIdsByParentId(Object parentId, List<Object> parentIdList, Transaction t, ArrayList<Object> excludeDetailIds) {
if (parentId != null) {
return findIdsByParentId(parentId, t, excludeDetailIds);
} else {
return findIdsByParentIdList(parentIdist, t, excludeDetailIds);
return findIdsByParentIdList(parentIdList, t, excludeDetailIds);
}
}
private List<Object> findIdsByParentId(Object parentId, Transaction t, ArrayList<Object> excludeDetailIds) {
String rawWhere = deriveWhereParentIdSql(false, "");
List<Object> bindValues = new ArrayList<Object>();
bindWhereParentId(bindValues, parentId);
EbeanServer server = getBeanDescriptor().getEbeanServer();
Query<?> q = server.find(getPropertyType())
.where().raw(rawWhere).query();
bindWhereParendId(1, q, parentId);
.where()
.raw(rawWhere, bindValues.toArray())
.query();
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
Expression idIn = q.getExpressionFactory().idIn(excludeDetailIds);
@@ -371,21 +368,24 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
query.where().raw(expr, bindValues.toArray());
}
private List<Object> findIdsByParentIdList(List<Object> parentIdist, Transaction t, ArrayList<Object> excludeDetailIds) {
private List<Object> findIdsByParentIdList(List<Object> parentIdList, Transaction t, ArrayList<Object> excludeDetailIds) {
String rawWhere = deriveWhereParentIdSql(true, "");
String inClause = buildInClauseBinding(parentIdist.size(), exportedPropertyBindProto);
String inClause = buildInClauseBinding(parentIdList.size(), exportedPropertyBindProto);
String expr = rawWhere + inClause;
EbeanServer server = getBeanDescriptor().getEbeanServer();
Query<?> q = server.find(getPropertyType()).where().raw(expr).query();
int pos = 1;
for (int i = 0; i < parentIdist.size(); i++) {
pos = bindWhereParendId(pos, q, parentIdist.get(i));
List<Object> bindValues = new ArrayList<Object>();
for (int i = 0; i < parentIdList.size(); i++) {
bindWhereParentId(bindValues, parentIdList.get(i));
}
EbeanServer server = getBeanDescriptor().getEbeanServer();
Query<?> q = server.find(getPropertyType())
.where()
.raw(expr, bindValues.toArray())
.query();
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
Expression idIn = q.getExpressionFactory().idIn(excludeDetailIds);
q.where().not(idIn);
@@ -690,22 +690,6 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
}
}
private int bindWhereParendId(int pos, Query<?> q, Object parentId) {
if (exportedProperties.length == 1) {
q.setParameter(pos++, parentId);
} else {
EntityBean parent = (EntityBean) parentId;
for (int i = 0; i < exportedProperties.length; i++) {
Object embVal = exportedProperties[i].getValue(parent);
q.setParameter(pos++, embVal);
}
}
return pos;
}
public void addSelectExported(DbSqlContext ctx, String tableAlias) {
String alias = manyToMany ? "int_" : tableAlias;
@@ -1028,7 +1012,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
if (isManyToMany()) {
if (liveBean == null) {
// add new relationship (Map not allowed here)
liveVal.addBean(targetDescriptor.createReference(Boolean.FALSE, id, null));
liveVal.addBean(targetDescriptor.createReference(Boolean.FALSE, false, id, null));
}
} else {
@@ -50,8 +50,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
*/
protected ImportedId importedId;
private ExportedProperty[] exportedProperties;
private String deleteByParentIdSql;
private String deleteByParentIdInSql;
@@ -240,47 +238,37 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
String rawWhere = deriveWhereParentIdSql(false);
List<Object> bindValues = new ArrayList<Object>();
bindWhereParentId(bindValues, parentId);
EbeanServer server = getBeanDescriptor().getEbeanServer();
Query<?> q = server.find(getPropertyType())
.where().raw(rawWhere).query();
.where()
.raw(rawWhere, bindValues.toArray())
.query();
bindWhereParendId(q, parentId);
return server.findIds(q, t);
}
private List<Object> findIdsByParentIdList(List<Object> parentIdist, Transaction t) {
private List<Object> findIdsByParentIdList(List<Object> parentIdList, Transaction t) {
String rawWhere = deriveWhereParentIdSql(true);
String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size());
String inClause = targetIdBinder.getIdInValueExpr(parentIdList.size());
String expr = rawWhere + inClause;
List<Object> bindValues = new ArrayList<Object>();
for (int i = 0; i < parentIdList.size(); i++) {
bindWhereParentId(bindValues, parentIdList.get(i));
}
EbeanServer server = getBeanDescriptor().getEbeanServer();
Query<?> q = (Query<?>) server.find(getPropertyType())
.where().raw(expr);
for (int i = 0; i < parentIdist.size(); i++) {
bindWhereParendId(q, parentIdist.get(i));
}
.where().raw(expr, bindValues.toArray());
return server.findIds(q, t);
}
private void bindWhereParendId(Query<?> q, Object parentId) {
if (exportedProperties.length == 1) {
q.setParameter(1, parentId);
} else {
int pos = 1;
EntityBean parent = (EntityBean) parentId;
for (int i = 0; i < exportedProperties.length; i++) {
Object embVal = exportedProperties[i].getValue(parent);
q.setParameter(pos++, embVal);
}
}
}
void addFkey() {
if (importedId != null) {
importedId.addFkeys(name);
@@ -422,7 +410,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
// cacheData is the id value, maybe already in persistence context
Object assocBean = targetDescriptor.contextGet(context, cacheData);
if (assocBean == null) {
assocBean = targetDescriptor.createReference(Boolean.FALSE, cacheData, context);
assocBean = targetDescriptor.createReference(Boolean.FALSE, false, cacheData, context);
}
setValue(bean, assocBean);
}
@@ -84,4 +84,9 @@ public interface DbReadContext {
* Return true if the underlying query is a 'asDraft' query.
*/
boolean isDraftQuery();
/**
* Return true if this request disables lazy loading.
*/
boolean isDisableLazyLoading();
}
@@ -51,14 +51,14 @@ public abstract class DeployParser {
*/
public abstract Set<String> getIncludes();
public void setEncrypted(boolean encrytped) {
this.encrypted = encrytped;
}
public void setEncrypted(boolean encrypted) {
this.encrypted = encrypted;
}
public String parse(String source) {
if (source == null) {
return source;
return null;
}
pos = -1;
@@ -3,7 +3,7 @@ package com.avaje.ebeaninternal.server.deploy;
/**
* Holds multiple column unique constraints defined for an entity.
*/
public class CompoundUniqueConstraint {
public class IndexDefinition {
private final String[] columns;
@@ -11,7 +11,16 @@ public class CompoundUniqueConstraint {
private final boolean unique;
public CompoundUniqueConstraint(String[] columns, String name, boolean unique) {
/**
* A single column index.
*/
public IndexDefinition(String column, String name, boolean unique) {
this.columns = new String[]{column};
this.unique = unique;
this.name = name;
}
public IndexDefinition(String[] columns, String name, boolean unique) {
this.columns = columns;
this.unique = unique;
this.name = name;
@@ -20,7 +29,7 @@ public class CompoundUniqueConstraint {
/**
* Create a unique constraint given the column names.
*/
public CompoundUniqueConstraint(String[] columns) {
public IndexDefinition(String[] columns) {
this.columns = columns;
this.unique = true;
this.name = null;
@@ -144,15 +144,13 @@ public class ImportedIdEmbedded implements ImportedId {
*/
public BeanProperty findMatchImport(String matchDbColumn) {
BeanProperty p = null;
for (int i = 0; i < imported.length; i++) {
p = imported[i].findMatchImport(matchDbColumn);
BeanProperty p = imported[i].findMatchImport(matchDbColumn);
if (p != null) {
return p;
}
}
return p;
return null;
}
}
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.deploy.meta;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.annotation.Cache;
import com.avaje.ebean.annotation.DocStore;
import com.avaje.ebean.annotation.DocStoreMode;
@@ -23,7 +24,7 @@ import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistController;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistListener;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanPostLoad;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanQueryAdapter;
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueConstraint;
import com.avaje.ebeaninternal.server.deploy.IndexDefinition;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.deploy.parse.DeployBeanInfo;
import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator;
@@ -35,14 +36,20 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Describes Beans including their deployment information.
*/
public class DeployBeanDescriptor<T> {
private static final Map<String, String> EMPTY_NAMED_QUERY = new HashMap<String, String>();
private static final Map<String, RawSql> EMPTY_RAW_MAP = new HashMap<String, RawSql>();
private static class PropOrder implements Comparator<DeployBeanProperty> {
public int compare(DeployBeanProperty o1, DeployBeanProperty o2) {
@@ -66,6 +73,10 @@ public class DeployBeanDescriptor<T> {
*/
private LinkedHashMap<String, DeployBeanProperty> propMap = new LinkedHashMap<String, DeployBeanProperty>();
private Map<String, RawSql> namedRawSql;
private Map<String, String> namedQuery;
private EntityType entityType;
private DeployBeanPropertyAssocOne<?> unidirectional;
@@ -108,7 +119,7 @@ public class DeployBeanDescriptor<T> {
private boolean updateChangesOnly;
private List<CompoundUniqueConstraint> compoundUniqueConstraints;
private List<IndexDefinition> indexDefinitions;
/**
* The base database table.
@@ -283,7 +294,7 @@ public class DeployBeanDescriptor<T> {
docStoreUpdate = docStore.update();
docStoreDelete = docStore.delete();
String doc = docStore.doc();
if (doc.length() > 0) {
if (!doc.isEmpty()) {
docStorePathProperties = PathProperties.parse(doc);
}
}
@@ -388,7 +399,7 @@ public class DeployBeanDescriptor<T> {
public void setCache(Cache cache) {
String naturalKey = null;
if (cache.naturalKey().length() > 0) {
if (!cache.naturalKey().isEmpty()) {
// find the property and mark as natural key property
String propName = cache.naturalKey().trim();
DeployBeanProperty beanProperty = getBeanProperty(propName);
@@ -440,21 +451,21 @@ public class DeployBeanDescriptor<T> {
/**
* Add a compound unique constraint.
*/
public void addCompoundUniqueConstraint(CompoundUniqueConstraint c) {
if (compoundUniqueConstraints == null) {
compoundUniqueConstraints = new ArrayList<CompoundUniqueConstraint>();
public void addIndex(IndexDefinition c) {
if (indexDefinitions == null) {
indexDefinitions = new ArrayList<IndexDefinition>();
}
compoundUniqueConstraints.add(c);
indexDefinitions.add(c);
}
/**
* Return the compound unique constraints (can be null).
*/
public CompoundUniqueConstraint[] getCompoundUniqueConstraints() {
if (compoundUniqueConstraints == null) {
public IndexDefinition[] getIndexDefinitions() {
if (indexDefinitions == null) {
return null;
} else {
return compoundUniqueConstraints.toArray(new CompoundUniqueConstraint[compoundUniqueConstraints.size()]);
return indexDefinitions.toArray(new IndexDefinition[indexDefinitions.size()]);
}
}
@@ -561,7 +572,7 @@ public class DeployBeanDescriptor<T> {
* Return the base table with as of suffix.
*/
public String getBaseTableAsOf() {
return baseTableAsOf;
return historySupport ? baseTableAsOf : baseTable;
}
/**
@@ -583,7 +594,7 @@ public class DeployBeanDescriptor<T> {
*/
public void setView(String viewName, String[] dependentTables) {
this.entityType = EntityType.VIEW;
this.dependentTables = this.dependentTables;
this.dependentTables = dependentTables;
setBaseTable(new TableName(viewName), "", "");
}
@@ -835,7 +846,7 @@ public class DeployBeanDescriptor<T> {
return null;
}
String selectClause = sb.toString();
if (selectClause.length() == 0) {
if (selectClause.isEmpty()) {
throw new IllegalStateException("Bean " + getFullName() + " has no properties?");
}
return selectClause.substring(0, selectClause.length() - 1);
@@ -1047,4 +1058,38 @@ public class DeployBeanDescriptor<T> {
if (docStorePersist != DocStoreMode.DEFAULT) return docStorePersist;
return serverConfig.getDocStoreConfig().getPersist();
}
/**
* Return the named ORM queries.
*/
public Map<String, String> getNamedQuery() {
return (namedQuery != null) ? namedQuery : EMPTY_NAMED_QUERY;
}
/**
* Add a named query.
*/
public void addNamedQuery(String name, String query) {
if (namedQuery == null) {
namedQuery = new LinkedHashMap<String, String>();
}
namedQuery.put(name, query);
}
/**
* Return the named RawSql queries.
*/
public Map<String, RawSql> getNamedRawSql() {
return (namedRawSql != null) ? namedRawSql : EMPTY_RAW_MAP;
}
/**
* Add a named RawSql from ebean.xml file.
*/
public void addRawSql(String name, RawSql rawSql) {
if (namedRawSql == null) {
namedRawSql = new HashMap<String, RawSql>();
}
namedRawSql.put(name, rawSql);
}
}
@@ -22,7 +22,6 @@ import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.properties.BeanPropertyGetter;
import com.avaje.ebeaninternal.server.properties.BeanPropertySetter;
import com.avaje.ebeaninternal.server.type.ScalarType;
import com.avaje.ebeaninternal.server.type.ScalarTypeEnum;
import com.avaje.ebeaninternal.server.type.ScalarTypeWrapper;
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyOptions;
@@ -192,9 +191,6 @@ public class DeployBeanProperty {
private int sortOrder;
private boolean indexed;
private String indexName;
private boolean excludedFromHistory;
private boolean draft;
@@ -386,7 +382,7 @@ public class DeployBeanProperty {
* Set a specific DB column definition.
*/
public void setDbColumnDefn(String dbColumnDefn) {
if (dbColumnDefn == null || dbColumnDefn.trim().length() == 0) {
if (dbColumnDefn == null || dbColumnDefn.trim().isEmpty()) {
this.dbColumnDefn = null;
} else {
this.dbColumnDefn = InternString.intern(dbColumnDefn);
@@ -849,22 +845,6 @@ public class DeployBeanProperty {
return desc.getFullName() + "." + name;
}
public boolean isIndexed() {
return indexed;
}
public void setIndexed() {
this.indexed = true;
}
public String getIndexName() {
return indexName;
}
public void setIndexName(String indexName) {
this.indexName = indexName;
}
public boolean isExcludedFromHistory() {
return excludedFromHistory;
}
@@ -178,7 +178,7 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
* Set the default mapKey to use when returning a Map.
*/
public void setMapKey(String mapKey) {
if (mapKey != null && mapKey.length() > 0) {
if (mapKey != null && !mapKey.isEmpty()) {
this.mapKey = mapKey;
}
}
@@ -188,7 +188,7 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
* list, set or map.
*/
public void setFetchOrderBy(String orderBy) {
if (orderBy != null && orderBy.length() > 0) {
if (orderBy != null && !orderBy.isEmpty()) {
fetchOrderBy = orderBy;
}
}
@@ -32,7 +32,7 @@ public abstract class AnnotationBase {
* Checks string is null or empty .
*/
protected boolean isEmpty(String s) {
return s == null || s.trim().length() == 0;
return s == null || s.trim().isEmpty();
}
@@ -12,7 +12,7 @@ import com.avaje.ebean.annotation.UpdateMode;
import com.avaje.ebean.annotation.View;
import com.avaje.ebean.config.TableName;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueConstraint;
import com.avaje.ebeaninternal.server.deploy.IndexDefinition;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -21,6 +21,8 @@ import javax.persistence.AttributeOverride;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@@ -68,13 +70,11 @@ public class AnnotationClass extends AnnotationParser {
if (override != null) {
String propertyName = override.name();
Column column = override.column();
if (column != null) {
DeployBeanProperty beanProperty = descriptor.getBeanProperty(propertyName);
if (beanProperty == null) {
logger.error("AttributeOverride property [" + propertyName + "] not found on " + descriptor.getFullName());
} else {
readColumn(column, beanProperty);
}
DeployBeanProperty beanProperty = descriptor.getBeanProperty(propertyName);
if (beanProperty == null) {
logger.error("AttributeOverride property [" + propertyName + "] not found on " + descriptor.getFullName());
} else {
readColumn(column, beanProperty);
}
}
}
@@ -120,12 +120,12 @@ public class AnnotationClass extends AnnotationParser {
Index index = cls.getAnnotation(Index.class);
if (index != null) {
descriptor.addCompoundUniqueConstraint(new CompoundUniqueConstraint(index.columnNames(), index.name(), index.unique()));
descriptor.addIndex(new IndexDefinition(index.columnNames(), index.name(), index.unique()));
}
UniqueConstraint uc = cls.getAnnotation(UniqueConstraint.class);
if (uc != null) {
descriptor.addCompoundUniqueConstraint(new CompoundUniqueConstraint(uc.columnNames()));
descriptor.addIndex(new IndexDefinition(uc.columnNames()));
}
View view = cls.getAnnotation(View.class);
@@ -136,7 +136,7 @@ public class AnnotationClass extends AnnotationParser {
if (table != null) {
UniqueConstraint[] uniqueConstraints = table.uniqueConstraints();
for (UniqueConstraint c : uniqueConstraints) {
descriptor.addCompoundUniqueConstraint(new CompoundUniqueConstraint(c.columnNames()));
descriptor.addIndex(new IndexDefinition(c.columnNames()));
}
}
@@ -179,6 +179,18 @@ public class AnnotationClass extends AnnotationParser {
if (cache != null && !disableL2Cache) {
descriptor.setCache(cache);
}
NamedQueries namedQueries = cls.getAnnotation(NamedQueries.class);
if (namedQueries != null) {
for (NamedQuery namedQuery : namedQueries.value()) {
descriptor.addNamedQuery(namedQuery.name(), namedQuery.query());
}
}
NamedQuery namedQuery = cls.getAnnotation(NamedQuery.class);
if (namedQuery != null) {
descriptor.addNamedQuery(namedQuery.name(), namedQuery.query());
}
}
}
@@ -7,6 +7,7 @@ import com.avaje.ebean.config.dbplatform.DbEncrypt;
import com.avaje.ebean.config.dbplatform.DbEncryptFunction;
import com.avaje.ebean.config.dbplatform.IdType;
import com.avaje.ebean.config.dbplatform.PlatformIdGenerator;
import com.avaje.ebeaninternal.server.deploy.IndexDefinition;
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
@@ -314,8 +315,7 @@ public class AnnotationFields extends AnnotationParser {
if (hasRelationshipItem(prop)) {
throw new RuntimeException("Can't use Index on foreign key relationships.");
}
prop.setIndexed();
prop.setIndexName(index.name());
descriptor.addIndex(new IndexDefinition(prop.getDbColumn(), index.name(), index.unique()));
}
}
@@ -1,12 +1,13 @@
package com.avaje.ebeaninternal.server.deploy.parse;
import java.util.HashMap;
import com.avaje.ebean.RawSql;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
import java.util.HashMap;
/**
* Wraps information about a bean during deployment parsing.
*/
@@ -75,4 +76,17 @@ public class DeployBeanInfo<T> {
tableJoin.setType(outerJoin ? SqlJoinType.OUTER : SqlJoinType.INNER);
}
/**
* Add named RawSql from ebean.xml.
*/
public void addRawSql(String name, RawSql rawSql) {
descriptor.addRawSql(name, rawSql);
}
/**
* Add the named query.
*/
public void addNamedQuery(String name, String query) {
descriptor.addNamedQuery(name, query);
}
}
@@ -188,7 +188,7 @@ public class DeployInheritInfo {
public void setDiscriminatorValue(String value) {
if (value != null) {
value = value.trim();
if (value.length() != 0) {
if (!value.isEmpty()) {
discriminatorStringValue = value;
// convert the value if desired
if (discriminatorType == Types.INTEGER) {
@@ -70,7 +70,7 @@ public class SqlReservedWords {
public static synchronized void addKeyword(String keyword){
if (keyword != null){
keyword = keyword.trim().toUpperCase();
if (keyword.length() > 0){
if (!keyword.isEmpty()){
keywords.add(keyword);
}
}
@@ -21,6 +21,11 @@ public abstract class AbstractExpression implements SpiExpression {
this.propName = propName;
}
@Override
public void simplify() {
// do nothing
}
@Override
public Object getIdEqualTo(String idName) {
// override on SimpleExpression
@@ -0,0 +1,32 @@
package com.avaje.ebeaninternal.server.expression;
/**
* Abstract expression that helps with named parameter use.
*/
public abstract class AbstractValueExpression extends AbstractExpression {
protected final Object bindValue;
/**
* Construct with property name and potential named parameter.
*/
protected AbstractValueExpression(String propName, Object bindValue) {
super(propName);
this.bindValue = bindValue;
}
/**
* Return the bind value taking into account named parameters.
*/
protected Object value() {
return NamedParamHelp.value(bindValue);
}
/**
* Return the String bind value taking into account named parameters.
*/
protected String strValue() {
return NamedParamHelp.valueAsString(bindValue);
}
}
@@ -14,21 +14,29 @@ class BetweenExpression extends AbstractExpression {
private final Object valueLow;
BetweenExpression(String propertyName, Object valLo, Object valHigh) {
BetweenExpression(String propertyName, Object valueLow, Object valueHigh) {
super(propertyName);
this.valueLow = valLo;
this.valueHigh = valHigh;
this.valueLow = valueLow;
this.valueHigh = valueHigh;
}
private Object low() {
return NamedParamHelp.value(valueLow);
}
private Object high() {
return NamedParamHelp.value(valueHigh);
}
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
context.writeRange(propName, Op.GT_EQ, valueLow, Op.LT_EQ, valueHigh);
context.writeRange(propName, Op.GT_EQ, low(), Op.LT_EQ, high());
}
@Override
public void addBindValues(SpiExpressionRequest request) {
request.addBindValue(valueLow);
request.addBindValue(valueHigh);
request.addBindValue(low());
request.addBindValue(high());
}
@Override
@@ -45,8 +53,8 @@ class BetweenExpression extends AbstractExpression {
@Override
public int queryBindHash() {
int hc = valueLow.hashCode();
hc = hc * 31 + valueHigh.hashCode();
int hc = low().hashCode();
hc = hc * 31 + high().hashCode();
return hc;
}
@@ -63,7 +71,7 @@ class BetweenExpression extends AbstractExpression {
@Override
public boolean isSameByBind(SpiExpression other) {
BetweenExpression that = (BetweenExpression) other;
return valueLow.equals(that.valueLow)
&& valueHigh.equals(that.valueHigh);
return low().equals(that.low())
&& high().equals(that.high());
}
}
@@ -32,11 +32,15 @@ class BetweenPropertyExpression extends NonPrepareExpression {
return propName;
}
private Object val() {
return NamedParamHelp.value(value);
}
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
context.startBoolMust();
context.writeSimple(Op.LT_EQ, lowProperty, value);
context.writeSimple(Op.GT_EQ, highProperty, value);
context.writeSimple(Op.LT_EQ, lowProperty, val());
context.writeSimple(Op.GT_EQ, highProperty, val());
context.endBool();
}
@@ -72,7 +76,7 @@ class BetweenPropertyExpression extends NonPrepareExpression {
@Override
public void addBindValues(SpiExpressionRequest request) {
request.addBindValue(value);
request.addBindValue(val());
}
@Override
@@ -89,7 +93,7 @@ class BetweenPropertyExpression extends NonPrepareExpression {
@Override
public int queryBindHash() {
return value.hashCode();
return val().hashCode();
}
@Override
@@ -106,6 +110,6 @@ class BetweenPropertyExpression extends NonPrepareExpression {
@Override
public boolean isSameByBind(SpiExpression other) {
BetweenPropertyExpression that = (BetweenPropertyExpression) other;
return value.equals(that.value);
return val().equals(that.val());
}
}
@@ -7,18 +7,22 @@ import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import java.io.IOException;
class CaseInsensitiveEqualExpression extends AbstractExpression {
class CaseInsensitiveEqualExpression extends AbstractValueExpression {
private final String value;
CaseInsensitiveEqualExpression(String propertyName, Object value) {
super(propertyName, value);
}
CaseInsensitiveEqualExpression(String propertyName, String value) {
super(propertyName);
this.value = value.toLowerCase();
/**
* Return the bind value taking into account named parameters.
*/
private String val() {
return strValue().toLowerCase();
}
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
context.writeIEqualTo(propName, value);
context.writeIEqualTo(propName, val());
}
@Override
@@ -31,7 +35,7 @@ class CaseInsensitiveEqualExpression extends AbstractExpression {
request.addBindEncryptKey(encryptKey);
}
request.addBindValue(value);
request.addBindValue(val());
}
@Override
@@ -54,7 +58,7 @@ class CaseInsensitiveEqualExpression extends AbstractExpression {
@Override
public int queryBindHash() {
return value.hashCode();
return val().hashCode();
}
@Override
@@ -70,6 +74,6 @@ class CaseInsensitiveEqualExpression extends AbstractExpression {
@Override
public boolean isSameByBind(SpiExpression other) {
CaseInsensitiveEqualExpression that = (CaseInsensitiveEqualExpression) other;
return value.equals(that.value);
return val().equals(that.val());
}
}
@@ -88,6 +88,11 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
}
}
@Override
public void simplify() {
// do nothing
}
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
if (!list.isEmpty()) {
@@ -157,6 +157,13 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
return new CaseInsensitiveEqualExpression(propertyName, value);
}
/**
* Create for named parameter use (and without support for equalsWithNullAsNoop).
*/
public Expression ieqObject(String propertyName, Object value) {
return new CaseInsensitiveEqualExpression(propertyName, value);
}
/**
* Between - property between the two given values.
*/
@@ -251,6 +258,11 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
return new DefaultExampleExpression(checkEntityBean(example), caseInsensitive, likeType);
}
@Override
public Expression like(String propertyName, Object value, boolean caseInsensitive, LikeType likeType) {
return new LikeExpression(propertyName, value, caseInsensitive, likeType);
}
/**
* Like - property like value where the value contains the SQL wild card
* characters % (percentage) and _ (underscore).
@@ -97,6 +97,16 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
}
}
void simplifyEntries() {
for (SpiExpression element : list) {
element.simplify();
}
}
public void simplify() {
simplifyEntries();
}
/**
* Write being aware if it is the Top level "text" expressions.
* <p>
@@ -36,6 +36,11 @@ class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpress
this.subQuery = null;
}
@Override
public void simplify() {
// do nothing
}
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
throw new IllegalStateException("Not supported");
@@ -1,35 +1,54 @@
package com.avaje.ebeaninternal.server.expression;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
class InExpression extends AbstractExpression {
private final boolean not;
private final Object[] values;
private final Collection<?> sourceValues;
InExpression(String propertyName, Collection<?> coll, boolean not) {
private Object[] bindValues;
InExpression(String propertyName, Collection<?> sourceValues, boolean not) {
super(propertyName);
this.values = coll.toArray(new Object[coll.size()]);
this.sourceValues = sourceValues;
this.not = not;
}
InExpression(String propertyName, Object[] array, boolean not) {
super(propertyName);
this.values = array;
this.sourceValues = Arrays.asList(array);
this.not = not;
}
private Object[] values() {
List<Object> vals = new ArrayList<Object>();
for (Object sourceValue : sourceValues) {
NamedParamHelp.valueAdd(vals, sourceValue);
}
return vals.toArray();
}
@Override
public void prepareExpression(BeanQueryRequest<?> request) {
bindValues = values();
}
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
context.writeIn(propName, values, not);
context.writeIn(propName, values(), not);
}
@Override
@@ -40,13 +59,13 @@ class InExpression extends AbstractExpression {
prop = null;
}
for (int i = 0; i < values.length; i++) {
for (int i = 0; i < bindValues.length; i++) {
if (prop == null) {
request.addBindValue(values[i]);
request.addBindValue(bindValues[i]);
} else {
// extract the id values from the bean
Object[] ids = prop.getAssocIdValues((EntityBean) values[i]);
Object[] ids = prop.getAssocIdValues((EntityBean) bindValues[i]);
if (ids != null) {
for (int j = 0; j < ids.length; j++) {
request.addBindValue(ids[j]);
@@ -59,7 +78,7 @@ class InExpression extends AbstractExpression {
@Override
public void addSql(SpiExpressionRequest request) {
if (values.length == 0) {
if (bindValues.length == 0) {
String expr = not ? "1=1" : "1=0";
request.append(expr);
return;
@@ -72,7 +91,7 @@ class InExpression extends AbstractExpression {
if (prop != null) {
request.append(prop.getAssocIdInExpr(propName));
String inClause = prop.getAssocIdInValueExpr(values.length);
String inClause = prop.getAssocIdInValueExpr(bindValues.length);
request.append(inClause);
} else {
@@ -81,7 +100,7 @@ class InExpression extends AbstractExpression {
request.append(" not");
}
request.append(" in (?");
for (int i = 1; i < values.length; i++) {
for (int i = 1; i < bindValues.length; i++) {
request.append(", ").append("?");
}
@@ -94,15 +113,15 @@ class InExpression extends AbstractExpression {
*/
@Override
public void queryPlanHash(HashQueryPlanBuilder builder) {
builder.add(InExpression.class).add(propName).add(values.length).add(not);
builder.bind(values.length);
builder.add(InExpression.class).add(propName).add(bindValues.length).add(not);
builder.bind(bindValues.length);
}
@Override
public int queryBindHash() {
int hc = 31;
for (int i = 0; i < values.length; i++) {
hc = 31 * hc + values[i].hashCode();
for (int i = 0; i < bindValues.length; i++) {
hc = 31 * hc + bindValues[i].hashCode();
}
return hc;
}
@@ -116,17 +135,17 @@ class InExpression extends AbstractExpression {
InExpression that = (InExpression) other;
return propName.equals(that.propName)
&& not == that.not
&& values.length == that.values.length;
&& bindValues.length == that.bindValues.length;
}
@Override
public boolean isSameByBind(SpiExpression other) {
InExpression that = (InExpression) other;
if (this.values.length != that.values.length) {
if (this.bindValues.length != that.bindValues.length) {
return false;
}
for (int i = 0; i < values.length; i++) {
if (!values[i].equals(that.values[i])) {
for (int i = 0; i < bindValues.length; i++) {
if (!bindValues[i].equals(that.bindValues[i])) {
return false;
}
}
@@ -38,6 +38,11 @@ class InQueryExpression extends AbstractExpression implements UnsupportedDocStor
this.bindParams = bindParams;
}
@Override
public void simplify() {
// do nothing
}
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
throw new IllegalStateException("Not supported");
@@ -39,9 +39,9 @@ import java.util.Set;
*/
class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, ExpressionList<T> {
protected final DefaultExpressionList<T> exprList;
protected DefaultExpressionList<T> exprList;
protected final Junction.Type type;
protected Junction.Type type;
JunctionExpression(Junction.Type type, Query<T> query, ExpressionList<T> parent) {
this.type = type;
@@ -56,6 +56,31 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
this.exprList = exprList;
}
/**
* Simplify nested expressions where possible.
* <p>
* This is expected to only used after expressions are built via query language parsing.
* </p>
*/
public void simplify() {
exprList.simplifyEntries();
List<SpiExpression> list = exprList.list;
if (list.size() == 1 && list.get(0) instanceof JunctionExpression) {
JunctionExpression nested = (JunctionExpression)list.get(0);
if (type == Type.AND && !nested.type.isText()) {
// and (and (a, b, c)) -> and (a, b, c)
// and (not (a, b, c)) -> not (a, b, c)
// and (or (a, b, c)) -> or (a, b, c)
this.exprList = nested.exprList;
this.type = nested.type;
} else if (type == Type.NOT && nested.type == Type.AND) {
// not (and (a, b, c)) -> not (a, b, c)
this.exprList = nested.exprList;
}
}
}
public SpiExpression copyForPlanKey() {
return new JunctionExpression<T>(type, exprList.copyForPlanKey());
}
@@ -8,24 +8,21 @@ import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import java.io.IOException;
class LikeExpression extends AbstractExpression {
private final String val;
class LikeExpression extends AbstractValueExpression {
private final boolean caseInsensitive;
private final LikeType type;
LikeExpression(String propertyName, String value, boolean caseInsensitive, LikeType type) {
super(propertyName);
LikeExpression(String propertyName, Object value, boolean caseInsensitive, LikeType type) {
super(propertyName, value);
this.caseInsensitive = caseInsensitive;
this.type = type;
this.val = value;
}
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
context.writeLike(propName, val, type, caseInsensitive);
context.writeLike(propName, strValue(), type, caseInsensitive);
}
@Override
@@ -38,7 +35,7 @@ class LikeExpression extends AbstractExpression {
request.addBindEncryptKey(encryptKey);
}
String bindValue = getValue(val, caseInsensitive, type);
String bindValue = getValue(strValue(), caseInsensitive, type);
request.addBindValue(bindValue);
}
@@ -74,7 +71,7 @@ class LikeExpression extends AbstractExpression {
@Override
public int queryBindHash() {
return val.hashCode();
return strValue().hashCode();
}
@Override
@@ -92,7 +89,7 @@ class LikeExpression extends AbstractExpression {
@Override
public boolean isSameByBind(SpiExpression other) {
LikeExpression that = (LikeExpression) other;
return val.equals(that.val);
return strValue().equals(that.strValue());
}
private static String getValue(String value, boolean caseInsensitive, LikeType type) {
@@ -57,6 +57,11 @@ abstract class LogicExpression implements SpiExpression {
this.expTwo = (SpiExpression) expTwo;
}
@Override
public void simplify() {
// do nothing
}
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
@@ -0,0 +1,43 @@
package com.avaje.ebeaninternal.server.expression;
import com.avaje.ebeaninternal.api.SpiNamedParam;
import java.util.Collection;
import java.util.List;
/**
* Helper for evaluating named parameters.
*/
class NamedParamHelp {
/**
* Return the bind value taking into account named parameters.
*/
static Object value(Object val) {
if (val instanceof SpiNamedParam) {
return ((SpiNamedParam) val).getValue();
}
return val;
}
/**
* Return the value as a string.
*/
static String valueAsString(Object val) {
Object value = value(val);
return (value == null) ? null : value.toString();
}
/**
* Add the potentially named parameter(s) to the values.
*/
public static void valueAdd(List<Object> values, Object sourceValue) {
Object value = value(sourceValue);
if (value instanceof Collection) {
values.addAll((Collection)value);
} else {
values.add(value);
}
}
}
@@ -24,6 +24,11 @@ class NestedPathWrapperExpression implements SpiExpression {
this.delegate = delegate;
}
@Override
public void simplify() {
// do nothing
}
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
context.startNested(nestedPath);
@@ -8,6 +8,11 @@ import com.avaje.ebeaninternal.api.SpiExpression;
*/
abstract class NonPrepareExpression implements SpiExpression {
@Override
public void simplify() {
// do nothing
}
@Override
public void prepareExpression(BeanQueryRequest<?> request) {
// do nothing
@@ -17,6 +17,11 @@ class NoopExpression implements SpiExpression {
protected static final NoopExpression INSTANCE = new NoopExpression();
@Override
public void simplify() {
// do nothing
}
@Override
public SpiExpression copyForPlanKey() {
return this;
@@ -22,6 +22,11 @@ final class NotExpression implements SpiExpression {
this.exp = (SpiExpression) exp;
}
@Override
public void simplify() {
// do nothing
}
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
context.startBoolMustNot();
@@ -9,22 +9,19 @@ import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import java.io.IOException;
public class SimpleExpression extends AbstractExpression {
public class SimpleExpression extends AbstractValueExpression {
private final Op type;
private final Object value;
public SimpleExpression(String propertyName, Op type, Object value) {
super(propertyName);
super(propertyName, value);
this.type = type;
this.value = value;
}
@Override
public Object getIdEqualTo(String idName) {
if (type == Op.EQ && idName.equals(propName)) {
return value;
return value();
}
return null;
}
@@ -37,13 +34,13 @@ public class SimpleExpression extends AbstractExpression {
ExpressionPath prop = context.getExpressionPath(propName);
if (prop != null && prop.isAssocId()) {
String idName = prop.getAssocIdExpression(propName, "");
Object[] ids = prop.getAssocIdValues((EntityBean) value);
Object[] ids = prop.getAssocIdValues((EntityBean) value());
if (ids == null || ids.length != 1) {
throw new IllegalArgumentException("Expecting 1 Id value for " + idName + " but got " + ids);
}
context.writeSimple(type, idName, ids[0]);
} else {
context.writeSimple(type, propName, value);
context.writeSimple(type, propName, value());
}
}
@@ -56,7 +53,7 @@ public class SimpleExpression extends AbstractExpression {
}
public Object getValue() {
return value;
return value();
}
@Override
@@ -65,7 +62,7 @@ public class SimpleExpression extends AbstractExpression {
ElPropertyValue prop = getElProp(request);
if (prop != null) {
if (prop.isAssocId()) {
Object[] ids = prop.getAssocIdValues((EntityBean) value);
Object[] ids = prop.getAssocIdValues((EntityBean) value());
if (ids != null) {
for (int i = 0; i < ids.length; i++) {
request.addBindValue(ids[i]);
@@ -83,7 +80,7 @@ public class SimpleExpression extends AbstractExpression {
// prop.getBeanProperty().getScalarType();
}
request.addBindValue(value);
request.addBindValue(value());
}
@Override
@@ -115,7 +112,7 @@ public class SimpleExpression extends AbstractExpression {
@Override
public int queryBindHash() {
return value.hashCode();
return value().hashCode();
}
@Override
@@ -132,6 +129,6 @@ public class SimpleExpression extends AbstractExpression {
@Override
public boolean isSameByBind(SpiExpression other) {
SimpleExpression that = (SimpleExpression) other;
return value.equals(that.value);
return value().equals(that.value());
}
}
@@ -0,0 +1,374 @@
package com.avaje.ebeaninternal.server.grammer;
import com.avaje.ebean.Expression;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.FetchConfig;
import com.avaje.ebean.LikeType;
import com.avaje.ebean.OrderBy;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.grammer.antlr.EQLBaseListener;
import com.avaje.ebeaninternal.server.grammer.antlr.EQLLexer;
import com.avaje.ebeaninternal.server.grammer.antlr.EQLParser;
import com.avaje.ebeaninternal.server.util.ArrayStack;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import java.util.ArrayList;
import java.util.List;
class EqlAdapter<T> extends EQLBaseListener {
private static final OperatorMapping operatorMapping = new OperatorMapping();
private static final String DISTINCT = "distinct";
private static final String NULLS = "nulls";
private static final String ASC = "asc";
private final SpiQuery<T> query;
private final EqlAdapterHelper helper;
private ArrayStack<ExpressionList<T>> textStack;
private ArrayStack<ExpressionList<T>> whereStack;
private boolean textMode;
private List<Object> inValues;
private String inPropertyName;
public EqlAdapter(SpiQuery<T> query) {
this.query = query;
this.helper = new EqlAdapterHelper(this);
}
/**
* Return the current expression list that expressions should be added to.
*/
protected ExpressionList<T> peekExprList() {
if (textMode) {
// return the current text expression list
return _peekText();
}
if (whereStack == null) {
whereStack = new ArrayStack<ExpressionList<T>>();
whereStack.push(query.where());
}
// return the current expression list
return whereStack.peek();
}
private ExpressionList<T> _peekText() {
if (textStack == null) {
textStack = new ArrayStack<ExpressionList<T>>();
// empty so push on the queries base expression list
textStack.push(query.text());
}
// return the current expression list
return textStack.peek();
}
/**
* Push the expression list onto the appropriate stack.
*/
private void pushExprList(ExpressionList<T> list) {
if (textMode) {
textStack.push(list);
} else {
whereStack.push(list);
}
}
/**
* End a list of expressions added by 'OR'.
*/
private void popJunction() {
if (textMode) {
textStack.pop();
} else {
whereStack.pop();
}
}
@Override
public void enterSelect_clause(EQLParser.Select_clauseContext ctx) {
checkChildren(ctx, 4);
if (DISTINCT.equals(child(ctx, 1))) {
query.setDistinct(true);
query.select(child(ctx, 3));
} else {
query.select(child(ctx, 2));
}
}
@Override
public void enterFetch_path(EQLParser.Fetch_pathContext ctx) {
int childCount = ctx.getChildCount();
checkChildren(ctx, 2);
String path = child(ctx, 1);
int noPropertiesLength = 2;
FetchConfig fetchConfig = ParseFetchConfig.parse(path);
if (fetchConfig != null) {
noPropertiesLength = 3;
path = child(ctx, 2);
}
if (childCount == noPropertiesLength) {
query.fetch(path, fetchConfig);
} else {
String fetchProperties = trimParenthesis(ctx.getChild(noPropertiesLength).getText());
query.fetch(path, fetchProperties, fetchConfig);
}
}
@Override
public void enterOrderby_property(EQLParser.Orderby_propertyContext ctx) {
int childCount = ctx.getChildCount();
String path = child(ctx, 0);
boolean asc = true;
String nulls = null;
String nullsFirstLast = null;
if (childCount == 3) {
asc = child(ctx, 1).startsWith(ASC);
nullsFirstLast = ctx.getChild(2).getChild(1).getText();
nulls = NULLS;
} else if (childCount == 2) {
String firstChild = child(ctx, 1);
if (firstChild.startsWith(NULLS)) {
nullsFirstLast = ctx.getChild(1).getChild(1).getText();
nulls = NULLS;
} else {
asc = firstChild.startsWith(ASC);
}
}
query.orderBy().add(new OrderBy.Property(path, asc, nulls, nullsFirstLast));
}
@Override
public void enterLimit_clause(EQLParser.Limit_clauseContext ctx) {
try {
String limitValue = child(ctx, 1);
query.setMaxRows(Integer.parseInt(limitValue));
int childCount = ctx.getChildCount();
if (childCount == 3) {
ParseTree offsetTree = ctx.getChild(2);
String offsetValue = offsetTree.getChild(1).getText();
query.setFirstRow(Integer.parseInt(offsetValue));
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Error parsing limit or offset parameter - not an integer", e);
}
}
/**
* Trim leading '(' and trailing ')'
*/
private String trimParenthesis(String text) {
text = text.substring(1);
text = text.substring(0, text.length()-1);
return text;
}
private String getLeftHandSidePath(ParserRuleContext ctx) {
TerminalNode pathToken = ctx.getToken(EQLLexer.PATH_VARIABLE, 0);
return pathToken.getText();
}
@Override
public void enterBetween_expression(EQLParser.Between_expressionContext ctx) {
checkChildren(ctx, 5);
String path = getLeftHandSidePath(ctx);
EqlOperator op = getOperator(ctx);
if (op != EqlOperator.BETWEEN) {
throw new IllegalStateException("Expecting BETWEEN operator but got "+op);
}
helper.addBetween(path, child(ctx,2), child(ctx,4));
}
@Override
public void enterPropertyBetween_expression(EQLParser.PropertyBetween_expressionContext ctx) {
checkChildren(ctx, 5);
String rawValue = child(ctx,0);
EqlOperator op = getOperator(ctx);
if (op != EqlOperator.BETWEEN) {
throw new IllegalStateException("Expecting BETWEEN operator but got "+op);
}
helper.addBetweenProperty(rawValue, child(ctx,2), child(ctx,4));
}
@Override
public void enterIn_expression(EQLParser.In_expressionContext ctx) {
this.inValues = new ArrayList<Object>();
this.inPropertyName = getLeftHandSidePath(ctx);
}
@Override
public void enterIn_value(EQLParser.In_valueContext ctx) {
int childCount = ctx.getChildCount();
for (int i = 0; i < childCount; i++) {
String text = child(ctx, i);
if (isValue(text)) {
inValues.add(helper.bind(text));
}
}
}
private String child(ParserRuleContext ctx, int position) {
ParseTree child = ctx.getChild(position);
return child.getText();
}
private boolean isValue(String text) {
if (text.length() == 1 && (text.equals("(") || text.equals(")") || text.equals(","))) {
return false;
}
return true;
}
@Override
public void exitIn_expression(EQLParser.In_expressionContext ctx) {
helper.addIn(inPropertyName, inValues);
}
@Override
public void enterIsNull_expression(EQLParser.IsNull_expressionContext ctx) {
String path = getLeftHandSidePath(ctx);
peekExprList().isNull(path);
}
@Override
public void enterIsNotNull_expression(EQLParser.IsNotNull_expressionContext ctx) {
String path = getLeftHandSidePath(ctx);
peekExprList().isNotNull(path);
}
@Override
public void enterIsEmpty_expression(EQLParser.IsEmpty_expressionContext ctx) {
String path = getLeftHandSidePath(ctx);
peekExprList().isEmpty(path);
}
@Override
public void enterIsNotEmpty_expression(EQLParser.IsNotEmpty_expressionContext ctx) {
String path = getLeftHandSidePath(ctx);
peekExprList().isNotEmpty(path);
}
@Override
public void enterLike_expression(EQLParser.Like_expressionContext ctx) {
addExpression(ctx);
}
@Override
public void enterComparison_expression(EQLParser.Comparison_expressionContext ctx) {
addExpression(ctx);
}
private void addExpression(ParserRuleContext ctx) {
int childCount = ctx.getChildCount();
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);
}
// RHS is Path, Literal or Named input parameter
helper.addExpression(path, op, child(ctx, 2));
}
@Override
public void enterConditional_term(EQLParser.Conditional_termContext ctx) {
int childCount = ctx.getChildCount();
if (childCount > 1) {
pushExprList(peekExprList().and());
}
}
@Override
public void exitConditional_term(EQLParser.Conditional_termContext ctx) {
if (ctx.getChildCount() > 1) {
popJunction();
}
}
@Override
public void enterConditional_expression(EQLParser.Conditional_expressionContext ctx) {
if (ctx.getChildCount() > 1) {
pushExprList(peekExprList().or());
}
}
@Override
public void exitConditional_expression(EQLParser.Conditional_expressionContext ctx) {
if (ctx.getChildCount() > 1) {
popJunction();
}
}
@Override
public void enterConditional_factor(EQLParser.Conditional_factorContext ctx) {
if (ctx.getChildCount() > 1) {
pushExprList(peekExprList().not());
}
}
@Override
public void exitConditional_factor(EQLParser.Conditional_factorContext ctx) {
if (ctx.getChildCount() > 1) {
popJunction();
}
}
private EqlOperator getOperator(ParserRuleContext ctx) {
String operator = child(ctx,1);
EqlOperator op = operatorMapping.get(operator);
if (op == null) {
throw new IllegalStateException("No operator found for " + operator);
}
return op;
}
/**
* Check for the minimum number of children.
*/
private void checkChildren(ParserRuleContext ctx, int min) {
if (ctx.getChildCount() < min) {
throw new IllegalStateException("expecting " + min + " children for comparison? " + ctx);
}
}
public Object namedParam(String parameterName) {
return query.createNamedParameter(parameterName);
}
public Expression like(boolean caseInsensitive, LikeType likeType, String property, Object bindValue) {
return query.getExpressionFactory().like(property, bindValue, caseInsensitive, likeType);
}
public Expression ieq(String property, Object bindValue) {
return query.getExpressionFactory().ieqObject(property, bindValue);
}
}

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