mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
66
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70cce1094e | ||
|
|
44c69dec72 | ||
|
|
6c7074e288 | ||
|
|
1c162b9b18 | ||
|
|
676f0d7a24 | ||
|
|
46e674db57 | ||
|
|
0e1aacdeee | ||
|
|
7268890332 | ||
|
|
b2a3f0c99b | ||
|
|
662be92a16 | ||
|
|
11d623cb69 | ||
|
|
a20822a9e1 | ||
|
|
0510304c0f | ||
|
|
fb2488e3b8 | ||
|
|
5959dcc7db | ||
|
|
3656091d1f | ||
|
|
5122ea4b4d | ||
|
|
db8dd48e53 | ||
|
|
4606c7c99f | ||
|
|
e3a7c3040b | ||
|
|
cb51ea4238 | ||
|
|
a1a48dac2c | ||
|
|
b8d9273152 | ||
|
|
026f0e161f | ||
|
|
75a2633590 | ||
|
|
362ceea79b | ||
|
|
1c9b974427 | ||
|
|
f181e3946f | ||
|
|
6df145b4fa | ||
|
|
a4cb116d4f | ||
|
|
3161664d58 | ||
|
|
4b3f63f6e0 | ||
|
|
d89b1b0367 | ||
|
|
17433bb70b | ||
|
|
fb6ea92ae9 | ||
|
|
08b0a6350d | ||
|
|
bd330b4c0e | ||
|
|
ec234f1f39 | ||
|
|
dd28b28095 | ||
|
|
2a5be6db3d | ||
|
|
545f40703e | ||
|
|
35d0481644 | ||
|
|
2282ff3c6e | ||
|
|
d1d4bf1da8 | ||
|
|
8c6653605d | ||
|
|
e344b5c0a1 | ||
|
|
0e5859716b | ||
|
|
90a794fca5 | ||
|
|
1d6e480402 | ||
|
|
54706302df | ||
|
|
4a59cf4c3d | ||
|
|
8cb8f9eabf | ||
|
|
58a36e6b67 | ||
|
|
064d282f7b | ||
|
|
e83ec42bbc | ||
|
|
409720d854 | ||
|
|
1d8bb8c800 | ||
|
|
ad3b9b2f84 | ||
|
|
b8ecbd507e | ||
|
|
45453bbcd8 | ||
|
|
44dfab2ace | ||
|
|
4ab5cdf841 | ||
|
|
b384cb4043 | ||
|
|
2a323f2256 | ||
|
|
8cfba1015f | ||
|
|
bd6ff5d855 |
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>7.11.1</version>
|
||||
<version>7.12.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>avaje-ebeanorm</name>
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:https://github.com/ebean-orm/avaje-ebeanorm.git</developerConnection>
|
||||
<tag>avaje-ebeanorm-7.11.1</tag>
|
||||
<tag>avaje-ebeanorm-7.12.1</tag>
|
||||
</scm>
|
||||
|
||||
<dependencies>
|
||||
@@ -78,6 +78,12 @@
|
||||
<version>2.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-dbmigration</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
@@ -162,7 +168,7 @@
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-agent</artifactId>
|
||||
<version>4.9.2</version>
|
||||
<version>4.10.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -237,7 +243,7 @@
|
||||
<plugin>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
|
||||
<version>4.9.2</version>
|
||||
<version>4.10.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
|
||||
@@ -639,58 +639,6 @@ public final class Ebean {
|
||||
return serverMgr.getDefaultServer().saveAll(beans);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associations (from the intersection table) of a ManyToMany given
|
||||
* the owner bean and the propertyName of the ManyToMany collection.
|
||||
* <p>
|
||||
* Typically these deletions occur automatically when persisting a ManyToMany
|
||||
* collection and this provides a way to invoke those deletions directly.
|
||||
* </p>
|
||||
*
|
||||
* @return the number of associations deleted (from the intersection table).
|
||||
*/
|
||||
public static int deleteManyToManyAssociations(Object ownerBean, String propertyName) {
|
||||
return serverMgr.getDefaultServer().deleteManyToManyAssociations(ownerBean, propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the associations of a ManyToMany given the owner bean and the
|
||||
* propertyName of the ManyToMany collection.
|
||||
* <p>
|
||||
* Typically the saving of these associations (inserting into the intersection
|
||||
* table) occurs automatically when persisting a ManyToMany. This provides a
|
||||
* way to invoke those insertions directly.
|
||||
* </p>
|
||||
* <p>
|
||||
* You can use this when the collection is new and in this case all the
|
||||
* entries in the collection are treated as additions are result in inserts
|
||||
* into the intersection table.
|
||||
* </p>
|
||||
*/
|
||||
public static void saveManyToManyAssociations(Object ownerBean, String propertyName) {
|
||||
serverMgr.getDefaultServer().saveManyToManyAssociations(ownerBean, propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the associated collection or bean given the property name.
|
||||
* <p>
|
||||
* This is similar to performing a save cascade on a specific property
|
||||
* manually/programmatically.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that you can turn on/off cascading for a transaction via
|
||||
* {@link Transaction#setPersistCascade(boolean)}
|
||||
* </p>
|
||||
*
|
||||
* @param ownerBean
|
||||
* the bean instance holding the property we want to save
|
||||
* @param propertyName
|
||||
* the property we want to save
|
||||
*/
|
||||
public static void saveAssociation(Object ownerBean, String propertyName) {
|
||||
serverMgr.getDefaultServer().saveAssociation(ownerBean, propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the bean.
|
||||
* <p>
|
||||
@@ -932,19 +880,6 @@ public final class Ebean {
|
||||
return serverMgr.getDefaultServer().createSqlQuery(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a named sql query.
|
||||
* <p>
|
||||
* The query statement will be defined in a deployment orm xml file.
|
||||
* </p>
|
||||
*
|
||||
* @param namedQuery
|
||||
* the name of the query
|
||||
*/
|
||||
public static SqlQuery createNamedSqlQuery(String namedQuery) {
|
||||
return serverMgr.getDefaultServer().createNamedSqlQuery(namedQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a sql update for executing native dml statements.
|
||||
* <p>
|
||||
@@ -954,10 +889,6 @@ public final class Ebean {
|
||||
* <p>
|
||||
* See {@link SqlUpdate} for example usage.
|
||||
* </p>
|
||||
* <p>
|
||||
* Where possible it would be expected practice to put the statement in a orm
|
||||
* xml file (named update) and use {@link #createNamedSqlUpdate(String)} .
|
||||
* </p>
|
||||
*/
|
||||
public static SqlUpdate createSqlUpdate(String sql) {
|
||||
return serverMgr.getDefaultServer().createSqlUpdate(sql);
|
||||
@@ -972,144 +903,6 @@ public final class Ebean {
|
||||
return serverMgr.getDefaultServer().createCallableSql(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a named sql update.
|
||||
* <p>
|
||||
* The statement (an Insert Update or Delete statement) will be defined in a
|
||||
* deployment orm xml file.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Use a namedQuery
|
||||
* UpdateSql update = Ebean.createNamedSqlUpdate("update.topic.count");
|
||||
*
|
||||
* update.setParameter("count", 1);
|
||||
* update.setParameter("topicId", 50);
|
||||
*
|
||||
* int modifiedCount = update.execute();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public static SqlUpdate createNamedSqlUpdate(String namedQuery) {
|
||||
return serverMgr.getDefaultServer().createNamedSqlUpdate(namedQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a named Query that will have defined fetch paths, predicates etc.
|
||||
* <p>
|
||||
* The query is created from a statement that will be defined in a deployment
|
||||
* orm xml file or NamedQuery annotations. The query will typically already
|
||||
* define fetch paths, predicates, order by clauses etc so often you will just
|
||||
* need to bind required parameters and then execute the query.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // example
|
||||
* Query<Order> query = Ebean.createNamedQuery(Order.class, "new.for.customer");
|
||||
* query.setParameter("customerId", 23);
|
||||
* List<Order> newOrders = query.findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param beanType
|
||||
* the class of entity to be fetched
|
||||
* @param namedQuery
|
||||
* the name of the query
|
||||
*/
|
||||
public static <T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery) {
|
||||
|
||||
return serverMgr.getDefaultServer().createNamedQuery(beanType, namedQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query using the query language.
|
||||
* <p>
|
||||
* Note that you are allowed to add additional clauses using where() as well
|
||||
* as use fetch() and setOrderBy() after the query has been created.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that this method signature used to map to named queries and that has
|
||||
* moved to {@link #createNamedQuery(Class, String)}.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* String q = "find order fetch details where status = :st";
|
||||
*
|
||||
* List<Order> newOrders = Ebean.>findOrder.class, q)
|
||||
* .setParameter("st", Order.Status.NEW)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param query
|
||||
* the object query
|
||||
*/
|
||||
public static <T> Query<T> createQuery(Class<T> beanType, String query) {
|
||||
return serverMgr.getDefaultServer().createQuery(beanType, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a named orm update. The update statement is specified via the
|
||||
* NamedUpdate annotation.
|
||||
* <p>
|
||||
* The orm update differs from the SqlUpdate in that it uses the bean name and
|
||||
* bean property names rather than table and column names.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that named update statements can be specified in raw sql (with column
|
||||
* and table names) or using bean name and bean property names. This can be
|
||||
* specified with the isSql flag.
|
||||
* </p>
|
||||
* <p>
|
||||
* Example named updates:
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
* package app.data;
|
||||
*
|
||||
* import ...
|
||||
*
|
||||
* @NamedUpdates(value = {
|
||||
* @NamedUpdate( name = "setTitle",
|
||||
* isSql = false,
|
||||
* notifyCache = false,
|
||||
* update = "update topic set title = :title, postCount = :postCount where id = :id"),
|
||||
* @NamedUpdate( name = "setPostCount",
|
||||
* notifyCache = false,
|
||||
* update = "update f_topic set post_count = :postCount where id = :id"),
|
||||
* @NamedUpdate( name = "incrementPostCount",
|
||||
* notifyCache = false,
|
||||
* isSql = false,
|
||||
* update = "update Topic set postCount = postCount + 1 where id = :id") })
|
||||
* @Entity
|
||||
* @Table(name = "f_topic")
|
||||
* public class Topic { ...
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Example using a named update:
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* Update<Topic> update = Ebean.createNamedUpdate(Topic.class, "setPostCount");
|
||||
* update.setParameter("postCount", 10);
|
||||
* update.setParameter("id", 3);
|
||||
*
|
||||
* int rows = update.execute();
|
||||
* System.out.println("rows updated: " + rows);
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public static <T> Update<T> createNamedUpdate(Class<T> beanType, String namedUpdate) {
|
||||
|
||||
return serverMgr.getDefaultServer().createNamedUpdate(beanType, namedUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a orm update where you will supply the insert/update or delete
|
||||
* statement (rather than using a named one that is already defined using the
|
||||
@@ -1226,6 +1019,28 @@ public final class Ebean {
|
||||
return serverMgr.getDefaultServer().find(beanType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Update query to perform a bulk update.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = Ebean.update(Customer.class)
|
||||
* .set("status", Customer.Status.ACTIVE)
|
||||
* .set("updtime", new Timestamp(System.currentTimeMillis()))
|
||||
* .where()
|
||||
* .gt("id", 1000)
|
||||
* .update();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param beanType The type of entity bean to update
|
||||
* @param <T> The type of entity bean
|
||||
* @return The update query to use
|
||||
*/
|
||||
public static <T> UpdateQuery<T> update(Class<T> beanType) {
|
||||
return serverMgr.getDefaultServer().update(beanType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a filter for sorting and filtering lists of entities locally without
|
||||
* going back to the database.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -135,16 +135,17 @@ public interface ExpressionList<T> {
|
||||
* optimal depending on the database platform.
|
||||
* </p>
|
||||
*
|
||||
* @return the number of beans/rows that were deleted.
|
||||
* @return the number of rows that were deleted.
|
||||
*/
|
||||
int delete();
|
||||
|
||||
/**
|
||||
* Execute the query iterating over the results.
|
||||
*
|
||||
* @see Query#findIterate()
|
||||
* Execute as a update query.
|
||||
*
|
||||
* @return the number of rows that were updated.
|
||||
* @see UpdateQuery
|
||||
*/
|
||||
QueryIterator<T> findIterate();
|
||||
int update();
|
||||
|
||||
/**
|
||||
* Execute the query process the beans one at a time.
|
||||
@@ -253,27 +254,6 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
FutureList<T> findFutureList();
|
||||
|
||||
/**
|
||||
* Return a PagedList for this query using pageIndex and pageSize.
|
||||
* <p>
|
||||
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
|
||||
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
|
||||
* {@link Query#findFutureRowCount()} to determine total row count, total page count etc.
|
||||
* </p>
|
||||
* <p>
|
||||
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
|
||||
* the query. This translates into SQL that uses limit offset, rownum or row_number
|
||||
* function to limit the result set.
|
||||
* </p>
|
||||
*
|
||||
* @param pageIndex
|
||||
* The zero based index of the page.
|
||||
* @param pageSize
|
||||
* The number of beans to return per page.
|
||||
* @return The PagedList
|
||||
*/
|
||||
PagedList<T> findPagedList(int pageIndex, int pageSize);
|
||||
|
||||
/**
|
||||
* Return a PagedList for this query using firstRow and maxRows.
|
||||
* <p>
|
||||
|
||||
@@ -674,16 +674,6 @@ public abstract class Model {
|
||||
return query().findMap(keyProperty, keyType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a PagedList of all entities of the given type (use where() to specify predicates as
|
||||
* needed).
|
||||
* <p>
|
||||
* Equivalent to {@link Query#findPagedList(int, int)}
|
||||
*/
|
||||
public PagedList<T> findPagedList(int pageIndex, int pageSize) {
|
||||
return query().findPagedList(pageIndex, pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a find row count query in a background thread.
|
||||
* <p>
|
||||
@@ -843,15 +833,6 @@ public abstract class Model {
|
||||
return query().setId(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new query using the OQL.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#createQuery(Class, String)}
|
||||
*/
|
||||
public Query<T> setQuery(String oql) {
|
||||
return db().createQuery(type, oql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new query based on the <code>RawSql</code>.
|
||||
* <p>
|
||||
|
||||
@@ -84,7 +84,7 @@ import java.util.concurrent.Future;
|
||||
* @param <T>
|
||||
* the entity bean type
|
||||
*
|
||||
* @see Query#findPagedList(int, int)
|
||||
* @see Query#findPagedList()
|
||||
*/
|
||||
public interface PagedList<T> {
|
||||
|
||||
@@ -181,15 +181,6 @@ public interface PagedList<T> {
|
||||
*/
|
||||
int getTotalPageCount();
|
||||
|
||||
/**
|
||||
* Return the index position of this page. Zero based.
|
||||
* <p>
|
||||
* Note that if firstRows/maxRows is used rather than pageIndex/pageSize then
|
||||
* this always returns 0.
|
||||
* </p>
|
||||
*/
|
||||
int getPageIndex();
|
||||
|
||||
/**
|
||||
* Return the page size used for this query. This is the same value as maxRows used by the query.
|
||||
*/
|
||||
|
||||
@@ -301,12 +301,6 @@ public interface Query<T> {
|
||||
*/
|
||||
Query<T> asDraft();
|
||||
|
||||
/**
|
||||
* Deprecated in favour of setIncludeSoftDeletes().
|
||||
*/
|
||||
@Deprecated
|
||||
Query<T> includeSoftDeletes();
|
||||
|
||||
/**
|
||||
* Cancel the query execution if supported by the underlying database and
|
||||
* driver.
|
||||
@@ -547,25 +541,6 @@ public interface Query<T> {
|
||||
*/
|
||||
List<Object> findIds();
|
||||
|
||||
/**
|
||||
* Execute the query iterating over the results.
|
||||
* <p>
|
||||
* Remember that with {@link QueryIterator} you must call
|
||||
* {@link QueryIterator#close()} when you have finished iterating the results
|
||||
* (typically in a finally block).
|
||||
* </p>
|
||||
* <p>
|
||||
* findEach() and findEachWhile() are preferred to findIterate() as they ensure
|
||||
* the jdbc statement and resultSet are closed at the end of the iteration.
|
||||
* </p>
|
||||
* <p>
|
||||
* This query will execute against the EbeanServer that was used to create it.
|
||||
* </p>
|
||||
*/
|
||||
QueryIterator<T> findIterate();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Execute the query processing the beans one at a time.
|
||||
* <p>
|
||||
@@ -788,6 +763,11 @@ public interface Query<T> {
|
||||
*/
|
||||
int delete();
|
||||
|
||||
/**
|
||||
* Execute the UpdateQuery returning the number of rows updated.
|
||||
*/
|
||||
int update();
|
||||
|
||||
/**
|
||||
* Return the count of entities this query should return.
|
||||
* <p>
|
||||
@@ -831,52 +811,6 @@ public interface Query<T> {
|
||||
*/
|
||||
FutureList<T> findFutureList();
|
||||
|
||||
/**
|
||||
* Return a PagedList for this query using pageIndex and pageSize.
|
||||
* <p>
|
||||
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
|
||||
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
|
||||
* {@link Query#findFutureRowCount()} to determine total row count, total page count etc.
|
||||
* </p>
|
||||
* <p>
|
||||
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
|
||||
* the query. This translates into SQL that uses limit offset, rownum or row_number function to
|
||||
* limit the result set.
|
||||
* </p>
|
||||
*
|
||||
* <h4>Example: typical use including total row count</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // We want to find the first 100 new orders
|
||||
* // ... 0 means first page
|
||||
* // ... page size is 100
|
||||
*
|
||||
* PagedList<Order> pagedList
|
||||
* = ebeanServer.find(Order.class)
|
||||
* .where().eq("status", Order.Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .findPagedList(0, 100);
|
||||
*
|
||||
* // Optional: initiate the loading of the total
|
||||
* // row count in a background thread
|
||||
* pagedList.loadRowCount();
|
||||
*
|
||||
* // fetch and return the list in the foreground thread
|
||||
* List<Order> orders = pagedList.getList();
|
||||
*
|
||||
* // get the total row count (from the future)
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param pageIndex
|
||||
* The zero based index of the page.
|
||||
* @param pageSize
|
||||
* The number of beans to return per page.
|
||||
* @return The PagedList
|
||||
*/
|
||||
PagedList<T> findPagedList(int pageIndex, int pageSize);
|
||||
|
||||
/**
|
||||
* Return a PagedList for this query using firstRow and maxRows.
|
||||
* <p>
|
||||
@@ -1304,13 +1238,19 @@ public interface Query<T> {
|
||||
Query<T> setMapKey(String mapKey);
|
||||
|
||||
/**
|
||||
* Set this to true to use the bean cache.
|
||||
* Set this to false to not use the bean cache.
|
||||
* <p>
|
||||
* If the query result is in cache then by default this same instance is
|
||||
* returned. In this sense it should be treated as a read only object graph.
|
||||
* By default "find by id" and "find by natural key" will use the bean cache
|
||||
* when bean caching is enabled. Setting this to false means that the query
|
||||
* will not use the bean cache and instead hit the database.
|
||||
* </p>
|
||||
* <p>
|
||||
* In the case of other queries (findList(), findEach() etc) then setting this to
|
||||
* false beans that the if lazy loading is invoked that lazy loading will not try
|
||||
* to use the bean cache.
|
||||
* </p>
|
||||
*/
|
||||
Query<T> setUseCache(boolean useBeanCache);
|
||||
Query<T> setUseCache(boolean useCache);
|
||||
|
||||
/**
|
||||
* Set this to true to use the query cache.
|
||||
|
||||
@@ -36,6 +36,8 @@ public final class TxScope {
|
||||
|
||||
int batchSize;
|
||||
|
||||
boolean skipGeneratedKeys;
|
||||
|
||||
boolean readOnly;
|
||||
|
||||
ArrayList<Class<? extends Throwable>> rollbackFor;
|
||||
@@ -188,6 +190,21 @@ public final class TxScope {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if the transaction should skip reading generated keys for inserts.
|
||||
*/
|
||||
public TxScope setSkipGeneratedKeys() {
|
||||
this.skipGeneratedKeys = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if getGeneratedKeys should be skipped for this transaction.
|
||||
*/
|
||||
public boolean isSkipGeneratedKeys() {
|
||||
return skipGeneratedKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the transaction should be treated as read only.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* An update query typically intended to perform a bulk update of many rows that match the query.
|
||||
* <p>
|
||||
* Also note that you can also just use a raw SQL update via {@link SqlUpdate} which is pretty light and simple.
|
||||
* This UpdateQuery is more for the cases where we want to build the where expression of the update using the
|
||||
* {@link ExpressionList} "Criteria API" that is used with a normal ORM query.
|
||||
* </p>
|
||||
*
|
||||
* <h4>Example: Simple update</h4>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* .set("status", Customer.Status.ACTIVE)
|
||||
* .set("updtime", new Timestamp(System.currentTimeMillis()))
|
||||
* .where()
|
||||
* .gt("id", 1000)
|
||||
* .update();
|
||||
*
|
||||
* }</pre>
|
||||
* <pre>{@code sql
|
||||
*
|
||||
* update o_customer set status=?, updtime=? where id > ?
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Note that if the where() clause contains a join then the SQL update changes to use a
|
||||
* <code> WHERE ID IN () </code> form.
|
||||
* </p>
|
||||
*
|
||||
* <h4>Example: Update with a JOIN</h4>
|
||||
* <p>
|
||||
* In this example the expression <code>.eq("billingAddress.country", nz)</code> requires a join
|
||||
* to the address table.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* .set("status", Customer.Status.ACTIVE)
|
||||
* .set("updtime", new Timestamp(System.currentTimeMillis()))
|
||||
* .where()
|
||||
* .eq("status", Customer.Status.NEW)
|
||||
* .eq("billingAddress.country", nz)
|
||||
* .gt("id", 1000)
|
||||
* .update();
|
||||
* }</pre>
|
||||
*
|
||||
* <pre>{@code sql
|
||||
*
|
||||
* update o_customer set status=?, updtime=?
|
||||
* where id in (
|
||||
* select t0.id c0
|
||||
* from o_customer t0
|
||||
* left outer join o_address t1 on t1.id = t0.billing_address_id
|
||||
* where t0.status = ?
|
||||
* and t1.country_code = ?
|
||||
* and t0.id > ? )
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T> The type of entity bean being updated
|
||||
*
|
||||
* @see SqlUpdate
|
||||
*/
|
||||
public interface UpdateQuery<T> {
|
||||
|
||||
/**
|
||||
* Set the value of a property.
|
||||
*
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* .set("status", Customer.Status.ACTIVE)
|
||||
* .set("updtime", new Timestamp(System.currentTimeMillis()))
|
||||
* .where()
|
||||
* .gt("id", 1000)
|
||||
* .update();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param property The bean property to be set
|
||||
* @param value The value to set the property to
|
||||
*/
|
||||
UpdateQuery<T> set(String property, Object value);
|
||||
|
||||
/**
|
||||
* Set the property to be null.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* .setNull("notes")
|
||||
* .where()
|
||||
* .gt("id", 1000)
|
||||
* .update();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param property The property to be set to null.
|
||||
*/
|
||||
UpdateQuery<T> setNull(String property);
|
||||
|
||||
/**
|
||||
*
|
||||
* Set using a property expression that does not need any bind values.
|
||||
* <p>
|
||||
* The property expression typically contains database functions.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* .setRaw("status = coalesce(status, 'A')")
|
||||
* .where()
|
||||
* .gt("id", 1000)
|
||||
* .update();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param propertyExpression A property expression
|
||||
*/
|
||||
UpdateQuery<T> setRaw(String propertyExpression);
|
||||
|
||||
/**
|
||||
* Set using a property expression that can contain <code>?</code> bind value placeholders.
|
||||
* <p>
|
||||
* For each <code>?</code> in the property expression there should be a matching bind value supplied.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* .setRaw("status = coalesce(status, ?)", Customer.Status.ACTIVE)
|
||||
* .where()
|
||||
* .gt("id", 1000)
|
||||
* .update();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param propertyExpression A raw property expression
|
||||
* @param values The values to bind with the property expression
|
||||
*/
|
||||
UpdateQuery<T> setRaw(String propertyExpression, Object... values);
|
||||
|
||||
/**
|
||||
* Return the query expression list to add predicates to.
|
||||
*/
|
||||
ExpressionList<T> where();
|
||||
|
||||
}
|
||||
@@ -28,6 +28,11 @@ public @interface Cache {
|
||||
* set this to false on a bean type that we want to use query caching but no
|
||||
* bean caching (and this is expected to be a rare case).
|
||||
* </p>
|
||||
* <p>
|
||||
* When bean caching is enabled by default "find by id" and "find by unique natural key"
|
||||
* queries will try to use the bean cache. We use {@link com.avaje.ebean.Query#setUseCache(boolean)}
|
||||
* with <code>false</code> for the case when we do NOT want to use the bean cache.
|
||||
* </p>
|
||||
*/
|
||||
boolean enableBeanCache() default true;
|
||||
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* An Update statement for a particular entity bean type.
|
||||
* <p>
|
||||
* The update can either be a sql insert,update or delete statement with tables
|
||||
* and columns etc or the equivalent statement but with table names and columns
|
||||
* expressed as bean types and bean properties.
|
||||
* </p>
|
||||
*/
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface NamedUpdate {
|
||||
|
||||
/**
|
||||
* The name of the update.
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* The insert, update or delete statement.
|
||||
*/
|
||||
String update();
|
||||
|
||||
/**
|
||||
* Set this to false if you do not want the cache to be notified. If true the
|
||||
* cache will invalidate appropriate objects from the cache (after a
|
||||
* successful transaction commit).
|
||||
*/
|
||||
boolean notifyCache() default true;
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Holds an array of named Update statements for a particular entity bean type.
|
||||
*/
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface NamedUpdates {
|
||||
|
||||
/**
|
||||
* An array of named updates.
|
||||
*/
|
||||
NamedUpdate[] value();
|
||||
|
||||
}
|
||||
@@ -90,6 +90,16 @@ public @interface Transactional {
|
||||
*/
|
||||
int batchSize() default 0;
|
||||
|
||||
/**
|
||||
* Set to false when we want to skip getting generatedKeys.
|
||||
* <p>
|
||||
* This is typically used in the case of large batch inserts where we get a
|
||||
* performance benefit from not calling getGeneratedKeys (as we are going to
|
||||
* insert a lot of rows and have no need for the Id values after the insert).
|
||||
* </p>
|
||||
*/
|
||||
boolean getGeneratedKeys() default true;
|
||||
|
||||
/**
|
||||
* The transaction isolation level this transaction should have.
|
||||
* <p>
|
||||
|
||||
@@ -54,10 +54,12 @@ public interface BeanCollection<E> extends Serializable {
|
||||
void reset(EntityBean ownerBean, String propertyName);
|
||||
|
||||
/**
|
||||
* Return true if the collection is empty and untouched. Used to detect if a
|
||||
* collection was 'cleared' deliberately or just un-initialised.
|
||||
* Return true if the collection is uninitialised or is empty without any held modifications.
|
||||
* <p>
|
||||
* Returning true means can safely skip cascade save for this bean collection.
|
||||
* </p>
|
||||
*/
|
||||
boolean isEmptyAndUntouched();
|
||||
boolean isSkipSave();
|
||||
|
||||
/**
|
||||
* Return the bean that owns this collection.
|
||||
@@ -92,11 +94,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
*/
|
||||
void setFilterMany(ExpressionList<?> filterMany);
|
||||
|
||||
/**
|
||||
* Set a listener to be notified when the BeanCollection is first touched.
|
||||
*/
|
||||
void setBeanCollectionTouched(BeanCollectionTouched notify);
|
||||
|
||||
/**
|
||||
* Return true if the collection has been registered with the batch loading context.
|
||||
*/
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.avaje.ebean.bean;
|
||||
|
||||
/**
|
||||
* Used to specify a listener to be notified when a BeanCollection is first
|
||||
* used.
|
||||
* <p>
|
||||
* To use this you can set a BeanCollectionTouched onto a BeanCollection before
|
||||
* it has been used. When the BeanCollection is first used by the client code
|
||||
* then the BeanCollectionTouched is notified. It can only be notified once.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public interface BeanCollectionTouched {
|
||||
|
||||
/**
|
||||
* Notify the listener that the bean collection has been used.
|
||||
*/
|
||||
void notifyTouched(BeanCollection<?> c);
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
package com.avaje.ebean.common;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.ExpressionList;
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.BeanCollectionTouched;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Base class for List Set and Map implementations of BeanCollection.
|
||||
*
|
||||
@@ -36,8 +34,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
|
||||
protected String ebeanServerName;
|
||||
|
||||
protected transient BeanCollectionTouched beanCollectionTouched;
|
||||
|
||||
/**
|
||||
* The owning bean (used for lazy fetch).
|
||||
*/
|
||||
@@ -55,12 +51,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
protected boolean modifyRemoveListening;
|
||||
protected boolean modifyListening;
|
||||
|
||||
/**
|
||||
* Flag used to tell if empty collections have been cleared etc or just
|
||||
* uninitialised.
|
||||
*/
|
||||
protected boolean touched;
|
||||
|
||||
/**
|
||||
* Constructor not non-lazy loading collection.
|
||||
*/
|
||||
@@ -109,25 +99,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
checkEmptyLazyLoad();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set touched. If setFlag is false then typically an isEmpty() call and still
|
||||
* considering that to be untouched.
|
||||
*/
|
||||
protected void touched(boolean setFlag) {
|
||||
if (setFlag) {
|
||||
touched = true;
|
||||
}
|
||||
if (beanCollectionTouched != null) {
|
||||
// only call this once
|
||||
beanCollectionTouched.notifyTouched(this);
|
||||
beanCollectionTouched = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setBeanCollectionTouched(BeanCollectionTouched notify) {
|
||||
this.beanCollectionTouched = notify;
|
||||
}
|
||||
|
||||
public boolean isRegisteredWithLoadContext() {
|
||||
return registeredWithLoadContext;
|
||||
}
|
||||
@@ -219,4 +190,11 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
return modifyHolder.getModifyRemovals();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are underlying additions or removals.
|
||||
*/
|
||||
public boolean holdsModifications() {
|
||||
return modifyHolder != null && modifyHolder.hasModifications();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,12 +52,11 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
this.ownerBean = ownerBean;
|
||||
this.propertyName = propertyName;
|
||||
this.list = null;
|
||||
this.touched = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmptyAndUntouched() {
|
||||
return !touched && (list == null || list.isEmpty());
|
||||
public boolean isSkipSave() {
|
||||
return list == null || (list.isEmpty() && !holdsModifications());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -109,24 +108,14 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
list = new ArrayList<E>();
|
||||
}
|
||||
}
|
||||
touched(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void initAsUntouched() {
|
||||
init(false);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
init(true);
|
||||
}
|
||||
|
||||
private void init(boolean setTouched) {
|
||||
synchronized (this) {
|
||||
if (list == null) {
|
||||
lazyLoadCollection(false);
|
||||
}
|
||||
touched(setTouched);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +285,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
initAsUntouched();
|
||||
init();
|
||||
return list.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -45,11 +45,10 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
this.ownerBean = ownerBean;
|
||||
this.propertyName = propertyName;
|
||||
this.map = null;
|
||||
this.touched = false;
|
||||
}
|
||||
|
||||
public boolean isEmptyAndUntouched() {
|
||||
return !touched && (map == null || map.isEmpty());
|
||||
public boolean isSkipSave() {
|
||||
return map == null || (map.isEmpty() && !holdsModifications());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -125,24 +124,14 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
map = new LinkedHashMap<K, E>();
|
||||
}
|
||||
}
|
||||
touched(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void initAsUntouched() {
|
||||
init(false);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
init(true);
|
||||
}
|
||||
|
||||
private void init(boolean setTouched) {
|
||||
synchronized (this) {
|
||||
if (map == null) {
|
||||
lazyLoadCollection(false);
|
||||
}
|
||||
touched(setTouched);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +238,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
initAsUntouched();
|
||||
init();
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -46,11 +46,10 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
this.ownerBean = ownerBean;
|
||||
this.propertyName = propertyName;
|
||||
this.set = null;
|
||||
this.touched = false;
|
||||
}
|
||||
|
||||
public boolean isEmptyAndUntouched() {
|
||||
return !touched && (set == null || set.isEmpty());
|
||||
public boolean isSkipSave() {
|
||||
return set == null || (set.isEmpty() && !holdsModifications());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -117,24 +116,14 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
set = new LinkedHashSet<E>();
|
||||
}
|
||||
}
|
||||
touched(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void initAsUntouched() {
|
||||
init(false);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
init(true);
|
||||
}
|
||||
|
||||
private void init(boolean setTouched) {
|
||||
synchronized (this) {
|
||||
if (set == null) {
|
||||
lazyLoadCollection(true);
|
||||
}
|
||||
touched(setTouched);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,7 +249,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
initAsUntouched();
|
||||
init();
|
||||
return set.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -71,4 +71,11 @@ class ModifyHolder<E> implements Serializable {
|
||||
Set<E> getModifyRemovals() {
|
||||
return modifyDeletions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there additions or removals.
|
||||
*/
|
||||
boolean hasModifications() {
|
||||
return !modifyDeletions.isEmpty() || !modifyAdditions.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.avaje.ebean.config;
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.config.dbplatform.DbPlatformName;
|
||||
import com.avaje.ebean.dbmigration.DbMigration;
|
||||
import org.avaje.dbmigration.MigrationConfig;
|
||||
import org.avaje.dbmigration.MigrationRunner;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -97,7 +99,7 @@ public class DbMigrationConfig {
|
||||
/**
|
||||
* DB user used to run the DB migration.
|
||||
*/
|
||||
protected String dbUser;
|
||||
protected String dbUsername;
|
||||
|
||||
/**
|
||||
* DB password used to run the DB migration.
|
||||
@@ -242,14 +244,6 @@ public class DbMigrationConfig {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the model, rollback and drop paths to be empty such that all the migration files are generated
|
||||
* into a single directory.
|
||||
*/
|
||||
public void singleDirectory() {
|
||||
this.modelPath = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the table name that holds the migration run details
|
||||
* (used by DB Migration runner only).
|
||||
@@ -323,22 +317,22 @@ public class DbMigrationConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DB user to use for running DB migrations.
|
||||
* Return the DB username to use for running DB migrations.
|
||||
*/
|
||||
public String getDbUser() {
|
||||
public String getDbUsername() {
|
||||
// environment properties take precedence
|
||||
String user = readEnvironment("ddl.migration.user");
|
||||
if (user != null) {
|
||||
return user;
|
||||
}
|
||||
return dbUser;
|
||||
return dbUsername;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DB user to use for running DB migrations.
|
||||
* Set the DB username to use for running DB migrations.
|
||||
*/
|
||||
public void setDbUser(String dbUser) {
|
||||
this.dbUser = dbUser;
|
||||
public void setDbUsername(String dbUsername) {
|
||||
this.dbUsername = dbUsername;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -365,11 +359,7 @@ public class DbMigrationConfig {
|
||||
public void loadSettings(PropertiesWrapper properties, String serverName) {
|
||||
|
||||
migrationPath = properties.get("migration.migrationPath", migrationPath);
|
||||
if (properties.getBoolean("migration.singleDirectory", false)) {
|
||||
singleDirectory();
|
||||
} else {
|
||||
modelPath = properties.get("migration.modelPath", modelPath);
|
||||
}
|
||||
modelPath = properties.get("migration.modelPath", modelPath);
|
||||
applyPrefix = properties.get("migration.applyPrefix", applyPrefix);
|
||||
applySuffix = properties.get("migration.applySuffix", applySuffix);
|
||||
modelSuffix = properties.get("migration.modelSuffix", modelSuffix);
|
||||
@@ -386,9 +376,9 @@ public class DbMigrationConfig {
|
||||
metaTable = properties.get("migration.metaTable", metaTable);
|
||||
runPlaceholders = properties.get("migration.placeholders", runPlaceholders);
|
||||
|
||||
String adminUser = properties.get("datasource."+serverName+".username", dbUser);
|
||||
String adminUser = properties.get("datasource."+serverName+".username", dbUsername);
|
||||
adminUser = properties.get("datasource."+serverName+".adminusername", adminUser);
|
||||
dbUser = properties.get("migration.dbuser", adminUser);
|
||||
dbUsername = properties.get("migration.dbusername", adminUser);
|
||||
|
||||
String adminPwd = properties.get("datasource."+serverName+".password", dbPassword);
|
||||
adminPwd = properties.get("datasource."+serverName+".adminpassword", adminPwd);
|
||||
@@ -494,4 +484,20 @@ public class DbMigrationConfig {
|
||||
return val == null || val.trim().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the MigrationRunner to run migrations if necessary.
|
||||
*/
|
||||
public MigrationRunner createRunner(ClassLoader classLoader) {
|
||||
|
||||
MigrationConfig runnerConfig = new MigrationConfig();
|
||||
runnerConfig.setMetaTable(metaTable);
|
||||
runnerConfig.setApplySuffix(applySuffix);
|
||||
runnerConfig.setMigrationPath(migrationPath);
|
||||
runnerConfig.setRunPlaceholderMap(runPlaceholderMap);
|
||||
runnerConfig.setRunPlaceholders(runPlaceholders);
|
||||
runnerConfig.setDbUsername(getDbUsername());
|
||||
runnerConfig.setDbPassword(getDbPassword());
|
||||
runnerConfig.setClassLoader(classLoader);
|
||||
return new MigrationRunner(runnerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
/**
|
||||
* Adds ANSI based OFFSET FETCH NEXT clauses to a SQL query.
|
||||
*/
|
||||
public class BasicSqlAnsiLimiter implements BasicSqlLimiter {
|
||||
|
||||
@Override
|
||||
public String limit(String dbSql, int firstRow, int maxRows) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(50 + dbSql.length());
|
||||
|
||||
sb.append(dbSql);
|
||||
if (firstRow > 0) {
|
||||
sb.append(" ").append("offset");
|
||||
sb.append(" ").append(firstRow).append(" rows");
|
||||
}
|
||||
if (maxRows > 0) {
|
||||
sb.append(" fetch next ").append(maxRows).append(" rows only");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
/**
|
||||
* Adds LIMIT OFFSET clauses to a SQL query.
|
||||
*/
|
||||
public class BasicSqlLimitOffset implements BasicSqlLimiter {
|
||||
|
||||
/**
|
||||
* LIMIT keyword.
|
||||
*/
|
||||
private static final String LIMIT = "limit";
|
||||
|
||||
/**
|
||||
* OFFSET keyword.
|
||||
*/
|
||||
private static final String OFFSET = "offset";
|
||||
|
||||
@Override
|
||||
public String limit(String dbSql, int firstRow, int maxRows) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(50 + dbSql.length());
|
||||
|
||||
sb.append(dbSql);
|
||||
|
||||
if (maxRows > 0) {
|
||||
sb.append(" ").append(LIMIT);
|
||||
sb.append(" ").append(maxRows);
|
||||
}
|
||||
if (firstRow > 0) {
|
||||
sb.append(" ").append(OFFSET).append(" ");
|
||||
sb.append(firstRow);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
/**
|
||||
* Simple SQL limiter for use with SqlQuery.
|
||||
*/
|
||||
public interface BasicSqlLimiter {
|
||||
|
||||
/**
|
||||
* Add basic offset/limit clause to SqlQuery query.
|
||||
*/
|
||||
String limit(String dbSql, int firstRow, int maxRows);
|
||||
}
|
||||
@@ -71,6 +71,11 @@ public class DatabasePlatform {
|
||||
*/
|
||||
protected SqlLimiter sqlLimiter = new LimitOffsetSqlLimiter();
|
||||
|
||||
/**
|
||||
* Limit/offset support for SqlQuery only.
|
||||
*/
|
||||
protected BasicSqlLimiter basicSqlLimiter = new BasicSqlLimitOffset();
|
||||
|
||||
/**
|
||||
* Mapping of JDBC to Database types.
|
||||
*/
|
||||
@@ -454,6 +459,13 @@ public class DatabasePlatform {
|
||||
return sqlLimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the BasicSqlLimiter for limit/offset of SqlQuery queries.
|
||||
*/
|
||||
public BasicSqlLimiter getBasicSqlLimiter() {
|
||||
return basicSqlLimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DB TRUE literal (from the registered boolean ScalarType)
|
||||
*/
|
||||
|
||||
@@ -18,6 +18,7 @@ public class OraclePlatform extends DatabasePlatform {
|
||||
this.maxConstraintNameLength = 30;
|
||||
this.dbEncrypt = new OracleDbEncrypt();
|
||||
this.sqlLimiter = new RownumSqlLimiter();
|
||||
this.basicSqlLimiter = new BasicSqlAnsiLimiter();
|
||||
this.platformDdl = new Oracle10Ddl(this);
|
||||
this.historySupport = new OracleDbHistorySupport();
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
/**
|
||||
* Use top xx and start at xx to limit sql results. Based on
|
||||
* MsSqlServer2005SqlLimiter and LimitOffsetSqlLimiter
|
||||
* Use top xx and start at xx to limit sql results.
|
||||
*/
|
||||
public class SqlAnywhereLimiter implements SqlLimiter {
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.dbmigration.model.CurrentModel;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
|
||||
import org.avaje.dbmigration.ddl.DdlRunner;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.File;
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
package com.avaje.ebean.dbmigration;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Parses string content into separate SQL/DDL statements.
|
||||
*/
|
||||
public class DdlParser {
|
||||
|
||||
/**
|
||||
* Break up the sql in reader into a list of statements using the semi-colon and $$ delimiters;
|
||||
*/
|
||||
public List<String> parse(StringReader reader) {
|
||||
|
||||
try {
|
||||
BufferedReader br = new BufferedReader(reader);
|
||||
StatementsSeparator statements = new StatementsSeparator();
|
||||
|
||||
String s;
|
||||
while ((s = br.readLine()) != null) {
|
||||
s = s.trim();
|
||||
statements.nextLine(s);
|
||||
}
|
||||
|
||||
return statements.statements;
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Local utility used to detect the end of statements / separate statements.
|
||||
* This is often just the semicolon character but for trigger/procedures this
|
||||
* detects the $$ demarcation used in the history DDL generation for MySql and
|
||||
* Postgres.
|
||||
*/
|
||||
static class StatementsSeparator {
|
||||
|
||||
ArrayList<String> statements = new ArrayList<String>();
|
||||
|
||||
boolean trimDelimiter;
|
||||
|
||||
boolean inDbProcedure;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
void lineContainsDollars(String line) {
|
||||
if (inDbProcedure) {
|
||||
if (trimDelimiter) {
|
||||
line = line.replace("$$","");
|
||||
}
|
||||
endOfStatement(line);
|
||||
} else {
|
||||
// MySql style delimiter needs to be trimmed/removed
|
||||
trimDelimiter = line.equals("delimiter $$");
|
||||
if (!trimDelimiter) {
|
||||
sb.append(line).append(" ");
|
||||
}
|
||||
}
|
||||
inDbProcedure = !inDbProcedure;
|
||||
}
|
||||
|
||||
void endOfStatement(String line) {
|
||||
// end of Db procedure
|
||||
sb.append(line);
|
||||
statements.add(sb.toString().trim());
|
||||
sb = new StringBuilder();
|
||||
}
|
||||
|
||||
void nextLine(String line) {
|
||||
|
||||
if (line.contains("$$")) {
|
||||
lineContainsDollars(line);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sb.length() == 0 && (line.isEmpty() || line.startsWith("--"))) {
|
||||
// ignore leading empty lines and sql comments
|
||||
return;
|
||||
}
|
||||
|
||||
if (inDbProcedure) {
|
||||
sb.append(line).append(" ");
|
||||
return;
|
||||
}
|
||||
|
||||
int semiPos = line.indexOf(';');
|
||||
if (semiPos == -1) {
|
||||
sb.append(line).append(" ");
|
||||
|
||||
} else if (semiPos == line.length() - 1) {
|
||||
// semicolon at end of line
|
||||
endOfStatement(line);
|
||||
|
||||
} else {
|
||||
// semicolon in middle of line
|
||||
String preSemi = line.substring(0, semiPos);
|
||||
endOfStatement(preSemi);
|
||||
sb.append(line.substring(semiPos + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
package com.avaje.ebean.dbmigration;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Runs DDL scripts.
|
||||
*/
|
||||
public class DdlRunner {
|
||||
|
||||
protected static final Logger logger = LoggerFactory.getLogger(DdlRunner.class);
|
||||
|
||||
private DdlParser ddlParser = new DdlParser();
|
||||
|
||||
private final String scriptName;
|
||||
|
||||
private final boolean expectErrors;
|
||||
|
||||
/**
|
||||
* Construct with a script name (for logging) and flag indicating if errors are expected.
|
||||
*/
|
||||
public DdlRunner(boolean expectErrors, String scriptName) {
|
||||
this.expectErrors = expectErrors;
|
||||
this.scriptName = scriptName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the content into sql statements and execute them in a transaction.
|
||||
*/
|
||||
public int runAll(String content, Connection connection) throws SQLException {
|
||||
|
||||
List<String> statements = ddlParser.parse(new StringReader(content));
|
||||
return runStatements(statements, connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all the statements in a single transaction.
|
||||
*/
|
||||
private int runStatements(List<String> statements, Connection connection) throws SQLException {
|
||||
|
||||
List<String> noDuplicates = new ArrayList<String>();
|
||||
|
||||
for (String statement : statements) {
|
||||
if (!noDuplicates.contains(statement)) {
|
||||
noDuplicates.add(statement);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Executing {} - {} statements", scriptName, noDuplicates.size());
|
||||
|
||||
for (int i = 0; i < noDuplicates.size(); i++) {
|
||||
String xOfy = (i + 1) + " of " + noDuplicates.size();
|
||||
runStatement(expectErrors, xOfy, noDuplicates.get(i), connection);
|
||||
}
|
||||
|
||||
return noDuplicates.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the statement.
|
||||
*/
|
||||
private void runStatement(boolean expectErrors, String oneOf, String stmt, Connection c) throws SQLException {
|
||||
|
||||
PreparedStatement pstmt = null;
|
||||
try {
|
||||
|
||||
// trim and remove trailing ; or /
|
||||
stmt = stmt.trim();
|
||||
if (stmt.endsWith(";")) {
|
||||
stmt = stmt.substring(0, stmt.length() - 1);
|
||||
} else if (stmt.endsWith("/")) {
|
||||
stmt = stmt.substring(0, stmt.length() - 1);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("executing " + oneOf + " " + getSummary(stmt));
|
||||
}
|
||||
|
||||
pstmt = c.prepareStatement(stmt);
|
||||
pstmt.execute();
|
||||
|
||||
} catch (SQLException e) {
|
||||
if (expectErrors) {
|
||||
logger.debug(" ... ignoring error executing " + getSummary(stmt) + " error: " + e.getMessage());
|
||||
} else {
|
||||
String msg = "Error executing stmt[" + stmt + "] error[" + e.getMessage() + "]";
|
||||
throw new SQLException(msg, e);
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (pstmt != null) {
|
||||
try {
|
||||
pstmt.close();
|
||||
} catch (SQLException e) {
|
||||
logger.error("Error closing pstmt", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getSummary(String s) {
|
||||
if (s.length() > 80) {
|
||||
return s.substring(0, 80).trim() + "...";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
package com.avaje.ebean.dbmigration;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.config.DbMigrationConfig;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.dbmigration.runner.LocalMigrationResource;
|
||||
import com.avaje.ebean.dbmigration.runner.LocalMigrationResources;
|
||||
import com.avaje.ebean.dbmigration.runner.MigrationTable;
|
||||
import com.avaje.ebeaninternal.util.JdbcClose;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Runs the DB migration typically on application start.
|
||||
*/
|
||||
public class MigrationRunner {
|
||||
|
||||
public static final Logger logger = LoggerFactory.getLogger("org.avaje.ebean.DbMigration");
|
||||
|
||||
private final EbeanServer server;
|
||||
|
||||
private final ServerConfig config;
|
||||
|
||||
private final DbMigrationConfig migrationConfig;
|
||||
|
||||
public MigrationRunner(EbeanServer server, DbMigrationConfig migrationConfig) {
|
||||
this.server = server;
|
||||
this.config = server.getPluginApi().getServerConfig();
|
||||
this.migrationConfig = migrationConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the migrations if there are any that need running.
|
||||
*/
|
||||
public void run() {
|
||||
|
||||
LocalMigrationResources resources = new LocalMigrationResources(config, migrationConfig);
|
||||
if (!resources.readResources()) {
|
||||
logger.debug("no migrations to check");
|
||||
return;
|
||||
}
|
||||
|
||||
String migrationUser = migrationConfig.getDbUser();
|
||||
String migrationPwd = migrationConfig.getDbPassword();
|
||||
if (migrationUser == null) {
|
||||
throw new IllegalStateException("No DB migration user specified (to run the db migration) ?");
|
||||
}
|
||||
|
||||
DataSource dataSource = server.getPluginApi().getDataSource();
|
||||
if (dataSource == null) {
|
||||
throw new IllegalStateException("No dataSource when trying to run migration? "
|
||||
+"Maybe trying to generate DBMigration when ebean.migration.run=true is set? "
|
||||
+"Perhaps need to set ebean.migration.run=false in test-ebean.properties?");
|
||||
}
|
||||
|
||||
Connection connection;
|
||||
try {
|
||||
connection = dataSource.getConnection(migrationUser, migrationPwd);
|
||||
} catch (SQLException e) {
|
||||
throw new IllegalArgumentException("Error trying to connect to database using DB Migration user [" + migrationUser + "]", e);
|
||||
}
|
||||
|
||||
try {
|
||||
logger.debug("using db user [{}] to run migrations ...");
|
||||
connection.setAutoCommit(false);
|
||||
runMigrations(resources, connection);
|
||||
|
||||
connection.commit();
|
||||
|
||||
} catch (Exception e) {
|
||||
JdbcClose.rollback(connection);
|
||||
throw new RuntimeException(e);
|
||||
|
||||
} finally {
|
||||
JdbcClose.close(connection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all the migrations as needed.
|
||||
*/
|
||||
private void runMigrations(LocalMigrationResources resources, Connection connection) throws SQLException, IOException {
|
||||
|
||||
MigrationTable table = new MigrationTable(server, migrationConfig, connection);
|
||||
table.createIfNeeded();
|
||||
|
||||
// get the migrations in version order
|
||||
List<LocalMigrationResource> localVersions = resources.getVersions();
|
||||
|
||||
logger.info("local migrations:{} existing migrations:{}", localVersions.size(), table.size());
|
||||
|
||||
LocalMigrationResource priorVersion = null;
|
||||
|
||||
// run migrations in order
|
||||
for (int i = 0; i < localVersions.size(); i++) {
|
||||
LocalMigrationResource localVersion = localVersions.get(i);
|
||||
if (!table.shouldRun(localVersion, priorVersion)) {
|
||||
break;
|
||||
}
|
||||
priorVersion = localVersion;
|
||||
connection.commit();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import com.avaje.ebean.dbmigration.ddlgeneration.platform.BaseTableDdl;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
|
||||
import com.avaje.ebean.dbmigration.migration.AddColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.AddHistoryTable;
|
||||
import com.avaje.ebean.dbmigration.migration.AddTableComment;
|
||||
import com.avaje.ebean.dbmigration.migration.AlterColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.ChangeSet;
|
||||
import com.avaje.ebean.dbmigration.migration.CreateIndex;
|
||||
@@ -37,6 +38,8 @@ public class BaseDdlHandler implements DdlHandler {
|
||||
generate(writer, (CreateTable) change);
|
||||
} else if (change instanceof DropTable) {
|
||||
generate(writer, (DropTable) change);
|
||||
} else if (change instanceof AddTableComment) {
|
||||
generate(writer, (AddTableComment) change);
|
||||
} else if (change instanceof CreateIndex) {
|
||||
generate(writer, (CreateIndex) change);
|
||||
} else if (change instanceof DropIndex) {
|
||||
@@ -70,6 +73,11 @@ public class BaseDdlHandler implements DdlHandler {
|
||||
tableDdl.generate(writer, dropTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(DdlWrite writer, AddTableComment addTableComment) throws IOException {
|
||||
tableDdl.generate(writer, addTableComment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(DdlWrite writer, AddColumn addColumn) throws IOException {
|
||||
tableDdl.generate(writer, addColumn);
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.avaje.ebean.dbmigration.ddlgeneration;
|
||||
|
||||
import com.avaje.ebean.dbmigration.migration.AddColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.AddHistoryTable;
|
||||
import com.avaje.ebean.dbmigration.migration.AddTableComment;
|
||||
import com.avaje.ebean.dbmigration.migration.AlterColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.ChangeSet;
|
||||
import com.avaje.ebean.dbmigration.migration.CreateIndex;
|
||||
@@ -14,6 +15,7 @@ import com.avaje.ebean.dbmigration.migration.DropTable;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* DDL generation interface.
|
||||
*/
|
||||
public interface DdlHandler {
|
||||
|
||||
@@ -23,6 +25,8 @@ public interface DdlHandler {
|
||||
|
||||
void generate(DdlWrite writer, DropTable dropTable) throws IOException;
|
||||
|
||||
void generate(DdlWrite writer, AddTableComment addTableComment) throws IOException;
|
||||
|
||||
void generate(DdlWrite writer, AddColumn addColumn) throws IOException;
|
||||
|
||||
void generate(DdlWrite writer, DropColumn dropColumn) throws IOException;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.avaje.ebean.dbmigration.ddlgeneration;
|
||||
|
||||
import com.avaje.ebean.dbmigration.migration.AddColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.AddHistoryTable;
|
||||
import com.avaje.ebean.dbmigration.migration.AddTableComment;
|
||||
import com.avaje.ebean.dbmigration.migration.AlterColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.CreateIndex;
|
||||
import com.avaje.ebean.dbmigration.migration.CreateTable;
|
||||
@@ -42,6 +43,11 @@ public interface TableDdl {
|
||||
*/
|
||||
void generate(DdlWrite writer, DropColumn dropColumn) throws IOException;
|
||||
|
||||
/**
|
||||
* Write the AddTableComment change.
|
||||
*/
|
||||
void generate(DdlWrite writer, AddTableComment addTableComment) throws IOException;
|
||||
|
||||
/**
|
||||
* Write the AddHistoryTable change.
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.avaje.ebean.dbmigration.ddlgeneration.TableDdl;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.platform.util.IndexSet;
|
||||
import com.avaje.ebean.dbmigration.migration.AddColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.AddHistoryTable;
|
||||
import com.avaje.ebean.dbmigration.migration.AddTableComment;
|
||||
import com.avaje.ebean.dbmigration.migration.AlterColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.Column;
|
||||
import com.avaje.ebean.dbmigration.migration.CreateIndex;
|
||||
@@ -554,6 +555,13 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(DdlWrite writer, AddTableComment addTableComment) throws IOException {
|
||||
if (hasValue(addTableComment.getComment())) {
|
||||
platformDdl.addTableComment(writer.apply(), addTableComment.getName(), addTableComment.getComment());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add add column DDL.
|
||||
*/
|
||||
@@ -634,7 +642,16 @@ public class BaseTableDdl implements TableDdl {
|
||||
if (hasValue(alterColumn.getUniqueOneToOne())) {
|
||||
alterColumnAddUniqueOneToOneConstraint(writer, alterColumn);
|
||||
}
|
||||
if (hasValue(alterColumn.getComment())) {
|
||||
alterColumnComment(writer, alterColumn);
|
||||
}
|
||||
|
||||
boolean alterCheckConstraint = hasValue(alterColumn.getCheckConstraint());
|
||||
|
||||
if (alterCheckConstraint) {
|
||||
// drop constraint before altering type etc
|
||||
dropCheckConstraint(writer, alterColumn);
|
||||
}
|
||||
boolean alterBaseAttributes = false;
|
||||
if (hasValue(alterColumn.getType())) {
|
||||
alterColumnType(writer, alterColumn);
|
||||
@@ -648,10 +665,17 @@ public class BaseTableDdl implements TableDdl {
|
||||
alterColumnNotnull(writer, alterColumn);
|
||||
alterBaseAttributes = true;
|
||||
}
|
||||
|
||||
if (alterBaseAttributes) {
|
||||
alterColumnBaseAttributes(writer, alterColumn);
|
||||
}
|
||||
if (alterCheckConstraint) {
|
||||
// add constraint last (after potential type change)
|
||||
addCheckConstraint(writer, alterColumn);
|
||||
}
|
||||
}
|
||||
|
||||
private void alterColumnComment(DdlWrite writer, AlterColumn alterColumn) throws IOException {
|
||||
platformDdl.addColumnComment(writer.apply(), alterColumn.getTableName(), alterColumn.getColumnName(), alterColumn.getComment());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -705,6 +729,22 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
}
|
||||
|
||||
protected void dropCheckConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterTableDropConstraint(alter.getTableName(), alter.getCheckConstraintName());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
}
|
||||
|
||||
protected void addCheckConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterTableAddCheckConstraint(alter.getTableName(), alter.getCheckConstraintName(), alter.getCheckConstraint());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
}
|
||||
|
||||
protected void alterColumnNotnull(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterColumnNotnull(alter.getTableName(), alter.getColumnName(), alter.isNotnull());
|
||||
|
||||
+2
-2
@@ -131,11 +131,11 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
}
|
||||
|
||||
protected String historyTableName(String baseTableName) {
|
||||
return normalise(baseTableName) + historySuffix;
|
||||
return baseTableName + historySuffix;
|
||||
}
|
||||
|
||||
protected String procedureName(String baseTableName) {
|
||||
return normalise(baseTableName) + "_history_version";
|
||||
return baseTableName + "_history_version";
|
||||
}
|
||||
|
||||
protected String triggerName(String baseTableName) {
|
||||
|
||||
@@ -38,8 +38,13 @@ public class MySqlDdl extends PlatformDdl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String alterColumnType(String tableName, String columnName, String type) {
|
||||
public String alterTableDropConstraint(String tableName, String constraintName) {
|
||||
// drop constraint not supported
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String alterColumnType(String tableName, String columnName, String type) {
|
||||
// can't alter itself - done in alterColumnBaseAttributes()
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -67,8 +67,12 @@ public class PlatformDdl {
|
||||
|
||||
protected String alterColumn = "alter column";
|
||||
|
||||
protected String dropConstraint = "drop constraint";
|
||||
|
||||
protected String dropUniqueConstraint = "drop constraint";
|
||||
|
||||
protected String addConstraint = "add constraint";
|
||||
|
||||
protected String columnSetType = "";
|
||||
|
||||
protected String columnSetDefault = "set default";
|
||||
@@ -327,12 +331,19 @@ public class PlatformDdl {
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop a unique constraint from the table.
|
||||
* Drop a unique constraint from the table (Sometimes this is an index).
|
||||
*/
|
||||
public String alterTableDropUniqueConstraint(String tableName, String uniqueConstraintName) {
|
||||
return "alter table " + tableName + " " + dropUniqueConstraint + " " + uniqueConstraintName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop a unique constraint from the table.
|
||||
*/
|
||||
public String alterTableDropConstraint(String tableName, String constraintName) {
|
||||
return "alter table " + tableName + " " + dropConstraint + " " + constraintName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a unique constraint to the table.
|
||||
* <p>
|
||||
@@ -377,6 +388,14 @@ public class PlatformDdl {
|
||||
return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter table adding the check constraint.
|
||||
*/
|
||||
public String alterTableAddCheckConstraint(String tableName, String checkConstraintName, String checkConstraint) {
|
||||
|
||||
return "alter table " + tableName + " " + addConstraint + " " + checkConstraintName + " " + checkConstraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the default value is the special DROP DEFAULT value.
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,7 @@ public class PostgresDdl extends PlatformDdl {
|
||||
this.dropTableCascade = " cascade";
|
||||
this.columnSetType = "type ";
|
||||
this.alterTableIfExists = "if exists ";
|
||||
this.columnSetNull = "drop not null";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
|
||||
package com.avaje.ebean.dbmigration.migration;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||
* <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="comment" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* </restriction>
|
||||
* </complexContent>
|
||||
* </complexType>
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(name = "")
|
||||
@XmlRootElement(name = "addTableComment")
|
||||
public class AddTableComment {
|
||||
|
||||
@XmlAttribute(name = "name", required = true)
|
||||
protected String name;
|
||||
@XmlAttribute(name = "comment", required = true)
|
||||
protected String comment;
|
||||
|
||||
/**
|
||||
* Gets the value of the name property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the name property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public void setName(String value) {
|
||||
this.name = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the comment property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the comment property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public void setComment(String value) {
|
||||
this.comment = value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import javax.xml.bind.annotation.XmlType;
|
||||
* <attribute name="currentDefaultValue" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="notnull" type="{http://www.w3.org/2001/XMLSchema}boolean" />
|
||||
* <attribute name="currentNotnull" type="{http://www.w3.org/2001/XMLSchema}boolean" />
|
||||
* <attribute name="comment" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="historyExclude" type="{http://www.w3.org/2001/XMLSchema}boolean" />
|
||||
* <attribute name="checkConstraint" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="checkConstraintName" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
@@ -68,6 +69,8 @@ public class AlterColumn {
|
||||
protected Boolean notnull;
|
||||
@XmlAttribute(name = "currentNotnull")
|
||||
protected Boolean currentNotnull;
|
||||
@XmlAttribute(name = "comment")
|
||||
protected String comment;
|
||||
@XmlAttribute(name = "historyExclude")
|
||||
protected Boolean historyExclude;
|
||||
@XmlAttribute(name = "checkConstraint")
|
||||
@@ -309,6 +312,30 @@ public class AlterColumn {
|
||||
this.currentNotnull = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the comment property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the comment property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public void setComment(String value) {
|
||||
this.comment = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the historyExclude property.
|
||||
*
|
||||
|
||||
@@ -52,6 +52,7 @@ public class ChangeSet {
|
||||
@XmlElement(name = "createTable", type = CreateTable.class),
|
||||
@XmlElement(name = "dropTable", type = DropTable.class),
|
||||
@XmlElement(name = "renameTable", type = RenameTable.class),
|
||||
@XmlElement(name = "addTableComment", type = AddTableComment.class),
|
||||
@XmlElement(name = "addHistoryTable", type = AddHistoryTable.class),
|
||||
@XmlElement(name = "dropHistoryTable", type = DropHistoryTable.class),
|
||||
@XmlElement(name = "addColumn", type = AddColumn.class),
|
||||
@@ -98,6 +99,7 @@ public class ChangeSet {
|
||||
* {@link CreateTable }
|
||||
* {@link DropTable }
|
||||
* {@link RenameTable }
|
||||
* {@link AddTableComment }
|
||||
* {@link AddHistoryTable }
|
||||
* {@link DropHistoryTable }
|
||||
* {@link AddColumn }
|
||||
|
||||
@@ -101,6 +101,14 @@ public class ObjectFactory {
|
||||
return new DefaultTablespace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link AddTableComment }
|
||||
*
|
||||
*/
|
||||
public AddTableComment createAddTableComment() {
|
||||
return new AddTableComment();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link RenameTable }
|
||||
*
|
||||
|
||||
@@ -260,7 +260,7 @@ public class MColumn {
|
||||
return c;
|
||||
}
|
||||
|
||||
private boolean different(String val1, String val2) {
|
||||
protected static boolean different(String val1, String val2) {
|
||||
return (val1 == null) ? val2 != null : !val1.equals(val2);
|
||||
}
|
||||
|
||||
@@ -268,6 +268,10 @@ public class MColumn {
|
||||
return val != null && !val.isEmpty();
|
||||
}
|
||||
|
||||
private boolean hasValue(Boolean val) {
|
||||
return val != null;
|
||||
}
|
||||
|
||||
private AlterColumn getAlterColumn(String tableName, boolean tableWithHistory) {
|
||||
if (alterColumn == null) {
|
||||
alterColumn = new AlterColumn();
|
||||
@@ -314,10 +318,13 @@ public class MColumn {
|
||||
alter.setDefaultValue(newColumn.defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (different(comment, newColumn.comment)) {
|
||||
AlterColumn alter = getAlterColumn(tableName, tableWithHistory);
|
||||
alter.setComment(newColumn.comment);
|
||||
}
|
||||
if (different(checkConstraint, newColumn.checkConstraint)) {
|
||||
AlterColumn alter = getAlterColumn(tableName, tableWithHistory);
|
||||
if (hasValue(checkConstraint)) {
|
||||
if (hasValue(checkConstraint) && !hasValue(newColumn.checkConstraint)) {
|
||||
alter.setDropCheckConstraint(checkConstraintName);
|
||||
}
|
||||
if (hasValue(newColumn.checkConstraint)) {
|
||||
@@ -393,6 +400,9 @@ public class MColumn {
|
||||
if (hasValue(alterColumn.getType())) {
|
||||
type = alterColumn.getType();
|
||||
}
|
||||
if (hasValue(alterColumn.isNotnull())) {
|
||||
notnull = alterColumn.isNotnull();
|
||||
}
|
||||
if (hasValue(alterColumn.getDefaultValue())) {
|
||||
defaultValue = alterColumn.getDefaultValue();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.avaje.ebean.dbmigration.model;
|
||||
|
||||
import com.avaje.ebean.dbmigration.migration.AddColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.AddHistoryTable;
|
||||
import com.avaje.ebean.dbmigration.migration.AddTableComment;
|
||||
import com.avaje.ebean.dbmigration.migration.AlterColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.Column;
|
||||
import com.avaje.ebean.dbmigration.migration.CreateTable;
|
||||
@@ -285,6 +286,13 @@ public class MTable {
|
||||
if (addColumn != null) {
|
||||
modelDiff.addAddColumn(addColumn);
|
||||
}
|
||||
|
||||
if (MColumn.different(comment, newTable.comment)) {
|
||||
AddTableComment addTableComment = new AddTableComment();
|
||||
addTableComment.setName(name);
|
||||
addTableComment.setComment(newTable.comment);
|
||||
modelDiff.addTableComment(addTableComment);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.avaje.ebean.dbmigration.model;
|
||||
|
||||
import com.avaje.ebean.dbmigration.migration.AddColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.AddHistoryTable;
|
||||
import com.avaje.ebean.dbmigration.migration.AddTableComment;
|
||||
import com.avaje.ebean.dbmigration.migration.AlterColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.ChangeSet;
|
||||
import com.avaje.ebean.dbmigration.migration.ChangeSetType;
|
||||
@@ -237,4 +238,11 @@ public class ModelDiff {
|
||||
public void addCreateIndex(CreateIndex createIndex) {
|
||||
applyChanges.add(createIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a table comment to the 'apply' changes.
|
||||
*/
|
||||
public void addTableComment(AddTableComment addTableComment) {
|
||||
applyChanges.add(addTableComment);
|
||||
}
|
||||
}
|
||||
|
||||
+25
-4
@@ -11,12 +11,13 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
|
||||
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueConstraint;
|
||||
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
|
||||
import com.avaje.ebeaninternal.server.deploy.TableJoinColumn;
|
||||
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Used as part of ModelBuildBeanVisitor and generally adds the MColumn to the associated
|
||||
@@ -249,9 +250,13 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
col.setUnique(determineUniqueConstraintName(col.getName()));
|
||||
indexSetAdd(col.getName());
|
||||
}
|
||||
String checkConstraint = p.getDbConstraintExpression();
|
||||
if (checkConstraint != null) {
|
||||
col.setCheckConstraint(checkConstraint);
|
||||
Set<String> checkConstraintValues = p.getDbCheckConstraintValues();
|
||||
if (checkConstraintValues != null) {
|
||||
if (beanDescriptor.hasInheritance()) {
|
||||
InheritInfo inheritInfo = beanDescriptor.getInheritInfo();
|
||||
inheritInfo.appendCheckConstraintValues(p.getName(), checkConstraintValues);
|
||||
}
|
||||
col.setCheckConstraint(buildCheckConstraint(p.getDbColumn(), checkConstraintValues));
|
||||
col.setCheckConstraintName(determineCheckConstraintName(col.getName()));
|
||||
}
|
||||
|
||||
@@ -268,6 +273,22 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
table.addColumn(col);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the check constraint clause given the db column and values.
|
||||
*/
|
||||
private String buildCheckConstraint(String dbColumn, Set<String> checkConstraintValues) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("check ( ").append(dbColumn).append(" in (");
|
||||
int count = 0;
|
||||
for (String value : checkConstraintValues) {
|
||||
if (count++ > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(value);
|
||||
}
|
||||
sb.append("))");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void indexSetAdd(String column) {
|
||||
indexSet.add(column);
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.avaje.ebean.dbmigration.runner;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
/**
|
||||
* Calculates the checksum for the given string content.
|
||||
*/
|
||||
public class Checksum {
|
||||
|
||||
/**
|
||||
* Returns the checksum of this string.
|
||||
*/
|
||||
public static int calculate(String str) {
|
||||
|
||||
final CRC32 crc32 = new CRC32();
|
||||
|
||||
BufferedReader bufferedReader = new BufferedReader(new StringReader(str));
|
||||
try {
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
crc32.update(line.getBytes("UTF-8"));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to calculate checksum", e);
|
||||
}
|
||||
|
||||
return (int) crc32.getValue();
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package com.avaje.ebean.dbmigration.runner;
|
||||
|
||||
import com.avaje.ebean.dbmigration.model.MigrationVersion;
|
||||
import org.avaje.classpath.scanner.Resource;
|
||||
|
||||
/**
|
||||
* A DB migration resource (DDL script with version).
|
||||
*/
|
||||
public class LocalMigrationResource implements Comparable<LocalMigrationResource> {
|
||||
|
||||
/**
|
||||
* Code for repeatable migrations.
|
||||
*/
|
||||
private static final String REPEAT_TYPE = "R";
|
||||
|
||||
/**
|
||||
* Code for version migrations.
|
||||
*/
|
||||
private static final String VERSION_TYPE = "V";
|
||||
|
||||
private final MigrationVersion version;
|
||||
|
||||
private final String location;
|
||||
|
||||
private final Resource resource;
|
||||
|
||||
/**
|
||||
* Construct with version and resource.
|
||||
*/
|
||||
public LocalMigrationResource(MigrationVersion version, String location, Resource resource) {
|
||||
this.version = version;
|
||||
this.location = location;
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return version.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the underlying version is "repeatable".
|
||||
*/
|
||||
public boolean isRepeatable() {
|
||||
return version.isRepeatable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the "key" that identifies the migration.
|
||||
*/
|
||||
public String key() {
|
||||
if (isRepeatable()) {
|
||||
return version.getComment().toLowerCase();
|
||||
} else {
|
||||
return version.normalised();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the migration comment.
|
||||
*/
|
||||
public String getComment() {
|
||||
String comment = version.getComment();
|
||||
return (comment == null || comment.isEmpty()) ? "-" : comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default ordering by version.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(LocalMigrationResource o) {
|
||||
return version.compareTo(o.version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying migration version.
|
||||
*/
|
||||
public MigrationVersion getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resource location.
|
||||
*/
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the content for the migration apply ddl script.
|
||||
*/
|
||||
public String getContent() {
|
||||
return resource.loadAsString("UTF-8");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type code ("R" or "V") for this migration.
|
||||
*/
|
||||
public String getType() {
|
||||
return isRepeatable() ? REPEAT_TYPE : VERSION_TYPE;
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package com.avaje.ebean.dbmigration.runner;
|
||||
|
||||
import com.avaje.ebean.config.DbMigrationConfig;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.dbmigration.model.MigrationVersion;
|
||||
import org.avaje.classpath.scanner.Resource;
|
||||
import org.avaje.classpath.scanner.ResourceFilter;
|
||||
import org.avaje.classpath.scanner.Scanner;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Loads the DB migration resources and sorts them into execution order.
|
||||
*/
|
||||
public class LocalMigrationResources {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(LocalMigrationResources.class);
|
||||
|
||||
private final ServerConfig serverConfig;
|
||||
|
||||
private final DbMigrationConfig migrationConfig;
|
||||
|
||||
private final List<LocalMigrationResource> versions = new ArrayList<LocalMigrationResource>();
|
||||
|
||||
/**
|
||||
* Construct with configuration options.
|
||||
*/
|
||||
public LocalMigrationResources(ServerConfig serverConfig, DbMigrationConfig migrationConfig) {
|
||||
this.serverConfig = serverConfig;
|
||||
this.migrationConfig = migrationConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read all the migration resources (SQL scripts) returning true if there are versions.
|
||||
*/
|
||||
public boolean readResources() {
|
||||
|
||||
String migrationPath = migrationConfig.getMigrationPath();
|
||||
|
||||
ClassLoader classLoader = serverConfig.getClassLoadConfig().getClassLoader();
|
||||
|
||||
Scanner scanner = new Scanner(classLoader);
|
||||
List<Resource> resourceList = scanner.scanForResources(migrationPath, new Match(migrationConfig));
|
||||
|
||||
logger.debug("resources: {}", resourceList);
|
||||
|
||||
for (Resource resource : resourceList) {
|
||||
String filename = resource.getFilename();
|
||||
if (filename.endsWith(migrationConfig.getApplySuffix())) {
|
||||
int pos = filename.lastIndexOf(migrationConfig.getApplySuffix());
|
||||
String mainName = filename.substring(0, pos);
|
||||
|
||||
MigrationVersion migrationVersion = MigrationVersion.parse(mainName);
|
||||
LocalMigrationResource res = new LocalMigrationResource(migrationVersion, resource.getLocation(), resource);
|
||||
versions.add(res);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(versions);
|
||||
return !versions.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of migration resources in version order.
|
||||
*/
|
||||
public List<LocalMigrationResource> getVersions() {
|
||||
return versions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter used to find the migration scripts.
|
||||
*/
|
||||
private static class Match implements ResourceFilter {
|
||||
|
||||
private final DbMigrationConfig migrationConfig;
|
||||
|
||||
Match(DbMigrationConfig migrationConfig) {
|
||||
this.migrationConfig = migrationConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMatch(String name) {
|
||||
return name.endsWith(migrationConfig.getApplySuffix());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
package com.avaje.ebean.dbmigration.runner;
|
||||
|
||||
import com.avaje.ebean.SqlRow;
|
||||
import com.avaje.ebean.SqlUpdate;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/**
|
||||
* Bean holding migration execution details stored in the migration table.
|
||||
*/
|
||||
class MigrationMetaRow {
|
||||
|
||||
private int id;
|
||||
|
||||
private String type;
|
||||
|
||||
private String version;
|
||||
|
||||
private String comment;
|
||||
|
||||
private int checksum;
|
||||
|
||||
private Timestamp runOn;
|
||||
|
||||
private String runBy;
|
||||
|
||||
private long runTime;
|
||||
|
||||
/**
|
||||
* Construct for inserting into table.
|
||||
*/
|
||||
MigrationMetaRow(int id, String type, String version, String comment, int checksum, String runBy, Timestamp runOn, long runTime) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.version = version;
|
||||
this.checksum = checksum;
|
||||
this.comment = comment;
|
||||
this.runBy = runBy;
|
||||
this.runOn = runOn;
|
||||
this.runTime = runTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct from the SqlRow (read from table).
|
||||
*/
|
||||
MigrationMetaRow(SqlRow row) {
|
||||
id = row.getInteger("id");
|
||||
type = row.getString("mtype");
|
||||
version = row.getString("mversion");
|
||||
comment = row.getString("mcomment");
|
||||
checksum = row.getInteger("mchecksum");
|
||||
runOn = row.getTimestamp("run_on");
|
||||
runBy = row.getString("run_by");
|
||||
runTime = row.getLong("run_time");
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "id:" + id + " type:" + type + " runVersion:" + version + " comment:" + comment + " runOn:" + runOn + " runBy:" + runBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the id for this migration.
|
||||
*/
|
||||
int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the normalised version for this migration.
|
||||
*/
|
||||
String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the checksum for this migration.
|
||||
*/
|
||||
int getChecksum() {
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind to the insert statement.
|
||||
*/
|
||||
void bindInsert(SqlUpdate insert) {
|
||||
insert.setParameter(1, id);
|
||||
insert.setParameter(2, type);
|
||||
insert.setParameter(3, "SUCCESS");
|
||||
insert.setParameter(4, version);
|
||||
insert.setParameter(5, comment);
|
||||
insert.setParameter(6, checksum);
|
||||
insert.setParameter(7, runOn);
|
||||
insert.setParameter(8, runBy);
|
||||
insert.setParameter(9, runTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL insert given the table migration meta data is stored in.
|
||||
*/
|
||||
static String insertSql(String table) {
|
||||
return "insert into " + table
|
||||
+ " (id, mtype, mstatus, mversion, mcomment, mchecksum, run_on, run_by, run_time)"
|
||||
+ " values (?,?,?,?,?,?,?,?,?)";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.avaje.ebean.dbmigration.runner;
|
||||
|
||||
import com.avaje.ebean.dbmigration.DdlRunner;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Runs the DDL migration scripts.
|
||||
*/
|
||||
public class MigrationScriptRunner {
|
||||
|
||||
private final Connection connection;
|
||||
|
||||
/**
|
||||
* Construct with a given connection.
|
||||
*/
|
||||
public MigrationScriptRunner(Connection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all the DDL statements in the script.
|
||||
*/
|
||||
int runScript(boolean expectErrors, String content, String scriptName) throws SQLException {
|
||||
|
||||
DdlRunner runner = new DdlRunner(expectErrors, scriptName);
|
||||
return runner.runAll(content, connection);
|
||||
}
|
||||
}
|
||||
@@ -1,259 +0,0 @@
|
||||
package com.avaje.ebean.dbmigration.runner;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.SqlQuery;
|
||||
import com.avaje.ebean.SqlRow;
|
||||
import com.avaje.ebean.SqlUpdate;
|
||||
import com.avaje.ebean.config.DbMigrationConfig;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.dbmigration.MigrationRunner;
|
||||
import com.avaje.ebean.plugin.SpiServer;
|
||||
import com.avaje.ebeaninternal.server.transaction.ExternalJdbcTransaction;
|
||||
import com.avaje.ebeaninternal.util.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Manages the migration table.
|
||||
*/
|
||||
public class MigrationTable {
|
||||
|
||||
private static final Logger logger = MigrationRunner.logger;
|
||||
|
||||
private final Connection connection;
|
||||
|
||||
private final EbeanServer server;
|
||||
|
||||
private final DatabasePlatform databasePlatform;
|
||||
|
||||
private final String catalog;
|
||||
private final String schema;
|
||||
private final String table;
|
||||
private final ServerConfig serverConfig;
|
||||
private final String envUserName;
|
||||
|
||||
private final Timestamp runOn = new Timestamp(System.currentTimeMillis());
|
||||
|
||||
private final ScriptTransform scriptTransform;
|
||||
|
||||
private final String insertSql;
|
||||
|
||||
private final LinkedHashMap<String, MigrationMetaRow> migrations;
|
||||
|
||||
private MigrationMetaRow lastMigration;
|
||||
|
||||
/**
|
||||
* Construct with server, configuration and jdbc connection (DB admin user).
|
||||
*/
|
||||
public MigrationTable(EbeanServer server, DbMigrationConfig migrationConfig, Connection connection) {
|
||||
this.connection = connection;
|
||||
this.server = server;
|
||||
this.migrations = new LinkedHashMap<String, MigrationMetaRow>();
|
||||
|
||||
SpiServer pluginApi = server.getPluginApi();
|
||||
this.serverConfig = pluginApi.getServerConfig();
|
||||
this.databasePlatform = pluginApi.getDatabasePlatform();
|
||||
this.catalog = null;
|
||||
this.schema = null;
|
||||
this.table = migrationConfig.getMetaTable();
|
||||
this.insertSql = MigrationMetaRow.insertSql(table);
|
||||
this.scriptTransform = createScriptTransform(migrationConfig);
|
||||
this.envUserName = System.getProperty("user.name");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of migrations in the DB migration table.
|
||||
*/
|
||||
public int size() {
|
||||
return migrations.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the ScriptTransform for placeholder key/value replacement.
|
||||
*/
|
||||
private ScriptTransform createScriptTransform(DbMigrationConfig config) {
|
||||
|
||||
Map<String, String> map = PlaceholderBuilder.build(config.getRunPlaceholders(), config.getRunPlaceholderMap());
|
||||
return new ScriptTransform(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the table is it does not exist.
|
||||
*/
|
||||
public void createIfNeeded() throws SQLException, IOException {
|
||||
|
||||
if (!tableExists(connection)) {
|
||||
createTable(connection);
|
||||
}
|
||||
|
||||
ExternalJdbcTransaction t = new ExternalJdbcTransaction(connection);
|
||||
SqlQuery sqlQuery = server.createSqlQuery("select * from " + table + " order by id for update");
|
||||
List<SqlRow> metaRows = server.findList(sqlQuery, t);
|
||||
|
||||
for (SqlRow row : metaRows) {
|
||||
MigrationMetaRow metaRow = new MigrationMetaRow(row);
|
||||
addMigration(metaRow.getVersion(), metaRow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void createTable(Connection connection) throws IOException, SQLException {
|
||||
|
||||
String script = ScriptTransform.table(table, getCreateTableScript());
|
||||
|
||||
MigrationScriptRunner run = new MigrationScriptRunner(connection);
|
||||
run.runScript(false, script, "create migration table");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the create table script.
|
||||
*/
|
||||
private String getCreateTableScript() throws IOException {
|
||||
// supply a script to override the default table create script
|
||||
String script = readResource("migration-support/create-table.sql");
|
||||
if (script == null) {
|
||||
// no, just use the default script
|
||||
script = readResource("migration-support/default-create-table.sql");
|
||||
}
|
||||
return script;
|
||||
}
|
||||
|
||||
private String readResource(String location) throws IOException {
|
||||
|
||||
Enumeration<URL> resources = serverConfig.getClassLoadConfig().getResources(location);
|
||||
if (resources.hasMoreElements()) {
|
||||
URL url = resources.nextElement();
|
||||
return IOUtils.readUtf8(url.openStream());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the table exists.
|
||||
*/
|
||||
private boolean tableExists(Connection connection) throws SQLException {
|
||||
return databasePlatform.tableExists(connection, catalog, schema, table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the migration ran successfully and false if the migration failed.
|
||||
*/
|
||||
public boolean shouldRun(LocalMigrationResource localVersion, LocalMigrationResource priorVersion) throws SQLException {
|
||||
|
||||
if (priorVersion != null && !localVersion.isRepeatable()) {
|
||||
if (!migrationExists(priorVersion)) {
|
||||
logger.error("Migration {} requires prior migration {} which has not been run", localVersion.getVersion(), priorVersion.getVersion());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MigrationMetaRow existing = migrations.get(localVersion.key());
|
||||
return runMigration(localVersion, existing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the migration script.
|
||||
*
|
||||
* @param local The local migration resource
|
||||
* @param existing The information for this migration existing in the table
|
||||
*
|
||||
* @return True if the migrations should continue
|
||||
*/
|
||||
private boolean runMigration(LocalMigrationResource local, MigrationMetaRow existing) throws SQLException {
|
||||
|
||||
String script = convertScript(local.getContent());
|
||||
int checksum = Checksum.calculate(script);
|
||||
|
||||
if (existing != null) {
|
||||
|
||||
boolean matchChecksum = (existing.getChecksum() == checksum);
|
||||
|
||||
if (!local.isRepeatable()) {
|
||||
if (!matchChecksum) {
|
||||
logger.error("Checksum mismatch on migration {}", local.getLocation());
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if (matchChecksum) {
|
||||
logger.trace("... skip unchanged repeatable migration {}", local.getLocation());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
runMigration(local, script, checksum);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a migration script as new migration or update on existing repeatable migration.
|
||||
*/
|
||||
private void runMigration(LocalMigrationResource local, String script, int checksum) throws SQLException {
|
||||
|
||||
logger.debug("run migration {}", local.getLocation());
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
MigrationScriptRunner run = new MigrationScriptRunner(connection);
|
||||
run.runScript(false, script, "run migration version: " + local.getVersion());
|
||||
|
||||
long exeMillis = System.currentTimeMillis() - start;
|
||||
// insert new migration row
|
||||
SqlUpdate insert = server.createSqlUpdate(insertSql);
|
||||
MigrationMetaRow metaRow = createMetaRow(local, checksum, exeMillis);
|
||||
metaRow.bindInsert(insert);
|
||||
server.execute(insert, new ExternalJdbcTransaction(connection));
|
||||
|
||||
addMigration(local.key(), metaRow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the MigrationMetaRow for this migration.
|
||||
*/
|
||||
private MigrationMetaRow createMetaRow(LocalMigrationResource migration, int checksum, long exeMillis) {
|
||||
|
||||
int nextId = 1;
|
||||
if (lastMigration != null) {
|
||||
nextId = lastMigration.getId() + 1;
|
||||
}
|
||||
|
||||
String type = migration.getType();
|
||||
String runVersion = migration.key();
|
||||
String comment = migration.getComment();
|
||||
|
||||
return new MigrationMetaRow(nextId, type, runVersion, comment, checksum, envUserName, runOn, exeMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the migration exists.
|
||||
*/
|
||||
private boolean migrationExists(LocalMigrationResource priorVersion) {
|
||||
return migrations.containsKey(priorVersion.key());
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the placeholder key/value replacement on the script.
|
||||
*/
|
||||
private String convertScript(String script) {
|
||||
return scriptTransform.transform(script);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the successfully executed migration (to allow dependant scripts to run).
|
||||
*/
|
||||
private void addMigration(String key, MigrationMetaRow metaRow) {
|
||||
lastMigration = metaRow;
|
||||
if (metaRow.getVersion() == null) {
|
||||
throw new IllegalStateException("No runVersion in db migration table row? " + metaRow);
|
||||
}
|
||||
migrations.put(key, metaRow);
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package com.avaje.ebean.dbmigration.runner;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Joins placeholder map and comma/equals delimited string.
|
||||
*/
|
||||
class PlaceholderBuilder {
|
||||
|
||||
private final Map<String,String> map = new HashMap<String,String>();
|
||||
|
||||
/**
|
||||
* Create with raw comma and equals delimited pairs plus map of key value pairs.
|
||||
*/
|
||||
public static Map<String,String> build(String commaDelimited, Map<String,String> placeholders) {
|
||||
|
||||
PlaceholderBuilder builder = new PlaceholderBuilder();
|
||||
builder.add(commaDelimited);
|
||||
builder.add(placeholders);
|
||||
|
||||
return builder.map;
|
||||
}
|
||||
|
||||
private PlaceholderBuilder() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a comma and equals delimited string to parse for key value pairs.
|
||||
*/
|
||||
public void add(String commaDelimited) {
|
||||
|
||||
if (commaDelimited != null) {
|
||||
String[] split = commaDelimited.split("[,;]");
|
||||
for (String keyValue : split) {
|
||||
String[] pair = keyValue.split("=");
|
||||
if (pair.length == 2) {
|
||||
map.put(pair[0].trim(), pair[1].trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a map of key value placeholder pairs.
|
||||
*/
|
||||
public void add(Map<String,String> placeholders) {
|
||||
if (placeholders != null) {
|
||||
map.putAll(placeholders);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package com.avaje.ebean.dbmigration.runner;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Transforms a SQL script given a map of key/value substitutions.
|
||||
*/
|
||||
class ScriptTransform {
|
||||
|
||||
/**
|
||||
* Transform just ${table} with the table name.
|
||||
*/
|
||||
public static String table(String tableName, String script) {
|
||||
return script.replace("${table}", tableName);
|
||||
}
|
||||
|
||||
private final Map<String,String> placeholders = new HashMap<String, String>();
|
||||
|
||||
ScriptTransform(Map<String,String> map) {
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
placeholders.put(wrapKey(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private String wrapKey(String key) {
|
||||
return "${"+key+"}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this contains no placeholders.
|
||||
*/
|
||||
boolean isEmpty() {
|
||||
return placeholders.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the script replacing placeholders in the form <code>${key}</code> with <code>value</code>.
|
||||
*/
|
||||
String transform(String source) {
|
||||
|
||||
for (Map.Entry<String, String> entry : placeholders.entrySet()) {
|
||||
source = source.replace(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return source;
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,7 @@ public abstract class AbstractBeanPersistListener implements BeanPersistListener
|
||||
* @param bean The bean that was inserted.
|
||||
*/
|
||||
@Override
|
||||
public boolean inserted(Object bean) {
|
||||
return false;
|
||||
public void inserted(Object bean) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,8 +30,7 @@ public abstract class AbstractBeanPersistListener implements BeanPersistListener
|
||||
* @param updatedProperties The properties that were modified by this update.
|
||||
*/
|
||||
@Override
|
||||
public boolean updated(Object bean, Set<String> updatedProperties) {
|
||||
return false;
|
||||
public void updated(Object bean, Set<String> updatedProperties) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,8 +40,7 @@ public abstract class AbstractBeanPersistListener implements BeanPersistListener
|
||||
* @param bean The bean that was deleted.
|
||||
*/
|
||||
@Override
|
||||
public boolean deleted(Object bean) {
|
||||
return false;
|
||||
public void deleted(Object bean) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.avaje.ebean.event;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Listens for committed bean events.
|
||||
* <p>
|
||||
@@ -13,22 +13,18 @@ import com.avaje.ebean.config.ServerConfig;
|
||||
* the listener is notified of the event.
|
||||
* </p>
|
||||
* <p>
|
||||
* For a cluster these events may need to be broadcast. Each of the inserted(),
|
||||
* updated() and deleted() methods return true if you want those events to be
|
||||
* broadcast to the other members of a cluster (the id values are broadcast). If
|
||||
* these methods return false then the events are not broadcast.
|
||||
* </p>
|
||||
* <p>
|
||||
* It is worth noting that BeanPersistListener is different in three main ways
|
||||
* It is worth noting that BeanPersistListener is different in two main ways
|
||||
* from BeanPersistController postXXX methods.
|
||||
* <ul>
|
||||
* <li>BeanPersistListener only sees successfully committed events.
|
||||
* BeanPersistController pre and post methods occur before the commit or a
|
||||
* rollback and will see events that are later rolled back</li>
|
||||
* <li>BeanPersistListener runs in a background thread and will not effect the
|
||||
* response time of the actual persist where as BeanPersistController code will</li>
|
||||
* <li>BeanPersistListener can be notified of events from other servers in a
|
||||
* cluster.</li>
|
||||
* <li>
|
||||
* BeanPersistListener only sees successfully committed events.
|
||||
* BeanPersistController pre and post methods occur before the commit or a
|
||||
* rollback and will see events that are later rolled back
|
||||
* </li>
|
||||
* <li>
|
||||
* BeanPersistListener runs in a background thread and will not effect the
|
||||
* response time of the actual persist where as BeanPersistController code will
|
||||
* </li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
@@ -46,32 +42,29 @@ public interface BeanPersistListener {
|
||||
boolean isRegisterFor(Class<?> cls);
|
||||
|
||||
/**
|
||||
* Notified that a bean has been inserted locally. Return true if you want the
|
||||
* cluster to be notified of the event.
|
||||
* Notified that a bean has been inserted.
|
||||
*
|
||||
* @param bean
|
||||
* The bean that was inserted.
|
||||
*/
|
||||
boolean inserted(Object bean);
|
||||
void inserted(Object bean);
|
||||
|
||||
/**
|
||||
* Notified that a bean has been updated locally. Return true if you want the
|
||||
* cluster to be notified of the event.
|
||||
* Notified that a bean has been updated.
|
||||
*
|
||||
* @param bean
|
||||
* The bean that was updated.
|
||||
* @param updatedProperties
|
||||
* The properties that were modified by this update.
|
||||
*/
|
||||
boolean updated(Object bean, Set<String> updatedProperties);
|
||||
void updated(Object bean, Set<String> updatedProperties);
|
||||
|
||||
/**
|
||||
* Notified that a bean has been deleted locally. Return true if you want the
|
||||
* cluster to be notified of the event.
|
||||
* Notified that a bean has been deleted.
|
||||
*
|
||||
* @param bean
|
||||
* The bean that was deleted.
|
||||
*/
|
||||
boolean deleted(Object bean);
|
||||
void deleted(Object bean);
|
||||
|
||||
}
|
||||
|
||||
@@ -74,4 +74,23 @@ public class ReadAuditQueryPlan {
|
||||
public void setSql(String sql) {
|
||||
this.sql = sql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ReadAuditQueryPlan that = (ReadAuditQueryPlan) o;
|
||||
if (!beanType.equals(that.beanType)) return false;
|
||||
if (!queryKey.equals(that.queryKey)) return false;
|
||||
return sql.equals(that.sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = beanType.hashCode();
|
||||
result = 31 * result + queryKey.hashCode();
|
||||
result = 31 * result + sql.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.avaje.ebean.TxScope;
|
||||
import com.avaje.ebean.config.PersistBatch;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Used internally to handle the scoping of transactions for methods.
|
||||
*/
|
||||
public class ScopeTrans implements Thread.UncaughtExceptionHandler {
|
||||
|
||||
private static final int OPCODE_ATHROW = 191;
|
||||
private static final int OPCODE_ATHROW = 191;
|
||||
|
||||
private final SpiTransactionScopeManager scopeMgr;
|
||||
private final SpiTransactionScopeManager scopeMgr;
|
||||
|
||||
/**
|
||||
* The suspended transaction (can be null).
|
||||
*/
|
||||
private final SpiTransaction suspendedTransaction;
|
||||
/**
|
||||
* The suspended transaction (can be null).
|
||||
*/
|
||||
private final SpiTransaction suspendedTransaction;
|
||||
|
||||
/**
|
||||
* The transaction in scope (can be null).
|
||||
*/
|
||||
private final SpiTransaction transaction;
|
||||
/**
|
||||
* The transaction in scope (can be null).
|
||||
*/
|
||||
private final SpiTransaction transaction;
|
||||
|
||||
/**
|
||||
* If true by default rollback on Checked exceptions.
|
||||
*/
|
||||
private final boolean rollbackOnChecked;
|
||||
/**
|
||||
* If true by default rollback on Checked exceptions.
|
||||
*/
|
||||
private final boolean rollbackOnChecked;
|
||||
|
||||
/**
|
||||
* True if the transaction was created and hence should be committed
|
||||
* on finally if it hasn't already been rolled back.
|
||||
*/
|
||||
private final boolean created;
|
||||
/**
|
||||
* True if the transaction was created and hence should be committed
|
||||
* on finally if it hasn't already been rolled back.
|
||||
*/
|
||||
private final boolean created;
|
||||
|
||||
/**
|
||||
* Explicit set of Exceptions that DO NOT cause a rollback to occur.
|
||||
*/
|
||||
private final ArrayList<Class<? extends Throwable>> noRollbackFor;
|
||||
/**
|
||||
* Explicit set of Exceptions that DO NOT cause a rollback to occur.
|
||||
*/
|
||||
private final ArrayList<Class<? extends Throwable>> noRollbackFor;
|
||||
|
||||
/**
|
||||
* Explicit set of Exceptions that DO cause a rollback to occur.
|
||||
*/
|
||||
private final ArrayList<Class<? extends Throwable>> rollbackFor;
|
||||
/**
|
||||
* Explicit set of Exceptions that DO cause a rollback to occur.
|
||||
*/
|
||||
private final ArrayList<Class<? extends Throwable>> rollbackFor;
|
||||
|
||||
private PersistBatch restoreBatch;
|
||||
|
||||
@@ -51,29 +51,32 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
|
||||
|
||||
private int restoreBatchSize;
|
||||
|
||||
/**
|
||||
* Flag set when a rollback has occurred.
|
||||
*/
|
||||
private boolean rolledBack;
|
||||
|
||||
|
||||
public ScopeTrans(boolean rollbackOnChecked, boolean created, SpiTransaction transaction, TxScope txScope,
|
||||
SpiTransaction suspendedTransaction, SpiTransactionScopeManager scopeMgr) {
|
||||
private Boolean restoreBatchGeneratedKeys;
|
||||
|
||||
this.rollbackOnChecked = rollbackOnChecked;
|
||||
this.created = created;
|
||||
this.transaction = transaction;
|
||||
this.suspendedTransaction = suspendedTransaction;
|
||||
this.scopeMgr = scopeMgr;
|
||||
|
||||
this.noRollbackFor = txScope.getNoRollbackFor();
|
||||
this.rollbackFor = txScope.getRollbackFor();
|
||||
/**
|
||||
* Flag set when a rollback has occurred.
|
||||
*/
|
||||
private boolean rolledBack;
|
||||
|
||||
|
||||
public ScopeTrans(boolean rollbackOnChecked, boolean created, SpiTransaction transaction, TxScope txScope,
|
||||
SpiTransaction suspendedTransaction, SpiTransactionScopeManager scopeMgr) {
|
||||
|
||||
this.rollbackOnChecked = rollbackOnChecked;
|
||||
this.created = created;
|
||||
this.transaction = transaction;
|
||||
this.suspendedTransaction = suspendedTransaction;
|
||||
this.scopeMgr = scopeMgr;
|
||||
|
||||
this.noRollbackFor = txScope.getNoRollbackFor();
|
||||
this.rollbackFor = txScope.getRollbackFor();
|
||||
|
||||
if (transaction != null) {
|
||||
if (!created && txScope.isBatchSet() || txScope.isBatchOnCascadeSet() || txScope.isBatchSizeSet()) {
|
||||
restoreBatch = transaction.getBatch();
|
||||
restoreBatchOnCascade = transaction.getBatchOnCascade();
|
||||
restoreBatchSize = transaction.getBatchSize();
|
||||
restoreBatchGeneratedKeys = transaction.getBatchGetGeneratedKeys();
|
||||
}
|
||||
if (txScope.isBatchSet()) {
|
||||
transaction.setBatch(txScope.getBatch());
|
||||
@@ -84,70 +87,74 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
|
||||
if (txScope.isBatchSizeSet()) {
|
||||
transaction.setBatchSize(txScope.getBatchSize());
|
||||
}
|
||||
if (txScope.isSkipGeneratedKeys()) {
|
||||
transaction.setBatchGetGeneratedKeys(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current/active transaction.
|
||||
*/
|
||||
protected SpiTransaction getTransaction() {
|
||||
return transaction;
|
||||
}
|
||||
/**
|
||||
* Return the current/active transaction.
|
||||
*/
|
||||
protected SpiTransaction getTransaction() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the Thread catches any uncaught exception.
|
||||
* For example, an unexpected NullPointerException or Error.
|
||||
*/
|
||||
public void uncaughtException(Thread thread, Throwable e) {
|
||||
|
||||
// rollback transaction if required
|
||||
caughtThrowable(e);
|
||||
|
||||
// reinstate suspended transaction
|
||||
onFinally();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returned via RETURN or expected Exception from the method.
|
||||
* @param returnOrThrowable the return value or Throwable
|
||||
* @param opCode indicates
|
||||
*/
|
||||
public void onExit(Object returnOrThrowable, int opCode) {
|
||||
|
||||
if (opCode == OPCODE_ATHROW){
|
||||
// exited with a Throwable
|
||||
caughtThrowable((Throwable)returnOrThrowable);
|
||||
}
|
||||
onFinally();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Commit if the transaction exists and has not already been rolled back.
|
||||
* Also reinstate the suspended transaction if there was one.
|
||||
*/
|
||||
public void onFinally() {
|
||||
/**
|
||||
* Called when the Thread catches any uncaught exception.
|
||||
* For example, an unexpected NullPointerException or Error.
|
||||
*/
|
||||
public void uncaughtException(Thread thread, Throwable e) {
|
||||
|
||||
try {
|
||||
if (!rolledBack) {
|
||||
commitTransaction();
|
||||
}
|
||||
} finally {
|
||||
restoreSuspended();
|
||||
}
|
||||
}
|
||||
// rollback transaction if required
|
||||
caughtThrowable(e);
|
||||
|
||||
protected void restoreSuspended() {
|
||||
if (suspendedTransaction != null){
|
||||
// reinstate suspended transaction
|
||||
onFinally();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returned via RETURN or expected Exception from the method.
|
||||
*
|
||||
* @param returnOrThrowable the return value or Throwable
|
||||
* @param opCode indicates
|
||||
*/
|
||||
public void onExit(Object returnOrThrowable, int opCode) {
|
||||
|
||||
if (opCode == OPCODE_ATHROW) {
|
||||
// exited with a Throwable
|
||||
caughtThrowable((Throwable) returnOrThrowable);
|
||||
}
|
||||
onFinally();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Commit if the transaction exists and has not already been rolled back.
|
||||
* Also reinstate the suspended transaction if there was one.
|
||||
*/
|
||||
public void onFinally() {
|
||||
|
||||
try {
|
||||
if (!rolledBack) {
|
||||
commitTransaction();
|
||||
}
|
||||
} finally {
|
||||
restoreSuspended();
|
||||
}
|
||||
}
|
||||
|
||||
protected void restoreSuspended() {
|
||||
if (suspendedTransaction != null) {
|
||||
// put the previously suspended transaction
|
||||
// back onto the ThreadLocal or equivalent
|
||||
scopeMgr.replace(suspendedTransaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void commitTransaction() {
|
||||
if (created) {
|
||||
protected void commitTransaction() {
|
||||
if (created) {
|
||||
transaction.commit();
|
||||
} else {
|
||||
if (restoreBatch != null) {
|
||||
@@ -159,78 +166,81 @@ public class ScopeTrans implements Thread.UncaughtExceptionHandler {
|
||||
if (restoreBatchSize > 0) {
|
||||
transaction.setBatchSize(restoreBatchSize);
|
||||
}
|
||||
if (restoreBatchGeneratedKeys != null) {
|
||||
transaction.setBatchGetGeneratedKeys(restoreBatchGeneratedKeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An Error was caught and this ALWAYS causes a rollback to occur.
|
||||
* Returns the error and this should be thrown by the calling code.
|
||||
*/
|
||||
public Error caughtError(Error e) {
|
||||
rollback(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* An Exception was caught and may or may not cause a rollback to occur.
|
||||
* Returns the exception and this should be thrown by the calling code.
|
||||
*/
|
||||
public <T extends Throwable> T caughtThrowable(T e) {
|
||||
|
||||
if (isRollbackThrowable(e)) {
|
||||
rollback(e);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
/**
|
||||
* An Error was caught and this ALWAYS causes a rollback to occur.
|
||||
* Returns the error and this should be thrown by the calling code.
|
||||
*/
|
||||
public Error caughtError(Error e) {
|
||||
rollback(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
protected void rollback(Throwable e) {
|
||||
if (transaction != null && transaction.isActive()) {
|
||||
// transaction is null for NOT_SUPPORTED and sometimes SUPPORTS
|
||||
// and Inactive (already rolled back) if nested REQUIRED
|
||||
transaction.rollback(e);
|
||||
}
|
||||
rolledBack = true;
|
||||
}
|
||||
/**
|
||||
* An Exception was caught and may or may not cause a rollback to occur.
|
||||
* Returns the exception and this should be thrown by the calling code.
|
||||
*/
|
||||
public <T extends Throwable> T caughtThrowable(T e) {
|
||||
|
||||
/**
|
||||
* Return true if this throwable should cause a rollback to occur.
|
||||
*/
|
||||
private boolean isRollbackThrowable(Throwable e) {
|
||||
if (isRollbackThrowable(e)) {
|
||||
rollback(e);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
if (e instanceof Error){
|
||||
return true;
|
||||
}
|
||||
|
||||
if (noRollbackFor != null){
|
||||
for (int i = 0; i < noRollbackFor.size(); i++) {
|
||||
if (noRollbackFor.get(i).equals(e.getClass())) {
|
||||
|
||||
// explicit no rollback for this one
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
protected void rollback(Throwable e) {
|
||||
if (transaction != null && transaction.isActive()) {
|
||||
// transaction is null for NOT_SUPPORTED and sometimes SUPPORTS
|
||||
// and Inactive (already rolled back) if nested REQUIRED
|
||||
transaction.rollback(e);
|
||||
}
|
||||
rolledBack = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this throwable should cause a rollback to occur.
|
||||
*/
|
||||
private boolean isRollbackThrowable(Throwable e) {
|
||||
|
||||
if (e instanceof Error) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (noRollbackFor != null) {
|
||||
for (int i = 0; i < noRollbackFor.size(); i++) {
|
||||
if (noRollbackFor.get(i).equals(e.getClass())) {
|
||||
|
||||
// explicit no rollback for this one
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rollbackFor != null) {
|
||||
for (int i = 0; i < rollbackFor.size(); i++) {
|
||||
if (rollbackFor.get(i).equals(e.getClass())) {
|
||||
// explicit rollback for this one
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (e instanceof RuntimeException) {
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// checked exceptions...
|
||||
// EJB defaults this to false which is not intuitive IMO
|
||||
// Ebean makes this configurable (default to true)
|
||||
return rollbackOnChecked;
|
||||
}
|
||||
}
|
||||
|
||||
if (rollbackFor != null){
|
||||
for (int i = 0; i < rollbackFor.size(); i++) {
|
||||
if (rollbackFor.get(i).equals(e.getClass())) {
|
||||
// explicit rollback for this one
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (e instanceof RuntimeException) {
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// checked exceptions...
|
||||
// EJB defaults this to false which is not intuitive IMO
|
||||
// Ebean makes this configurable (default to true)
|
||||
return rollbackOnChecked;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -230,6 +230,11 @@ public class ScopedTransaction implements SpiTransaction {
|
||||
transaction.setBatchGetGeneratedKeys(getGeneratedKeys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getBatchGetGeneratedKeys() {
|
||||
return transaction.getBatchGetGeneratedKeys();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatchFlushOnMixed(boolean batchFlushOnMixed) {
|
||||
transaction.setBatchFlushOnMixed(batchFlushOnMixed);
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.avaje.ebean.ExpressionList;
|
||||
import com.avaje.ebean.OrderBy;
|
||||
import com.avaje.ebean.PersistenceContextScope;
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebean.bean.BeanCollectionTouched;
|
||||
import com.avaje.ebean.bean.CallStack;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
@@ -19,6 +18,7 @@ import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
import com.avaje.ebeaninternal.server.query.CancelableQuery;
|
||||
import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmUpdateProperties;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
@@ -92,6 +92,11 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
* Delete query.
|
||||
*/
|
||||
DELETE,
|
||||
|
||||
/**
|
||||
* Update query.
|
||||
*/
|
||||
UPDATE,
|
||||
}
|
||||
|
||||
enum TemporalMode {
|
||||
@@ -226,18 +231,6 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
|
||||
List<String> getSoftDeletePredicates();
|
||||
|
||||
/**
|
||||
* Return a listener that wants to be notified when the bean collection is
|
||||
* first used.
|
||||
*/
|
||||
BeanCollectionTouched getBeanCollectionTouched();
|
||||
|
||||
/**
|
||||
* Set a listener to be notified when the bean collection has been touched
|
||||
* (when the list/set/map is first used).
|
||||
*/
|
||||
void setBeanCollectionTouched(BeanCollectionTouched notify);
|
||||
|
||||
/**
|
||||
* Set the list of Id's that is being populated.
|
||||
* <p>
|
||||
@@ -449,11 +442,6 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
void setUsageProfiling(boolean usageProfiling);
|
||||
|
||||
/**
|
||||
* Return the query name.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Prepare the query which prepares sub-query expressions and calculates
|
||||
* and returns the query plan key.
|
||||
@@ -477,12 +465,6 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
HashQuery queryHash();
|
||||
|
||||
/**
|
||||
* Return true if this is a query based on a SqlSelect rather than
|
||||
* generated.
|
||||
*/
|
||||
boolean isSqlSelect();
|
||||
|
||||
/**
|
||||
* Return true if this is a RawSql query.
|
||||
*/
|
||||
@@ -526,9 +508,15 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
boolean hasMaxRowsOrFirstRow();
|
||||
|
||||
/**
|
||||
* Return true if this query should use/check the bean cache.
|
||||
* Return true if the bean cache should be exclude for query or lazy loading.
|
||||
*/
|
||||
Boolean isUseBeanCache();
|
||||
boolean isExcludeBeanCache();
|
||||
|
||||
/**
|
||||
* Return true if this query should use the bean cache.
|
||||
* It is not skipped and bean caching is supported.
|
||||
*/
|
||||
boolean isUseBeanCache();
|
||||
|
||||
/**
|
||||
* Return true if this query should use/check the query cache.
|
||||
@@ -695,4 +683,9 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
Set<String> validate(BeanType<T> desc);
|
||||
|
||||
/**
|
||||
* Return the properties for an update query.
|
||||
*/
|
||||
OrmUpdateProperties getUpdateProperties();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.annotation.DocStoreMode;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
@@ -12,6 +9,9 @@ import com.avaje.ebeaninternal.server.core.PersistRequest;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
|
||||
import com.avaje.ebeaninternal.server.persist.BatchControl;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Extends Transaction with additional API required on server.
|
||||
* <p>
|
||||
@@ -125,6 +125,11 @@ public interface SpiTransaction extends Transaction {
|
||||
*/
|
||||
int getBatchSize();
|
||||
|
||||
/**
|
||||
* Return the getGeneratedKeys setting for this transaction.
|
||||
*/
|
||||
Boolean getBatchGetGeneratedKeys();
|
||||
|
||||
/**
|
||||
* Modify and return the current 'depth' of the transaction.
|
||||
* <p>
|
||||
|
||||
@@ -53,7 +53,7 @@ class BootupClassPathSearch {
|
||||
}
|
||||
|
||||
long searchTime = System.currentTimeMillis() - st;
|
||||
logger.info("Classpath search entities[{}] searchTime [{}]", bc.getEntities().size(), searchTime);
|
||||
logger.info("Classpath search entities[{}] searchTime[{}] in packages[{}]", bc.getEntities().size(), searchTime, packages);
|
||||
return bc;
|
||||
|
||||
} catch (Exception ex) {
|
||||
|
||||
@@ -114,8 +114,7 @@ public class DefaultContainer implements SpiContainer {
|
||||
SpiBackgroundExecutor executor = createBackgroundExecutor(serverConfig);
|
||||
ServerCacheManager cacheManager = getCacheManager(online, serverConfig, executor);
|
||||
|
||||
XmlConfig xmlConfig = new XmlConfigLoader(null).load();
|
||||
InternalConfiguration c = new InternalConfiguration(xmlConfig, clusterManager, cacheManager, executor, serverConfig, bootupClasses);
|
||||
InternalConfiguration c = new InternalConfiguration(clusterManager, cacheManager, executor, serverConfig, bootupClasses);
|
||||
|
||||
DefaultServer server = new DefaultServer(c, cacheManager);
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ import com.avaje.ebean.config.EncryptKeyManager;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.dbmigration.DdlGenerator;
|
||||
import com.avaje.ebean.dbmigration.MigrationRunner;
|
||||
import com.avaje.ebean.event.BeanPersistController;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditLogger;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditPrepare;
|
||||
@@ -39,9 +38,6 @@ import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.DNativeQuery;
|
||||
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.el.ElFilter;
|
||||
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
|
||||
@@ -57,6 +53,7 @@ import com.avaje.ebeaninternal.server.query.QueryFutureRowCount;
|
||||
import com.avaje.ebeaninternal.server.querydefn.DefaultOrmQuery;
|
||||
import com.avaje.ebeaninternal.server.querydefn.DefaultOrmUpdate;
|
||||
import com.avaje.ebeaninternal.server.querydefn.DefaultRelationalQuery;
|
||||
import com.avaje.ebeaninternal.server.querydefn.DefaultUpdateQuery;
|
||||
import com.avaje.ebeaninternal.server.text.csv.TCsvReader;
|
||||
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent;
|
||||
@@ -65,6 +62,7 @@ import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager;
|
||||
import com.avaje.ebeaninternal.util.ParamTypeHelper;
|
||||
import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreIntegration;
|
||||
import org.avaje.dbmigration.MigrationRunner;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -344,10 +342,13 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
DbMigrationConfig migrationConfig = serverConfig.getMigrationConfig();
|
||||
if (migrationConfig != null) {
|
||||
migrationConfig.generateOnStart(this);
|
||||
}
|
||||
|
||||
if (migrationConfig.isRunMigration()) {
|
||||
new MigrationRunner(this, migrationConfig).run();
|
||||
if (migrationConfig.isRunMigration()) {
|
||||
// classLoader used to load resources
|
||||
ClassLoader classLoader = serverConfig.getClassLoadConfig().getClassLoader();
|
||||
MigrationRunner runner = migrationConfig.createRunner(classLoader);
|
||||
runner.run(serverConfig.getDataSource());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -848,25 +849,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
beanDescriptor.sort(list, sortByClause);
|
||||
}
|
||||
|
||||
public <T> Query<T> createQuery(Class<T> beanType) throws PersistenceException {
|
||||
return createQuery(beanType, null);
|
||||
}
|
||||
|
||||
public <T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery) throws PersistenceException {
|
||||
|
||||
BeanDescriptor<T> desc = getBeanDescriptor(beanType);
|
||||
if (desc == null) {
|
||||
throw new PersistenceException("Is " + beanType.getName() + " an Entity Bean? BeanDescriptor not found?");
|
||||
}
|
||||
DeployNamedQuery deployQuery = desc.getNamedQuery(namedQuery);
|
||||
if (deployQuery == null) {
|
||||
throw new PersistenceException("named query " + namedQuery + " was not found for " + desc.getFullName());
|
||||
}
|
||||
|
||||
// this will parse the query
|
||||
return new DefaultOrmQuery<T>(desc, this, expressionFactory, deployQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Set<String> validateQuery(Query<T> query) {
|
||||
|
||||
@@ -894,42 +876,20 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return new TCsvReader<T>(this, descriptor);
|
||||
}
|
||||
|
||||
public <T> UpdateQuery<T> update(Class<T> beanType) {
|
||||
return new DefaultUpdateQuery<T>(createQuery(beanType));
|
||||
}
|
||||
|
||||
public <T> Query<T> find(Class<T> beanType) {
|
||||
return createQuery(beanType);
|
||||
}
|
||||
|
||||
public <T> Query<T> createQuery(Class<T> beanType, String query) {
|
||||
public <T> DefaultOrmQuery<T> createQuery(Class<T> beanType) {
|
||||
BeanDescriptor<T> desc = getBeanDescriptor(beanType);
|
||||
if (desc == null) {
|
||||
throw new PersistenceException(beanType.getName() + " is NOT an Entity Bean registered with this server?");
|
||||
}
|
||||
switch (desc.getEntityType()) {
|
||||
case SQL:
|
||||
if (query != null) {
|
||||
throw new PersistenceException("You must used Named queries for this Entity " + desc.getFullName());
|
||||
}
|
||||
// use the "default" SqlSelect
|
||||
DeployNamedQuery defaultSqlSelect = desc.getNamedQuery("default");
|
||||
return new DefaultOrmQuery<T>(desc, this, expressionFactory, defaultSqlSelect);
|
||||
|
||||
default:
|
||||
return new DefaultOrmQuery<T>(desc, this, expressionFactory, query);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> Update<T> createNamedUpdate(Class<T> beanType, String namedUpdate) {
|
||||
BeanDescriptor<?> desc = getBeanDescriptor(beanType);
|
||||
if (desc == null) {
|
||||
String m = beanType.getName() + " is NOT an Entity Bean registered with this server?";
|
||||
throw new PersistenceException(m);
|
||||
}
|
||||
|
||||
DeployNamedUpdate deployUpdate = desc.getNamedUpdate(namedUpdate);
|
||||
if (deployUpdate == null) {
|
||||
throw new PersistenceException("named update " + namedUpdate + " was not found for " + desc.getFullName());
|
||||
}
|
||||
|
||||
return new DefaultOrmUpdate<T>(beanType, this, desc.getBaseTable(), deployUpdate);
|
||||
return new DefaultOrmQuery<T>(desc, this, expressionFactory);
|
||||
}
|
||||
|
||||
public <T> Update<T> createUpdate(Class<T> beanType, String ormUpdate) {
|
||||
@@ -946,14 +906,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return new DefaultRelationalQuery(this, sql);
|
||||
}
|
||||
|
||||
public SqlQuery createNamedSqlQuery(String namedQuery) {
|
||||
DNativeQuery nq = beanDescriptorManager.getNativeQuery(namedQuery);
|
||||
if (nq == null) {
|
||||
throw new PersistenceException("SqlQuery " + namedQuery + " not found.");
|
||||
}
|
||||
return new DefaultRelationalQuery(this, nq.getQuery());
|
||||
}
|
||||
|
||||
public SqlUpdate createSqlUpdate(String sql) {
|
||||
return new DefaultSqlUpdate(this, sql);
|
||||
}
|
||||
@@ -962,14 +914,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return new DefaultCallableSql(this, sql);
|
||||
}
|
||||
|
||||
public SqlUpdate createNamedSqlUpdate(String namedQuery) {
|
||||
DNativeQuery nq = beanDescriptorManager.getNativeQuery(namedQuery);
|
||||
if (nq == null) {
|
||||
throw new PersistenceException("SqlUpdate " + namedQuery + " not found.");
|
||||
}
|
||||
return new DefaultSqlUpdate(this, nq.getQuery());
|
||||
}
|
||||
|
||||
public <T> T find(Class<T> beanType, Object uid) {
|
||||
|
||||
return find(beanType, uid, null);
|
||||
@@ -1047,8 +991,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
if (!desc.calculateUseCache(query.isUseBeanCache())) {
|
||||
// not using bean cache
|
||||
if (!query.isUseBeanCache()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1221,6 +1164,18 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
public <T> int update(Query<T> query, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.UPDATE, query, t);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
request.markNotQueryOnly();
|
||||
return request.update();
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
}
|
||||
|
||||
public <T> FutureRowCount<T> findFutureRowCount(Query<T> q, Transaction t) {
|
||||
|
||||
SpiQuery<T> copy = ((SpiQuery<T>) q).copy();
|
||||
@@ -1278,12 +1233,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return queryFuture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction, int pageIndex, int pageSize) {
|
||||
|
||||
return new LimitOffsetPagedList<T>(this, (SpiQuery<T>)query, pageIndex, pageSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction) {
|
||||
|
||||
@@ -1311,7 +1260,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
request.initTransIfRequired();
|
||||
request.findEach(consumer);
|
||||
// no try finally - findVisit guarantee's cleanup of the transaction if required
|
||||
// no try finally - findEach guarantee's cleanup of the transaction if required
|
||||
}
|
||||
|
||||
public <T> void findEachWhile(Query<T> query, QueryEachWhileConsumer<T> consumer, Transaction t) {
|
||||
@@ -1324,21 +1273,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
request.initTransIfRequired();
|
||||
request.findEachWhile(consumer);
|
||||
// no try finally - findVisit guarantee's cleanup of the transaction if required
|
||||
}
|
||||
|
||||
public <T> QueryIterator<T> findIterate(Query<T> query, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
|
||||
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return request.findIterate();
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
request.endTransIfRequired();
|
||||
throw ex;
|
||||
}
|
||||
// no try finally - findEachWhile guarantee's cleanup of the transaction if required
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1617,90 +1552,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return (EntityBean)bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associations (from the intersection table) of a ManyToMany given
|
||||
* the owner bean and the propertyName of the ManyToMany collection.
|
||||
* <p>
|
||||
* This returns the number of associations deleted.
|
||||
* </p>
|
||||
*/
|
||||
public int deleteManyToManyAssociations(Object ownerBean, String propertyName) {
|
||||
return deleteManyToManyAssociations(ownerBean, propertyName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the associations (from the intersection table) of a ManyToMany given
|
||||
* the owner bean and the propertyName of the ManyToMany collection.
|
||||
* <p>
|
||||
* This returns the number of associations deleted.
|
||||
* </p>
|
||||
*/
|
||||
public int deleteManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) {
|
||||
|
||||
EntityBean owner = checkEntityBean(ownerBean);
|
||||
TransWrapper wrap = initTransIfRequired(t);
|
||||
try {
|
||||
SpiTransaction trans = wrap.transaction;
|
||||
int rc = persister.deleteManyToManyAssociations(owner, propertyName, trans);
|
||||
wrap.commitIfCreated();
|
||||
return rc;
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
wrap.rollbackIfCreated();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the associations of a ManyToMany given the owner bean and the
|
||||
* propertyName of the ManyToMany collection.
|
||||
*/
|
||||
public void saveManyToManyAssociations(Object ownerBean, String propertyName) {
|
||||
saveManyToManyAssociations(ownerBean, propertyName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the associations of a ManyToMany given the owner bean and the
|
||||
* propertyName of the ManyToMany collection.
|
||||
*/
|
||||
public void saveManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) {
|
||||
|
||||
EntityBean owner = checkEntityBean(ownerBean);
|
||||
TransWrapper wrap = initTransIfRequired(t);
|
||||
try {
|
||||
SpiTransaction trans = wrap.transaction;
|
||||
|
||||
persister.saveManyToManyAssociations(owner, propertyName, trans);
|
||||
|
||||
wrap.commitIfCreated();
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
wrap.rollbackIfCreated();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void saveAssociation(Object ownerBean, String propertyName) {
|
||||
saveAssociation(ownerBean, propertyName, null);
|
||||
}
|
||||
|
||||
public void saveAssociation(Object ownerBean, String propertyName, Transaction t) {
|
||||
|
||||
EntityBean owner = checkEntityBean(ownerBean);
|
||||
|
||||
TransWrapper wrap = initTransIfRequired(t);
|
||||
try {
|
||||
SpiTransaction trans = wrap.transaction;
|
||||
persister.saveAssociation(owner, propertyName, trans);
|
||||
|
||||
wrap.commitIfCreated();
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
wrap.rollbackIfCreated();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int saveAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException {
|
||||
return saveAllInternal(beans.iterator(), transaction);
|
||||
|
||||
@@ -23,17 +23,15 @@ import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogPrepare;
|
||||
import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogRegister;
|
||||
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.CloneDataTimeZone;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.NoDataTimeZone;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.SimpleDataTimeZone;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import com.avaje.ebeaninternal.server.deploy.DeployOrmXml;
|
||||
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
|
||||
import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory;
|
||||
import com.avaje.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import org.avaje.datasource.DataSourcePool;
|
||||
import com.avaje.ebeaninternal.server.persist.Binder;
|
||||
import com.avaje.ebeaninternal.server.persist.DefaultPersister;
|
||||
import com.avaje.ebeaninternal.server.query.CQueryEngine;
|
||||
@@ -56,6 +54,7 @@ import com.avaje.ebeanservice.docstore.api.DocStoreIntegration;
|
||||
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateProcessor;
|
||||
import com.avaje.ebeanservice.docstore.none.NoneDocStoreFactory;
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import org.avaje.datasource.DataSourcePool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -79,8 +78,6 @@ public class InternalConfiguration {
|
||||
|
||||
private final DeployInherit deployInherit;
|
||||
|
||||
private final DeployOrmXml deployOrmXml;
|
||||
|
||||
private final TypeManager typeManager;
|
||||
|
||||
private final DataTimeZone dataTimeZone;
|
||||
@@ -103,8 +100,6 @@ public class InternalConfiguration {
|
||||
|
||||
private final SpiBackgroundExecutor backgroundExecutor;
|
||||
|
||||
private final XmlConfig xmlConfig;
|
||||
|
||||
private final JsonFactory jsonFactory;
|
||||
|
||||
private final DocStoreFactory docStoreFactory;
|
||||
@@ -114,13 +109,12 @@ public class InternalConfiguration {
|
||||
*/
|
||||
private final List<Plugin> plugins = new ArrayList<Plugin>();
|
||||
|
||||
public InternalConfiguration(XmlConfig xmlConfig, ClusterManager clusterManager,
|
||||
public InternalConfiguration(ClusterManager clusterManager,
|
||||
ServerCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor,
|
||||
ServerConfig serverConfig, BootupClasses bootupClasses) {
|
||||
|
||||
this.docStoreFactory = initDocStoreFactory(serverConfig.service(DocStoreFactory.class));
|
||||
this.jsonFactory = serverConfig.getJsonFactory();
|
||||
this.xmlConfig = xmlConfig;
|
||||
this.clusterManager = clusterManager;
|
||||
this.backgroundExecutor = backgroundExecutor;
|
||||
this.cacheManager = cacheManager;
|
||||
@@ -131,7 +125,6 @@ public class InternalConfiguration {
|
||||
this.expressionFactory = initExpressionFactory(serverConfig, databasePlatform);
|
||||
this.typeManager = new DefaultTypeManager(serverConfig, bootupClasses);
|
||||
|
||||
this.deployOrmXml = new DeployOrmXml();
|
||||
this.deployInherit = new DeployInherit(bootupClasses);
|
||||
|
||||
this.deployCreateProperties = new DeployCreateProperties(typeManager);
|
||||
@@ -264,10 +257,6 @@ public class InternalConfiguration {
|
||||
return new DJsonContext(server, jsonFactory, typeManager);
|
||||
}
|
||||
|
||||
public XmlConfig getXmlConfig() {
|
||||
return xmlConfig;
|
||||
}
|
||||
|
||||
public AutoTuneService createAutoTuneService(SpiEbeanServer server) {
|
||||
return AutoTuneServiceFactory.create(server, serverConfig);
|
||||
}
|
||||
@@ -316,10 +305,6 @@ public class InternalConfiguration {
|
||||
return deployInherit;
|
||||
}
|
||||
|
||||
public DeployOrmXml getDeployOrmXml() {
|
||||
return deployOrmXml;
|
||||
}
|
||||
|
||||
public DeployCreateProperties getDeployCreateProperties() {
|
||||
return deployCreateProperties;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import com.avaje.ebean.QueryIterator;
|
||||
import com.avaje.ebean.Version;
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebeaninternal.api.BeanIdList;
|
||||
@@ -43,4 +42,9 @@ public interface OrmQueryEngine {
|
||||
* Execute the query as a delete statement.
|
||||
*/
|
||||
<T> int delete(OrmQueryRequest<T> request);
|
||||
|
||||
/**
|
||||
* Execute the query as a update statement.
|
||||
*/
|
||||
<T> int update(OrmQueryRequest<T> request);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.avaje.ebeaninternal.server.core;
|
||||
import com.avaje.ebean.PersistenceContextScope;
|
||||
import com.avaje.ebean.QueryEachConsumer;
|
||||
import com.avaje.ebean.QueryEachWhileConsumer;
|
||||
import com.avaje.ebean.QueryIterator;
|
||||
import com.avaje.ebean.RawSql;
|
||||
import com.avaje.ebean.Version;
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
@@ -174,14 +173,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a query using generated sql. If false this query
|
||||
* will use raw sql (Entity bean based on raw sql select).
|
||||
*/
|
||||
public boolean isSqlSelect() {
|
||||
return query.isSqlSelect() && query.getRawSql() == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the PersistenceContext used for this request.
|
||||
*/
|
||||
@@ -295,6 +286,13 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
return queryEngine.delete(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query as a update.
|
||||
*/
|
||||
public int update() {
|
||||
return queryEngine.update(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query as findById.
|
||||
*/
|
||||
@@ -425,7 +423,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
}
|
||||
|
||||
public boolean isUseBeanCache() {
|
||||
return beanDescriptor.calculateUseCache(query.isUseBeanCache());
|
||||
return query.isUseBeanCache();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -305,7 +305,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
* Set the cache notify status.
|
||||
*/
|
||||
private void setNotifyCache() {
|
||||
this.notifyCache = beanDescriptor.isCacheNotify(publish);
|
||||
this.notifyCache = beanDescriptor.isCacheNotify(type, publish);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,26 +35,6 @@ public interface Persister {
|
||||
*/
|
||||
void save(EntityBean entityBean, Transaction t);
|
||||
|
||||
/**
|
||||
* Save the associations of a ManyToMany given the owner bean and the
|
||||
* propertyName of the ManyToMany collection.
|
||||
*/
|
||||
void saveManyToManyAssociations(EntityBean ownerBean, String propertyName, Transaction t);
|
||||
|
||||
/**
|
||||
* Save an association (OneToMany, ManyToOne, OneToOne or ManyToMany).
|
||||
*
|
||||
* @param parentBean the bean that owns the association.
|
||||
* @param propertyName the name of the property to save.
|
||||
* @param t the transaction to use.
|
||||
*/
|
||||
void saveAssociation(EntityBean parentBean, String propertyName, Transaction t);
|
||||
|
||||
/**
|
||||
* Delete the associations of a ManyToMany given the owner bean and the property name of the ManyToMany.
|
||||
*/
|
||||
int deleteManyToManyAssociations(EntityBean ownerBean, String propertyName, Transaction t);
|
||||
|
||||
/**
|
||||
* Delete a bean given it's type and id value.
|
||||
* <p>
|
||||
|
||||
@@ -24,29 +24,18 @@ public class PostgresCast {
|
||||
// for exists and not-exists expressions
|
||||
return "";
|
||||
}
|
||||
|
||||
if (isIntegerType(value)) {
|
||||
if (value instanceof Integer) {
|
||||
return asArray ? "::integer[]" : "::integer";
|
||||
}
|
||||
if (isNumberType(value)) {
|
||||
if (value instanceof Long) {
|
||||
return asArray ? "::bigint[]" : "::bigint";
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return asArray ? "::decimal[]" : "::decimal";
|
||||
}
|
||||
if (isBooleanType(value)) {
|
||||
if (value instanceof Boolean) {
|
||||
return asArray ? "::boolean[]" : "::boolean";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private static boolean isBooleanType(Object value) {
|
||||
return (value instanceof Boolean);
|
||||
}
|
||||
|
||||
private static boolean isIntegerType(Object value) {
|
||||
return (value instanceof Integer) || (value instanceof Long);
|
||||
}
|
||||
|
||||
private static boolean isNumberType(Object value) {
|
||||
return (value instanceof Number);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.avaje.ebean;
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
@@ -31,8 +31,6 @@ import java.util.List;
|
||||
*/
|
||||
public final class RelationalQueryRequest {
|
||||
|
||||
private static final int GLOBAL_ROW_LIMIT = Integer.valueOf(System.getProperty("ebean.query.globallimit", "1000000"));
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RelationalQueryRequest.class);
|
||||
|
||||
private final SpiSqlQuery query;
|
||||
@@ -212,7 +210,18 @@ public final class RelationalQueryRequest {
|
||||
// convert any named parameters if required
|
||||
sql = BindParamsParser.parse(bindParams, sql);
|
||||
}
|
||||
this.sql = sql;
|
||||
this.sql = limitOffset(sql);
|
||||
}
|
||||
|
||||
private String limitOffset(String sql) {
|
||||
|
||||
int firstRow = query.getFirstRow();
|
||||
int maxRows = query.getMaxRows();
|
||||
if (firstRow > 0 || maxRows > 0) {
|
||||
return ebeanServer.getDatabasePlatform().getBasicSqlLimiter()
|
||||
.limit(query.getQuery(), firstRow, maxRows);
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,13 +259,6 @@ public final class RelationalQueryRequest {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maxRows allowed to fetch.
|
||||
*/
|
||||
public int getMaxRows() {
|
||||
return query.getMaxRows() >= 1 ? query.getMaxRows() : GLOBAL_ROW_LIMIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL executed for this query.
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import com.avaje.ebean.QueryEachConsumer;
|
||||
import com.avaje.ebean.QueryEachWhileConsumer;
|
||||
import com.avaje.ebean.QueryIterator;
|
||||
import com.avaje.ebean.Version;
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
@@ -53,6 +52,11 @@ public interface SpiOrmQueryRequest<T> extends DocQueryRequest<T> {
|
||||
*/
|
||||
int delete();
|
||||
|
||||
/**
|
||||
* Execute the query as a update.
|
||||
*/
|
||||
int update();
|
||||
|
||||
/**
|
||||
* Execute the query as findById.
|
||||
*/
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebeaninternal.server.lib.util.Dnode;
|
||||
|
||||
/**
|
||||
* Holds the orm.xml and ebean-orm.xml deployment information.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class XmlConfig {
|
||||
|
||||
private final List<Dnode> ebeanOrmXml;
|
||||
private final List<Dnode> ormXml;
|
||||
private final List<Dnode> allXml;
|
||||
|
||||
public XmlConfig(List<Dnode> ormXml, List<Dnode> ebeanOrmXml){
|
||||
this.ormXml = ormXml;
|
||||
this.ebeanOrmXml = ebeanOrmXml;
|
||||
this.allXml = new ArrayList<Dnode>(ormXml.size() + ebeanOrmXml.size());
|
||||
allXml.addAll(ormXml);
|
||||
allXml.addAll(ebeanOrmXml);
|
||||
}
|
||||
|
||||
public List<Dnode> getEbeanOrmXml() {
|
||||
return ebeanOrmXml;
|
||||
}
|
||||
|
||||
public List<Dnode> getOrmXml() {
|
||||
return ormXml;
|
||||
}
|
||||
|
||||
public List<Dnode> find(List<Dnode> entityXml, String element) {
|
||||
ArrayList<Dnode> hits = new ArrayList<Dnode>();
|
||||
for (int i = 0; i < entityXml.size(); i++) {
|
||||
hits.addAll(entityXml.get(i).findAll(element, 1));
|
||||
}
|
||||
return hits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the deployment xml for a given entity.
|
||||
* <p>
|
||||
* This searches all the orm.xml and ebean-orm.xml files.
|
||||
* </p>
|
||||
*/
|
||||
public List<Dnode> findEntityXml(String className) {
|
||||
|
||||
ArrayList<Dnode> hits = new ArrayList<Dnode>(2);
|
||||
|
||||
for (Dnode ormXml : allXml) {
|
||||
Dnode entityMappings = ormXml.find("entity-mappings");
|
||||
|
||||
List<Dnode> entities = entityMappings.findAll("entity", "class", className, 1);
|
||||
if (entities.size() == 1) {
|
||||
hits.add(entities.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
return hits;
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import com.avaje.ebeaninternal.server.lib.util.Dnode;
|
||||
import com.avaje.ebeaninternal.server.lib.util.DnodeReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Used to read the orm.xml and ebean-orm.xml configuration files.
|
||||
*
|
||||
* @author rbygrave
|
||||
* @author Richard Vowles - http://plus.google.com/RichardVowles
|
||||
*/
|
||||
public class XmlConfigLoader {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(XmlConfigLoader.class);
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
public XmlConfigLoader(ClassLoader classLoader) {
|
||||
|
||||
if (classLoader == null) {
|
||||
classLoader = getClass().getClassLoader();
|
||||
}
|
||||
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
public XmlConfig load() {
|
||||
List<Dnode> ormXml = search("META-INF/orm.xml");
|
||||
List<Dnode> ebeanOrmXml = search("META-INF/ebean-orm.xml");
|
||||
|
||||
return new XmlConfig(ormXml, ebeanOrmXml);
|
||||
}
|
||||
|
||||
public List<Dnode> search(String resourceName) {
|
||||
ArrayList<Dnode> xmlList = new ArrayList<Dnode>();
|
||||
|
||||
try {
|
||||
Enumeration<URL> resources = classLoader.getResources(resourceName);
|
||||
|
||||
while (resources.hasMoreElements()) {
|
||||
URL url = resources.nextElement();
|
||||
|
||||
InputStream is = url.openStream();
|
||||
processInputStream(xmlList, is);
|
||||
is.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Unable to find resources {}", resourceName);
|
||||
}
|
||||
|
||||
return xmlList;
|
||||
}
|
||||
|
||||
private void processInputStream(ArrayList<Dnode> xmlList, InputStream is) throws IOException {
|
||||
|
||||
DnodeReader reader = new DnodeReader();
|
||||
Dnode xmlDoc = reader.parseXml(is);
|
||||
is.close();
|
||||
|
||||
xmlList.add(xmlDoc);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ public class SimpleDataTimeZone implements DataTimeZone {
|
||||
|
||||
@Override
|
||||
public Calendar getTimeZone() {
|
||||
// return null so Calendar is not used
|
||||
return zone;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,10 +347,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
*/
|
||||
private final String fullName;
|
||||
|
||||
private final Map<String, DeployNamedQuery> namedQueries;
|
||||
|
||||
private final Map<String, DeployNamedUpdate> namedUpdates;
|
||||
|
||||
/**
|
||||
* Flag used to determine if saves can be skipped.
|
||||
*/
|
||||
@@ -371,6 +367,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
|
||||
private String deleteByIdSql;
|
||||
private String deleteByIdInSql;
|
||||
private String whereIdInSql;
|
||||
private String softDeleteByIdSql;
|
||||
private String softDeleteByIdInSql;
|
||||
|
||||
@@ -414,9 +411,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
this.beanType = deploy.getBeanType();
|
||||
this.rootBeanType = PersistenceContextUtil.root(beanType);
|
||||
this.prototypeEntityBean = createPrototypeEntityBean(beanType);
|
||||
|
||||
this.namedQueries = deploy.getNamedQueries();
|
||||
this.namedUpdates = deploy.getNamedUpdates();
|
||||
|
||||
this.inheritInfo = deploy.getInheritInfo();
|
||||
|
||||
@@ -694,7 +688,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
String idEqualsSql = idBinder.getBindIdSql(null);
|
||||
|
||||
deleteByIdSql = "delete from " + baseTable + " where " + idEqualsSql;
|
||||
deleteByIdInSql = "delete from " + baseTable + " where " + idBinderInLHSSqlNoAlias + " ";
|
||||
whereIdInSql = " where " + idBinderInLHSSqlNoAlias + " ";
|
||||
deleteByIdInSql = "delete from " + baseTable + whereIdInSql;
|
||||
|
||||
if (softDelete) {
|
||||
softDeleteByIdSql = "update " + baseTable + " set " + getSoftDeleteDbSet() + " where " + idEqualsSql;
|
||||
@@ -703,14 +698,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
softDeleteByIdSql = null;
|
||||
softDeleteByIdInSql = null;
|
||||
}
|
||||
|
||||
if (!isEmbedded()) {
|
||||
// parse every named update up front into sql dml
|
||||
for (DeployNamedUpdate namedUpdate : namedUpdates.values()) {
|
||||
DeployUpdateParser parser = new DeployUpdateParser(this);
|
||||
namedUpdate.initialise(parser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -726,6 +713,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
}
|
||||
docMapping = docStoreAdapter.createDocMapping();
|
||||
docStoreAdapter.registerPaths();
|
||||
cacheHelp.deriveNotifyFlags();
|
||||
}
|
||||
|
||||
public void initInheritInfo() {
|
||||
@@ -810,6 +798,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the "where id in" sql (for use with UpdateQuery).
|
||||
*/
|
||||
public String getWhereIdInSql() {
|
||||
return whereIdInSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the "delete by id" sql.
|
||||
*/
|
||||
@@ -866,10 +861,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean calculateUseCache(Boolean queryUseCache) {
|
||||
return (queryUseCache != null) ? queryUseCache : isBeanCaching();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cache options.
|
||||
*/
|
||||
@@ -1059,12 +1050,12 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
/**
|
||||
* Return true if the persist request needs to notify the cache.
|
||||
*/
|
||||
public boolean isCacheNotify(boolean publish) {
|
||||
public boolean isCacheNotify(PersistRequest.Type type, boolean publish) {
|
||||
if (draftable && !publish) {
|
||||
// no caching when editing draft beans
|
||||
return false;
|
||||
}
|
||||
return cacheHelp.isCacheNotify();
|
||||
return cacheHelp.isCacheNotify(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1535,24 +1526,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
return idBinder.getBindValues(idValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a named query.
|
||||
*/
|
||||
public DeployNamedQuery getNamedQuery(String name) {
|
||||
return namedQueries.get(name);
|
||||
}
|
||||
|
||||
public void addNamedQuery(DeployNamedQuery deployNamedQuery) {
|
||||
namedQueries.put(deployNamedQuery.getName(), deployNamedQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a named update.
|
||||
*/
|
||||
public DeployNamedUpdate getNamedUpdate(String name) {
|
||||
return namedUpdates.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T createBean() {
|
||||
@@ -1657,7 +1630,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
return assocProp.getTargetDescriptor().getBeanDescriptor(splitBegin[1]);
|
||||
|
||||
} else {
|
||||
throw new RuntimeException("Error getting BeanDescriptor for path " + path + " from " + getFullName());
|
||||
throw new PersistenceException("Invalid path " + path + " from " + getFullName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.avaje.ebeaninternal.server.cache.CachedBeanDataFromBean;
|
||||
import com.avaje.ebeaninternal.server.cache.CachedBeanDataToBean;
|
||||
import com.avaje.ebeaninternal.server.cache.CachedManyIds;
|
||||
import com.avaje.ebeaninternal.server.core.CacheOptions;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequest;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
|
||||
import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
|
||||
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
@@ -33,6 +34,8 @@ import java.util.Map;
|
||||
*/
|
||||
final class BeanDescriptorCacheHelp<T> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanDescriptorCacheHelp.class);
|
||||
|
||||
private static final Logger queryLog = LoggerFactory.getLogger("org.avaje.ebean.cache.QUERY");
|
||||
private static final Logger beanLog = LoggerFactory.getLogger("org.avaje.ebean.cache.BEAN");
|
||||
private static final Logger manyLog = LoggerFactory.getLogger("org.avaje.ebean.cache.COLL");
|
||||
@@ -60,6 +63,16 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
private final ServerCache naturalKeyCache;
|
||||
private final ServerCache queryCache;
|
||||
|
||||
/**
|
||||
* Set to true if all persist changes need to notify the cache.
|
||||
*/
|
||||
private boolean cacheNotifyOnAll;
|
||||
|
||||
/**
|
||||
* Set to true if delete changes need to notify cache.
|
||||
*/
|
||||
private boolean cacheNotifyOnDelete;
|
||||
|
||||
BeanDescriptorCacheHelp(BeanDescriptor<T> desc, ServerCacheManager cacheManager, CacheOptions cacheOptions,
|
||||
boolean cacheSharableBeans, BeanPropertyAssocOne<?>[] propertiesOneImported) {
|
||||
|
||||
@@ -91,6 +104,43 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive the cache notify flags.
|
||||
*/
|
||||
public void deriveNotifyFlags() {
|
||||
cacheNotifyOnAll = (beanCache != null || queryCache != null);
|
||||
cacheNotifyOnDelete = !cacheNotifyOnAll && isNotifyOnDeletes();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (isBeanCaching() || isQueryCaching() || cacheNotifyOnAll || cacheNotifyOnDelete) {
|
||||
String notifyMode = cacheNotifyOnAll ? "All" : (cacheNotifyOnDelete ? "Delete" : "None");
|
||||
logger.debug("l2 caching on {} - beanCaching:{} queryCaching:{} notifyMode:{} ",
|
||||
desc.getFullName(), isBeanCaching(), isQueryCaching(), notifyMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is an imported bi-directional relationship to a bea
|
||||
* that does have bean caching enabled.
|
||||
*/
|
||||
private boolean isNotifyOnDeletes() {
|
||||
for (int i = 0; i < propertiesOneImported.length; i++) {
|
||||
if (propertiesOneImported[i].isCacheNotify()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the persist request needs to notify the cache.
|
||||
*/
|
||||
boolean isCacheNotify(PersistRequest.Type type) {
|
||||
return cacheNotifyOnAll
|
||||
|| cacheNotifyOnDelete && (type == PersistRequest.Type.DELETE || type == PersistRequest.Type.DELETE_PERMANENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is currently query caching for this type of bean.
|
||||
*/
|
||||
@@ -105,22 +155,6 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
return beanCache != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the persist request needs to notify the cache.
|
||||
*/
|
||||
boolean isCacheNotify() {
|
||||
|
||||
if (isBeanCaching() || isQueryCaching()) {
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < propertiesOneImported.length; i++) {
|
||||
if (propertiesOneImported[i].getTargetDescriptor().isBeanCaching()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CacheOptions getCacheOptions() {
|
||||
return cacheOptions;
|
||||
}
|
||||
@@ -244,7 +278,9 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
void manyPropPut(BeanPropertyAssocMany<?> many, Object details, Object parentId) {
|
||||
|
||||
CachedManyIds entry = createManyIds(many, details);
|
||||
cachePutManyIds(parentId, many.getName(), entry);
|
||||
if (entry != null) {
|
||||
cachePutManyIds(parentId, many.getName(), entry);
|
||||
}
|
||||
}
|
||||
|
||||
void cachePutManyIds(Object parentId, String manyName, CachedManyIds entry) {
|
||||
@@ -262,6 +298,9 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
|
||||
List<Object> idList = new ArrayList<Object>();
|
||||
Collection<?> actualDetails = BeanCollectionUtil.getActualEntries(details);
|
||||
if (actualDetails == null) {
|
||||
return null;
|
||||
}
|
||||
for (Object bean : actualDetails) {
|
||||
idList.add(targetDescriptor.getId((EntityBean) bean));
|
||||
}
|
||||
@@ -539,7 +578,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add appropriate cache changes to support delete.
|
||||
* Add appropriate cache changes to support delete by id.
|
||||
*/
|
||||
void handleDelete(Object id, CacheChangeSet changeSet) {
|
||||
if (beanCache != null) {
|
||||
@@ -549,7 +588,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add appropriate cache changes to support delete.
|
||||
* Add appropriate cache changes to support delete bean.
|
||||
*/
|
||||
void handleDelete(Object id, PersistRequestBean<T> deleteRequest, CacheChangeSet changeSet) {
|
||||
queryCacheClear(changeSet);
|
||||
@@ -587,7 +626,9 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
BeanPropertyAssocMany<?> many = manyCollections.get(i);
|
||||
Object details = many.getValue(updateRequest.getEntityBean());
|
||||
CachedManyIds entry = createManyIds(many, details);
|
||||
changeSet.addManyPut(desc, many.getName(), id, entry);
|
||||
if (entry != null) {
|
||||
changeSet.addManyPut(desc, many.getName(), id, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,6 @@ package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.Model;
|
||||
import com.avaje.ebean.RawSql;
|
||||
import com.avaje.ebean.RawSqlBuilder;
|
||||
import com.avaje.ebeaninternal.api.ConcurrencyMode;
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
@@ -15,20 +12,20 @@ import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.config.dbplatform.DbHistorySupport;
|
||||
import com.avaje.ebean.config.dbplatform.DbIdentity;
|
||||
import com.avaje.ebean.config.dbplatform.PlatformIdGenerator;
|
||||
import com.avaje.ebean.config.dbplatform.IdType;
|
||||
import com.avaje.ebean.config.dbplatform.PlatformIdGenerator;
|
||||
import com.avaje.ebean.event.changelog.ChangeLogFilter;
|
||||
import com.avaje.ebean.event.changelog.ChangeLogListener;
|
||||
import com.avaje.ebean.event.changelog.ChangeLogPrepare;
|
||||
import com.avaje.ebean.event.changelog.ChangeLogRegister;
|
||||
import com.avaje.ebean.plugin.BeanType;
|
||||
import com.avaje.ebeaninternal.api.ConcurrencyMode;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.TransactionEventTable;
|
||||
import com.avaje.ebeaninternal.server.core.BootupClasses;
|
||||
import com.avaje.ebeaninternal.server.core.InternString;
|
||||
import com.avaje.ebeaninternal.server.core.InternalConfiguration;
|
||||
import com.avaje.ebeaninternal.server.core.Message;
|
||||
import com.avaje.ebeaninternal.server.core.XmlConfig;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
|
||||
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
|
||||
import com.avaje.ebeaninternal.server.deploy.id.IdBinderEmbedded;
|
||||
@@ -46,8 +43,6 @@ import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.ReadAnnotations;
|
||||
import com.avaje.ebeaninternal.server.deploy.parse.TransientProperties;
|
||||
import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator;
|
||||
import com.avaje.ebeaninternal.server.lib.util.Dnode;
|
||||
import com.avaje.ebeaninternal.server.properties.BeanPropertiesReader;
|
||||
import com.avaje.ebeaninternal.server.properties.BeanPropertyInfo;
|
||||
import com.avaje.ebeaninternal.server.properties.BeanPropertyInfoFactory;
|
||||
@@ -109,8 +104,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
private final DeployCreateProperties createProperties;
|
||||
|
||||
private final DeployOrmXml deployOrmXml;
|
||||
|
||||
private final BeanManagerFactory beanManagerFactory;
|
||||
|
||||
private final ServerConfig serverConfig;
|
||||
@@ -163,8 +156,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
private final IdBinderFactory idBinderFactory;
|
||||
|
||||
private final XmlConfig xmlConfig;
|
||||
|
||||
private final BeanLifecycleAdapterFactory beanLifecycleAdapterFactory;
|
||||
|
||||
private final boolean eagerFetchLobs;
|
||||
@@ -190,7 +181,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
this.serverName = InternString.intern(serverConfig.getName());
|
||||
this.cacheManager = config.getCacheManager();
|
||||
this.docStoreFactory = config.getDocStoreFactory();
|
||||
this.xmlConfig = config.getXmlConfig();
|
||||
this.dbSequenceBatchSize = serverConfig.getDatabaseSequenceBatchSize();
|
||||
this.backgroundExecutor = config.getBackgroundExecutor();
|
||||
this.dataSource = serverConfig.getDataSource();
|
||||
@@ -207,7 +197,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
this.namingConvention = serverConfig.getNamingConvention();
|
||||
this.dbIdentity = config.getDatabasePlatform().getDbIdentity();
|
||||
this.deplyInherit = config.getDeployInherit();
|
||||
this.deployOrmXml = config.getDeployOrmXml();
|
||||
this.deployUtil = config.getDeployUtil();
|
||||
|
||||
this.beanManagerFactory = new BeanManagerFactory(config.getDatabasePlatform());
|
||||
@@ -316,10 +305,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
readEntityBeanTable();
|
||||
readEntityDeploymentAssociations();
|
||||
readInheritedIdGenerators();
|
||||
|
||||
// creates the BeanDescriptors
|
||||
readEntityRelationships();
|
||||
readRawSqlQueries();
|
||||
|
||||
List<BeanDescriptor<?>> list = new ArrayList<BeanDescriptor<?>>(descMap.values());
|
||||
Collections.sort(list, beanDescComparator);
|
||||
@@ -555,10 +542,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
return beanManagerMap.get(beanClassName);
|
||||
}
|
||||
|
||||
public DNativeQuery getNativeQuery(String name) {
|
||||
return deployOrmXml.getNativeQuery(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the BeanControllers, BeanFinders and BeanListeners.
|
||||
*/
|
||||
@@ -679,25 +662,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
return new BeanTable(beanTable, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the named Raw Sql queries using BeanDescriptor.
|
||||
*/
|
||||
private void readRawSqlQueries() {
|
||||
|
||||
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
|
||||
|
||||
DeployBeanDescriptor<?> deployDesc = info.getDescriptor();
|
||||
BeanDescriptor<?> desc = getBeanDescriptor(deployDesc.getBeanType());
|
||||
|
||||
for (DRawSqlMeta rawSqlMeta : deployDesc.getRawSqlMeta()) {
|
||||
if (rawSqlMeta.getQuery() != null) {
|
||||
DeployNamedQuery nq = new DRawSqlSelectBuilder(namingConvention, desc, rawSqlMeta).parse();
|
||||
desc.addNamedQuery(nq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void readEntityRelationships() {
|
||||
|
||||
@@ -1148,8 +1112,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
readAnnotations.readAssociations(info, this);
|
||||
|
||||
readXml(desc);
|
||||
|
||||
if (EntityType.SQL == desc.getEntityType()) {
|
||||
desc.setBaseTable(null, null, null);
|
||||
}
|
||||
@@ -1271,127 +1233,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
}
|
||||
|
||||
private void readXml(DeployBeanDescriptor<?> deployDesc) {
|
||||
|
||||
List<Dnode> eXml = xmlConfig.findEntityXml(deployDesc.getFullName());
|
||||
readXmlRawSql(deployDesc, eXml);
|
||||
|
||||
Dnode entityXml = deployOrmXml.findEntityDeploymentXml(deployDesc.getFullName());
|
||||
|
||||
if (entityXml != null) {
|
||||
readXmlNamedQueries(deployDesc, entityXml);
|
||||
readXmlSql(deployDesc, entityXml);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read sql-select (FUTURE: additionally sql-insert, sql-update, sql-delete).
|
||||
* If found this entity bean is based on raw sql.
|
||||
*/
|
||||
private void readXmlSql(DeployBeanDescriptor<?> deployDesc, Dnode entityXml) {
|
||||
|
||||
List<Dnode> sqlSelectList = entityXml.findAll("sql-select", entityXml.getLevel() + 1);
|
||||
for (int i = 0; i < sqlSelectList.size(); i++) {
|
||||
Dnode sqlSelect = sqlSelectList.get(i);
|
||||
readSqlSelect(deployDesc, sqlSelect);
|
||||
}
|
||||
}
|
||||
|
||||
private String findContent(Dnode node, String nodeName) {
|
||||
Dnode found = node.find(nodeName);
|
||||
if (found != null) {
|
||||
return found.getNodeContent();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void readSqlSelect(DeployBeanDescriptor<?> deployDesc, Dnode sqlSelect) {
|
||||
|
||||
String name = sqlSelect.getStringAttr("name", "default");
|
||||
String extend = sqlSelect.getStringAttr("extend", null);
|
||||
String queryDebug = sqlSelect.getStringAttr("debug", null);
|
||||
boolean debug = (queryDebug != null && queryDebug.equalsIgnoreCase("true"));
|
||||
|
||||
// the raw sql select
|
||||
String query = findContent(sqlSelect, "query");
|
||||
String where = findContent(sqlSelect, "where");
|
||||
String having = findContent(sqlSelect, "having");
|
||||
String columnMapping = findContent(sqlSelect, "columnMapping");
|
||||
|
||||
DRawSqlMeta m = new DRawSqlMeta(name, extend, query, debug, where, having, columnMapping);
|
||||
|
||||
deployDesc.add(m);
|
||||
|
||||
}
|
||||
|
||||
private void readXmlRawSql(DeployBeanDescriptor<?> deployDesc, List<Dnode> entityXml) {
|
||||
|
||||
List<Dnode> rawSqlQueries = xmlConfig.find(entityXml, "raw-sql");
|
||||
for (int i = 0; i < rawSqlQueries.size(); i++) {
|
||||
Dnode rawSqlDnode = rawSqlQueries.get(i);
|
||||
String name = rawSqlDnode.getAttribute("name");
|
||||
if (isEmpty(name)) {
|
||||
throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " missing name attribute");
|
||||
}
|
||||
Dnode queryNode = rawSqlDnode.find("query");
|
||||
if (queryNode == null) {
|
||||
throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " missing query element");
|
||||
}
|
||||
String sql = queryNode.getNodeContent();
|
||||
if (isEmpty(sql)) {
|
||||
throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " has empty sql in the query element?");
|
||||
}
|
||||
|
||||
List<Dnode> columnMappings = rawSqlDnode.findAll("columnMapping", 1);
|
||||
|
||||
RawSqlBuilder rawSqlBuilder = RawSqlBuilder.parse(sql);
|
||||
for (int j = 0; j < columnMappings.size(); j++) {
|
||||
Dnode cm = columnMappings.get(j);
|
||||
String column = cm.getAttribute("column");
|
||||
String property = cm.getAttribute("property");
|
||||
rawSqlBuilder.columnMapping(column, property);
|
||||
}
|
||||
RawSql rawSql = rawSqlBuilder.create();
|
||||
|
||||
DeployNamedQuery namedQuery = new DeployNamedQuery(name, rawSql);
|
||||
deployDesc.add(namedQuery);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isEmpty(String s) {
|
||||
return s == null || s.trim().length() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read named queries for this bean type.
|
||||
*/
|
||||
private void readXmlNamedQueries(DeployBeanDescriptor<?> deployDesc, Dnode entityXml) {
|
||||
|
||||
// look for named-query...
|
||||
List<Dnode> namedQueries = entityXml.findAll("named-query", 1);
|
||||
|
||||
for (Dnode namedQueryXml : namedQueries) {
|
||||
|
||||
String name = namedQueryXml.getAttribute("name");
|
||||
Dnode query = namedQueryXml.find("query");
|
||||
if (query == null) {
|
||||
logger.warn("orm.xml " + deployDesc.getFullName() + " named-query missing query element?");
|
||||
|
||||
} else {
|
||||
String oql = query.getNodeContent();
|
||||
// TODO: QueryHints not read from xml yet
|
||||
if (name == null || oql == null) {
|
||||
logger.warn("orm.xml " + deployDesc.getFullName() + " named-query has no query content?");
|
||||
} else {
|
||||
// add the named query
|
||||
DeployNamedQuery q = new DeployNamedQuery(name, oql, null);
|
||||
deployDesc.add(q);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BeanPropertyInfoFactory createReflectionFactory() {
|
||||
|
||||
return new EnhanceBeanPropertyInfoFactory();
|
||||
|
||||
@@ -129,13 +129,20 @@ public final class BeanFkeyProperty implements ElPropertyValue {
|
||||
throw new RuntimeException("Not Supported or Expected");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssocMany() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false as not an AssocOne.
|
||||
*/
|
||||
@Override
|
||||
public boolean isAssocId() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssocProperty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6,11 +6,9 @@ import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.config.EncryptKey;
|
||||
import com.avaje.ebean.config.dbplatform.DbEncryptFunction;
|
||||
import com.avaje.ebean.config.dbplatform.DbType;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocMappingBuilder;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyMapping;
|
||||
import com.avaje.ebean.plugin.Property;
|
||||
import com.avaje.ebean.text.StringParser;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.core.InternString;
|
||||
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedWhenCreated;
|
||||
@@ -28,7 +26,10 @@ import com.avaje.ebeaninternal.server.text.json.WriteJson;
|
||||
import com.avaje.ebeaninternal.server.type.DataBind;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarTypeBoolean;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarTypeEnum;
|
||||
import com.avaje.ebeaninternal.util.ValueUtil;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocMappingBuilder;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyMapping;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyOptions;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
import com.avaje.ebeanservice.docstore.api.support.DocStructure;
|
||||
@@ -45,6 +46,7 @@ import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Description of a property of a bean. Includes its deployment information such
|
||||
@@ -231,11 +233,6 @@ public class BeanProperty implements ElPropertyValue, Property {
|
||||
*/
|
||||
final String dbComment;
|
||||
|
||||
/**
|
||||
* DB Constraint (typically check constraint on enum)
|
||||
*/
|
||||
final String dbConstraintExpression;
|
||||
|
||||
final DbEncryptFunction dbEncryptFunction;
|
||||
|
||||
int deployOrder;
|
||||
@@ -304,7 +301,6 @@ public class BeanProperty implements ElPropertyValue, Property {
|
||||
this.dbLength = deploy.getDbLength();
|
||||
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();
|
||||
@@ -414,7 +410,6 @@ public class BeanProperty implements ElPropertyValue, Property {
|
||||
this.dbLength = source.getDbLength();
|
||||
this.dbScale = source.getDbScale();
|
||||
this.dbColumnDefn = InternString.intern(source.getDbColumnDefn());
|
||||
this.dbConstraintExpression = InternString.intern(source.getDbConstraintExpression());
|
||||
this.dbColumnDefault = source.dbColumnDefault;
|
||||
|
||||
this.inherited = source.isInherited();
|
||||
@@ -893,6 +888,12 @@ public class BeanProperty implements ElPropertyValue, Property {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssocMany() {
|
||||
// Returns false - override in BeanPropertyAssocMany.
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isAssocId() {
|
||||
// Returns false - override in BeanPropertyAssocOne.
|
||||
return false;
|
||||
@@ -977,8 +978,11 @@ public class BeanProperty implements ElPropertyValue, Property {
|
||||
* For an Enum returns IN expression for the set of Enum values.
|
||||
* </p>
|
||||
*/
|
||||
public String getDbConstraintExpression() {
|
||||
return dbConstraintExpression;
|
||||
public Set<String> getDbCheckConstraintValues() {
|
||||
if (scalarType instanceof ScalarTypeEnum) {
|
||||
return ((ScalarTypeEnum) scalarType).getDbCheckConstraintValues();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -238,13 +238,13 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
/**
|
||||
* Return true if this is considered 'empty' from a save perspective.
|
||||
*/
|
||||
public boolean isEmptyBeanCollection(EntityBean bean, boolean insertedParent) {
|
||||
public boolean isSkipSaveBeanCollection(EntityBean bean, boolean insertedParent) {
|
||||
Object val = getValue(bean);
|
||||
if (val == null) {
|
||||
return true;
|
||||
}
|
||||
if ((val instanceof BeanCollection<?>)) {
|
||||
return ((BeanCollection<?>) val).isEmptyAndUntouched();
|
||||
return ((BeanCollection<?>) val).isSkipSave();
|
||||
}
|
||||
if (insertedParent) {
|
||||
// check 'vanilla' collection types
|
||||
@@ -549,6 +549,11 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssocMany() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssocId() {
|
||||
return true;
|
||||
|
||||
@@ -134,19 +134,28 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
this.relationshipProperty = relationshipProperty;
|
||||
}
|
||||
|
||||
BeanPropertyAssocMany<?> getRelationshipProperty() {
|
||||
return relationshipProperty;
|
||||
/**
|
||||
* Return true if this relationship needs to maintain/update L2 cache.
|
||||
*/
|
||||
boolean isCacheNotify() {
|
||||
return targetDescriptor.isBeanCaching() && relationshipProperty != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the L2 relationship cache for this property.
|
||||
*/
|
||||
void cacheClear() {
|
||||
if (targetDescriptor.isBeanCaching() && relationshipProperty != null) {
|
||||
if (isCacheNotify()) {
|
||||
targetDescriptor.cacheManyPropClear(relationshipProperty.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear part of the L2 relationship cache for this property.
|
||||
*/
|
||||
void cacheDelete(boolean clear, EntityBean bean, CacheChangeSet changeSet) {
|
||||
|
||||
if (targetDescriptor.isBeanCaching() && relationshipProperty != null) {
|
||||
if (isCacheNotify()) {
|
||||
if (clear) {
|
||||
changeSet.addManyClear(targetDescriptor, relationshipProperty.getName());
|
||||
} else {
|
||||
|
||||
+6
-18
@@ -83,33 +83,21 @@ public class ChainedBeanPersistListener implements BeanPersistListener {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean deleted(Object bean) {
|
||||
boolean notifyCluster = false;
|
||||
public void deleted(Object bean) {
|
||||
for (int i = 0; i < chain.length; i++) {
|
||||
if (chain[i].deleted(bean)) {
|
||||
notifyCluster = true;
|
||||
}
|
||||
chain[i].deleted(bean);
|
||||
}
|
||||
return notifyCluster;
|
||||
}
|
||||
|
||||
public boolean inserted(Object bean) {
|
||||
boolean notifyCluster = false;
|
||||
public void inserted(Object bean) {
|
||||
for (int i = 0; i < chain.length; i++) {
|
||||
if (chain[i].inserted(bean)) {
|
||||
notifyCluster = true;
|
||||
}
|
||||
chain[i].inserted(bean);
|
||||
}
|
||||
return notifyCluster;
|
||||
}
|
||||
|
||||
public boolean updated(Object bean, Set<String> updatedProperties) {
|
||||
boolean notifyCluster = false;
|
||||
public void updated(Object bean, Set<String> updatedProperties) {
|
||||
for (int i = 0; i < chain.length; i++) {
|
||||
if (chain[i].updated(bean, updatedProperties)) {
|
||||
notifyCluster = true;
|
||||
}
|
||||
chain[i].updated(bean, updatedProperties);
|
||||
}
|
||||
return notifyCluster;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
/**
|
||||
* A native query defined in deployment xml.
|
||||
*/
|
||||
public class DNativeQuery {
|
||||
|
||||
final String query;
|
||||
|
||||
public DNativeQuery(String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
public class DRawSqlColumnInfo {
|
||||
|
||||
final String name;
|
||||
|
||||
final String label;
|
||||
|
||||
final String propertyName;
|
||||
|
||||
final boolean scalarProperty;
|
||||
|
||||
public DRawSqlColumnInfo(String name, String label, String propertyName, boolean scalarProperty) {
|
||||
this.name = name;
|
||||
this.label = label;
|
||||
this.propertyName = propertyName;
|
||||
this.scalarProperty = scalarProperty;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public String getPropertyName() {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
public boolean isScalarProperty() {
|
||||
return scalarProperty;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "name:" + name + " label:" + label + " prop:" + propertyName;
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
/**
|
||||
* Meta data for a sql-select object.
|
||||
* <p>
|
||||
* Created from SqlSelect annotation or xml deployment.
|
||||
* </p>
|
||||
*/
|
||||
public class DRawSqlMeta {
|
||||
|
||||
private String name;
|
||||
private String tableAlias;
|
||||
private String extend;
|
||||
private String query;
|
||||
private boolean debug;
|
||||
private String where;
|
||||
private String having;
|
||||
private String columnMapping;
|
||||
|
||||
public DRawSqlMeta(String name, String extend, String query, boolean debug,
|
||||
String where, String having, String columnMapping) {
|
||||
|
||||
this.name = name;
|
||||
this.extend = extend;
|
||||
this.query = query;
|
||||
this.debug = debug;
|
||||
this.having = having;
|
||||
this.where = where;
|
||||
this.columnMapping = columnMapping;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setTableAlias(String tableAlias) {
|
||||
this.tableAlias = tableAlias;
|
||||
}
|
||||
|
||||
public String getTableAlias() {
|
||||
return tableAlias;
|
||||
}
|
||||
|
||||
public String getExtend() {
|
||||
return extend;
|
||||
}
|
||||
|
||||
public void setExtend(String extend) {
|
||||
this.extend = extend;
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public void setQuery(String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public boolean isDebug() {
|
||||
return debug;
|
||||
}
|
||||
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
public String getWhere() {
|
||||
return where;
|
||||
}
|
||||
|
||||
public void setWhere(String where) {
|
||||
this.where = where;
|
||||
}
|
||||
|
||||
public String getHaving() {
|
||||
return having;
|
||||
}
|
||||
|
||||
public void setHaving(String having) {
|
||||
this.having = having;
|
||||
}
|
||||
|
||||
public String getColumnMapping() {
|
||||
return columnMapping;
|
||||
}
|
||||
|
||||
public void setColumnMapping(String columnMapping) {
|
||||
this.columnMapping = columnMapping;
|
||||
}
|
||||
|
||||
public void extend(DRawSqlMeta parentQuery){
|
||||
extendQuery(parentQuery.getQuery());
|
||||
extendColumnMapping(parentQuery.getColumnMapping());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend sql from the parent query that this query 'extends'.
|
||||
*/
|
||||
private void extendQuery(String parentSql) {
|
||||
if (query == null) {
|
||||
query = parentSql;
|
||||
} else {
|
||||
query = parentSql + " " + query;
|
||||
}
|
||||
}
|
||||
|
||||
private void extendColumnMapping(String parentColumnMapping) {
|
||||
if (columnMapping == null){
|
||||
columnMapping = parentColumnMapping;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.query.CQueryPredicates;
|
||||
import com.avaje.ebeaninternal.server.query.SqlTree;
|
||||
import com.avaje.ebeaninternal.server.query.SqlTreeNode;
|
||||
import com.avaje.ebeaninternal.server.query.SqlTreeNodeRoot;
|
||||
import com.avaje.ebeaninternal.server.query.SqlTreeProperties;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a SqlSelect raw sql query.
|
||||
*/
|
||||
public class DRawSqlSelect {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DRawSqlSelect.class);
|
||||
|
||||
private final BeanDescriptor<?> desc;
|
||||
|
||||
private final DRawSqlColumnInfo[] selectColumns;
|
||||
|
||||
private final Map<String,DRawSqlColumnInfo> columnMap;
|
||||
|
||||
private final String preWhereExprSql;
|
||||
|
||||
private final boolean andWhereExpr;
|
||||
|
||||
private final String preHavingExprSql;
|
||||
|
||||
private final boolean andHavingExpr;
|
||||
|
||||
private final String orderBySql;
|
||||
|
||||
private final String whereClause;
|
||||
|
||||
private final String havingClause;
|
||||
|
||||
private final String query;
|
||||
|
||||
private final String columnMapping;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final SqlTree sqlTree;
|
||||
|
||||
private boolean withId;
|
||||
|
||||
private final String tableAlias;
|
||||
|
||||
public DRawSqlSelect(BeanDescriptor<?> desc, List<DRawSqlColumnInfo> selectColumns,
|
||||
String tableAlias, String preWhereExprSql, boolean andWhereExpr, String preHavingExprSql,
|
||||
boolean andHavingExpr, String orderBySql, DRawSqlMeta meta) {
|
||||
|
||||
this.desc = desc;
|
||||
this.tableAlias = tableAlias;
|
||||
this.selectColumns = selectColumns.toArray(new DRawSqlColumnInfo[selectColumns.size()]);
|
||||
this.preHavingExprSql = preHavingExprSql;
|
||||
this.preWhereExprSql = preWhereExprSql;
|
||||
this.andHavingExpr = andHavingExpr;
|
||||
this.andWhereExpr = andWhereExpr;
|
||||
this.orderBySql = orderBySql;
|
||||
this.name = meta.getName();
|
||||
this.whereClause = meta.getWhere();
|
||||
this.havingClause = meta.getHaving();
|
||||
this.query = meta.getQuery();
|
||||
this.columnMapping = meta.getColumnMapping();
|
||||
|
||||
this.sqlTree = initialise(desc);
|
||||
this.columnMap = createColumnMap(this.selectColumns);
|
||||
}
|
||||
|
||||
private Map<String,DRawSqlColumnInfo> createColumnMap(DRawSqlColumnInfo[] selectColumns) {
|
||||
|
||||
HashMap<String,DRawSqlColumnInfo> m = new HashMap<String,DRawSqlColumnInfo>();
|
||||
for (int i = 0; i < selectColumns.length; i++) {
|
||||
m.put(selectColumns[i].getPropertyName(), selectColumns[i]);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find foreign keys for assoc one types and build SqlTree.
|
||||
*/
|
||||
private SqlTree initialise(BeanDescriptor<?> owner){
|
||||
|
||||
try {
|
||||
return buildSqlTree(owner);
|
||||
|
||||
} catch (Exception e){
|
||||
String m = "Bug? initialising query "+name+" on "+owner;
|
||||
throw new RuntimeException(m, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the RawSqlColumnInfo given it's logical property name.
|
||||
*/
|
||||
public DRawSqlColumnInfo getRawSqlColumnInfo(String propertyName){
|
||||
return columnMap.get(propertyName);
|
||||
}
|
||||
|
||||
public String getTableAlias() {
|
||||
return tableAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the SqlTree for this query.
|
||||
* <p>
|
||||
* Most commonly this is just a simple list of properties - aka flat, but it
|
||||
* could be a real object graph tree for more complex scenarios.
|
||||
* </p>
|
||||
*/
|
||||
private SqlTree buildSqlTree(BeanDescriptor<?> desc){
|
||||
|
||||
SqlTreeProperties selectProps = new SqlTreeProperties();
|
||||
|
||||
for (int i = 0; i < selectColumns.length; i++) {
|
||||
|
||||
DRawSqlColumnInfo columnInfo = selectColumns[i];
|
||||
String propName = columnInfo.getPropertyName();
|
||||
BeanProperty beanProperty = desc.getBeanProperty(propName);
|
||||
if (beanProperty != null) {
|
||||
if (beanProperty.isId()){
|
||||
if (i > 0){
|
||||
String m = "With "+desc+" query:"+name+" the ID is not the first column in the select. It must be...";
|
||||
throw new PersistenceException(m);
|
||||
} else {
|
||||
withId = true;
|
||||
}
|
||||
} else {
|
||||
selectProps.add(beanProperty);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
String m = "Mapping for " + desc.getFullName();
|
||||
m += " query["+name+"] column[" + columnInfo + "] index[" + i;
|
||||
m += "] not matched to bean property?";
|
||||
logger.error(m);
|
||||
}
|
||||
}
|
||||
|
||||
SqlTreeNode sqlRoot = new SqlTreeNodeRoot(desc, selectProps, withId);
|
||||
|
||||
return new SqlTree(desc.getName(), sqlRoot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the full SQL Select statement for the request.
|
||||
*/
|
||||
public String buildSql(String orderBy, CQueryPredicates predicates, OrmQueryRequest<?> request) {
|
||||
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(preWhereExprSql);
|
||||
sb.append(" ");
|
||||
|
||||
String dynamicWhere = null;
|
||||
if (request.getQuery().getId() != null) {
|
||||
// need to convert this as well. This avoids the
|
||||
// assumption that id has its proper dbColumn assigned
|
||||
// which may change if using multiple raw sql statements
|
||||
// against the same bean.
|
||||
BeanDescriptor<?> descriptor = request.getBeanDescriptor();
|
||||
//FIXME: I think this is broken... needs to be logical
|
||||
// and then parsed for RawSqlSelect...
|
||||
dynamicWhere = descriptor.getIdBinderIdSql();
|
||||
}
|
||||
|
||||
String dbWhere = predicates.getDbWhere();
|
||||
if (dbWhere != null && dbWhere.length() > 0) {
|
||||
if (dynamicWhere == null) {
|
||||
dynamicWhere = dbWhere;
|
||||
} else {
|
||||
dynamicWhere += " and " + dbWhere;
|
||||
}
|
||||
}
|
||||
|
||||
if (dynamicWhere != null) {
|
||||
if (andWhereExpr) {
|
||||
sb.append(" and ");
|
||||
} else {
|
||||
sb.append(" where ");
|
||||
}
|
||||
sb.append(dynamicWhere);
|
||||
sb.append(" ");
|
||||
}
|
||||
|
||||
if (preHavingExprSql != null) {
|
||||
sb.append(preHavingExprSql);
|
||||
sb.append(" ");
|
||||
}
|
||||
|
||||
String dbHaving = predicates.getDbHaving();
|
||||
|
||||
if (dbHaving != null && dbHaving.length() > 0) {
|
||||
if (andHavingExpr) {
|
||||
sb.append(" and ");
|
||||
} else {
|
||||
sb.append(" having ");
|
||||
}
|
||||
sb.append(dbHaving);
|
||||
sb.append(" ");
|
||||
}
|
||||
|
||||
if (orderBy != null) {
|
||||
sb.append(" order by ").append(orderBy);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getOrderBy(CQueryPredicates predicates) {
|
||||
String orderBy = predicates.getDbOrderBy();
|
||||
if (orderBy != null) {
|
||||
return orderBy;
|
||||
} else {
|
||||
return orderBySql;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public SqlTree getSqlTree() {
|
||||
return sqlTree;
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public String getColumnMapping() {
|
||||
return columnMapping;
|
||||
}
|
||||
|
||||
public String getWhereClause() {
|
||||
return whereClause;
|
||||
}
|
||||
|
||||
public String getHavingClause() {
|
||||
return havingClause;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return Arrays.toString(selectColumns);
|
||||
}
|
||||
|
||||
public BeanDescriptor<?> getBeanDescriptor() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public DeployParser createDeployPropertyParser() {
|
||||
return new DeployPropertyParserRawSql(this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,339 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.config.NamingConvention;
|
||||
import com.avaje.ebeaninternal.server.querydefn.SimpleTextParser;
|
||||
|
||||
/**
|
||||
* Parses sql-select queries to try and determine the location where WHERE and HAVING
|
||||
* clauses can be added dynamically to the sql.
|
||||
*/
|
||||
public class DRawSqlSelectBuilder {
|
||||
|
||||
public static final String $_AND_HAVING = "${andHaving}";
|
||||
|
||||
public static final String $_HAVING = "${having}";
|
||||
|
||||
public static final String $_AND_WHERE = "${andWhere}";
|
||||
|
||||
public static final String $_WHERE = "${where}";
|
||||
|
||||
private static final String ORDER_BY = "order by";
|
||||
|
||||
private final BeanDescriptor<?> desc;
|
||||
|
||||
private final NamingConvention namingConvention;
|
||||
|
||||
private final DRawSqlMeta meta;
|
||||
|
||||
private final boolean debug;
|
||||
|
||||
private String sql;
|
||||
|
||||
private final SimpleTextParser textParser;
|
||||
|
||||
private int placeHolderWhere;
|
||||
private int placeHolderAndWhere;
|
||||
private int placeHolderHaving;
|
||||
private int placeHolderAndHaving;
|
||||
private final boolean hasPlaceHolders;
|
||||
|
||||
private int selectPos = -1;
|
||||
private int fromPos = -1;
|
||||
private int wherePos = -1;
|
||||
private int groupByPos = -1;
|
||||
private int havingPos = -1;
|
||||
private int orderByPos = -1;
|
||||
|
||||
private boolean whereExprAnd;
|
||||
private int whereExprPos = -1;
|
||||
private boolean havingExprAnd;
|
||||
private int havingExprPos = -1;
|
||||
|
||||
private final String tableAlias;
|
||||
|
||||
public DRawSqlSelectBuilder(NamingConvention namingConvention, BeanDescriptor<?> desc, DRawSqlMeta sqlSelectMeta) {
|
||||
|
||||
this.namingConvention = namingConvention;
|
||||
this.desc = desc;
|
||||
this.tableAlias = sqlSelectMeta.getTableAlias();
|
||||
this.meta = sqlSelectMeta;
|
||||
this.debug = sqlSelectMeta.isDebug();
|
||||
this.sql = sqlSelectMeta.getQuery().trim();
|
||||
this.hasPlaceHolders = findAndRemovePlaceHolders();
|
||||
this.textParser = new SimpleTextParser(this.sql);
|
||||
}
|
||||
|
||||
protected NamingConvention getNamingConvention() {
|
||||
return namingConvention;
|
||||
}
|
||||
|
||||
protected BeanDescriptor<?> getBeanDescriptor() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
protected boolean isDebug() {
|
||||
return debug;
|
||||
}
|
||||
|
||||
protected void debug(String msg) {
|
||||
if (debug) {
|
||||
System.out.println("debug> " + msg);
|
||||
}
|
||||
}
|
||||
|
||||
public DeployNamedQuery parse() {
|
||||
|
||||
if (debug) {
|
||||
debug("");
|
||||
debug("Parsing sql-select in " + getErrName());
|
||||
}
|
||||
|
||||
if (!hasPlaceHolders()) {
|
||||
// parse the sql for the keywords...
|
||||
// select, from, where, having, group by, order by
|
||||
parseSqlFindKeywords(true);
|
||||
}
|
||||
|
||||
List<DRawSqlColumnInfo> selectColumns = findSelectColumns(meta.getColumnMapping());
|
||||
whereExprPos = findWhereExprPosition();
|
||||
havingExprPos = findHavingExprPosition();
|
||||
|
||||
String preWhereExprSql = removeWhitespace(findPreWhereExprSql());
|
||||
String preHavingExprSql = removeWhitespace(findPreHavingExprSql());
|
||||
|
||||
preWhereExprSql = trimSelectKeyword(preWhereExprSql);
|
||||
|
||||
String orderBySql = findOrderBySql();
|
||||
|
||||
DRawSqlSelect rawSqlSelect = new DRawSqlSelect(desc, selectColumns, tableAlias, preWhereExprSql,
|
||||
whereExprAnd, preHavingExprSql, havingExprAnd, orderBySql, meta);
|
||||
|
||||
return new DeployNamedQuery(rawSqlSelect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and remove the known place holders such as ${where}.
|
||||
*/
|
||||
private boolean findAndRemovePlaceHolders() {
|
||||
placeHolderWhere = removePlaceHolder($_WHERE);
|
||||
placeHolderAndWhere = removePlaceHolder($_AND_WHERE);
|
||||
placeHolderHaving = removePlaceHolder($_HAVING);
|
||||
placeHolderAndHaving = removePlaceHolder($_AND_HAVING);
|
||||
return hasPlaceHolders();
|
||||
}
|
||||
|
||||
private int removePlaceHolder(String placeHolder) {
|
||||
int pos = sql.indexOf(placeHolder);
|
||||
if (pos > -1) {
|
||||
int after = pos + placeHolder.length() + 1;
|
||||
if (after > sql.length()) {
|
||||
sql = sql.substring(0, pos);
|
||||
} else {
|
||||
sql = sql.substring(0, pos) + sql.substring(after);
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
private boolean hasPlaceHolders() {
|
||||
return placeHolderWhere > -1 || placeHolderAndWhere > -1 || placeHolderHaving > -1 || placeHolderAndHaving > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim off the select keyword (to support row_number() limit function).
|
||||
*/
|
||||
private String trimSelectKeyword(String preWhereExprSql) {
|
||||
|
||||
if (preWhereExprSql.length() < 7){
|
||||
throw new RuntimeException("Expecting at least 7 chars in ["+preWhereExprSql+"]");
|
||||
}
|
||||
|
||||
String select = preWhereExprSql.substring(0, 7);
|
||||
if (!select.equalsIgnoreCase("select ")){
|
||||
throw new RuntimeException("Expecting ["+preWhereExprSql+"] to start with \"select\"");
|
||||
}
|
||||
return preWhereExprSql.substring(7);
|
||||
}
|
||||
|
||||
|
||||
private String findOrderBySql() {
|
||||
if (orderByPos > -1) {
|
||||
int pos = orderByPos + ORDER_BY.length();
|
||||
return sql.substring(pos);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String findPreHavingExprSql() {
|
||||
if (havingExprPos > whereExprPos) {
|
||||
// an order by clause follows...
|
||||
return sql.substring(whereExprPos, havingExprPos - 1);
|
||||
}
|
||||
if (whereExprPos > -1) {
|
||||
// the rest of the sql...
|
||||
return sql.substring(whereExprPos);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String findPreWhereExprSql() {
|
||||
if (whereExprPos > -1) {
|
||||
return sql.substring(0, whereExprPos - 1);
|
||||
} else {
|
||||
return sql;
|
||||
}
|
||||
}
|
||||
|
||||
protected String getErrName() {
|
||||
return "entity[" + desc.getFullName() + "] query[" + meta.getName() + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the columns in the select clause including the table alias' and
|
||||
* column alias.
|
||||
*/
|
||||
private List<DRawSqlColumnInfo> findSelectColumns(String selectClause) {
|
||||
|
||||
if (selectClause == null || selectClause.trim().length() == 0) {
|
||||
if (hasPlaceHolders) {
|
||||
if (debug) {
|
||||
debug("... No explicit ColumnMapping, so parse the sql looking for SELECT and FROM keywords.");
|
||||
}
|
||||
parseSqlFindKeywords(false);
|
||||
}
|
||||
if (selectPos == -1 || fromPos == -1) {
|
||||
String msg = "Error in [" + getErrName() + "] parsing sql looking ";
|
||||
msg += "for SELECT and FROM keywords.";
|
||||
msg += " select:" + selectPos + " from:" + fromPos;
|
||||
msg += ". You could use an explicit columnMapping to bypass this error.";
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
selectPos += "select".length();
|
||||
selectClause = sql.substring(selectPos, fromPos);
|
||||
}
|
||||
|
||||
selectClause = selectClause.trim();
|
||||
if (debug) {
|
||||
debug("ColumnMapping ... [" + selectClause + "]");
|
||||
}
|
||||
|
||||
return new DRawSqlSelectColumnsParser(this,selectClause).parse();
|
||||
}
|
||||
|
||||
private void parseSqlFindKeywords(boolean allKeywords) {
|
||||
|
||||
debug("Parsing query looking for SELECT...");
|
||||
selectPos = textParser.findWordLower("select");
|
||||
if (selectPos == -1) {
|
||||
String msg = "Error in "+getErrName()+" parsing sql, can not find SELECT keyword in:";
|
||||
throw new RuntimeException(msg + sql);
|
||||
}
|
||||
debug("Parsing query looking for FROM... SELECT found at " + selectPos);
|
||||
fromPos = textParser.findWordLower("from");
|
||||
if (fromPos == -1) {
|
||||
String msg = "Error in "+getErrName()+" parsing sql, can not find FROM keyword in:";
|
||||
throw new RuntimeException(msg + sql);
|
||||
}
|
||||
|
||||
if (!allKeywords) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug("Parsing query looking for WHERE... FROM found at " + fromPos);
|
||||
wherePos = textParser.findWordLower("where");
|
||||
if (wherePos == -1) {
|
||||
debug("Parsing query looking for GROUP... no WHERE found");
|
||||
groupByPos = textParser.findWordLower("group", fromPos + 5);
|
||||
} else {
|
||||
debug("Parsing query looking for GROUP... WHERE found at " + wherePos);
|
||||
groupByPos = textParser.findWordLower("group");
|
||||
}
|
||||
if (groupByPos > -1) {
|
||||
debug("Parsing query looking for HAVING... GROUP found at " + groupByPos);
|
||||
havingPos = textParser.findWordLower("having");
|
||||
}
|
||||
|
||||
int startOrderBy = havingPos;
|
||||
if (startOrderBy == -1) {
|
||||
startOrderBy = groupByPos;
|
||||
}
|
||||
if (startOrderBy == -1) {
|
||||
startOrderBy = wherePos;
|
||||
}
|
||||
if (startOrderBy == -1) {
|
||||
startOrderBy = fromPos;
|
||||
}
|
||||
|
||||
debug("Parsing query looking for ORDER... starting at " + startOrderBy);
|
||||
orderByPos = textParser.findWordLower("order", startOrderBy);
|
||||
}
|
||||
|
||||
private int findWhereExprPosition() {
|
||||
if (hasPlaceHolders) {
|
||||
if (placeHolderWhere > -1) {
|
||||
return placeHolderWhere;
|
||||
} else {
|
||||
whereExprAnd = true;
|
||||
return placeHolderAndWhere;
|
||||
}
|
||||
}
|
||||
whereExprAnd = wherePos > 0;
|
||||
if (groupByPos > 0) {
|
||||
return groupByPos;
|
||||
}
|
||||
if (havingPos > 0) {
|
||||
return havingPos;
|
||||
}
|
||||
if (orderByPos > 0) {
|
||||
return orderByPos;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int findHavingExprPosition() {
|
||||
if (hasPlaceHolders) {
|
||||
if (placeHolderHaving > -1) {
|
||||
return placeHolderHaving;
|
||||
} else {
|
||||
havingExprAnd = true;
|
||||
return placeHolderAndHaving;
|
||||
}
|
||||
}
|
||||
havingExprAnd = havingPos > 0;
|
||||
if (orderByPos > 0) {
|
||||
return orderByPos;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private String removeWhitespace(String sql) {
|
||||
if (sql == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
boolean removeWhitespace = false;
|
||||
|
||||
int length = sql.length();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = sql.charAt(i);
|
||||
if (removeWhitespace) {
|
||||
if (!Character.isWhitespace(c)) {
|
||||
sb.append(c);
|
||||
removeWhitespace = false;
|
||||
}
|
||||
} else {
|
||||
if (c == '\r' || c == '\n') {
|
||||
sb.append('\n');
|
||||
removeWhitespace = true;
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,230 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.config.NamingConvention;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Parses columnMapping (select clause) mapping columns to bean properties.
|
||||
*/
|
||||
public final class DRawSqlSelectColumnsParser {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DRawSqlSelectColumnsParser.class);
|
||||
|
||||
/**
|
||||
* Description of how the match was made.
|
||||
*/
|
||||
private String matchDescription;
|
||||
|
||||
/**
|
||||
* The actual column string used in search for a matching property.
|
||||
* <p>
|
||||
* This has table alias' and quoted identifiers removed.
|
||||
* </p>
|
||||
*/
|
||||
private String searchColumn;
|
||||
|
||||
private int columnIndex;
|
||||
|
||||
private int pos;
|
||||
|
||||
private final int end;
|
||||
|
||||
private final String sqlSelect;
|
||||
|
||||
private final List<DRawSqlColumnInfo> columns = new ArrayList<DRawSqlColumnInfo>();
|
||||
|
||||
private final BeanDescriptor<?> desc;
|
||||
|
||||
private final NamingConvention namingConvention;
|
||||
|
||||
private final DRawSqlSelectBuilder parent;
|
||||
|
||||
private final boolean debug;
|
||||
|
||||
public DRawSqlSelectColumnsParser(DRawSqlSelectBuilder parent, String sqlSelect) {
|
||||
this.parent = parent;
|
||||
this.debug = parent.isDebug();
|
||||
this.namingConvention = parent.getNamingConvention();
|
||||
this.desc = parent.getBeanDescriptor();
|
||||
this.sqlSelect = sqlSelect;
|
||||
this.end = sqlSelect.length();
|
||||
}
|
||||
|
||||
public List<DRawSqlColumnInfo> parse() {
|
||||
while (pos <= end) {
|
||||
nextColumnInfo();
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
|
||||
private void nextColumnInfo() {
|
||||
int start = pos;
|
||||
nextComma();
|
||||
String colInfo = sqlSelect.substring(start, pos);
|
||||
pos++;
|
||||
colInfo = colInfo.trim();
|
||||
int secLastSpace = -1;
|
||||
int lastSpace = colInfo.lastIndexOf(' ');
|
||||
if (lastSpace > -1) {
|
||||
secLastSpace = colInfo.lastIndexOf(' ', lastSpace - 1);
|
||||
}
|
||||
String colName;
|
||||
String colLabel;
|
||||
if (lastSpace == -1) {
|
||||
// no column alias
|
||||
colName = colInfo;
|
||||
colLabel = colName;
|
||||
} else if (secLastSpace == -1) {
|
||||
// no 'as' keyword
|
||||
colName = colInfo.substring(0, lastSpace);
|
||||
colLabel = colInfo.substring(lastSpace + 1);
|
||||
if (colName.equals("")) {
|
||||
colName = colLabel;
|
||||
}
|
||||
} else {
|
||||
// check for as keyword
|
||||
String expectedAs = colInfo.substring(secLastSpace + 1, lastSpace);
|
||||
if (expectedAs.toLowerCase().equals("as")) {
|
||||
colName = colInfo.substring(0, secLastSpace);
|
||||
colLabel = colInfo.substring(lastSpace + 1);
|
||||
} else {
|
||||
String msg = "Error in " + parent.getErrName() + ". ";
|
||||
msg += "Expected \"AS\" keyword but got [" + expectedAs + "] in select clause ["
|
||||
+ colInfo + "]";
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BeanProperty prop = findProperty(colLabel);
|
||||
if (prop == null) {
|
||||
if (debug) {
|
||||
String msg = "ColumnMapping ... idx[" + columnIndex
|
||||
+ "] ERROR, no property found to match... column[" + colName + "] label[" + colLabel
|
||||
+ "] search[" + searchColumn + "]";
|
||||
parent.debug(msg);
|
||||
}
|
||||
String msg = "Error in " + parent.getErrName() + ". ";
|
||||
msg += "No matching bean property for column[" + colName + "] columnLabel[" + colLabel
|
||||
+ "] idx[" + columnIndex + "] using search[" + searchColumn + "] found?";
|
||||
logger.error(msg);
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
String msg = null;
|
||||
if (debug || logger.isDebugEnabled()) {
|
||||
msg = "ColumnMapping ... idx[" + columnIndex + "] match column[" + colName
|
||||
+ "] label[" + colLabel + "] to property[" + prop + "]"
|
||||
+ matchDescription;
|
||||
}
|
||||
if (debug) {
|
||||
parent.debug(msg);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(msg);
|
||||
}
|
||||
|
||||
DRawSqlColumnInfo info = new DRawSqlColumnInfo(colName, colLabel, prop.getName(), prop.isScalar());
|
||||
columns.add(info);
|
||||
columnIndex++;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private String removeQuotedIdentifierChars(String columnLabel) {
|
||||
|
||||
char c = columnLabel.charAt(0);
|
||||
if (Character.isJavaIdentifierStart(c)) {
|
||||
return columnLabel;
|
||||
}
|
||||
|
||||
// trim off first and last character
|
||||
String result = columnLabel.substring(1, columnLabel.length() - 1);
|
||||
|
||||
String msg = "sql-select trimming quoted identifier from["
|
||||
+ columnLabel + "] to[" + result+ "]";
|
||||
logger.debug(msg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the property to match against the given resultSet column.
|
||||
*/
|
||||
private BeanProperty findProperty(String column) {
|
||||
|
||||
searchColumn = column;
|
||||
int dotPos = searchColumn.indexOf(".");
|
||||
if (dotPos > -1) {
|
||||
searchColumn = searchColumn.substring(dotPos + 1);
|
||||
}
|
||||
|
||||
searchColumn = removeQuotedIdentifierChars(searchColumn);
|
||||
|
||||
BeanProperty matchingProp = desc.getBeanProperty(searchColumn);
|
||||
if (matchingProp != null) {
|
||||
matchDescription = "";
|
||||
return matchingProp;
|
||||
}
|
||||
|
||||
// convert columnName using the namingConvention
|
||||
String propertyName = namingConvention.getPropertyFromColumn(desc.getBeanType(), searchColumn);
|
||||
matchingProp = desc.getBeanProperty(propertyName);
|
||||
if (matchingProp != null) {
|
||||
matchDescription = " ... using naming convention";
|
||||
return matchingProp;
|
||||
}
|
||||
|
||||
matchDescription = " ... by linear search";
|
||||
|
||||
// search all properties matching against the property db column
|
||||
BeanProperty[] propertiesBase = desc.propertiesBaseScalar();
|
||||
for (int i = 0; i < propertiesBase.length; i++) {
|
||||
BeanProperty prop = propertiesBase[i];
|
||||
if (isMatch(prop, searchColumn)) {
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
|
||||
BeanProperty idProp = desc.getIdProperty();
|
||||
if (idProp != null) {
|
||||
if (isMatch(idProp, searchColumn)) {
|
||||
return idProp;
|
||||
}
|
||||
}
|
||||
|
||||
BeanPropertyAssocOne<?>[] propertiesAssocOne = desc.propertiesOne();
|
||||
for (int i = 0; i < propertiesAssocOne.length; i++) {
|
||||
BeanProperty prop = propertiesAssocOne[i];
|
||||
if (isMatch(prop, searchColumn)) {
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isMatch(BeanProperty prop, String columnLabel) {
|
||||
return columnLabel.equalsIgnoreCase(prop.getDbColumn()) || columnLabel.equalsIgnoreCase(prop.getName());
|
||||
}
|
||||
|
||||
private void nextComma() {
|
||||
boolean inQuote = false;
|
||||
while (pos < end) {
|
||||
char c = sqlSelect.charAt(pos);
|
||||
if (c == '\'') {
|
||||
inQuote = !inQuote;
|
||||
} else if (!inQuote && c == ',') {
|
||||
return;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.QueryHint;
|
||||
|
||||
import com.avaje.ebean.RawSql;
|
||||
|
||||
public class DeployNamedQuery {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String query;
|
||||
|
||||
private final QueryHint[] hints;
|
||||
|
||||
private final DRawSqlSelect sqlSelect;
|
||||
|
||||
private final RawSql rawSql;
|
||||
|
||||
public DeployNamedQuery(NamedQuery namedQuery) {
|
||||
this.name = namedQuery.name();
|
||||
this.query = namedQuery.query();
|
||||
this.hints = namedQuery.hints();
|
||||
this.sqlSelect = null;
|
||||
this.rawSql = null;
|
||||
}
|
||||
|
||||
public DeployNamedQuery(String name, String query, QueryHint[] hints) {
|
||||
this.name = name;
|
||||
this.query = query;
|
||||
this.hints = hints;
|
||||
this.sqlSelect = null;
|
||||
this.rawSql = null;
|
||||
}
|
||||
|
||||
public DeployNamedQuery(String name, RawSql rawSql) {
|
||||
this.name = name;
|
||||
this.query = null;
|
||||
this.hints = null;
|
||||
this.sqlSelect = null;
|
||||
this.rawSql = rawSql;
|
||||
}
|
||||
|
||||
public DeployNamedQuery(DRawSqlSelect sqlSelect) {
|
||||
this.name = sqlSelect.getName();
|
||||
this.query = null;
|
||||
this.hints = null;
|
||||
this.sqlSelect = sqlSelect;
|
||||
this.rawSql = null;
|
||||
}
|
||||
|
||||
public boolean isRawSql() {
|
||||
return rawSql != null;
|
||||
}
|
||||
|
||||
public boolean isSqlSelect() {
|
||||
return sqlSelect != null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public QueryHint[] getHints() {
|
||||
return hints;
|
||||
}
|
||||
|
||||
public RawSql getRawSql() {
|
||||
return rawSql;
|
||||
}
|
||||
|
||||
public DRawSqlSelect getSqlSelect() {
|
||||
return sqlSelect;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebean.annotation.NamedUpdate;
|
||||
|
||||
/**
|
||||
* Deployment information for a named update.
|
||||
*/
|
||||
public class DeployNamedUpdate {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String updateStatement;
|
||||
|
||||
private final boolean notifyCache;
|
||||
|
||||
private String sqlUpdateStatement;
|
||||
|
||||
public DeployNamedUpdate(NamedUpdate update) {
|
||||
this.name = update.name();
|
||||
this.updateStatement = update.update();
|
||||
this.notifyCache = update.notifyCache();
|
||||
}
|
||||
|
||||
public void initialise(DeployUpdateParser parser) {
|
||||
sqlUpdateStatement = parser.parse(updateStatement);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getSqlUpdateStatement() {
|
||||
return sqlUpdateStatement;
|
||||
}
|
||||
|
||||
public boolean isNotifyCache() {
|
||||
return notifyCache;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebeaninternal.server.lib.util.Dnode;
|
||||
import com.avaje.ebeaninternal.server.lib.util.DnodeReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Controls the creation and caching of BeanManager's, BeanDescriptors,
|
||||
* BeanTable etc for both beans and tables(MapBeans).
|
||||
* <p>
|
||||
* Also supports some other deployment features such as type conversion.
|
||||
* </p>
|
||||
*/
|
||||
public class DeployOrmXml {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DeployOrmXml.class);
|
||||
|
||||
private final HashMap<String, DNativeQuery> nativeQueryCache;
|
||||
|
||||
private final ArrayList<Dnode> ormXmlList;
|
||||
|
||||
public DeployOrmXml() {
|
||||
this.nativeQueryCache = new HashMap<String, DNativeQuery>();
|
||||
this.ormXmlList = findAllOrmXml();
|
||||
|
||||
initialiseNativeQueries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all the native queries in ALL orm xml deployment.
|
||||
*/
|
||||
private void initialiseNativeQueries() {
|
||||
for (Dnode ormXml : ormXmlList) {
|
||||
initialiseNativeQueries(ormXml);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the native queries in this particular orm xml deployment.
|
||||
*/
|
||||
private void initialiseNativeQueries(Dnode ormXml) {
|
||||
|
||||
Dnode entityMappings = ormXml.find("entity-mappings");
|
||||
if (entityMappings != null) {
|
||||
List<Dnode> nq = entityMappings.findAll("named-native-query", 1);
|
||||
for (int i = 0; i < nq.size(); i++) {
|
||||
Dnode nqNode = nq.get(i);
|
||||
Dnode nqQueryNode = nqNode.find("query");
|
||||
if (nqQueryNode != null) {
|
||||
String queryContent = nqQueryNode.getNodeContent();
|
||||
String queryName = nqNode.getAttribute("name");
|
||||
|
||||
if (queryName != null && queryContent != null) {
|
||||
DNativeQuery query = new DNativeQuery(queryContent);
|
||||
nativeQueryCache.put(queryName, query);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a native named query.
|
||||
* <p>
|
||||
* These are loaded from the orm.xml deployment file.
|
||||
* </p>
|
||||
*/
|
||||
public DNativeQuery getNativeQuery(String name) {
|
||||
return nativeQueryCache.get(name);
|
||||
}
|
||||
|
||||
private ArrayList<Dnode> findAllOrmXml() {
|
||||
|
||||
ArrayList<Dnode> ormXmlList = new ArrayList<Dnode>();
|
||||
|
||||
|
||||
String defaultFile = "orm.xml";
|
||||
readOrmXml(defaultFile, ormXmlList);
|
||||
|
||||
if (!ormXmlList.isEmpty()) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Dnode ox : ormXmlList) {
|
||||
sb.append(", ").append(ox.getAttribute("ebean.filename"));
|
||||
}
|
||||
String loadedFiles = sb.toString().substring(2);
|
||||
logger.info("Deployment xml [" + loadedFiles + "] loaded.");
|
||||
}
|
||||
|
||||
return ormXmlList;
|
||||
}
|
||||
|
||||
private void readOrmXml(String ormXmlName, ArrayList<Dnode> ormXmlList) {
|
||||
|
||||
try {
|
||||
Dnode ormXml = readOrmXmlFromClasspath(ormXmlName);
|
||||
if (ormXml != null) {
|
||||
ormXml.setAttribute("ebean.filename", ormXmlName);
|
||||
ormXmlList.add(ormXml);
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
logger.error("error reading orm xml deployment " + ormXmlName, e);
|
||||
}
|
||||
}
|
||||
|
||||
private Dnode readOrmXmlFromClasspath(String ormXmlName) throws IOException {
|
||||
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(ormXmlName);
|
||||
if (is == null) {
|
||||
return null;
|
||||
} else {
|
||||
return readOrmXml(is);
|
||||
}
|
||||
}
|
||||
|
||||
private Dnode readOrmXml(InputStream in) throws IOException {
|
||||
DnodeReader reader = new DnodeReader();
|
||||
Dnode ormXml = reader.parseXml(in);
|
||||
in.close();
|
||||
return ormXml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the deployment xml for a given entity. This will return null if no
|
||||
* matching deployment xml is found for this entity.
|
||||
* <p>
|
||||
* This searches all the ormXml files and returns the first match.
|
||||
* </p>
|
||||
*/
|
||||
public Dnode findEntityDeploymentXml(String className) {
|
||||
|
||||
for (Dnode ormXml : ormXmlList) {
|
||||
Dnode entityMappings = ormXml.find("entity-mappings");
|
||||
|
||||
List<Dnode> entities = entityMappings.findAll("entity", "class", className, 1);
|
||||
if (entities.size() == 1) {
|
||||
return entities.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Converts logical property names to database columns for the raw sql.
|
||||
* <p>
|
||||
* This is used in building the where and having clauses for SqlSelect queries.
|
||||
* </p>
|
||||
*/
|
||||
public final class DeployPropertyParserRawSql extends DeployParser {
|
||||
|
||||
private final DRawSqlSelect rawSqlSelect;
|
||||
|
||||
public DeployPropertyParserRawSql(DRawSqlSelect rawSqlSelect) {
|
||||
this.rawSqlSelect = rawSqlSelect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null for raw sql queries.
|
||||
*/
|
||||
public Set<String> getIncludes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String convertWord() {
|
||||
String r = getDeployWord(word);
|
||||
return r == null ? word : r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDeployWord(String expression) {
|
||||
DRawSqlColumnInfo columnInfo = rawSqlSelect.getRawSqlColumnInfo(expression);
|
||||
if (columnInfo == null) {
|
||||
return null;
|
||||
} else {
|
||||
return columnInfo.getName();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.deploy;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
@@ -88,6 +89,25 @@ public class InheritInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append check constraint values for the entire inheritance hierarchy.
|
||||
*/
|
||||
public void appendCheckConstraintValues(final String propertyName, final Set<String> checkConstraintValues) {
|
||||
|
||||
visitChildren(new InheritInfoVisitor() {
|
||||
@Override
|
||||
public void visit(InheritInfo inheritInfo) {
|
||||
BeanProperty prop = inheritInfo.desc().getBeanProperty(propertyName);
|
||||
if (prop != null) {
|
||||
Set<String> values = prop.getDbCheckConstraintValues();
|
||||
if (values != null) {
|
||||
checkConstraintValues.addAll(values);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if anything in the inheritance hierarchy has a relationship with a save cascade on
|
||||
* it.
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<!ELEMENT ebean (table | iudextender | findextender | datasource | primarykeys | property)* >
|
||||
<!ELEMENT table EMPTY >
|
||||
<!ATTLIST table name CDATA #REQUIRED>
|
||||
<!ELEMENT iudextender EMPTY >
|
||||
<!ATTLIST iudextender classname CDATA #REQUIRED>
|
||||
<!ELEMENT findextender EMPTY >
|
||||
<!ATTLIST findextender classname CDATA #REQUIRED>
|
||||
<!ELEMENT datasource EMPTY >
|
||||
<!ATTLIST datasource name CDATA #REQUIRED>
|
||||
<!ELEMENT primarykeys EMPTY >
|
||||
<!ATTLIST primarykeys keys CDATA #REQUIRED>
|
||||
<!ELEMENT property EMPTY >
|
||||
<!ATTLIST property name CDATA #REQUIRED
|
||||
datatype CDATA #REQUIRED
|
||||
deploydatatype CDATA #IMPLIED
|
||||
deployname CDATA #IMPLIED >
|
||||
@@ -1,13 +1,12 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import com.avaje.ebean.annotation.Cache;
|
||||
import com.avaje.ebeaninternal.api.ConcurrencyMode;
|
||||
import com.avaje.ebean.annotation.DocStore;
|
||||
import com.avaje.ebean.annotation.DocStoreMode;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.TableName;
|
||||
import com.avaje.ebean.config.dbplatform.PlatformIdGenerator;
|
||||
import com.avaje.ebean.config.dbplatform.IdType;
|
||||
import com.avaje.ebean.config.dbplatform.PlatformIdGenerator;
|
||||
import com.avaje.ebean.event.BeanFindController;
|
||||
import com.avaje.ebean.event.BeanPersistController;
|
||||
import com.avaje.ebean.event.BeanPersistListener;
|
||||
@@ -16,6 +15,7 @@ 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.api.ConcurrencyMode;
|
||||
import com.avaje.ebeaninternal.server.core.CacheOptions;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
@@ -24,9 +24,6 @@ import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistListener;
|
||||
import com.avaje.ebeaninternal.server.deploy.ChainedBeanPostLoad;
|
||||
import com.avaje.ebeaninternal.server.deploy.ChainedBeanQueryAdapter;
|
||||
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueConstraint;
|
||||
import com.avaje.ebeaninternal.server.deploy.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 com.avaje.ebeaninternal.server.idgen.UuidIdGenerator;
|
||||
@@ -40,7 +37,6 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Describes Beans including their deployment information.
|
||||
@@ -72,12 +68,6 @@ public class DeployBeanDescriptor<T> {
|
||||
|
||||
private EntityType entityType;
|
||||
|
||||
private final Map<String, DeployNamedQuery> namedQueries = new LinkedHashMap<String, DeployNamedQuery>();
|
||||
|
||||
private final Map<String, DeployNamedUpdate> namedUpdates = new LinkedHashMap<String, DeployNamedUpdate>();
|
||||
|
||||
private final Map<String, DRawSqlMeta> rawSqlMetas = new LinkedHashMap<String, DRawSqlMeta>();
|
||||
|
||||
private DeployBeanPropertyAssocOne<?> unidirectional;
|
||||
|
||||
/**
|
||||
@@ -309,32 +299,6 @@ public class DeployBeanDescriptor<T> {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Collection<DRawSqlMeta> getRawSqlMeta() {
|
||||
if (!processedRawSqlExtend) {
|
||||
rawSqlProcessExtend();
|
||||
processedRawSqlExtend = true;
|
||||
}
|
||||
return rawSqlMetas.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the "extend" attributes of raw SQL. Aka inherit the query and
|
||||
* column mapping.
|
||||
*/
|
||||
private void rawSqlProcessExtend() {
|
||||
|
||||
for (DRawSqlMeta rawSqlMeta : rawSqlMetas.values()) {
|
||||
String extend = rawSqlMeta.getExtend();
|
||||
if (extend != null) {
|
||||
DRawSqlMeta parentQuery = rawSqlMetas.get(extend);
|
||||
if (parentQuery == null) {
|
||||
throw new RuntimeException("parent query [" + extend + "] not found for sql-select " + rawSqlMeta.getName());
|
||||
}
|
||||
rawSqlMeta.extend(parentQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DeployBeanTable createDeployBeanTable() {
|
||||
|
||||
DeployBeanTable beanTable = new DeployBeanTable(getBeanType());
|
||||
@@ -380,32 +344,6 @@ public class DeployBeanDescriptor<T> {
|
||||
return sequenceAllocationSize;
|
||||
}
|
||||
|
||||
public void add(DRawSqlMeta rawSqlMeta) {
|
||||
rawSqlMetas.put(rawSqlMeta.getName(), rawSqlMeta);
|
||||
if ("default".equals(rawSqlMeta.getName())) {
|
||||
setEntityType(EntityType.SQL);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(DeployNamedUpdate namedUpdate) {
|
||||
namedUpdates.put(namedUpdate.getName(), namedUpdate);
|
||||
}
|
||||
|
||||
public void add(DeployNamedQuery namedQuery) {
|
||||
namedQueries.put(namedQuery.getName(), namedQuery);
|
||||
if ("default".equals(namedQuery.getName())) {
|
||||
setEntityType(EntityType.SQL);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, DeployNamedQuery> getNamedQueries() {
|
||||
return namedQueries;
|
||||
}
|
||||
|
||||
public Map<String, DeployNamedUpdate> getNamedUpdates() {
|
||||
return namedUpdates;
|
||||
}
|
||||
|
||||
public String[] getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@@ -393,17 +393,6 @@ public class DeployBeanProperty {
|
||||
}
|
||||
}
|
||||
|
||||
public String getDbConstraintExpression() {
|
||||
if (scalarType instanceof ScalarTypeEnum) {
|
||||
// create a check constraint for the enum
|
||||
ScalarTypeEnum etype = (ScalarTypeEnum) scalarType;
|
||||
|
||||
// check dbColName IN ('A', 'I', 'D')
|
||||
return "check (" + dbColumn + " in " + etype.getConstraintInValues() + ")";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the scalarType. This returns null for native JDBC types, otherwise
|
||||
* it is used to convert between logical types and jdbc types.
|
||||
|
||||
@@ -7,16 +7,12 @@ import com.avaje.ebean.annotation.Draftable;
|
||||
import com.avaje.ebean.annotation.DraftableElement;
|
||||
import com.avaje.ebean.annotation.History;
|
||||
import com.avaje.ebean.annotation.Index;
|
||||
import com.avaje.ebean.annotation.NamedUpdate;
|
||||
import com.avaje.ebean.annotation.NamedUpdates;
|
||||
import com.avaje.ebean.annotation.ReadAudit;
|
||||
import com.avaje.ebean.annotation.UpdateMode;
|
||||
import com.avaje.ebean.annotation.View;
|
||||
import com.avaje.ebean.config.TableName;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
|
||||
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueConstraint;
|
||||
import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -25,8 +21,6 @@ import javax.persistence.AttributeOverride;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
@@ -181,53 +175,10 @@ public class AnnotationClass extends AnnotationParser {
|
||||
descriptor.setUpdateChangesOnly(updateMode.updateChangesOnly());
|
||||
}
|
||||
|
||||
NamedQueries namedQueries = cls.getAnnotation(NamedQueries.class);
|
||||
if (namedQueries != null) {
|
||||
readNamedQueries(namedQueries);
|
||||
}
|
||||
NamedQuery namedQuery = cls.getAnnotation(NamedQuery.class);
|
||||
if (namedQuery != null) {
|
||||
readNamedQuery(namedQuery);
|
||||
}
|
||||
|
||||
NamedUpdates namedUpdates = cls.getAnnotation(NamedUpdates.class);
|
||||
if (namedUpdates != null) {
|
||||
readNamedUpdates(namedUpdates);
|
||||
}
|
||||
|
||||
NamedUpdate namedUpdate = cls.getAnnotation(NamedUpdate.class);
|
||||
if (namedUpdate != null) {
|
||||
readNamedUpdate(namedUpdate);
|
||||
}
|
||||
|
||||
Cache cache = cls.getAnnotation(Cache.class);
|
||||
if (cache != null && !disableL2Cache) {
|
||||
descriptor.setCache(cache);
|
||||
}
|
||||
}
|
||||
|
||||
private void readNamedQueries(NamedQueries namedQueries) {
|
||||
NamedQuery[] queries = namedQueries.value();
|
||||
for (int i = 0; i < queries.length; i++) {
|
||||
readNamedQuery(queries[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void readNamedQuery(NamedQuery namedQuery) {
|
||||
DeployNamedQuery q = new DeployNamedQuery(namedQuery);
|
||||
descriptor.add(q);
|
||||
}
|
||||
|
||||
private void readNamedUpdates(NamedUpdates updates) {
|
||||
NamedUpdate[] updateArray = updates.value();
|
||||
for (int i = 0; i < updateArray.length; i++) {
|
||||
readNamedUpdate(updateArray[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void readNamedUpdate(NamedUpdate update) {
|
||||
DeployNamedUpdate upd = new DeployNamedUpdate(update);
|
||||
descriptor.add(upd);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user