mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
241
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c2e9ccacd | ||
|
|
29aa395cd4 | ||
|
|
8160d1f01c | ||
|
|
6fd76ff697 | ||
|
|
249cc5df49 | ||
|
|
2646e8c27f | ||
|
|
3eaf47fd70 | ||
|
|
9d64352771 | ||
|
|
aa7325b249 | ||
|
|
2839434773 | ||
|
|
de9823b69b | ||
|
|
15cab24848 | ||
|
|
b87e360ac7 | ||
|
|
82e68d7756 | ||
|
|
0371043914 | ||
|
|
54b5684628 | ||
|
|
da95ec0c05 | ||
|
|
b5eec93995 | ||
|
|
fc26765a7e | ||
|
|
9a6d339449 | ||
|
|
5b09443cdf | ||
|
|
31093babe3 | ||
|
|
f8b9ca2034 | ||
|
|
c27749ae2d | ||
|
|
2c04430185 | ||
|
|
dcde47daf6 | ||
|
|
57056d7abd | ||
|
|
3409f264ec | ||
|
|
6a61614430 | ||
|
|
9734ae5787 | ||
|
|
b817d5ee4d | ||
|
|
0036473971 | ||
|
|
a020bb15a1 | ||
|
|
efa86043bf | ||
|
|
eb1ea5ddd2 | ||
|
|
a83a4fa1c4 | ||
|
|
fe71c7cc4e | ||
|
|
5522d56e8d | ||
|
|
7e995efb62 | ||
|
|
ef46bc86cc | ||
|
|
0e75c50be5 | ||
|
|
cd1cdb027f | ||
|
|
66703a3a29 | ||
|
|
56e65d7463 | ||
|
|
eb7277aa13 | ||
|
|
42f9e74d91 | ||
|
|
be24e11b91 | ||
|
|
42d3e9eb91 | ||
|
|
bb164a0763 | ||
|
|
3a50c264b1 | ||
|
|
0e7dc4209c | ||
|
|
72845acbad | ||
|
|
29d88d7de8 | ||
|
|
c81d329100 | ||
|
|
7c1c144af5 | ||
|
|
7809858634 | ||
|
|
bd3cb3c547 | ||
|
|
db25cb7bfb | ||
|
|
55a9ba7170 | ||
|
|
2df2224286 | ||
|
|
e1e8092144 | ||
|
|
4d4886d808 | ||
|
|
d11bb2c4e5 | ||
|
|
663b1bb4aa | ||
|
|
f3d26e32ec | ||
|
|
5f83584315 | ||
|
|
d28e3ee456 | ||
|
|
e1b7161b22 | ||
|
|
f63a3cb926 | ||
|
|
29c5a17202 | ||
|
|
340b100a1a | ||
|
|
80975acfbf | ||
|
|
95dab97b1b | ||
|
|
ce10dd79f9 | ||
|
|
3f12739e71 | ||
|
|
cd1ae20015 | ||
|
|
6d1e3a08e1 | ||
|
|
94e6fe90e5 | ||
|
|
9bbe99657a | ||
|
|
0faf9f54ea | ||
|
|
046a9b30b0 | ||
|
|
afb7679302 | ||
|
|
db35f0f5ed | ||
|
|
19756421ad | ||
|
|
1d678576f7 | ||
|
|
ded7d44974 | ||
|
|
c9497fd654 | ||
|
|
23a9cf9bb3 | ||
|
|
75395a8481 | ||
|
|
a161d0c01b | ||
|
|
1dd7720605 | ||
|
|
3d2f885e87 | ||
|
|
2b3e485beb | ||
|
|
d4be0718a8 | ||
|
|
6d0556a120 | ||
|
|
ec7e4e4935 | ||
|
|
471605619a | ||
|
|
02fe956056 | ||
|
|
db13034fec | ||
|
|
c2489beb82 | ||
|
|
9a424d7d6d | ||
|
|
9b74602617 | ||
|
|
5c99986476 | ||
|
|
7d38933e9d | ||
|
|
7668fb11a9 | ||
|
|
eac68166d9 | ||
|
|
cb0023345f | ||
|
|
f0f7feb125 | ||
|
|
c8e32253a5 | ||
|
|
01541bb208 | ||
|
|
2d44d06f36 | ||
|
|
fda930a162 | ||
|
|
5a6cdb9a86 | ||
|
|
5ef7fe8504 | ||
|
|
11586fb635 | ||
|
|
d555122067 | ||
|
|
b6d3dc3516 | ||
|
|
bbf54d6196 | ||
|
|
74c88ce511 | ||
|
|
309bf6a11a | ||
|
|
02b6ab0f74 | ||
|
|
0c41993ce7 | ||
|
|
d949c48245 | ||
|
|
93e603ca25 | ||
|
|
117d22fd67 | ||
|
|
f13de2c8a6 | ||
|
|
5b8ef8154a | ||
|
|
df892f4509 | ||
|
|
2755e6d1ec | ||
|
|
2024193da9 | ||
|
|
72788c7428 | ||
|
|
8789709fb8 | ||
|
|
588aaf9361 | ||
|
|
68649d0e98 | ||
|
|
f6047d3b73 | ||
|
|
629e096e28 | ||
|
|
c981944c8e | ||
|
|
1ba9f4e9de | ||
|
|
ce02e7ce70 | ||
|
|
1d9a2ed43a | ||
|
|
ae81ce5f47 | ||
|
|
9e42f98902 | ||
|
|
ada21729ec | ||
|
|
8b7d882b8a | ||
|
|
cf2d9df256 | ||
|
|
48b05eb358 | ||
|
|
0796fc0d88 | ||
|
|
df585644e9 | ||
|
|
ff3c5b3d82 | ||
|
|
1a03e84f0e | ||
|
|
49ab17cd34 | ||
|
|
9cd189034c | ||
|
|
ed566ca153 | ||
|
|
9159cec36c | ||
|
|
b58fc2df04 | ||
|
|
dd8607b072 | ||
|
|
a5a8765bd0 | ||
|
|
ae55b58438 | ||
|
|
671d082ad3 | ||
|
|
43e95d81f6 | ||
|
|
3a64cf6952 | ||
|
|
8d976f83a9 | ||
|
|
117737c790 | ||
|
|
085843339f | ||
|
|
bf33b3f96f | ||
|
|
e1d4159453 | ||
|
|
4f1f3cadf9 | ||
|
|
01e1a4642b | ||
|
|
3a75e1f66c | ||
|
|
47ac4c5c5b | ||
|
|
9c73309dfd | ||
|
|
bd3c99e67f | ||
|
|
5adbebaf6e | ||
|
|
6a9ab49ef0 | ||
|
|
4308a98a0a | ||
|
|
f86f1b2647 | ||
|
|
96cc8d7605 | ||
|
|
59537227b6 | ||
|
|
81c6d4601d | ||
|
|
e21839de91 | ||
|
|
a61db2ab35 | ||
|
|
bddfa3bc5f | ||
|
|
9ad460ecd8 | ||
|
|
50593a7f9d | ||
|
|
c088cfa339 | ||
|
|
e683e25a95 | ||
|
|
c7052c5bfa | ||
|
|
698ee2198d | ||
|
|
91c4eca528 | ||
|
|
81897bd54d | ||
|
|
2427630c07 | ||
|
|
3238c0def4 | ||
|
|
93f75151f0 | ||
|
|
32e9b48359 | ||
|
|
258e29d2f0 | ||
|
|
ec2f1f7243 | ||
|
|
5775985468 | ||
|
|
4788dd4ec9 | ||
|
|
ca7e5b79b8 | ||
|
|
90dfbcd7b3 | ||
|
|
6b39bad4da | ||
|
|
25dea0b5b0 | ||
|
|
0069d2fabc | ||
|
|
98964638ba | ||
|
|
4573a995f1 | ||
|
|
4bc80cc724 | ||
|
|
a2972abe3e | ||
|
|
54eaf561f4 | ||
|
|
3134dd3165 | ||
|
|
8fc6abd480 | ||
|
|
f031bb72b6 | ||
|
|
42f0b419e8 | ||
|
|
7cbae758fc | ||
|
|
fb32cfa170 | ||
|
|
ffa8268125 | ||
|
|
eba1003c19 | ||
|
|
eca6383dba | ||
|
|
624d08f46c | ||
|
|
14749daf9b | ||
|
|
2817875706 | ||
|
|
3f8d705439 | ||
|
|
e9faef2692 | ||
|
|
d459807af7 | ||
|
|
10ad9ef096 | ||
|
|
11e3649b85 | ||
|
|
ddc00fedb6 | ||
|
|
0aa9208dcc | ||
|
|
e3b1996dc9 | ||
|
|
24ca744e06 | ||
|
|
7f9ce5bdd4 | ||
|
|
23e5de0819 | ||
|
|
c23508819f | ||
|
|
af030e3da4 | ||
|
|
d8e67d09a9 | ||
|
|
66c08e663e | ||
|
|
9a096e272e | ||
|
|
21e87e12cb | ||
|
|
7c6d7dd4d0 | ||
|
|
c5329e9db0 | ||
|
|
bccb9f8ddb | ||
|
|
c57af29fca |
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>11.15.12</version>
|
||||
<version>11.22.10</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ebean</name>
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-11.15.12</tag>
|
||||
<tag>ebean-11.22.10</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -117,7 +117,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-annotation</artifactId>
|
||||
<version>3.11</version>
|
||||
<version>4.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -135,7 +135,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-migration</artifactId>
|
||||
<version>11.5.4</version>
|
||||
<version>11.9.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -192,7 +192,7 @@
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>9.4.1212</version>
|
||||
<version>42.2.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -360,7 +360,7 @@
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9.1</version>
|
||||
<configuration>
|
||||
<doctitle>Ebean 10</doctitle>
|
||||
<doctitle>Ebean 11</doctitle>
|
||||
<overview>src/main/java/io/ebean/overview.html</overview>
|
||||
<source>1.8</source>
|
||||
<doclet>org.avaje.doclet.PygmentsDoclet</doclet>
|
||||
|
||||
@@ -2,6 +2,8 @@ package io.ebean;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Provides finder functionality for use with "Dependency Injection style" use of Ebean.
|
||||
* <p>
|
||||
@@ -115,6 +117,13 @@ public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
|
||||
db().save(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save all the beans in the collection.
|
||||
*/
|
||||
public int saveAll(Collection<T> bean) {
|
||||
return db().saveAll(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this entity.
|
||||
*
|
||||
@@ -154,6 +163,13 @@ public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
|
||||
return db().delete(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all the beans in the collection.
|
||||
*/
|
||||
public int deleteAll(Collection<T> beans) {
|
||||
return db().deleteAll(beans);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a bean permanently without soft delete.
|
||||
* <p>
|
||||
@@ -167,6 +183,30 @@ public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
|
||||
return db().deletePermanent(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this entity using the default merge options.
|
||||
* <p>
|
||||
* Ebean will detect if this is a new bean or a previously fetched bean and perform either an
|
||||
* insert or an update based on that.
|
||||
*
|
||||
* @see EbeanServer#merge(Object)
|
||||
*/
|
||||
public void merge(T bean) {
|
||||
db().merge(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this entity using the specified merge options.
|
||||
* <p>
|
||||
* Ebean will detect if this is a new bean or a previously fetched bean and perform either an
|
||||
* insert or an update based on that.
|
||||
*
|
||||
* @see EbeanServer#merge(Object, MergeOptions)
|
||||
*/
|
||||
public void merge(T bean, MergeOptions options) {
|
||||
db().merge(bean, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes this entity from the database.
|
||||
*
|
||||
|
||||
@@ -638,6 +638,15 @@ public final class Ebean {
|
||||
serverMgr.getDefaultServer().updateAll(beans);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the bean using the default merge options.
|
||||
*
|
||||
* @param bean The bean to merge
|
||||
*/
|
||||
public static void merge(Object bean) {
|
||||
serverMgr.getDefaultServer().merge(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the bean using the given merge options.
|
||||
*
|
||||
|
||||
@@ -808,411 +808,18 @@ public interface EbeanServer {
|
||||
<T> T getReference(Class<T> beanType, Object id);
|
||||
|
||||
/**
|
||||
* Return the number of 'top level' or 'root' entities this query should return.
|
||||
*
|
||||
* @see Query#findCount()
|
||||
* @see Query#findFutureCount()
|
||||
*/
|
||||
<T> int findCount(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Return the Id values of the query as a List.
|
||||
*
|
||||
* @see Query#findIds()
|
||||
*/
|
||||
@Nonnull
|
||||
<A, T> List<A> findIds(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Return a QueryIterator for the query.
|
||||
* Return the extended API for EbeanServer.
|
||||
* <p>
|
||||
* Generally using {@link #findEach(Query, Consumer, Transaction)} or
|
||||
* {@link #findEachWhile(Query, Predicate, Transaction)} is preferred
|
||||
* to findIterate(). The reason is that those methods automatically take care of
|
||||
* closing the queryIterator (and the underlying jdbc statement and resultSet).
|
||||
* The extended API has the options for executing queries that take an explicit
|
||||
* transaction as an argument.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is similar to findEach in that not all the result beans need to be held
|
||||
* in memory at the same time and as such is good for processing large queries.
|
||||
* </p>
|
||||
*
|
||||
* @see Query#findIterate()
|
||||
* @see Query#findEach(Consumer)
|
||||
* @see Query#findEachWhile(Predicate)
|
||||
*/
|
||||
@Nonnull
|
||||
<T> QueryIterator<T> findIterate(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query visiting the each bean one at a time.
|
||||
* <p>
|
||||
* Unlike findList() this is suitable for processing a query that will return
|
||||
* a very large resultSet. The reason is that not all the result beans need to be
|
||||
* held in memory at the same time and instead processed one at a time.
|
||||
* </p>
|
||||
* <p>
|
||||
* Internally this query using a PersistenceContext scoped to each bean (and the
|
||||
* beans associated object graph).
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* ebeanServer.find(Order.class)
|
||||
* .where().eq("status", Order.Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .findEach((Order order) -> {
|
||||
*
|
||||
* // do something with the order bean
|
||||
* System.out.println(" -- processing order ... " + order);
|
||||
* });
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see Query#findEach(Consumer)
|
||||
* @see Query#findEachWhile(Predicate)
|
||||
*/
|
||||
<T> void findEach(Query<T> query, Consumer<T> consumer, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query visiting the each bean one at a time.
|
||||
* <p>
|
||||
* Compared to findEach() this provides the ability to stop processing the query
|
||||
* results early by returning false for the Predicate.
|
||||
* </p>
|
||||
* <p>
|
||||
* Unlike findList() this is suitable for processing a query that will return
|
||||
* a very large resultSet. The reason is that not all the result beans need to be
|
||||
* held in memory at the same time and instead processed one at a time.
|
||||
* </p>
|
||||
* <p>
|
||||
* Internally this query using a PersistenceContext scoped to each bean (and the
|
||||
* beans associated object graph).
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* ebeanServer.find(Order.class)
|
||||
* .where().eq("status", Order.Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .findEachWhile((Order order) -> {
|
||||
*
|
||||
* // do something with the order bean
|
||||
* System.out.println(" -- processing order ... " + order);
|
||||
*
|
||||
* boolean carryOnProcessing = ...
|
||||
* return carryOnProcessing;
|
||||
* });
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see Query#findEach(Consumer)
|
||||
* @see Query#findEachWhile(Predicate)
|
||||
*/
|
||||
<T> void findEachWhile(Query<T> query, Predicate<T> consumer, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Return versions of a @History entity bean.
|
||||
* <p>
|
||||
* Generally this query is expected to be a find by id or unique predicates query.
|
||||
* It will execute the query against the history returning the versions of the bean.
|
||||
* Typically we only need to use the extended API when we do NOT want to use the
|
||||
* usual ThreadLocal based mechanism to obtain the current transaction but instead
|
||||
* supply the transaction explicitly.
|
||||
* </p>
|
||||
*/
|
||||
@Nonnull
|
||||
<T> List<Version<T>> findVersions(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute a query returning a list of beans.
|
||||
* <p>
|
||||
* Generally you are able to use {@link Query#findList()} rather than
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* .where().ilike("name", "rob%")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T> the type of entity bean to fetch.
|
||||
* @param query the query to execute.
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the list of fetched beans.
|
||||
* @see Query#findList()
|
||||
*/
|
||||
@Nonnull
|
||||
<T> List<T> findList(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute find row count query in a background thread.
|
||||
* <p>
|
||||
* This returns a Future object which can be used to cancel, check the
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
* </p>
|
||||
*
|
||||
* @param query the query to execute the row count on
|
||||
* @param transaction the transaction (can be null).
|
||||
* @return a Future object for the row count query
|
||||
* @see Query#findFutureCount()
|
||||
*/
|
||||
@Nonnull
|
||||
<T> FutureRowCount<T> findFutureCount(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute find Id's query in a background thread.
|
||||
* <p>
|
||||
* This returns a Future object which can be used to cancel, check the
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
* </p>
|
||||
*
|
||||
* @param query the query to execute the fetch Id's on
|
||||
* @param transaction the transaction (can be null).
|
||||
* @return a Future object for the list of Id's
|
||||
* @see Query#findFutureIds()
|
||||
*/
|
||||
@Nonnull
|
||||
<T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute find list query in a background thread returning a FutureList object.
|
||||
* <p>
|
||||
* This returns a Future object which can be used to cancel, check the
|
||||
* execution status (isDone etc) and get the value (with or without a timeout).
|
||||
* <p>
|
||||
* This query will execute in it's own PersistenceContext and using its own transaction.
|
||||
* What that means is that it will not share any bean instances with other queries.
|
||||
*
|
||||
* @param query the query to execute in the background
|
||||
* @param transaction the transaction (can be null).
|
||||
* @return a Future object for the list result of the query
|
||||
* @see Query#findFutureList()
|
||||
*/
|
||||
@Nonnull
|
||||
<T> FutureList<T> findFutureList(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Return a PagedList for this query using firstRow and maxRows.
|
||||
* <p>
|
||||
* The benefit of using this over findList() is that it provides functionality to get the
|
||||
* total row count etc.
|
||||
* </p>
|
||||
* <p>
|
||||
* If maxRows is not set on the query prior to calling findPagedList() then a
|
||||
* PersistenceException is thrown.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* PagedList<Order> pagedList = Ebean.find(Order.class)
|
||||
* .setFirstRow(50)
|
||||
* .setMaxRows(20)
|
||||
* .findPagedList();
|
||||
*
|
||||
* // fetch the total row count in the background
|
||||
* pagedList.loadRowCount();
|
||||
*
|
||||
* List<Order> orders = pagedList.getList();
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return The PagedList
|
||||
* @see Query#findPagedList()
|
||||
*/
|
||||
@Nonnull
|
||||
<T> PagedList<T> findPagedList(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query returning a set of entity beans.
|
||||
* <p>
|
||||
* Generally you are able to use {@link Query#findSet()} rather than
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Set<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* .where().ilike("name", "rob%")
|
||||
* .findSet();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T> the type of entity bean to fetch.
|
||||
* @param query the query to execute
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the set of fetched beans.
|
||||
* @see Query#findSet()
|
||||
*/
|
||||
@Nonnull
|
||||
<T> Set<T> findSet(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query returning the entity beans in a Map.
|
||||
* <p>
|
||||
* Generally you are able to use {@link Query#findMap()} rather than
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of entity bean to fetch.
|
||||
* @param query the query to execute.
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the map of fetched beans.
|
||||
* @see Query#findMap()
|
||||
*/
|
||||
@Nonnull
|
||||
<K, T> Map<K, T> findMap(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query returning a list of values for a single property.
|
||||
* <p>
|
||||
* <h3>Example 1:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<String> names =
|
||||
* Ebean.find(Customer.class)
|
||||
* .select("name")
|
||||
* .orderBy().asc("name")
|
||||
* .findSingleAttributeList();
|
||||
*
|
||||
* }</pre>
|
||||
* <h3>Example 2:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<String> names =
|
||||
* Ebean.find(Customer.class)
|
||||
* .setDistinct(true)
|
||||
* .select("name")
|
||||
* .where().eq("status", Customer.Status.NEW)
|
||||
* .orderBy().asc("name")
|
||||
* .setMaxRows(100)
|
||||
* .findSingleAttributeList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return the list of values for the selected property
|
||||
* @see Query#findSingleAttributeList()
|
||||
*/
|
||||
@Nonnull
|
||||
<A, T> List<A> findSingleAttributeList(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query returning at most one entity bean or null (if no matching
|
||||
* bean is found).
|
||||
* <p>
|
||||
* This will throw a NonUniqueResultException if the query finds more than one result.
|
||||
* </p>
|
||||
* <p>
|
||||
* Generally you are able to use {@link Query#findOne()} rather than
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of entity bean to fetch.
|
||||
* @param query the query to execute.
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the list of fetched beans.
|
||||
* @throws NonUniqueResultException if more than one result was found
|
||||
* @see Query#findOne()
|
||||
*/
|
||||
@Nullable
|
||||
<T> T findOne(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Similar to findOne() but returns an Optional (rather than nullable).
|
||||
*/
|
||||
@Nonnull
|
||||
<T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute as a delete query deleting the 'root level' beans that match the predicates
|
||||
* in the query.
|
||||
* <p>
|
||||
* Note that if the query includes joins then the generated delete statement may not be
|
||||
* optimal depending on the database platform.
|
||||
* </p>
|
||||
*
|
||||
* @param query the query used for the delete
|
||||
* @param transaction the transaction to use (can be null)
|
||||
* @param <T> the type of entity bean to fetch.
|
||||
* @return the number of beans/rows that were deleted
|
||||
*/
|
||||
<T> int delete(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the update query returning the number of rows updated.
|
||||
* <p>
|
||||
* The update query must be created using {@link #update(Class)}.
|
||||
* </p>
|
||||
*
|
||||
* @param query the update query to execute
|
||||
* @param transaction the optional transaction to use for the update (can be null)
|
||||
* @param <T> the type of entity bean
|
||||
* @return The number of rows updated
|
||||
*/
|
||||
<T> int update(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the sql query returning a list of MapBean.
|
||||
* <p>
|
||||
* Generally you are able to use {@link SqlQuery#findList()} rather than
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
*
|
||||
* @param query the query to execute.
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the list of fetched MapBean.
|
||||
* @see SqlQuery#findList()
|
||||
*/
|
||||
@Nonnull
|
||||
List<SqlRow> findList(SqlQuery query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the SqlQuery iterating a row at a time.
|
||||
* <p>
|
||||
* This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
|
||||
* </p>
|
||||
*/
|
||||
void findEach(SqlQuery query, Consumer<SqlRow> consumer, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the SqlQuery iterating a row at a time with the ability to stop consuming part way through.
|
||||
* <p>
|
||||
* Returning false after processing a row stops the iteration through the query results.
|
||||
* </p>
|
||||
* <p>
|
||||
* This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
|
||||
* </p>
|
||||
*/
|
||||
void findEachWhile(SqlQuery query, Predicate<SqlRow> consumer, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the sql query returning a single MapBean or null.
|
||||
* <p>
|
||||
* This will throw a PersistenceException if the query found more than one
|
||||
* result.
|
||||
* </p>
|
||||
* <p>
|
||||
* Generally you are able to use {@link SqlQuery#findOne()} rather than
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
*
|
||||
* @param query the query to execute.
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the fetched MapBean or null if none was found.
|
||||
* @see SqlQuery#findOne()
|
||||
*/
|
||||
@Nullable
|
||||
SqlRow findOne(SqlQuery query, Transaction transaction);
|
||||
ExtendedServer extended();
|
||||
|
||||
/**
|
||||
* Either Insert or Update the bean depending on its state.
|
||||
@@ -1628,6 +1235,13 @@ public interface EbeanServer {
|
||||
*/
|
||||
void updateAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Merge the bean using the default merge options (no paths specified, default delete).
|
||||
*
|
||||
* @param bean The bean to merge
|
||||
*/
|
||||
void merge(Object bean);
|
||||
|
||||
/**
|
||||
* Merge the bean using the given merge options.
|
||||
*
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package io.ebean;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
/**
|
||||
* Class to determine the ebean version. (
|
||||
* @author Roland Praml, FOCONIS AG
|
||||
@@ -13,31 +13,32 @@ import org.slf4j.LoggerFactory;
|
||||
*/
|
||||
public class EbeanVersion {
|
||||
private EbeanVersion() {
|
||||
|
||||
|
||||
}
|
||||
private static final Logger logger = LoggerFactory.getLogger(EbeanVersion.class);
|
||||
|
||||
|
||||
private static String version = "unknown";
|
||||
static {
|
||||
try {
|
||||
Properties prop = new Properties();
|
||||
InputStream in = Ebean.class.getResourceAsStream("/META-INF/maven/io.ebean/ebean/pom.properties");
|
||||
if (in != null) {
|
||||
prop.load(in);
|
||||
in.close();
|
||||
version = prop.getProperty("version");
|
||||
try (InputStream in = Ebean.class.getResourceAsStream("/META-INF/maven/io.ebean/ebean/pom.properties")) {
|
||||
if (in != null) {
|
||||
prop.load(in);
|
||||
in.close();
|
||||
version = prop.getProperty("version");
|
||||
}
|
||||
}
|
||||
logger.info("ebean version: {}", version);
|
||||
} catch (IOException e) {
|
||||
logger.warn("Could not determine ebean version: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the ebean version (read from /META-INF/maven/io.ebean/ebean/pom.properties)
|
||||
*/
|
||||
public static String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -120,6 +120,14 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
Query<T> asDraft();
|
||||
|
||||
/**
|
||||
* Convert the query to a DTO bean query.
|
||||
* <p>
|
||||
* We effectively use the underlying ORM query to build the SQL and then execute
|
||||
* and map it into DTO beans.
|
||||
*/
|
||||
<D> DtoQuery<D> asDto(Class<D> dtoClass);
|
||||
|
||||
/**
|
||||
* Execute using "for update" clause which results in the DB locking the record.
|
||||
*/
|
||||
@@ -404,6 +412,11 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
Query<T> select(String properties);
|
||||
|
||||
/**
|
||||
* Apply the fetchGroup which defines what part of the object graph to load.
|
||||
*/
|
||||
Query<T> select(FetchGroup<T> fetchGroup);
|
||||
|
||||
/**
|
||||
* Set whether this query uses DISTINCT.
|
||||
* <p>
|
||||
@@ -1023,6 +1036,20 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> bitwiseAll(String propertyName, long flags);
|
||||
|
||||
/**
|
||||
* Add expression for the given bit flags to be NOT set.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().bitwiseNot("flags", BwFlags.HAS_COLOUR)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param propertyName The property that holds the flags value
|
||||
* @param flags The flags we are looking for
|
||||
*/
|
||||
ExpressionList<T> bitwiseNot(String propertyName, long flags);
|
||||
|
||||
/**
|
||||
* Add bitwise AND expression of the given bit flags to compare with the match/mask.
|
||||
* <p>
|
||||
|
||||
@@ -0,0 +1,452 @@
|
||||
package io.ebean;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.NonUniqueResultException;
|
||||
import java.time.Clock;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* The extended API for EbeanServer.
|
||||
* <p>
|
||||
* This provides the finder methods that take an explicit transaction rather than obtaining
|
||||
* the transaction from the usual mechanism (which is ThreadLocal based).
|
||||
* </p>
|
||||
* <p>
|
||||
* In general we only want to use this ExtendedServer API when we want to avoid / bypass
|
||||
* the use of the mechanism to get the current transaction and instead explicitly supply
|
||||
* the transaction to use.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that in all cases the transaction supplied can be null and in this case the EbeanServer
|
||||
* will use the normal mechanism to obtain the transaction to use.
|
||||
* </p>
|
||||
*/
|
||||
public interface ExtendedServer {
|
||||
|
||||
/**
|
||||
* Return the NOW time from the Clock.
|
||||
*/
|
||||
long clockNow();
|
||||
|
||||
/**
|
||||
* Set the Clock to use for <code>@WhenCreated</code> and <code>@WhenModified</code>.
|
||||
* <p>
|
||||
* Note that we only expect to change the Clock for testing purposes.
|
||||
* </p>
|
||||
*/
|
||||
void setClock(Clock clock);
|
||||
|
||||
/**
|
||||
* Return the number of 'top level' or 'root' entities this query should return.
|
||||
*
|
||||
* @see Query#findCount()
|
||||
* @see Query#findFutureCount()
|
||||
*/
|
||||
<T> int findCount(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Return the Id values of the query as a List.
|
||||
*
|
||||
* @see Query#findIds()
|
||||
*/
|
||||
@Nonnull
|
||||
<A, T> List<A> findIds(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Return a QueryIterator for the query.
|
||||
* <p>
|
||||
* Generally using {@link #findEach(Query, Consumer, Transaction)} or
|
||||
* {@link #findEachWhile(Query, Predicate, Transaction)} is preferred
|
||||
* to findIterate(). The reason is that those methods automatically take care of
|
||||
* closing the queryIterator (and the underlying jdbc statement and resultSet).
|
||||
* </p>
|
||||
* <p>
|
||||
* This is similar to findEach in that not all the result beans need to be held
|
||||
* in memory at the same time and as such is good for processing large queries.
|
||||
* </p>
|
||||
*
|
||||
* @see Query#findIterate()
|
||||
* @see Query#findEach(Consumer)
|
||||
* @see Query#findEachWhile(Predicate)
|
||||
*/
|
||||
@Nonnull
|
||||
<T> QueryIterator<T> findIterate(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query visiting the each bean one at a time.
|
||||
* <p>
|
||||
* Unlike findList() this is suitable for processing a query that will return
|
||||
* a very large resultSet. The reason is that not all the result beans need to be
|
||||
* held in memory at the same time and instead processed one at a time.
|
||||
* </p>
|
||||
* <p>
|
||||
* Internally this query using a PersistenceContext scoped to each bean (and the
|
||||
* beans associated object graph).
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* ebeanServer.find(Order.class)
|
||||
* .where().eq("status", Order.Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .findEach((Order order) -> {
|
||||
*
|
||||
* // do something with the order bean
|
||||
* System.out.println(" -- processing order ... " + order);
|
||||
* });
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see Query#findEach(Consumer)
|
||||
* @see Query#findEachWhile(Predicate)
|
||||
*/
|
||||
<T> void findEach(Query<T> query, Consumer<T> consumer, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query visiting the each bean one at a time.
|
||||
* <p>
|
||||
* Compared to findEach() this provides the ability to stop processing the query
|
||||
* results early by returning false for the Predicate.
|
||||
* </p>
|
||||
* <p>
|
||||
* Unlike findList() this is suitable for processing a query that will return
|
||||
* a very large resultSet. The reason is that not all the result beans need to be
|
||||
* held in memory at the same time and instead processed one at a time.
|
||||
* </p>
|
||||
* <p>
|
||||
* Internally this query using a PersistenceContext scoped to each bean (and the
|
||||
* beans associated object graph).
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* ebeanServer.find(Order.class)
|
||||
* .where().eq("status", Order.Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .findEachWhile((Order order) -> {
|
||||
*
|
||||
* // do something with the order bean
|
||||
* System.out.println(" -- processing order ... " + order);
|
||||
*
|
||||
* boolean carryOnProcessing = ...
|
||||
* return carryOnProcessing;
|
||||
* });
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see Query#findEach(Consumer)
|
||||
* @see Query#findEachWhile(Predicate)
|
||||
*/
|
||||
<T> void findEachWhile(Query<T> query, Predicate<T> consumer, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Return versions of a @History entity bean.
|
||||
* <p>
|
||||
* Generally this query is expected to be a find by id or unique predicates query.
|
||||
* It will execute the query against the history returning the versions of the bean.
|
||||
* </p>
|
||||
*/
|
||||
@Nonnull
|
||||
<T> List<Version<T>> findVersions(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute a query returning a list of beans.
|
||||
* <p>
|
||||
* Generally you are able to use {@link Query#findList()} rather than
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* .where().ilike("name", "rob%")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T> the type of entity bean to fetch.
|
||||
* @param query the query to execute.
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the list of fetched beans.
|
||||
* @see Query#findList()
|
||||
*/
|
||||
@Nonnull
|
||||
<T> List<T> findList(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute find row count query in a background thread.
|
||||
* <p>
|
||||
* This returns a Future object which can be used to cancel, check the
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
* </p>
|
||||
*
|
||||
* @param query the query to execute the row count on
|
||||
* @param transaction the transaction (can be null).
|
||||
* @return a Future object for the row count query
|
||||
* @see Query#findFutureCount()
|
||||
*/
|
||||
@Nonnull
|
||||
<T> FutureRowCount<T> findFutureCount(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute find Id's query in a background thread.
|
||||
* <p>
|
||||
* This returns a Future object which can be used to cancel, check the
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
* </p>
|
||||
*
|
||||
* @param query the query to execute the fetch Id's on
|
||||
* @param transaction the transaction (can be null).
|
||||
* @return a Future object for the list of Id's
|
||||
* @see Query#findFutureIds()
|
||||
*/
|
||||
@Nonnull
|
||||
<T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute find list query in a background thread returning a FutureList object.
|
||||
* <p>
|
||||
* This returns a Future object which can be used to cancel, check the
|
||||
* execution status (isDone etc) and get the value (with or without a timeout).
|
||||
* <p>
|
||||
* This query will execute in it's own PersistenceContext and using its own transaction.
|
||||
* What that means is that it will not share any bean instances with other queries.
|
||||
*
|
||||
* @param query the query to execute in the background
|
||||
* @param transaction the transaction (can be null).
|
||||
* @return a Future object for the list result of the query
|
||||
* @see Query#findFutureList()
|
||||
*/
|
||||
@Nonnull
|
||||
<T> FutureList<T> findFutureList(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Return a PagedList for this query using firstRow and maxRows.
|
||||
* <p>
|
||||
* The benefit of using this over findList() is that it provides functionality to get the
|
||||
* total row count etc.
|
||||
* </p>
|
||||
* <p>
|
||||
* If maxRows is not set on the query prior to calling findPagedList() then a
|
||||
* PersistenceException is thrown.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* PagedList<Order> pagedList = Ebean.find(Order.class)
|
||||
* .setFirstRow(50)
|
||||
* .setMaxRows(20)
|
||||
* .findPagedList();
|
||||
*
|
||||
* // fetch the total row count in the background
|
||||
* pagedList.loadRowCount();
|
||||
*
|
||||
* List<Order> orders = pagedList.getList();
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return The PagedList
|
||||
* @see Query#findPagedList()
|
||||
*/
|
||||
@Nonnull
|
||||
<T> PagedList<T> findPagedList(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query returning a set of entity beans.
|
||||
* <p>
|
||||
* Generally you are able to use {@link Query#findSet()} rather than
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Set<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* .where().ilike("name", "rob%")
|
||||
* .findSet();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T> the type of entity bean to fetch.
|
||||
* @param query the query to execute
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the set of fetched beans.
|
||||
* @see Query#findSet()
|
||||
*/
|
||||
@Nonnull
|
||||
<T> Set<T> findSet(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query returning the entity beans in a Map.
|
||||
* <p>
|
||||
* Generally you are able to use {@link Query#findMap()} rather than
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of entity bean to fetch.
|
||||
* @param query the query to execute.
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the map of fetched beans.
|
||||
* @see Query#findMap()
|
||||
*/
|
||||
@Nonnull
|
||||
<K, T> Map<K, T> findMap(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query returning a list of values for a single property.
|
||||
* <p>
|
||||
* <h3>Example 1:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<String> names =
|
||||
* Ebean.find(Customer.class)
|
||||
* .select("name")
|
||||
* .orderBy().asc("name")
|
||||
* .findSingleAttributeList();
|
||||
*
|
||||
* }</pre>
|
||||
* <h3>Example 2:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<String> names =
|
||||
* Ebean.find(Customer.class)
|
||||
* .setDistinct(true)
|
||||
* .select("name")
|
||||
* .where().eq("status", Customer.Status.NEW)
|
||||
* .orderBy().asc("name")
|
||||
* .setMaxRows(100)
|
||||
* .findSingleAttributeList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return the list of values for the selected property
|
||||
* @see Query#findSingleAttributeList()
|
||||
*/
|
||||
@Nonnull
|
||||
<A, T> List<A> findSingleAttributeList(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query returning at most one entity bean or null (if no matching
|
||||
* bean is found).
|
||||
* <p>
|
||||
* This will throw a NonUniqueResultException if the query finds more than one result.
|
||||
* </p>
|
||||
* <p>
|
||||
* Generally you are able to use {@link Query#findOne()} rather than
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of entity bean to fetch.
|
||||
* @param query the query to execute.
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the list of fetched beans.
|
||||
* @throws NonUniqueResultException if more than one result was found
|
||||
* @see Query#findOne()
|
||||
*/
|
||||
@Nullable
|
||||
<T> T findOne(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Similar to findOne() but returns an Optional (rather than nullable).
|
||||
*/
|
||||
@Nonnull
|
||||
<T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute as a delete query deleting the 'root level' beans that match the predicates
|
||||
* in the query.
|
||||
* <p>
|
||||
* Note that if the query includes joins then the generated delete statement may not be
|
||||
* optimal depending on the database platform.
|
||||
* </p>
|
||||
*
|
||||
* @param query the query used for the delete
|
||||
* @param transaction the transaction to use (can be null)
|
||||
* @param <T> the type of entity bean to fetch.
|
||||
* @return the number of beans/rows that were deleted
|
||||
*/
|
||||
<T> int delete(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the update query returning the number of rows updated.
|
||||
* <p>
|
||||
* The update query must be created using {@link EbeanServer#update(Class)}.
|
||||
* </p>
|
||||
*
|
||||
* @param query the update query to execute
|
||||
* @param transaction the optional transaction to use for the update (can be null)
|
||||
* @param <T> the type of entity bean
|
||||
* @return The number of rows updated
|
||||
*/
|
||||
<T> int update(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the sql query returning a list of MapBean.
|
||||
* <p>
|
||||
* Generally you are able to use {@link SqlQuery#findList()} rather than
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
*
|
||||
* @param query the query to execute.
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the list of fetched MapBean.
|
||||
* @see SqlQuery#findList()
|
||||
*/
|
||||
@Nonnull
|
||||
List<SqlRow> findList(SqlQuery query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the SqlQuery iterating a row at a time.
|
||||
* <p>
|
||||
* This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
|
||||
* </p>
|
||||
*/
|
||||
void findEach(SqlQuery query, Consumer<SqlRow> consumer, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the SqlQuery iterating a row at a time with the ability to stop consuming part way through.
|
||||
* <p>
|
||||
* Returning false after processing a row stops the iteration through the query results.
|
||||
* </p>
|
||||
* <p>
|
||||
* This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
|
||||
* </p>
|
||||
*/
|
||||
void findEachWhile(SqlQuery query, Predicate<SqlRow> consumer, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the sql query returning a single MapBean or null.
|
||||
* <p>
|
||||
* This will throw a PersistenceException if the query found more than one
|
||||
* result.
|
||||
* </p>
|
||||
* <p>
|
||||
* Generally you are able to use {@link SqlQuery#findOne()} rather than
|
||||
* explicitly calling this method. You could use this method if you wish to
|
||||
* explicitly control the transaction used for the query.
|
||||
* </p>
|
||||
*
|
||||
* @param query the query to execute.
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @return the fetched MapBean or null if none was found.
|
||||
* @see SqlQuery#findOne()
|
||||
*/
|
||||
@Nullable
|
||||
SqlRow findOne(SqlQuery query, Transaction transaction);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package io.ebean;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Defines what part of the object graph to load (select and fetch clauses).
|
||||
* <p>
|
||||
* Using a FetchGroup effectively sets the select() and fetch() clauses for a query. It is alternative
|
||||
* to specifying the select() and fetch() clauses on the query allowing for more re-use of "what to load"
|
||||
* that can be defined separately from the query and combined with other FetchGroups.
|
||||
* </p>
|
||||
*
|
||||
* <h3>Select example</h3>*
|
||||
* <pre>{@code
|
||||
*
|
||||
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class, "name, status");
|
||||
*
|
||||
* Customer.query()
|
||||
* .select(fetchGroup)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Select and fetch example</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class)
|
||||
* .select("name, status")
|
||||
* .fetch("contacts", "firstName, lastName, email")
|
||||
* .build();
|
||||
*
|
||||
* Customer.query()
|
||||
* .select(fetchGroup)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Combining FetchGroups</h3>
|
||||
* <p>
|
||||
* FetchGroups can be combined together to form another FetchGroup.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* FetchGroup<Address> FG_ADDRESS = FetchGroup.of(Address.class)
|
||||
* .select("line1, line2, city")
|
||||
* .fetch("country", "name")
|
||||
* .build();
|
||||
*
|
||||
* FetchGroup<Customer> FG_CUSTOMER = FetchGroup.of(Customer.class)
|
||||
* .select("name, version")
|
||||
* .fetch("billingAddress", FG_ADDRESS)
|
||||
* .build();
|
||||
*
|
||||
*
|
||||
* Customer.query()
|
||||
* .select(FG_CUSTOMER)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T> The bean type the Fetch group can be applied to
|
||||
*/
|
||||
public interface FetchGroup<T> {
|
||||
|
||||
/**
|
||||
* Return the FetchGroup with the given select clause.
|
||||
* <p>
|
||||
* We use this for simple FetchGroup that only select() properties and do not have additional fetch() clause.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class, "name, status");
|
||||
*
|
||||
* Customer.query()
|
||||
* .select(fetchGroup)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param select The select clause of the FetchGroup
|
||||
*
|
||||
* @return The FetchGroup with the given select clause
|
||||
*/
|
||||
@Nonnull
|
||||
static <T> FetchGroup<T> of(Class<T> cls, String select) {
|
||||
return XServiceProvider.fetchGroupOf(cls, select);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the FetchGroupBuilder with the given select clause that we can add fetch clauses to.
|
||||
* <p>
|
||||
* We chain select() with one or more fetch() clauses to define the object graph to load.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class)
|
||||
* .select("name, status")
|
||||
* .fetch("contacts", "firstName, lastName, email")
|
||||
* .build();
|
||||
*
|
||||
* Customer.query()
|
||||
* .select(fetchGroup)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return The FetchGroupBuilder with the given select clause which we will add fetch clauses to
|
||||
*/
|
||||
@Nonnull
|
||||
static <T> FetchGroupBuilder<T> of(Class<T> cls) {
|
||||
return XServiceProvider.fetchGroupOf(cls);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package io.ebean;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Builds a FetchGroup by adding fetch clauses.
|
||||
* <p>
|
||||
* We add select() and fetch() clauses to define the object graph we want to load.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* FetchGroup fetchGroup = FetchGroup
|
||||
* .select("name, status")
|
||||
* .fetch("contacts", "firstName, lastName, email")
|
||||
* .build();
|
||||
*
|
||||
* Customer.query()
|
||||
* .select(fetchGroup)
|
||||
* .where()
|
||||
* ...
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public interface FetchGroupBuilder<T> {
|
||||
|
||||
/**
|
||||
* Specify specific properties to select (top level properties).
|
||||
*/
|
||||
@Nonnull
|
||||
FetchGroupBuilder<T> select(String select);
|
||||
|
||||
/**
|
||||
* Fetch all the properties at the given path.
|
||||
*/
|
||||
@Nonnull
|
||||
FetchGroupBuilder<T> fetch(String path);
|
||||
|
||||
/**
|
||||
* Fetch the path with the nested fetch group.
|
||||
*/
|
||||
@Nonnull
|
||||
FetchGroupBuilder<T> fetch(String path, FetchGroup<?> nestedGroup);
|
||||
|
||||
/**
|
||||
* Fetch the path using a query join with the nested fetch group.
|
||||
*/
|
||||
@Nonnull
|
||||
FetchGroupBuilder<T> fetchQuery(String path, FetchGroup<?> nestedGroup);
|
||||
|
||||
/**
|
||||
* Fetch the path lazily with the nested fetch group.
|
||||
*/
|
||||
@Nonnull
|
||||
FetchGroupBuilder<T> fetchLazy(String path, FetchGroup<?> nestedGroup);
|
||||
|
||||
/**
|
||||
* Fetch the path including specified properties.
|
||||
*/
|
||||
@Nonnull
|
||||
FetchGroupBuilder<T> fetch(String path, String properties);
|
||||
|
||||
/**
|
||||
* Fetch the path including all its properties using a query join.
|
||||
*/
|
||||
@Nonnull
|
||||
FetchGroupBuilder<T> fetchQuery(String path);
|
||||
|
||||
/**
|
||||
* Fetch the path including specified properties using a query join.
|
||||
*/
|
||||
@Nonnull
|
||||
FetchGroupBuilder<T> fetchQuery(String path, String properties);
|
||||
|
||||
/**
|
||||
* Fetch the path including all its properties lazily.
|
||||
*/
|
||||
@Nonnull
|
||||
FetchGroupBuilder<T> fetchLazy(String path);
|
||||
|
||||
/**
|
||||
* Fetch the path including specified properties lazily.
|
||||
*/
|
||||
@Nonnull
|
||||
FetchGroupBuilder<T> fetchLazy(String path, String properties);
|
||||
|
||||
/**
|
||||
* Build and return the FetchGroup.
|
||||
*/
|
||||
@Nonnull
|
||||
FetchGroup<T> build();
|
||||
}
|
||||
@@ -8,11 +8,20 @@ import java.util.Set;
|
||||
*/
|
||||
public class MergeOptionsBuilder {
|
||||
|
||||
private static final MOptions DEFAULT_OPTIONS = new MOptions();
|
||||
|
||||
private Set<String> paths = new LinkedHashSet<>();
|
||||
|
||||
private boolean clientGeneratedIds;
|
||||
|
||||
private boolean deletePermanent = true;
|
||||
private boolean deletePermanent;
|
||||
|
||||
/**
|
||||
* Return the default options.
|
||||
*/
|
||||
public static MergeOptions defaultOptions() {
|
||||
return DEFAULT_OPTIONS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a path that will included in the merge.
|
||||
@@ -47,7 +56,7 @@ public class MergeOptionsBuilder {
|
||||
/**
|
||||
* Build and return the MergeOptions instance.
|
||||
*/
|
||||
public MOptions build() {
|
||||
public MergeOptions build() {
|
||||
return new MOptions(paths, clientGeneratedIds, deletePermanent);
|
||||
}
|
||||
|
||||
@@ -57,6 +66,12 @@ public class MergeOptionsBuilder {
|
||||
private final boolean deletePermanent;
|
||||
private final Set<String> paths;
|
||||
|
||||
private MOptions(){
|
||||
this.clientGeneratedIds = false;
|
||||
this.paths = new LinkedHashSet<>();
|
||||
this.deletePermanent = false;
|
||||
}
|
||||
|
||||
private MOptions(Set<String> paths, boolean clientGeneratedIds, boolean deletePermanent) {
|
||||
this.paths = paths;
|
||||
this.clientGeneratedIds = clientGeneratedIds;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package io.ebean;
|
||||
|
||||
import io.ebean.util.StringHelper;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents an Order By for a Query.
|
||||
@@ -74,6 +77,15 @@ public final class OrderBy<T> implements Serializable {
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a property with ascending order to this OrderBy.
|
||||
*/
|
||||
public Query<T> asc(String propertyName, String collation) {
|
||||
|
||||
list.add(new Property(propertyName, true, collation));
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a property with descending order to this OrderBy.
|
||||
*/
|
||||
@@ -83,6 +95,16 @@ public final class OrderBy<T> implements Serializable {
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a property with descending order to this OrderBy.
|
||||
*/
|
||||
public Query<T> desc(String propertyName, String collation) {
|
||||
|
||||
list.add(new Property(propertyName, false, collation));
|
||||
return query;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the property is known to be contained in the order by clause.
|
||||
*/
|
||||
@@ -231,6 +253,8 @@ public final class OrderBy<T> implements Serializable {
|
||||
|
||||
private boolean ascending;
|
||||
|
||||
private String collation;
|
||||
|
||||
private String nulls;
|
||||
|
||||
private String highLow;
|
||||
@@ -247,17 +271,32 @@ public final class OrderBy<T> implements Serializable {
|
||||
this.highLow = highLow;
|
||||
}
|
||||
|
||||
public Property(String property, boolean ascending, String collation) {
|
||||
this.property = property;
|
||||
this.ascending = ascending;
|
||||
this.collation = collation;
|
||||
}
|
||||
|
||||
public Property(String property, boolean ascending, String collation, String nulls, String highLow) {
|
||||
this.property = property;
|
||||
this.ascending = ascending;
|
||||
this.collation = collation;
|
||||
this.nulls = nulls;
|
||||
this.highLow = highLow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this Property with the path trimmed.
|
||||
*/
|
||||
public Property copyWithTrim(String path) {
|
||||
return new Property(property.substring(path.length() + 1), ascending, nulls, highLow);
|
||||
return new Property(property.substring(path.length() + 1), ascending, collation, nulls, highLow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hc = property.hashCode();
|
||||
hc = hc * 92821 + (ascending ? 0 : 1);
|
||||
hc = hc * 92821 + (collation == null ? 0 : collation.hashCode());
|
||||
hc = hc * 92821 + (nulls == null ? 0 : nulls.hashCode());
|
||||
hc = hc * 92821 + (highLow == null ? 0 : highLow.hashCode());
|
||||
return hc;
|
||||
@@ -274,8 +313,9 @@ public final class OrderBy<T> implements Serializable {
|
||||
Property e = (Property) obj;
|
||||
if (ascending != e.ascending) return false;
|
||||
if (!property.equals(e.property)) return false;
|
||||
if (nulls != null ? !nulls.equals(e.nulls) : e.nulls != null) return false;
|
||||
return highLow != null ? highLow.equals(e.highLow) : e.highLow == null;
|
||||
if (!Objects.equals(collation, e.collation)) return false;
|
||||
if (!Objects.equals(nulls, e.nulls)) return false;
|
||||
return Objects.equals(highLow, e.highLow);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -284,7 +324,7 @@ public final class OrderBy<T> implements Serializable {
|
||||
}
|
||||
|
||||
public String toStringFormat() {
|
||||
if (nulls == null) {
|
||||
if (nulls == null && collation == null) {
|
||||
if (ascending) {
|
||||
return property;
|
||||
} else {
|
||||
@@ -292,11 +332,23 @@ public final class OrderBy<T> implements Serializable {
|
||||
}
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(property);
|
||||
if (collation != null) {
|
||||
if (collation.contains("${}")) {
|
||||
// this is a complex collation, e.g. DB2 - we must replace the property
|
||||
sb.append(StringHelper.replaceString(collation, "${}", property));
|
||||
} else {
|
||||
sb.append(property);
|
||||
sb.append(" collate ").append(collation);
|
||||
}
|
||||
} else {
|
||||
sb.append(property);
|
||||
}
|
||||
if (!ascending) {
|
||||
sb.append(" ").append("desc");
|
||||
}
|
||||
sb.append(" ").append(nulls).append(" ").append(highLow);
|
||||
if (nulls != null) {
|
||||
sb.append(" ").append(nulls).append(" ").append(highLow);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -319,7 +371,7 @@ public final class OrderBy<T> implements Serializable {
|
||||
* Return a copy of this property.
|
||||
*/
|
||||
public Property copy() {
|
||||
return new Property(property, ascending, nulls, highLow);
|
||||
return new Property(property, ascending, collation, nulls, highLow);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -76,7 +76,6 @@ class PrimaryServer {
|
||||
if (defaultServerName == null) {
|
||||
defaultServerName = "db";
|
||||
}
|
||||
System.setProperty("ebean_db", defaultServerName);
|
||||
return defaultServerName;
|
||||
}
|
||||
|
||||
|
||||
@@ -397,6 +397,11 @@ public interface Query<T> {
|
||||
*/
|
||||
Query<T> select(String fetchProperties);
|
||||
|
||||
/**
|
||||
* Apply the fetchGroup which defines what part of the object graph to load.
|
||||
*/
|
||||
Query<T> select(FetchGroup<T> fetchGroup);
|
||||
|
||||
/**
|
||||
* Specify a path to fetch eagerly including specific properties.
|
||||
* <p>
|
||||
@@ -602,8 +607,6 @@ public interface Query<T> {
|
||||
* <p>
|
||||
* This query will execute against the EbeanServer that was used to create it.
|
||||
* </p>
|
||||
*
|
||||
* @see EbeanServer#findIds(Query, Transaction)
|
||||
*/
|
||||
@Nonnull
|
||||
<A> List<A> findIds();
|
||||
@@ -740,8 +743,6 @@ public interface Query<T> {
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see EbeanServer#findList(Query, Transaction)
|
||||
*/
|
||||
@Nonnull
|
||||
List<T> findList();
|
||||
@@ -759,8 +760,6 @@ public interface Query<T> {
|
||||
* .findSet();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see EbeanServer#findSet(Query, Transaction)
|
||||
*/
|
||||
@Nonnull
|
||||
Set<T> findSet();
|
||||
@@ -782,8 +781,6 @@ public interface Query<T> {
|
||||
* .findMap();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see EbeanServer#findMap(Query, Transaction)
|
||||
*/
|
||||
@Nonnull
|
||||
<K> Map<K, T> findMap();
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package io.ebean;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Used with SqlQuery to process potentially large queries reading directly from the JDBC ResultSet.
|
||||
* <p>
|
||||
* This provides a low level option that reads directly from the JDBC ResultSet.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql = "select id, name, status from o_customer order by name desc";
|
||||
*
|
||||
* Ebean.createSqlQuery(sql)
|
||||
* .findEachRow((resultSet, rowNum) -> {
|
||||
*
|
||||
* // read directly from ResultSet
|
||||
*
|
||||
* long id = resultSet.getLong(1);
|
||||
* String name = resultSet.getString(2);
|
||||
*
|
||||
* // do something interesting with the data
|
||||
*
|
||||
* });
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RowConsumer {
|
||||
|
||||
/**
|
||||
* Read the data from the ResultSet and process it.
|
||||
*
|
||||
* @param resultSet The JDBC ResultSet positioned to the current row
|
||||
* @param rowNum The number of the current row being mapped.
|
||||
*/
|
||||
void accept(ResultSet resultSet, int rowNum) throws SQLException;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package io.ebean;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Used with SqlQuery to map raw JDBC ResultSet to objects.
|
||||
* <p>
|
||||
* This provides a low level mapping option with direct use of JDBC ResultSet
|
||||
* with the option of having logic in the mapping. For example, only map some
|
||||
* columns depending on the values read from other columns.
|
||||
* </p>
|
||||
* <p>
|
||||
* For straight mapping into beans then DtoQuery would be the first choice as
|
||||
* it can automatically map the ResultSet into beans.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* //
|
||||
* // A mapper from ResultSet into our CustomerDto bean
|
||||
* //
|
||||
* class CustomerMapper implements RowMapper<CustomerDto> {
|
||||
*
|
||||
* @Override
|
||||
* public CustomerDto map(ResultSet rset, int rowNum) throws SQLException {
|
||||
*
|
||||
* long id = rset.getLong(1);
|
||||
* String name = rset.getString(2);
|
||||
* String status = rset.getString(3);
|
||||
*
|
||||
* return new CustomerDto(id, name, status);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*
|
||||
* //
|
||||
* // Then use the mapper
|
||||
* //
|
||||
*
|
||||
* String sql = "select id, name, status from o_customer where name = ?";
|
||||
*
|
||||
* CustomerDto rob = Ebean.createSqlQuery(sql)
|
||||
* .setParameter(1, "Rob")
|
||||
* .findOne(CUSTOMER_MAPPER);
|
||||
*
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T> The type the row data is mapped into.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RowMapper<T> {
|
||||
|
||||
/**
|
||||
* Read the data from the ResultSet and map to the return type.
|
||||
*
|
||||
* @param resultSet The JDBC ResultSet positioned to the current row
|
||||
* @param rowNum The number of the current row being mapped.
|
||||
*/
|
||||
T map(ResultSet resultSet, int rowNum) throws SQLException;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package io.ebean;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
@@ -77,12 +78,118 @@ public interface SqlQuery extends Serializable {
|
||||
@Nullable
|
||||
SqlRow findOne();
|
||||
|
||||
/**
|
||||
* Execute the query returning a single result using the mapper.
|
||||
*
|
||||
* @param mapper Used to map each ResultSet row into the result object.
|
||||
*/
|
||||
<T> T findOne(RowMapper<T> mapper);
|
||||
|
||||
/**
|
||||
* Execute the query returning a list using the mapper.
|
||||
*
|
||||
* @param mapper Used to map each ResultSet row into the result object.
|
||||
*/
|
||||
<T> List<T> findList(RowMapper<T> mapper);
|
||||
|
||||
/**
|
||||
* Execute the query reading each row from ResultSet using the RowConsumer.
|
||||
* <p>
|
||||
* This provides a low level option that reads directly from the JDBC ResultSet
|
||||
* and is good for processing very large results where (unlike findList) we don't
|
||||
* hold all the results in memory but instead can process row by row.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql = "select id, name, status from customer order by name desc";
|
||||
*
|
||||
* Ebean.createSqlQuery(sql)
|
||||
* .findEachRow((resultSet, rowNum) -> {
|
||||
*
|
||||
* // read directly from ResultSet
|
||||
*
|
||||
* long id = resultSet.getLong(1);
|
||||
* String name = resultSet.getString(2);
|
||||
*
|
||||
* // do something interesting with the data
|
||||
*
|
||||
* });
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param consumer Used to read and process each ResultSet row.
|
||||
*/
|
||||
void findEachRow(RowConsumer consumer);
|
||||
|
||||
/**
|
||||
* Execute the query returning an optional row.
|
||||
*/
|
||||
@Nonnull
|
||||
Optional<SqlRow> findOneOrEmpty();
|
||||
|
||||
/**
|
||||
* Execute the query returning a single scalar attribute.
|
||||
* <pre>@{code
|
||||
*
|
||||
* String sql = "select max(unit_price) from o_order_detail where order_qty > ?";
|
||||
*
|
||||
* BigDecimal maxPrice = Ebean.createSqlQuery(sql)
|
||||
* .setParameter(1, 2)
|
||||
* .findSingleAttribute(BigDecimal.class);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* The attributeType can be any scalar type that Ebean supports (includes javax time types, Joda types etc).
|
||||
* </p>
|
||||
*
|
||||
* @param attributeType The type of the returned value
|
||||
*/
|
||||
<T> T findSingleAttribute(Class<T> attributeType);
|
||||
|
||||
/**
|
||||
* Execute the query returning a single BigDecimal value.
|
||||
* <p>
|
||||
* This is an alias for <code>findSingleAttribute(BigDecimal.class)</code>
|
||||
* </p>
|
||||
*/
|
||||
BigDecimal findSingleDecimal();
|
||||
|
||||
/**
|
||||
* Execute the query returning a single Long value.
|
||||
* <p>
|
||||
* This is an alias for <code>findSingleAttribute(Long.class)</code>
|
||||
* </p>
|
||||
*/
|
||||
Long findSingleLong();
|
||||
|
||||
/**
|
||||
* Execute the query returning a list of scalar attribute values.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql =
|
||||
* " select (unit_price * order_qty) " +
|
||||
* " from o_order_detail " +
|
||||
* " where unit_price > ? " +
|
||||
* " order by (unit_price * order_qty) desc";
|
||||
*
|
||||
* //
|
||||
* List<BigDecimal> lineAmounts = Ebean.createSqlQuery(sql)
|
||||
* .setParameter(1, 3)
|
||||
* .findSingleAttributeList(BigDecimal.class);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* The attributeType can be any scalar type that Ebean supports (includes javax time types, Joda types etc).
|
||||
* </p>
|
||||
*
|
||||
* @param attributeType The type of the returned value
|
||||
*/
|
||||
<T> List<T> findSingleAttributeList(Class<T> attributeType);
|
||||
|
||||
/**
|
||||
* The same as bind for named parameters.
|
||||
*/
|
||||
|
||||
@@ -19,15 +19,17 @@ package io.ebean;
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // example that uses 'named' parameters
|
||||
* String s = "UPDATE f_topic set post_count = :count where id = :id";
|
||||
* SqlUpdate update = Ebean.createSqlUpdate(s);
|
||||
* update.setParameter("id", 1);
|
||||
* update.setParameter("count", 50);
|
||||
* // example that uses 'named' parameters
|
||||
*
|
||||
* int modifiedCount = Ebean.execute(update);
|
||||
* String s = "UPDATE f_topic set post_count = :count where id = :id";
|
||||
*
|
||||
* String msg = "There were " + modifiedCount + " rows updated";
|
||||
* SqlUpdate update = Ebean.createSqlUpdate(s);
|
||||
* update.setParameter("id", 1);
|
||||
* update.setParameter("count", 50);
|
||||
*
|
||||
* int modifiedCount = update.execute();
|
||||
*
|
||||
* String msg = "There were " + modifiedCount + " rows updated";
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
@@ -57,6 +59,33 @@ package io.ebean;
|
||||
*
|
||||
* txn.commit();
|
||||
* }
|
||||
* }</pre>
|
||||
* <p>
|
||||
* An alternative to the batch mode on the transaction is to use addBatch() and executeBatch() like:
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* try (Transaction txn = Ebean.beginTransaction()) {
|
||||
*
|
||||
* insert.setNextParameter(10000);
|
||||
* insert.setNextParameter("hello");
|
||||
* insert.setNextParameter("rob");
|
||||
* insert.addBatch();
|
||||
*
|
||||
* insert.setNextParameter(10001);
|
||||
* insert.setNextParameter("goodbye");
|
||||
* insert.setNextParameter("rob");
|
||||
* insert.addBatch();
|
||||
*
|
||||
* insert.setNextParameter(10002);
|
||||
* insert.setNextParameter("chow");
|
||||
* insert.setNextParameter("bob");
|
||||
* insert.addBatch();
|
||||
*
|
||||
* int[] rows = insert.executeBatch();
|
||||
*
|
||||
* txn.commit();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
@@ -69,6 +98,11 @@ public interface SqlUpdate {
|
||||
/**
|
||||
* Execute the update returning the number of rows modified.
|
||||
* <p>
|
||||
* Note that if the transaction has batch mode on then this update will use JDBC batch and may not execute until
|
||||
* later - at commit time or a transaction flush. In this case this method returns -1 indicating that the
|
||||
* update has been batched for later execution.
|
||||
* </p>
|
||||
* <p>
|
||||
* After you have executed the SqlUpdate you can bind new variables using
|
||||
* {@link #setParameter(String, Object)} etc and then execute the SqlUpdate
|
||||
* again.
|
||||
@@ -83,6 +117,23 @@ public interface SqlUpdate {
|
||||
*/
|
||||
int execute();
|
||||
|
||||
/**
|
||||
* Execute the statement now regardless of the JDBC batch mode of the transaction.
|
||||
*/
|
||||
int executeNow();
|
||||
|
||||
/**
|
||||
* Execute when addBatch() has been used to batch multiple bind executions.
|
||||
*
|
||||
* @return The row counts for each of the batched statements.
|
||||
*/
|
||||
int[] executeBatch();
|
||||
|
||||
/**
|
||||
* Add the statement to batch processing to then later execute via executeBatch().
|
||||
*/
|
||||
void addBatch();
|
||||
|
||||
/**
|
||||
* Return the generated key value.
|
||||
*/
|
||||
|
||||
@@ -359,7 +359,10 @@ public interface Transaction extends AutoCloseable {
|
||||
void setBatchMode(boolean useBatch);
|
||||
|
||||
/**
|
||||
* The JDBC batch mode to use for this transaction.
|
||||
* Deprecated - migrate to {@link #setBatchMode(boolean)}.
|
||||
* <p>
|
||||
* Set the JDBC batch mode to use for this transaction.
|
||||
* </p>
|
||||
* <p>
|
||||
* If this is NONE then JDBC batch can still be used for each request - save(), insert(), update() or delete()
|
||||
* and this would be useful if the request cascades to detail beans.
|
||||
@@ -368,15 +371,23 @@ public interface Transaction extends AutoCloseable {
|
||||
* @param persistBatchMode the batch mode to use for this transaction
|
||||
* @see io.ebean.config.ServerConfig#setPersistBatch(PersistBatch)
|
||||
*/
|
||||
@Deprecated
|
||||
void setBatch(PersistBatch persistBatchMode);
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to {@link #isBatchMode()}.
|
||||
* Return the batch mode at the transaction level.
|
||||
*/
|
||||
@Deprecated
|
||||
PersistBatch getBatch();
|
||||
|
||||
/**
|
||||
* Return the batch mode at the transaction level.
|
||||
*/
|
||||
PersistBatch getBatch();
|
||||
boolean isBatchMode();
|
||||
|
||||
/**
|
||||
* Set the JDBC batch mode to use for a save() or delete() request.
|
||||
* Set the JDBC batch mode to use for a save() or delete() when cascading to children.
|
||||
* <p>
|
||||
* This only takes effect when batch mode on the transaction has not already meant that
|
||||
* JDBC batch mode is being used.
|
||||
@@ -385,17 +396,36 @@ public interface Transaction extends AutoCloseable {
|
||||
* This is useful when the single save() or delete() cascades. For example, inserting a 'master' cascades
|
||||
* and inserts a collection of 'detail' beans. The detail beans can be inserted using JDBC batch.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is effectively already turned on for all platforms apart from older Sql Server.
|
||||
* </p>
|
||||
*
|
||||
* @param batchOnCascadeMode the batch mode to use per save(), insert(), update() or delete()
|
||||
* @param batchMode the batch mode to use per save(), insert(), update() or delete()
|
||||
* @see io.ebean.config.ServerConfig#setPersistBatchOnCascade(PersistBatch)
|
||||
*/
|
||||
void setBatchOnCascade(boolean batchMode);
|
||||
|
||||
/**
|
||||
* Set the batch mode when cascading.
|
||||
* <p>
|
||||
* Deprecated in favour of {@link #setBatchOnCascade(boolean)}
|
||||
* </p>
|
||||
*/
|
||||
@Deprecated
|
||||
void setBatchOnCascade(PersistBatch batchOnCascadeMode);
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to {@link #isBatchMode()}.
|
||||
* Return the batch mode at the request level (for each save(), insert(), update() or delete()).
|
||||
*/
|
||||
@Deprecated
|
||||
PersistBatch getBatchOnCascade();
|
||||
|
||||
/**
|
||||
* Return the batch mode at the request level.
|
||||
*/
|
||||
boolean isBatchOnCascade();
|
||||
|
||||
/**
|
||||
* Specify the number of statements before a batch is flushed automatically.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebean;
|
||||
|
||||
import io.ebean.service.SpiFetchGroupService;
|
||||
import io.ebean.service.SpiProfileLocationFactory;
|
||||
import io.ebean.service.SpiRawSqlService;
|
||||
|
||||
@@ -15,6 +16,16 @@ class XServiceProvider {
|
||||
|
||||
private static SpiProfileLocationFactory profileLocationFactory = initProfileLocation();
|
||||
|
||||
private static SpiFetchGroupService fetchGroupService = initSpiFetchGroupService();
|
||||
|
||||
private static SpiFetchGroupService initSpiFetchGroupService() {
|
||||
Iterator<SpiFetchGroupService> loader = ServiceLoader.load(SpiFetchGroupService.class).iterator();
|
||||
if (loader.hasNext()) {
|
||||
return loader.next();
|
||||
}
|
||||
throw new IllegalStateException("No service implementation found for SpiFetchGroupService?");
|
||||
}
|
||||
|
||||
private static SpiRawSqlService initRawSql() {
|
||||
|
||||
Iterator<SpiRawSqlService> loader = ServiceLoader.load(SpiRawSqlService.class).iterator();
|
||||
@@ -47,4 +58,17 @@ class XServiceProvider {
|
||||
return profileLocationFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the FetchGroup with the given select clause.
|
||||
*/
|
||||
static <T> FetchGroup<T> fetchGroupOf(Class<T> cls, String select) {
|
||||
return fetchGroupService.of(cls, select);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the FetchGroupBuilder with the given select clause.
|
||||
*/
|
||||
static <T> FetchGroupBuilder<T> fetchGroupOf(Class<T> cls) {
|
||||
return fetchGroupService.of(cls);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,21 +71,30 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
/**
|
||||
* Used when a bean is partially filled.
|
||||
*/
|
||||
private final boolean[] loadedProps;
|
||||
|
||||
private boolean fullyLoadedBean;
|
||||
private static final byte FLAG_LOADED_PROP = 1;
|
||||
|
||||
/**
|
||||
* Set of changed properties.
|
||||
*/
|
||||
private boolean[] changedProps;
|
||||
private static final byte FLAG_CHANGED_PROP = 2;
|
||||
|
||||
/**
|
||||
* Flags indicating if a property is a dirty embedded bean. Used to distingush
|
||||
* between an embedded bean being completely overwritten and one of its
|
||||
* embedded properties being made dirty.
|
||||
*/
|
||||
private boolean[] embeddedDirty;
|
||||
private static final byte FLAG_EMBEDDED_DIRTY = 4;
|
||||
|
||||
/**
|
||||
* Flags indicating if a property is a dirty embedded bean. Used to distingush
|
||||
* between an embedded bean being completely overwritten and one of its
|
||||
* embedded properties being made dirty.
|
||||
*/
|
||||
private static final byte FLAG_ORIG_VALUE_SET = 8;
|
||||
|
||||
private final byte[] flags;
|
||||
|
||||
private boolean fullyLoadedBean;
|
||||
|
||||
private Object[] origValues;
|
||||
|
||||
@@ -102,7 +111,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
*/
|
||||
public EntityBeanIntercept(Object ownerBean) {
|
||||
this.owner = (EntityBean) ownerBean;
|
||||
this.loadedProps = new boolean[owner._ebean_getPropertyNames().length];
|
||||
this.flags = new byte[owner._ebean_getPropertyNames().length];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,8 +222,8 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Check each property to see if the bean is partially loaded.
|
||||
*/
|
||||
public boolean isPartial() {
|
||||
for (boolean loadedProp : loadedProps) {
|
||||
if (!loadedProp) {
|
||||
for (byte flag : flags) {
|
||||
if ((flag & FLAG_LOADED_PROP) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -259,10 +268,10 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Return true if only the Id property has been loaded.
|
||||
*/
|
||||
public boolean hasIdOnly(int idIndex) {
|
||||
for (int i = 0; i < loadedProps.length; i++) {
|
||||
for (int i = 0; i < flags.length; i++) {
|
||||
if (i == idIndex) {
|
||||
if (!loadedProps[i]) return false;
|
||||
} else if (loadedProps[i]) {
|
||||
if ((flags[i] & FLAG_LOADED_PROP) == 0) return false;
|
||||
} else if ((flags[i] & FLAG_LOADED_PROP) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -284,9 +293,9 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
if (idPos > -1) {
|
||||
// For cases where properties are set on constructor
|
||||
// set every non Id property to unloaded (for lazy loading)
|
||||
for (int i = 0; i < loadedProps.length; i++) {
|
||||
for (int i = 0; i < flags.length; i++) {
|
||||
if (i != idPos) {
|
||||
loadedProps[i] = false;
|
||||
flags[i] &= ~FLAG_LOADED_PROP;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -352,7 +361,9 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
this.owner._ebean_setEmbeddedLoaded();
|
||||
this.lazyLoadProperty = -1;
|
||||
this.origValues = null;
|
||||
this.changedProps = null;
|
||||
for (int i = 0; i < flags.length; i++) {
|
||||
flags[i] &= ~(FLAG_CHANGED_PROP + FLAG_ORIG_VALUE_SET);
|
||||
}
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
@@ -475,7 +486,11 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
if (position == -1) {
|
||||
throw new IllegalArgumentException("Property " + propertyName + " not found");
|
||||
}
|
||||
loadedProps[position] = loaded;
|
||||
if (loaded) {
|
||||
flags[position] |= FLAG_LOADED_PROP;
|
||||
} else {
|
||||
flags[position] &= ~FLAG_LOADED_PROP;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -483,22 +498,22 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* constructor.
|
||||
*/
|
||||
public void setPropertyUnloaded(int propertyIndex) {
|
||||
loadedProps[propertyIndex] = false;
|
||||
flags[propertyIndex] &= ~FLAG_LOADED_PROP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the property to be loaded.
|
||||
*/
|
||||
public void setLoadedProperty(int propertyIndex) {
|
||||
loadedProps[propertyIndex] = true;
|
||||
flags[propertyIndex] |= FLAG_LOADED_PROP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all properties to be loaded (post insert).
|
||||
*/
|
||||
public void setLoadedPropertyAll() {
|
||||
for (int i = 0; i < loadedProps.length; i++) {
|
||||
loadedProps[i] = true;
|
||||
for (int i = 0; i < flags.length; i++) {
|
||||
flags[i] |= FLAG_LOADED_PROP;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,14 +521,14 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Return true if the property is loaded.
|
||||
*/
|
||||
public boolean isLoadedProperty(int propertyIndex) {
|
||||
return loadedProps[propertyIndex];
|
||||
return (flags[propertyIndex] & FLAG_LOADED_PROP) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the property is considered changed.
|
||||
*/
|
||||
public boolean isChangedProperty(int propertyIndex) {
|
||||
return (changedProps != null && changedProps[propertyIndex]);
|
||||
return (flags[propertyIndex] & FLAG_CHANGED_PROP) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -521,8 +536,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* embedded properties is dirty.
|
||||
*/
|
||||
public boolean isDirtyProperty(int propertyIndex) {
|
||||
return (changedProps != null && changedProps[propertyIndex]
|
||||
|| embeddedDirty != null && embeddedDirty[propertyIndex]);
|
||||
return (flags[propertyIndex] & (FLAG_CHANGED_PROP + FLAG_EMBEDDED_DIRTY)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -534,27 +548,22 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
|
||||
public void setChangedProperty(int propertyIndex) {
|
||||
if (changedProps == null) {
|
||||
changedProps = new boolean[owner._ebean_getPropertyNames().length];
|
||||
}
|
||||
changedProps[propertyIndex] = true;
|
||||
flags[propertyIndex] |= FLAG_CHANGED_PROP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set that an embedded bean has had one of its properties changed.
|
||||
*/
|
||||
private void setEmbeddedPropertyDirty(int propertyIndex) {
|
||||
if (embeddedDirty == null) {
|
||||
embeddedDirty = new boolean[owner._ebean_getPropertyNames().length];
|
||||
}
|
||||
embeddedDirty[propertyIndex] = true;
|
||||
flags[propertyIndex] |= FLAG_EMBEDDED_DIRTY;
|
||||
}
|
||||
|
||||
private void setOriginalValue(int propertyIndex, Object value) {
|
||||
if (origValues == null) {
|
||||
origValues = new Object[owner._ebean_getPropertyNames().length];
|
||||
}
|
||||
if (origValues[propertyIndex] == null) {
|
||||
if ((flags[propertyIndex] & FLAG_ORIG_VALUE_SET) == 0) {
|
||||
flags[propertyIndex] |= FLAG_ORIG_VALUE_SET;
|
||||
origValues[propertyIndex] = value;
|
||||
}
|
||||
}
|
||||
@@ -574,13 +583,9 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
*/
|
||||
public void setNewBeanForUpdate() {
|
||||
|
||||
if (changedProps == null) {
|
||||
changedProps = new boolean[owner._ebean_getPropertyNames().length];
|
||||
}
|
||||
|
||||
for (int i = 0; i < loadedProps.length; i++) {
|
||||
if (loadedProps[i]) {
|
||||
changedProps[i] = true;
|
||||
for (int i = 0; i < flags.length; i++) {
|
||||
if ((flags[i] & FLAG_LOADED_PROP) != 0) {
|
||||
flags[i] |= FLAG_CHANGED_PROP;
|
||||
}
|
||||
}
|
||||
setDirty(true);
|
||||
@@ -594,8 +599,8 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
return null;
|
||||
}
|
||||
Set<String> props = new LinkedHashSet<>();
|
||||
for (int i = 0; i < loadedProps.length; i++) {
|
||||
if (loadedProps[i]) {
|
||||
for (int i = 0; i < flags.length; i++) {
|
||||
if ((flags[i] & FLAG_LOADED_PROP) != 0) {
|
||||
props.add(getProperty(i));
|
||||
}
|
||||
}
|
||||
@@ -609,12 +614,8 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
int len = getPropertyLength();
|
||||
boolean[] dirties = new boolean[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (changedProps != null && changedProps[i]) {
|
||||
dirties[i] = true;
|
||||
} else if (embeddedDirty != null && embeddedDirty[i]) {
|
||||
// an embedded property has been changed - recurse
|
||||
dirties[i] = true;
|
||||
}
|
||||
// this, or an embedded property has been changed - recurse
|
||||
dirties[i] = (flags[i] & (FLAG_CHANGED_PROP + FLAG_EMBEDDED_DIRTY)) != 0;
|
||||
}
|
||||
return dirties;
|
||||
}
|
||||
@@ -634,11 +635,11 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
public void addDirtyPropertyNames(Set<String> props, String prefix) {
|
||||
int len = getPropertyLength();
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (changedProps != null && changedProps[i]) {
|
||||
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
|
||||
// the property has been changed on this bean
|
||||
String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i));
|
||||
props.add(propName);
|
||||
} else if (embeddedDirty != null && embeddedDirty[i]) {
|
||||
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
|
||||
// an embedded property has been changed - recurse
|
||||
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
|
||||
embeddedBean._ebean_getIntercept().addDirtyPropertyNames(props, getProperty(i) + ".");
|
||||
@@ -654,12 +655,12 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
String[] names = owner._ebean_getPropertyNames();
|
||||
int len = getPropertyLength();
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (changedProps != null && changedProps[i]) {
|
||||
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
|
||||
// the property has been changed on this bean
|
||||
if (propertyNames.contains(names[i])) {
|
||||
return true;
|
||||
}
|
||||
} else if (embeddedDirty != null && embeddedDirty[i]) {
|
||||
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
|
||||
if (propertyNames.contains(names[i])) {
|
||||
return true;
|
||||
}
|
||||
@@ -683,15 +684,16 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
public void addDirtyPropertyValues(Map<String, ValuePair> dirtyValues, String prefix) {
|
||||
int len = getPropertyLength();
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (changedProps != null && changedProps[i]) {
|
||||
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
|
||||
// the property has been changed on this bean
|
||||
String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i));
|
||||
Object newVal = owner._ebean_getField(i);
|
||||
Object oldVal = getOrigValue(i);
|
||||
if (!areEqual(oldVal, newVal)) {
|
||||
dirtyValues.put(propName, new ValuePair(newVal, oldVal));
|
||||
}
|
||||
|
||||
dirtyValues.put(propName, new ValuePair(newVal, oldVal));
|
||||
|
||||
} else if (embeddedDirty != null && embeddedDirty[i]) {
|
||||
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
|
||||
// an embedded property has been changed - recurse
|
||||
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
|
||||
embeddedBean._ebean_getIntercept().addDirtyPropertyValues(dirtyValues, getProperty(i) + ".");
|
||||
@@ -705,13 +707,15 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
public void addDirtyPropertyValues(BeanDiffVisitor visitor) {
|
||||
int len = getPropertyLength();
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (changedProps != null && changedProps[i]) {
|
||||
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
|
||||
// the property has been changed on this bean
|
||||
Object newVal = owner._ebean_getField(i);
|
||||
Object oldVal = getOrigValue(i);
|
||||
visitor.visit(i, newVal, oldVal);
|
||||
if (!areEqual(oldVal, newVal)) {
|
||||
visitor.visit(i, newVal, oldVal);
|
||||
}
|
||||
|
||||
} else if (embeddedDirty != null && embeddedDirty[i]) {
|
||||
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
|
||||
// an embedded property has been changed - recurse
|
||||
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
|
||||
visitor.visitPush(i);
|
||||
@@ -739,9 +743,9 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
int len = getPropertyLength();
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (changedProps != null && changedProps[i]) {
|
||||
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
|
||||
sb.append(i).append(',');
|
||||
} else if (embeddedDirty != null && embeddedDirty[i]) {
|
||||
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
|
||||
// an embedded property has been changed - recurse
|
||||
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
|
||||
sb.append(i).append('[');
|
||||
@@ -765,15 +769,12 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
return sb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of property names for changed properties.
|
||||
*/
|
||||
public boolean[] getChanged() {
|
||||
return changedProps;
|
||||
}
|
||||
|
||||
public boolean[] getLoaded() {
|
||||
return loadedProps;
|
||||
boolean[] ret= new boolean[flags.length];
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
ret[i] = (flags[i] & FLAG_LOADED_PROP) != 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -821,7 +822,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
*/
|
||||
private void loadBeanInternal(int loadProperty, BeanLoader loader) {
|
||||
|
||||
if (loadedProps == null || loadedProps[loadProperty]) {
|
||||
if ((flags[loadProperty] & FLAG_LOADED_PROP) != 0) {
|
||||
// race condition where multiple threads calling preGetter concurrently
|
||||
return;
|
||||
}
|
||||
@@ -887,7 +888,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Called when a BeanCollection is initialised automatically.
|
||||
*/
|
||||
public void initialisedMany(int propertyIndex) {
|
||||
loadedProps[propertyIndex] = true;
|
||||
flags[propertyIndex] |= FLAG_LOADED_PROP;
|
||||
}
|
||||
|
||||
private void preGetterCallback(int propertyIndex) {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package io.ebean.cache;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* For query cache entries we additionally hold the dependent tables and timestamp for the query result.
|
||||
* <p>
|
||||
* We use the dependent tables and timestamp to validate that tables the query joins to have not been
|
||||
* modified since the query cache entry was cached. If any dependent tables have since been modified
|
||||
* the query cache entry is treated as invalid.
|
||||
* </p>
|
||||
*/
|
||||
public class QueryCacheEntry {
|
||||
|
||||
private final Object value;
|
||||
|
||||
private final Set<String> dependentTables;
|
||||
|
||||
private final long timestamp;
|
||||
|
||||
/**
|
||||
* Create with dependent tables and timestamp.
|
||||
*
|
||||
* @param value The query result being cached
|
||||
* @param dependentTables The extra tables the query is dependent on (joins to)
|
||||
* @param timestamp The timestamp that the query uses to check for modifications
|
||||
*/
|
||||
public QueryCacheEntry(Object value, Set<String> dependentTables, long timestamp) {
|
||||
this.value = value;
|
||||
this.dependentTables = dependentTables;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the actual query result.
|
||||
*/
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the tables the query result is dependent on.
|
||||
*/
|
||||
public Set<String> getDependentTables() {
|
||||
return dependentTables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the timestamp used to check for modifications on the dependent tables.
|
||||
*/
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.ebean.cache;
|
||||
|
||||
/**
|
||||
* Used to validate that a query cache entry is still valid based on dependent tables.
|
||||
*/
|
||||
public interface QueryCacheEntryValidate {
|
||||
|
||||
/**
|
||||
* Return true if the entry is still valid based on dependent tables.
|
||||
*/
|
||||
boolean isValid(QueryCacheEntry queryCacheEntry);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package io.ebean.cache;
|
||||
|
||||
import io.ebean.config.CurrentTenantProvider;
|
||||
|
||||
/**
|
||||
* Configuration used to create ServerCache instances.
|
||||
*/
|
||||
public class ServerCacheConfig {
|
||||
|
||||
private final ServerCacheType type;
|
||||
private final String cacheKey;
|
||||
private final ServerCacheOptions cacheOptions;
|
||||
private final CurrentTenantProvider tenantProvider;
|
||||
private final QueryCacheEntryValidate queryCacheEntryValidate;
|
||||
|
||||
public ServerCacheConfig(ServerCacheType type, String cacheKey, ServerCacheOptions cacheOptions, CurrentTenantProvider tenantProvider, QueryCacheEntryValidate queryCacheEntryValidate) {
|
||||
this.type = type;
|
||||
this.cacheKey = cacheKey;
|
||||
this.cacheOptions = cacheOptions;
|
||||
this.tenantProvider = tenantProvider;
|
||||
this.queryCacheEntryValidate = queryCacheEntryValidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cache type.
|
||||
*/
|
||||
public ServerCacheType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the cache.
|
||||
*/
|
||||
public String getCacheKey() {
|
||||
return cacheKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the tuning options.
|
||||
*/
|
||||
public ServerCacheOptions getCacheOptions() {
|
||||
return cacheOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current tenant provider.
|
||||
*/
|
||||
public CurrentTenantProvider getTenantProvider() {
|
||||
return tenantProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the service that provides validation for query cache entries.
|
||||
*/
|
||||
public QueryCacheEntryValidate getQueryCacheEntryValidate() {
|
||||
return queryCacheEntryValidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the cache is a query cache.
|
||||
*/
|
||||
public boolean isQueryCache() {
|
||||
return type == ServerCacheType.QUERY;
|
||||
}
|
||||
}
|
||||
+10
-3
@@ -1,7 +1,5 @@
|
||||
package io.ebean.cache;
|
||||
|
||||
import io.ebean.config.CurrentTenantProvider;
|
||||
|
||||
/**
|
||||
* Defines method for constructing caches for beans and queries.
|
||||
*/
|
||||
@@ -10,6 +8,15 @@ public interface ServerCacheFactory {
|
||||
/**
|
||||
* Create the cache for the given type with options.
|
||||
*/
|
||||
ServerCache createCache(ServerCacheType type, String cacheKey, CurrentTenantProvider tenantProvider, ServerCacheOptions cacheOptions);
|
||||
ServerCache createCache(ServerCacheConfig config);
|
||||
|
||||
/**
|
||||
* Return a ServerCacheNotify that we will send ServerCacheNotification events to.
|
||||
* <p>
|
||||
* This is used if a ServerCacheNotifyPlugin is not supplied.
|
||||
* </p>
|
||||
*
|
||||
* @param listener The listener that should be used to process the notification events.
|
||||
*/
|
||||
ServerCacheNotify createCacheNotify(ServerCacheNotify listener);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package io.ebean.cache;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Notification event that dependent tables have been modified.
|
||||
* <p>
|
||||
* This is sent to other interested servers (in the cluster).
|
||||
* </p>
|
||||
*/
|
||||
public class ServerCacheNotification {
|
||||
|
||||
private final long modifyTimestamp;
|
||||
|
||||
private final Set<String> dependentTables;
|
||||
|
||||
public ServerCacheNotification(long modifyTimestamp, Set<String> dependentTables) {
|
||||
this.modifyTimestamp = modifyTimestamp;
|
||||
this.dependentTables = dependentTables;
|
||||
}
|
||||
|
||||
public long getModifyTimestamp() {
|
||||
return modifyTimestamp;
|
||||
}
|
||||
|
||||
public Set<String> getDependentTables() {
|
||||
return dependentTables;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.ebean.cache;
|
||||
|
||||
/**
|
||||
* Interface for both listening to notification changes and sending them to other members of the cluster.
|
||||
*/
|
||||
public interface ServerCacheNotify {
|
||||
|
||||
/**
|
||||
* Notify other server cache members of the table modifications or process the notifications.
|
||||
*/
|
||||
void notify(ServerCacheNotification notification);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.ebean.cache;
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
|
||||
/**
|
||||
* Plugin that provides a ServerCacheNotify implementation.
|
||||
* <p>
|
||||
* Is supplied this will be used to send the ServerCacheNotification event to other cluster members.
|
||||
* </p>
|
||||
*/
|
||||
public interface ServerCacheNotifyPlugin {
|
||||
|
||||
/**
|
||||
* Create a ServerCacheNotify implementation given the server configuration.
|
||||
*/
|
||||
ServerCacheNotify create(ServerConfig serverConfig);
|
||||
}
|
||||
@@ -273,14 +273,16 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
checkReadOnly();
|
||||
init();
|
||||
if (modifyListening) {
|
||||
Object oldBean = map.put(key, value);
|
||||
E oldBean = map.put(key, value);
|
||||
if (value != oldBean) {
|
||||
// register the add of the new and the removal of the old
|
||||
modifyAddition(value);
|
||||
modifyRemoval(oldBean);
|
||||
}
|
||||
return oldBean;
|
||||
} else {
|
||||
return map.put(key, value);
|
||||
}
|
||||
return map.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -295,8 +297,9 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
modifyRemoval(oldBean);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
map.putAll(puts);
|
||||
}
|
||||
map.putAll(puts);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -102,6 +102,16 @@ public class DbMigrationConfig {
|
||||
*/
|
||||
protected Map<String, String> runPlaceholderMap;
|
||||
|
||||
/**
|
||||
* DB schema used for the migration (and testing).
|
||||
*/
|
||||
protected String dbSchema;
|
||||
|
||||
/**
|
||||
* Set to true if we consider this the 'default schema' (Postgres schema that matches DB username)
|
||||
*/
|
||||
protected boolean defaultDbSchema;
|
||||
|
||||
/**
|
||||
* DB user used to run the DB migration.
|
||||
*/
|
||||
@@ -373,6 +383,39 @@ public class DbMigrationConfig {
|
||||
this.dbPassword = dbPassword;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DB schema to use (for migration, testing etc).
|
||||
*/
|
||||
public String getDbSchema() {
|
||||
String schema = readEnvironment("ddl.migration.schema");
|
||||
if (schema != null) {
|
||||
return schema;
|
||||
}
|
||||
return dbSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Db schema to use.
|
||||
*/
|
||||
public void setDbSchema(String dbSchema) {
|
||||
this.dbSchema = dbSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Db schema if it hasn't already been defined.
|
||||
*/
|
||||
public void setDefaultDbSchema(String dbSchema) {
|
||||
this.defaultDbSchema = true;
|
||||
this.dbSchema = dbSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is considered the default DB schema (Postgres schema matching DB username).
|
||||
*/
|
||||
public boolean isDefaultDbSchema() {
|
||||
return defaultDbSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return migration versions that should be added to history without running.
|
||||
*/
|
||||
@@ -475,6 +518,7 @@ public class DbMigrationConfig {
|
||||
runMigration = properties.getBoolean("migration.run", runMigration);
|
||||
metaTable = properties.get("migration.metaTable", metaTable);
|
||||
runPlaceholders = properties.get("migration.placeholders", runPlaceholders);
|
||||
dbSchema = properties.get("migration.dbSchema", dbSchema);
|
||||
|
||||
//Do not set user and pass from "datasource.db.username"
|
||||
//There is a null test in MigrationRunner::getConnection to handle this
|
||||
@@ -557,6 +601,10 @@ public class DbMigrationConfig {
|
||||
runnerConfig.setRunPlaceholders(runPlaceholders);
|
||||
runnerConfig.setDbUsername(getDbUsername());
|
||||
runnerConfig.setDbPassword(getDbPassword());
|
||||
runnerConfig.setDbSchema(getDbSchema());
|
||||
if (defaultDbSchema) {
|
||||
runnerConfig.setSetCurrentSchema(false);
|
||||
}
|
||||
runnerConfig.setClassLoader(classLoader);
|
||||
if (patchInsertOn != null) {
|
||||
runnerConfig.setPatchInsertOn(patchInsertOn);
|
||||
|
||||
@@ -33,6 +33,7 @@ import io.ebean.util.StringHelper;
|
||||
import org.avaje.datasource.DataSourceConfig;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.time.Clock;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -57,22 +58,24 @@ import java.util.ServiceLoader;
|
||||
* <pre>{@code
|
||||
*
|
||||
* ServerConfig c = new ServerConfig();
|
||||
* c.setName("db");
|
||||
*
|
||||
* // read the ebean.properties and load
|
||||
* // those settings into this serverConfig object
|
||||
* c.loadFromProperties();
|
||||
*
|
||||
* // add any classes found in the app.data package
|
||||
* c.addPackage("com.myapp.domain");
|
||||
*
|
||||
* // register as the 'Default' server
|
||||
* c.setDefaultServer(true);
|
||||
* // explicitly register the entity beans to avoid classpath scanning
|
||||
* c.addClass(Customer.class);
|
||||
* c.addClass(User.class);
|
||||
*
|
||||
* EbeanServer server = EbeanServerFactory.create(c);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Note that ServerConfigProvider provides a standard Java ServiceLoader mechanism that can
|
||||
* be used to apply configuration to the ServerConfig.
|
||||
* </p>
|
||||
*
|
||||
* @author emcgreal
|
||||
* @author rbygrave
|
||||
* @see EbeanServerFactory
|
||||
@@ -293,6 +296,11 @@ public class ServerConfig {
|
||||
*/
|
||||
private DataSourceConfig readOnlyDataSourceConfig = new DataSourceConfig();
|
||||
|
||||
/**
|
||||
* Optional - the database schema that should be used to own the tables etc.
|
||||
*/
|
||||
private String dbSchema;
|
||||
|
||||
/**
|
||||
* The db migration config (migration resource path etc).
|
||||
*/
|
||||
@@ -360,6 +368,11 @@ public class ServerConfig {
|
||||
*/
|
||||
private String uuidStateFile = "ebean-uuid.state";
|
||||
|
||||
/**
|
||||
* The clock used for setting the timestamps (e.g. @UpdatedTimestamp) on objects.
|
||||
*/
|
||||
private Clock clock = Clock.systemUTC();
|
||||
|
||||
private List<IdGenerator> idGenerators = new ArrayList<>();
|
||||
private List<BeanFindController> findControllers = new ArrayList<>();
|
||||
private List<BeanPersistController> persistControllers = new ArrayList<>();
|
||||
@@ -505,6 +518,20 @@ public class ServerConfig {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the clock used for setting the timestamps (e.g. @UpdatedTimestamp) on objects.
|
||||
*/
|
||||
public Clock getClock() {
|
||||
return clock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the clock used for setting the timestamps (e.g. @UpdatedTimestamp) on objects.
|
||||
*/
|
||||
public void setClock(final Clock clock) {
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the slow query time in millis.
|
||||
*/
|
||||
@@ -1098,6 +1125,25 @@ public class ServerConfig {
|
||||
this.profilingConfig = profilingConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DB schema to use.
|
||||
*/
|
||||
public String getDbSchema() {
|
||||
return dbSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DB schema to use. This specifies to use this schema for:
|
||||
* <ul>
|
||||
* <li>Running Database migrations - Create and use the DB schema</li>
|
||||
* <li>Testing DDL - Create-all.sql DDL execution creates and uses schema</li>
|
||||
* <li>Testing Docker - Set default schema on connection URL</li>
|
||||
* </ul>
|
||||
*/
|
||||
public void setDbSchema(String dbSchema) {
|
||||
this.dbSchema = dbSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DB migration configuration.
|
||||
*/
|
||||
@@ -1919,6 +1965,15 @@ public class ServerConfig {
|
||||
* Return the UUID state file.
|
||||
*/
|
||||
public String getUuidStateFile() {
|
||||
if (uuidStateFile == null || uuidStateFile.isEmpty()) {
|
||||
// by default, add servername...
|
||||
uuidStateFile = name + "-uuid.state";
|
||||
// and store it in the user's home directory
|
||||
String homeDir = System.getProperty("user.home");
|
||||
if (homeDir != null && homeDir.isEmpty()) {
|
||||
uuidStateFile = homeDir + "/.ebean/" + uuidStateFile;
|
||||
}
|
||||
}
|
||||
return uuidStateFile;
|
||||
}
|
||||
|
||||
@@ -2742,6 +2797,10 @@ public class ServerConfig {
|
||||
*/
|
||||
protected void loadSettings(PropertiesWrapper p) {
|
||||
|
||||
dbSchema = p.get("dbSchema", dbSchema);
|
||||
if (dbSchema != null) {
|
||||
migrationConfig.setDefaultDbSchema(dbSchema);
|
||||
}
|
||||
profilingConfig.loadSettings(p, name);
|
||||
migrationConfig.loadSettings(p, name);
|
||||
platformConfig.loadSettings(p);
|
||||
@@ -3080,9 +3139,10 @@ public class ServerConfig {
|
||||
* @return A copy of the PlatformConfig with overridden properties
|
||||
*/
|
||||
public PlatformConfig newPlatformConfig(String propertiesPath, String platformPrefix) {
|
||||
|
||||
if (properties == null) {
|
||||
properties = new Properties();
|
||||
}
|
||||
PropertiesWrapper p = new PropertiesWrapper(propertiesPath, platformPrefix, properties, classLoadConfig);
|
||||
|
||||
PlatformConfig config = new PlatformConfig(platformConfig);
|
||||
config.loadSettings(p);
|
||||
return config;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package io.ebean.config;
|
||||
|
||||
/**
|
||||
* Provides a ServiceLoader based mechanism to configure a ServerConfig.
|
||||
* <p>
|
||||
* Provide an implementation and register it via the standard Java ServiceLoader mechanism
|
||||
* via a file at <code>META-INF/services/io.ebean.config.ServerConfigProvider</code>.
|
||||
* </p>
|
||||
* <p>
|
||||
* If you are using a DI container like Spring or Guice you are unlikely to use this but instead use a
|
||||
* spring specific configuration. When we are not using a DI container we may use this mechanism to
|
||||
* explicitly register the entity beans and avoid classpath scanning.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* public class EbeanConfigProvider implements ServerConfigProvider {
|
||||
*
|
||||
* @Override
|
||||
* public void apply(ServerConfig config) {
|
||||
*
|
||||
* // register the entity bean classes explicitly
|
||||
* config.addClass(Customer.class);
|
||||
* config.addClass(User.class);
|
||||
* ...
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public interface ServerConfigProvider {
|
||||
|
||||
/**
|
||||
* Apply the configuration to the ServerConfig.
|
||||
* <p>
|
||||
* Typically we explicitly register entity bean classes and thus avoid classpath scanning.
|
||||
* </p>
|
||||
*/
|
||||
void apply(ServerConfig config);
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package io.ebean.config.dbplatform;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.annotation.PartitionMode;
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.config.CustomDbTypeMapping;
|
||||
@@ -16,6 +17,7 @@ import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
@@ -50,7 +52,7 @@ public class DatabasePlatform {
|
||||
/**
|
||||
* The behaviour used when ending a read only transaction at read committed isolation level.
|
||||
*/
|
||||
protected OnQueryOnly onQueryOnly = OnQueryOnly.ROLLBACK;
|
||||
protected OnQueryOnly onQueryOnly = OnQueryOnly.COMMIT;
|
||||
|
||||
/**
|
||||
* The open quote used by quoted identifiers.
|
||||
@@ -62,8 +64,6 @@ public class DatabasePlatform {
|
||||
*/
|
||||
protected String closeQuote = "\"";
|
||||
|
||||
protected String concatOperator = "||";
|
||||
|
||||
/**
|
||||
* When set to true all db column names and table names use quoted identifiers.
|
||||
*/
|
||||
@@ -453,13 +453,6 @@ public class DatabasePlatform {
|
||||
return openQuote;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DB concat operator.
|
||||
*/
|
||||
public String getConcatOperator() {
|
||||
return concatOperator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JDBC type used to store booleans.
|
||||
*/
|
||||
@@ -658,6 +651,39 @@ public class DatabasePlatform {
|
||||
return persistBatchOnCascade;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the DB schema if it does not exist.
|
||||
*/
|
||||
public void createSchemaIfNotExists(String dbSchema, Connection connection) throws SQLException {
|
||||
if (!schemaExists(dbSchema, connection)) {
|
||||
Statement query = connection.createStatement();
|
||||
try {
|
||||
logger.info("create schema:{}", dbSchema);
|
||||
query.executeUpdate("create schema " + dbSchema);
|
||||
} finally {
|
||||
JdbcClose.close(query);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the schema exists.
|
||||
*/
|
||||
public boolean schemaExists(String dbSchema, Connection connection) throws SQLException {
|
||||
ResultSet schemas = connection.getMetaData().getSchemas();
|
||||
try {
|
||||
while (schemas.next()) {
|
||||
String schema = schemas.getString(1);
|
||||
if (schema.equalsIgnoreCase(dbSchema)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
JdbcClose.close(schemas);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the table exists.
|
||||
*/
|
||||
@@ -672,6 +698,20 @@ public class DatabasePlatform {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if partitions exist for the given table.
|
||||
*/
|
||||
public boolean tablePartitionsExist(Connection connection, String table) throws SQLException {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL to create an initial partition for the given table.
|
||||
*/
|
||||
public String tablePartitionInit(String tableName, PartitionMode mode, String property, String singlePrimaryKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes the like string for this DB-Platform
|
||||
*/
|
||||
|
||||
@@ -8,7 +8,9 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public class DbIdentity {
|
||||
|
||||
private static final Pattern TABLE_REPLACE = Pattern.compile("{table}", Pattern.LITERAL);
|
||||
private static final String TABLE_PLACEHOLDER = "{table}";
|
||||
|
||||
private static final Pattern TABLE_REPLACE = Pattern.compile(TABLE_PLACEHOLDER, Pattern.LITERAL);
|
||||
|
||||
/**
|
||||
* Set if this DB supports sequences. Note some DB's support both Sequences
|
||||
@@ -53,7 +55,9 @@ public class DbIdentity {
|
||||
if (selectLastInsertedIdTemplate == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!selectLastInsertedIdTemplate.contains(TABLE_PLACEHOLDER)) {
|
||||
return selectLastInsertedIdTemplate;
|
||||
}
|
||||
return TABLE_REPLACE.matcher(selectLastInsertedIdTemplate).replaceAll(Matcher.quoteReplacement(table));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebean.config.dbplatform.postgres;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.annotation.PartitionMode;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
@@ -11,6 +12,10 @@ import io.ebean.config.dbplatform.PlatformIdGenerator;
|
||||
import io.ebean.config.dbplatform.SqlErrorCodes;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
@@ -116,4 +121,31 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
return sql + " for update";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tablePartitionsExist(Connection connection, String table) throws SQLException {
|
||||
|
||||
try (PreparedStatement statement = connection.prepareStatement("select count(*) from pg_inherits i WHERE i.inhparent = ?::regclass")) {
|
||||
statement.setString(1, table);
|
||||
try (ResultSet resultSet = statement.executeQuery()) {
|
||||
return resultSet.next() && resultSet.getInt(1) > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return SQL using built in partition helper functions to create some initial partitions.
|
||||
*
|
||||
* Only use this if extra-dll doesn't have some initial partitions defined (which it should).
|
||||
*/
|
||||
public String tablePartitionInit(String tableName, PartitionMode mode, String property, String pkey) {
|
||||
if (property == null) {
|
||||
property = "";
|
||||
}
|
||||
if (pkey == null) {
|
||||
pkey = "";
|
||||
}
|
||||
return "select partition('" + mode.name().toLowerCase() + "','" + tableName + "','" + pkey + "','" + property + "',1);";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,22 +18,22 @@ import java.util.Set;
|
||||
*/
|
||||
class LoadContext {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(LoadContext.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(LoadContext.class);
|
||||
|
||||
/**
|
||||
* Map we are loading the properties into.
|
||||
*/
|
||||
private final Map<String, String> map = new LinkedHashMap<>();
|
||||
private final Map<String, String> map = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Names of resources/files that were loaded.
|
||||
*/
|
||||
private final Set<String> loadedResources = new LinkedHashSet<>();
|
||||
private final Set<String> loadedResources = new LinkedHashSet<>();
|
||||
|
||||
/**
|
||||
* Return the input stream (maybe null) for the given source.
|
||||
*/
|
||||
InputStream resource(String resourcePath, Loader.Source source) {
|
||||
InputStream resource(String resourcePath, Loader.Source source) {
|
||||
|
||||
InputStream is = null;
|
||||
if (source == Loader.Source.RESOURCE) {
|
||||
@@ -46,21 +46,21 @@ class LoadContext {
|
||||
if (file.exists()) {
|
||||
try {
|
||||
is = new FileInputStream(file);
|
||||
loadedResources.add("file:"+resourcePath);
|
||||
loadedResources.add("file:" + resourcePath);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return is;
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
private InputStream resourceStream(String resourcePath) {
|
||||
InputStream is = getClass().getResourceAsStream("/" + resourcePath);
|
||||
if (is == null) {
|
||||
// search the module path for top level resource
|
||||
is = ClassLoader.getSystemResourceAsStream(resourcePath);
|
||||
is = ClassLoader.getSystemResourceAsStream(resourcePath);
|
||||
}
|
||||
return is;
|
||||
}
|
||||
@@ -68,30 +68,30 @@ class LoadContext {
|
||||
/**
|
||||
* Add a property entry.
|
||||
*/
|
||||
void put(String key, String val) {
|
||||
if (val != null) {
|
||||
val = val.trim();
|
||||
void put(String key, String val) {
|
||||
if (val != null) {
|
||||
val = val.trim();
|
||||
}
|
||||
map.put(key, val);
|
||||
}
|
||||
map.put(key, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate all the expressions and return as a Properties object.
|
||||
*/
|
||||
Properties eval() {
|
||||
Properties eval() {
|
||||
|
||||
log.info("loaded properties from {}", loadedResources);
|
||||
log.info("loaded properties from {}", loadedResources);
|
||||
|
||||
Properties properties = new Properties();
|
||||
Properties properties = new Properties();
|
||||
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = PropertyEval.eval(entry.getValue());
|
||||
properties.setProperty(key, value);
|
||||
}
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = PropertyEval.eval(entry.getValue());
|
||||
properties.setProperty(key, value);
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the special properties that can point to an external properties source.
|
||||
|
||||
@@ -16,10 +16,10 @@ import java.util.Properties;
|
||||
*/
|
||||
class Loader {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(Loader.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(Loader.class);
|
||||
|
||||
enum Source {
|
||||
RESOURCE,
|
||||
enum Source {
|
||||
RESOURCE,
|
||||
FILE
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class Loader {
|
||||
|
||||
private YamlLoader yamlLoader;
|
||||
|
||||
Loader() {
|
||||
Loader() {
|
||||
String skipYaml = System.getProperty("ebeanSkipYaml");
|
||||
if (!"true".equals(skipYaml)) {
|
||||
try {
|
||||
@@ -44,19 +44,19 @@ class Loader {
|
||||
/**
|
||||
* Load the configuration with the expected ordering.
|
||||
*/
|
||||
void load() {
|
||||
void load() {
|
||||
|
||||
loadMain(Source.RESOURCE);
|
||||
// external file configuration overrides the resources configuration
|
||||
loadMain(Source.FILE);
|
||||
|
||||
loadViaSystemProperty();
|
||||
loadViaIndirection();
|
||||
loadViaSystemProperty();
|
||||
loadViaIndirection();
|
||||
|
||||
// test configuration (if found) overrides main configuration
|
||||
// test configuration (if found) overrides main configuration
|
||||
// we should only find these resources when running tests
|
||||
loadTest();
|
||||
}
|
||||
loadTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load test configuration.
|
||||
@@ -89,7 +89,7 @@ class Loader {
|
||||
}
|
||||
|
||||
private void loadViaSystemProperty() {
|
||||
String fileName = System.getenv("EBEAN_PROPS_FILE");
|
||||
String fileName = System.getenv("EBEAN_PROPS_FILE");
|
||||
if (fileName == null) {
|
||||
fileName = System.getProperty("ebean.props.file");
|
||||
if (fileName != null) {
|
||||
@@ -112,59 +112,66 @@ class Loader {
|
||||
* Evaluate all the configuration entries and return as properties.
|
||||
*/
|
||||
Properties eval() {
|
||||
return loadContext.eval();
|
||||
}
|
||||
return loadContext.eval();
|
||||
}
|
||||
|
||||
void loadYaml(String resourcePath, Source source) {
|
||||
if (yamlLoader != null) {
|
||||
try {
|
||||
yamlLoader.load(resource(resourcePath, source));
|
||||
void loadYaml(String resourcePath, Source source) {
|
||||
if (yamlLoader != null) {
|
||||
try {
|
||||
try (InputStream is = resource(resourcePath, source)) {
|
||||
yamlLoader.load(is);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to read yml from:" + resourcePath, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loadProperties(String resourcePath, Source source) {
|
||||
InputStream is = resource(resourcePath, source);
|
||||
if (is != null) {
|
||||
loadProperties(is);
|
||||
}
|
||||
}
|
||||
void loadProperties(String resourcePath, Source source) {
|
||||
try {
|
||||
try (InputStream is = resource(resourcePath, source)) {
|
||||
if (is != null) {
|
||||
loadProperties(is);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to read properties from:" + resourcePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream resource(String resourcePath, Source source) {
|
||||
return loadContext.resource(resourcePath, source);
|
||||
}
|
||||
private InputStream resource(String resourcePath, Source source) {
|
||||
return loadContext.resource(resourcePath, source);
|
||||
}
|
||||
|
||||
private void loadProperties(InputStream is) {
|
||||
private void loadProperties(InputStream is) {
|
||||
|
||||
if (is != null) {
|
||||
try {
|
||||
Properties properties = new Properties();
|
||||
properties.load(is);
|
||||
put(properties);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to load properties?", e);
|
||||
} finally {
|
||||
close(is);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is != null) {
|
||||
try {
|
||||
Properties properties = new Properties();
|
||||
properties.load(is);
|
||||
put(properties);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to load properties?", e);
|
||||
} finally {
|
||||
close(is);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void put(Properties properties) {
|
||||
Enumeration<?> enumeration = properties.propertyNames();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
String key = (String) enumeration.nextElement();
|
||||
String property = properties.getProperty(key);
|
||||
loadContext.put(key, property);
|
||||
}
|
||||
}
|
||||
private void put(Properties properties) {
|
||||
Enumeration<?> enumeration = properties.propertyNames();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
String key = (String) enumeration.nextElement();
|
||||
String property = properties.getProperty(key);
|
||||
loadContext.put(key, property);
|
||||
}
|
||||
}
|
||||
|
||||
private void close(InputStream is) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
log.warn("Error closing input stream for properties", e);
|
||||
}
|
||||
}
|
||||
private void close(InputStream is) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
log.warn("Error closing input stream for properties", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
@@ -98,6 +99,11 @@ public interface DbMigration {
|
||||
*/
|
||||
void setIncludeGeneratedFileComment(boolean includeGeneratedFileComment);
|
||||
|
||||
/**
|
||||
* Set this to false to exclude the builtin support for table partitioning (with @DbPartition).
|
||||
*/
|
||||
void setIncludeBuiltInPartitioning(boolean includeBuiltInPartitioning);
|
||||
|
||||
/**
|
||||
* Set the header that is included in the generated DDL script.
|
||||
*/
|
||||
@@ -142,6 +148,11 @@ public interface DbMigration {
|
||||
*/
|
||||
void addDatabasePlatform(DatabasePlatform databasePlatform, String prefix);
|
||||
|
||||
/**
|
||||
* Return the list of versions that contain pending drops.
|
||||
*/
|
||||
List<String> getPendingDrops();
|
||||
|
||||
/**
|
||||
* Generate the next migration xml file and associated apply and rollback sql scripts.
|
||||
* <p>
|
||||
|
||||
@@ -9,6 +9,10 @@ import io.ebean.event.BeanQueryAdapter;
|
||||
import io.ebeanservice.docstore.api.mapping.DocumentMapping;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Information and methods on BeanDescriptors made available to plugins.
|
||||
@@ -18,6 +22,7 @@ public interface BeanType<T> {
|
||||
/**
|
||||
* Return the short name of the bean type.
|
||||
*/
|
||||
@Nonnull
|
||||
String getName();
|
||||
|
||||
/**
|
||||
@@ -28,11 +33,13 @@ public interface BeanType<T> {
|
||||
/**
|
||||
* Return the full name of the bean type.
|
||||
*/
|
||||
@Nonnull
|
||||
String getFullName();
|
||||
|
||||
/**
|
||||
* Return the class type this BeanDescriptor describes.
|
||||
*/
|
||||
@Nonnull
|
||||
Class<T> getBeanType();
|
||||
|
||||
/**
|
||||
@@ -43,6 +50,7 @@ public interface BeanType<T> {
|
||||
/**
|
||||
* Return all the properties for this bean type.
|
||||
*/
|
||||
@Nonnull
|
||||
Collection<? extends Property> allProperties();
|
||||
|
||||
/**
|
||||
@@ -197,6 +205,28 @@ public interface BeanType<T> {
|
||||
*/
|
||||
boolean hasInheritance();
|
||||
|
||||
/**
|
||||
* Return true if this object is the root level object in its entity
|
||||
* inheritance.
|
||||
*/
|
||||
boolean isInheritanceRoot();
|
||||
|
||||
/**
|
||||
* Returns all direct children of this beantype
|
||||
*/
|
||||
List<BeanType<?>> getInheritanceChildren();
|
||||
|
||||
/**
|
||||
* Returns the parent in inheritance hiearchy
|
||||
*/
|
||||
BeanType<?> getInheritanceParent();
|
||||
|
||||
/**
|
||||
* Visit all children recursively
|
||||
* @param visitor
|
||||
*/
|
||||
void visitAllInheritanceChildren(Consumer<BeanType<?>> visitor);
|
||||
|
||||
/**
|
||||
* Return the discriminator column.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package io.ebean.plugin;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Property of a entity bean that can be read.
|
||||
*/
|
||||
@@ -8,8 +10,15 @@ public interface Property {
|
||||
/**
|
||||
* Return the name of the property.
|
||||
*/
|
||||
@Nonnull
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Return the type of the property.
|
||||
*/
|
||||
@Nonnull
|
||||
Class<?> getPropertyType();
|
||||
|
||||
/**
|
||||
* Return the value of the property on the given bean.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package io.ebean.service;
|
||||
|
||||
import io.ebean.FetchGroup;
|
||||
import io.ebean.FetchGroupBuilder;
|
||||
|
||||
/**
|
||||
* Service that parses FetchGroup expressions.
|
||||
*/
|
||||
public interface SpiFetchGroupService {
|
||||
|
||||
/**
|
||||
* Return the FetchGroup with the given select clause.
|
||||
*
|
||||
* @param beanType The type of entity bean the fetch group is for
|
||||
* @param select The properties to select (top level properties)
|
||||
*/
|
||||
<T> FetchGroup<T> of(Class<T> beanType, String select);
|
||||
|
||||
/**
|
||||
* Create and return a FetchGroupBuilder starting with a select() clause.
|
||||
*
|
||||
* @param beanType The type of entity bean the fetch group is for
|
||||
* @return The FetchGroupBuilder to add additional select and fetch clauses
|
||||
*/
|
||||
<T> FetchGroupBuilder<T> of(Class<T> beanType);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Context used to read binary format messages.
|
||||
*/
|
||||
public class BinaryReadContext {
|
||||
|
||||
private final DataInputStream in;
|
||||
|
||||
/**
|
||||
* Create with protocol 0 and byte data.
|
||||
*/
|
||||
public BinaryReadContext(byte[] byteData) {
|
||||
this(new DataInputStream(new ByteArrayInputStream(byteData)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create with protocol version and DataInputStream data.
|
||||
*/
|
||||
public BinaryReadContext(DataInputStream in) {
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
public DataInputStream in() {
|
||||
return in;
|
||||
}
|
||||
|
||||
public boolean readBoolean() throws IOException {
|
||||
return in.readBoolean();
|
||||
}
|
||||
|
||||
public int readInt() throws IOException {
|
||||
return in.readInt();
|
||||
}
|
||||
|
||||
public String readUTF() throws IOException {
|
||||
return in.readUTF();
|
||||
}
|
||||
|
||||
public long readLong() throws IOException {
|
||||
return in.readLong();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Messages that can be sent in binary form.
|
||||
* <p>
|
||||
* Mainly RemoteTransactionEvent which is sent to cluster members.
|
||||
* </p>
|
||||
*/
|
||||
public interface BinaryWritable {
|
||||
|
||||
int TYPE_BEANIUD = 1;
|
||||
int TYPE_TABLEIUD = 2;
|
||||
int TYPE_CACHE = 3;
|
||||
int TYPE_TABLEMOD = 4;
|
||||
|
||||
/**
|
||||
* Write message in binary format.
|
||||
*/
|
||||
void writeBinary(BinaryWriteContext out) throws IOException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Context used to write binary message (like RemoteTransactionEvent).
|
||||
*/
|
||||
public class BinaryWriteContext {
|
||||
|
||||
private final DataOutputStream out;
|
||||
|
||||
private long counter;
|
||||
|
||||
public BinaryWriteContext(DataOutputStream out) {
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of message parts that have been written.
|
||||
*/
|
||||
public long counter() {
|
||||
return counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the output stream to write to.
|
||||
*/
|
||||
public DataOutputStream os() {
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a message part with a given type code.
|
||||
*/
|
||||
public DataOutputStream start(int type) throws IOException {
|
||||
counter++;
|
||||
out.writeBoolean(true);
|
||||
out.writeInt(type);
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* End of message parts.
|
||||
*/
|
||||
public void end() throws IOException {
|
||||
out.writeBoolean(false);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.persist.MultiValueWrapper;
|
||||
import io.ebeaninternal.server.querydefn.NaturalKeyBindParam;
|
||||
|
||||
import java.io.Serializable;
|
||||
@@ -154,6 +155,10 @@ public class BindParams implements Serializable {
|
||||
public void setParameter(int position, Object value) {
|
||||
|
||||
Param p = getParam(position);
|
||||
if (value instanceof Collection) {
|
||||
// use of postgres ANY with positioned parameter
|
||||
value = new MultiValueWrapper((Collection)value);
|
||||
}
|
||||
p.setInValue(value);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,10 @@ public class ScopeTrans {
|
||||
*/
|
||||
private boolean rolledBack;
|
||||
|
||||
/**
|
||||
* Flag set when nested commit has occurred.
|
||||
*/
|
||||
private boolean nestedCommit;
|
||||
|
||||
public ScopeTrans(boolean rollbackOnChecked, boolean created, SpiTransaction transaction, TxScope txScope) {
|
||||
|
||||
@@ -113,7 +117,6 @@ public class ScopeTrans {
|
||||
complete();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Complete the transaction programmatically. Try to commit.
|
||||
*/
|
||||
@@ -124,7 +127,7 @@ public class ScopeTrans {
|
||||
}
|
||||
|
||||
public void end() {
|
||||
if (created) {
|
||||
if (created || !nestedCommit) {
|
||||
transaction.end();
|
||||
}
|
||||
}
|
||||
@@ -133,6 +136,7 @@ public class ScopeTrans {
|
||||
if (created) {
|
||||
transaction.commit();
|
||||
} else {
|
||||
nestedCommit = true;
|
||||
transaction.setBatchFlushOnQuery(restoreBatchFlushOnQuery);
|
||||
if (restoreBatch != null) {
|
||||
transaction.setBatch(restoreBatch);
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* Plugin API available to invoke something prior to container bootup.
|
||||
* <p>
|
||||
* The initial intent is to provide a hook for 'docker-run' such that we can automatically ensure
|
||||
* we have a test DB docker container running and setup ready to go.
|
||||
* </p>
|
||||
*/
|
||||
public interface SpiContainerBootup {
|
||||
|
||||
/**
|
||||
* Run something at bootup prior to the container starting.
|
||||
*
|
||||
* For example, start DB docker container(s).
|
||||
*/
|
||||
void bootup();
|
||||
}
|
||||
@@ -2,8 +2,11 @@ package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.DtoQuery;
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.ExtendedServer;
|
||||
import io.ebean.PersistenceContextScope;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.RowConsumer;
|
||||
import io.ebean.RowMapper;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.TxScope;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
@@ -29,7 +32,12 @@ import java.util.function.Predicate;
|
||||
/**
|
||||
* Service Provider extension to EbeanServer.
|
||||
*/
|
||||
public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionLoader {
|
||||
public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanLoader, BeanCollectionLoader {
|
||||
|
||||
/**
|
||||
* Return the log manager.
|
||||
*/
|
||||
SpiLogManager log();
|
||||
|
||||
/**
|
||||
* Return the server extended Json context.
|
||||
@@ -229,6 +237,31 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
*/
|
||||
void scopedTransactionExit(Object returnOrThrowable, int opCode);
|
||||
|
||||
/**
|
||||
* SqlQuery find single attribute.
|
||||
*/
|
||||
<T> T findSingleAttribute(SpiSqlQuery query, Class<T> cls);
|
||||
|
||||
/**
|
||||
* SqlQuery find single attribute list.
|
||||
*/
|
||||
<T> List<T> findSingleAttributeList(SpiSqlQuery query, Class<T> cls);
|
||||
|
||||
/**
|
||||
* SqlQuery find one with mapper.
|
||||
*/
|
||||
<T> T findOneMapper(SpiSqlQuery query, RowMapper<T> mapper);
|
||||
|
||||
/**
|
||||
* SqlQuery find list with mapper.
|
||||
*/
|
||||
<T> List<T> findListMapper(SpiSqlQuery query, RowMapper<T> mapper);
|
||||
|
||||
/**
|
||||
* SqlQuery find each with consumer.
|
||||
*/
|
||||
void findEachRow(SpiSqlQuery query, RowConsumer consumer);
|
||||
|
||||
/**
|
||||
* DTO findList query.
|
||||
*/
|
||||
@@ -268,4 +301,19 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
* Return true if a row for the bean type and id exists.
|
||||
*/
|
||||
boolean exists(Class<?> beanType, Object beanId, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Add to JDBC batch for later execution.
|
||||
*/
|
||||
void addBatch(SpiSqlUpdate defaultSqlUpdate, SpiTransaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the batched statement.
|
||||
*/
|
||||
int[] executeBatch(SpiSqlUpdate defaultSqlUpdate, SpiTransaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the sql update regardless of transaction batch mode.
|
||||
*/
|
||||
int executeNow(SpiSqlUpdate sqlUpdate);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* Log manager for SQL, TXN and Summary logging.
|
||||
* <p>
|
||||
* In general at runtime this uses SLF4J Logger but this abstraction allows us to capture
|
||||
* the logged SQL during testing such that we can assert against the executed sql if desired.
|
||||
* </p>
|
||||
*/
|
||||
public interface SpiLogManager {
|
||||
|
||||
/**
|
||||
* Return the SQL logger.
|
||||
*/
|
||||
SpiLogger sql();
|
||||
|
||||
/**
|
||||
* Return the TXN logger.
|
||||
*/
|
||||
SpiLogger txn();
|
||||
|
||||
/**
|
||||
* Return the Summary logger.
|
||||
*/
|
||||
SpiLogger sum();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* Logger for SQL, TXN and Summary logging.
|
||||
* <p>
|
||||
* In general at runtime this uses SLF4J Logger but this abstraction allows us to capture
|
||||
* the logged SQL during testing such that we can assert against the executed sql if desired.
|
||||
* </p>
|
||||
*/
|
||||
public interface SpiLogger {
|
||||
|
||||
/**
|
||||
* Is debug logging enabled.
|
||||
*/
|
||||
boolean isDebug();
|
||||
|
||||
/**
|
||||
* Is trace logging enabled.
|
||||
*/
|
||||
boolean isTrace();
|
||||
|
||||
/**
|
||||
* Log a debug level message.
|
||||
*/
|
||||
void debug(String msg);
|
||||
|
||||
/**
|
||||
* Log a trace level message.
|
||||
*/
|
||||
void trace(String msg);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* API for Plugins to define the logger for io.ebean.SQL, io.ebean.TXN and io.ebean.SUM.
|
||||
*/
|
||||
public interface SpiLoggerFactory {
|
||||
|
||||
/**
|
||||
* Create the logger given the name.
|
||||
*/
|
||||
SpiLogger create(String name);
|
||||
}
|
||||
@@ -134,27 +134,40 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
/**
|
||||
* Includes soft deletes rows in the result.
|
||||
*/
|
||||
SOFT_DELETED,
|
||||
SOFT_DELETED(false),
|
||||
|
||||
/**
|
||||
* Query runs against draft tables.
|
||||
*/
|
||||
DRAFT,
|
||||
DRAFT(false),
|
||||
|
||||
/**
|
||||
* Query runs against current data (normal).
|
||||
*/
|
||||
CURRENT,
|
||||
CURRENT(false),
|
||||
|
||||
/**
|
||||
* Query runs potentially returning many versions of the same bean.
|
||||
*/
|
||||
VERSIONS,
|
||||
VERSIONS(true),
|
||||
|
||||
/**
|
||||
* Query runs 'As Of' a given date time.
|
||||
*/
|
||||
AS_OF;
|
||||
AS_OF(true);
|
||||
|
||||
private final boolean history;
|
||||
|
||||
TemporalMode(boolean history) {
|
||||
this.history = history;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a history query.
|
||||
*/
|
||||
public boolean isHistory() {
|
||||
return history;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mode of the query of if null return CURRENT mode.
|
||||
@@ -644,9 +657,9 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
boolean isBeanCachePut();
|
||||
|
||||
/**
|
||||
* Return true if the bean cache is being explicitly loaded via RECACHE mode.
|
||||
* Return true if we must hit the DB (Cache reload or select for update).
|
||||
*/
|
||||
boolean isBeanCacheReload();
|
||||
boolean isForceHitDatabase();
|
||||
|
||||
/**
|
||||
* Return the cache mode for using the bean cache (Get and Put).
|
||||
|
||||
@@ -101,6 +101,11 @@ public interface SpiTransaction extends Transaction {
|
||||
*/
|
||||
String getId();
|
||||
|
||||
/**
|
||||
* Return the start timestamp for the transaction (JVM side).
|
||||
*/
|
||||
long getStartMillis();
|
||||
|
||||
/**
|
||||
* Return true if this transaction has updateAllLoadedProperties set.
|
||||
* If null is returned the server default is used (set on ServerConfig).
|
||||
@@ -179,7 +184,7 @@ public interface SpiTransaction extends Transaction {
|
||||
* Return true if this request should be batched. Conversely returns false
|
||||
* if this request should be executed immediately.
|
||||
*/
|
||||
boolean isBatchThisRequest(PersistRequest.Type type);
|
||||
boolean isBatchThisRequest();
|
||||
|
||||
/**
|
||||
* Return the BatchControl used to batch up persist requests.
|
||||
|
||||
@@ -30,6 +30,11 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
return transaction.translate(message, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartMillis() {
|
||||
return transaction.getStartMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLabel(String label) {
|
||||
transaction.setLabel(label);
|
||||
@@ -245,6 +250,11 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
transaction.setBatchMode(useBatch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBatchMode() {
|
||||
return transaction.isBatchMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatch(PersistBatch persistBatchMode) {
|
||||
transaction.setBatch(persistBatchMode);
|
||||
@@ -255,11 +265,21 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
return transaction.getBatch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatchOnCascade(boolean batchMode) {
|
||||
transaction.setBatchOnCascade(batchMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBatchOnCascade(PersistBatch batchOnCascadeMode) {
|
||||
transaction.setBatchOnCascade(batchOnCascadeMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBatchOnCascade() {
|
||||
return transaction.isBatchOnCascade();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistBatch getBatchOnCascade() {
|
||||
return transaction.getBatchOnCascade();
|
||||
@@ -356,8 +376,8 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBatchThisRequest(PersistRequest.Type type) {
|
||||
return transaction.isBatchThisRequest(type);
|
||||
public boolean isBatchThisRequest() {
|
||||
return transaction.isBatchThisRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,7 +3,9 @@ package io.ebeaninternal.api;
|
||||
import io.ebeaninternal.server.cache.CacheChangeSet;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import io.ebeaninternal.server.transaction.DeleteByIdMap;
|
||||
import io.ebeaninternal.server.transaction.TransactionManager;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdates;
|
||||
|
||||
import java.io.Serializable;
|
||||
@@ -108,8 +110,20 @@ public class TransactionEvent implements Serializable {
|
||||
/**
|
||||
* Build and return the cache changeSet.
|
||||
*/
|
||||
public CacheChangeSet buildCacheChanges(boolean viewInvalidation) {
|
||||
CacheChangeSet changeSet = new CacheChangeSet(viewInvalidation);
|
||||
public CacheChangeSet buildCacheChanges(TransactionManager manager) {
|
||||
|
||||
if (eventBeans == null && deleteByIdMap == null && eventTables == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CacheChangeSet changeSet = new CacheChangeSet(manager.clockNowMillis());
|
||||
if (eventTables != null && !eventTables.isEmpty()) {
|
||||
// notify cache with table based changes
|
||||
BeanDescriptorManager dm = manager.getBeanDescriptorManager();
|
||||
for (TransactionEventTable.TableIUD tableIUD : eventTables.values()) {
|
||||
dm.cacheNotify(tableIUD, changeSet);
|
||||
}
|
||||
}
|
||||
if (eventBeans != null) {
|
||||
eventBeans.notifyCache(changeSet);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.event.BulkTableEvent;
|
||||
import io.ebeaninternal.server.cluster.BinaryMessage;
|
||||
import io.ebeaninternal.server.cluster.BinaryMessageList;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
@@ -12,7 +9,7 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class TransactionEventTable implements Serializable {
|
||||
public final class TransactionEventTable implements Serializable, BinaryWritable {
|
||||
|
||||
private static final long serialVersionUID = 2236555729767483264L;
|
||||
|
||||
@@ -23,20 +20,13 @@ public final class TransactionEventTable implements Serializable {
|
||||
return "TransactionEventTable " + map.values();
|
||||
}
|
||||
|
||||
public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
|
||||
|
||||
@Override
|
||||
public void writeBinary(BinaryWriteContext out) throws IOException {
|
||||
for (TableIUD tableIud : map.values()) {
|
||||
tableIud.writeBinaryMessage(msgList);
|
||||
tableIud.writeBinary(out);
|
||||
}
|
||||
}
|
||||
|
||||
public void readBinaryMessage(DataInput dataInput) throws IOException {
|
||||
|
||||
TableIUD tableIud = TableIUD.readBinaryMessage(dataInput);
|
||||
map.put(tableIud.getTableName(), tableIud);
|
||||
}
|
||||
|
||||
|
||||
public void add(TransactionEventTable table) {
|
||||
|
||||
for (TableIUD iud : table.values()) {
|
||||
@@ -47,7 +37,6 @@ public final class TransactionEventTable implements Serializable {
|
||||
public void add(String table, boolean insert, boolean update, boolean delete) {
|
||||
|
||||
table = table.toUpperCase();
|
||||
|
||||
add(new TableIUD(table, insert, update, delete));
|
||||
}
|
||||
|
||||
@@ -67,7 +56,7 @@ public final class TransactionEventTable implements Serializable {
|
||||
return map.values();
|
||||
}
|
||||
|
||||
public static class TableIUD implements Serializable, BulkTableEvent {
|
||||
public static class TableIUD implements Serializable, BulkTableEvent, BinaryWritable {
|
||||
|
||||
private static final long serialVersionUID = -1958317571064162089L;
|
||||
|
||||
@@ -83,7 +72,7 @@ public final class TransactionEventTable implements Serializable {
|
||||
this.delete = delete;
|
||||
}
|
||||
|
||||
public static TableIUD readBinaryMessage(DataInput dataInput) throws IOException {
|
||||
public static TableIUD readBinaryMessage(BinaryReadContext dataInput) throws IOException {
|
||||
|
||||
String table = dataInput.readUTF();
|
||||
boolean insert = dataInput.readBoolean();
|
||||
@@ -93,17 +82,13 @@ public final class TransactionEventTable implements Serializable {
|
||||
return new TableIUD(table, insert, update, delete);
|
||||
}
|
||||
|
||||
public void writeBinaryMessage(BinaryMessageList msgList) throws IOException {
|
||||
|
||||
BinaryMessage msg = new BinaryMessage(table.length() + 10);
|
||||
DataOutputStream os = msg.getOs();
|
||||
os.writeInt(BinaryMessage.TYPE_TABLEIUD);
|
||||
@Override
|
||||
public void writeBinary(BinaryWriteContext out) throws IOException {
|
||||
DataOutputStream os = out.start(TYPE_TABLEIUD);
|
||||
os.writeUTF(table);
|
||||
os.writeBoolean(insert);
|
||||
os.writeBoolean(update);
|
||||
os.writeBoolean(delete);
|
||||
|
||||
msgList.add(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -13,7 +13,7 @@ public interface TxnProfileEventCodes {
|
||||
String EVT_INSERT = "i";
|
||||
String EVT_UPDATE = "u";
|
||||
String EVT_DELETE = "d";
|
||||
String EVT_SOFT_DELETE = "ds";
|
||||
String EVT_DELETE_SOFT = "ds";
|
||||
String EVT_DELETE_PERMANENT = "dp";
|
||||
String EVT_ORMUPDATE = "uo";
|
||||
String FIND_UPDATE = "uq";
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package io.ebeaninternal.dbmigration;
|
||||
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebeaninternal.dbmigration.model.CurrentModel;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.migration.ddl.DdlRunner;
|
||||
import io.ebean.util.JdbcClose;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.dbmigration.model.CurrentModel;
|
||||
import io.ebeaninternal.dbmigration.model.MTable;
|
||||
import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
|
||||
import io.ebeaninternal.server.deploy.PartitionMeta;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -38,6 +41,7 @@ public class DdlGenerator {
|
||||
private final boolean createOnly;
|
||||
private final boolean jaxbPresent;
|
||||
private final boolean ddlCommitOnCreateIndex;
|
||||
private final String dbSchema;
|
||||
|
||||
private CurrentModel currentModel;
|
||||
private String dropAllContent;
|
||||
@@ -48,6 +52,7 @@ public class DdlGenerator {
|
||||
this.jaxbPresent = serverConfig.getClassLoadConfig().isJavaxJAXBPresent();
|
||||
this.generateDdl = serverConfig.isDdlGenerate();
|
||||
this.createOnly = serverConfig.isDdlCreateOnly();
|
||||
this.dbSchema = serverConfig.getDbSchema();
|
||||
if (!serverConfig.getTenantMode().isDdlEnabled() && serverConfig.isDdlRun()) {
|
||||
log.warn("DDL can't be run on startup with TenantMode " + serverConfig.getTenantMode());
|
||||
this.runDdl = false;
|
||||
@@ -85,30 +90,54 @@ public class DdlGenerator {
|
||||
* Run the DDL drop and DDL create scripts if properties have been set.
|
||||
*/
|
||||
protected void runDdl() {
|
||||
|
||||
if (runDdl) {
|
||||
Connection connection = null;
|
||||
try {
|
||||
runInitSql();
|
||||
runDropSql();
|
||||
runCreateSql();
|
||||
runSeedSql();
|
||||
|
||||
} catch (IOException e) {
|
||||
String msg = "Error reading drop/create script from file system";
|
||||
throw new RuntimeException(msg, e);
|
||||
connection = obtainConnection();
|
||||
runDdlWith(connection);
|
||||
} finally {
|
||||
JdbcClose.rollback(connection);
|
||||
JdbcClose.close(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void runDdlWith(Connection connection) {
|
||||
try {
|
||||
if (dbSchema != null) {
|
||||
createSchemaIfRequired(connection);
|
||||
}
|
||||
runInitSql(connection);
|
||||
runDropSql(connection);
|
||||
runCreateSql(connection);
|
||||
runSeedSql(connection);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error reading drop/create script from file system", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Connection obtainConnection() {
|
||||
try {
|
||||
return server.getPluginApi().getDataSource().getConnection();
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException("Failed to obtain connection to run DDL", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void createSchemaIfRequired(Connection connection) {
|
||||
try {
|
||||
server.getDatabasePlatform().createSchemaIfNotExists(dbSchema, connection);
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException("Failed to create DB Schema", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all the DDL statements in the script.
|
||||
*/
|
||||
public int runScript(boolean expectErrors, String content, String scriptName) {
|
||||
public int runScript(Connection connection, boolean expectErrors, String content, String scriptName) {
|
||||
|
||||
DdlRunner runner = new DdlRunner(expectErrors, scriptName);
|
||||
|
||||
Transaction transaction = server.createTransaction();
|
||||
Connection connection = transaction.getConnection();
|
||||
try {
|
||||
if (expectErrors) {
|
||||
connection.setAutoCommit(true);
|
||||
@@ -119,64 +148,104 @@ public class DdlGenerator {
|
||||
if (expectErrors) {
|
||||
connection.setAutoCommit(false);
|
||||
}
|
||||
transaction.commit();
|
||||
connection.commit();
|
||||
return count;
|
||||
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException("Failed to run script", e);
|
||||
|
||||
} finally {
|
||||
transaction.end();
|
||||
JdbcClose.rollback(connection);
|
||||
}
|
||||
}
|
||||
|
||||
protected void runDropSql() throws IOException {
|
||||
protected void runDropSql(Connection connection) throws IOException {
|
||||
if (!createOnly) {
|
||||
String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl");
|
||||
if (!"true".equalsIgnoreCase(ignoreExtraDdl) && jaxbPresent) {
|
||||
String extraApply = ExtraDdlXmlReader.buildExtra(server.getDatabasePlatform().getName(), true);
|
||||
if (extraApply != null) {
|
||||
runScript(false, extraApply, "extra-dll");
|
||||
runScript(connection, false, extraApply, "extra-dll");
|
||||
}
|
||||
}
|
||||
|
||||
if (dropAllContent == null) {
|
||||
dropAllContent = readFile(getDropFileName());
|
||||
}
|
||||
runScript(true, dropAllContent, getDropFileName());
|
||||
runScript(connection, true, dropAllContent, getDropFileName());
|
||||
}
|
||||
}
|
||||
|
||||
protected void runCreateSql() throws IOException {
|
||||
protected void runCreateSql(Connection connection) throws IOException {
|
||||
if (createAllContent == null) {
|
||||
createAllContent = readFile(getCreateFileName());
|
||||
}
|
||||
runScript(false, createAllContent, getCreateFileName());
|
||||
runScript(connection, false, createAllContent, getCreateFileName());
|
||||
|
||||
String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl");
|
||||
if (!"true".equalsIgnoreCase(ignoreExtraDdl) && jaxbPresent) {
|
||||
if (currentModel.isTablePartitioning()) {
|
||||
String extraPartitioning = ExtraDdlXmlReader.buildPartitioning(server.getDatabasePlatform().getName());
|
||||
if (extraPartitioning != null && !extraPartitioning.isEmpty()) {
|
||||
runScript(connection, false, extraPartitioning, "builtin-partitioning-dll");
|
||||
}
|
||||
}
|
||||
|
||||
String extraApply = ExtraDdlXmlReader.buildExtra(server.getDatabasePlatform().getName(), false);
|
||||
if (extraApply != null) {
|
||||
runScript(false, extraApply, "extra-dll");
|
||||
runScript(connection, false, extraApply, "extra-dll");
|
||||
}
|
||||
|
||||
if (currentModel.isTablePartitioning()) {
|
||||
checkInitialTablePartitions(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void runInitSql() throws IOException {
|
||||
runResourceScript(server.getServerConfig().getDdlInitSql());
|
||||
/**
|
||||
* Check if table partitions exist and if not create some. The expectation is that
|
||||
* extra-dll.xml should have some partition initialisation but this helps people get going.
|
||||
*/
|
||||
private void checkInitialTablePartitions(Connection connection) {
|
||||
|
||||
DatabasePlatform databasePlatform = server.getDatabasePlatform();
|
||||
try {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (MTable table : currentModel.getPartitionedTables()) {
|
||||
String tableName = table.getName();
|
||||
if (!databasePlatform.tablePartitionsExist(connection, tableName)) {
|
||||
log.info("No table partitions for table {}", tableName);
|
||||
PartitionMeta meta = table.getPartitionMeta();
|
||||
String initPart = databasePlatform.tablePartitionInit(tableName, meta.getMode(), meta.getProperty(), table.singlePrimaryKey());
|
||||
sb.append(initPart).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
String initialPartitionSql = sb.toString();
|
||||
if (!initialPartitionSql.isEmpty()) {
|
||||
runScript(connection, false, initialPartitionSql, "initial table partitions");
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
log.error("Error checking initial table partitions", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void runSeedSql() throws IOException {
|
||||
runResourceScript(server.getServerConfig().getDdlSeedSql());
|
||||
protected void runInitSql(Connection connection) throws IOException {
|
||||
runResourceScript(connection, server.getServerConfig().getDdlInitSql());
|
||||
}
|
||||
|
||||
protected void runResourceScript(String sqlScript) throws IOException {
|
||||
protected void runSeedSql(Connection connection) throws IOException {
|
||||
runResourceScript(connection, server.getServerConfig().getDdlSeedSql());
|
||||
}
|
||||
|
||||
protected void runResourceScript(Connection connection, String sqlScript) throws IOException {
|
||||
|
||||
if (sqlScript != null) {
|
||||
InputStream is = getClassLoader().getResourceAsStream(sqlScript);
|
||||
if (is != null) {
|
||||
String content = readContent(new InputStreamReader(is));
|
||||
runScript(false, content, sqlScript);
|
||||
try (InputStream is = getClassLoader().getResourceAsStream(sqlScript)) {
|
||||
if (is != null) {
|
||||
String content = readContent(new InputStreamReader(is));
|
||||
runScript(connection, false, content, sqlScript);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ import java.util.List;
|
||||
*/
|
||||
public class DefaultDbMigration implements DbMigration {
|
||||
|
||||
protected static final Logger logger = LoggerFactory.getLogger(DefaultDbMigration.class);
|
||||
protected static final Logger logger = LoggerFactory.getLogger("io.ebean.GenerateMigration");
|
||||
|
||||
private static final String initialVersion = "1.0";
|
||||
|
||||
@@ -84,6 +84,8 @@ public class DefaultDbMigration implements DbMigration {
|
||||
|
||||
protected DatabasePlatform databasePlatform;
|
||||
|
||||
private boolean vanillaPlatform;
|
||||
|
||||
protected List<Pair> platforms = new ArrayList<>();
|
||||
|
||||
protected ServerConfig serverConfig;
|
||||
@@ -98,6 +100,8 @@ public class DefaultDbMigration implements DbMigration {
|
||||
protected String name;
|
||||
protected String generatePendingDrop;
|
||||
|
||||
protected boolean includeBuiltInPartitioning = true;
|
||||
|
||||
/**
|
||||
* Create for offline migration generation.
|
||||
*/
|
||||
@@ -179,6 +183,11 @@ public class DefaultDbMigration implements DbMigration {
|
||||
this.includeGeneratedFileComment = includeGeneratedFileComment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIncludeBuiltInPartitioning(boolean includeBuiltInPartitioning) {
|
||||
this.includeBuiltInPartitioning = includeBuiltInPartitioning;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeader(String header) {
|
||||
this.header = header;
|
||||
@@ -192,6 +201,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
*/
|
||||
@Override
|
||||
public void setPlatform(Platform platform) {
|
||||
vanillaPlatform = true;
|
||||
setPlatform(getPlatform(platform));
|
||||
}
|
||||
|
||||
@@ -276,7 +286,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
try {
|
||||
Request request = createRequest();
|
||||
if (platforms.isEmpty()) {
|
||||
generateExtraDdl(request.migrationDir, databasePlatform);
|
||||
generateExtraDdl(request.migrationDir, databasePlatform, request.isTablePartitioning());
|
||||
}
|
||||
|
||||
String pendingVersion = generatePendingDrop();
|
||||
@@ -293,6 +303,23 @@ public class DefaultDbMigration implements DbMigration {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the versions containing pending drops.
|
||||
*/
|
||||
public List<String> getPendingDrops() {
|
||||
if (!online) {
|
||||
DbOffline.setGenerateMigration();
|
||||
}
|
||||
setDefaults();
|
||||
try {
|
||||
return createRequest().getPendingDrops();
|
||||
} finally {
|
||||
if (!online) {
|
||||
DbOffline.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the configuration for each of the target platforms.
|
||||
*/
|
||||
@@ -311,16 +338,23 @@ public class DefaultDbMigration implements DbMigration {
|
||||
* migration runner.
|
||||
* </p>
|
||||
*/
|
||||
private void generateExtraDdl(File migrationDir, DatabasePlatform dbPlatform) throws IOException {
|
||||
private void generateExtraDdl(File migrationDir, DatabasePlatform dbPlatform, boolean tablePartitioning) throws IOException {
|
||||
|
||||
if (dbPlatform != null) {
|
||||
ExtraDdl extraDdl = ExtraDdlXmlReader.read("/extra-ddl.xml");
|
||||
if (extraDdl != null) {
|
||||
List<DdlScript> ddlScript = extraDdl.getDdlScript();
|
||||
for (DdlScript script : ddlScript) {
|
||||
if (!script.isDrop() && ExtraDdlXmlReader.matchPlatform(dbPlatform.getName(), script.getPlatforms())) {
|
||||
writeExtraDdl(migrationDir, script);
|
||||
}
|
||||
if (tablePartitioning && includeBuiltInPartitioning) {
|
||||
generateExtraDdlFor(migrationDir, dbPlatform, ExtraDdlXmlReader.readBuiltinTablePartitioning());
|
||||
}
|
||||
generateExtraDdlFor(migrationDir, dbPlatform, ExtraDdlXmlReader.readBuiltin());
|
||||
generateExtraDdlFor(migrationDir, dbPlatform, ExtraDdlXmlReader.read());
|
||||
}
|
||||
}
|
||||
|
||||
private void generateExtraDdlFor(File migrationDir, DatabasePlatform dbPlatform, ExtraDdl extraDdl) throws IOException {
|
||||
if (extraDdl != null) {
|
||||
List<DdlScript> ddlScript = extraDdl.getDdlScript();
|
||||
for (DdlScript script : ddlScript) {
|
||||
if (!script.isDrop() && ExtraDdlXmlReader.matchPlatform(dbPlatform.getName(), script.getPlatforms())) {
|
||||
writeExtraDdl(migrationDir, script);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -331,7 +365,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
*/
|
||||
private void writeExtraDdl(File migrationDir, DdlScript script) throws IOException {
|
||||
|
||||
String fullName = repeatableMigrationName(script.getName());
|
||||
String fullName = repeatableMigrationName(script.isInit(), script.getName());
|
||||
|
||||
logger.info("writing repeatable script {}", fullName);
|
||||
|
||||
@@ -342,8 +376,16 @@ public class DefaultDbMigration implements DbMigration {
|
||||
}
|
||||
}
|
||||
|
||||
private String repeatableMigrationName(String scriptName) {
|
||||
return "R__" + scriptName.replace(' ', '_') + migrationConfig.getApplySuffix();
|
||||
private String repeatableMigrationName(boolean init, String scriptName) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (init) {
|
||||
sb.append("I__");
|
||||
} else {
|
||||
sb.append("R__");
|
||||
}
|
||||
sb.append(scriptName.replace(' ', '_'));
|
||||
sb.append(migrationConfig.getApplySuffix());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,6 +446,10 @@ public class DefaultDbMigration implements DbMigration {
|
||||
this.current = currentModel.read();
|
||||
}
|
||||
|
||||
boolean isTablePartitioning() {
|
||||
return current.isTablePartitioning();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the migration for the pending drops for a given version.
|
||||
*/
|
||||
@@ -465,6 +511,9 @@ public class DefaultDbMigration implements DbMigration {
|
||||
if (nextDrop != null) {
|
||||
return nextDrop;
|
||||
}
|
||||
if (generatePendingDrop != null) {
|
||||
return generatePendingDrop;
|
||||
}
|
||||
return migrationConfig.getGeneratePendingDrop();
|
||||
}
|
||||
|
||||
@@ -511,7 +560,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
File subPath = platformWriter.subPath(writePath, pair.prefix);
|
||||
platformWriter.processMigration(dbMigration, platformBuffer, subPath, fullVersion);
|
||||
|
||||
generateExtraDdl(subPath, pair.platform);
|
||||
generateExtraDdl(subPath, pair.platform, currentModel.isTablePartitioning());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,11 +591,10 @@ public class DefaultDbMigration implements DbMigration {
|
||||
if (server == null) {
|
||||
setServer(Ebean.getDefaultServer());
|
||||
}
|
||||
if (databasePlatform == null && platforms.isEmpty()) {
|
||||
// not explicitly set not set a list of platforms so
|
||||
// default to the platform of the default server
|
||||
if (vanillaPlatform || databasePlatform == null) {
|
||||
// not explicitly set so use the platform of the server
|
||||
databasePlatform = server.getDatabasePlatform();
|
||||
logger.debug("set platform to {}", databasePlatform.getName());
|
||||
logger.trace("set platform to {}", databasePlatform.getName());
|
||||
}
|
||||
if (migrationConfig != null) {
|
||||
if (strictMode != null) {
|
||||
@@ -567,9 +615,6 @@ public class DefaultDbMigration implements DbMigration {
|
||||
if (name != null) {
|
||||
migrationConfig.setName(name);
|
||||
}
|
||||
if (generatePendingDrop != null) {
|
||||
migrationConfig.setGeneratePendingDrop(generatePendingDrop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-4
@@ -257,6 +257,8 @@ public class BaseTableDdl implements TableDdl {
|
||||
useSequence = (IdType.SEQUENCE == useDbIdentityType);
|
||||
}
|
||||
|
||||
String partitionMode = createTable.getPartitionMode();
|
||||
|
||||
DdlBuffer apply = writer.apply();
|
||||
apply.append("create table ").append(tableName).append(" (");
|
||||
writeTableColumns(apply, columns, useIdentity);
|
||||
@@ -265,7 +267,9 @@ public class BaseTableDdl implements TableDdl {
|
||||
writeCompoundUniqueConstraints(apply, createTable);
|
||||
if (!pk.isEmpty()) {
|
||||
// defined on the columns
|
||||
writePrimaryKeyConstraint(apply, createTable.getPkName(), toColumnNames(pk));
|
||||
if (partitionMode == null || !platformDdl.suppressPrimaryKeyOnPartition()) {
|
||||
writePrimaryKeyConstraint(apply, createTable.getPkName(), toColumnNames(pk));
|
||||
}
|
||||
}
|
||||
if (platformDdl.isInlineForeignKeys()) {
|
||||
writeInlineForeignKeys(writer, createTable);
|
||||
@@ -273,6 +277,9 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
apply.newLine().append(")");
|
||||
addTableCommentInline(apply, createTable);
|
||||
if (partitionMode != null) {
|
||||
platformDdl.addTablePartition(apply, partitionMode, createTable.getPartitionColumn());
|
||||
}
|
||||
apply.endOfStatement();
|
||||
|
||||
addComments(apply, createTable);
|
||||
@@ -840,12 +847,15 @@ public class BaseTableDdl implements TableDdl {
|
||||
if (hasValue(alterColumn.getComment())) {
|
||||
alterColumnComment(writer, alterColumn);
|
||||
}
|
||||
if (hasValue(alterColumn.getDropCheckConstraint())) {
|
||||
dropCheckConstraint(writer, alterColumn, alterColumn.getDropCheckConstraint());
|
||||
}
|
||||
|
||||
boolean alterCheckConstraint = hasValue(alterColumn.getCheckConstraint());
|
||||
|
||||
if (alterCheckConstraint) {
|
||||
// drop constraint before altering type etc
|
||||
dropCheckConstraint(writer, alterColumn);
|
||||
dropCheckConstraint(writer, alterColumn, alterColumn.getCheckConstraintName());
|
||||
}
|
||||
boolean alterBaseAttributes = false;
|
||||
if (hasValue(alterColumn.getType())) {
|
||||
@@ -922,9 +932,9 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
}
|
||||
|
||||
protected void dropCheckConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
protected void dropCheckConstraint(DdlWrite writer, AlterColumn alter, String constraintName) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterTableDropConstraint(alter.getTableName(), alter.getCheckConstraintName());
|
||||
String ddl = platformDdl.alterTableDropConstraint(alter.getTableName(), constraintName);
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
|
||||
@@ -38,9 +38,20 @@ public class MySqlDdl extends PlatformDdl {
|
||||
return "alter table " + tableName + " drop foreign key " + maxConstraintName(fkName);
|
||||
}
|
||||
|
||||
/**
|
||||
* It is rather complex to delete a column on MySql as there must not exist any foreign keys.
|
||||
* That's why we call a user stored procedure here
|
||||
*/
|
||||
@Override
|
||||
public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException {
|
||||
|
||||
buffer.append("CALL usp_ebean_drop_column('").append(tableName).append("', '").append(columnName).append("')").endOfStatement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String alterTableDropConstraint(String tableName, String constraintName) {
|
||||
// drop constraint not supported
|
||||
// drop constraint not supported in MySQL 5.7 and 8.0 but starting with MariaDB 10.2.1 CHECK is evaluated
|
||||
// TODO: Implement for MariaDB >= 10.2.1
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -60,7 +71,7 @@ public class MySqlDdl extends PlatformDdl {
|
||||
@Override
|
||||
public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) {
|
||||
|
||||
String suffix = DdlHelp.isDropDefault(defaultValue) ? columnDropDefault : columnSetDefault + " " + defaultValue;
|
||||
String suffix = DdlHelp.isDropDefault(defaultValue) ? columnDropDefault : columnSetDefault + " " + convertDefaultValue(defaultValue);
|
||||
|
||||
// use alter
|
||||
return "alter table " + tableName + " alter " + columnName + " " + suffix;
|
||||
|
||||
@@ -79,8 +79,6 @@ public class PlatformDdl {
|
||||
|
||||
protected String alterColumn = "alter column";
|
||||
|
||||
protected String dropConstraint = "drop constraint";
|
||||
|
||||
protected String dropUniqueConstraint = "drop constraint";
|
||||
|
||||
protected String addConstraint = "add constraint";
|
||||
@@ -248,6 +246,9 @@ public class PlatformDdl {
|
||||
* Convert the standard type to the platform specific type.
|
||||
*/
|
||||
public String convert(String type, boolean identity) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
if (type.contains("[]")) {
|
||||
return convertArrayType(type);
|
||||
}
|
||||
@@ -420,7 +421,7 @@ public class PlatformDdl {
|
||||
* Drop a unique constraint from the table.
|
||||
*/
|
||||
public String alterTableDropConstraint(String tableName, String constraintName) {
|
||||
return "alter table " + tableName + " " + dropConstraint + " " + maxConstraintName(constraintName);
|
||||
return "alter table " + tableName + " " + dropConstraintIfExists + " " + maxConstraintName(constraintName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -661,4 +662,12 @@ public class PlatformDdl {
|
||||
public void unlockTables(DdlBuffer buffer, Collection<String> tables) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
public boolean suppressPrimaryKeyOnPartition() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addTablePartition(DdlBuffer apply, String partitionMode, String partitionColumn) throws IOException {
|
||||
// only supported by postgres initially
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Postgres specific DDL.
|
||||
@@ -16,6 +19,11 @@ public class PostgresDdl extends PlatformDdl {
|
||||
this.columnSetNull = "drop not null";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean suppressPrimaryKeyOnPartition() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String convertArrayType(String logicalArrayType) {
|
||||
return NativeDbArray.logicalToNative(logicalArrayType);
|
||||
@@ -38,4 +46,9 @@ public class PostgresDdl extends PlatformDdl {
|
||||
}
|
||||
return columnDefn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTablePartition(DdlBuffer apply, String partitionMode, String partitionColumn) throws IOException {
|
||||
apply.append(" partition by range (").append(partitionColumn).append(")");
|
||||
}
|
||||
}
|
||||
|
||||
+12
-4
@@ -79,13 +79,21 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
|
||||
protected void createOrReplaceFunction(DdlBuffer apply, String procedureName, String historyTable, List<String> includedColumns) throws IOException {
|
||||
apply
|
||||
.append("create or replace function ").append(procedureName).append("() returns trigger as $$").newLine()
|
||||
.append("begin").newLine();
|
||||
.append("create or replace function ").append(procedureName).append("() returns trigger as $$").newLine();
|
||||
|
||||
apply.append("declare").newLine()
|
||||
.append(" lowerTs timestamptz;").newLine()
|
||||
.append(" upperTs timestamptz;").newLine();
|
||||
|
||||
apply.append("begin").newLine()
|
||||
.append(" lowerTs = lower(OLD.sys_period);").newLine()
|
||||
.append(" upperTs = greatest(lowerTs + '1 microsecond',current_timestamp);").newLine();
|
||||
|
||||
apply
|
||||
.append(" if (TG_OP = 'UPDATE') then").newLine();
|
||||
appendInsertIntoHistory(apply, historyTable, includedColumns);
|
||||
apply
|
||||
.append(" NEW.").append(sysPeriod).append(" = tstzrange(").append(currentTimestamp).append(",null);").newLine()
|
||||
.append(" NEW.").append(sysPeriod).append(" = tstzrange(upperTs,null);").newLine()
|
||||
.append(" return new;").newLine();
|
||||
apply
|
||||
.append(" elsif (TG_OP = 'DELETE') then").newLine();
|
||||
@@ -124,7 +132,7 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
|
||||
buffer.append(" insert into ").append(historyTable).append(" (").append(sysPeriod).append(",");
|
||||
appendColumnNames(buffer, columns, "");
|
||||
buffer.append(") values (tstzrange(lower(OLD.").append(sysPeriod).append("), ").append(currentTimestamp).append("), ");
|
||||
buffer.append(") values (tstzrange(lowerTs,upperTs), ");
|
||||
appendColumnNames(buffer, columns, "OLD.");
|
||||
buffer.append(");").newLine();
|
||||
}
|
||||
|
||||
+43
-22
@@ -3,7 +3,9 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
import io.ebean.annotation.ConstraintMode;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
|
||||
import io.ebeaninternal.dbmigration.migration.AlterColumn;
|
||||
import io.ebeaninternal.server.persist.platform.MultiValueBind;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -137,13 +139,7 @@ public class SqlServerDdl extends PlatformDdl {
|
||||
// a rather complex statement.
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (DdlHelp.isDropDefault(defaultValue)) {
|
||||
sb.append("delimiter $$\n");
|
||||
sb.append("DECLARE @Tmp nvarchar(200);");
|
||||
sb.append("select @Tmp = t1.name from sys.default_constraints t1\n");
|
||||
sb.append(" join sys.columns t2 on t1.object_id = t2.default_object_id\n");
|
||||
sb.append(" where t1.parent_object_id = OBJECT_ID('").append(tableName)
|
||||
.append("') and t2.name = '").append(columnName).append("';\n");
|
||||
sb.append("if @Tmp is not null EXEC('alter table ").append(tableName).append(" drop constraint ' + @Tmp)$$");
|
||||
sb.append("EXEC usp_ebean_drop_default_constraint ").append(tableName).append(", ").append(columnName);
|
||||
} else {
|
||||
sb.append("alter table ").append(tableName);
|
||||
sb.append(" add default ").append(convertDefaultValue(defaultValue)).append(" for ").append(columnName);
|
||||
@@ -200,25 +196,50 @@ public class SqlServerDdl extends PlatformDdl {
|
||||
|
||||
/**
|
||||
* It is rather complex to delete a column on SqlServer as there must not exist any references
|
||||
* (constraints, default values, indices and foreign keys). The list is not yet complete, as
|
||||
* indices over multiple columns will not yet deleted.
|
||||
* (This may be changed to delete all refering objects by using the sys.* tables later)
|
||||
* (constraints, default values, indices and foreign keys). That's why we call a user stored procedure here
|
||||
*/
|
||||
@Override
|
||||
public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException {
|
||||
buffer.append("-- drop column ").append(tableName).append(".").append(columnName).endOfStatement();
|
||||
|
||||
buffer.append(alterTableDropUniqueConstraint(tableName, naming.uniqueConstraintName(tableName, columnName)));
|
||||
buffer.endOfStatement();
|
||||
buffer.append(alterColumnDefaultValue(tableName, columnName, DdlHelp.DROP_DEFAULT));
|
||||
buffer.endOfStatement();
|
||||
buffer.append(alterTableDropConstraint(tableName, naming.checkConstraintName(tableName, columnName)));
|
||||
buffer.endOfStatement();
|
||||
buffer.append(dropIndex(naming.indexName(tableName, columnName), tableName));
|
||||
buffer.endOfStatement();
|
||||
buffer.append(alterTableDropForeignKey(tableName, naming.foreignKeyConstraintName(tableName, columnName)));
|
||||
buffer.endOfStatement();
|
||||
super.alterTableDropColumn(buffer, tableName, columnName);
|
||||
buffer.append("EXEC usp_ebean_drop_column ").append(tableName).append(", ").append(columnName).endOfStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* This writes the multi value datatypes needed for {@link MultiValueBind}
|
||||
*/
|
||||
@Override
|
||||
public void generateProlog(DdlWrite write) throws IOException {
|
||||
super.generateProlog(write);
|
||||
|
||||
generateTVPDefinitions(write, "bigint");
|
||||
generateTVPDefinitions(write, "float");
|
||||
generateTVPDefinitions(write, "bit");
|
||||
generateTVPDefinitions(write, "date");
|
||||
generateTVPDefinitions(write, "time");
|
||||
//generateTVPDefinitions(write, "datetime2");
|
||||
generateTVPDefinitions(write, "uniqueidentifier");
|
||||
generateTVPDefinitions(write, "nvarchar(max)");
|
||||
|
||||
}
|
||||
|
||||
private void generateTVPDefinitions(DdlWrite write, String definition) throws IOException {
|
||||
int pos = definition.indexOf('(');
|
||||
String name = pos == -1 ? definition : definition.substring(0, pos);
|
||||
|
||||
dropTVP(write.dropAll(), name);
|
||||
//TVPs are included in "I__create_procs.sql"
|
||||
//createTVP(write.apply(), name, definition);
|
||||
}
|
||||
|
||||
private void dropTVP(DdlBuffer ddl, String name) throws IOException {
|
||||
ddl.append("if exists (select name from sys.types where name = 'ebean_").append(name)
|
||||
.append("_tvp') drop type ebean_").append(name).append("_tvp").endOfStatement();
|
||||
}
|
||||
|
||||
private void createTVP(DdlBuffer ddl, String name, String definition) throws IOException {
|
||||
ddl.append("if not exists (select name from sys.types where name = 'ebean_").append(name)
|
||||
.append("_tvp') create type ebean_").append(name).append("_tvp as table (c1 ").append(definition).append(")")
|
||||
.endOfStatement();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
@@ -45,20 +45,20 @@ public class AddColumn {
|
||||
|
||||
/**
|
||||
* Gets the value of the column property.
|
||||
* <p>
|
||||
*
|
||||
* <p>
|
||||
* This accessor method returns a reference to the live list,
|
||||
* not a snapshot. Therefore any modification you make to the
|
||||
* returned list will be present inside the JAXB object.
|
||||
* This is why there is not a <CODE>set</CODE> method for the column property.
|
||||
* <p>
|
||||
*
|
||||
* <p>
|
||||
* For example, to add a new item, do as follows:
|
||||
* <pre>
|
||||
* getColumn().add(newItem);
|
||||
* </pre>
|
||||
* <p>
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Objects of the following type(s) are allowed in the list
|
||||
* {@link Column }
|
||||
|
||||
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
|
||||
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
|
||||
@@ -9,11 +9,21 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* TODO
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||
* <attribute name="constraintName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="columnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="oneToOne" type="{http://www.w3.org/2001/XMLSchema}boolean" />
|
||||
* <attribute name="nullableColumns" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* </restriction>
|
||||
* </complexContent>
|
||||
* </complexType>
|
||||
* </pre>
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@@ -23,18 +33,14 @@ public class AddUniqueConstraint {
|
||||
|
||||
@XmlAttribute(name = "constraintName", required = true)
|
||||
protected String constraintName;
|
||||
|
||||
@XmlAttribute(name = "tableName", required = true)
|
||||
protected String tableName;
|
||||
|
||||
@XmlAttribute(name = "columnNames", required = true)
|
||||
protected String columnNames;
|
||||
|
||||
@XmlAttribute(name = "nullableColumns", required = true)
|
||||
protected String nullableColumns;
|
||||
|
||||
@XmlAttribute(name = "oneToOne", required = false)
|
||||
@XmlAttribute(name = "oneToOne")
|
||||
protected Boolean oneToOne;
|
||||
@XmlAttribute(name = "nullableColumns")
|
||||
protected String nullableColumns;
|
||||
|
||||
/**
|
||||
* Gets the value of the constraintName property.
|
||||
@@ -55,7 +61,7 @@ public class AddUniqueConstraint {
|
||||
public void setConstraintName(String value) {
|
||||
this.constraintName = value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the value of the tableName property.
|
||||
*
|
||||
@@ -65,15 +71,15 @@ public class AddUniqueConstraint {
|
||||
public String getTableName() {
|
||||
return tableName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the value of the tableName property.
|
||||
*
|
||||
* @param value allowed object is
|
||||
* {@link String }
|
||||
*/
|
||||
public void setTableName(String tableName) {
|
||||
this.tableName = tableName;
|
||||
public void setTableName(String value) {
|
||||
this.tableName = value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,6 +102,26 @@ public class AddUniqueConstraint {
|
||||
this.columnNames = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the oneToOne property.
|
||||
*
|
||||
* @return possible object is
|
||||
* {@link Boolean }
|
||||
*/
|
||||
public Boolean isOneToOne() {
|
||||
return oneToOne;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the oneToOne property.
|
||||
*
|
||||
* @param value allowed object is
|
||||
* {@link Boolean }
|
||||
*/
|
||||
public void setOneToOne(Boolean value) {
|
||||
this.oneToOne = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the nullableColumns property.
|
||||
*
|
||||
@@ -115,23 +141,5 @@ public class AddUniqueConstraint {
|
||||
public void setNullableColumns(String value) {
|
||||
this.nullableColumns = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the oneToOne property.
|
||||
*
|
||||
* @return true if oneToOne was set
|
||||
*/
|
||||
public boolean isOneToOne() {
|
||||
return Boolean.TRUE.equals(oneToOne);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the oneToOne property.
|
||||
*
|
||||
* @param value boolean
|
||||
*/
|
||||
public void setOneToOne(boolean oneToOne) {
|
||||
this.oneToOne = oneToOne;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package io.ebeaninternal.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.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
import java.util.ArrayList;
|
||||
@@ -12,13 +11,17 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||
* <sequence>
|
||||
* <element name="before" type="{http://ebean-orm.github.io/xml/ns/dbmigration}ddl-script" maxOccurs="unbounded" minOccurs="0"/>
|
||||
* <element name="after" type="{http://ebean-orm.github.io/xml/ns/dbmigration}ddl-script" maxOccurs="unbounded" minOccurs="0"/>
|
||||
* </sequence>
|
||||
* <attribute name="columnName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="withHistory" type="{http://www.w3.org/2001/XMLSchema}boolean" />
|
||||
@@ -50,16 +53,14 @@ import java.util.List;
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(name = "", propOrder = {
|
||||
"before", "after"
|
||||
"before",
|
||||
"after"
|
||||
})
|
||||
@XmlRootElement(name = "alterColumn")
|
||||
public class AlterColumn {
|
||||
|
||||
@XmlElement(required = false)
|
||||
protected List<DdlScript> before;
|
||||
@XmlElement(required = false)
|
||||
protected List<DdlScript> after;
|
||||
|
||||
@XmlAttribute(name = "columnName", required = true)
|
||||
protected String columnName;
|
||||
@XmlAttribute(name = "tableName", required = true)
|
||||
@@ -109,6 +110,60 @@ public class AlterColumn {
|
||||
@XmlAttribute(name = "dropForeignKeyIndex")
|
||||
protected String dropForeignKeyIndex;
|
||||
|
||||
/**
|
||||
* Gets the value of the before property.
|
||||
*
|
||||
* <p>
|
||||
* This accessor method returns a reference to the live list,
|
||||
* not a snapshot. Therefore any modification you make to the
|
||||
* returned list will be present inside the JAXB object.
|
||||
* This is why there is not a <CODE>set</CODE> method for the before property.
|
||||
*
|
||||
* <p>
|
||||
* For example, to add a new item, do as follows:
|
||||
* <pre>
|
||||
* getBefore().add(newItem);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Objects of the following type(s) are allowed in the list
|
||||
* {@link DdlScript }
|
||||
*/
|
||||
public List<DdlScript> getBefore() {
|
||||
if (before == null) {
|
||||
before = new ArrayList<>();
|
||||
}
|
||||
return this.before;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the after property.
|
||||
*
|
||||
* <p>
|
||||
* This accessor method returns a reference to the live list,
|
||||
* not a snapshot. Therefore any modification you make to the
|
||||
* returned list will be present inside the JAXB object.
|
||||
* This is why there is not a <CODE>set</CODE> method for the after property.
|
||||
*
|
||||
* <p>
|
||||
* For example, to add a new item, do as follows:
|
||||
* <pre>
|
||||
* getAfter().add(newItem);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Objects of the following type(s) are allowed in the list
|
||||
* {@link DdlScript }
|
||||
*/
|
||||
public List<DdlScript> getAfter() {
|
||||
if (after == null) {
|
||||
after = new ArrayList<>();
|
||||
}
|
||||
return this.after;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the columnName property.
|
||||
*
|
||||
@@ -589,17 +644,4 @@ public class AlterColumn {
|
||||
this.dropForeignKeyIndex = value;
|
||||
}
|
||||
|
||||
public List<DdlScript> getBefore() {
|
||||
if (before == null) {
|
||||
before = new ArrayList<>();
|
||||
}
|
||||
return before;
|
||||
}
|
||||
|
||||
public List<DdlScript> getAfter() {
|
||||
if (after == null) {
|
||||
after = new ArrayList<>();
|
||||
}
|
||||
return after;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,25 +9,38 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* TODO
|
||||
* <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="columnNames" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="refColumnNames" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="refTableName" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="indexName" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="onDelete" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="onUpdate" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* </restriction>
|
||||
* </complexContent>
|
||||
* </complexType>
|
||||
* </pre>
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(name = "")
|
||||
@XmlRootElement(name = "foreignKey")
|
||||
@XmlRootElement(name = "alterForeignKey")
|
||||
public class AlterForeignKey {
|
||||
|
||||
@XmlAttribute(name = "name", required = true)
|
||||
protected String name;
|
||||
@XmlAttribute(name = "columnNames", required = true)
|
||||
@XmlAttribute(name = "columnNames")
|
||||
protected String columnNames;
|
||||
@XmlAttribute(name = "refColumnNames", required = true)
|
||||
@XmlAttribute(name = "refColumnNames")
|
||||
protected String refColumnNames;
|
||||
@XmlAttribute(name = "refTableName", required = true)
|
||||
@XmlAttribute(name = "refTableName")
|
||||
protected String refTableName;
|
||||
@XmlAttribute(name = "indexName")
|
||||
protected String indexName;
|
||||
@@ -37,6 +50,7 @@ public class AlterForeignKey {
|
||||
protected String onDelete;
|
||||
@XmlAttribute(name = "onUpdate")
|
||||
protected String onUpdate;
|
||||
|
||||
/**
|
||||
* Gets the value of the name property.
|
||||
*
|
||||
@@ -196,4 +210,5 @@ public class AlterForeignKey {
|
||||
public void setOnUpdate(String value) {
|
||||
this.onUpdate = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
|
||||
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlValue;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <simpleContent>
|
||||
|
||||
@@ -13,9 +13,9 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
@@ -50,16 +50,16 @@ public class ChangeSet {
|
||||
@XmlElement(name = "dropTable", type = DropTable.class),
|
||||
@XmlElement(name = "renameTable", type = RenameTable.class),
|
||||
@XmlElement(name = "addTableComment", type = AddTableComment.class),
|
||||
@XmlElement(name = "addUniqueConstraint", type = AddUniqueConstraint.class),
|
||||
@XmlElement(name = "addHistoryTable", type = AddHistoryTable.class),
|
||||
@XmlElement(name = "dropHistoryTable", type = DropHistoryTable.class),
|
||||
@XmlElement(name = "alterForeignKey", type = AlterForeignKey.class),
|
||||
@XmlElement(name = "addColumn", type = AddColumn.class),
|
||||
@XmlElement(name = "dropColumn", type = DropColumn.class),
|
||||
@XmlElement(name = "alterColumn", type = AlterColumn.class),
|
||||
@XmlElement(name = "renameColumn", type = RenameColumn.class),
|
||||
@XmlElement(name = "createIndex", type = CreateIndex.class),
|
||||
@XmlElement(name = "dropIndex", type = DropIndex.class),
|
||||
@XmlElement(name = "addUniqueConstraint", type = AddUniqueConstraint.class),
|
||||
@XmlElement(name = "alterForeignKey", type = AlterForeignKey.class),
|
||||
@XmlElement(name = "dropIndex", type = DropIndex.class)
|
||||
})
|
||||
protected List<Object> changeSetChildren;
|
||||
@XmlAttribute(name = "type", required = true)
|
||||
@@ -77,20 +77,20 @@ public class ChangeSet {
|
||||
|
||||
/**
|
||||
* Gets the value of the changeSetChildren property.
|
||||
* <p>
|
||||
*
|
||||
* <p>
|
||||
* This accessor method returns a reference to the live list,
|
||||
* not a snapshot. Therefore any modification you make to the
|
||||
* returned list will be present inside the JAXB object.
|
||||
* This is why there is not a <CODE>set</CODE> method for the changeSetChildren property.
|
||||
* <p>
|
||||
*
|
||||
* <p>
|
||||
* For example, to add a new item, do as follows:
|
||||
* <pre>
|
||||
* getChangeSetChildren().add(newItem);
|
||||
* </pre>
|
||||
* <p>
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Objects of the following type(s) are allowed in the list
|
||||
* {@link Configuration }
|
||||
@@ -99,8 +99,10 @@ public class ChangeSet {
|
||||
* {@link DropTable }
|
||||
* {@link RenameTable }
|
||||
* {@link AddTableComment }
|
||||
* {@link AddUniqueConstraint }
|
||||
* {@link AddHistoryTable }
|
||||
* {@link DropHistoryTable }
|
||||
* {@link AlterForeignKey }
|
||||
* {@link AddColumn }
|
||||
* {@link DropColumn }
|
||||
* {@link AlterColumn }
|
||||
|
||||
@@ -7,7 +7,7 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for changeSetType.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
* <pre>
|
||||
@@ -16,6 +16,7 @@ import javax.xml.bind.annotation.XmlType;
|
||||
* <enumeration value="apply"/>
|
||||
* <enumeration value="pendingDrops"/>
|
||||
* <enumeration value="baseline"/>
|
||||
* <enumeration value="drop"/>
|
||||
* </restriction>
|
||||
* </simpleType>
|
||||
* </pre>
|
||||
@@ -29,7 +30,9 @@ public enum ChangeSetType {
|
||||
@XmlEnumValue("pendingDrops")
|
||||
PENDING_DROPS("pendingDrops"),
|
||||
@XmlEnumValue("baseline")
|
||||
BASELINE("baseline");
|
||||
BASELINE("baseline"),
|
||||
@XmlEnumValue("drop")
|
||||
DROP("drop");
|
||||
private final String value;
|
||||
|
||||
ChangeSetType(String v) {
|
||||
|
||||
@@ -3,7 +3,6 @@ package io.ebeaninternal.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.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
import java.util.ArrayList;
|
||||
@@ -12,13 +11,17 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||
* <sequence>
|
||||
* <element name="before" type="{http://ebean-orm.github.io/xml/ns/dbmigration}ddl-script" maxOccurs="unbounded" minOccurs="0"/>
|
||||
* <element name="after" type="{http://ebean-orm.github.io/xml/ns/dbmigration}ddl-script" maxOccurs="unbounded" minOccurs="0"/>
|
||||
* </sequence>
|
||||
* <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="type" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="defaultValue" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
@@ -43,16 +46,14 @@ import java.util.List;
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(name = "", propOrder = {
|
||||
"before", "after"
|
||||
"before",
|
||||
"after"
|
||||
})
|
||||
@XmlRootElement(name = "column")
|
||||
public class Column {
|
||||
|
||||
@XmlElement(required = false)
|
||||
protected List<DdlScript> before;
|
||||
@XmlElement(required = false)
|
||||
protected List<DdlScript> after;
|
||||
|
||||
@XmlAttribute(name = "name", required = true)
|
||||
protected String name;
|
||||
@XmlAttribute(name = "type", required = true)
|
||||
@@ -88,19 +89,58 @@ public class Column {
|
||||
@XmlAttribute(name = "comment")
|
||||
protected String comment;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the value of the before property.
|
||||
*
|
||||
* <p>
|
||||
* This accessor method returns a reference to the live list,
|
||||
* not a snapshot. Therefore any modification you make to the
|
||||
* returned list will be present inside the JAXB object.
|
||||
* This is why there is not a <CODE>set</CODE> method for the before property.
|
||||
*
|
||||
* <p>
|
||||
* For example, to add a new item, do as follows:
|
||||
* <pre>
|
||||
* getBefore().add(newItem);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Objects of the following type(s) are allowed in the list
|
||||
* {@link DdlScript }
|
||||
*/
|
||||
public List<DdlScript> getBefore() {
|
||||
if (before == null) {
|
||||
before = new ArrayList<>();
|
||||
}
|
||||
return before;
|
||||
return this.before;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the after property.
|
||||
*
|
||||
* <p>
|
||||
* This accessor method returns a reference to the live list,
|
||||
* not a snapshot. Therefore any modification you make to the
|
||||
* returned list will be present inside the JAXB object.
|
||||
* This is why there is not a <CODE>set</CODE> method for the after property.
|
||||
*
|
||||
* <p>
|
||||
* For example, to add a new item, do as follows:
|
||||
* <pre>
|
||||
* getAfter().add(newItem);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Objects of the following type(s) are allowed in the list
|
||||
* {@link DdlScript }
|
||||
*/
|
||||
public List<DdlScript> getAfter() {
|
||||
if (after == null) {
|
||||
after = new ArrayList<>();
|
||||
}
|
||||
return after;
|
||||
return this.after;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -384,7 +424,7 @@ public class Column {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the foreignOnDelete property.
|
||||
* Gets the value of the foreignKeyOnDelete property.
|
||||
*
|
||||
* @return possible object is
|
||||
* {@link String }
|
||||
@@ -404,7 +444,7 @@ public class Column {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the foreignOnUpdate property.
|
||||
* Gets the value of the foreignKeyOnUpdate property.
|
||||
*
|
||||
* @return possible object is
|
||||
* {@link String }
|
||||
|
||||
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
|
||||
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
|
||||
@@ -14,9 +14,9 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
@@ -28,6 +28,8 @@ import java.util.List;
|
||||
* </sequence>
|
||||
* <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}tablespaceAttributes"/>
|
||||
* <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="partitionMode" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="partitionColumn" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="withHistory" type="{http://www.w3.org/2001/XMLSchema}boolean" />
|
||||
* <attribute name="draft" type="{http://www.w3.org/2001/XMLSchema}boolean" />
|
||||
* <attribute name="identityType" type="{http://ebean-orm.github.io/xml/ns/dbmigration}identityType" />
|
||||
@@ -55,6 +57,10 @@ public class CreateTable {
|
||||
protected List<ForeignKey> foreignKey;
|
||||
@XmlAttribute(name = "name", required = true)
|
||||
protected String name;
|
||||
@XmlAttribute(name = "partitionMode")
|
||||
protected String partitionMode;
|
||||
@XmlAttribute(name = "partitionColumn")
|
||||
protected String partitionColumn;
|
||||
@XmlAttribute(name = "withHistory")
|
||||
protected Boolean withHistory;
|
||||
@XmlAttribute(name = "draft")
|
||||
@@ -80,20 +86,20 @@ public class CreateTable {
|
||||
|
||||
/**
|
||||
* Gets the value of the column property.
|
||||
* <p>
|
||||
*
|
||||
* <p>
|
||||
* This accessor method returns a reference to the live list,
|
||||
* not a snapshot. Therefore any modification you make to the
|
||||
* returned list will be present inside the JAXB object.
|
||||
* This is why there is not a <CODE>set</CODE> method for the column property.
|
||||
* <p>
|
||||
*
|
||||
* <p>
|
||||
* For example, to add a new item, do as follows:
|
||||
* <pre>
|
||||
* getColumn().add(newItem);
|
||||
* </pre>
|
||||
* <p>
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Objects of the following type(s) are allowed in the list
|
||||
* {@link Column }
|
||||
@@ -107,20 +113,20 @@ public class CreateTable {
|
||||
|
||||
/**
|
||||
* Gets the value of the uniqueConstraint property.
|
||||
* <p>
|
||||
*
|
||||
* <p>
|
||||
* This accessor method returns a reference to the live list,
|
||||
* not a snapshot. Therefore any modification you make to the
|
||||
* returned list will be present inside the JAXB object.
|
||||
* This is why there is not a <CODE>set</CODE> method for the uniqueConstraint property.
|
||||
* <p>
|
||||
*
|
||||
* <p>
|
||||
* For example, to add a new item, do as follows:
|
||||
* <pre>
|
||||
* getUniqueConstraint().add(newItem);
|
||||
* </pre>
|
||||
* <p>
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Objects of the following type(s) are allowed in the list
|
||||
* {@link UniqueConstraint }
|
||||
@@ -134,20 +140,20 @@ public class CreateTable {
|
||||
|
||||
/**
|
||||
* Gets the value of the foreignKey property.
|
||||
* <p>
|
||||
*
|
||||
* <p>
|
||||
* This accessor method returns a reference to the live list,
|
||||
* not a snapshot. Therefore any modification you make to the
|
||||
* returned list will be present inside the JAXB object.
|
||||
* This is why there is not a <CODE>set</CODE> method for the foreignKey property.
|
||||
* <p>
|
||||
*
|
||||
* <p>
|
||||
* For example, to add a new item, do as follows:
|
||||
* <pre>
|
||||
* getForeignKey().add(newItem);
|
||||
* </pre>
|
||||
* <p>
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Objects of the following type(s) are allowed in the list
|
||||
* {@link ForeignKey }
|
||||
@@ -179,6 +185,46 @@ public class CreateTable {
|
||||
this.name = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the partitionMode property.
|
||||
*
|
||||
* @return possible object is
|
||||
* {@link String }
|
||||
*/
|
||||
public String getPartitionMode() {
|
||||
return partitionMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the partitionMode property.
|
||||
*
|
||||
* @param value allowed object is
|
||||
* {@link String }
|
||||
*/
|
||||
public void setPartitionMode(String value) {
|
||||
this.partitionMode = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the partitionColumn property.
|
||||
*
|
||||
* @return possible object is
|
||||
* {@link String }
|
||||
*/
|
||||
public String getPartitionColumn() {
|
||||
return partitionColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the partitionColumn property.
|
||||
*
|
||||
* @param value allowed object is
|
||||
* {@link String }
|
||||
*/
|
||||
public void setPartitionColumn(String value) {
|
||||
this.partitionColumn = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the withHistory property.
|
||||
*
|
||||
|
||||
@@ -1,42 +1,61 @@
|
||||
package io.ebeaninternal.dbmigration.migration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
import javax.xml.bind.annotation.XmlValue;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
* <p>Java class for ddl-script complex type.
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* TODO @Rob: Can this generated automatically?
|
||||
* <complexType name="ddl-script">
|
||||
* <complexContent>
|
||||
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||
* <sequence>
|
||||
* <element name="ddl" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded"/>
|
||||
* </sequence>
|
||||
* <attribute name="platforms" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* </restriction>
|
||||
* </complexContent>
|
||||
* </complexType>
|
||||
* </pre>
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(name = "", propOrder = {
|
||||
@XmlType(name = "ddl-script", propOrder = {
|
||||
"ddl"
|
||||
})
|
||||
@XmlRootElement(name = "ddl-script")
|
||||
public class DdlScript {
|
||||
|
||||
@XmlValue
|
||||
@XmlElement(required = true)
|
||||
protected List<String> ddl;
|
||||
|
||||
@XmlAttribute(name = "platforms")
|
||||
protected String platforms;
|
||||
|
||||
/**
|
||||
* Gets the value of the value property.
|
||||
* Gets the value of the ddl property.
|
||||
*
|
||||
* @return possible object is
|
||||
* <p>
|
||||
* This accessor method returns a reference to the live list,
|
||||
* not a snapshot. Therefore any modification you make to the
|
||||
* returned list will be present inside the JAXB object.
|
||||
* This is why there is not a <CODE>set</CODE> method for the ddl property.
|
||||
*
|
||||
* <p>
|
||||
* For example, to add a new item, do as follows:
|
||||
* <pre>
|
||||
* getDdl().add(newItem);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Objects of the following type(s) are allowed in the list
|
||||
* {@link String }
|
||||
*/
|
||||
public List<String> getDdl() {
|
||||
|
||||
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
|
||||
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
|
||||
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
|
||||
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
|
||||
@@ -9,14 +9,16 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <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="sequenceCol" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="sequenceName" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* </restriction>
|
||||
* </complexContent>
|
||||
* </complexType>
|
||||
@@ -64,16 +66,6 @@ public class DropTable {
|
||||
return sequenceCol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the sequenceName property.
|
||||
*
|
||||
* @return possible object is
|
||||
* {@link String }
|
||||
*/
|
||||
public String getSequenceName() {
|
||||
return sequenceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the sequenceCol property.
|
||||
*
|
||||
@@ -84,6 +76,16 @@ public class DropTable {
|
||||
this.sequenceCol = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the sequenceName property.
|
||||
*
|
||||
* @return possible object is
|
||||
* {@link String }
|
||||
*/
|
||||
public String getSequenceName() {
|
||||
return sequenceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the sequenceName property.
|
||||
*
|
||||
|
||||
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
@@ -47,6 +47,7 @@ public class ForeignKey {
|
||||
protected String onDelete;
|
||||
@XmlAttribute(name = "onUpdate")
|
||||
protected String onUpdate;
|
||||
|
||||
/**
|
||||
* Gets the value of the name property.
|
||||
*
|
||||
|
||||
@@ -7,7 +7,7 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for identityType.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
* <pre>
|
||||
|
||||
@@ -11,9 +11,9 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
@@ -38,20 +38,20 @@ public class Migration {
|
||||
|
||||
/**
|
||||
* Gets the value of the changeSet property.
|
||||
* <p>
|
||||
*
|
||||
* <p>
|
||||
* This accessor method returns a reference to the live list,
|
||||
* not a snapshot. Therefore any modification you make to the
|
||||
* returned list will be present inside the JAXB object.
|
||||
* This is why there is not a <CODE>set</CODE> method for the changeSet property.
|
||||
* <p>
|
||||
*
|
||||
* <p>
|
||||
* For example, to add a new item, do as follows:
|
||||
* <pre>
|
||||
* getChangeSet().add(newItem);
|
||||
* </pre>
|
||||
* <p>
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Objects of the following type(s) are allowed in the list
|
||||
* {@link ChangeSet }
|
||||
|
||||
@@ -6,7 +6,7 @@ import javax.xml.bind.annotation.XmlRegistry;
|
||||
/**
|
||||
* This object contains factory methods for each
|
||||
* Java content interface and Java element interface
|
||||
* generated in the io.ebean.dbmigration.migration package.
|
||||
* generated in the io.ebeaninternal.dbmigration.migration package.
|
||||
* <p>An ObjectFactory allows you to programatically
|
||||
* construct new instances of the Java representation
|
||||
* for XML content. The Java representation of XML
|
||||
@@ -21,23 +21,23 @@ public class ObjectFactory {
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: io.ebean.dbmigration.migration
|
||||
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: io.ebeaninternal.dbmigration.migration
|
||||
*/
|
||||
public ObjectFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link Rollback }
|
||||
* Create an instance of {@link AddUniqueConstraint }
|
||||
*/
|
||||
public Rollback createRollback() {
|
||||
return new Rollback();
|
||||
public AddUniqueConstraint createAddUniqueConstraint() {
|
||||
return new AddUniqueConstraint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link AddColumn }
|
||||
* Create an instance of {@link CreateTable }
|
||||
*/
|
||||
public AddColumn createAddColumn() {
|
||||
return new AddColumn();
|
||||
public CreateTable createCreateTable() {
|
||||
return new CreateTable();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,10 +48,10 @@ public class ObjectFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link CreateTable }
|
||||
* Create an instance of {@link DdlScript }
|
||||
*/
|
||||
public CreateTable createCreateTable() {
|
||||
return new CreateTable();
|
||||
public DdlScript createDdlScript() {
|
||||
return new DdlScript();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,13 +68,6 @@ public class ObjectFactory {
|
||||
return new ForeignKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link Apply }
|
||||
*/
|
||||
public Apply createApply() {
|
||||
return new Apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link Configuration }
|
||||
*/
|
||||
@@ -89,13 +82,6 @@ public class ObjectFactory {
|
||||
return new DefaultTablespace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link AddTableComment }
|
||||
*/
|
||||
public AddTableComment createAddTableComment() {
|
||||
return new AddTableComment();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link RenameTable }
|
||||
*/
|
||||
@@ -111,10 +97,10 @@ public class ObjectFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link AlterColumn }
|
||||
* Create an instance of {@link AlterForeignKey }
|
||||
*/
|
||||
public AlterColumn createAlterColumn() {
|
||||
return new AlterColumn();
|
||||
public AlterForeignKey createAlterForeignKey() {
|
||||
return new AlterForeignKey();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,6 +110,69 @@ public class ObjectFactory {
|
||||
return new DropColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link Sql }
|
||||
*/
|
||||
public Sql createSql() {
|
||||
return new Sql();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link Apply }
|
||||
*/
|
||||
public Apply createApply() {
|
||||
return new Apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link Rollback }
|
||||
*/
|
||||
public Rollback createRollback() {
|
||||
return new Rollback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link DropIndex }
|
||||
*/
|
||||
public DropIndex createDropIndex() {
|
||||
return new DropIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link RenameColumn }
|
||||
*/
|
||||
public RenameColumn createRenameColumn() {
|
||||
return new RenameColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link DropTable }
|
||||
*/
|
||||
public DropTable createDropTable() {
|
||||
return new DropTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link AddColumn }
|
||||
*/
|
||||
public AddColumn createAddColumn() {
|
||||
return new AddColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link AddTableComment }
|
||||
*/
|
||||
public AddTableComment createAddTableComment() {
|
||||
return new AddTableComment();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link AlterColumn }
|
||||
*/
|
||||
public AlterColumn createAlterColumn() {
|
||||
return new AlterColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link CreateIndex }
|
||||
*/
|
||||
@@ -138,20 +187,6 @@ public class ObjectFactory {
|
||||
return new ChangeSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link Sql }
|
||||
*/
|
||||
public Sql createSql() {
|
||||
return new Sql();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link DropTable }
|
||||
*/
|
||||
public DropTable createDropTable() {
|
||||
return new DropTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link AddHistoryTable }
|
||||
*/
|
||||
@@ -159,20 +194,6 @@ public class ObjectFactory {
|
||||
return new AddHistoryTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link RenameColumn }
|
||||
*/
|
||||
public RenameColumn createRenameColumn() {
|
||||
return new RenameColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link DropIndex }
|
||||
*/
|
||||
public DropIndex createDropIndex() {
|
||||
return new DropIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link AlterHistoryTable }
|
||||
*/
|
||||
@@ -187,24 +208,4 @@ public class ObjectFactory {
|
||||
return new Migration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link DdlScript }
|
||||
*/
|
||||
public DdlScript createDdlScript() {
|
||||
return new DdlScript();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link AddUniqueConstraint }
|
||||
*/
|
||||
public AddUniqueConstraint createAddUniqueConstraint() {
|
||||
return new AddUniqueConstraint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link AddUniqueConstraint }
|
||||
*/
|
||||
public AlterForeignKey createAlterForeignKey() {
|
||||
return new AlterForeignKey();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
|
||||
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
|
||||
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlValue;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <simpleContent>
|
||||
|
||||
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
|
||||
@@ -9,15 +9,17 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
* <p>
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
* <p>
|
||||
*
|
||||
* <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="columnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="oneToOne" type="{http://www.w3.org/2001/XMLSchema}boolean" />
|
||||
* <attribute name="nullableColumns" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* </restriction>
|
||||
* </complexContent>
|
||||
* </complexType>
|
||||
@@ -30,16 +32,13 @@ public class UniqueConstraint {
|
||||
|
||||
@XmlAttribute(name = "name", required = true)
|
||||
protected String name;
|
||||
|
||||
@XmlAttribute(name = "columnNames", required = true)
|
||||
protected String columnNames;
|
||||
|
||||
@XmlAttribute(name = "oneToOne", required = false)
|
||||
@XmlAttribute(name = "oneToOne")
|
||||
protected Boolean oneToOne;
|
||||
|
||||
@XmlAttribute(name = "nullableColumns", required = true)
|
||||
@XmlAttribute(name = "nullableColumns")
|
||||
protected String nullableColumns;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the value of the name property.
|
||||
*
|
||||
@@ -79,23 +78,25 @@ public class UniqueConstraint {
|
||||
public void setColumnNames(String value) {
|
||||
this.columnNames = value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the value of the oneToOne property.
|
||||
*
|
||||
* @return true if oneToOne was set
|
||||
* @return possible object is
|
||||
* {@link Boolean }
|
||||
*/
|
||||
public boolean isOneToOne() {
|
||||
return Boolean.TRUE.equals(oneToOne);
|
||||
public Boolean isOneToOne() {
|
||||
return oneToOne;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the value of the oneToOne property.
|
||||
*
|
||||
* @param value boolean
|
||||
* @param value allowed object is
|
||||
* {@link Boolean }
|
||||
*/
|
||||
public void setOneToOne(boolean oneToOne) {
|
||||
this.oneToOne = oneToOne;
|
||||
public void setOneToOne(Boolean value) {
|
||||
this.oneToOne = value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,12 +24,14 @@ public class MigrationXmlReader {
|
||||
*/
|
||||
public static Migration read(String resourcePath) {
|
||||
|
||||
InputStream is = MigrationXmlReader.class.getResourceAsStream(resourcePath);
|
||||
if (is == null) {
|
||||
throw new IllegalArgumentException("No resource found for path [" + resourcePath + "]");
|
||||
try (InputStream is = MigrationXmlReader.class.getResourceAsStream(resourcePath)) {
|
||||
if (is == null) {
|
||||
throw new IllegalArgumentException("No resource found for path [" + resourcePath + "]");
|
||||
}
|
||||
return read(is);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("Failed to auto close resource " + resourcePath, e);
|
||||
}
|
||||
|
||||
return read(is);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,8 +9,12 @@ import io.ebeaninternal.dbmigration.migration.ChangeSet;
|
||||
import io.ebeaninternal.dbmigration.model.build.ModelBuildBeanVisitor;
|
||||
import io.ebeaninternal.dbmigration.model.build.ModelBuildContext;
|
||||
import io.ebeaninternal.dbmigration.model.visitor.VisitAllUsing;
|
||||
import io.ebeaninternal.extraddl.model.DdlScript;
|
||||
import io.ebeaninternal.extraddl.model.ExtraDdl;
|
||||
import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Reads EbeanServer bean descriptors to build the current model.
|
||||
@@ -56,6 +60,20 @@ public class CurrentModel {
|
||||
this.platformTypes = platformTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the model contains tables that are partitioned.
|
||||
*/
|
||||
public boolean isTablePartitioning() {
|
||||
return model.isTablePartitioning();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the tables that have partitioning.
|
||||
*/
|
||||
public List<MTable> getPartitionedTables() {
|
||||
return model.getPartitionedTables();
|
||||
}
|
||||
|
||||
private static DbConstraintNaming.MaxLength maxLength(SpiEbeanServer server, DbConstraintNaming naming) {
|
||||
|
||||
if (naming.getMaxLength() != null) {
|
||||
@@ -111,6 +129,9 @@ public class CurrentModel {
|
||||
if (header != null && !header.isEmpty()) {
|
||||
ddl.append(header).append('\n');
|
||||
}
|
||||
|
||||
addExtraDdl(ddl, ExtraDdlXmlReader.readBuiltin(), "-- init script ");
|
||||
|
||||
ddl.append(write.apply().getBuffer());
|
||||
ddl.append(write.applyForeignKeys().getBuffer());
|
||||
ddl.append(write.applyHistoryView().getBuffer());
|
||||
@@ -119,6 +140,18 @@ public class CurrentModel {
|
||||
return ddl.toString();
|
||||
}
|
||||
|
||||
private void addExtraDdl(StringBuilder ddl, ExtraDdl extraDdl, String prefix) {
|
||||
if (extraDdl != null) {
|
||||
List<DdlScript> ddlScript = extraDdl.getDdlScript();
|
||||
for (DdlScript script : ddlScript) {
|
||||
if (script.isInit() && ExtraDdlXmlReader.matchPlatform(server.getDatabasePlatform().getName(), script.getPlatforms())) {
|
||||
ddl.append(prefix + script.getName()).append('\n');
|
||||
ddl.append(script.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the 'Drop' DDL.
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package io.ebeaninternal.dbmigration.model;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.DdlHelp;
|
||||
import io.ebeaninternal.dbmigration.migration.AddUniqueConstraint;
|
||||
import io.ebeaninternal.dbmigration.migration.UniqueConstraint;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A unique constraint for multiple columns.
|
||||
* <p>
|
||||
@@ -26,13 +27,13 @@ public class MCompoundUniqueConstraint {
|
||||
* The columns combined to be unique.
|
||||
*/
|
||||
private final String[] columns;
|
||||
|
||||
|
||||
private String[] nullableColumns;
|
||||
|
||||
public MCompoundUniqueConstraint(String[] columns, boolean oneToOne, String name) {
|
||||
public MCompoundUniqueConstraint(String[] columns, Boolean oneToOne, String name) {
|
||||
this.name = name;
|
||||
this.columns = columns;
|
||||
this.oneToOne = oneToOne;
|
||||
this.oneToOne = Boolean.TRUE.equals(oneToOne);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,6 +56,7 @@ public class MCompoundUniqueConstraint {
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public UniqueConstraint getUniqueConstraint() {
|
||||
UniqueConstraint uq = new UniqueConstraint();
|
||||
uq.setName(getName());
|
||||
@@ -63,6 +65,7 @@ public class MCompoundUniqueConstraint {
|
||||
uq.setOneToOne(isOneToOne());
|
||||
return uq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a AddUniqueConstraint migration for this constraint.
|
||||
*/
|
||||
@@ -87,7 +90,7 @@ public class MCompoundUniqueConstraint {
|
||||
dropUniqueConstraint.setNullableColumns(join(nullableColumns));
|
||||
return dropUniqueConstraint;
|
||||
}
|
||||
|
||||
|
||||
public void setNullableColumns(String[] nullableColumns) {
|
||||
if (nullableColumns != null && nullableColumns.length == 0) {
|
||||
this.nullableColumns = null;
|
||||
@@ -109,12 +112,12 @@ public class MCompoundUniqueConstraint {
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(columns) + 31 * Objects.hash(name, oneToOne);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
@@ -125,8 +128,8 @@ public class MCompoundUniqueConstraint {
|
||||
}
|
||||
MCompoundUniqueConstraint other = (MCompoundUniqueConstraint) obj;
|
||||
return Arrays.equals(columns, other.columns)
|
||||
&& Arrays.equals(nullableColumns, other.nullableColumns)
|
||||
&& Objects.equals(name, other.name)
|
||||
&& oneToOne == other.oneToOne;
|
||||
&& Arrays.equals(nullableColumns, other.nullableColumns)
|
||||
&& Objects.equals(name, other.name)
|
||||
&& oneToOne == other.oneToOne;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.dbmigration.model;
|
||||
|
||||
import io.ebean.annotation.PartitionMode;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.DdlHelp;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.SplitColumns;
|
||||
import io.ebeaninternal.dbmigration.migration.AddColumn;
|
||||
@@ -14,6 +15,7 @@ import io.ebeaninternal.dbmigration.migration.DropTable;
|
||||
import io.ebeaninternal.dbmigration.migration.ForeignKey;
|
||||
import io.ebeaninternal.dbmigration.migration.IdentityType;
|
||||
import io.ebeaninternal.dbmigration.migration.UniqueConstraint;
|
||||
import io.ebeaninternal.server.deploy.PartitionMeta;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -61,6 +63,8 @@ public class MTable {
|
||||
*/
|
||||
private boolean draft;
|
||||
|
||||
private PartitionMeta partitionMeta;
|
||||
|
||||
/**
|
||||
* Primary key name.
|
||||
*/
|
||||
@@ -237,6 +241,10 @@ public class MTable {
|
||||
createTable.setName(name);
|
||||
createTable.setPkName(pkName);
|
||||
createTable.setComment(comment);
|
||||
if (partitionMeta != null) {
|
||||
createTable.setPartitionMode(partitionMeta.getMode().name());
|
||||
createTable.setPartitionColumn(partitionMeta.getProperty());
|
||||
}
|
||||
createTable.setTablespace(tablespace);
|
||||
createTable.setIndexTablespace(indexTablespace);
|
||||
createTable.setSequenceName(sequenceName);
|
||||
@@ -419,6 +427,20 @@ public class MTable {
|
||||
return draft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this table is partitioned.
|
||||
*/
|
||||
public boolean isPartitioned() {
|
||||
return partitionMeta != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the partition meta for this table.
|
||||
*/
|
||||
public PartitionMeta getPartitionMeta() {
|
||||
return partitionMeta;
|
||||
}
|
||||
|
||||
public void setPkName(String pkName) {
|
||||
this.pkName = pkName;
|
||||
}
|
||||
@@ -531,6 +553,17 @@ public class MTable {
|
||||
return pk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the primary key column if it is a simple primary key.
|
||||
*/
|
||||
public String singlePrimaryKey() {
|
||||
List<MColumn> columns = primaryKeyColumns();
|
||||
if (columns.size() == 1) {
|
||||
return columns.get(0).getName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void checkTableName(String tableName) {
|
||||
if (!name.equals(tableName)) {
|
||||
throw new IllegalArgumentException("addColumn tableName [" + tableName + "] does not match [" + name + "]");
|
||||
@@ -745,4 +778,8 @@ public class MTable {
|
||||
compoundKey.setIndexName(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPartitionMeta(PartitionMeta partitionMeta) {
|
||||
this.partitionMeta = partitionMeta;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,9 +42,10 @@ public class MigrationModel {
|
||||
|
||||
// find all the migration xml files
|
||||
File[] xmlFiles = modelDirectory.listFiles(pathname -> pathname.getName().toLowerCase().endsWith(modelSuffix));
|
||||
|
||||
if (xmlFiles == null || xmlFiles.length == 0) {
|
||||
return;
|
||||
}
|
||||
List<MigrationResource> resources = new ArrayList<>(xmlFiles.length);
|
||||
|
||||
for (File xmlFile : xmlFiles) {
|
||||
resources.add(new MigrationResource(xmlFile, createVersion(xmlFile)));
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@ import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
|
||||
import io.ebeaninternal.dbmigration.migration.DropIndex;
|
||||
import io.ebeaninternal.dbmigration.migration.DropTable;
|
||||
import io.ebeaninternal.dbmigration.migration.Migration;
|
||||
import io.ebeaninternal.dbmigration.migration.Sql;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -42,9 +44,25 @@ public class ModelContainer {
|
||||
|
||||
private final PendingDrops pendingDrops = new PendingDrops();
|
||||
|
||||
private final List<MTable> partitionedTables = new ArrayList<>();
|
||||
|
||||
public ModelContainer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the model contains tables that are partitioned.
|
||||
*/
|
||||
public boolean isTablePartitioning() {
|
||||
return !partitionedTables.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of partitioned tables.
|
||||
*/
|
||||
public List<MTable> getPartitionedTables() {
|
||||
return partitionedTables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the FK references on all the draft tables.
|
||||
*/
|
||||
@@ -143,6 +161,8 @@ public class ModelContainer {
|
||||
applyChange((AlterForeignKey) change);
|
||||
} else if (change instanceof AddTableComment) {
|
||||
applyChange((AddTableComment) change);
|
||||
} else if (change instanceof Sql) {
|
||||
// do nothing
|
||||
} else {
|
||||
throw new IllegalArgumentException("No rule for " + change);
|
||||
}
|
||||
@@ -164,13 +184,12 @@ public class ModelContainer {
|
||||
/**
|
||||
* Unset the withHistory flag on the associated base table.
|
||||
*/
|
||||
private void applyChange(DropHistoryTable change) {
|
||||
protected void applyChange(DropHistoryTable change) {
|
||||
|
||||
MTable table = tables.get(change.getBaseTable());
|
||||
if (table == null) {
|
||||
throw new IllegalStateException("Table [" + change.getBaseTable() + "] does not exist in model?");
|
||||
if (table != null) {
|
||||
table.setWithHistory(false);
|
||||
}
|
||||
table.setWithHistory(false);
|
||||
}
|
||||
|
||||
private void applyChange(AddUniqueConstraint change) {
|
||||
@@ -221,19 +240,14 @@ public class ModelContainer {
|
||||
if (tables.containsKey(tableName)) {
|
||||
throw new IllegalStateException("Table [" + tableName + "] already exists in model?");
|
||||
}
|
||||
MTable table = new MTable(createTable);
|
||||
tables.put(tableName, table);
|
||||
tables.put(tableName, new MTable(createTable));
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a DropTable change to the model.
|
||||
*/
|
||||
protected void applyChange(DropTable dropTable) {
|
||||
String tableName = dropTable.getName();
|
||||
if (!tables.containsKey(tableName)) {
|
||||
throw new IllegalStateException("Table [" + tableName + "] does not exists in model?");
|
||||
}
|
||||
tables.remove(tableName);
|
||||
tables.remove(dropTable.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,22 +258,16 @@ public class ModelContainer {
|
||||
if (indexes.containsKey(indexName)) {
|
||||
throw new IllegalStateException("Index [" + indexName + "] already exists in model?");
|
||||
}
|
||||
MIndex index = new MIndex(createIndex);
|
||||
indexes.put(createIndex.getIndexName(), index);
|
||||
indexes.put(createIndex.getIndexName(), new MIndex(createIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a DropTable change to the model.
|
||||
*/
|
||||
protected void applyChange(DropIndex dropIndex) {
|
||||
String name = dropIndex.getIndexName();
|
||||
if (!indexes.containsKey(name)) {
|
||||
throw new IllegalStateException("Index [" + name + "] does not exist in model?");
|
||||
}
|
||||
indexes.remove(name);
|
||||
indexes.remove(dropIndex.getIndexName());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply a AddColumn change to the model.
|
||||
*/
|
||||
@@ -297,6 +305,9 @@ public class ModelContainer {
|
||||
* Add a table (typically from reading EbeanServer meta data).
|
||||
*/
|
||||
public MTable addTable(MTable table) {
|
||||
if (table.isPartitioned()) {
|
||||
partitionedTables.add(table);
|
||||
}
|
||||
return tables.put(table.getName(), table);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.ebeaninternal.dbmigration.model;
|
||||
import io.ebeaninternal.dbmigration.migration.ChangeSet;
|
||||
import io.ebeaninternal.dbmigration.migration.ChangeSetType;
|
||||
import io.ebeaninternal.dbmigration.migration.DropColumn;
|
||||
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
|
||||
import io.ebeaninternal.dbmigration.migration.DropTable;
|
||||
import io.ebeaninternal.dbmigration.migration.Migration;
|
||||
|
||||
@@ -207,10 +208,26 @@ public class PendingDrops {
|
||||
|
||||
} else if (pendingDrop instanceof DropTable && dropTableIn((DropTable) pendingDrop, appliedDrops)) {
|
||||
iterator.remove();
|
||||
|
||||
} else if (pendingDrop instanceof DropHistoryTable && dropHistoryTableIn((DropHistoryTable) pendingDrop, appliedDrops)) {
|
||||
iterator.remove();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the pendingDrop is contained in the appliedDrops.
|
||||
*/
|
||||
private boolean dropHistoryTableIn(DropHistoryTable pendingDrop, ChangeSet appliedDrops) {
|
||||
for (Object o : appliedDrops.getChangeSetChildren()) {
|
||||
if (o instanceof DropHistoryTable && sameHistoryTable(pendingDrop, (DropHistoryTable) o)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the pendingDrop is contained in the appliedDrops.
|
||||
*/
|
||||
@@ -235,6 +252,13 @@ public class PendingDrops {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the DropHistoryTable match by base-table name.
|
||||
*/
|
||||
private boolean sameHistoryTable(DropHistoryTable pendingDrop, DropHistoryTable o) {
|
||||
return pendingDrop.getBaseTable().equals(o.getBaseTable());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the DropTable match by table name.
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user