Compare commits

..
Author SHA1 Message Date
Robin Bygrave 2b00899513 [maven-release-plugin] prepare release avaje-ebeanorm-7.2.3 2016-03-23 18:20:35 +13:00
Robin Bygrave 6fe557e237 #616 - No effective change - rename methods 2016-03-23 18:11:25 +13:00
Robin Bygrave a227f8c469 #616 - ElasticSearch expression - IllegalArgumentException: unhandled type class org.example.domain.Customer 2016-03-23 18:08:37 +13:00
Robin Bygrave a9d7ea0299 #614 - 'Matches for the concatenated key columns where not found' - fix test to run on Postgres 2016-03-23 17:11:39 +13:00
Robin Bygrave 0c4a2d5664 #614 - 'Matches for the concatenated key columns where not found' - fix 2016-03-23 17:03:55 +13:00
Robin Bygrave 577dbad9bd #614 - 'Matches for the concatenated key columns where not found' - tests 2016-03-23 17:01:43 +13:00
Robin Bygrave ae28599825 No effective change - use existing CamelCaseHelper implementation 2016-03-23 16:10:50 +13:00
Robin Bygrave 0627976f6d No effective change - format 2016-03-23 14:39:24 +13:00
Robin Bygrave 7d89b97e2b #615 - ENH: Change DDL generation with @SoftDelete to add 'default false' to column definition 2016-03-23 11:10:35 +13:00
Robin Bygrave 90ee903c52 No effective change - alter tests to run against Oracle 2016-03-23 10:41:55 +13:00
Robin Bygrave ffa68f6815 No effective change - alter tests to run against Oracle 2016-03-23 10:41:42 +13:00
Robin Bygrave 2cb0794a01 #612 - ENH: Add deletePermanent() to com.avaje.ebean.Model 2016-03-22 12:06:39 +13:00
Robin Bygrave 67a267f843 #604 - @SoftDelete with deletePermanent() on lazy loaded @OneToOne 2016-03-22 11:57:44 +13:00
Robin Bygrave e57ce7a6c4 #611 - findRowCount ignores @SoftDelete, does not add predicate 2016-03-22 10:35:20 +13:00
Robin Bygrave 5d3d73aac8 [maven-release-plugin] prepare for next development iteration 2016-03-22 09:47:16 +13:00
Robin Bygrave 436988694d [maven-release-plugin] prepare release avaje-ebeanorm-7.2.2 2016-03-22 09:46:58 +13:00
Robin Bygrave 90f786babd #610 - ManyToOne returns wrong ID value (prior ManyToOne with inheritance and null value). Regression introduced in 7.2.1. 2016-03-22 09:46:12 +13:00
Robin Bygrave 0d8146d453 [maven-release-plugin] prepare for next development iteration 2016-03-21 12:20:17 +13:00
Robin Bygrave 0eca613b41 [maven-release-plugin] prepare release avaje-ebeanorm-7.2.1 2016-03-21 12:19:57 +13:00
Robin Bygrave dcde54fca4 #100 - SQLITE (365) : ADD CONSTRAINT DDL SQLite Incompatibility 2016-03-21 11:06:52 +13:00
Robin Bygrave 49b8efbc30 #609 - SQLite DDL - disable use of comments and alter table add foreign key (as these are not supported by SQLite) 2016-03-21 10:31:57 +13:00
Robin Bygrave fc70161909 No effective change - bump SQLite jdbc driver for testing 2016-03-21 10:31:42 +13:00
Rob Bygrave 44ba6d3bb2 Merge pull request #608 from btimo/fixSQLiteDdl
SQLite Ddl : autoincrement attribute
2016-03-21 10:14:05 +13:00
Robin Bygrave d56e071f53 No effective change - javadoc only 2016-03-21 09:20:11 +13:00
Robin Bygrave ce33c230c7 No effective change - javadoc only 2016-03-21 08:43:33 +13:00
btimo 2da6a6960e - fix SQLiteDdl : https://www.sqlite.org/autoinc.html 2016-03-18 17:55:29 +01:00
Robin Bygrave 62f88c9e4f #607 - bool conversion for and(), or() and not() 2016-03-18 16:19:41 +13:00
Robin Bygrave 42f65ead47 #607 - must(), should(), mustNot() always mark setUseDocStore(true); 2016-03-18 16:17:32 +13:00
Robin Bygrave 8e98e88e8a #607 - Tidy "text" extensions to criteria API - remove endMust(), endShould(), endMustNot() ... just use endJunction() 2016-03-18 14:24:07 +13:00
Robin Bygrave 5de67e1939 #606 - Add not() as a junction expression (like and() and or()) to the query criteria API 2016-03-18 14:11:51 +13:00
Robin Bygrave 3ddd7099b5 #605 - Add and() and or() ... as synonyms for conjunction() and disjunction() ... to the "Query Criteria API" 2016-03-18 13:06:10 +13:00
Robin Bygrave 5d889790c2 No effective change - move related methods together 2016-03-18 12:53:15 +13:00
Robin Bygrave 636acc61e5 #594 ElasticSearch - Add match / TextExpression support - move expressions onto ExpressionList (remove TextExpressionList) 2016-03-18 12:51:39 +13:00
Robin Bygrave cb347ff5fa #602 - Elastic - Add support for ebean.docstore.generateMapping 2016-03-15 17:10:03 +13:00
Robin Bygrave db9d39ef61 #583 - MSSQL Server - Delete query syntax error, mssql does not like the table alias in the delete statement 2016-03-15 01:03:08 +13:00
Robin Bygrave 5216014d50 #601 - Second Nested filterMany not executed but joined 2016-03-14 23:54:34 +13:00
Robin Bygrave c40715733e #600 - Unique index or primary key violation - double insert attempted on ManyToMany intersection 2016-03-14 22:32:03 +13:00
Robin Bygrave 9bcc8a77fe No effective change - imports 2016-03-14 21:28:14 +13:00
Robin Bygrave 7be3a01823 No effective change - tidy up tests 2016-03-14 21:06:48 +13:00
Robin Bygrave 0ca4f92b26 #592 - Fetched empty collection of null child of parent entity 2016-03-14 20:10:07 +13:00
Robin Bygrave b99f002968 #599 - ElasticSearch - add query join support for find, findList, findPagedList, findEach, findEachWhile 2016-03-14 17:40:26 +13:00
Robin Bygrave fa451078b7 No effective change - cleanup imports 2016-03-11 21:56:11 +13:00
Robin Bygrave 49121a9c1f #595 - Refactor - persistence context root type handling - fully expect root type 2016-03-11 20:54:57 +13:00
Robin Bygrave 33f69902dc #595 - Refactor - persistence context root type handling - contextGet() and contextRef() 2016-03-11 20:26:03 +13:00
Robin Bygrave cf783fbbb7 #595 - Refactor - persistence context root type handling - AssocOne split simple and inherit 2016-03-11 19:40:02 +13:00
Robin Bygrave cf62ea4c2f #595 - Refactor - persistence context root type handling 2016-03-11 17:34:45 +13:00
Robin Bygrave 049bf76cc4 #594 - ElasticSearch - Add support for TextSimple, TextQueryString and TextCommon full text search expressions 2016-03-11 14:52:27 +13:00
Robin Bygrave 75a84af3f8 #594 - ElasticSearch - Use AbstractTextExpression 2016-03-10 11:30:42 +13:00
Robin Bygrave 93680f5d19 Bump agent to 4.9.2 2016-03-10 11:29:51 +13:00
Robin Bygrave faf7df1c69 #594 - ElasticSearch - multi match support 2016-03-09 23:44:23 +13:00
Robin Bygrave 34dcc98908 #594 - ElasticSearch - Add match / TextExpression support (Full text search expression) 2016-03-09 21:48:22 +13:00
Robin Bygrave 3c63b79324 #589 - ElasticSearch - Improve update notification when not mapped but has nested invalidation 2016-03-08 22:14:23 +13:00
Robin Bygrave 2d93d592c1 #593 - Refactor SPI - Move methods from ElPropertyValue to ExpressionPath 2016-03-08 21:34:27 +13:00
Robin Bygrave 57836af5b5 #593 - Refactor SPI - Move methods from ElPropertyValue to ExpressionPath 2016-03-08 21:21:46 +13:00
Robin Bygrave ba871d6684 #593 - Refactor SPI - Move methods from ElPropertyValue to ExpressionPath 2016-03-08 21:09:49 +13:00
Robin Bygrave 583fc11945 #593 - Refactor SPI - Move methods from ElPropertyValue to ExpressionPath 2016-03-08 21:02:39 +13:00
Robin Bygrave 3ba4660bbb No effective change - format code 2016-03-08 18:33:32 +13:00
Robin Bygrave 37b86f2191 #589 - ElasticSearch - refactor to DocQueryContext 2016-03-08 00:00:02 +13:00
Robin Bygrave da8766533f #589 - ElasticSearch - extend mapping options 2016-03-07 22:00:41 +13:00
Robin Bygrave ae31117b8f #590 - Refactor internals - Remove duplicate isBatch() logic in JdbcTransaction 2016-03-07 19:23:50 +13:00
Robin Bygrave cf825f7ac4 #589 - ElasticSearch, Transaction - rename DocStoreMode and batch size methods 2016-03-07 19:15:08 +13:00
Robin Bygrave a0609b1726 #589 - ElasticSearch - DocStoreMode renamed from DocStoreEvent 2016-03-07 19:10:15 +13:00
Robin Bygrave 32bcd5e853 #589 - ElasticSearch - JsonReader given BeanType 2016-03-07 16:27:16 +13:00
Robin Bygrave f082fce1ff Bump pom to 7.2.1-SNAPSHOT 2016-03-07 16:26:18 +13:00
Robin Bygrave 7fc58a1842 #589 - ElasticSearch - DocStoreConfig ... mapping path, suffix, resources 2016-03-07 14:21:09 +13:00
Robin Bygrave f2ffbb2c5b #589 - ElasticSearch - Add copyIndex() via Query, Add findEachWhile() 2016-03-07 13:49:23 +13:00
Robin Bygrave 5c0fb7c8cd #584 - ElasticSearch: Internal changes - Enum and UUID tests 2016-03-04 00:04:58 +13:00
Robin Bygrave 04c9db1e42 #586 - Refactor the UUID ScalarTypes (Native, Varchar and Binary) to have a common base 2016-03-04 00:00:05 +13:00
Robin Bygrave 8f7de950e2 No effective change - reformat, early check for UUID type 2016-03-03 23:56:30 +13:00
Robin Bygrave 53dd810b9a #587 - Refactor - Change JsonContext.getScalar() to JsonContext.writeScalar() 2016-03-03 23:38:09 +13:00
Robin Bygrave 290ca6ba90 #586 - Refactor the UUID ScalarTypes (Native, Varchar and Binary) to have a common base 2016-03-03 22:10:28 +13:00
Robin Bygrave 992f5797a7 #586 - Refactor the UUID ScalarTypes (Native, Varchar and Binary) to have a common base 2016-03-03 22:10:04 +13:00
Robin Bygrave 798976fbd2 #585 - Refactor of ScalarType jsonWrite() method such that it only takes JsonGenerator - part 4, tidy up 2016-03-03 21:39:10 +13:00
Robin Bygrave c315067ff0 #585 - Refactor of ScalarType jsonWrite() method such that it only takes JsonGenerator - part 3, remove JsonToken from jsonRead() 2016-03-03 21:06:09 +13:00
Robin Bygrave a4f0e3936a #585 - Refactor of ScalarType jsonWrite() method such that it only takes JsonGenerator - part 2, use JsonGenerator 2016-03-03 20:27:13 +13:00
Robin Bygrave fc96685ed0 #585 - Refactor of ScalarType jsonWrite() method such that it only takes JsonGenerator - part 1, remove field name 2016-03-03 19:51:07 +13:00
Robin Bygrave 5b10d06348 #584 - ElasticSearch: Internal changes for config, expressions and JSON conversion 2016-03-03 17:46:32 +13:00
Robin Bygrave ca73eff519 #582 - ElasticSearch: Add setUseDocStore(true) on ExpressionList ... for fluid query style 2016-03-02 23:25:58 +13:00
Robin Bygrave 646de50ae8 #581 - ElasticSearch - DocStoreConfig, read dropCreate not read from properties file 2016-03-02 23:22:13 +13:00
Robin Bygrave e2558a1e24 #578 - Deprecated Joda Time API - LocalDate.toDateMidnight() usage causes exception to be thrown, change to LocalDate.toDateTimeAtStartOfDay() 2016-03-02 08:12:53 +13:00
Robin Bygrave ad596a2043 #580 - @Enumerated(EnumType.STRING) on Enum uses toString() rather than name() 2016-03-01 17:35:55 +13:00
Robin Bygrave 0a60cd4e58 [maven-release-plugin] prepare for next development iteration 2016-03-01 16:17:14 +13:00
279 changed files with 7857 additions and 3764 deletions
+4 -4
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>7.1.1</version>
<version>7.2.3</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -152,7 +152,7 @@
<dependency>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-agent</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
<scope>test</scope>
</dependency>
@@ -168,7 +168,7 @@
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.7.2</version>
<version>3.8.11.2</version>
<scope>test</scope>
</dependency>
@@ -219,7 +219,7 @@
<plugin>
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
<version>4.9.1</version>
<version>4.9.2</version>
<executions>
<!-- Not going to enhance Model bean -->
<execution>
+159 -10
View File
@@ -1,5 +1,6 @@
package com.avaje.ebean;
import com.avaje.ebeanservice.docstore.api.DocQueryRequest;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
@@ -26,13 +27,16 @@ public interface DocumentStore {
* that will fetch a lot of beans. The default bulkBatchSize is used.
* </p>
*
* @param query The query used to update the associated document store.
* @param query The query that selects object to send to the document store.
*/
<T> void indexByQuery(Query<T> query);
/**
* Update the associated document store index using the result of the query additionally specifying a
* bulkBatchSize to use for sending the messages to ElasticSearch.
*
* @param query The query that selects object to send to the document store.
* @param bulkBatchSize The batch size to use when bulk sending to the document store.
*/
<T> void indexByQuery(Query<T> query, int bulkBatchSize);
@@ -40,28 +44,68 @@ public interface DocumentStore {
* Update the document store for all beans of this type.
* <p>
* This is the same as indexByQuery where the query has no predicates and so fetches all rows.
* </p>
*/
void indexAll(Class<?> beanType);
/**
* Return the bean by fetching it's content from the document store.
* If the document is not found null is returned.
* <p>
* Typically this is called indirectly by findUnique() on the query.
* </p>
* <pre>{@code
*
* Customer customer =
* server.find(Customer.class)
* .setUseDocStore(true)
* .setId(42)
* .findUnique();
*
* }</pre>
*/
@Nullable
<T> T getById(Class<T> beanType, Object id);
<T> T find(DocQueryRequest<T> request);
/**
* Execute the query against the document store returning the list.
* Execute the find list query. This request is prepared to execute secondary queries.
* <p>
* Typically this is called indirectly by findList() on the query that has setUseDocStore(true).
* </p>
* <pre>{@code
*
* List<Customer> newCustomers =
* server.find(Customer.class)
* .setUseDocStore(true)
* .where().eq("status, Customer.Status.NEW)
* .findList();
*
* }</pre>
*/
<T> List<T> findList(Query<T> query);
<T> List<T> findList(DocQueryRequest<T> request);
/**
* Execute the query against the document store returning the paged list.
* <p>
* The query should have <code>firstRow</code> or <code>maxRows</code> set prior to calling this method.
* </p>
* <p>
* Typically this is called indirectly by findPagedList() on the query that has setUseDocStore(true).
* </p>
*
* <pre>{@code
*
* PagedList<Customer> newCustomers =
* server.find(Customer.class)
* .setUseDocStore(true)
* .where().eq("status, Customer.Status.NEW)
* .setMaxRows(50)
* .findPagedList();
*
* }</pre>
*
*/
<T> PagedList<T> findPagedList(Query<T> query);
<T> PagedList<T> findPagedList(DocQueryRequest<T> request);
/**
* Execute the query against the document store with the expectation of a large set of results
@@ -69,8 +113,59 @@ public interface DocumentStore {
* <p>
* For example, with the ElasticSearch doc store this uses SCROLL.
* </p>
* <p>
* Typically this is called indirectly by findEach() on the query that has setUseDocStore(true).
* </p>
*
* <pre>{@code
*
* server.find(Order.class)
* .setUseDocStore(true)
* .where()... // perhaps add predicates
* .findEach(new QueryEachConsumer<Order>() {
* @Override
* public void accept(Order bean) {
* // process the bean
* }
* });
*
* }</pre>
*/
<T> void findEach(Query<T> query, QueryEachConsumer<T> consumer);
<T> void findEach(DocQueryRequest<T> query, QueryEachConsumer<T> consumer);
/**
* Execute the query against the document store with the expectation of a large set of results
* that are processed in a scrolling resultSet fashion.
* <p>
* Unlike findEach() this provides the opportunity to stop iterating through the large query.
* </p>
* <p>
* For example, with the ElasticSearch doc store this uses SCROLL.
* </p>
* <p>
* Typically this is called indirectly by findEachWhile() on the query that has setUseDocStore(true).
* </p>
*
*
* <pre>{@code
*
* server.find(Order.class)
* .setUseDocStore(true)
* .where()... // perhaps add predicates
* .findEachWhile(new QueryEachWhileConsumer<Order>() {
* @Override
* public void accept(Order bean) {
* // process the bean
*
* // return true to continue, false to stop
* // boolean shouldContinue = ...
* return shouldContinue;
* }
* });
*
* }</pre>
*/
<T> void findEachWhile(DocQueryRequest<T> query, QueryEachWhileConsumer<T> consumer);
/**
* Process the queue entries sending updates to the document store or queuing them for later processing.
@@ -79,17 +174,36 @@ public interface DocumentStore {
/**
* Drop the index from the document store (similar to DDL drop table).
*
* <pre>{@code
*
* DocumentStore documentStore = server.docStore();
*
* documentStore.dropIndex("product_copy");
*
* }</pre>
*
*/
void dropIndex(String indexName);
/**
* Create an index given a mapping file as a resource in the classPath (similar to DDL create table).
*
* <pre>{@code
*
* DocumentStore documentStore = server.docStore();
*
* // uses product_copy.mapping.json resource
* // ... to define mappings for the index
*
* documentStore.createIndex("product_copy", null);
*
* }</pre>
*
* @param indexName the name of the new index
* @param alias the alias of the index
* @param mappingResource the path of the mapping file as a resource in the classpath
*/
void createIndex(String indexName, String alias, String mappingResource);
void createIndex(String indexName, String alias);
/**
* Copy the index to a new index.
@@ -97,9 +211,14 @@ public interface DocumentStore {
* This copy process does not use the database but instead will copy from the source index to a destination index.
* </p>
*
* <pre>{@code
*
* long copyCount = documentStore.copyIndex(Product.class, "product_copy");
*
* }</pre>
*
* @param beanType The bean type of the source index
* @param newIndex The name of the index to copy to
*
* @return the number of documents copied to the new index
*/
long copyIndex(Class<?> beanType, String newIndex);
@@ -111,11 +230,41 @@ public interface DocumentStore {
* To support this the document needs to have a <code>@WhenModified</code> property.
* </p>
*
* <pre>{@code
*
* long copyCount = documentStore.copyIndex(Product.class, "product_copy", sinceMillis);
*
* }</pre>
*
* @param beanType The bean type of the source index
* @param newIndex The name of the index to copy to
*
* @return the number of documents copied to the new index
*/
long copyIndex(Class<?> beanType, String newIndex, long sinceEpochMillis);
/**
* Copy from a source index to a new index taking only the documents
* matching the given query.
*
* <pre>{@code
*
* // predicates to select the source documents to copy
* Query<Product> query = server.find(Product.class)
* .where()
* .ge("whenModified", new Timestamp(since))
* .ge("name", "A")
* .lt("name", "D")
* .query();
*
* // copy from the source index to "product_copy" index
* long copyCount = documentStore.copyIndex(query, "product_copy", 1000);
*
* }</pre>
*
* @param query The query to select the source documents to copy
* @param newIndex The target index to copy the documents to
* @param bulkBatchSize The ElasticSearch bulk batch size, if 0 uses the default.
* @return The number of documents copied to the new index.
*/
long copyIndex(Query<?> query, String newIndex, int bulkBatchSize);
}
@@ -1,10 +1,8 @@
package com.avaje.ebean;
import java.io.Serializable;
/**
* An expression that is part of a WHERE or HAVING clause.
*/
public interface Expression extends Serializable {
public interface Expression {
}
@@ -1,5 +1,11 @@
package com.avaje.ebean;
import com.avaje.ebean.search.Match;
import com.avaje.ebean.search.MultiMatch;
import com.avaje.ebean.search.TextCommonTerms;
import com.avaje.ebean.search.TextQueryString;
import com.avaje.ebean.search.TextSimple;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -285,6 +291,31 @@ public interface ExpressionFactory {
*/
Expression raw(String raw);
/**
* Create a Text Match expression (currently doc store/Elastic only).
*/
Expression textMatch(String propertyName, String search, Match options);
/**
* Create a Text Multi match expression (currently doc store/Elastic only).
*/
Expression textMultiMatch(String query, MultiMatch options);
/**
* Create a text simple query expression (currently doc store/Elastic only).
*/
Expression textSimple(String search, TextSimple options);
/**
* Create a text query string expression (currently doc store/Elastic only).
*/
Expression textQueryString(String search, TextQueryString options);
/**
* Create a text common terms expression (currently doc store/Elastic only).
*/
Expression textCommonTerms(String search, TextCommonTerms options);
/**
* And - join two expressions with a logical and.
*/
@@ -319,4 +350,13 @@ public interface ExpressionFactory {
* Return a list of expressions that will be joined by OR's.
*/
<T> Junction<T> disjunction(Query<T> query, ExpressionList<T> parent);
/**
* Return a Text query junction for MUST, SHOULD or MUST NOT.
* <p>
* This is doc store Elastic only.
* </p>
*/
<T> Junction<T> junction(Junction.Type type, Query<T> query, ExpressionList<T> parent);
}
@@ -1,9 +1,13 @@
package com.avaje.ebean;
import com.avaje.ebean.search.Match;
import com.avaje.ebean.search.MultiMatch;
import com.avaje.ebean.search.TextCommonTerms;
import com.avaje.ebean.search.TextQueryString;
import com.avaje.ebean.search.TextSimple;
import org.jetbrains.annotations.Nullable;
import javax.persistence.NonUniqueResultException;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.List;
@@ -30,7 +34,7 @@ import java.util.Set;
*
* @see Query#where()
*/
public interface ExpressionList<T> extends Serializable {
public interface ExpressionList<T> {
/**
* Return the query that owns this expression list.
@@ -380,6 +384,32 @@ public interface ExpressionList<T> extends Serializable {
*/
Query<T> setUseQueryCache(boolean useCache);
/**
* Set to true if this query should execute against the doc store.
* <p>
* When setting this you may also consider disabling lazy loading.
* </p>
*/
Query<T> setUseDocStore(boolean useDocsStore);
/**
* Set true if you want to disable lazy loading.
* <p>
* That is, once the object graph is returned further lazy loading is disabled.
* </p>
*/
Query<T> setDisableLazyLoading(boolean disableLazyLoading);
/**
* Disable read auditing for this query.
* <p>
* This is intended to be used when the query is not a user initiated query and instead
* part of the internal processing in an application to load a cache or document store etc.
* In these cases we don't want the query to be part of read auditing.
* </p>
*/
Query<T> setDisableReadAuditing();
/**
* Add expressions to the having clause.
* <p>
@@ -802,6 +832,47 @@ public interface ExpressionList<T> extends Serializable {
*/
ExpressionList<T> raw(String raw);
/**
* Add a match expression.
*
* @param propertyName The property name for the match
* @param search The search value
*/
ExpressionList<T> match(String propertyName, String search);
/**
* Add a match expression with options.
*
* @param propertyName The property name for the match
* @param search The search value
*/
ExpressionList<T> match(String propertyName, String search, Match options);
/**
* Add a multi-match expression.
*/
ExpressionList<T> multiMatch(String search, String... properties);
/**
* Add a multi-match expression using options.
*/
ExpressionList<T> multiMatch(String search, MultiMatch options);
/**
* Add a simple query string expression.
*/
ExpressionList<T> textSimple(String search, TextSimple options);
/**
* Add a query string expression.
*/
ExpressionList<T> textQueryString(String search, TextQueryString options);
/**
* Add common terms expression.
*/
ExpressionList<T> textCommonTerms(String search, TextCommonTerms options);
/**
* And - join two expressions with a logical and.
*/
@@ -818,20 +889,168 @@ public interface ExpressionList<T> extends Serializable {
ExpressionList<T> not(Expression exp);
/**
* Return a list of expressions that will be joined by AND's.
* Start a list of expressions that will be joined by AND's
* returning the expression list the expressions are added to.
* <p>
* This is exactly the same as conjunction();
* </p>
* <p>
* Use endJunction() to end the AND junction.
* </p>
* <p>
* Note that a where() clause defaults to an AND junction so
* typically you only explicitly need to use the and() junction
* when it is nested inside an or() or not() junction.
* </p>
*
* <pre>{@code
*
* // Example: Nested and()
*
* Ebean.find(Customer.class)
* .where()
* .or()
* .and() // nested and
* .startsWith("name", "r")
* .eq("anniversary", onAfter)
* .endJunction() // end AND junction
* .and()
* .eq("status", Customer.Status.ACTIVE)
* .gt("id", 0)
* .endJunction() // end AND junction
* .orderBy().asc("name")
* .findList();
* }</pre>
*/
Junction<T> and();
/**
* Return a list of expressions that will be joined by OR's.
* This is exactly the same as disjunction();
*
* <p>
* Use endJunction() to end the OR junction.
* </p>
*
* <pre>{@code
*
* // Example: Use or() to join
* // two nested and() expressions
*
* Ebean.find(Customer.class)
* .where()
* .or()
* .and()
* .startsWith("name", "r")
* .eq("anniversary", onAfter)
* .endJunction() // end AND junction
* .and()
* .eq("status", Customer.Status.ACTIVE)
* .gt("id", 0)
* .endJunction() // end AND junction
* .orderBy().asc("name")
* .findList();
*
* }</pre>
*/
Junction<T> or();
/**
* Return a list of expressions that will be wrapped by NOT.
* <p>
* Use endJunction() to end expressions being added to the
* NOT expression list.
* </p>
*
* <pre>@{code
*
* .where()
* .not()
* .gt("id", 1)
* .eq("anniversary", onAfter)
* .endJunction() // end the not expressions
*
* }</pre>
*
* <pre>@{code
*
* // Example: nested not()
*
* Ebean.find(Customer.class)
* .where()
* .eq("status", Customer.Status.ACTIVE)
* .not()
* .gt("id", 1)
* .eq("anniversary", onAfter)
* .endJunction() // end the not expressions
* .orderBy()
* .asc("name")
* .findList();
*
* }</pre>
*/
Junction<T> not();
/**
* Start (and return) a list of expressions that will be joined by AND's.
* <p>
* This is the same as and().
* </p>
*/
Junction<T> conjunction();
/**
* Return a list of expressions that will be joined by OR's.
* Start (and return) a list of expressions that will be joined by OR's.
* <p>
* This is the same as or().
* </p>
*/
Junction<T> disjunction();
/**
* End a Conjunction or Disjunction returning the parent expression list.
* Start a list of expressions that will be joined by MUST.
* <p>
* Alternatively you can always use where() to return the top level expression
* list.
* This automatically makes the query a useDocStore(true) query that
* will execute against the document store (ElasticSearch etc).
* </p>
* <p>
* This is logically similar to and().
* </p>
*/
Junction<T> must();
/**
* Start a list of expressions that will be joined by SHOULD.
* <p>
* This automatically makes the query a useDocStore(true) query that
* will execute against the document store (ElasticSearch etc).
* </p>
* <p>
* This is logically similar to or().
* </p>
*/
Junction<T> should();
/**
* Start a list of expressions that will be joined by MUST NOT.
* <p>
* This automatically makes the query a useDocStore(true) query that
* will execute against the document store (ElasticSearch etc).
* </p>
* <p>
* This is logically similar to not().
* </p>
*/
Junction<T> mustNot();
/**
* End a junction returning the parent expression list.
* <p>
* Ends a and(), or(), not(), must(), mustNot() or should() junction
* such that you get the parent expression.
* </p>
* <p>
* Alternatively you can always use where() to return the top level expression list.
* </p>
*/
ExpressionList<T> endJunction();
+104 -45
View File
@@ -9,70 +9,129 @@ package com.avaje.ebean;
* <p>
* Note: where() always takes you to the top level WHERE expression list.
* </p>
*
* <pre class="code">
*
* <pre>{@code
* Query q =
* Ebean.find(Person.class)
* .where().disjunction()
* .like(&quot;name&quot;, &quot;Rob%&quot;)
* .eq(&quot;status&quot;, Status.NEW)
*
* // where() returns us to the top level expression list
* .where().gt(&quot;id&quot;, 10);
*
* .where()
* .or()
* .like("name", "Rob%")
* .eq("status", Status.NEW)
*
* // where() returns us to the top level expression list
* .where().gt("id", 10);
*
* // read as...
* // where ( ((name like Rob%) or (status = NEW)) AND (id &gt; 10) )
* </pre>
*
* }</pre>
*
* <p>
* Note: endJunction() takes you to the parent expression list
* </p>
*
* <pre class="code">
*
* <pre>{@code
* Query q =
* Ebean.find(Person.class)
* .where().disjunction()
* .like(&quot;name&quot;, &quot;Rob%&quot;)
* .eq(&quot;status&quot;, Status.NEW)
* .endJunction()
*
* // endJunction().. takes us to the 'parent' expression list
* // which in this case is the top level (same as where())
*
* .gt(&quot;id&quot;, 10);
*
* .where()
* .or()
* .like("name", "Rob%")
* .eq("status", Status.NEW)
* .endJunction()
*
* // endJunction().. takes us to the 'parent' expression list
* // which in this case is the top level (same as where())
*
* .gt("id", 10);
*
* // read as...
* // where ( ((name like Rob%) or (status = NEW)) AND (id &gt; 10) )
* </pre>
*
* // where ( ((name like Rob%) or (status = NEW)) AND (id > 10) )
* }</pre>
*
* <p>
* Example of a nested disjunction.
* </p>
*
* <pre class="code">
* Query&lt;Customer&gt; q =
*
* <pre>{@code
* Query<Customer> q =
* Ebean.find(Customer.class)
* .where()
* .disjunction()
* .conjunction()
* .startsWith(&quot;name&quot;, &quot;r&quot;)
* .eq(&quot;anniversary&quot;, onAfter)
* .endJunction()
* .conjunction()
* .eq(&quot;status&quot;, Customer.Status.ACTIVE)
* .gt(&quot;id&quot;, 0)
* .endJunction()
* .order().asc(&quot;name&quot;);
*
* .or()
* .and()
* .startsWith("name", "r")
* .eq("anniversary", onAfter)
* .endJunction()
* .and()
* .eq("status", Customer.Status.ACTIVE)
* .gt("id", 0)
* .endJunction()
* .order().asc("name");
*
* q.findList();
* String s = q.getGeneratedSql();
*
*
* // this produces an expression like:
*
* ( name like ? and c.anniversary = ? ) or (c.status = ? and c.id &gt; ? )
*
* </pre>
* ( name like ? and c.anniversary = ? ) or (c.status = ? and c.id > ? )
*
* }</pre>
*/
public interface Junction<T> extends Expression, ExpressionList<T> {
/**
* The type of Junction used in full text expressions.
*/
enum Type {
/**
* AND group.
*/
AND(" and ", ""),
/**
* OR group.
*/
OR(" or ", ""),
/**
* NOT group.
*/
NOT(" and ", "not "),
/**
* Text search AND group.
*/
MUST("must", ""),
/**
* Text search NOT group.
*/
MUST_NOT("must_not", ""),
/**
* Text search OR group.
*/
SHOULD("should", "");
String prefix;
String literal;
Type(String literal, String prefix) {
this.literal = literal;
this.prefix = prefix;
}
/**
* Return the literal value for this type.
*/
public String literal() {
return literal;
}
/**
* Return the prefix value for this type.
*/
public String prefix() {
return prefix;
}
}
}
+13
View File
@@ -270,6 +270,19 @@ public abstract class Model {
return db().delete(this);
}
/**
* Delete a bean permanently without soft delete.
* <p>
* This is used when the bean contains a <code>@SoftDelete</code> property and we
* want to perform a hard/permanent delete.
* </p>
*
* @see EbeanServer#deletePermanent(Object)
*/
public boolean deletePermanent() {
return db().deletePermanent(this);
}
/**
* Perform an update using this entity against the specified server.
*/
+21 -6
View File
@@ -3,7 +3,6 @@ package com.avaje.ebean;
import org.jetbrains.annotations.Nullable;
import javax.persistence.NonUniqueResultException;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
@@ -1042,6 +1041,27 @@ public interface Query<T> {
*/
ExpressionList<T> where();
/**
* Add Full text search expressions for Document store queries.
* <p>
* This is currently ElasticSearch only and provides the full text
* expressions such as Match and Multi-Match.
* </p>
* <p>
* This automatically makes this query a "Doc Store" query and will execute
* against the document store (ElasticSearch).
* </p>
* <p>
* Expressions added here are added to the "query" section of an ElasticSearch
* query rather than the "filter" section.
* </p>
* <p>
* Expressions added to the where() are added to the "filter" section of an
* ElasticSearch query.
* </p>
*/
ExpressionList<T> text();
/**
* This applies a filter on the 'many' property list rather than the root
* level objects.
@@ -1373,9 +1393,4 @@ public interface Query<T> {
*/
Set<String> validate();
/**
* Return the query in JSON form for ElasticSearch doc store.
*/
String asElasticQuery();
}
@@ -1,6 +1,6 @@
package com.avaje.ebean;
import com.avaje.ebean.annotation.DocStoreEvent;
import com.avaje.ebean.annotation.DocStoreMode;
import com.avaje.ebean.config.DocStoreConfig;
import com.avaje.ebean.config.PersistBatch;
@@ -92,7 +92,7 @@ public interface Transaction extends Closeable {
* to control how that is sent to the document store.
* </p>
*/
void setDocStoreUpdateMode(DocStoreEvent updateMode);
void setDocStoreMode(DocStoreMode mode);
/**
* Set the batch size to use for sending messages to the document store.
@@ -104,7 +104,7 @@ public interface Transaction extends Closeable {
* Setting this overrides the default of {@link DocStoreConfig#getBulkBatchSize()}
* </p>
*/
void setDocStoreUpdateBatchSize(int batchSize);
void setDocStoreBatchSize(int batchSize);
/**
* Explicitly turn off or on the cascading nature of save() and delete(). This
@@ -18,27 +18,7 @@ public @interface DocMapping {
String name();
/**
* Set this to true to indicate that this property should be un-analysed.
* Mapping options.
*/
boolean code() default false;
/**
* Set this to true to get an additional un-analysed 'raw' field to use for sorting etc.
*/
boolean sortable() default false;
/**
* Set to true to have the property additionally stored separately from _source.
*/
boolean store() default false;
/**
* Set a boost value specific to this property.
*/
float boost() default 1;
/**
* Set a value to use instead of null.
*/
String nullValue() default "";
DocProperty options();
}
@@ -36,4 +36,75 @@ public @interface DocProperty {
* Set a value to use instead of null.
*/
String nullValue() default "";
/**
* Set this to false to exclude this from the _all property.
*/
boolean includeInAll() default true;
/**
* The analyzer to use.
*/
String analyzer() default "";
/**
* The analyzer to use for searches.
*/
String searchAnalyzer() default "";
/**
* The index options for this property.
*/
Option options() default Option.DEFAULT;
/**
* Set this to false such that doc values are not stored separately for this property.
*/
boolean docValues() default true;
/**
* Set a copyTo field.
*/
String copyTo() default "";
/**
* Set to false to disable the field from indexing, it will only be get/set in _source.
*/
boolean enabled() default true;
/**
* Set to false such that norms are not stored.
*/
boolean norms() default true;
/**
* Index options for a property.
*/
enum Option {
/**
* Only index the doc number.
*/
DOCS,
/**
* Doc number and term frequencies are indexed.
*/
FREQS,
/**
* Doc number, term frequencies and term positions are indexed.
*/
POSITIONS,
/**
* Doc number, term frequencies, term positions and start/end offsets are indexed.
*/
OFFSETS,
/**
* Use the default which means analysed string properties use POSITIONS as the default and all other types use DOCS.
*/
DEFAULT
}
}
@@ -45,22 +45,22 @@ public @interface DocStore {
/**
* Specify the behavior when bean Insert, Update, Delete events occur.
*/
DocStoreEvent persist() default DocStoreEvent.DEFAULT;
DocStoreMode persist() default DocStoreMode.DEFAULT;
/**
* Specify the behavior when bean Insert occurs.
*/
DocStoreEvent insert() default DocStoreEvent.DEFAULT;
DocStoreMode insert() default DocStoreMode.DEFAULT;
/**
* Specify the behavior when bean Update occurs.
*/
DocStoreEvent update() default DocStoreEvent.DEFAULT;
DocStoreMode update() default DocStoreMode.DEFAULT;
/**
* Specify the behavior when bean Delete occurs.
*/
DocStoreEvent delete() default DocStoreEvent.DEFAULT;
DocStoreMode delete() default DocStoreMode.DEFAULT;
/**
* Specify to include only some properties in the doc store document.
@@ -8,7 +8,7 @@ package com.avaje.ebean.annotation;
* for later processing rather than look to update ElasticSearch at that time.
* </p>
*/
public enum DocStoreEvent {
public enum DocStoreMode {
/**
* Add the event to the queue for processing later (delaying the update to the document store).
@@ -31,6 +31,11 @@ public interface BeanCollection<E> extends Serializable {
ALL
}
/**
* Load bean from another collection.
*/
void loadFrom(BeanCollection<?> other);
/**
* Add a bean to the list/set with modifyListen notification.
*/
@@ -12,7 +12,7 @@ public interface PersistenceContext {
/**
* Put the entity bean into the PersistenceContext.
*/
void put(Object id, Object bean);
void put(Class<?> rootType, Object id, Object bean);
/**
* Put the entity bean into the PersistenceContext if one is not already
@@ -22,19 +22,19 @@ public interface PersistenceContext {
* returns null.
* </p>
*/
Object putIfAbsent(Object id, Object bean);
Object putIfAbsent(Class<?> rootType, Object id, Object bean);
/**
* Return an object given its type and unique id.
*/
Object get(Class<?> beanType, Object uid);
Object get(Class<?> rootType, Object uid);
/**
* Get the bean from the persistence context also checked to see if it had
* been previously deleted (if so then you also can't hit the L2 cache to
* fetch the bean for this particular persistence context).
*/
WithOption getWithOption(Class<?> beanType, Object uid);
WithOption getWithOption(Class<?> rootType, Object uid);
/**
* Clear all the references.
@@ -44,22 +44,22 @@ public interface PersistenceContext {
/**
* Clear all the references for a given type of entity bean.
*/
void clear(Class<?> beanType);
void clear(Class<?> rootType);
/**
* Clear the reference to a specific entity bean.
*/
void clear(Class<?> beanType, Object uid);
void clear(Class<?> rootType, Object uid);
/**
* Clear the reference as a result of an entity being deleted.
*/
void deleted(Class<?> beanType, Object id);
void deleted(Class<?> rootType, Object id);
/**
* Return the number of beans of the given type in the persistence context.
*/
int size(Class<?> beanType);
int size(Class<?> rootType);
/**
* Wrapper on a bean to also indicate if a bean has been deleted.
@@ -0,0 +1,21 @@
package com.avaje.ebean.bean;
import javax.persistence.Entity;
/**
* Utility to find the root bean type.
*/
public class PersistenceContextUtil {
/**
* Find and return the root bean type for the given class.
*/
public static Class<?> root(Class<?> beanType) {
Class<?> parent = beanType.getSuperclass();
while (parent != null && parent.isAnnotationPresent(Entity.class)) {
beanType = parent;
parent = parent.getSuperclass();
}
return beanType;
}
}
@@ -1,5 +1,6 @@
package com.avaje.ebean.common;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
@@ -64,6 +65,15 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
list.add((E) bean);
}
@Override
@SuppressWarnings("unchecked")
public void loadFrom(BeanCollection<?> other) {
if (list == null) {
list = new ArrayList<E>();
}
list.addAll((Collection<? extends E>) other.getActualDetails());
}
@SuppressWarnings("unchecked")
public void internalAdd(Object bean) {
if (list == null) {
@@ -1,5 +1,6 @@
package com.avaje.ebean.common;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
@@ -51,6 +52,14 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
return !touched && (map == null || map.isEmpty());
}
@Override
@SuppressWarnings("unchecked")
public void loadFrom(BeanCollection<?> other) {
BeanMap<K,E> otherMap = (BeanMap<K,E>)other;
internalPutNull();
map.putAll(otherMap.getActualMap());
}
public void internalPutNull() {
if (map == null) {
map = new LinkedHashMap<K, E>();
@@ -1,5 +1,6 @@
package com.avaje.ebean.common;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
@@ -57,6 +58,15 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
set.add((E) bean);
}
@Override
@SuppressWarnings("unchecked")
public void loadFrom(BeanCollection<?> other) {
if (set == null) {
set = new LinkedHashSet<E>();
}
set.addAll((Collection<? extends E>) other.getActualDetails());
}
@Override
public void internalAddWithCheck(Object bean) {
if (set == null || !set.contains(bean)) {
@@ -1,42 +1,70 @@
package com.avaje.ebean.config;
import com.avaje.ebean.annotation.DocStoreEvent;
import com.avaje.ebean.annotation.DocStoreMode;
/**
* Configuration for the Document store (ElasticSearch) integration.
* Configuration for the Document store integration (e.g. ElasticSearch).
*/
public class DocStoreConfig {
/**
* True when the Document store integration is active/on.
*/
boolean active;
protected boolean active;
/**
* When true the Document store should drop and re-create any document mapping (like DDL).
* Set to true means Ebean will generate mapping files on startup.
*/
boolean dropCreate;
protected boolean generateMapping;
/**
* When true the Document store should drop and re-create document indexes.
*/
protected boolean dropCreate;
/**
* When true the Document store should create any document indexes that don't already exist.
*/
protected boolean create;
/**
* The URL of the Document store server. For example: http://localhost:9200.
*/
String url;
protected String url;
/**
* The default mode used by indexes.
*/
DocStoreEvent persist = DocStoreEvent.UPDATE;
protected DocStoreMode persist = DocStoreMode.UPDATE;
/**
* The default batch size to use for the Bulk API calls.
*/
int bulkBatchSize = 1000;
protected int bulkBatchSize = 1000;
/**
* Resource path for the Document store mapping files.
*/
protected String mappingPath;
/**
* Suffix used for mapping files.
*/
protected String mappingSuffix;
/**
* Location of resources that mapping files are generated into.
*/
protected String pathToResources = "src/main/resources";
/**
* Return true if the Document store (ElasticSearch) integration is active.
*/
public boolean isActive() {
String systemValue = System.getProperty("ebean.docstore.active");
if (systemValue != null) {
return Boolean.parseBoolean(systemValue);
}
return active;
}
@@ -48,24 +76,134 @@ public class DocStoreConfig {
}
/**
* Return true if the document store should recreate mappings.
* Return the URL to the Document store.
*/
public String getUrl() {
String systemValue = System.getProperty("ebean.docstore.url");
if (systemValue != null) {
return systemValue;
}
return url;
}
/**
* Set the URL to the Document store server.
* <p>
* For a local ElasticSearch server this would be: http://localhost:9200
*/
public void setUrl(String url) {
this.url = url;
}
/**
* Return true if Ebean should generate mapping files on server startup.
*/
public boolean isGenerateMapping() {
return generateMapping;
}
/**
* Set to true if Ebean should generate mapping files on server startup.
*/
public void setGenerateMapping(boolean generateMapping) {
this.generateMapping = generateMapping;
}
/**
* Return true if the document store should recreate mapped indexes.
*/
public boolean isDropCreate() {
return dropCreate;
}
/**
* Set to true if the document store should recreate mappings.
* Set to true if the document store should recreate mapped indexes.
*/
public void setDropCreate(boolean dropCreate) {
this.dropCreate = dropCreate;
}
/**
* Create true if the document store should create mapped indexes that don't yet exist.
* This is only used if dropCreate is false.
*/
public boolean isCreate() {
return create;
}
/**
* Set to true if the document store should create mapped indexes that don't yet exist.
* This is only used if dropCreate is false.
*/
public void setCreate(boolean create) {
this.create = create;
}
/**
* Return the default batch size to use for calls to the Bulk API.
*/
public int getBulkBatchSize() {
return bulkBatchSize;
}
/**
* Set the default batch size to use for calls to the Bulk API.
* <p>
* The batch size can be set on a transaction via {@link com.avaje.ebean.Transaction#setDocStoreBatchSize(int)}.
* </p>
*/
public void setBulkBatchSize(int bulkBatchSize) {
this.bulkBatchSize = bulkBatchSize;
}
/**
* Return the mapping path.
*/
public String getMappingPath() {
return mappingPath;
}
/**
* Set the mapping path.
*/
public void setMappingPath(String mappingPath) {
this.mappingPath = mappingPath;
}
/**
* Return the mapping suffix.
*/
public String getMappingSuffix() {
return mappingSuffix;
}
/**
* Set the mapping suffix.
*/
public void setMappingSuffix(String mappingSuffix) {
this.mappingSuffix = mappingSuffix;
}
/**
* Return the relative file system path to resources when generating mapping files.
*/
public String getPathToResources() {
return pathToResources;
}
/**
* Set the relative file system path to resources when generating mapping files.
*/
public void setPathToResources(String pathToResources) {
this.pathToResources = pathToResources;
}
/**
* Return the default behavior for when Insert, Update and Delete events occur on beans that have an associated
* Document store.
*/
public DocStoreEvent getPersist() {
public DocStoreMode getPersist() {
return persist;
}
@@ -87,44 +225,10 @@ public class DocStoreConfig {
* you don't want Ebean to do anything when the data changes.
* </p>
*/
public void setPersist(DocStoreEvent persist) {
public void setPersist(DocStoreMode persist) {
this.persist = persist;
}
/**
* Return the URL to the Document store.
*/
public String getUrl() {
return url;
}
/**
* Set the URL to the Document store server.
*
* For a local ElasticSearch server this would be: http://localhost:9200
*/
public void setUrl(String url) {
this.url = url;
}
/**
* Return the default batch size to use for calls to the Bulk API.
*/
public int getBulkBatchSize() {
return bulkBatchSize;
}
/**
* Set the default batch size to use for calls to the Bulk API.
* <p>
* The batch size can be set on a transaction via {@link com.avaje.ebean.Transaction#setDocStoreUpdateBatchSize(int)}.
* </p>
*/
public void setBulkBatchSize(int bulkBatchSize) {
this.bulkBatchSize = bulkBatchSize;
}
/**
* Load settings specified in properties files.
*/
@@ -132,7 +236,13 @@ public class DocStoreConfig {
active = properties.getBoolean("docstore.active", active);
url = properties.get("docstore.url", url);
persist = properties.getEnum(DocStoreEvent.class, "docstore.persist", persist);
persist = properties.getEnum(DocStoreMode.class, "docstore.persist", persist);
bulkBatchSize = properties.getInt("docstore.bulkBatchSize", bulkBatchSize);
generateMapping = properties.getBoolean("docstore.generateMapping", generateMapping);
dropCreate = properties.getBoolean("docstore.dropCreate", dropCreate);
create = properties.getBoolean("docstore.create", create);
mappingPath = properties.get("docstore.mappingPath", mappingPath);
mappingSuffix = properties.get("docstore.mappingSuffix", mappingSuffix);
pathToResources = properties.get("docstore.pathToResources", pathToResources);
}
}
@@ -2250,14 +2250,14 @@ public class ServerConfig {
}
/**
* This is broken out for the same reason as above - preserve existing behaviour but let it be overridden.
* This is broken out to allow overridden behaviour.
*/
protected void loadDocStoreSettings(PropertiesWrapper p) {
docStoreConfig.loadSettings(p);
}
/**
* This is broken out for the same reason as above - preserve existing behaviour but let it be overridden.
* This is broken out to allow overridden behaviour.
*/
protected void loadAutoTuneSettings(PropertiesWrapper p) {
autoTuneConfig.loadSettings(p);
@@ -2287,7 +2287,7 @@ public class ServerConfig {
if (docStoreConfig == null) {
docStoreConfig = new DocStoreConfig();
}
docStoreConfig.loadSettings(p);
loadDocStoreSettings(p);
explicitTransactionBeginMode = p.getBoolean("explicitTransactionBeginMode", explicitTransactionBeginMode);
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
@@ -1,5 +1,7 @@
package com.avaje.ebean.config;
import com.avaje.ebean.util.CamelCaseHelper;
/**
* Converts between Camel Case and Underscore based names for both table and
* column names (and is the default naming convention in Ebean).
@@ -141,21 +143,6 @@ public class UnderscoreNamingConvention extends AbstractNamingConvention {
* Convert and return the from string from underscore to camel case.
*/
protected String toCamelFromUnderscore(String underscore) {
StringBuilder result = new StringBuilder(underscore.length());
String[] vals = underscore.split("_");
for (int i = 0; i < vals.length; i++) {
String lower = vals[i].toLowerCase();
if (i > 0) {
char c = Character.toUpperCase(lower.charAt(0));
result.append(c);
result.append(lower.substring(1));
} else {
result.append(lower);
}
}
return result.toString();
return CamelCaseHelper.toCamelFromUnderscore(underscore);
}
}
@@ -18,7 +18,7 @@ public class DB2Platform extends DatabasePlatform {
this.maxTableNameLength = 18;
this.maxConstraintNameLength = 18;
this.sqlLimiter = new Db2SqlLimiter();
this.platformDdl = new DB2Ddl(dbTypeMap, dbIdentity);
this.platformDdl = new DB2Ddl(this);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
@@ -18,7 +18,6 @@ public class DatabasePlatform {
private static final Logger logger = LoggerFactory.getLogger(DatabasePlatform.class);
/**
* Behavior used when ending a query only transaction (at read committed isolation level).
*/
@@ -72,6 +71,11 @@ public class DatabasePlatform {
*/
protected DbTypeMap dbTypeMap = new DbTypeMap();
/**
* Default values for DB columns.
*/
protected DbDefaultValue dbDefaultValue = new DbDefaultValue();
/**
* Set to true if the DB has native UUID type support.
*/
@@ -291,6 +295,13 @@ public class DatabasePlatform {
return dbTypeMap;
}
/**
* Return the mapping for DB column default values.
*/
public DbDefaultValue getDbDefaultValue() {
return dbDefaultValue;
}
/**
* Return the column alias prefix.
*/
@@ -425,6 +436,20 @@ public class DatabasePlatform {
return sqlLimiter;
}
/**
* Set the DB TRUE literal (from the registered boolean ScalarType)
*/
public void setDbTrueLiteral(String dbTrueLiteral) {
this.dbDefaultValue.setTrue(dbTrueLiteral);
}
/**
* Set the DB FALSE literal (from the registered boolean ScalarType)
*/
public void setDbFalseLiteral(String dbFalseLiteral) {
this.dbDefaultValue.setFalse(dbFalseLiteral);
}
/**
* Convert backticks to the platform specific open quote and close quote
* <p>
@@ -0,0 +1,70 @@
package com.avaje.ebean.config.dbplatform;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* DB Column default values mapping to database platform specific literals.
*/
public class DbDefaultValue {
/**
* The key for FALSE.
*/
public static final String FALSE = "false";
/**
* The key for TRUE.
*/
public static final String TRUE = "true";
/**
* The key for the NOW / current timestamp.
*/
public static final String NOW = "now";
protected Map<String,String> map = new LinkedHashMap<String,String>();
/**
* Set the DB now function.
*/
public void setNow(String dbFunction) {
put(NOW, dbFunction);
}
/**
* Set the DB false literal.
*/
public void setFalse(String dbFalseLiteral) {
put(FALSE, dbFalseLiteral);
}
/**
* Set the DB true literal.
*/
public void setTrue(String dbTrueLiteral) {
put(TRUE, dbTrueLiteral);
}
/**
* Add an translation entry.
*/
public void put(String dbLiteral, String dbTranslated) {
map.put(dbLiteral, dbTranslated);
}
/**
* Convert the DB default literal to platform specific type or function.
* <p>
* This is intended for the DB column default clause in DDL.
* </p>
*/
public String convert(String dbDefaultLiteral) {
if (dbDefaultLiteral == null) {
return null;
}
String val = map.get(dbDefaultLiteral);
return val != null ? val : dbDefaultLiteral;
}
}
@@ -14,9 +14,10 @@ public class H2Platform extends DatabasePlatform {
super();
this.name = "h2";
this.dbEncrypt = new H2DbEncrypt();
this.platformDdl = new H2Ddl(this.dbTypeMap, dbIdentity);
this.platformDdl = new H2Ddl(this);
this.historySupport = new H2HistorySupport();
this.nativeUuidType = true;
this.dbDefaultValue.setNow("now()");
// only support getGeneratedKeys with non-batch JDBC
// so generally use SEQUENCE instead of IDENTITY for H2
@@ -15,7 +15,7 @@ public class HsqldbPlatform extends DatabasePlatform {
super();
this.name = "hsqldb";
this.dbEncrypt = new H2DbEncrypt();
this.platformDdl = new HsqldbDdl(dbTypeMap, dbIdentity);
this.platformDdl = new HsqldbDdl(this);
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
@@ -25,7 +25,7 @@ public class MsSqlServer2005Platform extends DatabasePlatform {
this.idInExpandedForm = true;
this.selectCountWithAlias = true;
this.sqlLimiter = new MsSqlServer2005SqlLimiter();
this.platformDdl = new MsSqlServerDdl(dbTypeMap, dbIdentity);
this.platformDdl = new MsSqlServerDdl(this);
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsIdentity(true);
@@ -25,7 +25,7 @@ public class MySqlPlatform extends DatabasePlatform {
this.likeClause = "like ? escape''";
this.selectCountWithAlias = true;
this.dbEncrypt = new MySqlDbEncrypt();
this.platformDdl = new MySqlDdl(this.dbTypeMap, this.dbIdentity);
this.platformDdl = new MySqlDdl(this);
this.historySupport = new MySqlHistorySupport();
this.dbIdentity.setIdType(IdType.IDENTITY);
@@ -18,7 +18,7 @@ public class OraclePlatform extends DatabasePlatform {
this.maxConstraintNameLength = 30;
this.dbEncrypt = new OracleDbEncrypt();
this.sqlLimiter = new RownumSqlLimiter();
this.platformDdl = new Oracle10Ddl(this.dbTypeMap, this.dbIdentity);
this.platformDdl = new Oracle10Ddl(this);
this.historySupport = new OracleDbHistorySupport();
// Not using getGeneratedKeys as instead we will
@@ -27,7 +27,7 @@ public class PostgresPlatform extends DatabasePlatform {
this.dbEncrypt = new PostgresDbEncrypt();
this.historySupport = new PostgresHistorySupport();
this.platformDdl = new PostgresDdl(this.dbTypeMap, this.dbIdentity);
this.platformDdl = new PostgresDdl(this);
// Use Identity and getGeneratedKeys
this.dbIdentity.setIdType(IdType.IDENTITY);
@@ -9,7 +9,7 @@ public class SQLitePlatform extends DatabasePlatform {
public SQLitePlatform() {
super();
this.name = "sqlite";
this.platformDdl = new SQLiteDdl(dbTypeMap, dbIdentity);
this.platformDdl = new SQLiteDdl(this);
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(false);
@@ -123,6 +123,9 @@ public class BaseTableDdl implements TableDdl {
// defined on the columns
writePrimaryKeyConstraint(apply, createTable.getPkName(), toColumnNames(pk));
}
if (platformDdl.isInlineForeignKeys()) {
writeInlineForeignKeys(writer, createTable);
}
apply.newLine().append(")");
addTableCommentInline(apply, createTable);
@@ -151,8 +154,9 @@ public class BaseTableDdl implements TableDdl {
apply.end();
writer.dropAll().end();
writeAddForeignKeys(writer, createTable);
if (!platformDdl.isInlineForeignKeys()) {
writeAddForeignKeys(writer, createTable);
}
}
/**
@@ -235,6 +239,43 @@ public class BaseTableDdl implements TableDdl {
platformDdl.createWithHistory(writer, table);
}
protected void writeInlineForeignKeys(DdlWrite write, CreateTable createTable) throws IOException {
for (Column column : createTable.getColumn()) {
String references = column.getReferences();
if (hasValue(references)) {
writeInlineForeignKey(write, column);
}
}
writeInlineCompoundForeignKeys(write, createTable);
}
protected void writeInlineForeignKey(DdlWrite write, Column column) throws IOException {
String references = column.getReferences();
int pos = references.lastIndexOf('.');
if (pos == -1) {
throw new IllegalStateException("Expecting period '.' character for table.column split but not found in [" + references + "]");
}
String refTableName = references.substring(0, pos);
String refColumnName = references.substring(pos + 1);
String fkConstraint = platformDdl.tableInlineForeignKey(new String[]{column.getName()}, refTableName, new String[]{refColumnName});
write.apply().append(",").newLine().append(" ").append(fkConstraint);
}
protected void writeInlineCompoundForeignKeys(DdlWrite write, CreateTable createTable) throws IOException {
List<ForeignKey> foreignKey = createTable.getForeignKey();
for (ForeignKey key : foreignKey) {
String refTableName = key.getRefTableName();
String[] cols = toColumnNamesSplit(key.getColumnNames());
String[] refColumns = toColumnNamesSplit(key.getRefColumnNames());
String fkConstraint = platformDdl.tableInlineForeignKey(cols, refTableName, refColumns);
write.apply().append(",").newLine().append(" ").append(fkConstraint);
}
}
protected void writeAddForeignKeys(DdlWrite write, CreateTable createTable) throws IOException {
String tableName = createTable.getName();
@@ -309,7 +350,10 @@ public class BaseTableDdl implements TableDdl {
protected void alterTableAddForeignKey(DdlBuffer buffer, String fkName, String tableName, String[] columns, String refTable, String[] refColumns) throws IOException {
buffer.append(platformDdl.alterTableAddForeignKey(tableName, fkName, columns, refTable, refColumns)).endOfStatement();
String fkConstraint = platformDdl.alterTableAddForeignKey(tableName, fkName, columns, refTable, refColumns);
if (fkConstraint != null && !fkConstraint.isEmpty()) {
buffer.append(fkConstraint).endOfStatement();
}
}
protected void appendColumns(String[] columns, DdlBuffer buffer) throws IOException {
@@ -1,15 +1,14 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.dbplatform.DbIdentity;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
/**
* DB2 platform specific DDL.
*/
public class DB2Ddl extends PlatformDdl {
public DB2Ddl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
super(platformTypes, dbIdentity);
public DB2Ddl(DatabasePlatform platform) {
super(platform);
this.identitySuffix = " generated by default as identity";
}
@@ -1,15 +1,14 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.dbplatform.DbIdentity;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
/**
* H2 platform specific DDL.
*/
public class H2Ddl extends PlatformDdl {
public H2Ddl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
super(platformTypes, dbIdentity);
public H2Ddl(DatabasePlatform platform) {
super(platform);
this.historyDdl = new H2HistoryDdl();
}
@@ -1,15 +1,14 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.dbplatform.DbIdentity;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
/**
* Hsqldb platform specific DDL.
*/
public class HsqldbDdl extends PlatformDdl {
public HsqldbDdl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
super(platformTypes, dbIdentity);
public HsqldbDdl(DatabasePlatform platform) {
super(platform);
this.identitySuffix = " generated by default as identity (start with 1) ";
}
@@ -1,7 +1,6 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.dbplatform.DbIdentity;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer;
import com.avaje.ebean.dbmigration.migration.AlterColumn;
@@ -12,8 +11,8 @@ import java.io.IOException;
*/
public class MsSqlServerDdl extends PlatformDdl {
public MsSqlServerDdl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
super(platformTypes, dbIdentity);
public MsSqlServerDdl(DatabasePlatform platform) {
super(platform);
this.identitySuffix = " identity(1,1)";
this.foreignKeyRestrict = "";
this.inlineUniqueOneToOne = false;
@@ -1,7 +1,6 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.dbplatform.DbIdentity;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer;
import com.avaje.ebean.dbmigration.migration.AlterColumn;
import com.avaje.ebean.dbmigration.migration.Column;
@@ -14,8 +13,8 @@ import java.io.IOException;
*/
public class MySqlDdl extends PlatformDdl {
public MySqlDdl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
super(platformTypes, dbIdentity);
public MySqlDdl(DatabasePlatform platform) {
super(platform);
this.alterColumn = "modify";
this.dropUniqueConstraint = "drop index";
this.historyDdl = new MySqlHistoryDdl();
@@ -1,15 +1,14 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.dbplatform.DbIdentity;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
/**
* Oracle platform specific DDL.
*/
public class Oracle10Ddl extends PlatformDdl {
public Oracle10Ddl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
super(platformTypes, dbIdentity);
public Oracle10Ddl(DatabasePlatform platform) {
super(platform);
this.dropTableIfExists = "drop table ";
this.dropSequenceIfExists = "drop sequence ";
this.dropTableCascade = " cascade constraints purge";
@@ -2,8 +2,9 @@ package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.DbConstraintNaming;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.DbDefaultValue;
import com.avaje.ebean.config.dbplatform.DbIdentity;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebean.config.dbplatform.IdType;
import com.avaje.ebean.dbmigration.ddlgeneration.BaseDdlHandler;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer;
@@ -85,9 +86,17 @@ public class PlatformDdl {
protected DbConstraintNaming naming;
public PlatformDdl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
this.dbIdentity = dbIdentity;
this.typeConverter = new PlatformTypeConverter(platformTypes);
/**
* Generally not desired as then they are not named (used with SQLite).
*/
protected boolean inlineForeignKeys;
protected final DbDefaultValue dbDefaultValue;
public PlatformDdl(DatabasePlatform platform) {
this.dbIdentity = platform.getDbIdentity();
this.dbDefaultValue = platform.getDbDefaultValue();
this.typeConverter = new PlatformTypeConverter(platform.getDbTypeMap());
}
/**
@@ -128,6 +137,14 @@ public class PlatformDdl {
return inlineComments;
}
/**
* Return true if foreign key reference constraints need to inlined with create table.
* Ideally we don't do this as then the constraints are not named. Do this for SQLite.
*/
public boolean isInlineForeignKeys() {
return inlineForeignKeys;
}
/**
* Write all the table columns converting to platform types as necessary.
*/
@@ -152,6 +169,12 @@ public class PlatformDdl {
buffer.append(" ");
buffer.append(lowerColumnName(column.getName()), 29);
buffer.append(platformType);
if (!typeContainsDefault(platformType)) {
String defaultValue = convertDefaultValue(column.getDefaultValue());
if (defaultValue != null) {
buffer.append(" default ").append(defaultValue);
}
}
if (isTrue(column.isNotnull()) || isTrue(column.isPrimaryKey())) {
buffer.append(" not null");
}
@@ -160,6 +183,20 @@ public class PlatformDdl {
// so that the database can potentially provide a nice SQL error
}
/**
* Return true if the type definition already contains a default value.
*/
private boolean typeContainsDefault(String platformType) {
return platformType.toLowerCase().contains(" default");
}
/**
* Convert the DB column default literal to platform specific.
*/
private String convertDefaultValue(String dbDefault) {
return dbDefaultValue.convert(dbDefault);
}
/**
* Return the drop foreign key clause.
*/
@@ -255,6 +292,20 @@ public class PlatformDdl {
return buffer.toString();
}
/**
* Return the foreign key constraint when used inline with create table.
*/
public String tableInlineForeignKey(String[] columns, String refTable, String[] refColumns) {
StringBuilder buffer = new StringBuilder(90);
buffer.append("foreign key");
appendColumns(columns, buffer);
buffer.append(" references ").append(lowerTableName(refTable));
appendColumns(refColumns, buffer);
appendWithSpace(foreignKeyRestrict, buffer);
return buffer.toString();
}
/**
* Add foreign key.
*/
@@ -1,15 +1,14 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.dbplatform.DbIdentity;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
/**
* Postgres specific DDL.
*/
public class PostgresDdl extends PlatformDdl {
public PostgresDdl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
super(platformTypes, dbIdentity);
public PostgresDdl(DatabasePlatform platform) {
super(platform);
this.historyDdl = new PostgresHistoryDdl();
this.dropTableCascade = " cascade";
this.columnSetType = "type ";
@@ -1,16 +1,34 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.dbplatform.DbIdentity;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer;
import java.io.IOException;
/**
* DB2 platform specific DDL.
*/
public class SQLiteDdl extends PlatformDdl {
public SQLiteDdl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
super(platformTypes, dbIdentity);
this.identitySuffix = " autoincrement";
public SQLiteDdl(DatabasePlatform platform) {
super(platform);
this.identitySuffix = "";
this.inlineForeignKeys = true;
}
@Override
public void addTableComment(DdlBuffer apply, String tableName, String tableComment) throws IOException {
// not supported
}
@Override
public void addColumnComment(DdlBuffer apply, String table, String column, String comment) throws IOException {
// not supported
}
@Override
public String alterTableAddForeignKey(String tableName, String fkName, String[] columns, String refTable, String[] refColumns) {
// not supported
return null;
}
}
@@ -230,6 +230,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
}
MColumn col = new MColumn(p.getDbColumn(), ctx.getColumnDefn(p));
col.setDefaultValue(p.getDbColumnDefault());
col.setComment(p.getDbComment());
col.setDraftOnly(p.isDraftOnly());
@@ -5,11 +5,9 @@ import com.avaje.ebean.event.BeanFindController;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistListener;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.text.json.JsonReadOptions;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeanservice.docstore.api.mapping.DocumentMapping;
import com.fasterxml.jackson.core.JsonParser;
import java.io.IOException;
import java.util.Collection;
/**
@@ -17,6 +15,11 @@ import java.util.Collection;
*/
public interface BeanType<T> {
/**
* Return the short name of the bean type.
*/
String getName();
/**
* Return the full name of the bean type.
*/
@@ -53,12 +56,12 @@ public interface BeanType<T> {
Property getWhenCreatedProperty();
/**
* Return the SpiProperty for a property to read values from a bean.
* Return the Property to read values from a bean.
*/
Property getProperty(String propertyName);
/**
* Return the SpiExpressionPath for a given property path.
* Return the ExpressionPath for a given property path.
* <p>
* This can return a property or nested property path.
* </p>
@@ -150,8 +153,27 @@ public interface BeanType<T> {
BeanDocType<T> docStore();
/**
* Read the JSON content returning the bean.
* Add the discriminator value to the query if needed.
*/
T jsonRead(JsonParser parser, JsonReadOptions readOptions, Object objectMapper) throws IOException;
void addInheritanceWhere(SpiQuery<?> query);
/**
* Return the root bean type for an inheritance hierarchy.
*/
BeanType<?> root();
/**
* Return true if this bean type has an inheritance hierarchy.
*/
boolean hasInheritance();
/**
* Return the discriminator column.
*/
String getDiscColumn();
/**
* Create a bean given the discriminator value.
*/
T createBeanUsingDisc(Object discValue);
}
@@ -1,5 +1,8 @@
package com.avaje.ebean.plugin;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.text.StringParser;
/**
* A dot notation expression path.
*/
@@ -10,11 +13,64 @@ public interface ExpressionPath {
*/
boolean containsMany();
/**
* Return the value from a given entity bean.
*/
Object pathGet(Object bean);
/**
* Set a value to the bean for this expression path.
*
* @param bean the bean to set the value on
* @param value the value to set
*/
void set(Object bean, Object value);
void pathSet(Object bean, Object value);
/**
* Convert the value to the expected type.
* <p>
* Typically useful for converting strings to the appropriate number type etc.
* </p>
*/
Object convert(Object value);
/**
* Return the default StringParser for the scalar property.
*/
StringParser getStringParser();
/**
* For DateTime capable scalar types convert the long systemTimeMillis into
* an appropriate java time (Date,Timestamp,Time,Calendar, JODA type etc).
*/
Object parseDateTime(long systemTimeMillis);
/**
* Return true if the last type is "DateTime capable" - can support
* {@link #parseDateTime(long)}.
*/
boolean isDateTimeCapable();
/**
* Return the underlying JDBC type or 0 if this is not a scalar type.
*/
int getJdbcType();
/**
* Return true if this is an ManyToOne or OneToOne associated bean property.
*/
boolean isAssocId();
/**
* Return the Id expression string.
* <p>
* Typically used to produce id = ? expression strings.
* </p>
*/
String getAssocIdExpression(String propName, String bindOperator);
/**
* Return the Id values for the given bean value.
*/
Object[] getAssocIdValues(EntityBean bean);
}
@@ -0,0 +1,98 @@
package com.avaje.ebean.search;
/**
* Options for the text match and multi match expressions.
*/
public abstract class AbstractMatch {
protected boolean operatorAnd;
protected String analyzer;
protected double boost;
protected String minShouldMatch;
protected int maxExpansions;
protected String zeroTerms;
protected double cutoffFrequency;
protected String fuzziness;
protected int prefixLength;
protected String rewrite;
/**
* Return true if using the AND operator otherwise using the OR operator.
*/
public boolean isOperatorAnd() {
return operatorAnd;
}
/**
* Return the boost.
*/
public double getBoost() {
return boost;
}
/**
* Return the minimum should match.
*/
public String getMinShouldMatch() {
return minShouldMatch;
}
/**
* Return the zero terms option.
*/
public String getZeroTerms() {
return zeroTerms;
}
/**
* Return the cutoff frequency.
*/
public double getCutoffFrequency() {
return cutoffFrequency;
}
/**
* Return the max expansions.
*/
public int getMaxExpansions() {
return maxExpansions;
}
/**
* Return the analyzer.
*/
public String getAnalyzer() {
return analyzer;
}
/**
* Return the fuzziness.
*/
public String getFuzziness() {
return fuzziness;
}
/**
* Return the prefix length.
*/
public int getPrefixLength() {
return prefixLength;
}
/**
* Return the rewrite option.
*/
public String getRewrite() {
return rewrite;
}
}
@@ -0,0 +1,117 @@
package com.avaje.ebean.search;
/**
* Options for the text match expression.
*/
public class Match extends AbstractMatch {
protected boolean phrase;
protected boolean phrasePrefix;
public Match() {
}
/**
* Set this to be a "Phrase" type expression.
*/
public Match phrase() {
phrase = true;
return this;
}
/**
* Set this to be a "Phrase Prefix" type expression.
*/
public Match phrasePrefix() {
phrasePrefix = true;
return this;
}
/**
* Use the AND operator (rather than OR).
*/
public Match opAnd() {
operatorAnd = true;
return this;
}
/**
* Use the OR operator (rather than AND).
*/
public Match opOr() {
operatorAnd = false;
return this;
}
/**
* Set the zero terms.
*/
public Match zeroTerms(String zeroTerms) {
this.zeroTerms = zeroTerms;
return this;
}
/**
* Set the cutoff frequency.
*/
public Match cutoffFrequency(double cutoffFrequency) {
this.cutoffFrequency = cutoffFrequency;
return this;
}
/**
* Set the max expansions (for phrase prefix only).
*/
public Match maxExpansions(int maxExpansions) {
this.maxExpansions = maxExpansions;
return this;
}
/**
* Set the Analyzer to use for this expression.
*/
public Match analyzer(String analyzer) {
this.analyzer = analyzer;
return this;
}
/**
* Set the boost.
*/
public Match boost(double boost) {
this.boost = boost;
return this;
}
/**
* Set the rewrite to use.
*/
public Match minShouldMatch(String minShouldMatch) {
this.minShouldMatch = minShouldMatch;
return this;
}
/**
* Set the rewrite to use.
*/
public Match rewrite(String rewrite) {
this.rewrite = rewrite;
return this;
}
/**
* Return true if this is a phrase query.
*/
public boolean isPhrase() {
return phrase;
}
/**
* Return true if this is a phrase prefix query.
*/
public boolean isPhrasePrefix() {
return phrasePrefix;
}
}
@@ -0,0 +1,148 @@
package com.avaje.ebean.search;
/**
* Options for the text match expression.
*/
public class MultiMatch extends AbstractMatch {
/**
* The MultiMatch type.
*/
public enum Type {
BEST_FIELDS,
MOST_FIELDS,
CROSS_FIELDS,
PHRASE,
PHRASE_PREFIX
}
protected final String[] fields;
protected Type type = Type.BEST_FIELDS;
protected double tieBreaker;
/**
* Create with the given fields.
*/
public static MultiMatch fields(String... fields) {
return new MultiMatch(fields);
}
/**
* Construct with a set of fields.
*/
public MultiMatch(String... fields) {
this.fields = fields;
}
/**
* Set the type of query.
*/
public MultiMatch type(Type type) {
this.type = type;
return this;
}
/**
* Set the tieBreaker to use.
*/
public MultiMatch tieBreaker(double tieBreaker) {
this.tieBreaker = tieBreaker;
return this;
}
/**
* Use the AND operator (rather than OR).
*/
public MultiMatch opAnd() {
operatorAnd = true;
return this;
}
/**
* Use the OR operator (rather than AND).
*/
public MultiMatch opOr() {
operatorAnd = false;
return this;
}
/**
* Set the minimum should match value.
*/
public MultiMatch minShouldMatch(String minShouldMatch) {
this.minShouldMatch = minShouldMatch;
return this;
}
/**
* Set the boost.
*/
public MultiMatch boost(double boost) {
this.boost = boost;
return this;
}
/**
* Set the zero terms.
*/
public MultiMatch zeroTerms(String zeroTerms) {
this.zeroTerms = zeroTerms;
return this;
}
/**
* Set the cutoff frequency.
*/
public MultiMatch cutoffFrequency(double cutoffFrequency) {
this.cutoffFrequency = cutoffFrequency;
return this;
}
/**
* Set the max expansions (for phrase prefix only).
*/
public MultiMatch maxExpansions(int maxExpansions) {
this.maxExpansions = maxExpansions;
return this;
}
/**
* Set the Analyzer to use for this expression.
*/
public MultiMatch analyzer(String analyzer) {
this.analyzer = analyzer;
return this;
}
/**
* Set the rewrite to use.
*/
public MultiMatch rewrite(String rewrite) {
this.rewrite = rewrite;
return this;
}
/**
* Return the type.
*/
public Type getType() {
return type;
}
/**
* Return the fields to search.
*/
public String[] getFields() {
return fields;
}
/**
* Return the tie breaker.
*/
public double getTieBreaker() {
return tieBreaker;
}
}
@@ -0,0 +1,141 @@
package com.avaje.ebean.search;
/**
* Text common terms query.
* <p>
* This maps to an ElasticSearch "common terms query".
* </p>
* <pre>{@code
*
* TextCommonTerms options = new TextCommonTerms()
* .cutoffFrequency(0.001)
* .minShouldMatch("50%")
* .lowFreqOperatorAnd(true)
* .highFreqOperatorAnd(true);
*
* List<Customer> customers = server.find(Customer.class)
* .text()
* .textCommonTerms("the brown", options)
* .findList();
*
* }</pre>
*
* <pre>{@code
*
* // ElasticSearch expression
*
* "common": {
* "body": {
* "query": "the brown",
* "cutoff_frequency": 0.001,
* "low_freq_operator": "and",
* "high_freq_operator": "and",
* "minimum_should_match": "50%"
* }
* }
*
* }</pre>
*/
public class TextCommonTerms {
protected double cutoffFrequency;
protected boolean lowFreqOperatorAnd;
protected boolean highFreqOperatorAnd;
protected String minShouldMatch;
protected String minShouldMatchLowFreq;
protected String minShouldMatchHighFreq;
/**
* Set the cutoff frequency.
*/
public TextCommonTerms cutoffFrequency(double cutoffFrequency) {
this.cutoffFrequency = cutoffFrequency;
return this;
}
/**
* Set to true if low frequency terms should use AND operator.
*/
public TextCommonTerms lowFreqOperatorAnd(boolean opAnd) {
this.lowFreqOperatorAnd = opAnd;
return this;
}
/**
* Set to true if high frequency terms should use AND operator.
*/
public TextCommonTerms highFreqOperatorAnd(boolean opAnd) {
this.highFreqOperatorAnd = opAnd;
return this;
}
/**
* Set the minimum should match.
*/
public TextCommonTerms minShouldMatch(String minShouldMatch) {
this.minShouldMatch = minShouldMatch;
return this;
}
/**
* Set the minimum should match for low frequency terms.
*/
public TextCommonTerms minShouldMatchLowFreq(String minShouldMatchLowFreq) {
this.minShouldMatchLowFreq = minShouldMatchLowFreq;
return this;
}
/**
* Set the minimum should match for high frequency terms.
*/
public TextCommonTerms minShouldMatchHighFreq(String minShouldMatchHighFreq) {
this.minShouldMatchHighFreq = minShouldMatchHighFreq;
return this;
}
/**
* Return true if low freq should use the AND operator.
*/
public boolean isLowFreqOperatorAnd() {
return lowFreqOperatorAnd;
}
/**
* Return true if high freq should use the AND operator.
*/
public boolean isHighFreqOperatorAnd() {
return highFreqOperatorAnd;
}
/**
* Return the cutoff frequency.
*/
public double getCutoffFrequency() {
return cutoffFrequency;
}
/**
* Return the minimum to match.
*/
public String getMinShouldMatch() {
return minShouldMatch;
}
/**
* Return the minimum to match for high frequency.
*/
public String getMinShouldMatchHighFreq() {
return minShouldMatchHighFreq;
}
/**
* Return the minimum to match for low frequency.
*/
public String getMinShouldMatchLowFreq() {
return minShouldMatchLowFreq;
}
}
@@ -0,0 +1,436 @@
package com.avaje.ebean.search;
/**
* Text query string options.
* <p>
* This maps to an ElasticSearch "query string query".
* </p>
*
* <pre>{@code
*
* TextQueryString options = new TextQueryString()
* .analyzeWildcard(true)
* .fields("name")
* .lenient(true)
* .opAnd();
*
* List<Customer> customers = server.find(Customer.class)
* .text()
* .textSimple("quick brown", options)
* .findList();
*
* }</pre>
*
*
* <pre>{@code
*
* // just use default options
* TextQueryString options = new TextQueryString();
*
* List<Customer> customers = server.find(Customer.class)
* .text()
* .textSimple("quick brown", options)
* .findList();
*
* }</pre>
*
*/
public class TextQueryString {
public static final int DEFAULT_FUZZY_MAX_EXPANSIONS = 50;
public static final int DEFAULT_MAX_DETERMINIZED_STATES = 10000;
protected final String[] fields;
/**
* Only used when multiple fields set.
*/
protected boolean useDisMax = true;
/**
* Only used when multiple fields set.
*/
protected double tieBreaker;
protected String defaultField;
protected boolean operatorAnd;
protected String analyzer;
protected boolean allowLeadingWildcard = true;
protected boolean lowercaseExpandedTerms = true;
protected boolean enablePositionIncrements = true;
protected int fuzzyMaxExpansions = DEFAULT_FUZZY_MAX_EXPANSIONS;
protected String fuzziness;
protected int fuzzyPrefixLength;
protected double phraseSlop;
protected double boost;
protected boolean analyzeWildcard;
protected boolean autoGeneratePhraseQueries;
protected int maxDeterminizedStates = DEFAULT_MAX_DETERMINIZED_STATES;
protected String minShouldMatch;
protected boolean lenient;
protected String locale;
protected String timeZone;
protected String rewrite;
/**
* Create with given fields.
*/
public static TextQueryString fields(String... fields) {
return new TextQueryString(fields);
}
/**
* Construct with the fields to use.
*/
public TextQueryString(String... fields) {
this.fields = fields;
}
/**
* Use the AND operator (rather than OR).
*/
public TextQueryString opAnd() {
this.operatorAnd = true;
return this;
}
/**
* Use the OR operator (rather than AND).
*/
public TextQueryString opOr() {
this.operatorAnd = false;
return this;
}
/**
* Set the locale.
*/
public TextQueryString locale(String locale) {
this.locale = locale;
return this;
}
/**
* Set lenient mode.
*/
public TextQueryString lenient(boolean lenient) {
this.lenient = lenient;
return this;
}
/**
* Set the minimum should match.
*/
public TextQueryString minShouldMatch(String minShouldMatch) {
this.minShouldMatch = minShouldMatch;
return this;
}
/**
* Set the analyzer.
*/
public TextQueryString analyzer(String analyzer) {
this.analyzer = analyzer;
return this;
}
/**
* Set useDisMax option (when multiple fields only).
*/
public TextQueryString useDisMax(boolean useDisMax) {
this.useDisMax = useDisMax;
return this;
}
/**
* Set tieBreaker option (when multiple fields only).
*/
public TextQueryString tieBreaker(double tieBreaker) {
this.tieBreaker = tieBreaker;
return this;
}
/**
* Set the default field.
*/
public TextQueryString defaultField(String defaultField) {
this.defaultField = defaultField;
return this;
}
/**
* Set allow leading wildcard mode.
*/
public TextQueryString allowLeadingWildcard(boolean allowLeadingWildcard) {
this.allowLeadingWildcard = allowLeadingWildcard;
return this;
}
/**
* Set lowercase expanded terms mode.
*/
public TextQueryString lowercaseExpandedTerms(boolean lowercaseExpandedTerms) {
this.lowercaseExpandedTerms = lowercaseExpandedTerms;
return this;
}
/**
* Set enable position increments mode.
*/
public TextQueryString enablePositionIncrements(boolean enablePositionIncrements) {
this.enablePositionIncrements = enablePositionIncrements;
return this;
}
/**
* Set fuzzy max expansions.
*/
public TextQueryString fuzzyMaxExpansions(int fuzzyMaxExpansions) {
this.fuzzyMaxExpansions = fuzzyMaxExpansions;
return this;
}
/**
* Set fuzziness.
*/
public TextQueryString fuzziness(String fuzziness) {
this.fuzziness = fuzziness;
return this;
}
/**
* Set the fuzzy prefix length.
*/
public TextQueryString fuzzyPrefixLength(int fuzzyPrefixLength) {
this.fuzzyPrefixLength = fuzzyPrefixLength;
return this;
}
/**
* Set the phrase slop.
*/
public TextQueryString phraseSlop(double phraseSlop) {
this.phraseSlop = phraseSlop;
return this;
}
/**
* Set the boost.
*/
public TextQueryString boost(double boost) {
this.boost = boost;
return this;
}
/**
* Set the analyze wildcard mode.
*/
public TextQueryString analyzeWildcard(boolean analyzeWildcard) {
this.analyzeWildcard = analyzeWildcard;
return this;
}
/**
* Set the auto generate phrase queries mode.
*/
public TextQueryString autoGeneratePhraseQueries(boolean autoGeneratePhraseQueries) {
this.autoGeneratePhraseQueries = autoGeneratePhraseQueries;
return this;
}
/**
* Set the max determinized states.
*/
public TextQueryString maxDeterminizedStates(int maxDeterminizedStates) {
this.maxDeterminizedStates = maxDeterminizedStates;
return this;
}
/**
* Set the time zone.
*/
public TextQueryString timeZone(String timeZone) {
this.timeZone = timeZone;
return this;
}
/**
* Set the rewrite option.
*/
public TextQueryString rewrite(String rewrite) {
this.rewrite = rewrite;
return this;
}
/**
* Return the rewrite option.
*/
public String getRewrite() {
return rewrite;
}
/**
* Return the fields.
*/
public String[] getFields() {
return fields;
}
/**
* Return true if AND is the default operator.
*/
public boolean isOperatorAnd() {
return operatorAnd;
}
/**
* Return the analyzer.
*/
public String getAnalyzer() {
return analyzer;
}
/**
* Return the locale.
*/
public String getLocale() {
return locale;
}
/**
* Return lenient mode.
*/
public boolean isLenient() {
return lenient;
}
/**
* Return the minimum should match.
*/
public String getMinShouldMatch() {
return minShouldMatch;
}
/**
* Return the useDixMax mode.
*/
public boolean isUseDisMax() {
return useDisMax;
}
/**
* Return the tie breaker.
*/
public double getTieBreaker() {
return tieBreaker;
}
/**
* Return the default field.
*/
public String getDefaultField() {
return defaultField;
}
/**
* Return the allow leading wildcard mode.
*/
public boolean isAllowLeadingWildcard() {
return allowLeadingWildcard;
}
/**
* Return the lowercase expanded terms mode.
*/
public boolean isLowercaseExpandedTerms() {
return lowercaseExpandedTerms;
}
/**
* Return the enable position increments mode.
*/
public boolean isEnablePositionIncrements() {
return enablePositionIncrements;
}
/**
* Return the fuzzy max expansions.
*/
public int getFuzzyMaxExpansions() {
return fuzzyMaxExpansions;
}
/**
* Return the fuzziness.
*/
public String getFuzziness() {
return fuzziness;
}
/**
* Return the fuzzy prefix length.
*/
public int getFuzzyPrefixLength() {
return fuzzyPrefixLength;
}
/**
* Return the phrase slop.
*/
public double getPhraseSlop() {
return phraseSlop;
}
/**
* Return the analyze wildcard mode.
*/
public boolean isAnalyzeWildcard() {
return analyzeWildcard;
}
/**
* Return the boost.
*/
public double getBoost() {
return boost;
}
/**
* Return the auto generate phase queries mode.
*/
public boolean isAutoGeneratePhraseQueries() {
return autoGeneratePhraseQueries;
}
/**
* Return the max determinized states.
*/
public int getMaxDeterminizedStates() {
return maxDeterminizedStates;
}
/**
* Return the time zone.
*/
public String getTimeZone() {
return timeZone;
}
}
@@ -0,0 +1,194 @@
package com.avaje.ebean.search;
/**
* Simple text query options.
* <p>
* This maps to an ElasticSearch "simple text query".
* </p>
*
* <pre>{@code
*
* TextSimple options = new TextSimple()
* .analyzeWildcard(true)
* .fields("name")
* .lenient(true)
* .opAnd();
*
* List<Customer> customers = server.find(Customer.class)
* .text()
* .textSimple("quick brown", options)
* .findList();
*
* }</pre>
*/
public class TextSimple {
protected String[] fields;
protected boolean operatorAnd;
protected String analyzer;
protected String flags;
protected boolean lowercaseExpandedTerms = true;
protected boolean analyzeWildcard;
protected String locale;
protected boolean lenient;
protected String minShouldMatch;
/**
* Construct
*/
public TextSimple() {
}
/**
* Set the fields.
*/
public TextSimple fields(String... fields) {
this.fields = fields;
return this;
}
/**
* Use AND as the default operator.
*/
public TextSimple opAnd() {
this.operatorAnd = true;
return this;
}
/**
* Use OR as the default operator.
*/
public TextSimple opOr() {
this.operatorAnd = false;
return this;
}
/**
* Set the analyzer
*/
public TextSimple analyzer(String analyzer) {
this.analyzer = analyzer;
return this;
}
/**
* Set the flags.
*/
public TextSimple flags(String flags) {
this.flags = flags;
return this;
}
/**
* Set the false to not use lowercase expanded terms.
*/
public TextSimple lowercaseExpandedTerms(boolean lowercaseExpandedTerms) {
this.lowercaseExpandedTerms = lowercaseExpandedTerms;
return this;
}
/**
* Set to true to use analyze wildcard.
*/
public TextSimple analyzeWildcard(boolean analyzeWildcard) {
this.analyzeWildcard = analyzeWildcard;
return this;
}
/**
* Set the locale.
*/
public TextSimple locale(String locale) {
this.locale = locale;
return this;
}
/**
* Set the lenient mode.
*/
public TextSimple lenient(boolean lenient) {
this.lenient = lenient;
return this;
}
/**
* Set the minimum should match.
*/
public TextSimple minShouldMatch(String minShouldMatch) {
this.minShouldMatch = minShouldMatch;
return this;
}
/**
* Return lenient mode.
*/
public boolean isLenient() {
return lenient;
}
/**
* Return true to analyse wildcard.
*/
public boolean isAnalyzeWildcard() {
return analyzeWildcard;
}
/**
* Return lowercase expanded terms mode.
*/
public boolean isLowercaseExpandedTerms() {
return lowercaseExpandedTerms;
}
/**
* Return true if the default operator should be AND.
*/
public boolean isOperatorAnd() {
return operatorAnd;
}
/**
* Return the analyzer to use.
*/
public String getAnalyzer() {
return analyzer;
}
/**
* Return the fields.
*/
public String[] getFields() {
return fields;
}
/**
* Return the locale.
*/
public String getLocale() {
return locale;
}
/**
* Return the flags.
*/
public String getFlags() {
return flags;
}
/**
* Return the minimum should match.
*/
public String getMinShouldMatch() {
return minShouldMatch;
}
}
@@ -20,7 +20,7 @@ public interface JsonBeanReader<T> {
/**
* Create a new reader taking the context from the existing one but using a new JsonParser.
*/
JsonBeanReader<T> forJson(JsonParser moreJson);
JsonBeanReader<T> forJson(JsonParser moreJson, boolean resetContext);
/**
* Add a bean explicitly to the persistence context.
@@ -1,9 +1,11 @@
package com.avaje.ebean.text.json;
import com.avaje.ebean.FetchPath;
import com.avaje.ebean.plugin.BeanType;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Type;
@@ -66,6 +68,16 @@ public interface JsonContext {
*/
<T> JsonBeanReader<T> createBeanReader(Class<T> cls, JsonParser parser, JsonReadOptions options) throws JsonIOException;
/**
* Create and return a new bean reading for the bean type given the JSON options and source.
* <p>
* Note that JsonOption provides an option for setting a persistence context and also enabling
* further lazy loading. Further lazy loading requires a persistence context so if that is set
* on then a persistence context is created if there is not one set.
* </p>
*/
<T> JsonBeanReader<T> createBeanReader(BeanType<T> beanType, JsonParser parser, JsonReadOptions options) throws JsonIOException;
/**
* Convert json string input into a list of beans of a specific type.
*
@@ -213,13 +225,13 @@ public interface JsonContext {
JsonParser createParser(Reader reader) throws JsonIOException;
/**
* Return a helper that can write scalar types known to Ebean to Jackson.
* Write a scalar types known to Ebean to Jackson.
* <p>
* Ebean has built in support for java8 and Joda types as well as the other
* standard JDK types like URI, URL, UUID etc. This is a fast simple way to
* write any of those types to Jackson.
* </p>
*/
JsonScalar getScalar(JsonGenerator generator);
void writeScalar(JsonGenerator generator, Object scalarValue) throws IOException;
}
@@ -23,6 +23,8 @@ public class JsonReadOptions {
protected PersistenceContext persistenceContext;
protected Object loadContext;
/**
* Default constructor.
*/
@@ -100,4 +102,19 @@ public class JsonReadOptions {
public PersistenceContext getPersistenceContext() {
return persistenceContext;
}
/**
* Return the load context to use.
*/
public Object getLoadContext() {
return loadContext;
}
/**
* Set the load context to use.
*/
public void setLoadContext(Object loadContext) {
this.loadContext = loadContext;
}
}
@@ -1,15 +0,0 @@
package com.avaje.ebean.text.json;
import java.io.IOException;
/**
* Writes any scalar type known to Ebean to the underlying Jackson generator.
*/
public interface JsonScalar {
/**
* Write the scalar type to JSON where the value can be any type known to Ebean
* including Enums, Java8 time types, Joda types, URL, URI etc.
*/
void write(String name, Object value) throws IOException;
}
@@ -135,6 +135,11 @@ public interface JsonWriter {
*/
void writeNumber(long value);
/**
* Write a double value.
*/
void writeNumber(double value);
/**
* Write a BigDecimal value (typically inside a list).
*/
@@ -5,9 +5,6 @@ import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
import java.util.List;
/**
* Controls the loading of reference objects for a query instance.
@@ -19,38 +16,40 @@ public interface LoadContext {
*/
int getSecondaryQueriesMinBatchSize(int defaultQueryBatch);
/**
* Execute any secondary (+query) queries if there are any defined.
* @param parentRequest the originating query request
*/
void executeSecondaryQueries(OrmQueryRequest<?> parentRequest);
/**
* Execute any secondary (+query) queries if there are any defined.
*
* @param parentRequest the originating query request
* @param forEach set true when using findEach iteration
*/
void executeSecondaryQueries(OrmQueryRequest<?> parentRequest, boolean forEach);
/**
* Return the node for a given path which is used by AutoTune profiling.
*/
ObjectGraphNode getObjectGraphNode(String path);
* Return the node for a given path which is used by AutoTune profiling.
*/
ObjectGraphNode getObjectGraphNode(String path);
/**
* Return the persistence context used by this query and future lazy loading.
*/
PersistenceContext getPersistenceContext();
/**
* Return the persistence context used by this query and future lazy loading.
*/
PersistenceContext getPersistenceContext();
/**
* Set the persistence context used by this query and future lazy loading.
* <p>
* Used by query iterator when processing large result sets.
* </p>
*/
void resetPersistenceContext(PersistenceContext persistenceContext);
/**
* Set the persistence context used by this query and future lazy loading.
* <p>
* Used by query iterator when processing large result sets.
* </p>
*/
void resetPersistenceContext(PersistenceContext persistenceContext);
/**
* Register a Bean for lazy loading.
*/
void register(String path, EntityBeanIntercept ebi);
/**
* Register a Bean for lazy loading.
*/
void register(String path, EntityBeanIntercept ebi);
/**
* Register a collection for lazy loading.
*/
void register(String path, BeanCollection<?> bc);
/**
* Register a collection for lazy loading.
*/
void register(String path, BeanCollection<?> bc);
}
@@ -14,5 +14,5 @@ public interface LoadSecondaryQuery {
/**
* Execute the secondary query with a given batch size.
*/
void loadSecondaryQuery(OrmQueryRequest<?> parentRequest);
void loadSecondaryQuery(OrmQueryRequest<?> parentRequest, boolean forEach);
}
@@ -1,7 +1,7 @@
package com.avaje.ebeaninternal.api;
import com.avaje.ebean.TransactionCallback;
import com.avaje.ebean.annotation.DocStoreEvent;
import com.avaje.ebean.annotation.DocStoreMode;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.config.PersistBatch;
import com.avaje.ebean.event.changelog.BeanChange;
@@ -61,23 +61,23 @@ public class ScopedTransaction implements SpiTransaction {
}
@Override
public DocStoreEvent getDocStoreUpdateMode() {
return transaction.getDocStoreUpdateMode();
public DocStoreMode getDocStoreMode() {
return transaction.getDocStoreMode();
}
@Override
public void setDocStoreUpdateMode(DocStoreEvent indexUpdateMode) {
transaction.setDocStoreUpdateMode(indexUpdateMode);
public void setDocStoreMode(DocStoreMode mode) {
transaction.setDocStoreMode(mode);
}
@Override
public int getDocStoreBulkBatchSize() {
return transaction.getDocStoreBulkBatchSize();
public int getDocStoreBatchSize() {
return transaction.getDocStoreBatchSize();
}
@Override
public void setDocStoreUpdateBatchSize(int batchSize) {
transaction.setDocStoreUpdateBatchSize(batchSize);
public void setDocStoreBatchSize(int batchSize) {
transaction.setDocStoreBatchSize(batchSize);
}
@Override
@@ -3,7 +3,7 @@ package com.avaje.ebeaninternal.api;
import com.avaje.ebean.Expression;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.expression.ElasticExpressionContext;
import com.avaje.ebeaninternal.server.expression.DocQueryContext;
import java.io.IOException;
@@ -16,7 +16,7 @@ public interface SpiExpression extends Expression {
/**
* Write the expression as an elastic search expression.
*/
void writeElastic(ElasticExpressionContext context) throws IOException;
void writeDocQuery(DocQueryContext context) throws IOException;
/**
* Process "Many" properties populating ManyWhereJoins.
@@ -1,7 +1,9 @@
package com.avaje.ebeaninternal.api;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebeaninternal.server.expression.DocQueryContext;
import java.io.IOException;
import java.util.List;
/**
@@ -24,4 +26,8 @@ public interface SpiExpressionList<T> extends ExpressionList<T>, SpiExpression {
*/
boolean isEmpty();
/**
* Write the top level where expressions taking into account possible extra idEquals expression.
*/
void writeDocQuery(DocQueryContext context, SpiExpression idEquals) throws IOException;
}
@@ -0,0 +1,17 @@
package com.avaje.ebeaninternal.api;
import com.avaje.ebean.Junction;
import com.avaje.ebeaninternal.server.expression.DocQueryContext;
import java.io.IOException;
/**
* SPI methods for Junction.
*/
public interface SpiJunction<T> extends Junction<T> {
/**
* Write the Junction taking into account it is implied.
*/
void writeDocQueryJunction(DocQueryContext context) throws IOException;
}
@@ -16,12 +16,10 @@ import com.avaje.ebeaninternal.server.autotune.ProfilingListener;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
import com.avaje.ebeaninternal.server.expression.ElasticExpressionContext;
import com.avaje.ebeaninternal.server.query.CancelableQuery;
import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.List;
import java.util.Set;
@@ -130,11 +128,6 @@ public interface SpiQuery<T> extends Query<T> {
}
}
/**
* Write the query as an elastic search query.
*/
void writeElastic(ElasticExpressionContext context) throws IOException;
/**
* Return true if AutoTune should be attempted on this query.
*/
@@ -516,6 +509,11 @@ public interface SpiQuery<T> extends Query<T> {
*/
SpiExpressionList<T> getHavingExpressions();
/**
* Return the text expressions.
*/
SpiExpressionList<T> getTextExpression();
/**
* Return additional having clause. Where raw String expressions are added
* to having clause rather than Expression objects.
@@ -4,7 +4,7 @@ import java.sql.Connection;
import java.util.List;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.annotation.DocStoreEvent;
import com.avaje.ebean.annotation.DocStoreMode;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.event.changelog.BeanChange;
import com.avaje.ebean.event.changelog.ChangeSet;
@@ -109,13 +109,13 @@ public interface SpiTransaction extends Transaction {
* Returning 0 implies to use the system wide default batch size.
* </p>
*/
DocStoreEvent getDocStoreUpdateMode();
DocStoreMode getDocStoreMode();
/**
* Return the batch size to us for ElasticSearch Bulk API calls
* as a result of this transaction.
*/
int getDocStoreBulkBatchSize();
int getDocStoreBatchSize();
/**
* Return the batchSize specifically set for this transaction or 0.
@@ -5,7 +5,6 @@ import com.avaje.ebean.event.changelog.BeanChange;
import com.avaje.ebean.event.changelog.ChangeSet;
import com.avaje.ebean.event.changelog.ChangeType;
import com.avaje.ebean.text.json.JsonContext;
import com.avaje.ebean.text.json.JsonScalar;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
@@ -97,10 +96,7 @@ public class ChangeJsonBuilder {
if (bean.getType() != ChangeType.DELETE) {
gen.writeFieldName("values");
gen.writeStartObject();
// use JsonScalar as it knows how to encode all the scalar
// property types that Ebean supports (Java8, Joda etc)
JsonScalar scalarWriter = json.getScalar(gen);
writeValuePairs(bean, scalarWriter, gen);
writeValuePairs(bean, gen);
gen.writeEndObject();
}
}
@@ -111,7 +107,7 @@ public class ChangeJsonBuilder {
* We are intentionally keeping the same new/old structure for both inserts and updates.
* </p>
*/
protected void writeValuePairs(BeanChange bean, JsonScalar scalarWriter, JsonGenerator gen) throws IOException {
protected void writeValuePairs(BeanChange bean, JsonGenerator gen) throws IOException {
for (Map.Entry<String, ValuePair> entry : bean.getValues().entrySet()) {
gen.writeFieldName(entry.getKey());
@@ -119,11 +115,13 @@ public class ChangeJsonBuilder {
ValuePair value = entry.getValue();
Object newValue = value.getNewValue();
if (newValue != null) {
scalarWriter.write("new", newValue);
gen.writeFieldName("new");
json.writeScalar(gen, newValue);
}
Object oldValue = value.getOldValue();
if (oldValue != null) {
scalarWriter.write("old", oldValue);
gen.writeFieldName("old");
json.writeScalar(gen, oldValue);
}
gen.writeEndObject();
}
@@ -1,5 +1,7 @@
package com.avaje.ebeaninternal.server.core;
import com.avaje.ebeaninternal.server.type.ScalarTypeUUIDBinary;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
@@ -8,8 +10,6 @@ import java.sql.Types;
import java.util.Calendar;
import java.util.UUID;
import com.avaje.ebeaninternal.server.type.ScalarTypeUUIDBinary;
/**
* Default implementation of TypeConverter.
* <p>
@@ -18,451 +18,452 @@ import com.avaje.ebeaninternal.server.type.ScalarTypeUUIDBinary;
*/
public final class BasicTypeConverter implements Serializable {
private static final long serialVersionUID = 7691463236204070311L;
/**
* Type code for java.util.Calendar.
*/
public static final int UTIL_CALENDAR = -999998986;
private static final long serialVersionUID = 7691463236204070311L;
/**
* Type code for java.util.Date.
*/
public static final int UTIL_DATE = -999998988;
/**
* Type code for java.util.Calendar.
*/
public static final int UTIL_CALENDAR = -999998986;
private BasicTypeConverter() {
}
/**
* Type code for java.util.Date.
*/
public static final int UTIL_DATE = -999998988;
/**
* Convert the Object to the required data type.
*
* @param value
* the Object value
* @param toDataType
* the dataType as per java.sql.Types.
*/
public static Object convert(Object value, int toDataType) {
private BasicTypeConverter() {
}
try {
switch (toDataType) {
case UTIL_DATE: {
return toUtilDate(value);
}
case UTIL_CALENDAR: {
return toCalendar(value);
}
case Types.BIGINT: {
return toLong(value);
}
case Types.INTEGER: {
return toInteger(value);
}
case Types.BIT: {
return toBoolean(value);
}
case Types.TINYINT: {
return toByte(value);
}
case Types.SMALLINT: {
return toShort(value);
}
case Types.NUMERIC: {
return toBigDecimal(value);
}
case Types.DECIMAL: {
return toBigDecimal(value);
}
case Types.REAL: {
return toFloat(value);
}
case Types.DOUBLE: {
return toDouble(value);
}
case Types.FLOAT: {
return toDouble(value);
}
case Types.BOOLEAN: {
return toBoolean(value);
}
case Types.TIMESTAMP: {
return toTimestamp(value);
}
case Types.DATE: {
return toDate(value);
}
case Types.VARCHAR: {
return toString(value);
}
case Types.CHAR: {
return toString(value);
}
case Types.OTHER: {
return value;
}
case Types.JAVA_OBJECT: {
return value;
}
case Types.BINARY:
case Types.LONGVARBINARY:
case Types.BLOB: {
return value;
}
case Types.LONGVARCHAR:
case Types.CLOB: {
return value;
}
default: {
String msg = "Unhandled data type [" + toDataType + "] converting [" + value + "]";
throw new RuntimeException(msg);
}
}
} catch (ClassCastException e) {
String m = "ClassCastException converting to data type [" + toDataType + "] value [" + value + "]";
throw new RuntimeException(m);
}
}
/**
* Convert the Object to the required data type.
*
* @param value the Object value
* @param toDataType the dataType as per java.sql.Types.
*/
public static Object convert(Object value, int toDataType) {
/**
* Convert the value to a String.
*/
public static String toString(Object value) {
if (value == null) {
return null;
}
if (value instanceof String) {
return (String) value;
}
if (value instanceof char[]) {
return String.valueOf((char[]) value);
}
return value.toString();
}
/**
* Convert the value to a Boolean with an explicit String true value.
*/
public static Boolean toBoolean(Object value, String dbTrueValue) {
if (value == null) {
return null;
try {
switch (toDataType) {
case UTIL_DATE: {
return toUtilDate(value);
}
if (value instanceof Boolean) {
return (Boolean) value;
case UTIL_CALENDAR: {
return toCalendar(value);
}
String s = value.toString();
return s.equalsIgnoreCase(dbTrueValue);
}
/**
* Convert the value to a Boolean. Can be a Boolean or the string values
* "true" or "false".
*/
public static Boolean toBoolean(Object value) {
case Types.BIGINT: {
return toLong(value);
}
case Types.INTEGER: {
return toInteger(value);
}
case Types.BIT: {
return toBoolean(value);
}
case Types.TINYINT: {
return toByte(value);
}
case Types.SMALLINT: {
return toShort(value);
}
case Types.NUMERIC: {
return toBigDecimal(value);
}
case Types.DECIMAL: {
return toBigDecimal(value);
}
case Types.REAL: {
return toFloat(value);
}
case Types.DOUBLE: {
return toDouble(value);
}
case Types.FLOAT: {
return toDouble(value);
}
case Types.BOOLEAN: {
return toBoolean(value);
}
case Types.TIMESTAMP: {
return toTimestamp(value);
}
case Types.DATE: {
return toDate(value);
}
case Types.VARCHAR: {
return toString(value);
}
case Types.CHAR: {
return toString(value);
}
case Types.OTHER: {
return value;
}
case Types.JAVA_OBJECT: {
return value;
}
case Types.BINARY:
case Types.LONGVARBINARY:
case Types.BLOB: {
return value;
}
case Types.LONGVARCHAR:
case Types.CLOB: {
return value;
}
default: {
String msg = "Unhandled data type [" + toDataType + "] converting [" + value + "]";
throw new RuntimeException(msg);
}
}
} catch (ClassCastException e) {
String m = "ClassCastException converting to data type [" + toDataType + "] value [" + value + "]";
throw new RuntimeException(m);
}
}
if (value == null) {
return null;
}
if (value instanceof Boolean) {
return (Boolean) value;
}
/**
* Convert the value to a String.
*/
public static String toString(Object value) {
return Boolean.valueOf(value.toString());
}
if (value == null) {
return null;
}
if (value instanceof String) {
return (String) value;
}
if (value instanceof char[]) {
return String.valueOf((char[]) value);
}
/**
* Convert the value to a UUID.
*/
public static UUID toUUID(Object value) {
return value.toString();
}
if (value == null) {
return null;
}
if (value instanceof String) {
return UUID.fromString((String) value);
}
if (value instanceof byte[]) {
return ScalarTypeUUIDBinary.convertFromBytes((byte[])value);
}
return (UUID) value;
}
/**
* convert the passed in object to a BigDecimal. It should be another
* numeric type.
*/
public static BigDecimal toBigDecimal(Object value) {
/**
* Convert the value to a Boolean with an explicit String true value.
*/
public static Boolean toBoolean(Object value, String dbTrueValue) {
if (value == null) {
return null;
}
if (value instanceof BigDecimal) {
return (BigDecimal) value;
}
return new BigDecimal(value.toString());
}
if (value == null) {
return null;
}
if (value instanceof Boolean) {
return (Boolean) value;
}
String s = value.toString();
return s.equalsIgnoreCase(dbTrueValue);
}
public static Float toFloat(Object value) {
/**
* Convert the value to a Boolean. Can be a Boolean or the string values
* "true" or "false".
*/
public static Boolean toBoolean(Object value) {
if (value == null) {
return null;
}
if (value instanceof Float) {
return (Float) value;
}
if (value instanceof Number) {
return ((Number) value).floatValue();
}
return Float.valueOf(value.toString());
}
if (value == null) {
return null;
}
if (value instanceof Boolean) {
return (Boolean) value;
}
public static Short toShort(Object value) {
return Boolean.valueOf(value.toString());
}
if (value == null) {
return null;
}
if (value instanceof Short) {
return (Short) value;
}
if (value instanceof Number) {
return ((Number) value).shortValue();
}
return Short.valueOf(value.toString());
}
/**
* Convert the value to a UUID.
*/
public static UUID toUUID(Object value) {
public static Byte toByte(Object value) {
if (value == null) {
return null;
}
if (value instanceof UUID) {
return (UUID) value;
}
if (value instanceof String) {
return UUID.fromString((String) value);
}
if (value instanceof byte[]) {
return ScalarTypeUUIDBinary.convertFromBytes((byte[]) value);
}
return UUID.fromString(value.toString());
}
if (value == null) {
return null;
}
if (value instanceof Byte) {
return (Byte) value;
}
return Byte.valueOf(value.toString());
}
/**
* convert the passed in object to a BigDecimal. It should be another
* numeric type.
*/
public static BigDecimal toBigDecimal(Object value) {
/**
* convert the passed in object to a Integer. It should be another numeric
* type.
*/
public static Integer toInteger(Object value) {
if (value == null) {
return null;
}
if (value instanceof BigDecimal) {
return (BigDecimal) value;
}
return new BigDecimal(value.toString());
}
if (value == null) {
return null;
}
if (value instanceof Integer) {
return (Integer) value;
}
if (value instanceof Number) {
return ((Number) value).intValue();
}
return Integer.valueOf(value.toString());
}
public static Float toFloat(Object value) {
/**
* Convert the object to a Long. It should be another numeric type.
*/
public static Long toLong(Object value) {
if (value == null) {
return null;
}
if (value instanceof Float) {
return (Float) value;
}
if (value instanceof Number) {
return ((Number) value).floatValue();
}
return Float.valueOf(value.toString());
}
if (value == null) {
return null;
}
if (value instanceof Long) {
return (Long) value;
}
if (value instanceof String) {
return Long.valueOf((String) value);
}
if (value instanceof Number) {
return ((Number) value).longValue();
}
if (value instanceof java.util.Date) {
return ((java.util.Date) value).getTime();
}
if (value instanceof Calendar) {
return ((Calendar) value).getTime().getTime();
}
return Long.valueOf(value.toString());
}
public static Short toShort(Object value) {
public static BigInteger toMathBigInteger(Object value) {
if (value == null) {
return null;
}
if (value instanceof Short) {
return (Short) value;
}
if (value instanceof Number) {
return ((Number) value).shortValue();
}
return Short.valueOf(value.toString());
}
if (value == null) {
return null;
}
if (value instanceof BigInteger) {
return (BigInteger) value;
}
return new BigInteger(value.toString());
}
public static Byte toByte(Object value) {
/**
* Convert the object to a Double. It should be another numberic type.
*/
public static Double toDouble(Object value) {
if (value == null) {
return null;
}
if (value instanceof Byte) {
return (Byte) value;
}
return Byte.valueOf(value.toString());
}
if (value == null) {
return null;
}
if (value instanceof Double) {
return (Double) value;
}
if (value instanceof Number) {
return ((Number) value).doubleValue();
}
return Double.valueOf(value.toString());
}
/**
* convert the passed in object to a Integer. It should be another numeric
* type.
*/
public static Integer toInteger(Object value) {
/**
* convert the passed in object to a Timestamp. It is expected to be a
* java.sql.Date really.
*/
public static Timestamp toTimestamp(Object value) {
if (value == null) {
return null;
}
if (value instanceof Integer) {
return (Integer) value;
}
if (value instanceof Number) {
return ((Number) value).intValue();
}
return Integer.valueOf(value.toString());
}
if (value == null) {
return null;
}
if (value instanceof Timestamp) {
return (Timestamp) value;
/**
* Convert the object to a Long. It should be another numeric type.
*/
public static Long toLong(Object value) {
} else if (value instanceof java.util.Date) {
// no nanos here... so hopefully ok
return new Timestamp(((java.util.Date) value).getTime());
if (value == null) {
return null;
}
if (value instanceof Long) {
return (Long) value;
}
if (value instanceof String) {
return Long.valueOf((String) value);
}
if (value instanceof Number) {
return ((Number) value).longValue();
}
if (value instanceof java.util.Date) {
return ((java.util.Date) value).getTime();
}
if (value instanceof Calendar) {
return ((Calendar) value).getTime().getTime();
}
return Long.valueOf(value.toString());
}
} else if (value instanceof Calendar) {
return new Timestamp(((Calendar) value).getTime().getTime());
public static BigInteger toMathBigInteger(Object value) {
} else if (value instanceof String) {
return Timestamp.valueOf((String) value);
if (value == null) {
return null;
}
if (value instanceof BigInteger) {
return (BigInteger) value;
}
return new BigInteger(value.toString());
}
} else if (value instanceof Number) {
return new Timestamp(((Number) value).longValue());
/**
* Convert the object to a Double. It should be another numberic type.
*/
public static Double toDouble(Object value) {
} else {
String msg = "Unable to convert [" + value.getClass().getName() + "] into a Timestamp.";
throw new RuntimeException(msg);
}
}
if (value == null) {
return null;
}
if (value instanceof Double) {
return (Double) value;
}
if (value instanceof Number) {
return ((Number) value).doubleValue();
}
return Double.valueOf(value.toString());
}
public static java.sql.Time toTime(Object value) {
/**
* convert the passed in object to a Timestamp. It is expected to be a
* java.sql.Date really.
*/
public static Timestamp toTimestamp(Object value) {
if (value == null) {
return null;
}
if (value instanceof java.sql.Time) {
return (java.sql.Time) value;
if (value == null) {
return null;
}
if (value instanceof Timestamp) {
return (Timestamp) value;
} else if (value instanceof String) {
return java.sql.Time.valueOf((String) value);
} else if (value instanceof java.util.Date) {
// no nanos here... so hopefully ok
return new Timestamp(((java.util.Date) value).getTime());
} else {
String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
throw new RuntimeException(m);
}
}
} else if (value instanceof Calendar) {
return new Timestamp(((Calendar) value).getTime().getTime());
/**
* convert the passed in object to a java sql Date.
*/
public static java.sql.Date toDate(Object value) {
} else if (value instanceof String) {
return Timestamp.valueOf((String) value);
if (value == null) {
return null;
}
if (value instanceof java.sql.Date) {
return (java.sql.Date) value;
} else if (value instanceof Number) {
return new Timestamp(((Number) value).longValue());
} else if (value instanceof java.util.Date) {
return new java.sql.Date(((java.util.Date) value).getTime());
} else {
String msg = "Unable to convert [" + value.getClass().getName() + "] into a Timestamp.";
throw new RuntimeException(msg);
}
}
} else if (value instanceof Calendar) {
return new java.sql.Date(((Calendar) value).getTime().getTime());
public static java.sql.Time toTime(Object value) {
} else if (value instanceof String) {
return java.sql.Date.valueOf((String) value);
if (value == null) {
return null;
}
if (value instanceof java.sql.Time) {
return (java.sql.Time) value;
} else if (value instanceof Number) {
return new java.sql.Date(((Number) value).longValue());
} else if (value instanceof String) {
return java.sql.Time.valueOf((String) value);
} else {
String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
throw new RuntimeException(m);
}
}
} else {
String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
throw new RuntimeException(m);
}
}
/**
* convert the passed in object to a java sql Date.
*/
public static java.util.Date toUtilDate(Object value) {
/**
* convert the passed in object to a java sql Date.
*/
public static java.sql.Date toDate(Object value) {
if (value == null) {
return null;
}
if (value instanceof java.sql.Timestamp) {
// loss of nanos precision
return new java.util.Date(((java.sql.Timestamp) value).getTime());
}
// DEVNOTE: strictly speaking do I need to convert a java.sql.Date to
// java.util.Date? equals() is symmetrical so perhaps this is not
// really required?
if (value instanceof java.sql.Date) {
return new java.util.Date(((java.sql.Date) value).getTime());
}
if (value instanceof java.util.Date) {
return (java.util.Date) value;
if (value == null) {
return null;
}
if (value instanceof java.sql.Date) {
return (java.sql.Date) value;
} else if (value instanceof Calendar) {
return ((Calendar) value).getTime();
} else if (value instanceof java.util.Date) {
return new java.sql.Date(((java.util.Date) value).getTime());
} else if (value instanceof String) {
return new java.util.Date(Timestamp.valueOf((String) value).getTime());
} else if (value instanceof Calendar) {
return new java.sql.Date(((Calendar) value).getTime().getTime());
} else if (value instanceof Number) {
return new java.util.Date(((Number) value).longValue());
} else if (value instanceof String) {
return java.sql.Date.valueOf((String) value);
} else {
throw new RuntimeException("Unable to convert [" + value.getClass().getName() + "] into a java.util.Date");
}
}
} else if (value instanceof Number) {
return new java.sql.Date(((Number) value).longValue());
/**
* convert the passed in object to a java sql Date.
*/
public static Calendar toCalendar(Object value) {
} else {
String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date.";
throw new RuntimeException(m);
}
}
if (value == null) {
return null;
}
if (value instanceof Calendar) {
return (Calendar) value;
/**
* convert the passed in object to a java sql Date.
*/
public static java.util.Date toUtilDate(Object value) {
} else if (value instanceof java.util.Date) {
java.util.Date date = ((java.util.Date) value);
return toCalendarFromDate(date);
if (value == null) {
return null;
}
if (value instanceof java.sql.Timestamp) {
// loss of nanos precision
return new java.util.Date(((java.sql.Timestamp) value).getTime());
}
// DEVNOTE: strictly speaking do I need to convert a java.sql.Date to
// java.util.Date? equals() is symmetrical so perhaps this is not
// really required?
if (value instanceof java.sql.Date) {
return new java.util.Date(((java.sql.Date) value).getTime());
}
if (value instanceof java.util.Date) {
return (java.util.Date) value;
} else if (value instanceof String) {
java.util.Date date = toUtilDate(value);
return toCalendarFromDate(date);
} else if (value instanceof Calendar) {
return ((Calendar) value).getTime();
} else if (value instanceof Number) {
long timeMillis = ((Number) value).longValue();
java.util.Date date = new java.util.Date(timeMillis);
return toCalendarFromDate(date);
} else if (value instanceof String) {
return new java.util.Date(Timestamp.valueOf((String) value).getTime());
} else {
String m = "Unable to convert [" + value.getClass().getName() + "] into a java.util.Date";
throw new RuntimeException(m);
}
}
} else if (value instanceof Number) {
return new java.util.Date(((Number) value).longValue());
private static Calendar toCalendarFromDate(java.util.Date date) {
} else {
throw new RuntimeException("Unable to convert [" + value.getClass().getName() + "] into a java.util.Date");
}
}
Calendar cal = Calendar.getInstance();
cal.setTime(date);
/**
* convert the passed in object to a java sql Date.
*/
public static Calendar toCalendar(Object value) {
return cal;
}
if (value == null) {
return null;
}
if (value instanceof Calendar) {
return (Calendar) value;
} else if (value instanceof java.util.Date) {
java.util.Date date = ((java.util.Date) value);
return toCalendarFromDate(date);
} else if (value instanceof String) {
java.util.Date date = toUtilDate(value);
return toCalendarFromDate(date);
} else if (value instanceof Number) {
long timeMillis = ((Number) value).longValue();
java.util.Date date = new java.util.Date(timeMillis);
return toCalendarFromDate(date);
} else {
String m = "Unable to convert [" + value.getClass().getName() + "] into a java.util.Date";
throw new RuntimeException(m);
}
}
private static Calendar toCalendarFromDate(java.util.Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
return cal;
}
}
@@ -126,7 +126,7 @@ public class DefaultBeanLoader {
if (pc == null) {
pc = new DefaultPersistenceContext();
pc.put(parentId, parentBean);
parentDesc.contextPut(pc, parentId, parentBean);
}
boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching();
@@ -263,7 +263,7 @@ public class DefaultBeanLoader {
if (pc == null) {
// a reference with no existing persistenceContext
pc = new DefaultPersistenceContext();
pc.put(id, bean);
desc.contextPut(pc, id, bean);
ebi.setPersistenceContext(pc);
}
@@ -580,48 +580,31 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
BeanDescriptor desc = getBeanDescriptor(type);
// convert the id type if necessary
id = desc.convertId(id);
Object ref = null;
PersistenceContext ctx = null;
PersistenceContext pc = null;
SpiTransaction t = transactionScopeManager.get();
if (t != null) {
// first try the persistence context
ctx = t.getPersistenceContext();
ref = ctx.get(type, id);
}
if (ref == null) {
InheritInfo inheritInfo = desc.getInheritInfo();
if (inheritInfo != null) {
// we actually need to do a query because
// we don't know the type without the
// discriminator value
BeanProperty idProp = desc.getIdProperty();
if (idProp == null) {
throw new PersistenceException("No ID properties for this type? " + desc);
}
// just select the id properties and
// the discriminator column (auto added)
Query<T> query = createQuery(type);
query.select(idProp.getName()).setId(id);
ref = query.findUnique();
} else {
// use the default reference options
ref = desc.createReference(null, id);
}
if (ctx != null && (ref instanceof EntityBean)) {
// Not putting a vanilla reference in the persistence context
ctx.put(id, ref);
pc = t.getPersistenceContext();
Object existing = desc.contextGet(pc, id);
if (existing != null) {
return (T)existing;
}
}
return (T) ref;
InheritInfo inheritInfo = desc.getInheritInfo();
if (inheritInfo == null) {
return (T)desc.contextRef(pc, null, id);
}
BeanProperty idProp = desc.getIdProperty();
if (idProp == null) {
throw new PersistenceException("No ID properties for this type? " + desc);
}
// we actually need to do a query because we don't know the type without the discriminator
// value, just select the id property and discriminator column (auto added)
return find(type).select(idProp.getName()).setId(id).findUnique();
}
@Override
@@ -1082,31 +1065,32 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
if (t == null) {
t = getCurrentServerTransaction();
}
PersistenceContext context = null;
BeanDescriptor<T> desc = query.getBeanDescriptor();
PersistenceContext pc = null;
if (t != null && useTransactionPersistenceContext(query)) {
// first look in the transaction scoped persistence context
context = t.getPersistenceContext();
if (context != null) {
WithOption o = context.getWithOption(query.getBeanType(), query.getId());
pc = t.getPersistenceContext();
if (pc != null) {
WithOption o = desc.contextGetWithOption(pc, query.getId());
if (o != null) {
if (o.isDeleted()) {
// Bean was previously deleted in the same transaction / persistence context
return null;
}
// Return the entity bean instance from the persistence context
return (T) o.getBean();
}
}
}
BeanDescriptor<T> desc = query.getBeanDescriptor();
if (!desc.calculateUseCache(query.isUseBeanCache())) {
// not using bean cache
return null;
}
// Hit the L2 bean cache
return desc.cacheBeanGet(query, context);
return desc.cacheBeanGet(query, pc);
}
/**
@@ -1129,7 +1113,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
spiQuery.setType(Type.BEAN);
if (SpiQuery.Mode.NORMAL.equals(spiQuery.getMode()) && !spiQuery.isLoadBeanCache()) {
// See if we can skip doing the fetch completely by getting the bean from the
// persistence context or the bean cache
@@ -1140,6 +1123,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
SpiOrmQueryRequest<T> request = createQueryRequest(spiQuery, t);
if (request.isUseDocStore()) {
return docStore().find(request);
}
try {
request.initTransIfRequired();
return (T) request.findId();
@@ -1158,9 +1144,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return findId(query, t);
}
BeanDescriptor<T> desc = beanDescriptorManager.getBeanDescriptor(query.getBeanType());
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
BeanDescriptor<T> desc = spiQuery.getBeanDescriptor();
T bean = desc.cacheNaturalKeyLookup((SpiQuery<T>) query, (SpiTransaction) t);
T bean = desc.cacheNaturalKeyLookup(spiQuery, (SpiTransaction) t);
if (bean != null) {
return bean;
}
@@ -1342,7 +1329,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
if (spiQuery.isUseDocStore()) {
return docStore().findPagedList(query);
return docStore().findPagedList(createQueryRequest(Type.LIST, query, transaction));
}
return new LimitOffsetPagedList<T>(this, spiQuery);
@@ -1352,6 +1339,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
if (request.isUseDocStore()) {
docStore().findEach(request, consumer);
return;
}
request.initTransIfRequired();
request.findEach(consumer);
// no try finally - findVisit guarantee's cleanup of the transaction if required
@@ -1360,6 +1352,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
public <T> void findEachWhile(Query<T> query, QueryEachWhileConsumer<T> consumer, Transaction t) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
if (request.isUseDocStore()) {
docStore().findEachWhile(request, consumer);
return;
}
request.initTransIfRequired();
request.findEachWhile(consumer);
@@ -1397,13 +1393,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
public <T> List<T> findList(Query<T> query, Transaction t) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, t);
Object result = request.getFromQueryCache();
if (result != null) {
return (List<T>) result;
}
if (request.isUseDocStore()) {
return docStore().findList(query);
return docStore().findList(request);
}
try {
@@ -12,6 +12,7 @@ import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.event.BeanFindController;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebean.text.json.JsonReadOptions;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.CQueryPlanKey;
import com.avaje.ebeaninternal.api.HashQuery;
@@ -59,6 +60,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
private PersistenceContext persistenceContext;
private JsonReadOptions jsonRead;
private HashQuery cacheKey;
private CQueryPlanKey queryPlanKey;
@@ -94,8 +97,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
return ebeanServer.getDatabasePlatform().getLikeClause();
}
public void executeSecondaryQueries() {
loadContext.executeSecondaryQueries(this);
public void executeSecondaryQueries(boolean forEach) {
// disable lazy loading leaves loadContext null
if (loadContext != null) {
loadContext.executeSecondaryQueries(this, forEach);
}
}
/**
@@ -152,7 +158,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
public void prepareQuery() {
adapterPreQuery();
this.secondaryQueries = query.convertJoins();
this.queryPlanKey = query.prepare(this);
}
@@ -189,7 +194,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
*/
public void persistenceContextAdd(EntityBean bean) {
Object id = beanDescriptor.getId(bean);
persistenceContext.put(id, bean);
beanDescriptor.contextPut(persistenceContext, id, bean);
}
/**
@@ -213,9 +218,27 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
createdTransaction = true;
}
}
// initialise the persistenceContext and loadContext
this.persistenceContext = getPersistenceContext(query, transaction);
this.loadContext = new DLoadContext(this, secondaryQueries);
persistenceContext = getPersistenceContext(query, transaction);
loadContext = new DLoadContext(this, secondaryQueries);
}
/**
* Return the JsonReadOptions taking into account lazy loading and persistence context.
*/
public JsonReadOptions createJsonReadOptions() {
persistenceContext = getPersistenceContext(query, transaction);
if (query.getPersistenceContext() == null) {
query.setPersistenceContext(persistenceContext);
}
jsonRead = new JsonReadOptions();
jsonRead.setPersistenceContext(persistenceContext);
if (!query.isDisableLazyLoading()) {
loadContext = new DLoadContext(this, secondaryQueries);
jsonRead.setLoadContext(loadContext);
}
return jsonRead;
}
/**
@@ -224,6 +247,10 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
public void flushPersistenceContextOnIterate() {
persistenceContext = new DefaultPersistenceContext();
loadContext.resetPersistenceContext(persistenceContext);
if (jsonRead != null) {
jsonRead.setPersistenceContext(persistenceContext);
jsonRead.setLoadContext(loadContext);
}
}
/**
@@ -239,7 +266,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
// determine the scope (from the query and then server)
PersistenceContextScope scope = ebeanServer.getPersistenceContextScope(query);
return (scope == PersistenceContextScope.QUERY) ? new DefaultPersistenceContext() : t.getPersistenceContext();
return (scope == PersistenceContextScope.QUERY || t == null) ? new DefaultPersistenceContext() : t.getPersistenceContext();
}
/**
@@ -2,7 +2,7 @@ package com.avaje.ebeaninternal.server.core;
import com.avaje.ebean.ValuePair;
import com.avaje.ebean.annotation.ConcurrencyMode;
import com.avaje.ebean.annotation.DocStoreEvent;
import com.avaje.ebean.annotation.DocStoreMode;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.event.BeanPersistController;
@@ -69,7 +69,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
private final boolean publish;
private DocStoreEvent docStoreEvent;
private DocStoreMode docStoreMode;
private ConcurrencyMode concurrencyMode;
@@ -147,7 +147,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
this.parentBean = parentBean;
this.controller = beanDescriptor.getPersistController();
this.type = type;
this.docStoreEvent = calcDocStoreEvent(transaction, type);
this.docStoreMode = calcDocStoreMode(transaction, type);
if (saveRecurse) {
this.persistCascade = t.isPersistCascade();
}
@@ -175,9 +175,9 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* Used to check if the Transaction has set the mode to IGNORE when doing large batch inserts that we
* don't want to send to the doc store.
*/
private DocStoreEvent calcDocStoreEvent(SpiTransaction txn, Type type) {
DocStoreEvent docStoreEvent = (txn == null) ? null : txn.getDocStoreUpdateMode();
return beanDescriptor.getDocStoreEvent(type, docStoreEvent);
private DocStoreMode calcDocStoreMode(SpiTransaction txn, Type type) {
DocStoreMode txnMode = (txn == null) ? null : txn.getDocStoreMode();
return beanDescriptor.getDocStoreMode(type, txnMode);
}
/**
@@ -203,7 +203,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
public void initTransIfRequiredWithBatchCascade() {
if (createImplicitTransIfRequired()) {
docStoreEvent = calcDocStoreEvent(transaction, type);
docStoreMode = calcDocStoreMode(transaction, type);
}
if (transaction.checkBatchEscalationOnCascade(this)) {
// we escalated to use batch mode so flush when done
@@ -308,8 +308,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* by queuing an event or direct updateAdd (via Bulk API).
*/
private boolean isDocStoreNotify() {
// Either queue or directly update the document store
return docStoreEvent != DocStoreEvent.IGNORE;
return docStoreMode != DocStoreMode.IGNORE;
}
public boolean isNotifyPersistListener() {
@@ -563,6 +562,13 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
return false;
}
/**
* Return true if this was a hard/permanent delete request (and should cascade as such).
*/
public boolean isHardDeleteCascade() {
return (type == Type.DELETE && beanDescriptor.isSoftDelete());
}
/**
* Checks for @Draftable entity beans with @Draft property that the bean is a 'draft'.
* Save or Update is not allowed to execute using 'live' beans - must use publish().
@@ -699,7 +705,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
*/
private void postUpdate() {
if (statelessUpdate) {
transaction.getPersistenceContext().clear(beanDescriptor.getBeanType(), idValue);
beanDescriptor.contextClear(transaction.getPersistenceContext(), idValue);
}
}
@@ -707,9 +713,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* Aggressive L1 and L2 cache cleanup for deletes.
*/
private void postDelete() {
// Delete the bean from the PersistenceContent
transaction.getPersistenceContext().clear(beanDescriptor.getBeanType(), idValue);
// Delete from cache early even if transaction fails
beanDescriptor.contextClear(transaction.getPersistenceContext(), idValue);
beanDescriptor.cacheHandleDelete(idValue, this);
}
@@ -731,7 +735,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
controllerPost();
}
if (type == Type.UPDATE && docStoreEvent == DocStoreEvent.UPDATE) {
if (type == Type.UPDATE && docStoreMode == DocStoreMode.UPDATE) {
// get the dirty properties for update notification to the doc store
dirtyProperties = intercept.getDirtyProperties();
}
@@ -913,7 +917,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
if (type == Type.UPDATE) {
beanDescriptor.docStoreUpdateEmbedded(this, docStoreUpdates);
}
switch (docStoreEvent) {
switch (docStoreMode) {
case UPDATE: {
docStoreUpdates.addPersist(this);
return;
@@ -7,6 +7,7 @@ import com.avaje.ebean.Version;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeanservice.docstore.api.DocQueryRequest;
import java.util.List;
import java.util.Map;
@@ -15,7 +16,7 @@ import java.util.Set;
/**
* Defines the ORM query request api.
*/
public interface SpiOrmQueryRequest<T> {
public interface SpiOrmQueryRequest<T> extends DocQueryRequest<T> {
/**
* Return the query.
@@ -0,0 +1,79 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
import java.sql.SQLException;
/**
* Local interface to handle Embedded, Reference and Reference Exported
* cases.
*/
abstract class AssocOneHelp {
protected final BeanPropertyAssocOne property;
protected final BeanDescriptor target;
AssocOneHelp(BeanPropertyAssocOne property) {
this.property = property;
this.target = property.targetDescriptor;
}
/**
* Effectively skip reading (the jdbc resultSet as already in the persistence context etc).
*/
void loadIgnore(DbReadContext ctx) {
property.targetIdBinder.loadIgnore(ctx);
}
/**
* Read and return the bean.
*/
Object read(DbReadContext ctx) throws SQLException {
// Support for Inheritance hierarchy on exported OneToOne ?
Object id = property.targetIdBinder.read(ctx);
if (id == null) {
return null;
}
PersistenceContext pc = ctx.getPersistenceContext();
Object existing = target.contextGet(pc, id);
if (existing != null) {
return existing;
}
Object ref = target.contextRef(pc, ctx.isReadOnly(), id);
EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept();
ctx.register(property.name, ebi);
return ref;
}
/**
* Read setting values into the bean.
*/
Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException {
Object val = read(ctx);
if (bean != null) {
property.setValue(bean, val);
ctx.propagateState(val);
}
return val;
}
/**
* Append to the select clause.
*/
abstract void appendSelect(DbSqlContext ctx, boolean subQuery);
/**
* Append to the from clause.
*/
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
// nothing required here
}
}
@@ -0,0 +1,61 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.bean.EntityBean;
import java.sql.SQLException;
/**
* Helper for Embedded BeanPropertyAssocOne.
*/
final class AssocOneHelpEmbedded extends AssocOneHelp {
public AssocOneHelpEmbedded(BeanPropertyAssocOne property) {
super(property);
}
void loadIgnore(DbReadContext ctx) {
for (int i = 0; i < property.embeddedProps.length; i++) {
property.embeddedProps[i].loadIgnore(ctx);
}
}
@Override
Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException {
Object dbVal = read(ctx);
if (bean != null) {
// set back to the parent bean
property.setValue(bean, dbVal);
ctx.propagateState(dbVal);
return dbVal;
} else {
return null;
}
}
@Override
Object read(DbReadContext ctx) throws SQLException {
EntityBean embeddedBean = property.targetDescriptor.createEntityBean();
boolean notNull = false;
for (int i = 0; i < property.embeddedProps.length; i++) {
Object value = property.embeddedProps[i].readSet(ctx, embeddedBean);
if (value != null) {
notNull = true;
}
}
if (notNull) {
ctx.propagateState(embeddedBean);
return embeddedBean;
} else {
return null;
}
}
@Override
void appendSelect(DbSqlContext ctx, boolean subQuery) {
for (int i = 0; i < property.embeddedProps.length; i++) {
property.embeddedProps[i].appendSelect(ctx, subQuery);
}
}
}
@@ -0,0 +1,33 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
/**
* Helper for BeanPropertyAssocOne for OneToOne exported reference - not so common.
*/
class AssocOneHelpRefExported extends AssocOneHelp {
public AssocOneHelpRefExported(BeanPropertyAssocOne property) {
super(property);
}
/**
* Append columns for foreign key columns.
*/
@Override
void appendSelect(DbSqlContext ctx, boolean subQuery) {
// set appropriate tableAlias for the exported id columns
String relativePrefix = ctx.getRelativePrefix(property.getName());
ctx.pushTableAlias(relativePrefix);
property.targetIdBinder.appendSelect(ctx, subQuery);
ctx.popTableAlias();
}
@Override
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
String relativePrefix = ctx.getRelativePrefix(property.getName());
property.tableJoin.addJoin(joinType, relativePrefix, ctx);
}
}
@@ -0,0 +1,84 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.server.query.SqlJoinType;
import java.sql.SQLException;
/**
* Helper for BeanPropertyAssocOne imported reference but with inheritance.
*/
class AssocOneHelpRefInherit extends AssocOneHelp {
private final InheritInfo inherit;
AssocOneHelpRefInherit(BeanPropertyAssocOne property) {
super(property);
this.inherit = property.targetInheritInfo;
}
@Override
void loadIgnore(DbReadContext ctx) {
property.targetIdBinder.loadIgnore(ctx);
ctx.getDataReader().incrementPos(1);
}
/**
* Read and set a Reference bean.
*/
@Override
Object read(DbReadContext ctx) throws SQLException {
// read discriminator to determine the type
InheritInfo rowInheritInfo = inherit.readType(ctx);
if (rowInheritInfo == null) {
// ignore the id property
property.targetIdBinder.loadIgnore(ctx);
return null;
}
Object id = property.targetIdBinder.read(ctx);
if (id == null) {
return null;
}
// check transaction context to see if it already exists
PersistenceContext pc = ctx.getPersistenceContext();
BeanDescriptor<?> desc = rowInheritInfo.getBeanDescriptor();
Object existing = desc.contextGet(pc, id);
if (existing != null) {
return existing;
}
// 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);
return ref;
}
@Override
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
// add join to support the discriminator column
String relativePrefix = ctx.getRelativePrefix(property.name);
property.tableJoin.addJoin(joinType, relativePrefix, ctx);
}
/**
* Append columns for foreign key columns.
*/
@Override
void appendSelect(DbSqlContext ctx, boolean subQuery) {
if (!subQuery) {
// add discriminator column
String relativePrefix = ctx.getRelativePrefix(property.getName());
String tableAlias = ctx.getTableAlias(relativePrefix);
ctx.appendColumn(tableAlias, property.targetInheritInfo.getDiscriminatorColumn());
}
property.importedId.sqlAppend(ctx);
}
}
@@ -0,0 +1,19 @@
package com.avaje.ebeaninternal.server.deploy;
/**
* Helper for BeanPropertyAssocOne imported reference - this is the common case.
*/
class AssocOneHelpRefSimple extends AssocOneHelp {
AssocOneHelpRefSimple(BeanPropertyAssocOne property) {
super(property);
}
/**
* Append columns for foreign key columns.
*/
@Override
void appendSelect(DbSqlContext ctx, boolean subQuery) {
property.importedId.sqlAppend(ctx);
}
}
@@ -7,11 +7,12 @@ import com.avaje.ebean.SqlUpdate;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.ValuePair;
import com.avaje.ebean.annotation.ConcurrencyMode;
import com.avaje.ebean.annotation.DocStoreEvent;
import com.avaje.ebean.annotation.DocStoreMode;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
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.IdGenerator;
@@ -33,7 +34,6 @@ import com.avaje.ebean.plugin.BeanDocType;
import com.avaje.ebean.plugin.BeanType;
import com.avaje.ebean.plugin.ExpressionPath;
import com.avaje.ebean.plugin.Property;
import com.avaje.ebean.text.json.JsonReadOptions;
import com.avaje.ebeaninternal.api.CQueryPlanKey;
import com.avaje.ebeaninternal.api.LoadContext;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
@@ -71,8 +71,9 @@ import com.avaje.ebeanservice.docstore.api.DocStoreBeanAdapter;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateContext;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdates;
import com.avaje.ebeanservice.docstore.api.mapping.DocMappingBuilder;
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyMapping;
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyType;
import com.avaje.ebeanservice.docstore.api.mapping.DocumentMapping;
import com.fasterxml.jackson.core.JsonParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -80,11 +81,11 @@ import javax.persistence.PersistenceException;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -106,6 +107,28 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
private final ConcurrentHashMap<String, ElComparator<T>> comparatorCache = new ConcurrentHashMap<String, ElComparator<T>>();
public void merge(EntityBean bean, EntityBean existing) {
EntityBeanIntercept fromEbi = bean._ebean_getIntercept();
EntityBeanIntercept toEbi = existing._ebean_getIntercept();
int propertyLength = toEbi.getPropertyLength();
String[] names = getProperties();
for (int i = 0; i < propertyLength; i++) {
if (fromEbi.isLoadedProperty(i) ) {
BeanProperty property = getBeanProperty(names[i]);
if (!toEbi.isLoadedProperty(i)) {
Object val = property.getValue(bean);
property.setValue(existing, val);
} else if (property.isMany()) {
property.merge(bean, existing);
}
}
}
}
public enum EntityType {
ORM, EMBEDDED, SQL
}
@@ -193,6 +216,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
* The type of bean this describes.
*/
private final Class<T> beanType;
protected final Class<?> rootBeanType;
/**
* This is not sent to a remote client.
@@ -359,15 +383,14 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
private final String docStoreQueueId;
private DocumentMapping docMapping;
private final BeanDescriptorDraftHelp<T> draftHelp;
private final BeanDescriptorCacheHelp<T> cacheHelp;
private final BeanDescriptorJsonHelp<T> jsonHelp;
private final DocStoreBeanAdapter<T> docStoreAdapter;
private DocStoreBeanAdapter<T> docStoreAdapter;
private DocumentMapping docMapping;
private final String defaultSelectClause;
private final LinkedHashSet<String> defaultSelectClauseSet;
private SpiEbeanServer ebeanServer;
@@ -386,6 +409,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
this.fullName = InternString.intern(deploy.getFullName());
this.beanType = deploy.getBeanType();
this.rootBeanType = PersistenceContextUtil.root(beanType);
this.prototypeEntityBean = createPrototypeEntityBean(beanType);
this.namedQueries = deploy.getNamedQueries();
@@ -401,8 +425,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
this.changeLogFilter = deploy.getChangeLogFilter();
this.defaultSelectClause = deploy.getDefaultSelectClause();
this.defaultSelectClauseSet = deploy.parseDefaultSelectClause(defaultSelectClause);
this.idType = deploy.getIdType();
this.idTypePlatformDefault = deploy.isIdTypePlatformDefault();
this.idGenerator = deploy.getIdGenerator();
@@ -684,7 +706,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
namedUpdate.initialise(parser);
}
}
docStoreAdapter.registerPaths();
}
/**
@@ -694,7 +715,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
for (int i = 0; i < propertiesMany.length; i++) {
propertiesMany[i].initialisePostTarget();
}
if (inheritInfo != null && !inheritInfo.isRoot()) {
docStoreAdapter = (DocStoreBeanAdapter<T>)inheritInfo.getRoot().getBeanDescriptor().docStoreAdapter();
}
docMapping = docStoreAdapter.createDocMapping();
docStoreAdapter.registerPaths();
}
public void initInheritInfo() {
@@ -889,13 +914,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
return defaultSelectClause;
}
/**
* Return the default select clause already parsed into an ordered Set.
*/
public LinkedHashSet<String> getDefaultSelectClauseSet() {
return defaultSelectClauseSet;
}
/**
* Return true if this object is the root level object in its entity
* inheritance.
@@ -929,7 +947,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
* Return the doc store helper for this bean type.
*/
@Override
public BeanDocType docStore() {
public BeanDocType<T> docStore() {
return docStoreAdapter;
}
@@ -943,24 +961,52 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
/**
* Build the Document mapping recursively with the given prefix relative to the root of the document.
*/
public void docStoreMapping(DocMappingBuilder mapping, String prefix) {
public void docStoreMapping(final DocMappingBuilder mapping, final String prefix) {
if (prefix != null && idProperty != null) {
// id property not included in the
idProperty.docStoreMapping(mapping, prefix);
}
if (inheritInfo != null) {
String discCol = inheritInfo.getDiscriminatorColumn();
if (Types.VARCHAR == inheritInfo.getDiscriminatorType()) {
mapping.add(new DocPropertyMapping(discCol, DocPropertyType.ENUM));
} else {
mapping.add(new DocPropertyMapping(discCol, DocPropertyType.INTEGER));
}
}
for (BeanProperty prop: propertiesNonTransient) {
prop.docStoreMapping(mapping, prefix);
}
if (inheritInfo != null) {
inheritInfo.visitChildren(new InheritInfoVisitor() {
@Override
public void visit(InheritInfo inheritInfo) {
for (BeanProperty localProperty : inheritInfo.localProperties()) {
localProperty.docStoreMapping(mapping, prefix);
}
}
});
}
}
/**
* Return the type of DocStoreEvent that should occur for this type of persist request
* Return the root bean type if part of inheritance hierarchy.
*/
public BeanType<?> root() {
if (inheritInfo != null && !inheritInfo.isRoot()) {
return inheritInfo.getRoot().getBeanDescriptor();
}
return this;
}
/**
* Return the type of DocStoreMode that should occur for this type of persist request
* given the transactions requested mode.
*/
public DocStoreEvent getDocStoreEvent(PersistRequest.Type persistType, DocStoreEvent txnMode) {
return docStoreAdapter.getEvent(persistType, txnMode);
public DocStoreMode getDocStoreMode(PersistRequest.Type persistType, DocStoreMode txnMode) {
return docStoreAdapter.getMode(persistType, txnMode);
}
public void docStoreInsert(Object idValue, PersistRequestBean<T> persistRequest, DocStoreUpdateContext bulkUpdate) throws IOException {
@@ -1457,6 +1503,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
@Override
@SuppressWarnings("unchecked")
public T createBean() {
return (T)createEntityBean();
}
@@ -1464,6 +1511,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
/**
* Creates a new EntityBean.
*/
@SuppressWarnings("unchecked")
public EntityBean createEntityBean() {
try {
EntityBean bean = (EntityBean)prototypeEntityBean._ebean_newInstance();
@@ -1502,14 +1550,14 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
try {
EntityBean eb = createEntityBean();
convertSetId(id, eb);
EntityBeanIntercept ebi = eb._ebean_getIntercept();
ebi.setBeanLoader(ebeanServer);
// Note: not creating proxies for many's...
ebi.setReference(idPropertyIndex);
if (Boolean.TRUE == readOnly) {
ebi.setReadOnly(true);
}
return (T) eb;
@@ -1548,7 +1596,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
String[] splitBegin = SplitName.splitBegin(path);
BeanProperty beanProperty = propMap.get(splitBegin[0]);
BeanProperty beanProperty = findBeanProperty(splitBegin[0]);
if (beanProperty instanceof BeanPropertyAssoc<?>) {
BeanPropertyAssoc<?> assocProp = (BeanPropertyAssoc<?>) beanProperty;
return assocProp.getTargetDescriptor().getBeanDescriptor(splitBegin[1]);
@@ -1642,6 +1690,59 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
return fullName;
}
/**
* Get the bean from the persistence context.
*/
public Object contextGet(PersistenceContext pc, Object id) {
return pc.get(rootBeanType, id);
}
/**
* Get the bean from the persistence context with delete check option.
*/
public PersistenceContext.WithOption contextGetWithOption(PersistenceContext pc, Object id) {
return pc.getWithOption(rootBeanType, id);
}
/**
* Put the bean into the persistence context.
*/
public void contextPut(PersistenceContext pc, Object id, Object bean) {
pc.put(rootBeanType, id, bean);
}
/**
* Put the bean into the persistence context if it is absent.
*/
public Object contextPutIfAbsent(PersistenceContext pc, Object id, EntityBean localBean) {
return pc.putIfAbsent(rootBeanType, id, localBean);
}
/**
* Create a reference bean and put it in the persistence context (and return it).
*/
public Object contextRef(PersistenceContext pc, Boolean readOnly, Object id) {
Object ref = createReference(readOnly, id);
if (pc != null) {
contextPut(pc, id, ref);
}
return ref;
}
/**
* Clear a bean from the persistence context.
*/
public void contextClear(PersistenceContext pc, Object idValue) {
pc.clear(rootBeanType, idValue);
}
/**
* Delete a bean from the persistence context (such that we don't fetch it in the same transaction).
*/
public void contextDeleted(PersistenceContext pc, Object idValue) {
pc.deleted(rootBeanType, idValue);
}
/**
* Helper method to return the unique property. If only one property makes up
* the unique id then it's value is returned. If there is a concatenated
@@ -1718,7 +1819,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
@Override
public Property getProperty(String propName) {
return getBeanProperty(propName);
return findBeanProperty(propName);
}
/**
@@ -1985,6 +2086,30 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
return inheritInfo;
}
@Override
public boolean hasInheritance() {
return inheritInfo != null;
}
@Override
public String getDiscColumn() {
return inheritInfo.getDiscriminatorColumn();
}
@Override
@SuppressWarnings("unchecked")
public T createBeanUsingDisc(Object discValue) {
InheritInfo type = inheritInfo.getType(discValue.toString());
return (T)type.getBeanDescriptor().createBean();
}
@Override
public void addInheritanceWhere(SpiQuery<?> query) {
if (inheritInfo != null && !inheritInfo.isRoot()) {
query.where().eq(inheritInfo.getDiscriminatorColumn(), inheritInfo.getDiscriminatorValue());
}
}
/**
* Return true if this is an embedded bean.
*/
@@ -2660,9 +2785,4 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
return jsonHelp.jsonReadObject(jsonRead, path);
}
@Override
public T jsonRead(JsonParser parser, JsonReadOptions readOptions, Object objectMapper) throws IOException {
ReadJson jsonRead = new ReadJson(this, parser, readOptions, objectMapper);
return jsonRead(jsonRead, null);
}
}
@@ -245,7 +245,7 @@ public final class BeanDescriptorCacheHelp<T> {
EntityBeanIntercept refEbi = ((EntityBean) refBean)._ebean_getIntercept();
many.add(bc, (EntityBean) refBean);
persistenceContext.put(id, refBean);
targetDescriptor.contextPut(persistenceContext, id, refBean);
refEbi.setPersistenceContext(persistenceContext);
}
return true;
@@ -343,8 +343,7 @@ public final class BeanDescriptorCacheHelp<T> {
EntityBeanIntercept ebi = entityBean._ebean_getIntercept();
ebi.setPersistenceContext(context);
Object id = desc.getId(entityBean);
context.put(id, bean);
desc.contextPut(context, id, bean);
}
/**
@@ -254,7 +254,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
@Override
public <T> DocStoreBeanAdapter<T> createDocStoreBeanAdapter(BeanDescriptor descriptor, DeployBeanDescriptor<T> deploy) {
public <T> DocStoreBeanAdapter<T> createDocStoreBeanAdapter(BeanDescriptor<T> descriptor, DeployBeanDescriptor<T> deploy) {
return docStoreFactory.createAdapter(descriptor, deploy);
}
@@ -311,8 +311,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
try {
createListeners();
readEmbeddedDeployment();
readEntityDeploymentInitial();
readEmbeddedDeployment();
readEntityBeanTable();
readEntityDeploymentAssociations();
readInheritedIdGenerators();
@@ -338,8 +338,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
return asOfTableMap;
} catch (RuntimeException e) {
String msg = "Error in deployment";
logger.error(msg, e);
logger.error("Error in deployment", e);
throw e;
}
}
@@ -532,12 +531,17 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
private <T> BeanDescriptor<T> createEmbedded(Class<T> beanClass) {
DeployBeanInfo<T> info = createDeployBeanInfo(beanClass);
readDeployAssociations(info);
DeployBeanInfo<T> info = getDeploy(beanClass);
return new BeanDescriptor<T>(this, info.getDescriptor());
}
/**
* Return the bean deploy info for the given class.
*/
public <T> DeployBeanInfo<T> getDeploy(Class<T> cls) {
return (DeployBeanInfo<T>) deplyInfoMap.get(cls);
}
private void registerBeanDescriptor(BeanDescriptor<?> desc) {
descMap.put(desc.getBeanType().getName(), desc);
if (desc.isDocStoreMapped()) {
@@ -552,13 +556,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
List<Class<?>> embeddedClasses = bootupClasses.getEmbeddables();
for (int i = 0; i < embeddedClasses.size(); i++) {
Class<?> cls = embeddedClasses.get(i);
if (logger.isTraceEnabled()) {
String msg = "load deployinfo for embeddable:" + cls.getName();
logger.trace(msg);
}
BeanDescriptor<?> embDesc = createEmbedded(cls);
registerBeanDescriptor(embDesc);
registerBeanDescriptor(createEmbedded(embeddedClasses.get(i)));
}
}
@@ -571,12 +569,15 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private void readEntityDeploymentInitial() {
List<Class<?>> entityClasses = bootupClasses.getEntities();
for (Class<?> entityClass : entityClasses) {
for (Class<?> entityClass : bootupClasses.getEntities()) {
DeployBeanInfo<?> info = createDeployBeanInfo(entityClass);
deplyInfoMap.put(entityClass, info);
}
for (Class<?> entityClass : bootupClasses.getEmbeddables()) {
DeployBeanInfo<?> info = createDeployBeanInfo(entityClass);
readDeployAssociations(info);
deplyInfoMap.put(entityClass, info);
}
}
/**
@@ -1075,7 +1076,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
*/
private <T> DeployBeanInfo<T> createDeployBeanInfo(Class<T> beanClass) {
DeployBeanDescriptor<T> desc = new DeployBeanDescriptor<T>(beanClass, serverConfig);
DeployBeanDescriptor<T> desc = new DeployBeanDescriptor<T>(this, beanClass, serverConfig);
desc.setUpdateChangesOnly(updateChangesOnly);
@@ -48,5 +48,5 @@ public interface BeanDescriptorMap {
/**
* Create a doc store specific adapter for this bean type.
*/
<T> DocStoreBeanAdapter<T> createDocStoreBeanAdapter(BeanDescriptor descriptor, DeployBeanDescriptor<T> deploy);
<T> DocStoreBeanAdapter<T> createDocStoreBeanAdapter(BeanDescriptor<T> descriptor, DeployBeanDescriptor<T> deploy);
}
@@ -98,14 +98,14 @@ public final class BeanFkeyProperty implements ElPropertyValue {
/**
* Returns null as not an AssocOne.
*/
public Object[] getAssocOneIdValues(EntityBean value) {
public Object[] getAssocIdValues(EntityBean value) {
return null;
}
/**
* Returns null as not an AssocOne.
*/
public String getAssocOneIdExpr(String prefix, String operator) {
public String getAssocIdExpression(String prefix, String operator) {
return null;
}
@@ -162,24 +162,23 @@ public final class BeanFkeyProperty implements ElPropertyValue {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public Object elConvertType(Object value) {
@Override
public Object convert(Object value) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
@Override
public void set(Object bean, Object value) {
public void pathSet(Object bean, Object value) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public void elSetValue(EntityBean bean, Object value, boolean populate) {
@Override
public Object pathGet(Object bean) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public Object elGetValue(EntityBean bean) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public Object elGetReference(EntityBean bean) {
@Override
public Object pathGetNested(Object bean) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
@@ -5,7 +5,6 @@ import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.config.EncryptKey;
import com.avaje.ebean.config.dbplatform.DbEncryptFunction;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebeaninternal.server.type.ScalarTypeEnum;
import com.avaje.ebeanservice.docstore.api.mapping.DocMappingBuilder;
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyMapping;
import com.avaje.ebean.plugin.Property;
@@ -220,6 +219,11 @@ public class BeanProperty implements ElPropertyValue, Property {
*/
final String dbColumnDefn;
/**
* DB Column default value for DDL definition (FALSE, NOW etc).
*/
final String dbColumnDefault;
/**
* Database DDL column comment.
*/
@@ -299,6 +303,7 @@ public class BeanProperty implements ElPropertyValue, Property {
this.dbScale = deploy.getDbScale();
this.dbColumnDefn = InternString.intern(deploy.getDbColumnDefn());
this.dbConstraintExpression = InternString.intern(deploy.getDbConstraintExpression());
this.dbColumnDefault = deploy.getDbColumnDefault();
this.inherited = false;// deploy.isInherited();
this.owningType = deploy.getOwningType();
@@ -408,6 +413,7 @@ public class BeanProperty implements ElPropertyValue, Property {
this.dbScale = source.getDbScale();
this.dbColumnDefn = InternString.intern(source.getDbColumnDefn());
this.dbConstraintExpression = InternString.intern(source.getDbConstraintExpression());
this.dbColumnDefault = source.dbColumnDefault;
this.inherited = source.isInherited();
this.owningType = source.owningType;
@@ -462,7 +468,7 @@ public class BeanProperty implements ElPropertyValue, Property {
public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain,
boolean propertyDeploy) {
throw new PersistenceException("Not valid on scalar bean property " + getFullBeanName());
return null;
}
/**
@@ -768,7 +774,7 @@ public class BeanProperty implements ElPropertyValue, Property {
}
}
public Object elConvertType(Object value) {
public Object convert(Object value) {
if (value == null) {
return null;
}
@@ -776,28 +782,24 @@ public class BeanProperty implements ElPropertyValue, Property {
}
@Override
public void set(Object bean, Object value) {
public void pathSet(Object bean, Object value) {
// convert for Enums etc
Object logicalVal = convertToLogicalType(value);
elSetValue((EntityBean) bean, logicalVal, true);
}
public void elSetValue(EntityBean bean, Object value, boolean populate) {
if (bean != null) {
// Not using setValueIntercept at this stage
setValue(bean, value);
Object logicalVal = convertToLogicalType(value);
setValue((EntityBean)bean, logicalVal);
}
}
public Object elGetValue(EntityBean bean) {
@Override
public Object pathGet(Object bean) {
if (bean == null) {
return null;
}
return getValueIntercept(bean);
return getValueIntercept((EntityBean)bean);
}
public Object elGetReference(EntityBean bean) {
@Override
public Object pathGetNested(Object bean) {
throw new RuntimeException("Not expected to call this");
}
@@ -832,12 +834,12 @@ public class BeanProperty implements ElPropertyValue, Property {
return false;
}
public Object[] getAssocOneIdValues(EntityBean bean) {
public Object[] getAssocIdValues(EntityBean bean) {
// Returns null as not an AssocOne.
return null;
}
public String getAssocOneIdExpr(String prefix, String operator) {
public String getAssocIdExpression(String prefix, String operator) {
// Returns null as not an AssocOne.
return null;
}
@@ -950,6 +952,13 @@ public class BeanProperty implements ElPropertyValue, Property {
return dbType.renderType(dbLength, dbScale);
}
/**
* Return the DB column default to use for DDL.
*/
public String getDbColumnDefault() {
return dbColumnDefn != null ? null : dbColumnDefault;
}
/**
* Return the bean Field associated with this property.
*/
@@ -1243,7 +1252,8 @@ public class BeanProperty implements ElPropertyValue, Property {
writeJson.writeNullField(name);
} else {
if (scalarType != null) {
scalarType.jsonWrite(writeJson, name, value);
writeJson.writeFieldName(name);
scalarType.jsonWrite(writeJson.gen(), value);
} else {
writeJson.writeValueUsingObjectMapper(name, value);
}
@@ -1261,7 +1271,7 @@ public class BeanProperty implements ElPropertyValue, Property {
// expect to read non-null json value
Object objValue;
if (scalarType != null) {
objValue = scalarType.jsonRead(ctx.getParser(), event);
objValue = scalarType.jsonRead(ctx.getParser());
} else {
try {
objValue = ctx.readValueUsingObjectMapper(propertyType);
@@ -1318,7 +1328,7 @@ public class BeanProperty implements ElPropertyValue, Property {
DocPropertyType type = scalarType.getDocType();
DocPropertyOptions options = docOptions.copy();
if (DocPropertyType.STRING == type && isDocCode()) {
if (DocPropertyType.ENUM == type || isStringId(type)) {
options.setCode(true);
}
@@ -1327,9 +1337,13 @@ public class BeanProperty implements ElPropertyValue, Property {
}
/**
* Return true if this should be treated as a 'code' (effectively not analysed).
* Return true if this is a String Id property and should be treated as a code by the document store.
*/
private boolean isDocCode() {
return id || scalarType instanceof ScalarTypeEnum;
private boolean isStringId(DocPropertyType type) {
return DocPropertyType.STRING == type && (id || discriminator);
}
public void merge(EntityBean bean, EntityBean existing) {
// do nothing unless Many property
}
}
@@ -201,6 +201,27 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
// by default not including "Many" properties in document store
}
/**
* Copy collection value if existing is empty.
*/
@Override
public void merge(EntityBean bean, EntityBean existing) {
Object existingCollection = getVal(existing);
if (existingCollection instanceof BeanCollection<?>) {
BeanCollection<?> toBC = (BeanCollection<?>)existingCollection;
if (!toBC.isPopulated()) {
Object fromCollection = getVal(bean);
if (fromCollection instanceof BeanCollection<?>) {
BeanCollection<?> fromBC = (BeanCollection<?>)fromCollection;
if (fromBC.isPopulated()) {
toBC.loadFrom(fromBC);
}
}
}
}
}
/**
* Add the bean to the appropriate collection on the parent bean.
*/
@@ -464,14 +485,14 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
* Return the Id values from the given bean.
*/
@Override
public Object[] getAssocOneIdValues(EntityBean bean) {
public Object[] getAssocIdValues(EntityBean bean) {
return targetDescriptor.getIdBinder().getIdValues(bean);
}
/**
* Return the Id expression to add to where clause etc.
*/
public String getAssocOneIdExpr(String prefix, String operator) {
public String getAssocIdExpression(String prefix, String operator) {
return targetDescriptor.getIdBinder().getAssocOneIdExpr(prefix, operator);
}
@@ -6,11 +6,8 @@ import com.avaje.ebean.SqlUpdate;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.ValuePair;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.server.cache.CachedBeanData;
import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
@@ -40,22 +37,23 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
private final boolean importedPrimaryKey;
private final LocalHelp localHelp;
private AssocOneHelp localHelp;
private final BeanProperty[] embeddedProps;
protected final BeanProperty[] embeddedProps;
private final HashMap<String, BeanProperty> embeddedPropsMap;
/**
* The information for Imported foreign Keys.
*/
private ImportedId importedId;
protected ImportedId importedId;
private ExportedProperty[] exportedProperties;
private String deleteByParentIdSql;
private String deleteByParentIdInSql;
BeanPropertyAssocMany<?> relationshipProperty;
private BeanPropertyAssocMany<?> relationshipProperty;
/**
* Create based on deploy information of an EmbeddedId.
@@ -72,7 +70,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
super(descriptor, deploy);
importedPrimaryKey = false;//TODO: Review this - deploy.isImportedPrimaryKey();
importedPrimaryKey = deploy.isImportedPrimaryKey();
oneToOne = deploy.isOneToOne();
oneToOneExported = deploy.isOneToOneExported();
@@ -89,12 +87,13 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
embeddedProps = null;
embeddedPropsMap = null;
}
localHelp = createHelp(embedded, oneToOneExported);
}
@Override
public void initialise() {
super.initialise();
localHelp = createHelp(embedded, oneToOneExported);
if (!isTransient) {
//noinspection StatementWithEmptyBody
if (embedded) {
@@ -129,21 +128,21 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
return (EntityBean) getValue(owner);
}
public void setRelationshipProperty(BeanPropertyAssocMany<?> relationshipProperty) {
void setRelationshipProperty(BeanPropertyAssocMany<?> relationshipProperty) {
this.relationshipProperty = relationshipProperty;
}
public BeanPropertyAssocMany<?> getRelationshipProperty() {
BeanPropertyAssocMany<?> getRelationshipProperty() {
return relationshipProperty;
}
public void cacheClear() {
void cacheClear() {
if (targetDescriptor.isBeanCaching() && relationshipProperty != null) {
targetDescriptor.cacheManyPropClear(relationshipProperty.getName());
}
}
public void cacheDelete(boolean clearOnNull, EntityBean bean) {
void cacheDelete(boolean clearOnNull, EntityBean bean) {
if (targetDescriptor.isBeanCaching() && relationshipProperty != null) {
Object assocBean = getValue(bean);
if (assocBean != null) {
@@ -270,7 +269,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
}
public void addFkey() {
void addFkey() {
if (importedId != null) {
importedId.addFkeys(name);
}
@@ -416,14 +415,14 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
* Return the Id values from the given bean.
*/
@Override
public Object[] getAssocOneIdValues(EntityBean bean) {
public Object[] getAssocIdValues(EntityBean bean) {
return targetDescriptor.getIdBinder().getIdValues(bean);
}
/**
* Return the Id expression to add to where clause etc.
*/
public String getAssocOneIdExpr(String prefix, String operator) {
public String getAssocIdExpression(String prefix, String operator) {
return targetDescriptor.getIdBinder().getAssocOneIdExpr(prefix, operator);
}
@@ -463,11 +462,11 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
@Override
public Object elGetReference(EntityBean bean) {
Object value = getValueIntercept(bean);
public Object pathGetNested(Object bean) {
Object value = getValueIntercept((EntityBean)bean);
if (value == null) {
value = targetDescriptor.createEntityBean();
setValueIntercept(bean, value);
setValueIntercept((EntityBean)bean, value);
}
return value;
}
@@ -594,7 +593,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
/**
* Set the owner on the embedded bean property.
*/
public void setEmbeddedOwner(EntityBean owner) {
void setEmbeddedOwner(EntityBean owner) {
Object emb = getValue(owner);
if (emb != null) {
@@ -629,269 +628,17 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
}
private LocalHelp createHelp(boolean embedded, boolean oneToOneExported) {
private AssocOneHelp createHelp(boolean embedded, boolean oneToOneExported) {
if (embedded) {
return new Embedded();
return new AssocOneHelpEmbedded(this);
} else if (oneToOneExported) {
return new ReferenceExported();
return new AssocOneHelpRefExported(this);
} else {
return new Reference();
}
}
/**
* Local interface to handle Embedded, Reference and Reference Exported
* cases.
*/
private abstract class LocalHelp {
abstract void loadIgnore(DbReadContext ctx);
abstract Object read(DbReadContext ctx) throws SQLException;
abstract Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException;
abstract void appendSelect(DbSqlContext ctx, boolean subQuery);
abstract void appendFrom(DbSqlContext ctx, SqlJoinType joinType);
}
private final class Embedded extends LocalHelp {
void loadIgnore(DbReadContext ctx) {
for (int i = 0; i < embeddedProps.length; i++) {
embeddedProps[i].loadIgnore(ctx);
}
}
@Override
Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException {
Object dbVal = read(ctx);
if (bean != null) {
// set back to the parent bean
setValue(bean, dbVal);
ctx.propagateState(dbVal);
return dbVal;
} else {
return null;
}
}
@Override
Object read(DbReadContext ctx) throws SQLException {
EntityBean embeddedBean = targetDescriptor.createEntityBean();
boolean notNull = false;
for (int i = 0; i < embeddedProps.length; i++) {
Object value = embeddedProps[i].readSet(ctx, embeddedBean);
if (value != null) {
notNull = true;
}
}
if (notNull) {
ctx.propagateState(embeddedBean);
return embeddedBean;
} else {
return null;
}
}
@Override
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
}
@Override
void appendSelect(DbSqlContext ctx, boolean subQuery) {
for (int i = 0; i < embeddedProps.length; i++) {
embeddedProps[i].appendSelect(ctx, subQuery);
}
}
}
/**
* For imported reference - this is the common case.
*/
private final class Reference extends LocalHelp {
Reference() {
}
@Override
void loadIgnore(DbReadContext ctx) {
targetIdBinder.loadIgnore(ctx);
if (targetInheritInfo != null) {
ctx.getDataReader().incrementPos(1);
}
}
@Override
Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException {
Object val = read(ctx);
if (bean != null) {
setValue(bean, val);
ctx.propagateState(val);
}
return val;
}
/**
* Read and set a Reference bean.
*/
@Override
Object read(DbReadContext ctx) throws SQLException {
BeanDescriptor<?> rowDescriptor = null;
Class<?> rowType = targetType;
if (targetInheritInfo != null) {
// read discriminator to determine the type
InheritInfo rowInheritInfo = targetInheritInfo.readType(ctx);
if (rowInheritInfo != null) {
rowType = rowInheritInfo.getType();
rowDescriptor = rowInheritInfo.getBeanDescriptor();
}
}
// read the foreign key column(s)
Object id = targetIdBinder.read(ctx);
if (id == null) {
return null;
}
// check transaction context to see if it already exists
Object existing = ctx.getPersistenceContext().get(rowType, id);
if (existing != null) {
return existing;
}
Boolean readOnly = ctx.isReadOnly();
Object ref;
if (targetInheritInfo != null) {
// for inheritance hierarchy create the correct type for this row...
ref = rowDescriptor.createReference(readOnly, id);
return new AssocOneHelpRefInherit(this);
} else {
ref = targetDescriptor.createReference(readOnly, id);
return new AssocOneHelpRefSimple(this);
}
Object existingBean = ctx.getPersistenceContext().putIfAbsent(id, ref);
if (existingBean != null) {
// advanced case when we use multiple concurrent threads to
// build a single object graph, and another thread has since
// loaded a matching bean so we will use that instead.
ref = existingBean;
} else {
EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept();
if (Boolean.TRUE.equals(ctx.isReadOnly())) {
ebi.setReadOnly(true);
}
ctx.register(name, ebi);
}
return ref;
}
@Override
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
if (targetInheritInfo != null) {
// add join to support the discriminator column
String relativePrefix = ctx.getRelativePrefix(name);
tableJoin.addJoin(joinType, relativePrefix, ctx);
}
}
/**
* Append columns for foreign key columns.
*/
@Override
void appendSelect(DbSqlContext ctx, boolean subQuery) {
if (!subQuery && targetInheritInfo != null) {
// add discriminator column
String relativePrefix = ctx.getRelativePrefix(getName());
String tableAlias = ctx.getTableAlias(relativePrefix);
ctx.appendColumn(tableAlias, targetInheritInfo.getDiscriminatorColumn());
}
importedId.sqlAppend(ctx);
}
}
/**
* For OneToOne exported reference - not so common.
*/
private final class ReferenceExported extends LocalHelp {
@Override
void loadIgnore(DbReadContext ctx) {
targetDescriptor.getIdBinder().loadIgnore(ctx);
}
/**
* Read and set a Reference bean.
*/
@Override
Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException {
Object dbVal = read(ctx);
if (bean != null) {
setValue(bean, dbVal);
ctx.propagateState(dbVal);
}
return dbVal;
}
@Override
Object read(DbReadContext ctx) throws SQLException {
// TODO: Support for Inheritance hierarchy on exported OneToOne ?
IdBinder idBinder = targetDescriptor.getIdBinder();
Object id = idBinder.read(ctx);
if (id == null) {
return null;
}
PersistenceContext persistCtx = ctx.getPersistenceContext();
Object existing = persistCtx.get(targetType, id);
if (existing != null) {
return existing;
}
Object ref = targetDescriptor.createReference(ctx.isReadOnly(), id);
EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept();
if (Boolean.TRUE.equals(ctx.isReadOnly())) {
ebi.setReadOnly(true);
}
persistCtx.put(id, ref);
ctx.register(name, ebi);
return ref;
}
/**
* Append columns for foreign key columns.
*/
@Override
void appendSelect(DbSqlContext ctx, boolean subQuery) {
// set appropriate tableAlias for the exported id columns
String relativePrefix = ctx.getRelativePrefix(getName());
ctx.pushTableAlias(relativePrefix);
IdBinder idBinder = targetDescriptor.getIdBinder();
idBinder.appendSelect(ctx, subQuery);
ctx.popTableAlias();
}
@Override
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
String relativePrefix = ctx.getRelativePrefix(getName());
tableJoin.addJoin(joinType, relativePrefix, ctx);
}
}
@@ -143,7 +143,7 @@ public class BeanPropertyCompound extends BeanProperty {
}
@Override
public Object elGetReference(EntityBean bean) {
public Object pathGetNested(Object bean) {
return bean;
}
@@ -155,6 +155,7 @@ public class BeanPropertyCompound extends BeanProperty {
if (value == null) {
ctx.writeNullField(name);
} else {
ctx.writeFieldName(name);
compoundType.jsonWrite(ctx, value, name);
}
}
@@ -72,13 +72,13 @@ public class BeanPropertyCompoundScalar extends BeanProperty {
}
@Override
public Object elGetReference(EntityBean bean) {
return getValue(bean);
public Object pathGetNested(Object bean) {
return pathGet(bean);
}
@Override
public Object elGetValue(EntityBean bean) {
return getValue(bean);
public Object pathGet(Object bean) {
return ctProperty.getValue(bean);
}
}
@@ -10,64 +10,71 @@ import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyOptions;
*/
public class DeployDocPropertyOptions {
private Boolean code;
private static DocPropertyOptions EMPTY = new DocPropertyOptions();
private Boolean sortable;
private Boolean store;
private Float boost;
private String nullValue;
private DocPropertyOptions mapping;
private void createOptions() {
if (mapping == null) {
mapping = new DocPropertyOptions();
}
}
/**
* Read the DocProperty deployment options.
*/
public void setDocProperty(DocProperty doc) {
code = checkDefault(doc.code());
sortable = checkDefault(doc.sortable());
store = checkDefault(doc.store());
boost = checkDefault(doc.boost());
nullValue = checkDefault(doc.nullValue());
createOptions();
mapping.apply(doc);
}
/**
* Read the DocSortable deployment options.
*/
public void setDocSortable(DocSortable doc) {
sortable = Boolean.TRUE;
store = checkDefault(doc.store());
boost = checkDefault(doc.boost());
nullValue = checkDefault(doc.nullValue());
createOptions();
mapping.setSortable(true);
setStore(doc.store());
setBoost(doc.boost());
setNullValue(doc.nullValue());
}
/**
* Read the DocCode deployment options.
*/
public void setDocCode(DocCode doc) {
code = Boolean.TRUE;
store = checkDefault(doc.store());
boost = checkDefault(doc.boost());
nullValue = checkDefault(doc.nullValue());
createOptions();
mapping.setCode(true);
setStore(doc.store());
setBoost(doc.boost());
setNullValue(doc.nullValue());
}
private String checkDefault(String s) {
return "".equals(s) ? null : s;
private void setNullValue(String value) {
if (!value.equals("")) {
mapping.setNullValue(value);
}
}
private Float checkDefault(float boost) {
return (boost == 1) ? null : boost;
private void setBoost(float boost) {
if (boost != 1) {
mapping.setBoost(boost);
}
}
private Boolean checkDefault(boolean store) {
return (store) ? Boolean.TRUE : null;
private void setStore(boolean store) {
if (store) {
mapping.setStore(true);
}
}
/**
* Return the DocPropertyOptions with the collected options.
*/
public DocPropertyOptions create() {
return new DocPropertyOptions(code, sortable, store, boost, nullValue);
return (mapping == null) ? EMPTY : mapping;
}
}
@@ -145,6 +145,13 @@ public class InheritInfo {
return descriptor;
}
/**
* Return the local properties for this node in the hierarchy.
*/
public BeanProperty[] localProperties() {
return descriptor.propertiesLocal();
}
/**
* Get the bean property additionally looking in the sub types.
*/
@@ -2,7 +2,7 @@ package com.avaje.ebeaninternal.server.deploy.meta;
import com.avaje.ebean.annotation.ConcurrencyMode;
import com.avaje.ebean.annotation.DocStore;
import com.avaje.ebean.annotation.DocStoreEvent;
import com.avaje.ebean.annotation.DocStoreMode;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.TableName;
import com.avaje.ebean.config.dbplatform.IdGenerator;
@@ -14,8 +14,10 @@ import com.avaje.ebean.event.BeanPostLoad;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.event.changelog.ChangeLogFilter;
import com.avaje.ebean.text.PathProperties;
import com.avaje.ebean.util.CamelCaseHelper;
import com.avaje.ebeaninternal.server.core.CacheOptions;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistController;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistListener;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanPostLoad;
@@ -25,6 +27,7 @@ import com.avaje.ebeaninternal.server.deploy.DRawSqlMeta;
import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.deploy.parse.DeployBeanInfo;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
@@ -34,7 +37,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -43,7 +45,7 @@ import java.util.Map;
*/
public class DeployBeanDescriptor<T> {
static class PropOrder implements Comparator<DeployBeanProperty> {
private static class PropOrder implements Comparator<DeployBeanProperty> {
public int compare(DeployBeanProperty o1, DeployBeanProperty o2) {
@@ -59,6 +61,8 @@ public class DeployBeanDescriptor<T> {
private final ServerConfig serverConfig;
private final BeanDescriptorManager manager;
/**
* Map of BeanProperty Linked so as to preserve order.
*/
@@ -187,19 +191,29 @@ public class DeployBeanDescriptor<T> {
private String docStoreIndexType;
private DocStoreEvent docStorePersist;
private DocStoreEvent docStoreInsert;
private DocStoreEvent docStoreUpdate;
private DocStoreEvent docStoreDelete;
private DocStoreMode docStorePersist;
private DocStoreMode docStoreInsert;
private DocStoreMode docStoreUpdate;
private DocStoreMode docStoreDelete;
private List<DeployBeanProperty> idProperties;
/**
* Construct the BeanDescriptor.
*/
public DeployBeanDescriptor(Class<T> beanType, ServerConfig serverConfig) {
public DeployBeanDescriptor(BeanDescriptorManager manager, Class<T> beanType, ServerConfig serverConfig) {
this.manager = manager;
this.serverConfig = serverConfig;
this.beanType = beanType;
}
/**
* Return the DeployBeanInfo for the given bean class.
*/
DeployBeanInfo<?> getDeploy(Class<?> cls) {
return manager.getDeploy(cls);
}
/**
* Return true if this beanType is an abstract class.
*/
@@ -275,7 +289,7 @@ public class DeployBeanDescriptor<T> {
docStoreUpdate = docStore.update();
docStoreDelete = docStore.delete();
String doc = docStore.doc();
if (doc != null && doc.length() > 0) {
if (doc.length() > 0) {
docStorePathProperties = PathProperties.parse(doc);
}
}
@@ -628,6 +642,34 @@ public class DeployBeanDescriptor<T> {
return propMap.put(prop.getName(), prop);
}
/**
* Find the matching property for a given property name or dbColumn.
* <p>
* This is primarily to find imported primary key columns (ManyToOne that also match the PK).
* </p>
*/
DeployBeanProperty findMatch(String propertyName, String dbColumn) {
DeployBeanProperty prop = propMap.get(propertyName);
if (prop != null) {
return prop;
}
if (dbColumn != null) {
String asCamel = CamelCaseHelper.toCamelFromUnderscore(dbColumn);
prop = propMap.get(asCamel);
if (prop != null) {
return prop;
}
// scan looking for dbColumn match
for (DeployBeanProperty property : propMap.values()) {
if (dbColumn.equals(property.getDbColumn())) {
return property;
}
}
}
return null;
}
/**
* Get a BeanProperty by its name.
*/
@@ -698,7 +740,7 @@ public class DeployBeanDescriptor<T> {
/**
* Set the DB sequence name.
*/
public void setSequenceName(String sequenceName) {
private void setSequenceName(String sequenceName) {
this.sequenceName = sequenceName;
}
@@ -765,7 +807,7 @@ public class DeployBeanDescriptor<T> {
tableJoinList.add(join);
}
public List<DeployTableJoin> getTableJoins() {
List<DeployTableJoin> getTableJoins() {
return tableJoinList;
}
@@ -805,29 +847,6 @@ public class DeployBeanDescriptor<T> {
return selectClause.substring(0, selectClause.length() - 1);
}
/**
* Parse the include separating by comma or semicolon.
*/
public LinkedHashSet<String> parseDefaultSelectClause(String rawList) {
if (rawList == null) {
return null;
}
String[] res = rawList.split(",");
LinkedHashSet<String> set = new LinkedHashSet<String>(res.length + 3);
String temp;
for (int i = 0; i < res.length; i++) {
temp = res[i].trim();
if (temp.length() > 0) {
set.add(temp);
}
}
return set;
}
/**
* Return true if the primary key is a compound key or if it's database type
* is non-numeric (and hence not suitable for db identity or sequence.
@@ -874,15 +893,15 @@ public class DeployBeanDescriptor<T> {
*/
public List<DeployBeanProperty> propertiesId() {
ArrayList<DeployBeanProperty> list = new ArrayList<DeployBeanProperty>(2);
for (DeployBeanProperty prop : propMap.values()) {
if (prop.isId()) {
list.add(prop);
if (idProperties == null) {
idProperties = new ArrayList<DeployBeanProperty>(2);
for (DeployBeanProperty prop : propMap.values()) {
if (prop.isId()) {
idProperties.add(prop);
}
}
}
return list;
return idProperties;
}
public DeployBeanPropertyAssocOne<?> findJoinToTable(String tableName) {
@@ -1008,30 +1027,30 @@ public class DeployBeanDescriptor<T> {
/**
* Return the DocStore index behavior for bean inserts.
*/
public DocStoreEvent getDocStoreInsertEvent() {
public DocStoreMode getDocStoreInsertEvent() {
return getDocStoreIndexEvent(docStoreInsert);
}
/**
* Return the DocStore index behavior for bean updates.
*/
public DocStoreEvent getDocStoreUpdateEvent() {
public DocStoreMode getDocStoreUpdateEvent() {
return getDocStoreIndexEvent(docStoreUpdate);
}
/**
* Return the DocStore index behavior for bean deletes.
*/
public DocStoreEvent getDocStoreDeleteEvent() {
public DocStoreMode getDocStoreDeleteEvent() {
return getDocStoreIndexEvent(docStoreDelete);
}
private DocStoreEvent getDocStoreIndexEvent(DocStoreEvent mostSpecific) {
private DocStoreMode getDocStoreIndexEvent(DocStoreMode mostSpecific) {
if (!docStoreMapped) {
return DocStoreEvent.IGNORE;
return DocStoreMode.IGNORE;
}
if (mostSpecific != DocStoreEvent.DEFAULT) return mostSpecific;
if (docStorePersist != DocStoreEvent.DEFAULT) return docStorePersist;
if (mostSpecific != DocStoreMode.DEFAULT) return mostSpecific;
if (docStorePersist != DocStoreMode.DEFAULT) return docStorePersist;
return serverConfig.getDocStoreConfig().getPersist();
}
}
@@ -11,6 +11,7 @@ import com.avaje.ebean.annotation.WhenModified;
import com.avaje.ebean.annotation.WhoCreated;
import com.avaje.ebean.annotation.WhoModified;
import com.avaje.ebean.config.ScalarTypeConverter;
import com.avaje.ebean.config.dbplatform.DbDefaultValue;
import com.avaje.ebean.config.dbplatform.DbEncrypt;
import com.avaje.ebean.config.dbplatform.DbEncryptFunction;
import com.avaje.ebeaninternal.server.core.InternString;
@@ -182,7 +183,7 @@ public class DeployBeanProperty {
*/
private GeneratedProperty generatedProperty;
private final DeployBeanDescriptor<?> desc;
protected final DeployBeanDescriptor<?> desc;
private boolean undirectionalShadow;
@@ -202,6 +203,8 @@ public class DeployBeanProperty {
private String dbComment;
private String dbColumnDefault;
public DeployBeanProperty(DeployBeanDescriptor<?> desc, Class<?> propertyType, ScalarType<?> scalarType, ScalarTypeConverter<?, ?> typeConverter) {
this.desc = desc;
this.propertyType = propertyType;
@@ -902,6 +905,7 @@ public class DeployBeanProperty {
public void setSoftDelete() {
this.softDelete = true;
this.nullable = false;
this.dbColumnDefault = DbDefaultValue.FALSE;
}
public boolean isSoftDelete() {
@@ -932,4 +936,7 @@ public class DeployBeanProperty {
return docMapping.create();
}
public String getDbColumnDefault() {
return dbColumnDefault;
}
}
@@ -12,43 +12,50 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
/**
* The type of the joined bean.
*/
Class<T> targetType;
protected Class<T> targetType;
/**
* Persist settings.
*/
final BeanCascadeInfo cascadeInfo = new BeanCascadeInfo();
private final BeanCascadeInfo cascadeInfo = new BeanCascadeInfo();
/**
* The join table information.
*/
BeanTable beanTable;
private BeanTable beanTable;
/**
* Join between the beans.
*/
final DeployTableJoin tableJoin = new DeployTableJoin();
protected final DeployTableJoin tableJoin = new DeployTableJoin();
/**
* Literal added to where clause of lazy loading query.
*/
String extraWhere;
private String extraWhere;
/**
* From the deployment mappedBy attribute.
*/
String mappedBy;
private String mappedBy;
String docStoreDoc;
private String docStoreDoc;
/**
* Construct the property.
*/
public DeployBeanPropertyAssoc(DeployBeanDescriptor<?> desc, Class<T> targetType) {
DeployBeanPropertyAssoc(DeployBeanDescriptor<?> desc, Class<T> targetType) {
super(desc, targetType, null, null);
this.targetType = targetType;
}
/**
* Return the target DeployBeanDescriptor for this associated bean property.
*/
public DeployBeanDescriptor<?> getTargetDeploy() {
return desc.getDeploy(targetType).getDescriptor();
}
/**
* Return the type of the target.
* <p>
@@ -5,30 +5,32 @@ package com.avaje.ebeaninternal.server.deploy.meta;
*/
public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
boolean oneToOne;
boolean oneToOneExported;
private boolean oneToOne;
DeployBeanEmbedded deployEmbedded;
/**
* Create the property.
*/
public DeployBeanPropertyAssocOne(DeployBeanDescriptor<?> desc, Class<T> targetType) {
super(desc, targetType);
}
private boolean oneToOneExported;
/**
* Return the deploy information specifically for the deployment
* of Embedded beans.
*/
public DeployBeanEmbedded getDeployEmbedded() {
// deployment should be single threaded
if (deployEmbedded == null){
deployEmbedded = new DeployBeanEmbedded();
}
return deployEmbedded;
}
private boolean importedPrimaryKey;
private DeployBeanEmbedded deployEmbedded;
/**
* Create the property.
*/
public DeployBeanPropertyAssocOne(DeployBeanDescriptor<?> desc, Class<T> targetType) {
super(desc, targetType);
}
/**
* Return the deploy information specifically for the deployment
* of Embedded beans.
*/
public DeployBeanEmbedded getDeployEmbedded() {
// deployment should be single threaded
if (deployEmbedded == null) {
deployEmbedded = new DeployBeanEmbedded();
}
return deployEmbedded;
}
/**
* Return true if this has multiple properties (expected for embedded id).
@@ -38,42 +40,60 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
return getDeployEmbedded().getPropertyColumnMap().size() > 1;
}
@Override
public String getDbColumn() {
DeployTableJoinColumn[] columns = tableJoin.columns();
if (columns.length == 1){
return columns[0].getLocalDbColumn();
}
return super.getDbColumn();
@Override
public String getDbColumn() {
DeployTableJoinColumn[] columns = tableJoin.columns();
if (columns.length == 1) {
return columns[0].getLocalDbColumn();
}
return super.getDbColumn();
}
/**
* Return true if this a OneToOne property. Otherwise assumed ManyToOne.
*/
public boolean isOneToOne() {
return oneToOne;
}
/**
* Return true if this a OneToOne property. Otherwise assumed ManyToOne.
*/
public boolean isOneToOne() {
return oneToOne;
}
/**
* Set to true if this is a OneToOne.
*/
public void setOneToOne() {
this.oneToOne = true;
}
/**
* Set to true if this is a OneToOne.
*/
public void setOneToOne() {
this.oneToOne = true;
}
/**
* Return true if this is the exported side of a OneToOne.
*/
public boolean isOneToOneExported() {
return oneToOneExported;
}
/**
* Return true if this is the exported side of a OneToOne.
*/
public boolean isOneToOneExported() {
return oneToOneExported;
}
/**
* Set to true if this is the exported side of a OneToOne. This means
* it doesn't 'own' the foreign key column. A OneToMany without the many.
*/
public void setOneToOneExported() {
this.oneToOneExported = true;
}
/**
* Set to true if this is the exported side of a OneToOne. This means
* it doesn't 'own' the foreign key column. A OneToMany without the many.
*/
public void setOneToOneExported() {
this.oneToOneExported = true;
}
/**
* Return true if this is part of the primary key.
*/
public boolean isImportedPrimaryKey() {
return importedPrimaryKey;
}
/**
* Set to true if this is part of the primary key.
*/
void setImportedPrimaryKey(DeployBeanProperty primaryKey) {
this.importedPrimaryKey = true;
String dbColumn = primaryKey.getDbColumn();
if (dbColumn != null) {
// change join db column if matched by property name
tableJoin.setLocalColumn(dbColumn);
}
}
}
@@ -1,12 +1,5 @@
package com.avaje.ebeaninternal.server.deploy.meta;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.bean.BeanCollection.ModifyListenMode;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorMap;
@@ -18,6 +11,12 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertySimpleCollection;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
import com.avaje.ebeaninternal.server.type.ScalarTypeString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
/**
* Helper object to classify BeanProperties into appropriate lists.
@@ -68,6 +67,8 @@ public class DeployBeanPropertyLists {
public DeployBeanPropertyLists(BeanDescriptorMap owner, BeanDescriptor<?> desc, DeployBeanDescriptor<?> deploy) {
this.desc = desc;
setImportedPrimaryKeys(deploy);
DeployBeanPropertyAssocOne<?> deployUnidirectional = deploy.getUnidirectional();
if (deployUnidirectional == null) {
unidirectional = null;
@@ -125,6 +126,31 @@ public class DeployBeanPropertyLists {
}
}
/**
* Find and set imported primary keys.
* <p>
* This is where @ManyToOne properties maps to a PFK (Primary and foreign key).
* Perform the match by naming convention on property name and db column.
* </p>
*/
private void setImportedPrimaryKeys(DeployBeanDescriptor<?> deploy) {
List<DeployBeanProperty> ids = deploy.propertiesId();
if (ids.size() == 1) {
DeployBeanProperty id = ids.get(0);
if (id instanceof DeployBeanPropertyAssocOne<?>) {
// only interested if the primary key is a compound key
DeployBeanDescriptor<?> targetDeploy = ((DeployBeanPropertyAssocOne<?>) id).getTargetDeploy();
for (DeployBeanPropertyAssocOne<?> assoc : deploy.propertiesAssocOne()) {
DeployBeanProperty pkMatch = targetDeploy.findMatch(assoc.getName(), assoc.getDbColumn());
if (pkMatch != null) {
assoc.setImportedPrimaryKey(pkMatch);
}
}
}
}
}
/**
* Return the unidirectional.
*/
@@ -155,4 +155,13 @@ public class DeployTableJoin {
public void setInheritInfo(InheritInfo inheritInfo) {
this.inheritInfo = inheritInfo;
}
/**
* Change the join column (based on imported primary key match on property name etc).
*/
void setLocalColumn(String dbColumn) {
if (columns.size() == 1) {
columns.get(0).setLocalDbColumn(dbColumn);
}
}
}
@@ -126,4 +126,10 @@ public class DeployTableJoinColumn {
return localDbColumn;
}
/**
* Set the local database column name.
*/
public void setLocalDbColumn(String localDbColumn) {
this.localDbColumn = localDbColumn;
}
}
@@ -184,17 +184,17 @@ public class AnnotationFields extends AnnotationParser {
}
}
DocProperty docProperty = get(prop, DocProperty.class);
if (docProperty != null) {
prop.setDocProperty(docProperty);
DocCode docCode = get(prop, DocCode.class);
if (docCode != null) {
prop.setDocCode(docCode);
}
DocSortable docSortable = get(prop, DocSortable.class);
if (docSortable != null) {
prop.setDocSortable(docSortable);
}
DocCode docCode = get(prop, DocCode.class);
if (docCode != null) {
prop.setDocCode(docCode);
DocProperty docProperty = get(prop, DocProperty.class);
if (docProperty != null) {
prop.setDocProperty(docProperty);
}
if (get(prop, DbHstore.class) != null) {
@@ -2,8 +2,6 @@ package com.avaje.ebeaninternal.server.el;
import java.util.Comparator;
import com.avaje.ebean.bean.EntityBean;
/**
* Comparator based on a ElGetValue.
*/
@@ -23,16 +21,14 @@ public final class ElComparatorProperty<T> implements Comparator<T>, ElComparato
public int compare(T o1, T o2) {
Object val1 = elGetValue.elGetValue((EntityBean) o1);
Object val2 = elGetValue.elGetValue((EntityBean) o2);
Object val1 = elGetValue.pathGet(o1);
Object val2 = elGetValue.pathGet(o2);
return compareValues(val1, val2);
}
public int compareValue(Object value, T o2) {
Object val2 = elGetValue.elGetValue((EntityBean) o2);
Object val2 = elGetValue.pathGet(o2);
return compareValues(value, val2);
}
@@ -28,7 +28,7 @@ public final class ElFilter<T> implements Filter<T> {
private Object convertValue(String propertyName, Object value) {
// convert type of value to match expected type
ElPropertyValue elGetValue = beanDescriptor.getElGetValue(propertyName);
return elGetValue.elConvertType(value);
return elGetValue.convert(value);
}
private ElComparator<T> getElComparator(String propertyName) {
@@ -87,8 +87,8 @@ public final class ElFilter<T> implements Filter<T> {
public Filter<T> between(String propertyName, Object min, Object max) {
ElPropertyValue elGetValue = getElGetValue(propertyName);
min = elGetValue.elConvertType(min);
max = elGetValue.elConvertType(max);
min = elGetValue.convert(min);
max = elGetValue.convert(max);
ElComparator<T> elComparator = getElComparator(propertyName);
@@ -28,7 +28,7 @@ class ElMatchBuilder {
}
public boolean isMatch(T bean) {
String v = (String) elGetValue.elGetValue((EntityBean) bean);
String v = (String) elGetValue.pathGet(bean);
return pattern.matcher(v).matches();
}
}
@@ -55,7 +55,7 @@ class ElMatchBuilder {
}
public boolean isMatch(T bean) {
String v = (String) elGetValue.elGetValue((EntityBean) bean);
String v = (String) elGetValue.pathGet(bean);
return value.equalsIgnoreCase(v);
}
}
@@ -75,7 +75,7 @@ class ElMatchBuilder {
public boolean isMatch(T bean) {
String v = (String) elGetValue.elGetValue((EntityBean) bean);
String v = (String) elGetValue.pathGet(bean);
return charMatch.startsWith(v);
}
}
@@ -95,7 +95,7 @@ class ElMatchBuilder {
public boolean isMatch(T bean) {
String v = (String) elGetValue.elGetValue((EntityBean) bean);
String v = (String) elGetValue.pathGet(bean);
return charMatch.endsWith(v);
}
}
@@ -106,7 +106,7 @@ class ElMatchBuilder {
}
public boolean isMatch(T bean) {
String v = (String) elGetValue.elGetValue((EntityBean) bean);
String v = (String) elGetValue.pathGet(bean);
return value.startsWith(v);
}
}
@@ -117,7 +117,7 @@ class ElMatchBuilder {
}
public boolean isMatch(T bean) {
String v = (String) elGetValue.elGetValue((EntityBean) bean);
String v = (String) elGetValue.pathGet(bean);
return value.endsWith(v);
}
}
@@ -131,7 +131,7 @@ class ElMatchBuilder {
}
public boolean isMatch(T bean) {
return (null == elGetValue.elGetValue((EntityBean) bean));
return (null == elGetValue.pathGet(bean));
}
}
@@ -144,7 +144,7 @@ class ElMatchBuilder {
}
public boolean isMatch(T bean) {
return (null != elGetValue.elGetValue((EntityBean) bean));
return (null != elGetValue.pathGet(bean));
}
}
@@ -175,7 +175,7 @@ class ElMatchBuilder {
public boolean isMatch(T bean) {
Object value = elGetValue.elGetValue((EntityBean) bean);
Object value = elGetValue.pathGet(bean);
return value != null && set.contains(value);
}
}
@@ -1,7 +1,6 @@
package com.avaje.ebeaninternal.server.el;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.text.StringFormatter;
import com.avaje.ebean.text.StringParser;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
@@ -155,14 +154,14 @@ public class ElPropertyChain implements ElPropertyValue {
return lastElPropertyValue.isLocalEncrypted();
}
public Object[] getAssocOneIdValues(EntityBean bean) {
public Object[] getAssocIdValues(EntityBean bean) {
// Don't navigate the object graph as bean
// is assumed to be the appropriate type
return lastElPropertyValue.getAssocOneIdValues(bean);
return lastElPropertyValue.getAssocIdValues(bean);
}
public String getAssocOneIdExpr(String prefix, String operator) {
return lastElPropertyValue.getAssocOneIdExpr(expression, operator);
public String getAssocIdExpression(String prefix, String operator) {
return lastElPropertyValue.getAssocIdExpression(expression, operator);
}
public String getAssocIdInExpr(String prefix) {
@@ -173,16 +172,6 @@ public class ElPropertyChain implements ElPropertyValue {
return lastElPropertyValue.getAssocIdInValueExpr(size);
}
public int getDeployOrder() {
int i = lastBeanProperty.getDeployOrder();
int max = chain.length - 1;
for (int j = 0; j < max; j++) {
int xtra = ((max - j) * 1000) * chain[j].getDeployOrder();
i += xtra;
}
return i;
}
public boolean isAssocId() {
return assocId;
}
@@ -221,64 +210,49 @@ public class ElPropertyChain implements ElPropertyValue {
return scalarType;
}
public Object elConvertType(Object value) {
public Object convert(Object value) {
// just convert using the last one in the chain
return lastElPropertyValue.elConvertType(value);
return lastElPropertyValue.convert(value);
}
public Object elGetValue(EntityBean bean) {
@Override
public Object pathGet(Object bean) {
for (int i = 0; i < chain.length; i++) {
bean = (EntityBean) chain[i].elGetValue(bean);
bean = chain[i].pathGet(bean);
if (bean == null) {
return null;
}
}
return bean;
}
public Object elGetReference(EntityBean bean) {
@Override
public Object pathGetNested(Object bean) {
EntityBean prevBean = bean;
Object prevBean = bean;
for (int i = 0; i < last; i++) {
// always return non null prevBean
prevBean = (EntityBean) chain[i].elGetReference(prevBean);
prevBean = chain[i].pathGetNested(prevBean);
}
// try the last step in the chain
return chain[last].elGetValue(prevBean);
return chain[last].pathGet(prevBean);
}
@Override
public void set(Object bean, Object value) {
elSetValue((EntityBean)bean, value, true);
}
public void pathSet(Object bean, Object value) {
public void elSetValue(EntityBean bean, Object value, boolean populate) {
EntityBean prevBean = bean;
if (populate) {
for (int i = 0; i < last; i++) {
// always return non null prevBean
prevBean = (EntityBean) chain[i].elGetReference(prevBean);
}
} else {
for (int i = 0; i < last; i++) {
// always return non null prevBean
prevBean = (EntityBean) chain[i].elGetValue(prevBean);
if (prevBean == null) {
break;
}
}
Object prevBean = bean;
for (int i = 0; i < last; i++) {
prevBean = chain[i].pathGetNested(prevBean);
}
if (prevBean != null) {
if (lastBeanProperty != null) {
// last chain element maps to a real scalar property
lastBeanProperty.setValueIntercept(prevBean, value);
lastBeanProperty.pathSet(prevBean, value);
} else {
// a non-scalar property of a Compound value object
lastElPropertyValue.elSetValue(prevBean, value, populate);
lastElPropertyValue.pathSet(prevBean, value);
}
}
}

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