mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
215
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5cfce3f44 | ||
|
|
1c6363e85c | ||
|
|
b8bcbd8af9 | ||
|
|
d70768277b | ||
|
|
b39b4a2e8c | ||
|
|
93b2f4035c | ||
|
|
31ee0c4582 | ||
|
|
6c2dad4851 | ||
|
|
ed190ede03 | ||
|
|
f252b80c2d | ||
|
|
2859ea657e | ||
|
|
664b1cbd75 | ||
|
|
8bc283fef4 | ||
|
|
7d85398139 | ||
|
|
f42be3d65a | ||
|
|
ff8e5af4e5 | ||
|
|
526d67eeb4 | ||
|
|
374fc8d702 | ||
|
|
9c6c59afab | ||
|
|
c3eae0d3a5 | ||
|
|
25ec73364e | ||
|
|
c7818e7f23 | ||
|
|
8318bbc4ca | ||
|
|
337646a6ce | ||
|
|
fa6ee93e5b | ||
|
|
0fe691112f | ||
|
|
6946ff07e0 | ||
|
|
7c3d052da0 | ||
|
|
aea7a7fd65 | ||
|
|
f39a684c8e | ||
|
|
3cb5854786 | ||
|
|
e8192723cc | ||
|
|
3787748eae | ||
|
|
0ad52cf6a7 | ||
|
|
51bee836d4 | ||
|
|
9193b6b971 | ||
|
|
e48b9fab0f | ||
|
|
a14ca24b33 | ||
|
|
761f43d6a1 | ||
|
|
737f9a893e | ||
|
|
579812d717 | ||
|
|
fae39fe650 | ||
|
|
debd8ad617 | ||
|
|
6450ba219c | ||
|
|
a578eda2b1 | ||
|
|
a2ee0bfdbe | ||
|
|
872f0e21cb | ||
|
|
8620dbf337 | ||
|
|
4f51a3b130 | ||
|
|
5f194f066a | ||
|
|
31f6e56333 | ||
|
|
0efbe061ab | ||
|
|
555e5ba6fd | ||
|
|
5d3da60828 | ||
|
|
2ac0d136e6 | ||
|
|
97462cb21a | ||
|
|
afaacd4eeb | ||
|
|
5e8afbe50d | ||
|
|
db371cbc6a | ||
|
|
fd9a5167b1 | ||
|
|
7b76e13bce | ||
|
|
42e7103aa6 | ||
|
|
29ca678f01 | ||
|
|
cca6143420 | ||
|
|
fe7b110577 | ||
|
|
16df55eafa | ||
|
|
905a0ce9ec | ||
|
|
bfa8837e61 | ||
|
|
8d5845b9a2 | ||
|
|
87a7d4958e | ||
|
|
511ebd8a29 | ||
|
|
11f4c8d7a3 | ||
|
|
d417a92843 | ||
|
|
4bd5e52df3 | ||
|
|
788a200d16 | ||
|
|
e18d2004dc | ||
|
|
b326724139 | ||
|
|
fc0b47342b | ||
|
|
ec1a68a133 | ||
|
|
8ca12521d7 | ||
|
|
2362ef4130 | ||
|
|
7e233edcc3 | ||
|
|
a8b98282a1 | ||
|
|
b4506aa718 | ||
|
|
848a3b8888 | ||
|
|
67c711d75f | ||
|
|
3d62f58930 | ||
|
|
a30948d38d | ||
|
|
a8232d2d8b | ||
|
|
d4b8f23a5a | ||
|
|
bcf2a44187 | ||
|
|
f3102ba02c | ||
|
|
0351eab3a7 | ||
|
|
409e2b75ce | ||
|
|
dc968f16bf | ||
|
|
bbbb6482fb | ||
|
|
001968fcf8 | ||
|
|
6abcb186a4 | ||
|
|
f250447fbe | ||
|
|
bfa86f241b | ||
|
|
19459b71b4 | ||
|
|
5e97c3bbbc | ||
|
|
986eadd023 | ||
|
|
c6787d0901 | ||
|
|
3792e06859 | ||
|
|
c77446ba24 | ||
|
|
dba245c517 | ||
|
|
489eb483c0 | ||
|
|
11301846be | ||
|
|
d022e4aec8 | ||
|
|
aa788edb68 | ||
|
|
288adcdf68 | ||
|
|
ddb07ca202 | ||
|
|
524b592e82 | ||
|
|
0124f0f254 | ||
|
|
eddd6a36d6 | ||
|
|
91e309d770 | ||
|
|
5726410f6f | ||
|
|
054f7dc86f | ||
|
|
06b5ebc14e | ||
|
|
f6ee82428f | ||
|
|
e1d45dd79d | ||
|
|
08c455279f | ||
|
|
32dcbc6703 | ||
|
|
c9e733df4e | ||
|
|
606e1e6d3a | ||
|
|
fcb50ea13a | ||
|
|
5b26e77cfd | ||
|
|
9fc5d8a201 | ||
|
|
ba78c5e904 | ||
|
|
6627195fc2 | ||
|
|
b76c4a83ec | ||
|
|
3dcb82b1f5 | ||
|
|
ae55a40ebd | ||
|
|
8ba07419fa | ||
|
|
5f8f735bbe | ||
|
|
47829f3985 | ||
|
|
ec4f655312 | ||
|
|
04fda0a6b4 | ||
|
|
0a8c902790 | ||
|
|
9632279c2f | ||
|
|
262d52ed44 | ||
|
|
74330918e1 | ||
|
|
373598ab26 | ||
|
|
0096477757 | ||
|
|
a2a4a15f32 | ||
|
|
71dd0e5728 | ||
|
|
116cc3a3fe | ||
|
|
92132d6613 | ||
|
|
d6e3292a7d | ||
|
|
227a1ba032 | ||
|
|
953cae6ce8 | ||
|
|
98fc739600 | ||
|
|
fc3ab10984 | ||
|
|
5b8ce8eaa0 | ||
|
|
97cf8b30f6 | ||
|
|
7e66e0db1f | ||
|
|
572bb41b73 | ||
|
|
088fcfb296 | ||
|
|
54172aec6c | ||
|
|
5c1de0a886 | ||
|
|
c292da736b | ||
|
|
987fe38618 | ||
|
|
f4ed813c08 | ||
|
|
a358fba31d | ||
|
|
76783d525a | ||
|
|
3f0f5c7fde | ||
|
|
6df1058aab | ||
|
|
335c5fb69b | ||
|
|
94ed568812 | ||
|
|
1c23720605 | ||
|
|
ccba5d08ef | ||
|
|
641ada001e | ||
|
|
fca80ac649 | ||
|
|
379cc39e38 | ||
|
|
386d3a37f1 | ||
|
|
37612c6f83 | ||
|
|
7984553d96 | ||
|
|
d592e7d35f | ||
|
|
5a0b65b4f2 | ||
|
|
aaefaa5dcd | ||
|
|
f3e66c459a | ||
|
|
16d5e04809 | ||
|
|
b01df515bd | ||
|
|
7fda911931 | ||
|
|
e332aace16 | ||
|
|
c79231c846 | ||
|
|
efad23d482 | ||
|
|
fe8a32ea18 | ||
|
|
31099f6dda | ||
|
|
58f4f791f6 | ||
|
|
b27989f541 | ||
|
|
7bc1a902ed | ||
|
|
358720776e | ||
|
|
417ee4061e | ||
|
|
394cb3854a | ||
|
|
547fa342c4 | ||
|
|
f8b8a41bce | ||
|
|
ba39897b18 | ||
|
|
1437843f08 | ||
|
|
7c705e2baa | ||
|
|
2caeb4fff1 | ||
|
|
84190f3c2a | ||
|
|
46b158a0a0 | ||
|
|
b172a32710 | ||
|
|
f8ef87f7a8 | ||
|
|
ea54d1b794 | ||
|
|
b1780948a8 | ||
|
|
1a0f3a6614 | ||
|
|
580b51381e | ||
|
|
75e9770d5e | ||
|
|
10937de24b | ||
|
|
18aeedc153 | ||
|
|
9ca1a87f8b | ||
|
|
111354f391 |
@@ -7,6 +7,8 @@
|
||||
target/
|
||||
logs/
|
||||
log/
|
||||
ebean-autotune.xml
|
||||
ebean-profiling*.xml
|
||||
/db
|
||||
/mydb.db
|
||||
!src/test/ddl-review/*.sql
|
||||
|
||||
@@ -5,16 +5,7 @@
|
||||
|
||||
Ebean ORM
|
||||
==============
|
||||
[http://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Website")
|
||||
|
||||
|
||||
Maven Dependency
|
||||
----------------
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>4.7.3</version>
|
||||
</dependency>
|
||||
[https://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Website")
|
||||
|
||||
|
||||
[Maven central - avaje-ebeanorm](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.avaje.ebeanorm%22%20AND%20a%3A%22avaje-ebeanorm%22 "maven central ebeanorm")
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>6.3.1</version>
|
||||
<version>6.13.3</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>avaje-ebeanorm</name>
|
||||
@@ -61,6 +61,14 @@
|
||||
<version>1.7.7</version>
|
||||
</dependency>
|
||||
|
||||
<!-- provided scope for Spring boot loader classpath scanning support -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-loader</artifactId>
|
||||
<version>1.2.7.RELEASE</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- provided scope for JsonNode support -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
@@ -109,23 +117,23 @@
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>9.4-1201-jdbc41</version>
|
||||
<version>9.4-1202-jdbc41</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>oracle</groupId>
|
||||
<artifactId>oracle-jdbc</artifactId>
|
||||
<version>7.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!--<dependency>-->
|
||||
<!--<groupId>oracle</groupId>-->
|
||||
<!--<artifactId>oracle-jdbc</artifactId>-->
|
||||
<!--<version>7.0</version>-->
|
||||
<!--<scope>test</scope>-->
|
||||
<!--</dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>microsoft</groupId>
|
||||
<artifactId>sqlserver-jdbc</artifactId>
|
||||
<version>4.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!--<dependency>-->
|
||||
<!--<groupId>microsoft</groupId>-->
|
||||
<!--<artifactId>sqlserver-jdbc</artifactId>-->
|
||||
<!--<version>4.2</version>-->
|
||||
<!--<scope>test</scope>-->
|
||||
<!--</dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp</groupId>
|
||||
@@ -144,15 +152,17 @@
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-agent</artifactId>
|
||||
<version>4.5.1</version>
|
||||
<version>4.7.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Provided scope so that the H2HistoryTrigger can live in Ebean core
|
||||
and not require a separate module for it -->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>1.4.182</version>
|
||||
<scope>test</scope>
|
||||
<version>1.4.189</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -209,7 +219,7 @@
|
||||
<plugin>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
|
||||
<version>4.5.1</version>
|
||||
<version>4.7.1</version>
|
||||
<executions>
|
||||
<!-- Not going to enhance Model bean -->
|
||||
<execution>
|
||||
@@ -263,11 +273,11 @@
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9.1</version>
|
||||
<configuration>
|
||||
<doctitle>Ebean 4</doctitle>
|
||||
<doctitle>Ebean 6</doctitle>
|
||||
<overview>src/main/java/com/avaje/ebean/overview.html</overview>
|
||||
<source>1.8</source>
|
||||
<doclet>org.avaje.doclet.PygmentsDoclet</doclet>
|
||||
<excludePackageNames>com.avaje.ebeaninternal.*:com.avaje.ebean.util</excludePackageNames>
|
||||
<excludePackageNames>com.avaje.ebeaninternal.*:com.avaje.ebean.util:com.avaje.ebean.dbmigration.ddlgeneration:com.avaje.ebean.dbmigration.migration:com.avaje.ebean.dbmigration.migrationreader:com.avaje.ebean.dbmigration.model</excludePackageNames>
|
||||
<docletArtifact>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>pygments-doclet</artifactId>
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* Administrative control of Autofetch during runtime.
|
||||
*/
|
||||
public interface AdminAutofetch {
|
||||
|
||||
/**
|
||||
* Return true if profiling is enabled.
|
||||
*/
|
||||
boolean isProfiling();
|
||||
|
||||
/**
|
||||
* Set to true to enable profiling.
|
||||
*/
|
||||
void setProfiling(boolean enable);
|
||||
|
||||
/**
|
||||
* Return true if autoFetch automatic query tuning is enabled.
|
||||
*/
|
||||
boolean isQueryTuning();
|
||||
|
||||
/**
|
||||
* Set to true to enable autoFetch automatic query tuning.
|
||||
*/
|
||||
void setQueryTuning(boolean enable);
|
||||
|
||||
/**
|
||||
* Returns the rate which profiling is collected. This is an int between 0 and
|
||||
* 100.
|
||||
*/
|
||||
double getProfilingRate();
|
||||
|
||||
/**
|
||||
* Set the rate at which profiling is collected after the base.
|
||||
*
|
||||
* @param rate
|
||||
* a int between 0 and 100.
|
||||
*/
|
||||
void setProfilingRate(double rate);
|
||||
|
||||
/**
|
||||
* Return the number of queries profiled after which profiling is collected at
|
||||
* a percentage rate.
|
||||
*/
|
||||
int getProfilingBase();
|
||||
|
||||
/**
|
||||
* Set a base number of queries to profile per query point.
|
||||
* <p>
|
||||
* After this amount of profiling has been obtained profiling is collected at
|
||||
* the Profiling Percentage rate.
|
||||
* </p>
|
||||
*/
|
||||
void setProfilingBase(int profilingBase);
|
||||
|
||||
/**
|
||||
* Return the minimum number of queries profiled before autoFetch will start
|
||||
* automatically tuning the queries.
|
||||
* <p>
|
||||
* This could be one which means start autoFetch tuning after the first
|
||||
* profiling information is collected.
|
||||
* </p>
|
||||
*/
|
||||
int getProfilingMin();
|
||||
|
||||
/**
|
||||
* Set the minimum number of queries profiled per query point before autoFetch
|
||||
* will automatically tune the queries.
|
||||
* <p>
|
||||
* Increasing this number will mean more profiling is collected before
|
||||
* autoFetch starts tuning the query.
|
||||
* </p>
|
||||
*/
|
||||
void setProfilingMin(int autoFetchMinThreshold);
|
||||
|
||||
/**
|
||||
* Fire a garbage collection (hint to the JVM). Assuming garbage collection
|
||||
* fires this will gather the usage profiling information.
|
||||
*/
|
||||
String collectUsageViaGC();
|
||||
|
||||
/**
|
||||
* This will take the current profiling information and update the "tuned
|
||||
* query detail".
|
||||
* <p>
|
||||
* This is done periodically and can also be manually invoked.
|
||||
* </p>
|
||||
*
|
||||
* @return a summary of the updates that occurred
|
||||
*/
|
||||
String updateTunedQueryInfo();
|
||||
|
||||
/**
|
||||
* Clear all the tuned query info.
|
||||
* <p>
|
||||
* Should only need do this for testing and playing around.
|
||||
* </p>
|
||||
*
|
||||
* @return the amount of tuned query information cleared.
|
||||
*/
|
||||
int clearTunedQueryInfo();
|
||||
|
||||
/**
|
||||
* Clear all the profiling information.
|
||||
* <p>
|
||||
* This means the profiling information will need to be re-gathered.
|
||||
* </p>
|
||||
* <p>
|
||||
* Should only need do this for testing and playing around.
|
||||
* </p>
|
||||
*
|
||||
* @return the amount of profiled information cleared.
|
||||
*/
|
||||
int clearProfilingInfo();
|
||||
|
||||
/**
|
||||
* Clear the query execution statistics.
|
||||
*/
|
||||
void clearQueryStatistics();
|
||||
|
||||
/**
|
||||
* Return the number of queries tuned by AutoFetch.
|
||||
*/
|
||||
int getTotalTunedQueryCount();
|
||||
|
||||
/**
|
||||
* Return the size of the TuneQuery map.
|
||||
*/
|
||||
int getTotalTunedQuerySize();
|
||||
|
||||
/**
|
||||
* Return the size of the profile map.
|
||||
*/
|
||||
int getTotalProfileSize();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* Administrative control of AutoTune during runtime.
|
||||
*/
|
||||
public interface AutoTune {
|
||||
|
||||
/**
|
||||
* Fire a garbage collection (hint to the JVM). Assuming garbage collection
|
||||
* fires this will gather remaining usage profiling information.
|
||||
*/
|
||||
void collectProfiling();
|
||||
|
||||
}
|
||||
@@ -38,6 +38,33 @@ public interface BeanState {
|
||||
*/
|
||||
void setDisableLazyLoad(boolean disableLazyLoading);
|
||||
|
||||
/**
|
||||
* Set the loaded state of the property given it's name.
|
||||
*
|
||||
* <p>
|
||||
* Typically this would be used to set the loaded state of a property
|
||||
* to false to ensure that the specific property is excluded from a
|
||||
* stateless update.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // populate a bean via say JSON
|
||||
* User user = ...;
|
||||
*
|
||||
* // set loaded state on the email property to false so that
|
||||
* // the email property is not included in a stateless update
|
||||
* Ebean.getBeanState(user).setPropertyLoaded("email", false);
|
||||
*
|
||||
* user.update();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*
|
||||
* This will throw an IllegalArgumentException if the property is unknown.
|
||||
*/
|
||||
void setPropertyLoaded(String propertyName, boolean loaded);
|
||||
|
||||
/**
|
||||
* For partially populated beans returns the properties that are loaded on the
|
||||
* bean.
|
||||
|
||||
@@ -22,7 +22,7 @@ import com.avaje.ebean.text.json.JsonContext;
|
||||
/**
|
||||
* This Ebean object is effectively a singleton that holds a map of registered
|
||||
* {@link EbeanServer}s. It additionally provides a convenient way to use the
|
||||
* 'default/primary' EbeanServer.
|
||||
* 'default' EbeanServer.
|
||||
* <p>
|
||||
* If you are using a Dependency Injection framework such as
|
||||
* <strong>Spring</strong> or <strong>Guice</strong> you will probably
|
||||
@@ -168,9 +168,9 @@ public final class Ebean {
|
||||
defaultServer = getWithCreate(defaultName.trim());
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
} catch (Throwable e) {
|
||||
logger.error("Error trying to create the default EbeanServer", e);
|
||||
throw e;
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ public final class Ebean {
|
||||
if (defaultServer == null) {
|
||||
String msg = "The default EbeanServer has not been defined?";
|
||||
msg += " This is normally set via the ebean.datasource.default property.";
|
||||
msg += " Otherwise it should be registered programatically via registerServer()";
|
||||
msg += " Otherwise it should be registered programmatically via registerServer()";
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
return defaultServer;
|
||||
@@ -217,8 +217,8 @@ public final class Ebean {
|
||||
/**
|
||||
* Register a server so we can get it by its name.
|
||||
*/
|
||||
private void register(EbeanServer server, boolean isPrimaryServer) {
|
||||
registerWithName(server.getName(), server, isPrimaryServer);
|
||||
private void register(EbeanServer server, boolean isDefaultServer) {
|
||||
registerWithName(server.getName(), server, isDefaultServer);
|
||||
}
|
||||
|
||||
private void registerWithName(String name, EbeanServer server, boolean isDefaultServer) {
|
||||
@@ -697,12 +697,31 @@ public final class Ebean {
|
||||
/**
|
||||
* Delete the bean.
|
||||
* <p>
|
||||
* This will return true if the bean was deleted successfully or JDBC batch is being used.
|
||||
* </p>
|
||||
* <p>
|
||||
* If there is no current transaction one will be created and committed for
|
||||
* you automatically.
|
||||
* </p>
|
||||
* <p>
|
||||
* If the bean is configured with <code>@SoftDelete</code> then this will perform a soft
|
||||
* delete rather than a hard/permanent delete.
|
||||
* </p>
|
||||
* <p>
|
||||
* If the Bean does not have a version property (or loaded version property) and
|
||||
* the bean does not exist then this returns false indicating that nothing was
|
||||
* deleted. Note that, if JDBC batch mode is used then this always returns true.
|
||||
* </p>
|
||||
*/
|
||||
public static void delete(Object bean) throws OptimisticLockException {
|
||||
serverMgr.getDefaultServer().delete(bean);
|
||||
public static boolean delete(Object bean) throws OptimisticLockException {
|
||||
return serverMgr.getDefaultServer().delete(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the bean in permanent fashion (will not use soft delete).
|
||||
*/
|
||||
public static boolean deletePermanent(Object bean) throws OptimisticLockException {
|
||||
return serverMgr.getDefaultServer().deletePermanent(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -726,6 +745,13 @@ public final class Ebean {
|
||||
return serverMgr.getDefaultServer().deleteAll(beans);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete permanent all the beans in the Collection (will not use soft delete).
|
||||
*/
|
||||
public static int deleteAllPermanent(Collection<?> beans) throws OptimisticLockException {
|
||||
return serverMgr.getDefaultServer().deleteAllPermanent(beans);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the values of a bean.
|
||||
* <p>
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.annotation.CacheStrategy;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.meta.MetaInfoManager;
|
||||
import com.avaje.ebean.plugin.SpiServer;
|
||||
import com.avaje.ebean.text.csv.CsvReader;
|
||||
import com.avaje.ebean.text.json.JsonContext;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.persistence.NonUniqueResultException;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Provides the API for fetching and saving beans to a particular DataSource.
|
||||
* <p>
|
||||
@@ -106,10 +107,9 @@ public interface EbeanServer {
|
||||
void shutdown(boolean shutdownDataSource, boolean deregisterDriver);
|
||||
|
||||
/**
|
||||
* Return the AdminAutofetch which is used to control and configure the
|
||||
* Autofetch service at runtime.
|
||||
* Return AutoTune which is used to control the AutoTune service at runtime.
|
||||
*/
|
||||
AdminAutofetch getAdminAutofetch();
|
||||
AutoTune getAutoTune();
|
||||
|
||||
/**
|
||||
* Return the name. This is used with {@link Ebean#getServer(String)} to get a
|
||||
@@ -128,6 +128,11 @@ public interface EbeanServer {
|
||||
*/
|
||||
MetaInfoManager getMetaInfoManager();
|
||||
|
||||
/**
|
||||
* Return the extended API intended for use by plugins.
|
||||
*/
|
||||
SpiServer getPluginApi();
|
||||
|
||||
/**
|
||||
* Return the BeanState for a given entity bean.
|
||||
* <p>
|
||||
@@ -1087,14 +1092,17 @@ public interface EbeanServer {
|
||||
<T> Map<?, T> findMap(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query returning at most one entity bean. This will throw a
|
||||
* PersistenceException if the query finds more than one result.
|
||||
* 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#findUnique()} 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
|
||||
@@ -1102,12 +1110,28 @@ public interface EbeanServer {
|
||||
* @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#findUnique()
|
||||
*/
|
||||
@Nullable
|
||||
<T> T findUnique(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 sql query returning a list of MapBean.
|
||||
* <p>
|
||||
@@ -1227,16 +1251,52 @@ public interface EbeanServer {
|
||||
/**
|
||||
* Delete the bean.
|
||||
* <p>
|
||||
* This will return true if the bean was deleted successfully or JDBC batch is being used.
|
||||
* </p>
|
||||
* <p>
|
||||
* If there is no current transaction one will be created and committed for
|
||||
* you automatically.
|
||||
* </p>
|
||||
* <p>
|
||||
* If the Bean does not have a version property (or loaded version property) and
|
||||
* the bean does not exist then this returns false indicating that nothing was
|
||||
* deleted. Note that, if JDBC batch mode is used then this always returns true.
|
||||
* </p>
|
||||
*/
|
||||
void delete(Object bean) throws OptimisticLockException;
|
||||
boolean delete(Object bean) throws OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Delete the bean with an explicit transaction.
|
||||
* <p>
|
||||
* This will return true if the bean was deleted successfully or JDBC batch is being used.
|
||||
* </p>
|
||||
* <p>
|
||||
* If the Bean does not have a version property (or loaded version property) and
|
||||
* the bean does not exist then this returns false indicating that nothing was
|
||||
* deleted. However, if JDBC batch mode is used then this always returns true.
|
||||
* </p>
|
||||
*/
|
||||
void delete(Object bean, Transaction transaction) throws OptimisticLockException;
|
||||
boolean delete(Object bean, Transaction transaction) throws OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Delete a bean permanently without soft delete.
|
||||
*/
|
||||
boolean deletePermanent(Object bean) throws OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Delete a bean permanently without soft delete using an explicit transaction.
|
||||
*/
|
||||
boolean deletePermanent(Object bean, Transaction transaction) throws OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Delete all the beans in the collection permanently without soft delete.
|
||||
*/
|
||||
int deleteAllPermanent(Collection<?> beans) throws OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Delete all the beans in the collection permanently without soft delete using an explicit transaction.
|
||||
*/
|
||||
int deleteAllPermanent(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Delete the bean given its type and id.
|
||||
@@ -1437,7 +1497,7 @@ public interface EbeanServer {
|
||||
void markAsDirty(Object bean);
|
||||
|
||||
/**
|
||||
* Saves the bean using an update. If you know you are updating a bean then it is preferrable to
|
||||
* Saves the bean using an update. If you know you are updating a bean then it is preferable to
|
||||
* use this update() method rather than save().
|
||||
* <p>
|
||||
* <b>Stateless updates:</b> Note that the bean does not have to be previously fetched to call
|
||||
@@ -1489,7 +1549,6 @@ public interface EbeanServer {
|
||||
* @param deleteMissingChildren
|
||||
* specify false if you do not want 'missing children' of a OneToMany
|
||||
* or ManyToMany to be automatically deleted.
|
||||
|
||||
*/
|
||||
void update(Object bean, Transaction transaction, boolean deleteMissingChildren) throws OptimisticLockException;
|
||||
|
||||
@@ -1800,4 +1859,114 @@ public interface EbeanServer {
|
||||
*/
|
||||
JsonContext json();
|
||||
|
||||
/**
|
||||
* Publish a single bean given its type and id returning the resulting live bean.
|
||||
* <p>
|
||||
* The values are published from the draft to the live bean.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param beanType the type of the entity bean
|
||||
* @param id the id of the entity bean
|
||||
* @param transaction the transaction the publish process should use (can be null)
|
||||
*/
|
||||
<T> T publish(Class<T> beanType, Object id, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Publish a single bean given its type and id returning the resulting live bean.
|
||||
* This will use the current transaction or create one if required.
|
||||
* <p>
|
||||
* The values are published from the draft to the live bean.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param beanType the type of the entity bean
|
||||
* @param id the id of the entity bean
|
||||
*/
|
||||
<T> T publish(Class<T> beanType, Object id);
|
||||
|
||||
/**
|
||||
* Publish the beans that match the query returning the resulting published beans.
|
||||
* <p>
|
||||
* The values are published from the draft beans to the live beans.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param query the query used to select the draft beans to publish
|
||||
* @param transaction the transaction the publish process should use (can be null)
|
||||
*/
|
||||
<T> List<T> publish(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Publish the beans that match the query returning the resulting published beans.
|
||||
* This will use the current transaction or create one if required.
|
||||
* <p>
|
||||
* The values are published from the draft beans to the live beans.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param query the query used to select the draft beans to publish
|
||||
*/
|
||||
<T> List<T> publish(Query<T> query);
|
||||
|
||||
/**
|
||||
* Restore the draft bean back to the live state.
|
||||
* <p>
|
||||
* The values from the live beans are set back to the draft bean and the
|
||||
* <code>@DraftDirty</code> and <code>@DraftReset</code> properties are reset.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param beanType the type of the entity bean
|
||||
* @param id the id of the entity bean to restore
|
||||
* @param transaction the transaction the restore process should use (can be null)
|
||||
*/
|
||||
<T> T draftRestore(Class<T> beanType, Object id, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Restore the draft bean back to the live state.
|
||||
* <p>
|
||||
* The values from the live beans are set back to the draft bean and the
|
||||
* <code>@DraftDirty</code> and <code>@DraftReset</code> properties are reset.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param beanType the type of the entity bean
|
||||
* @param id the id of the entity bean to restore
|
||||
*/
|
||||
<T> T draftRestore(Class<T> beanType, Object id);
|
||||
|
||||
/**
|
||||
* Restore the draft beans matching the query back to the live state.
|
||||
* <p>
|
||||
* The values from the live beans are set back to the draft bean and the
|
||||
* <code>@DraftDirty</code> and <code>@DraftReset</code> properties are reset.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param query the query used to select the draft beans to restore
|
||||
* @param transaction the transaction the restore process should use (can be null)
|
||||
*/
|
||||
<T> List<T> draftRestore(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Restore the draft beans matching the query back to the live state.
|
||||
* <p>
|
||||
* The values from the live beans are set back to the draft bean and the
|
||||
* <code>@DraftDirty</code> and <code>@DraftReset</code> properties are reset.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of the entity bean
|
||||
* @param query the query used to select the draft beans to restore
|
||||
*/
|
||||
<T> List<T> draftRestore(Query<T> query);
|
||||
|
||||
/**
|
||||
* Returns the set of properties/paths that are unknown (do not map to known properties or paths).
|
||||
* <p>
|
||||
* Validate the query checking the where and orderBy expression paths to confirm if
|
||||
* they represent valid properties/path for the given bean type.
|
||||
* </p>
|
||||
*/
|
||||
<T> Set<String> validateQuery(Query<T> query);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,51 @@ import java.util.Map;
|
||||
*/
|
||||
public interface ExpressionFactory {
|
||||
|
||||
/**
|
||||
* Path exists - for the given path in a JSON document.
|
||||
*/
|
||||
Expression jsonExists(String propertyName, String path);
|
||||
|
||||
/**
|
||||
* Path does not exist - for the given path in a JSON document.
|
||||
*/
|
||||
Expression jsonNotExists(String propertyName, String path);
|
||||
|
||||
/**
|
||||
* Equal to - for the given path in a JSON document.
|
||||
*/
|
||||
Expression jsonEqualTo(String propertyName, String path, Object val);
|
||||
|
||||
/**
|
||||
* Not Equal to - for the given path in a JSON document.
|
||||
*/
|
||||
Expression jsonNotEqualTo(String propertyName, String path, Object val);
|
||||
|
||||
/**
|
||||
* Greater than - for the given path in a JSON document.
|
||||
*/
|
||||
Expression jsonGreaterThan(String propertyName, String path, Object val);
|
||||
|
||||
/**
|
||||
* Greater than or equal to - for the given path in a JSON document.
|
||||
*/
|
||||
Expression jsonGreaterOrEqual(String propertyName, String path, Object val);
|
||||
|
||||
/**
|
||||
* Less than - for the given path in a JSON document.
|
||||
*/
|
||||
Expression jsonLessThan(String propertyName, String path, Object val);
|
||||
|
||||
/**
|
||||
* Less than or equal to - for the given path in a JSON document.
|
||||
*/
|
||||
Expression jsonLessOrEqualTo(String propertyName, String path, Object val);
|
||||
|
||||
/**
|
||||
* Between - for the given path in a JSON document.
|
||||
*/
|
||||
Expression jsonBetween(String propertyName, String path, Object lowerValue, Object upperValue);
|
||||
|
||||
/**
|
||||
* Equal To - property equal to the given value.
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebean;
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.persistence.NonUniqueResultException;
|
||||
import java.io.Serializable;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Collection;
|
||||
@@ -107,6 +108,28 @@ public interface ExpressionList<T> extends Serializable {
|
||||
*/
|
||||
Query<T> asOf(Timestamp asOf);
|
||||
|
||||
/**
|
||||
* Execute the query against the draft set of tables.
|
||||
*/
|
||||
Query<T> asDraft();
|
||||
|
||||
/**
|
||||
* Execute the query including soft deleted rows.
|
||||
*/
|
||||
Query<T> includeSoftDeletes();
|
||||
|
||||
/**
|
||||
* 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>
|
||||
*
|
||||
* @return the number of beans/rows that were deleted.
|
||||
*/
|
||||
int delete();
|
||||
|
||||
/**
|
||||
* Execute the query iterating over the results.
|
||||
*
|
||||
@@ -171,8 +194,15 @@ public interface ExpressionList<T> extends Serializable {
|
||||
<K> Map<K, T> findMap(String keyProperty, Class<K> keyType);
|
||||
|
||||
/**
|
||||
* Execute the query returning a single bean.
|
||||
*
|
||||
* Execute the query returning a single bean or null (if no matching
|
||||
* bean is found).
|
||||
* <p>
|
||||
* If more than 1 row is found for this query then a NonUniqueResultException is
|
||||
* thrown.
|
||||
* </p>
|
||||
*
|
||||
* @throws NonUniqueResultException if more than one result was found
|
||||
*
|
||||
* @see Query#findUnique()
|
||||
*/
|
||||
@Nullable
|
||||
@@ -244,6 +274,15 @@ public interface ExpressionList<T> extends Serializable {
|
||||
*/
|
||||
List<Version<T>> findVersions();
|
||||
|
||||
/**
|
||||
* Return versions of a @History entity bean between the 2 timestamps.
|
||||
* <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>
|
||||
*/
|
||||
List<Version<T>> findVersionsBetween(Timestamp start, Timestamp end);
|
||||
|
||||
/**
|
||||
* Add some filter predicate expressions to the many property.
|
||||
*/
|
||||
@@ -257,6 +296,24 @@ public interface ExpressionList<T> extends Serializable {
|
||||
*/
|
||||
Query<T> select(String properties);
|
||||
|
||||
/**
|
||||
* Set whether this query uses DISTINCT.
|
||||
* <p>
|
||||
* The select() clause MUST be specified when setDistinct(true) is set. The reason for this is that
|
||||
* generally ORM queries include the "id" property and this doesn't make sense for distinct queries.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* Ebean.find(Customer.class)
|
||||
* .setDistinct(true)
|
||||
* .select("name") // only select the customer name
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
Query<T> setDistinct(boolean distinct);
|
||||
|
||||
/**
|
||||
* Set the first row to fetch.
|
||||
*
|
||||
@@ -285,6 +342,13 @@ public interface ExpressionList<T> extends Serializable {
|
||||
*/
|
||||
Query<T> setUseCache(boolean useCache);
|
||||
|
||||
/**
|
||||
* Set to true to use the query for executing this query.
|
||||
*
|
||||
* @see Query#setUseQueryCache(boolean)
|
||||
*/
|
||||
Query<T> setUseQueryCache(boolean useCache);
|
||||
|
||||
/**
|
||||
* Add expressions to the having clause.
|
||||
* <p>
|
||||
@@ -299,20 +363,136 @@ public interface ExpressionList<T> extends Serializable {
|
||||
*/
|
||||
ExpressionList<T> where();
|
||||
|
||||
/**
|
||||
* Path exists - for the given path in a JSON document.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonExists("content", "path.other")
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param propertyName the property that holds a JSON document
|
||||
* @param path the nested path in the JSON document in dot notation
|
||||
*/
|
||||
ExpressionList<T> jsonExists(String propertyName, String path);
|
||||
|
||||
/**
|
||||
* Path does not exist - for the given path in a JSON document.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonNotExists("content", "path.other")
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param propertyName the property that holds a JSON document
|
||||
* @param path the nested path in the JSON document in dot notation
|
||||
*/
|
||||
ExpressionList<T> jsonNotExists(String propertyName, String path);
|
||||
|
||||
/**
|
||||
* Equal to expression for the value at the given path in the JSON document.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonEqualTo("content", "path.other", 34)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param propertyName the property that holds a JSON document
|
||||
* @param path the nested path in the JSON document in dot notation
|
||||
* @param value the value used to test against the document path's value
|
||||
*/
|
||||
ExpressionList<T> jsonEqualTo(String propertyName, String path, Object value);
|
||||
|
||||
/**
|
||||
* Not Equal to - for the given path in a JSON document.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonNotEqualTo("content", "path.other", 34)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param propertyName the property that holds a JSON document
|
||||
* @param path the nested path in the JSON document in dot notation
|
||||
* @param value the value used to test against the document path's value
|
||||
*/
|
||||
ExpressionList<T> jsonNotEqualTo(String propertyName, String path, Object value);
|
||||
|
||||
/**
|
||||
* Greater than - for the given path in a JSON document.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonGreaterThan("content", "path.other", 34)
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> jsonGreaterThan(String propertyName, String path, Object value);
|
||||
|
||||
/**
|
||||
* Greater than or equal to - for the given path in a JSON document.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonGreaterOrEqual("content", "path.other", 34)
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> jsonGreaterOrEqual(String propertyName, String path, Object value);
|
||||
|
||||
/**
|
||||
* Less than - for the given path in a JSON document.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonLessThan("content", "path.other", 34)
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> jsonLessThan(String propertyName, String path, Object value);
|
||||
|
||||
/**
|
||||
* Less than or equal to - for the given path in a JSON document.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonLessOrEqualTo("content", "path.other", 34)
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> jsonLessOrEqualTo(String propertyName, String path, Object value);
|
||||
|
||||
/**
|
||||
* Between - for the given path in a JSON document.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonBetween("content", "orderDate", lowerDateTime, upperDateTime)
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> jsonBetween(String propertyName, String path, Object lowerValue, Object upperValue);
|
||||
|
||||
/**
|
||||
* Add an Expression to the list.
|
||||
* <p>
|
||||
* This returns the list so that add() can be chained.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* Query<Customer> query = Ebean.createQuery(Customer.class);
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* Query<Customer> query = Ebean.find(Customer.class);
|
||||
* query.where()
|
||||
* .like("name","Rob%")
|
||||
* .eq("status", Customer.ACTIVE);
|
||||
* List<Customer> list = query.findList();
|
||||
* .like("name","Rob%")
|
||||
* .eq("status", Customer.ACTIVE);
|
||||
*
|
||||
* List<Customer> list = query.findList();
|
||||
* ...
|
||||
* </pre>
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> add(Expression expr);
|
||||
|
||||
@@ -393,33 +573,36 @@ public interface ExpressionList<T> extends Serializable {
|
||||
* those options such as case insensitive etc.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* // create an example bean and set the properties
|
||||
* // with the query parameters you want
|
||||
* Customer example = new Customer();
|
||||
* example.setName("Rob%");
|
||||
* example.setNotes("%something%");
|
||||
* example.setName("Rob%");
|
||||
* example.setNotes("%something%");
|
||||
*
|
||||
* List<Customer> list = Ebean.find(Customer.class).where()
|
||||
* // pass the bean into the where() clause
|
||||
* .exampleLike(example)
|
||||
* // you can add other expressions to the same query
|
||||
* .gt("id", 2).findList();
|
||||
* .gt("id", 2).findList();
|
||||
*
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* Similarly you can create an ExampleExpression
|
||||
*
|
||||
* <pre>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Customer example = new Customer();
|
||||
* example.setName("Rob%");
|
||||
* example.setNotes("%something%");
|
||||
* example.setName("Rob%");
|
||||
* example.setNotes("%something%");
|
||||
*
|
||||
* // create a ExampleExpression with more control
|
||||
* ExampleExpression qbe = new ExampleExpression(example, true, LikeType.EQUAL_TO).includeZeros();
|
||||
*
|
||||
* List<Customer> list = Ebean.find(Customer.class).where().add(qbe).findList();
|
||||
* </pre>
|
||||
* List<Customer> list = Ebean.find(Customer.class).where().add(qbe).findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> exampleLike(Object example);
|
||||
|
||||
@@ -547,6 +730,14 @@ public interface ExpressionList<T> extends Serializable {
|
||||
* then they are not translated. logical property name names (not fully
|
||||
* qualified) will still be translated to their physical name.
|
||||
* </p>
|
||||
*
|
||||
* <h4>Example:</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // use a database function
|
||||
* raw("add_days(orderDate, 10) < ?", someDate)
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> raw(String raw, Object value);
|
||||
|
||||
@@ -571,6 +762,12 @@ public interface ExpressionList<T> extends Serializable {
|
||||
* then they are not translated. logical property name names (not fully
|
||||
* qualified) will still be translated to their physical name.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* raw("orderQty < shipQty")
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> raw(String raw);
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ import java.util.Set;
|
||||
* .sort("avgTimeMicros desc")
|
||||
* .gt("executionCount", 0)
|
||||
* .gt("lastQueryTime", nowMinus24Hrs)
|
||||
* .eq("autofetchTuned", true)
|
||||
* .eq("autoTuned", true)
|
||||
* .maxRows(10)
|
||||
* .filter(list);
|
||||
*
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Intended to be used as a base class for 'Finder' implementations that can then
|
||||
* be injected or used as public static fields on the associated entity bean.
|
||||
* <p>
|
||||
* These 'finders' are a place to organise all the finder methods for that bean type
|
||||
* and specific finder methods are expected to be added (find by unique properties etc).
|
||||
* </p>
|
||||
*
|
||||
* <h3>Testing</h3>
|
||||
* <p>
|
||||
* For testing the mocki-ebean project has the ability to replace the finder implementation
|
||||
*
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* public class CustomerFinder extends Finder<Customer> {
|
||||
*
|
||||
* public CustomerFinder() {
|
||||
* super(Customer.class);
|
||||
* }
|
||||
*
|
||||
* // Add your customer finder methods ...
|
||||
*
|
||||
* public Customer byName(String name) {
|
||||
* return query().eq("name", name).findUnique();
|
||||
* }
|
||||
*
|
||||
* public List<Customer> findNew() {
|
||||
* return query()
|
||||
* .eq("status", Customer.Status.NEW)
|
||||
* .orderBy("name")
|
||||
* .findList()
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Entity
|
||||
* public class Customer extends BaseModel {
|
||||
*
|
||||
* public static final CustomerFinder find = new CustomerFinder();
|
||||
* ...
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public class Finder<I, T> {
|
||||
|
||||
/**
|
||||
* The entity bean type.
|
||||
*/
|
||||
private final Class<T> type;
|
||||
|
||||
/**
|
||||
* The name of the EbeanServer, null for the default server.
|
||||
*/
|
||||
private final String serverName;
|
||||
|
||||
/**
|
||||
* Create with the type of the entity bean.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* public class CustomerFinder extends Finder<Customer> {
|
||||
*
|
||||
* public CustomerFinder() {
|
||||
* super(Customer.class);
|
||||
* }
|
||||
*
|
||||
* // ... add extra customer specific finder methods
|
||||
* }
|
||||
*
|
||||
* @Entity
|
||||
* public class Customer extends BaseModel {
|
||||
*
|
||||
* public static final CustomerFinder find = new CustomerFinder();
|
||||
* ...
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public Finder(Class<T> type) {
|
||||
this.type = type;
|
||||
this.serverName = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create with the type of the entity bean and specific server name.
|
||||
*/
|
||||
public Finder(Class<T> type, String serverName) {
|
||||
this.type = type;
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying 'default' EbeanServer.
|
||||
*
|
||||
* <p>
|
||||
* This provides full access to the API such as explicit transaction demarcation etc.
|
||||
*
|
||||
*/
|
||||
public EbeanServer db() {
|
||||
return Ebean.getServer(serverName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return typically a different EbeanServer to the default.
|
||||
* <p>
|
||||
* This is equivalent to {@link Ebean#getServer(String)}
|
||||
*
|
||||
* @param server
|
||||
* The name of the EbeanServer. If this is null then the default EbeanServer is
|
||||
* returned.
|
||||
*/
|
||||
public EbeanServer db(String server) {
|
||||
return Ebean.getServer(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an entity reference for this ID.
|
||||
*
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#getReference(Class, Object)}
|
||||
*/
|
||||
public T ref(I id) {
|
||||
return db().getReference(type, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an entity by ID.
|
||||
*
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#find(Class, Object)}
|
||||
*/
|
||||
@Nullable
|
||||
public T byId(I id) {
|
||||
return db().find(type, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a bean by Id.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#delete(Class, Object)}
|
||||
*/
|
||||
public void deleteById(I id) {
|
||||
db().delete(type, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all entities of the given type.
|
||||
*/
|
||||
public List<T> all() {
|
||||
return query().findList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#find(Class)}
|
||||
*/
|
||||
public Query<T> query() {
|
||||
return db().find(type);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import com.avaje.ebean.util.ClassUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -193,7 +194,31 @@ public abstract class Model {
|
||||
public void markAsDirty() {
|
||||
db().markAsDirty(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mark the property as unset or 'not loaded'.
|
||||
* <p>
|
||||
* This would be used to specify a property that we did not wish to include in a stateless update.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // populate an entity bean from JSON or whatever
|
||||
* User user = ...;
|
||||
*
|
||||
* // mark the email property as 'unset' so that it is not
|
||||
* // included in a 'stateless update'
|
||||
* user.markPropertyUnset("email");
|
||||
*
|
||||
* user.update();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param propertyName the name of the property on the bean to be marked as 'unset'
|
||||
*/
|
||||
public void markPropertyUnset(String propertyName) {
|
||||
((EntityBean)this)._ebean_getIntercept().setPropertyLoaded(propertyName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or update this entity depending on its state.
|
||||
*
|
||||
@@ -226,12 +251,24 @@ public abstract class Model {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete this entity.
|
||||
* Delete this bean.
|
||||
* <p>
|
||||
* This will return true if the bean was deleted successfully or JDBC batch is being used.
|
||||
* </p>
|
||||
* <p>
|
||||
* If there is no current transaction one will be created and committed for
|
||||
* you automatically.
|
||||
* </p>
|
||||
* <p>
|
||||
* If the Bean does not have a version property (or loaded version property) and
|
||||
* the bean does not exist then this returns false indicating that nothing was
|
||||
* deleted. Note that, if JDBC batch mode is used then this always returns true.
|
||||
* </p>
|
||||
*
|
||||
* @see EbeanServer#delete(Object)
|
||||
*/
|
||||
public void delete() {
|
||||
db().delete(this);
|
||||
public boolean delete() {
|
||||
return db().delete(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,8 +288,8 @@ public abstract class Model {
|
||||
/**
|
||||
* Perform a delete using this entity against the specified server.
|
||||
*/
|
||||
public void delete(String server) {
|
||||
db(server).delete(this);
|
||||
public boolean delete(String server) {
|
||||
return db(server).delete(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -836,10 +873,10 @@ public abstract class Model {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query with explicit 'Autofetch' use.
|
||||
* Create a query with explicit 'AutoTune' use.
|
||||
*/
|
||||
public Query<T> setAutofetch(boolean autofetch) {
|
||||
return query().setAutofetch(autofetch);
|
||||
public Query<T> setAutoTune(boolean autoTune) {
|
||||
return query().setAutoTune(autoTune);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,7 +28,7 @@ public final class OrderBy<T> implements Serializable {
|
||||
* Create an empty OrderBy with no associated query.
|
||||
*/
|
||||
public OrderBy() {
|
||||
this.list = new ArrayList<Property>(2);
|
||||
this.list = new ArrayList<Property>(3);
|
||||
}
|
||||
|
||||
private OrderBy(List<Property> list) {
|
||||
@@ -52,7 +52,7 @@ public final class OrderBy<T> implements Serializable {
|
||||
*/
|
||||
public OrderBy(Query<T> query, String orderByClause) {
|
||||
this.query = query;
|
||||
this.list = new ArrayList<Property>(2);
|
||||
this.list = new ArrayList<Property>(3);
|
||||
parse(orderByClause);
|
||||
}
|
||||
|
||||
@@ -83,6 +83,19 @@ public final class OrderBy<T> implements Serializable {
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the property is known to be contained in the order by clause.
|
||||
*/
|
||||
public boolean containsProperty(String propertyName) {
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (propertyName.equals(list.get(i).getProperty())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this OrderBy with the path trimmed.
|
||||
*/
|
||||
@@ -185,6 +198,18 @@ public final class OrderBy<T> implements Serializable {
|
||||
return list.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the orderBy removing any current order by properties.
|
||||
* <p>
|
||||
* This is intended to be used when some code creates a query with a
|
||||
* 'default' order by clause and some other code may clear the 'default'
|
||||
* order by clause and replace.
|
||||
* </p>
|
||||
*/
|
||||
public void clear() {
|
||||
list.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* A property and its ascending descending order.
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebean;
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.persistence.NonUniqueResultException;
|
||||
import java.io.Serializable;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
@@ -67,9 +68,9 @@ import java.util.Set;
|
||||
* ...
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Autofetch</h3>
|
||||
* <h3>AutoTune</h3>
|
||||
* <p>
|
||||
* Ebean has built in support for "Autofetch". This is a mechanism where a query
|
||||
* Ebean has built in support for "AutoTune". This is a mechanism where a query
|
||||
* can be automatically tuned based on profiling information that is collected.
|
||||
* </p>
|
||||
* <p>
|
||||
@@ -78,7 +79,7 @@ import java.util.Set;
|
||||
* more.
|
||||
* </p>
|
||||
* <p>
|
||||
* It is expected that Autofetch will be the default approach for many queries
|
||||
* It is expected that AutoTune will be the default approach for many queries
|
||||
* in a system. It is possibly not as useful where the result of a query is sent
|
||||
* to a remote client or where there is some requirement for "Read Consistency"
|
||||
* guarantees.
|
||||
@@ -297,6 +298,16 @@ public interface Query<T> extends Serializable {
|
||||
*/
|
||||
Query<T> asOf(Timestamp asOf);
|
||||
|
||||
/**
|
||||
* Execute the query against the draft set of tables.
|
||||
*/
|
||||
Query<T> asDraft();
|
||||
|
||||
/**
|
||||
* Execute the query including soft deleted rows.
|
||||
*/
|
||||
Query<T> includeSoftDeletes();
|
||||
|
||||
/**
|
||||
* Cancel the query execution if supported by the underlying database and
|
||||
* driver.
|
||||
@@ -338,23 +349,23 @@ public interface Query<T> extends Serializable {
|
||||
ExpressionFactory getExpressionFactory();
|
||||
|
||||
/**
|
||||
* Returns true if this query was tuned by autoFetch.
|
||||
* Returns true if this query was tuned by autoTune.
|
||||
*/
|
||||
boolean isAutofetchTuned();
|
||||
boolean isAutoTuned();
|
||||
|
||||
/**
|
||||
* Explicitly specify whether to use Autofetch for this query.
|
||||
* Explicitly specify whether to use AutoTune for this query.
|
||||
* <p>
|
||||
* If you do not call this method on a query the "Implicit Autofetch mode" is
|
||||
* used to determine if Autofetch should be used for a given query.
|
||||
* If you do not call this method on a query the "Implicit AutoTune mode" is
|
||||
* used to determine if AutoTune should be used for a given query.
|
||||
* </p>
|
||||
* <p>
|
||||
* Autofetch can add additional fetch paths to the query and specify which
|
||||
* AutoTune can add additional fetch paths to the query and specify which
|
||||
* properties are included for each path. If you have explicitly defined some
|
||||
* fetch paths Autofetch will not remove.
|
||||
* fetch paths AutoTune will not remove them.
|
||||
* </p>
|
||||
*/
|
||||
Query<T> setAutofetch(boolean autofetch);
|
||||
Query<T> setAutoTune(boolean autoTune);
|
||||
|
||||
/**
|
||||
* Set the default lazy loading batch size to use.
|
||||
@@ -366,6 +377,16 @@ public interface Query<T> extends Serializable {
|
||||
*/
|
||||
Query<T> setLazyLoadBatchSize(int lazyLoadBatchSize);
|
||||
|
||||
/**
|
||||
* Disable read auditing for this query.
|
||||
* <p>
|
||||
* This is intended to be used when the query is not a user initiated query and instead
|
||||
* part of the internal processing in an application to load a cache or document store etc.
|
||||
* In these cases we don't want the query to be part of read auditing.
|
||||
* </p>
|
||||
*/
|
||||
Query<T> setDisableReadAuditing();
|
||||
|
||||
/**
|
||||
* Explicitly set a comma delimited list of the properties to fetch on the
|
||||
* 'main' root level entity bean (aka partial object). Note that '*' means all
|
||||
@@ -680,7 +701,7 @@ public interface Query<T> extends Serializable {
|
||||
* Execute the query returning either a single bean or null (if no matching
|
||||
* bean is found).
|
||||
* <p>
|
||||
* If more than 1 row is found for this query then a PersistenceException is
|
||||
* If more than 1 row is found for this query then a NonUniqueResultException is
|
||||
* thrown.
|
||||
* </p>
|
||||
* <p>
|
||||
@@ -716,6 +737,8 @@ public interface Query<T> extends Serializable {
|
||||
* List<OrderDetail> details = order.getDetails();
|
||||
* ...
|
||||
* }</pre>
|
||||
*
|
||||
* @throws NonUniqueResultException if more than one result was found
|
||||
*/
|
||||
@Nullable
|
||||
T findUnique();
|
||||
@@ -723,12 +746,38 @@ public interface Query<T> extends Serializable {
|
||||
/**
|
||||
* Return versions of a @History entity bean.
|
||||
* <p>
|
||||
* Note that this query will work against view based history implementations
|
||||
* but not sql2011 standards based implementations that require a start and
|
||||
* end timestamp to be specified.
|
||||
* </p>
|
||||
* <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>
|
||||
*/
|
||||
List<Version<T>> findVersions();
|
||||
|
||||
/**
|
||||
* Return versions of a @History entity bean between the 2 timestamps.
|
||||
* <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>
|
||||
*/
|
||||
List<Version<T>> findVersionsBetween(Timestamp start, Timestamp end);
|
||||
|
||||
/**
|
||||
* 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>
|
||||
*
|
||||
* @return the number of beans/rows that were deleted.
|
||||
*/
|
||||
int delete();
|
||||
|
||||
/**
|
||||
* Return the count of entities this query should return.
|
||||
* <p>
|
||||
@@ -1129,6 +1178,19 @@ public interface Query<T> extends Serializable {
|
||||
|
||||
/**
|
||||
* Set whether this query uses DISTINCT.
|
||||
* <p>
|
||||
* The select() clause MUST be specified when setDistinct(true) is set. The reason for this is that
|
||||
* generally ORM queries include the "id" property and this doesn't make sense for distinct queries.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* Ebean.find(Customer.class)
|
||||
* .setDistinct(true)
|
||||
* .select("name")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
Query<T> setDistinct(boolean isDistinct);
|
||||
|
||||
@@ -1264,4 +1326,13 @@ public interface Query<T> extends Serializable {
|
||||
* </p>
|
||||
*/
|
||||
Query<T> setDisableLazyLoading(boolean disableLazyLoading);
|
||||
|
||||
/**
|
||||
* Returns the set of properties or paths that are unknown (do not map to known properties or paths).
|
||||
* <p>
|
||||
* Validate the query checking the where and orderBy expression paths to confirm if
|
||||
* they represent valid properties or paths for the given bean type.
|
||||
* </p>
|
||||
*/
|
||||
Set<String> validate();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks an entity bean as being included in the change logging.
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ChangeLog {
|
||||
|
||||
/**
|
||||
* Specify if inserts should be explicitly Included or Excluded.
|
||||
* <p>
|
||||
* If not defined explicitly then the server default behaviour defined
|
||||
* on ServerConfig is used.
|
||||
* </p>
|
||||
*/
|
||||
ChangeLogInsertMode inserts() default ChangeLogInsertMode.DEFAULT;
|
||||
|
||||
/**
|
||||
* When specified only include update requests that have at least one
|
||||
* of the given properties as a dirty property.
|
||||
* <p>
|
||||
* This provides a way to filter requests to include in the change log such that
|
||||
* only updates that include at least one of the given properties is included
|
||||
* in the change log.
|
||||
* </p>
|
||||
*/
|
||||
String[] updatesThatInclude() default {};
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
/**
|
||||
* The mode used to determine if inserts should be included or not for a given bean type.
|
||||
*/
|
||||
public enum ChangeLogInsertMode {
|
||||
|
||||
/**
|
||||
* Use the default behaviour as defined on ServerConfig.
|
||||
*/
|
||||
DEFAULT,
|
||||
|
||||
/**
|
||||
* Include inserts in the change log.
|
||||
*/
|
||||
INCLUDE,
|
||||
|
||||
/**
|
||||
* Exclude inserts in the change log.
|
||||
*/
|
||||
EXCLUDE
|
||||
}
|
||||
@@ -9,6 +9,10 @@ import java.lang.annotation.Target;
|
||||
* For a timestamp property that is set to the datetime when the entity is
|
||||
* created/inserted.
|
||||
* <p>
|
||||
* This is effectively an alias for @WhenCreated which was added as it hints
|
||||
* towards a better naming convention (WhenCreated, WhenModified).
|
||||
* </p>
|
||||
* <p>
|
||||
* An alternative to using this annotation would be to use insertable=false,
|
||||
* updateable=false with @Column and have the DB insert the current time
|
||||
* (default value on the DB column is SYSTIME etc).
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
/**
|
||||
* Specify the DB storage type used to with <code>@DbEnumValue</code>.
|
||||
*/
|
||||
public enum DbEnumType {
|
||||
|
||||
/**
|
||||
* Store values as database INTEGER.
|
||||
*/
|
||||
INTEGER,
|
||||
|
||||
/**
|
||||
* Store values as database VARCHAR.
|
||||
*/
|
||||
VARCHAR
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specify a method on an Enum that returns the value that should be stored in the DB.
|
||||
* <p>
|
||||
* This is the preferred option for mapping Enum's to DB values (preferred over the JPA
|
||||
* standard @Enumerated and Ebean's @EnumValue annotations).
|
||||
* </p>
|
||||
* <h3>Example:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* public enum Status {
|
||||
* NEW("N"),
|
||||
* ACTIVE("A"),
|
||||
* INACTIVE("I");
|
||||
*
|
||||
* String dbValue;
|
||||
* Status(String dbValue) {
|
||||
* this.dbValue = dbValue;
|
||||
* }
|
||||
*
|
||||
* @DbEnumValue
|
||||
* public String getValue() {
|
||||
* return dbValue;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface DbEnumValue {
|
||||
|
||||
/**
|
||||
* Specify the database type used to store the values (VARCHAR or INTEGER).
|
||||
*/
|
||||
DbEnumType storage() default DbEnumType.VARCHAR;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks a boolean property on a @Draftable bean that indicates if the bean instance is a 'draft' or 'live' bean.
|
||||
* The property is transient and has no underlying DB column.
|
||||
* <p>
|
||||
* For beans returned from an <code>asDraft()</code> query this property will be set to true.
|
||||
* </p>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Draft {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks a boolean property on a @Draftable bean that only exists on the 'draft' table
|
||||
* and is used to detect when a draft has unpublished changes.
|
||||
* <p>
|
||||
* This property will automatically have it's value set to true when a draft is saved and
|
||||
* automatically have it's value set to false when the bean is published.
|
||||
* </p>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface DraftDirty {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks a property on a @Draftable bean that only exists on the 'draft' and not the 'live' table.
|
||||
* <p>
|
||||
* Typically this would be used on a property that is used as part of application 'workflow' such as
|
||||
* a publish workflow status or when publish timestamp.
|
||||
* </p>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface DraftOnly {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks a property on a @Draftable bean that is set to null on the 'draft bean' on publish.
|
||||
* <p>
|
||||
* This is expected to be put on properties that get 'reset' or 'cleared' after a publish.
|
||||
* These properties might represent a publish comment or publish timestamp.
|
||||
* </p>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface DraftReset {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Used to indicate an entity bean that has 'draftable' support.
|
||||
* <p>
|
||||
* This means that a second set of tables is created to hold draft versions of
|
||||
* the rows and that these can then be published which effectively copies/transfers
|
||||
* the values from the 'draft' table to the 'live' table.
|
||||
* </p>
|
||||
* <p>
|
||||
* Ebean Query supports 'find as draft' which builds the resulting object graph using
|
||||
* the draft tables. This object graph is typically edited, approved in some application
|
||||
* specific manor and then published.
|
||||
* </p>
|
||||
* <p>
|
||||
* EbeanServer has a publish method which transfers/copies the draft object graph to
|
||||
* the 'live' tables.
|
||||
* </p>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface Draftable {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Used to indicate an entity bean that has 'draftable' support but it not a 'top level'
|
||||
* (or root level) bean but instead child related to another @Draftable entity bean.
|
||||
* <p>
|
||||
* Relationships to @DraftableElements (@OneToMany, @ManyToMany etc) are automatically
|
||||
* deemed to have Cascade.ALL for save and delete (as well as orphan removal mode).
|
||||
* </p>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface DraftableElement {
|
||||
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import java.lang.annotation.Target;
|
||||
* }</pre>
|
||||
* <p>
|
||||
* This is an alternative to using the JPA standard approach or Ebean's
|
||||
* {@link EnumMapping} annotation.
|
||||
* {@link DbEnumValue} annotation.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that if all the EnumValue values are parsable as Integers then Ebean
|
||||
|
||||
@@ -10,15 +10,24 @@ import java.lang.annotation.Target;
|
||||
*
|
||||
* @author rvbiljouw
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Target({ElementType.TYPE, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Index {
|
||||
|
||||
/**
|
||||
* Name of the index
|
||||
*
|
||||
* @return index name
|
||||
* Name of the index. If left blank a name is derived using the built in naming convention.
|
||||
*/
|
||||
String value() default "";
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* If set true indicates this is a unique index.
|
||||
*/
|
||||
boolean unique() default false;
|
||||
|
||||
/**
|
||||
* When placed on the class (rather than field) you can specify the columns
|
||||
* to include in the index in order.
|
||||
*/
|
||||
String[] columnNames() default {};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks an entity bean as being included in read auditing.
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ReadAudit {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Used to indicate a property on an entity bean used to control 'soft delete'
|
||||
* (also known as 'logical delete').
|
||||
* <p>
|
||||
* The property should be of type boolean.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* @SoftDelete
|
||||
* boolean deleted;
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface SoftDelete {
|
||||
|
||||
}
|
||||
@@ -8,6 +8,10 @@ import java.lang.annotation.Target;
|
||||
/**
|
||||
* For a timestamp property that is set to the datetime when the entity was last
|
||||
* updated.
|
||||
* <p>
|
||||
* This is effectively an alias for @WhenModified which was added as it hints
|
||||
* towards a better naming convention (WhenCreated, WhenModified).
|
||||
* </p>
|
||||
*/
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
||||
@@ -11,7 +11,6 @@ import java.lang.annotation.Target;
|
||||
* This is effectively an alias for @UpdatedTimestamp and added to hint
|
||||
* towards a better naming convention (WhenCreated, WhenModified).
|
||||
* </p>
|
||||
* </p>
|
||||
*/
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
||||
@@ -31,6 +31,16 @@ public interface BeanCollection<E> extends Serializable {
|
||||
ALL
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a bean to the list/set with modifyListen notification.
|
||||
*/
|
||||
void addBean(E bean);
|
||||
|
||||
/**
|
||||
* Remove a bean to the list/set with modifyListen notification.
|
||||
*/
|
||||
void removeBean(E bean);
|
||||
|
||||
/**
|
||||
* Reset the collection back to an empty state ready for reloading.
|
||||
* <p>
|
||||
|
||||
@@ -12,5 +12,5 @@ public interface BeanCollectionAdd {
|
||||
/**
|
||||
* Add a loaded bean to the collection.
|
||||
*/
|
||||
void addBean(EntityBean bean);
|
||||
void addEntityBean(EntityBean bean);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import java.util.Arrays;
|
||||
/**
|
||||
* Represent the call stack (stack trace elements).
|
||||
* <p>
|
||||
* Used with a query to identify a CallStackQuery for AutoFetch automatic query
|
||||
* Used with a query to identify a CallStackQuery for AutoTune automatic query
|
||||
* tuning.
|
||||
* </p>
|
||||
* <p>
|
||||
@@ -26,16 +26,12 @@ public final class CallStack implements Serializable {
|
||||
|
||||
private final StackTraceElement[] callStack;
|
||||
|
||||
public CallStack(StackTraceElement[] callStack) {
|
||||
public CallStack(StackTraceElement[] callStack, int zeroHash, int pathHash) {
|
||||
this.callStack = callStack;
|
||||
this.zeroHash = enc(callStack[0].hashCode());
|
||||
int hc = 0;
|
||||
for (int i = 1; i < callStack.length; i++) {
|
||||
hc = 31 * hc + callStack[i].hashCode();
|
||||
}
|
||||
this.pathHash = enc(hc);
|
||||
this.zeroHash = enc(zeroHash);
|
||||
this.pathHash = enc(pathHash);
|
||||
}
|
||||
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 0;
|
||||
for (int i = 0; i < callStack.length; i++) {
|
||||
@@ -87,8 +83,19 @@ public final class CallStack implements Serializable {
|
||||
return zeroHash + ":" + pathHash + ":" + callStack[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the call stack lines appended with the given newLine string.
|
||||
*/
|
||||
public String description(String newLine) {
|
||||
StringBuilder sb = new StringBuilder(400);
|
||||
for (int i = 0; i < callStack.length; i++) {
|
||||
sb.append(callStack[i].toString()).append(newLine);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getOriginKey(int queryHash) {
|
||||
return zeroHash + "." + enc(queryHash) + "." + pathHash;
|
||||
return enc(queryHash)+ "." + zeroHash + "." + pathHash;
|
||||
}
|
||||
|
||||
private static final int radix = 1 << 6;
|
||||
|
||||
@@ -449,7 +449,18 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
public int getPropertyLength() {
|
||||
return owner._ebean_getPropertyNames().length;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the loaded state of the property given it's name.
|
||||
*/
|
||||
public void setPropertyLoaded(String propertyName, boolean loaded) {
|
||||
int position = findProperty(propertyName);
|
||||
if (position == -1) {
|
||||
throw new IllegalArgumentException("Property "+propertyName+" not found");
|
||||
}
|
||||
loadedProps[position] = loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the property to be treated as unloaded. Used for properties initialised in default
|
||||
* constructor.
|
||||
@@ -496,7 +507,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
private void setChangedProperty(int propertyIndex) {
|
||||
public void setChangedProperty(int propertyIndex) {
|
||||
if (changedProps == null) {
|
||||
changedProps = new boolean[owner._ebean_getPropertyNames().length];
|
||||
}
|
||||
@@ -581,7 +592,29 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if any of the given property names are dirty.
|
||||
*/
|
||||
public boolean hasDirtyProperty(Set<String> propertyNames) {
|
||||
|
||||
String[] names = owner._ebean_getPropertyNames();
|
||||
int len = getPropertyLength();
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (changedProps != null && changedProps[i]) {
|
||||
// the property has been changed on this bean
|
||||
if (propertyNames.contains(names[i])) {
|
||||
return true;
|
||||
}
|
||||
} else if (embeddedDirty != null && embeddedDirty[i]) {
|
||||
if (propertyNames.contains(names[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a map of dirty properties with their new and old values.
|
||||
*/
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.avaje.ebean.bean;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Collects profile information for a bean (or reference/proxy bean) at a given
|
||||
* AutoFetchNode.
|
||||
* Collects profile information for a bean (or reference/proxy bean) at a given node.
|
||||
* <p>
|
||||
* The AutoFetchNode identifies the location of the bean in the object graph.
|
||||
* The node identifies the location of the bean in the object graph.
|
||||
* </p>
|
||||
* <p>
|
||||
* It has to use a weak reference so as to ensure that it does not stop the
|
||||
@@ -29,7 +29,7 @@ public final class NodeUsageCollector {
|
||||
/**
|
||||
* The properties used at this profile point.
|
||||
*/
|
||||
private final HashSet<String> used = new HashSet<String>();
|
||||
private final Set<String> used = new LinkedHashSet<String>();
|
||||
|
||||
/**
|
||||
* set to true if the bean is modified (setter called)
|
||||
@@ -105,7 +105,7 @@ public final class NodeUsageCollector {
|
||||
/**
|
||||
* Return the set of used properties.
|
||||
*/
|
||||
public HashSet<String> getUsed() {
|
||||
public Set<String> getUsed() {
|
||||
return used;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ public interface NodeUsageListener {
|
||||
* Collect node usage "profiling" information.
|
||||
* <p>
|
||||
* This is the properties that are used for a given bean in the object graph.
|
||||
* This information is used by autoFetch to tune queries.
|
||||
* This information is used by autoTune to tune queries.
|
||||
* </p>
|
||||
*/
|
||||
void collectNodeUsage(NodeUsageCollector collector);
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.io.Serializable;
|
||||
* The call stack is included so that the query can have different tuned fetches
|
||||
* for each unique call stack. For example, a query to fetch a customer could be
|
||||
* called by three different methods and each can be treated as a separate
|
||||
* origin point (and autoFetch can tune each one separately).
|
||||
* origin point (and autoTune can tune each one separately).
|
||||
* </p>
|
||||
*/
|
||||
public final class ObjectGraphOrigin implements Serializable {
|
||||
|
||||
@@ -60,7 +60,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addBean(EntityBean bean) {
|
||||
public void addEntityBean(EntityBean bean) {
|
||||
list.add((E) bean);
|
||||
}
|
||||
|
||||
@@ -206,6 +206,11 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
list.add(index, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBean(E bean) {
|
||||
add(bean);
|
||||
}
|
||||
|
||||
public boolean add(E o) {
|
||||
checkReadOnly();
|
||||
init();
|
||||
@@ -319,6 +324,13 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
return list.listIterator(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBean(E bean) {
|
||||
if (list.remove(bean)) {
|
||||
getModifyHolder().modifyRemoval(bean);
|
||||
}
|
||||
}
|
||||
|
||||
public E remove(int index) {
|
||||
checkReadOnly();
|
||||
init();
|
||||
|
||||
@@ -147,8 +147,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
public Collection<E> getActualDetails() {
|
||||
return map.values();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the map entrySet.
|
||||
* <p>
|
||||
@@ -158,7 +157,6 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
*/
|
||||
public Collection<?> getActualEntries() {
|
||||
return map.entrySet();
|
||||
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
@@ -274,6 +272,16 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
map.putAll(puts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBean(E bean) {
|
||||
throw new IllegalStateException("Method not allowed on Map. Please use List instead.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBean(E bean) {
|
||||
throw new IllegalStateException("Method not allowed on Map. Please use List instead.");
|
||||
}
|
||||
|
||||
public E remove(Object key) {
|
||||
checkReadOnly();
|
||||
init();
|
||||
|
||||
@@ -53,7 +53,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addBean(EntityBean bean) {
|
||||
public void addEntityBean(EntityBean bean) {
|
||||
set.add((E) bean);
|
||||
}
|
||||
|
||||
@@ -174,6 +174,18 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
return set.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBean(E bean) {
|
||||
add(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBean(E bean) {
|
||||
if (set.remove(bean)) {
|
||||
getModifyHolder().modifyRemoval(bean);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------//
|
||||
// proxy method for map
|
||||
// -----------------------------------------------------//
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
/**
|
||||
* Defines the AutoTune behaviour for a EbeanServer.
|
||||
*/
|
||||
public class AutoTuneConfig {
|
||||
|
||||
private AutoTuneMode mode = AutoTuneMode.DEFAULT_ON;
|
||||
|
||||
private String queryTuningFile = "ebean-autotune.xml";
|
||||
|
||||
private boolean queryTuning;
|
||||
|
||||
private boolean queryTuningAddVersion;
|
||||
|
||||
private boolean profiling;
|
||||
|
||||
private String profilingFile = "ebean-profiling";
|
||||
|
||||
private int profilingBase = 5;
|
||||
|
||||
private double profilingRate = 0.01;
|
||||
|
||||
private int garbageCollectionWait = 100;
|
||||
|
||||
private boolean skipCollectionOnShutdown;
|
||||
|
||||
public AutoTuneConfig() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the file that holds the query tuning information.
|
||||
*/
|
||||
public String getQueryTuningFile() {
|
||||
return queryTuningFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the file that holds the query tuning information.
|
||||
*/
|
||||
public void setQueryTuningFile(String queryTuningFile) {
|
||||
this.queryTuningFile = queryTuningFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the file that profiling information is written to.
|
||||
*/
|
||||
public String getProfilingFile() {
|
||||
return profilingFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the file that profiling information is written to.
|
||||
*/
|
||||
public void setProfilingFile(String profilingFile) {
|
||||
this.profilingFile = profilingFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mode used when autoTune has not been explicit defined on a
|
||||
* query.
|
||||
*/
|
||||
public AutoTuneMode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mode used when autoTune has not been explicit defined on a query.
|
||||
*/
|
||||
public void setMode(AutoTuneMode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the queries are being tuned.
|
||||
*/
|
||||
public boolean isQueryTuning() {
|
||||
return queryTuning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if the queries should be tuned by autoTune.
|
||||
*/
|
||||
public void setQueryTuning(boolean queryTuning) {
|
||||
this.queryTuning = queryTuning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the version property should be added when the query is
|
||||
* tuned.
|
||||
* <p>
|
||||
* If this is false then the version property will be added when profiling
|
||||
* detects that the bean is possibly going to be modified.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isQueryTuningAddVersion() {
|
||||
return queryTuningAddVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to force the version property to be always added by the query
|
||||
* tuning.
|
||||
* <p>
|
||||
* If this is false then the version property will be added when profiling
|
||||
* detects that the bean is possibly going to be modified.
|
||||
* </p>
|
||||
* <p>
|
||||
* Generally this is not expected to be turned on.
|
||||
* </p>
|
||||
*/
|
||||
public void setQueryTuningAddVersion(boolean queryTuningAddVersion) {
|
||||
this.queryTuningAddVersion = queryTuningAddVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if profiling information should be collected.
|
||||
*/
|
||||
public boolean isProfiling() {
|
||||
return profiling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if profiling information should be collected.
|
||||
* <p>
|
||||
* The profiling information is collected and then used to generate the tuned
|
||||
* queries for autoTune.
|
||||
* </p>
|
||||
*/
|
||||
public void setProfiling(boolean profiling) {
|
||||
this.profiling = profiling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base number of queries to profile before changing to profile
|
||||
* only a percentage of following queries (profileRate).
|
||||
*/
|
||||
public int getProfilingBase() {
|
||||
return profilingBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the based number of queries to profile.
|
||||
*/
|
||||
public void setProfilingBase(int profilingBase) {
|
||||
this.profilingBase = profilingBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the rate (%) of queries to be profiled after the 'base' amount of
|
||||
* profiling.
|
||||
*/
|
||||
public double getProfilingRate() {
|
||||
return profilingRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rate (%) of queries to be profiled after the 'base' amount of
|
||||
* profiling.
|
||||
*/
|
||||
public void setProfilingRate(double profilingRate) {
|
||||
this.profilingRate = profilingRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time in millis to wait after a system gc to collect profiling
|
||||
* information.
|
||||
* <p>
|
||||
* The profiling information is collected on object finalise. As such we
|
||||
* generally don't want to trigger GC (let the JVM do its thing) but on
|
||||
* shutdown the autoTune manager will trigger System.gc() and then wait
|
||||
* (default 100 millis) to hopefully collect profiling information -
|
||||
* especially for short run unit tests.
|
||||
* </p>
|
||||
*/
|
||||
public int getGarbageCollectionWait() {
|
||||
return garbageCollectionWait;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time in millis to wait after a System.gc() to collect profiling information.
|
||||
*/
|
||||
public void setGarbageCollectionWait(int garbageCollectionWait) {
|
||||
this.garbageCollectionWait = garbageCollectionWait;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if profiling collection should be skipped on shutdown.
|
||||
*/
|
||||
public boolean isSkipCollectionOnShutdown() {
|
||||
return skipCollectionOnShutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if profiling collection should be skipped on shutdown.
|
||||
*/
|
||||
public void setSkipCollectionOnShutdown(boolean skipCollectionOnShutdown) {
|
||||
this.skipCollectionOnShutdown = skipCollectionOnShutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the settings from the properties file.
|
||||
*/
|
||||
public void loadSettings(PropertiesWrapper p) {
|
||||
|
||||
queryTuning = p.getBoolean("autoTune.queryTuning", queryTuning);
|
||||
queryTuningAddVersion = p.getBoolean("autoTune.queryTuningAddVersion", queryTuningAddVersion);
|
||||
queryTuningFile = p.get("autoTune.queryTuningFile", queryTuningFile);
|
||||
|
||||
skipCollectionOnShutdown = p.getBoolean("autoTune.skipCollectionOnShutdown", skipCollectionOnShutdown);
|
||||
|
||||
mode = p.getEnum(AutoTuneMode.class, "autoTune.mode", mode);
|
||||
|
||||
profiling = p.getBoolean("autoTune.profiling", profiling);
|
||||
profilingBase = p.getInt("autoTune.profilingBase", profilingBase);
|
||||
profilingRate = p.getDouble("autoTune.profilingRate", profilingRate);
|
||||
profilingFile = p.get("autoTune.profilingFile", profilingFile);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
import com.avaje.ebean.Query;
|
||||
|
||||
/**
|
||||
* The mode for determining if AutoTune will be used for a given query when
|
||||
* {@link Query#setAutoTune(boolean)} has not been explicitly set on a query.
|
||||
* <p>
|
||||
* The explicit control of {@link Query#setAutoTune(boolean)} will always take
|
||||
* precedence. This mode is used when this has not been explicitly set on a
|
||||
* query.
|
||||
* </p>
|
||||
*/
|
||||
public enum AutoTuneMode {
|
||||
|
||||
/**
|
||||
* Don't implicitly use AutoTune. Must explicitly turn it on.
|
||||
*/
|
||||
DEFAULT_OFF,
|
||||
|
||||
/**
|
||||
* Use AutoTune implicitly. Must explicitly turn it off.
|
||||
*/
|
||||
DEFAULT_ON,
|
||||
|
||||
/**
|
||||
* Implicitly use AutoTune if the query has not got either select() or join()
|
||||
* defined.
|
||||
*/
|
||||
DEFAULT_ONIFEMPTY
|
||||
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
/**
|
||||
* Defines the Autofetch behaviour for a EbeanServer.
|
||||
*/
|
||||
public class AutofetchConfig {
|
||||
|
||||
private AutofetchMode mode = AutofetchMode.DEFAULT_ONIFEMPTY;
|
||||
|
||||
private boolean queryTuning = false;
|
||||
|
||||
private boolean queryTuningAddVersion = false;
|
||||
|
||||
private boolean profiling = false;
|
||||
|
||||
private int profilingMin = 1;
|
||||
|
||||
private int profilingBase = 10;
|
||||
|
||||
private double profilingRate = 0.05;
|
||||
|
||||
private String logDirectory;
|
||||
|
||||
private int profileUpdateFrequency = 60;
|
||||
|
||||
private int garbageCollectionWait = 100;
|
||||
|
||||
private boolean garbageCollectionOnShutdown;
|
||||
|
||||
public AutofetchConfig() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mode used when autofetch has not been explicit defined on a
|
||||
* query.
|
||||
*/
|
||||
public AutofetchMode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mode used when autofetch has not been explicit defined on a query.
|
||||
*/
|
||||
public void setMode(AutofetchMode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the queries are being tuned.
|
||||
*/
|
||||
public boolean isQueryTuning() {
|
||||
return queryTuning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if the queries should be tuned by autofetch.
|
||||
*/
|
||||
public void setQueryTuning(boolean queryTuning) {
|
||||
this.queryTuning = queryTuning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the version property should be added when the query is
|
||||
* tuned.
|
||||
* <p>
|
||||
* If this is false then the version property will be added when profiling
|
||||
* detects that the bean is possibly going to be modified.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isQueryTuningAddVersion() {
|
||||
return queryTuningAddVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to force the version property to be always added by the query
|
||||
* tuning.
|
||||
* <p>
|
||||
* If this is false then the version property will be added when profiling
|
||||
* detects that the bean is possibly going to be modified.
|
||||
* </p>
|
||||
*/
|
||||
public void setQueryTuningAddVersion(boolean queryTuningAddVersion) {
|
||||
this.queryTuningAddVersion = queryTuningAddVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if profiling information should be collected.
|
||||
*/
|
||||
public boolean isProfiling() {
|
||||
return profiling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if profiling information should be collected.
|
||||
* <p>
|
||||
* The profiling information is collected and then used to generate the tuned
|
||||
* queries for autofetch.
|
||||
* </p>
|
||||
*/
|
||||
public void setProfiling(boolean profiling) {
|
||||
this.profiling = profiling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum number of queries to profile before autofetch will start
|
||||
* tuning the queries.
|
||||
*/
|
||||
public int getProfilingMin() {
|
||||
return profilingMin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum number of queries to profile before autofetch will start
|
||||
* tuning the queries.
|
||||
*/
|
||||
public void setProfilingMin(int profilingMin) {
|
||||
this.profilingMin = profilingMin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base number of queries to profile before changing to profile
|
||||
* only a percentage of following queries (profileRate).
|
||||
*/
|
||||
public int getProfilingBase() {
|
||||
return profilingBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the based number of queries to profile.
|
||||
*/
|
||||
public void setProfilingBase(int profilingBase) {
|
||||
this.profilingBase = profilingBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the rate (%) of queries to be profiled after the 'base' amount of
|
||||
* profiling.
|
||||
*/
|
||||
public double getProfilingRate() {
|
||||
return profilingRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rate (%) of queries to be profiled after the 'base' amount of
|
||||
* profiling.
|
||||
*/
|
||||
public void setProfilingRate(double profilingRate) {
|
||||
this.profilingRate = profilingRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the log directory to put the autofetch log.
|
||||
*/
|
||||
public String getLogDirectory() {
|
||||
return logDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the directory to put the autofetch log in.
|
||||
*/
|
||||
public void setLogDirectory(String logDirectory) {
|
||||
this.logDirectory = logDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the frequency in seconds to update the autofetch tuned queries from
|
||||
* the profiled information.
|
||||
*/
|
||||
public int getProfileUpdateFrequency() {
|
||||
return profileUpdateFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the frequency in seconds to update the autofetch tuned queries from the
|
||||
* profiled information.
|
||||
*/
|
||||
public void setProfileUpdateFrequency(int profileUpdateFrequency) {
|
||||
this.profileUpdateFrequency = profileUpdateFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time in millis to wait after a system gc to collect profiling
|
||||
* information.
|
||||
* <p>
|
||||
* The profiling information is collected on object finalise. As such we
|
||||
* generally don't want to trigger GC (let the JVM do its thing) but on
|
||||
* shutdown the autofetch manager will trigger System.gc() and then wait
|
||||
* (default 100 millis) to hopefully collect profiling information -
|
||||
* especially for short run unit tests.
|
||||
* </p>
|
||||
*/
|
||||
public int getGarbageCollectionWait() {
|
||||
return garbageCollectionWait;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time in millis to wait after a System.gc() to collect profiling
|
||||
* information.
|
||||
*/
|
||||
public void setGarbageCollectionWait(int garbageCollectionWait) {
|
||||
this.garbageCollectionWait = garbageCollectionWait;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if GC should be trigger on shutdown.
|
||||
* <p>
|
||||
* Autofetch profiling information is collected as part of garbage collection.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isGarbageCollectionOnShutdown() {
|
||||
return garbageCollectionOnShutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if you want GC to trigger on shutdown.
|
||||
* <p>
|
||||
* This would be done if you want to try and collect Autofetch profiling information
|
||||
* on shutdown.
|
||||
* </p>
|
||||
*/
|
||||
public void setGarbageCollectionOnShutdown(boolean garbageCollectionOnShutdown) {
|
||||
this.garbageCollectionOnShutdown = garbageCollectionOnShutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the settings from the properties file.
|
||||
*/
|
||||
public void loadSettings(PropertiesWrapper p) {
|
||||
|
||||
logDirectory = p.get("autofetch.logDirectory", logDirectory);
|
||||
queryTuning = p.getBoolean("autofetch.querytuning", queryTuning);
|
||||
queryTuningAddVersion = p.getBoolean("autofetch.queryTuningAddVersion", queryTuningAddVersion);
|
||||
garbageCollectionOnShutdown = p.getBoolean("autofetch.garbageCollectionOnShutdown", garbageCollectionOnShutdown);
|
||||
|
||||
profiling = p.getBoolean("autofetch.profiling", profiling);
|
||||
mode = p.getEnum(AutofetchMode.class, "autofetch.implicitmode", mode);
|
||||
|
||||
profilingMin = p.getInt("autofetch.profiling.min", profilingMin);
|
||||
profilingBase = p.getInt("autofetch.profiling.base", profilingBase);
|
||||
|
||||
profilingRate = p.getDouble("autofetch.profiling.rate", profilingRate);
|
||||
profileUpdateFrequency = p.getInt("autofetch.profiling.updatefrequency", profileUpdateFrequency);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
import com.avaje.ebean.Query;
|
||||
|
||||
/**
|
||||
* The mode for determining if Autofetch will be used for a given query when
|
||||
* {@link Query#setAutofetch(boolean)} has not been explicitly set on a query.
|
||||
* <p>
|
||||
* The explicit control of {@link Query#setAutofetch(boolean)} will always take
|
||||
* precedence. This mode is used when this has not been explicitly set on a
|
||||
* query.
|
||||
* </p>
|
||||
*/
|
||||
public enum AutofetchMode {
|
||||
|
||||
/**
|
||||
* Don't implicitly use Autofetch. Must explicitly turn it on.
|
||||
*/
|
||||
DEFAULT_OFF,
|
||||
|
||||
/**
|
||||
* Use Autofetch implicitly. Must explicitly turn it off.
|
||||
*/
|
||||
DEFAULT_ON,
|
||||
|
||||
/**
|
||||
* Implicitly use Autofetch if the query has not got either select() or join()
|
||||
* defined.
|
||||
*/
|
||||
DEFAULT_ONIFEMPTY
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
|
||||
/**
|
||||
* Helper to find classes taking into account the context class loader.
|
||||
*/
|
||||
public class ClassLoadConfig {
|
||||
|
||||
protected final ClassLoaderContext context;
|
||||
|
||||
/**
|
||||
* Construct with the default classLoader search with context classLoader first.
|
||||
*/
|
||||
public ClassLoadConfig() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the classLoader to use for class detection and new instance creation.
|
||||
*/
|
||||
public ClassLoadConfig(ClassLoader classLoader) {
|
||||
this.context = new ClassLoaderContext(classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the Java.time types are available and should be supported.
|
||||
*/
|
||||
public boolean isJavaTimePresent() {
|
||||
return isPresent("java.time.LocalDate");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the Joda types are available and should be supported.
|
||||
*/
|
||||
public boolean isJodaTimePresent() {
|
||||
return isPresent("org.joda.time.LocalDateTime");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if javax validation annotations like Size and NotNull are present.
|
||||
*/
|
||||
public boolean isJavaxValidationAnnotationsPresent() {
|
||||
return isPresent("javax.validation.constraints.NotNull");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if Jackson annotations like JsonIgnore are present.
|
||||
*/
|
||||
public boolean isJacksonAnnotationsPresent() {
|
||||
return isPresent("com.fasterxml.jackson.annotation.JsonIgnore");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if Jackson ObjectMapper is present.
|
||||
*/
|
||||
public boolean isJacksonObjectMapperPresent() {
|
||||
return isPresent("com.fasterxml.jackson.databind.ObjectMapper");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance of the class using the default constructor.
|
||||
*/
|
||||
public Object newInstance(String className) {
|
||||
|
||||
try {
|
||||
Class<?> cls = forName(className);
|
||||
return cls.newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Error constructing " + className, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given class is present.
|
||||
*/
|
||||
protected boolean isPresent(String className) {
|
||||
try {
|
||||
forName(className);
|
||||
return true;
|
||||
} catch (Throwable ex) {
|
||||
// Class or one of its dependencies is not present...
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a class taking into account a context class loader (if present).
|
||||
*/
|
||||
protected Class<?> forName(String name) throws ClassNotFoundException {
|
||||
return context.forName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the preferred, caller and context class loaders.
|
||||
*/
|
||||
protected class ClassLoaderContext {
|
||||
|
||||
/**
|
||||
* Optional - if set only use this classLoader (no fallback).
|
||||
*/
|
||||
protected final ClassLoader preferredLoader;
|
||||
|
||||
protected final ClassLoader contextLoader;
|
||||
|
||||
protected final ClassLoader callerLoader;
|
||||
|
||||
ClassLoaderContext(ClassLoader preferredLoader) {
|
||||
this.preferredLoader = preferredLoader;
|
||||
this.callerLoader = ServerConfig.class.getClassLoader();
|
||||
this.contextLoader = contextLoader();
|
||||
}
|
||||
|
||||
ClassLoader contextLoader() {
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
return (loader != null) ? loader: callerLoader;
|
||||
}
|
||||
|
||||
Class<?> forName(String name) throws ClassNotFoundException {
|
||||
|
||||
if (preferredLoader != null) {
|
||||
// only use the explicitly set classLoader
|
||||
return classForName(name, preferredLoader);
|
||||
}
|
||||
try {
|
||||
// try the context loader first
|
||||
return classForName(name, contextLoader);
|
||||
} catch (ClassNotFoundException e) {
|
||||
if (callerLoader == contextLoader) {
|
||||
throw e;
|
||||
} else {
|
||||
// fallback to the caller classLoader
|
||||
return classForName(name, callerLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> classForName(String name, ClassLoader classLoader) throws ClassNotFoundException {
|
||||
return Class.forName(name, true, classLoader);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,13 +45,29 @@ public class DbConstraintNaming {
|
||||
protected String ckPrefix = "ck_";
|
||||
protected String ckSuffix = "";
|
||||
|
||||
protected boolean lowerCaseNames = true;
|
||||
|
||||
protected MaxLength maxLength;
|
||||
|
||||
protected DbConstraintNormalise normalise = new DbConstraintNormalise();
|
||||
protected DbConstraintNormalise normalise;
|
||||
|
||||
/**
|
||||
* Construct using default of lower case for both table and column names.
|
||||
*/
|
||||
public DbConstraintNaming() {
|
||||
this(true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct specifying if lower case should be used (for both table and column names).
|
||||
*/
|
||||
public DbConstraintNaming(boolean lowerCase) {
|
||||
this(lowerCase, lowerCase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct specifying if lower case should be used for both table and column names.
|
||||
*/
|
||||
public DbConstraintNaming(boolean lowerCaseTableNames, boolean lowerCaseColumnNames) {
|
||||
this.normalise = new DbConstraintNormalise(lowerCaseTableNames, lowerCaseColumnNames);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,7 +183,6 @@ public class DbConstraintNaming {
|
||||
* quoted identifier characters (",',[,] etc).
|
||||
*/
|
||||
public String normaliseTable(String tableName) {
|
||||
|
||||
return normalise.normaliseTable(tableName);
|
||||
}
|
||||
|
||||
@@ -175,18 +190,20 @@ public class DbConstraintNaming {
|
||||
* Normalise the column name by removing any quoted identifier characters (",',[,] etc).
|
||||
*/
|
||||
public String normaliseColumn(String tableName) {
|
||||
|
||||
return normalise.normaliseColumn(tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lower case the table or column name checking for quoted identifiers.
|
||||
* Lower case the table name checking for quoted identifiers.
|
||||
*/
|
||||
public String lowerName(String tableName) {
|
||||
if (lowerCaseNames && normalise.notQuoted(tableName)) {
|
||||
return tableName.toLowerCase();
|
||||
}
|
||||
return tableName;
|
||||
public String lowerTableName(String tableName) {
|
||||
return normalise.lowerTableName(tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lower case the column name checking for quoted identifiers.
|
||||
*/
|
||||
public String lowerColumnName(String name) {
|
||||
return normalise.lowerColumnName(name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,17 @@ public class DbConstraintNormalise {
|
||||
|
||||
protected final String[] quotedIdentifiers;
|
||||
|
||||
protected boolean lowerCaseTables = true;
|
||||
protected final boolean lowerCaseTables;
|
||||
|
||||
protected boolean lowerCaseColumns = true;
|
||||
protected final boolean lowerCaseColumns;
|
||||
|
||||
public DbConstraintNormalise() {
|
||||
this(true, true);
|
||||
}
|
||||
|
||||
public DbConstraintNormalise(boolean lowerCaseTables, boolean lowerCaseColumns) {
|
||||
this.lowerCaseTables = lowerCaseTables;
|
||||
this.lowerCaseColumns = lowerCaseColumns;
|
||||
this.quotedIdentifiers = new String[]{"\"", "'", "[", "]", "`"};
|
||||
}
|
||||
|
||||
@@ -46,6 +52,26 @@ public class DbConstraintNormalise {
|
||||
return columnName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lower case the table name checking for quoted identifiers.
|
||||
*/
|
||||
public String lowerTableName(String tableName) {
|
||||
if (lowerCaseTables && notQuoted(tableName)) {
|
||||
return tableName.toLowerCase();
|
||||
}
|
||||
return tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lower case the column name checking for quoted identifiers.
|
||||
*/
|
||||
public String lowerColumnName(String name) {
|
||||
if (lowerCaseColumns && notQuoted(name)) {
|
||||
return name.toLowerCase();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim off the platform quoted identifier quotes like [ ' and ".
|
||||
*/
|
||||
|
||||
@@ -6,27 +6,26 @@ package com.avaje.ebean.config;
|
||||
public class DbMigrationConfig {
|
||||
|
||||
/**
|
||||
* The application name which is used as the unique code when applying migrations.
|
||||
* Resource path for the migration xml and sql.
|
||||
* Typically you would change 'app' to be a better/more unique.
|
||||
*/
|
||||
private String appName;
|
||||
private String resourcePath = "dbmigration/app";
|
||||
|
||||
/**
|
||||
* Path where migration
|
||||
* Return the resource path for db migrations.
|
||||
*/
|
||||
private String resourcePath;
|
||||
|
||||
public String getAppName() {
|
||||
return appName;
|
||||
}
|
||||
|
||||
public void setAppName(String appName) {
|
||||
this.appName = appName;
|
||||
}
|
||||
|
||||
public String getResourcePath() {
|
||||
return resourcePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource path for db migrations.
|
||||
* <p>
|
||||
* Typically this would be something like "dbmigration/myapp" where myapp gives it a
|
||||
* unique resource path in the case there are multiple EbeanServer applications in the
|
||||
* single classpath.
|
||||
* </p>
|
||||
*/
|
||||
public void setResourcePath(String resourcePath) {
|
||||
this.resourcePath = resourcePath;
|
||||
}
|
||||
@@ -35,8 +34,6 @@ public class DbMigrationConfig {
|
||||
* Load the settings from the PropertiesWrapper.
|
||||
*/
|
||||
public void loadSettings(PropertiesWrapper properties) {
|
||||
|
||||
appName = properties.get("migration.appName", appName);
|
||||
resourcePath = properties.get("migration.resourcePath", resourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,14 @@ public final class PropertyMap implements Serializable {
|
||||
|
||||
private final LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Return properties loaded from test-ebean.properties.
|
||||
*/
|
||||
public static Properties testProperties() {
|
||||
PropertyMap propertyMap = PropertyMapLoader.loadTestProperties();
|
||||
return (propertyMap == null) ? new Properties() : propertyMap.asProperties();
|
||||
}
|
||||
|
||||
public static Properties defaultProperties() {
|
||||
PropertyMap propertyMap = PropertyMapLoader.loadGlobalProperties();
|
||||
return (propertyMap == null) ? new Properties() : propertyMap.asProperties();
|
||||
|
||||
@@ -14,17 +14,34 @@ final class PropertyMapLoader {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PropertyMapLoader.class);
|
||||
|
||||
/**
|
||||
* Load the <code>test-ebean.properties</code>.
|
||||
*/
|
||||
public static PropertyMap loadTestProperties() {
|
||||
return load(null, "test-ebean.properties");
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the ebean.properties (and test-ebean.properties if present).
|
||||
*/
|
||||
public static PropertyMap loadGlobalProperties() {
|
||||
|
||||
boolean loadTestProperties = false;
|
||||
String fileName = System.getenv("EBEAN_PROPS_FILE");
|
||||
if (fileName == null) {
|
||||
fileName = System.getProperty("ebean.props.file");
|
||||
if (fileName == null) {
|
||||
loadTestProperties = true;
|
||||
fileName = "ebean.properties";
|
||||
}
|
||||
}
|
||||
|
||||
return load(null, fileName);
|
||||
PropertyMap map = load(null, fileName);
|
||||
if (loadTestProperties) {
|
||||
// load test properties if present in classpath
|
||||
map = load(map, "test-ebean.properties");
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,7 +56,6 @@ final class PropertyMapLoader {
|
||||
|
||||
InputStream is = findInputStream(fileName);
|
||||
if (is == null) {
|
||||
logger.error(fileName + " not found");
|
||||
return p;
|
||||
} else {
|
||||
return load(p, is);
|
||||
@@ -47,7 +63,7 @@ final class PropertyMapLoader {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the inputstream returning the property map.
|
||||
* Load the InputStream returning the property map.
|
||||
*
|
||||
* @param p
|
||||
* an existing property map to load into.
|
||||
|
||||
@@ -7,9 +7,20 @@ import com.avaje.ebean.cache.ServerCacheFactory;
|
||||
import com.avaje.ebean.cache.ServerCacheManager;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.config.dbplatform.DbEncrypt;
|
||||
import com.avaje.ebean.event.*;
|
||||
import com.avaje.ebean.event.BeanFindController;
|
||||
import com.avaje.ebean.event.BeanPersistController;
|
||||
import com.avaje.ebean.event.BeanPersistListener;
|
||||
import com.avaje.ebean.event.BeanPostLoad;
|
||||
import com.avaje.ebean.event.BeanQueryAdapter;
|
||||
import com.avaje.ebean.event.BulkTableEventListener;
|
||||
import com.avaje.ebean.event.ServerConfigStartup;
|
||||
import com.avaje.ebean.event.TransactionEventListener;
|
||||
import com.avaje.ebean.event.changelog.ChangeLogListener;
|
||||
import com.avaje.ebean.event.changelog.ChangeLogPrepare;
|
||||
import com.avaje.ebean.event.changelog.ChangeLogRegister;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditLogger;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import com.avaje.ebean.meta.MetaInfoManager;
|
||||
import com.avaje.ebean.util.ClassUtil;
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
@@ -70,6 +81,11 @@ public class ServerConfig {
|
||||
|
||||
private ContainerConfig containerConfig;
|
||||
|
||||
/**
|
||||
* The underlying properties that were used during configuration.
|
||||
*/
|
||||
private Properties properties;
|
||||
|
||||
/**
|
||||
* The resource directory.
|
||||
*/
|
||||
@@ -120,9 +136,9 @@ public class ServerConfig {
|
||||
private CurrentUserProvider currentUserProvider;
|
||||
|
||||
/**
|
||||
* Config controlling the autofetch behaviour.
|
||||
* Config controlling the AutoTune behaviour.
|
||||
*/
|
||||
private AutofetchConfig autofetchConfig = new AutofetchConfig();
|
||||
private AutoTuneConfig autoTuneConfig = new AutoTuneConfig();
|
||||
|
||||
/**
|
||||
* The JSON format used for DateTime types. Default to millis.
|
||||
@@ -218,13 +234,23 @@ public class ServerConfig {
|
||||
*/
|
||||
private DbMigrationConfig migrationConfig = new DbMigrationConfig();
|
||||
|
||||
/**
|
||||
* The ClassLoadConfig used to detect Joda, Java8, Jackson etc and create plugin instances given a className.
|
||||
*/
|
||||
private ClassLoadConfig classLoadConfig = new ClassLoadConfig();
|
||||
|
||||
/**
|
||||
* Set to true if the DataSource uses autoCommit.
|
||||
* <p>
|
||||
* Indicates that Ebean should use autoCommit friendly Transactions and TransactionManager.
|
||||
*/
|
||||
private boolean autoCommitMode;
|
||||
|
||||
|
||||
/**
|
||||
* Set to true if transaction begin should be started with explicit statement.
|
||||
*/
|
||||
private boolean explicitTransactionBeginMode;
|
||||
|
||||
/**
|
||||
* The data source JNDI name if using a JNDI DataSource.
|
||||
*/
|
||||
@@ -266,9 +292,9 @@ public class ServerConfig {
|
||||
private boolean updatesDeleteMissingChildren = true;
|
||||
|
||||
/**
|
||||
* Setting to indicate if UUID should be stored as binary(16) or varchar(40).
|
||||
* Setting to indicate if UUID should be stored as binary(16) or varchar(40) or native DB type (for H2 and Postgres).
|
||||
*/
|
||||
private boolean uuidStoreAsBinary;
|
||||
private DbUuid dbUuid = DbUuid.AUTO;
|
||||
|
||||
|
||||
private List<BeanFindController> findControllers = new ArrayList<BeanFindController>();
|
||||
@@ -280,6 +306,21 @@ public class ServerConfig {
|
||||
private List<ServerConfigStartup> configStartupListeners = new ArrayList<ServerConfigStartup>();
|
||||
private List<TransactionEventListener> transactionEventListeners = new ArrayList<TransactionEventListener>();
|
||||
|
||||
/**
|
||||
* By default inserts are included in the change log.
|
||||
*/
|
||||
private boolean changeLogIncludeInserts = true;
|
||||
|
||||
private ChangeLogPrepare changeLogPrepare;
|
||||
|
||||
private ChangeLogListener changeLogListener;
|
||||
|
||||
private ChangeLogRegister changeLogRegister;
|
||||
|
||||
private ReadAuditLogger readAuditLogger;
|
||||
|
||||
private ReadAuditPrepare readAuditPrepare;
|
||||
|
||||
private EncryptKeyManager encryptKeyManager;
|
||||
|
||||
private EncryptDeployManager encryptDeployManager;
|
||||
@@ -334,7 +375,6 @@ public class ServerConfig {
|
||||
private int queryCacheMaxIdleTime = 600;
|
||||
private int queryCacheMaxTimeToLive = 60*60*6;
|
||||
private Object objectMapper;
|
||||
private boolean diffFlatMode;
|
||||
|
||||
/**
|
||||
* Set to true if you want eq("someProperty", null) to generate 1=1 rather than "is null" sql expression.
|
||||
@@ -636,6 +676,111 @@ public class ServerConfig {
|
||||
this.databaseSequenceBatchSize = databaseSequenceBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ChangeLogPrepare.
|
||||
* <p>
|
||||
* This is used to set user context information to the ChangeSet in the
|
||||
* foreground thread prior to the logging occurring in a background thread.
|
||||
* </p>
|
||||
*/
|
||||
public ChangeLogPrepare getChangeLogPrepare() {
|
||||
return changeLogPrepare;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ChangeLogPrepare.
|
||||
* <p>
|
||||
* This is used to set user context information to the ChangeSet in the
|
||||
* foreground thread prior to the logging occurring in a background thread.
|
||||
* </p>
|
||||
*/
|
||||
public void setChangeLogPrepare(ChangeLogPrepare changeLogPrepare) {
|
||||
this.changeLogPrepare = changeLogPrepare;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ChangeLogListener which actually performs the logging of change sets
|
||||
* in the background.
|
||||
*/
|
||||
public ChangeLogListener getChangeLogListener() {
|
||||
return changeLogListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ChangeLogListener which actually performs the logging of change sets
|
||||
* in the background.
|
||||
*/
|
||||
public void setChangeLogListener(ChangeLogListener changeLogListener) {
|
||||
this.changeLogListener = changeLogListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ChangeLogRegister which controls which ChangeLogFilter is used for each
|
||||
* bean type and in this way provide fine grained control over which persist requests
|
||||
* are included in the change log.
|
||||
*/
|
||||
public ChangeLogRegister getChangeLogRegister() {
|
||||
return changeLogRegister;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ChangeLogRegister which controls which ChangeLogFilter is used for each
|
||||
* bean type and in this way provide fine grained control over which persist requests
|
||||
* are included in the change log.
|
||||
*/
|
||||
public void setChangeLogRegister(ChangeLogRegister changeLogRegister) {
|
||||
this.changeLogRegister = changeLogRegister;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if inserts should be included in the change log by default.
|
||||
*/
|
||||
public boolean isChangeLogIncludeInserts() {
|
||||
return changeLogIncludeInserts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if inserts should be included in the change log by default.
|
||||
*/
|
||||
public void setChangeLogIncludeInserts(boolean changeLogIncludeInserts) {
|
||||
this.changeLogIncludeInserts = changeLogIncludeInserts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ReadAuditLogger to use.
|
||||
*/
|
||||
public ReadAuditLogger getReadAuditLogger() {
|
||||
return readAuditLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ReadAuditLogger to use. If not set the default implementation is used
|
||||
* which logs the read events in JSON format to a standard named SLF4J logger
|
||||
* (which can be configured in say logback to log to a separate log file).
|
||||
*/
|
||||
public void setReadAuditLogger(ReadAuditLogger readAuditLogger) {
|
||||
this.readAuditLogger = readAuditLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ReadAuditPrepare to use.
|
||||
*/
|
||||
public ReadAuditPrepare getReadAuditPrepare() {
|
||||
return readAuditPrepare;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ReadAuditPrepare to use.
|
||||
* <p>
|
||||
* It is expected that an implementation is used that read user context information
|
||||
* (user id, user ip address etc) and sets it on the ReadEvent bean before it is sent
|
||||
* to the ReadAuditLogger.
|
||||
* </p>
|
||||
*/
|
||||
public void setReadAuditPrepare(ReadAuditPrepare readAuditPrepare) {
|
||||
this.readAuditPrepare = readAuditPrepare;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DB migration configuration.
|
||||
*/
|
||||
@@ -1015,17 +1160,17 @@ public class ServerConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configuration for the Autofetch feature.
|
||||
* Return the configuration for AutoTune.
|
||||
*/
|
||||
public AutofetchConfig getAutofetchConfig() {
|
||||
return autofetchConfig;
|
||||
public AutoTuneConfig getAutoTuneConfig() {
|
||||
return autoTuneConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the configuration for the Autofetch feature.
|
||||
* Set the configuration for AutoTune.
|
||||
*/
|
||||
public void setAutofetchConfig(AutofetchConfig autofetchConfig) {
|
||||
this.autofetchConfig = autofetchConfig;
|
||||
public void setAutoTuneConfig(AutoTuneConfig autoTuneConfig) {
|
||||
this.autoTuneConfig = autoTuneConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1091,6 +1236,28 @@ public class ServerConfig {
|
||||
this.autoCommitMode = autoCommitMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if transaction begin should be started with explicit statement.
|
||||
*/
|
||||
public boolean isExplicitTransactionBeginMode() {
|
||||
return explicitTransactionBeginMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if transaction begin should be started with explicit statement.
|
||||
* <p>
|
||||
* This works for H2 and Postgres but not for Oracle - only use this if you first name
|
||||
* is Daryl or you have explicitly talked to Rob about this feature.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is generally not expected to be turned on but instead allow transactions to start
|
||||
* implicitly which is generally the standard approach.
|
||||
* </p>
|
||||
*/
|
||||
public void setExplicitTransactionBeginMode(boolean explicitTransactionBeginMode) {
|
||||
this.explicitTransactionBeginMode = explicitTransactionBeginMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a value used to represent TRUE in the database.
|
||||
* <p>
|
||||
@@ -1310,19 +1477,18 @@ public class ServerConfig {
|
||||
this.dbEncrypt = dbEncrypt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if UUID should be stored as binary(16) (as opposed to varchar(40)).
|
||||
* Return the DB type used to store UUID.
|
||||
*/
|
||||
public boolean isUuidStoreAsBinary() {
|
||||
return uuidStoreAsBinary;
|
||||
public DbUuid getDbUuid() {
|
||||
return dbUuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if UUID should be stored as binary(16) (as opposed to varchar(40)).
|
||||
* Set the DB type used to store UUID.
|
||||
*/
|
||||
public void setUuidStoreAsBinary(boolean uuidStoreAsBinary) {
|
||||
this.uuidStoreAsBinary = uuidStoreAsBinary;
|
||||
public void setDbUuid(DbUuid dbUuid) {
|
||||
this.dbUuid = dbUuid;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1868,6 +2034,22 @@ public class ServerConfig {
|
||||
this.persistenceContextScope = persistenceContextScope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ClassLoadConfig which is used to detect Joda, Java8 types etc and also
|
||||
* create new instances of plugins given a className.
|
||||
*/
|
||||
public ClassLoadConfig getClassLoadConfig() {
|
||||
return classLoadConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ClassLoadConfig which is used to detect Joda, Java8 types etc and also
|
||||
* create new instances of plugins given a className.
|
||||
*/
|
||||
public void setClassLoadConfig(ClassLoadConfig classLoadConfig) {
|
||||
this.classLoadConfig = classLoadConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings from ebean.properties.
|
||||
*/
|
||||
@@ -1879,16 +2061,59 @@ public class ServerConfig {
|
||||
* Load the settings from the given properties
|
||||
*/
|
||||
public void loadFromProperties(Properties properties) {
|
||||
// keep the properties used for configuration so that these are available for plugins
|
||||
this.properties = properties;
|
||||
PropertiesWrapper p = new PropertiesWrapper("ebean", name, properties);
|
||||
loadSettings(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings from test-ebean.properties and do nothing if the properties is not found.
|
||||
* <p>
|
||||
* This is typically used when test-ebean.properties is put into the test class path and used
|
||||
* to configure Ebean for running tests.
|
||||
* </p>
|
||||
*/
|
||||
public void loadTestProperties() {
|
||||
Properties properties = PropertyMap.testProperties();
|
||||
if (!properties.isEmpty()) {
|
||||
PropertiesWrapper p = new PropertiesWrapper("ebean", name, properties);
|
||||
loadSettings(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the properties that we used for configuration and were set via a call to loadFromProperties().
|
||||
*/
|
||||
public Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the instance to use (can be null) for the given plugin.
|
||||
*
|
||||
* @param properties the properties
|
||||
* @param pluginType the type of plugin
|
||||
* @param key properties key
|
||||
* @param instance existing instance
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T createInstance(PropertiesWrapper p, Class<T> pluginType, String key) {
|
||||
protected <T> T createInstance(PropertiesWrapper properties, Class<T> pluginType, String key, T instance) {
|
||||
|
||||
String classname = p.get(key, null);
|
||||
return classname == null ? null : (T) ClassUtil.newInstance(classname);
|
||||
if (instance != null) {
|
||||
return instance;
|
||||
}
|
||||
String classname = properties.get(key, null);
|
||||
return createInstance(pluginType, classname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the instance to use (can be null) for the given plugin.
|
||||
* @param pluginType the type of plugin
|
||||
* @param classname the implementation class as per properties
|
||||
*/
|
||||
protected <T> T createInstance(Class<T> pluginType, String classname) {
|
||||
return classname == null ? null : (T) classLoadConfig.newInstance(classname);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1905,8 +2130,8 @@ public class ServerConfig {
|
||||
/**
|
||||
* This is broken out for the same reason as above - preserve existing behaviour but let it be overridden.
|
||||
*/
|
||||
protected void loadAutofetchSettings(PropertiesWrapper p) {
|
||||
autofetchConfig.loadSettings(p);
|
||||
protected void loadAutoTuneSettings(PropertiesWrapper p) {
|
||||
autoTuneConfig.loadSettings(p);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1920,28 +2145,29 @@ public class ServerConfig {
|
||||
if (namingConvention != null) {
|
||||
namingConvention.loadFromProperties(p);
|
||||
}
|
||||
if (autofetchConfig == null) {
|
||||
autofetchConfig = new AutofetchConfig();
|
||||
if (autoTuneConfig == null) {
|
||||
autoTuneConfig = new AutoTuneConfig();
|
||||
}
|
||||
loadAutofetchSettings(p);
|
||||
loadAutoTuneSettings(p);
|
||||
|
||||
if (dataSourceConfig == null) {
|
||||
dataSourceConfig = new DataSourceConfig();
|
||||
}
|
||||
loadDataSourceSettings(p);
|
||||
|
||||
explicitTransactionBeginMode = p.getBoolean("explicitTransactionBeginMode", explicitTransactionBeginMode);
|
||||
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
|
||||
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
|
||||
|
||||
currentUserProvider = createInstance(p, CurrentUserProvider.class, "currentUserProvider");
|
||||
disableClasspathSearch = p.getBoolean("disableClasspathSearch", disableClasspathSearch);
|
||||
databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform");
|
||||
encryptKeyManager = createInstance(p, EncryptKeyManager.class, "encryptKeyManager");
|
||||
encryptDeployManager = createInstance(p, EncryptDeployManager.class, "encryptDeployManager");
|
||||
encryptor = createInstance(p, Encryptor.class, "encryptor");
|
||||
dbEncrypt = createInstance(p, DbEncrypt.class, "dbEncrypt");
|
||||
serverCacheFactory = createInstance(p, ServerCacheFactory.class, "serverCacheFactory");
|
||||
serverCacheManager = createInstance(p, ServerCacheManager.class, "serverCacheManager");
|
||||
currentUserProvider = createInstance(p, CurrentUserProvider.class, "currentUserProvider", currentUserProvider);
|
||||
databasePlatform = createInstance(p, DatabasePlatform.class, "databasePlatform", databasePlatform);
|
||||
encryptKeyManager = createInstance(p, EncryptKeyManager.class, "encryptKeyManager", encryptKeyManager);
|
||||
encryptDeployManager = createInstance(p, EncryptDeployManager.class, "encryptDeployManager", encryptDeployManager);
|
||||
encryptor = createInstance(p, Encryptor.class, "encryptor", encryptor);
|
||||
dbEncrypt = createInstance(p, DbEncrypt.class, "dbEncrypt", dbEncrypt);
|
||||
serverCacheFactory = createInstance(p, ServerCacheFactory.class, "serverCacheFactory", serverCacheFactory);
|
||||
serverCacheManager = createInstance(p, ServerCacheManager.class, "serverCacheManager", serverCacheManager);
|
||||
cacheWarmingDelay = p.getInt("cacheWarmingDelay", cacheWarmingDelay);
|
||||
classPathReaderClassName = p.get("classpathreader");
|
||||
|
||||
@@ -1976,8 +2202,9 @@ public class ServerConfig {
|
||||
|
||||
persistenceContextScope = PersistenceContextScope.valueOf(p.get("persistenceContextScope", "TRANSACTION"));
|
||||
|
||||
changeLogIncludeInserts = p.getBoolean("changeLogIncludeInserts", changeLogIncludeInserts);
|
||||
expressionEqualsWithNullAsNoop = p.getBoolean("expressionEqualsWithNullAsNoop", expressionEqualsWithNullAsNoop);
|
||||
diffFlatMode = p.getBoolean("diffFlatMode", diffFlatMode);
|
||||
|
||||
asOfViewSuffix = p.get("asOfViewSuffix", asOfViewSuffix);
|
||||
asOfSysPeriod = p.get("asOfSysPeriod", asOfSysPeriod);
|
||||
historyTableSuffix = p.get("historyTableSuffix", historyTableSuffix);
|
||||
@@ -1986,7 +2213,10 @@ public class ServerConfig {
|
||||
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
|
||||
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
|
||||
databasePlatformName = p.get("databasePlatformName", databasePlatformName);
|
||||
uuidStoreAsBinary = p.getBoolean("uuidStoreAsBinary", uuidStoreAsBinary);
|
||||
dbUuid = p.getEnum(DbUuid.class, "dbuuid", dbUuid);
|
||||
if (p.getBoolean("uuidStoreAsBinary", false)) {
|
||||
dbUuid = DbUuid.BINARY;
|
||||
}
|
||||
localTimeWithNanos = p.getBoolean("localTimeWithNanos", localTimeWithNanos);
|
||||
|
||||
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", lazyLoadBatchSize);
|
||||
@@ -2008,7 +2238,7 @@ public class ServerConfig {
|
||||
|
||||
private NamingConvention createNamingConvention(PropertiesWrapper properties, NamingConvention namingConvention) {
|
||||
|
||||
NamingConvention nc = createInstance(properties, NamingConvention.class, "namingconvention");
|
||||
NamingConvention nc = createInstance(properties, NamingConvention.class, "namingconvention", null);
|
||||
return (nc != null) ? nc : namingConvention;
|
||||
}
|
||||
|
||||
@@ -2090,22 +2320,6 @@ public class ServerConfig {
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if diff should return flat properties with dot notation rather than
|
||||
* embedded beans or reference beans (when the associated bean id is different).
|
||||
*/
|
||||
public boolean isDiffFlatMode() {
|
||||
return diffFlatMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if diff should return flat properties with dot notation rather than
|
||||
* embedded beans or reference beans (when the associated bean id is different).
|
||||
*/
|
||||
public void setDiffFlatMode(boolean diffFlatMode) {
|
||||
this.diffFlatMode = diffFlatMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if eq("someProperty", null) should to generate "1=1" rather than "is null" sql expression.
|
||||
*/
|
||||
@@ -2124,4 +2338,25 @@ public class ServerConfig {
|
||||
public void setExpressionEqualsWithNullAsNoop(boolean expressionEqualsWithNullAsNoop) {
|
||||
this.expressionEqualsWithNullAsNoop = expressionEqualsWithNullAsNoop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify how UUID is stored.
|
||||
*/
|
||||
public enum DbUuid {
|
||||
|
||||
/**
|
||||
* Store using native UUID in H2 and Postgres.
|
||||
*/
|
||||
AUTO,
|
||||
|
||||
/**
|
||||
* Store using DB VARCHAR.
|
||||
*/
|
||||
VARCHAR,
|
||||
|
||||
/**
|
||||
* Store using DB BINARY.
|
||||
*/
|
||||
BINARY
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public class DB2SequenceIdGenerator extends SequenceIdGenerator {
|
||||
*/
|
||||
public DB2SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
|
||||
super(be, ds, seqName, batchSize);
|
||||
this.baseSql = "select nextval for " + seqName;
|
||||
this.baseSql = "values nextval for " + seqName;
|
||||
this.unionBaseSql = " union " + baseSql;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import java.sql.Types;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.Query;
|
||||
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* Database platform specific settings.
|
||||
*/
|
||||
@@ -54,28 +52,33 @@ public class DatabasePlatform {
|
||||
*/
|
||||
protected OnQueryOnly onQueryOnly = OnQueryOnly.ROLLBACK;
|
||||
|
||||
/**
|
||||
* The open quote used by quoted identifiers.
|
||||
/**
|
||||
* The open quote used by quoted identifiers.
|
||||
*/
|
||||
protected String openQuote = "\"";
|
||||
|
||||
/**
|
||||
* The close quote used by quoted identifiers.
|
||||
* The close quote used by quoted identifiers.
|
||||
*/
|
||||
protected String closeQuote = "\"";
|
||||
|
||||
/**
|
||||
* For limit/offset, row_number etc limiting of SQL queries.
|
||||
/**
|
||||
* For limit/offset, row_number etc limiting of SQL queries.
|
||||
*/
|
||||
protected SqlLimiter sqlLimiter = new LimitOffsetSqlLimiter();
|
||||
|
||||
/**
|
||||
* Mapping of JDBC to Database types.
|
||||
/**
|
||||
* Mapping of JDBC to Database types.
|
||||
*/
|
||||
protected DbTypeMap dbTypeMap = new DbTypeMap();
|
||||
|
||||
/**
|
||||
* Defines DB identity/sequence features.
|
||||
* Set to true if the DB has native UUID type support.
|
||||
*/
|
||||
protected boolean nativeUuidType;
|
||||
|
||||
/**
|
||||
* Defines DB identity/sequence features.
|
||||
*/
|
||||
protected DbIdentity dbIdentity = new DbIdentity();
|
||||
|
||||
@@ -84,28 +87,28 @@ public class DatabasePlatform {
|
||||
*/
|
||||
protected DbHistorySupport historySupport;
|
||||
|
||||
/**
|
||||
* The JDBC type to map booleans to (by default).
|
||||
/**
|
||||
* The JDBC type to map booleans to (by default).
|
||||
*/
|
||||
protected int booleanDbType = Types.BOOLEAN;
|
||||
|
||||
/**
|
||||
* The JDBC type to map Blob to.
|
||||
/**
|
||||
* The JDBC type to map Blob to.
|
||||
*/
|
||||
protected int blobDbType = Types.BLOB;
|
||||
|
||||
/**
|
||||
* The JDBC type to map Clob to.
|
||||
/**
|
||||
* The JDBC type to map Clob to.
|
||||
*/
|
||||
protected int clobDbType = Types.CLOB;
|
||||
|
||||
/**
|
||||
* For Oracle treat empty strings as null.
|
||||
/**
|
||||
* For Oracle treat empty strings as null.
|
||||
*/
|
||||
protected boolean treatEmptyStringsAsNull;
|
||||
|
||||
/**
|
||||
* The database platform name.
|
||||
/**
|
||||
* The database platform name.
|
||||
*/
|
||||
protected String name = "generic";
|
||||
|
||||
@@ -124,7 +127,7 @@ public class DatabasePlatform {
|
||||
* The like clause. Can be overridden to disable default escape character.
|
||||
*/
|
||||
protected String likeClause = "like ?";
|
||||
|
||||
|
||||
protected DbEncrypt dbEncrypt;
|
||||
|
||||
protected boolean idInExpandedForm;
|
||||
@@ -219,16 +222,12 @@ public class DatabasePlatform {
|
||||
|
||||
/**
|
||||
* Return a DB Sequence based IdGenerator.
|
||||
*
|
||||
* @param be
|
||||
* the BackgroundExecutor that can be used to load the sequence if
|
||||
* desired
|
||||
* @param ds
|
||||
* the DataSource
|
||||
* @param seqName
|
||||
* the name of the sequence
|
||||
* @param batchSize
|
||||
* the number of sequences that should be loaded
|
||||
*
|
||||
* @param be the BackgroundExecutor that can be used to load the sequence if
|
||||
* desired
|
||||
* @param ds the DataSource
|
||||
* @param seqName the name of the sequence
|
||||
* @param batchSize the number of sequences that should be loaded
|
||||
*/
|
||||
public IdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
|
||||
return null;
|
||||
@@ -276,9 +275,16 @@ public class DatabasePlatform {
|
||||
this.historySupport = historySupport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the DB supports native UUID.
|
||||
*/
|
||||
public boolean isNativeUuidType() {
|
||||
return nativeUuidType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mapping of JDBC to DB types.
|
||||
*
|
||||
*
|
||||
* @return the db type map
|
||||
*/
|
||||
public DbTypeMap getDbTypeMap() {
|
||||
@@ -315,7 +321,7 @@ public class DatabasePlatform {
|
||||
|
||||
/**
|
||||
* Return the close quote for quoted identifiers.
|
||||
*
|
||||
*
|
||||
* @return the close quote
|
||||
*/
|
||||
public String getCloseQuote() {
|
||||
@@ -324,7 +330,7 @@ public class DatabasePlatform {
|
||||
|
||||
/**
|
||||
* Return the open quote for quoted identifiers.
|
||||
*
|
||||
*
|
||||
* @return the open quote
|
||||
*/
|
||||
public String getOpenQuote() {
|
||||
@@ -333,7 +339,7 @@ public class DatabasePlatform {
|
||||
|
||||
/**
|
||||
* Return the JDBC type used to store booleans.
|
||||
*
|
||||
*
|
||||
* @return the boolean db type
|
||||
*/
|
||||
public int getBooleanDbType() {
|
||||
@@ -363,7 +369,7 @@ public class DatabasePlatform {
|
||||
|
||||
/**
|
||||
* Return true if empty strings should be treated as null.
|
||||
*
|
||||
*
|
||||
* @return true, if checks if is treat empty strings as null
|
||||
*/
|
||||
public boolean isTreatEmptyStringsAsNull() {
|
||||
@@ -399,7 +405,7 @@ public class DatabasePlatform {
|
||||
|
||||
/**
|
||||
* Return the DB identity/sequence features for this platform.
|
||||
*
|
||||
*
|
||||
* @return the db identity
|
||||
*/
|
||||
public DbIdentity getDbIdentity() {
|
||||
@@ -412,7 +418,7 @@ public class DatabasePlatform {
|
||||
* <p>
|
||||
* Basically add the clauses for limit/offset, rownum, row_number().
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return the sql limiter
|
||||
*/
|
||||
public SqlLimiter getSqlLimiter() {
|
||||
@@ -421,15 +427,12 @@ public class DatabasePlatform {
|
||||
|
||||
/**
|
||||
* Convert backticks to the platform specific open quote and close quote
|
||||
*
|
||||
* <p>
|
||||
* Specific plugins may implement this method to cater for platform specific
|
||||
* naming rules.
|
||||
* </p>
|
||||
*
|
||||
* @param dbName
|
||||
* the db name
|
||||
*
|
||||
*
|
||||
* @param dbName the db name
|
||||
* @return the string
|
||||
*/
|
||||
public String convertQuotedIdentifiers(String dbName) {
|
||||
|
||||
@@ -5,14 +5,37 @@ package com.avaje.ebean.config.dbplatform;
|
||||
*/
|
||||
public interface DbHistorySupport {
|
||||
|
||||
/**
|
||||
* Return true if the 'As of' predicate is part of the from clause
|
||||
* (more standard sql2011). So true for Oracle total recall and false
|
||||
* for Postgres and MySql (where we use views and history tables).
|
||||
*/
|
||||
boolean isBindWithFromClause();
|
||||
|
||||
/**
|
||||
* Return the number of columns bound in a 'As Of' predicate.
|
||||
* <p>
|
||||
* Typically this is 2 but 1 for postgres using it's range type.
|
||||
* This is 1 for more standard sql2011 style and Postgres which has the
|
||||
* special range type and 2 for view based solutions with 2 columns such as
|
||||
* MySql.
|
||||
* </p>
|
||||
*/
|
||||
int getBindCount();
|
||||
|
||||
/**
|
||||
* For sql2011 style this ignores the passed in view suffix and returns something
|
||||
* like the ' as of timestamp ?' clause to be appended after the base table name.
|
||||
*
|
||||
* @param asOfViewSuffix the configured view suffix (typically "_with_history").
|
||||
* @return The suffix appended after the base table name in the from and join clauses.
|
||||
*/
|
||||
String getAsOfViewSuffix(String asOfViewSuffix);
|
||||
|
||||
/**
|
||||
* Return the 'versions between timestamp' suffix.
|
||||
*/
|
||||
String getVersionsBetweenSuffix(String asOfViewSuffix);
|
||||
|
||||
/**
|
||||
* Return the 'as of' predicate added for the given table alias.
|
||||
*
|
||||
@@ -26,7 +49,7 @@ public interface DbHistorySupport {
|
||||
* Return the column for the system period lower bound that will be included in findVersions() queries.
|
||||
*
|
||||
* @param tableAlias the table alias which will typically be 't0'
|
||||
* @param sysPeriod the name of the sys_period column
|
||||
* @param sysPeriod the name of the sys_period column
|
||||
*/
|
||||
String getSysPeriodLower(String tableAlias, String sysPeriod);
|
||||
|
||||
@@ -34,7 +57,7 @@ public interface DbHistorySupport {
|
||||
* Return the column for the system period upper bound that will be included in findVersions() queries.
|
||||
*
|
||||
* @param tableAlias the table alias which will typically be 't0'
|
||||
* @param sysPeriod the name of the sys_period column
|
||||
* @param sysPeriod the name of the sys_period column
|
||||
*/
|
||||
String getSysPeriodUpper(String tableAlias, String sysPeriod);
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
/**
|
||||
* SQL2011 based history support using 'as of timestamp' type clause appended as part of the the from or join clause.
|
||||
*/
|
||||
public abstract class DbStandardHistorySupport implements DbHistorySupport {
|
||||
|
||||
/**
|
||||
* Return true as with sql2011 the 'as of timestamp' clause included in from or join clause.
|
||||
*/
|
||||
@Override
|
||||
public boolean isBindWithFromClause() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return 1 as the bind count (not 2 for effective start and effective end columns).
|
||||
*/
|
||||
@Override
|
||||
public int getBindCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return null - not used for sql2011 based history.
|
||||
*/
|
||||
@Override
|
||||
public String getAsOfPredicate(String tableAlias, String sysPeriod) {
|
||||
// not used for sql2011 based history
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,11 @@ package com.avaje.ebean.config.dbplatform;
|
||||
*/
|
||||
public class DbType {
|
||||
|
||||
/**
|
||||
* DB native UUID type (H2 and Postgres).
|
||||
*/
|
||||
public static final int UUID = 5010;
|
||||
|
||||
/**
|
||||
* Type to map Map content to Postgres HSTORE.
|
||||
*/
|
||||
|
||||
@@ -49,6 +49,8 @@ public class DbTypeMap {
|
||||
lookup.put("TIME", Types.TIME);
|
||||
lookup.put("TIMESTAMP", Types.TIMESTAMP);
|
||||
|
||||
lookup.put("UUID", DbType.UUID);
|
||||
|
||||
// Not standard java.sql.Types
|
||||
// logical JSON storage types
|
||||
lookup.put("JSON", DbType.JSON);
|
||||
@@ -101,8 +103,12 @@ public class DbTypeMap {
|
||||
put(Types.BLOB, new DbType("blob"));
|
||||
put(Types.CLOB, new DbType("clob"));
|
||||
|
||||
// DB native UUID support (H2 and Postgres)
|
||||
put(DbType.UUID, new DbType("uuid"));
|
||||
|
||||
if (logicalTypes) {
|
||||
// keep it logical for 2 layer DDL generation
|
||||
put(DbType.HSTORE, new DbType("hstore"));
|
||||
put(DbType.JSON, new DbType("json"));
|
||||
put(DbType.JSONB, new DbType("jsonb"));
|
||||
put(DbType.JSONClob, new DbType("jsonclob"));
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
/**
|
||||
* Database view based implementation of DbHistorySupport.
|
||||
* <p>
|
||||
* These implementations have explicit history tables, a view to union the
|
||||
* base table and history table and triggers to maintain the history.
|
||||
* </p>
|
||||
*/
|
||||
public abstract class DbViewHistorySupport implements DbHistorySupport {
|
||||
|
||||
/**
|
||||
* Return false for view based implementations where we append extra 'as of' predicates to the end.
|
||||
*/
|
||||
@Override
|
||||
public boolean isBindWithFromClause() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured view suffix.
|
||||
*
|
||||
* @param asOfViewSuffix the configured view suffix (typically "_with_history").
|
||||
*/
|
||||
@Override
|
||||
public String getAsOfViewSuffix(String asOfViewSuffix) {
|
||||
// just return the configured suffix
|
||||
return asOfViewSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured view suffix (same as getAsOfViewSuffix()).
|
||||
*
|
||||
* @param asOfViewSuffix the configured view suffix (typically "_with_history").
|
||||
*/
|
||||
@Override
|
||||
public String getVersionsBetweenSuffix(String asOfViewSuffix) {
|
||||
// just return the configured asOfViewSuffix (using the view for versions between query)
|
||||
return asOfViewSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return 2 if we have effective start and effective end as 2 columns.
|
||||
* Note that for postgres we can use a single range type so that returns 1.
|
||||
*/
|
||||
@Override
|
||||
public int getBindCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the 'as of' predicate clause appended to the end of the normal query predicates.
|
||||
*/
|
||||
@Override
|
||||
public String getAsOfPredicate(String asOfTableAlias, String asOfSysPeriod) {
|
||||
|
||||
// (sys_period_start < ? and (sys_period_end is null or sys_period_end > ?));
|
||||
return "(" + asOfTableAlias + "." + asOfSysPeriod + "_start" + " <= ? and (" + asOfTableAlias + "." + asOfSysPeriod + "_end" + " is null or " + asOfTableAlias + "." + asOfSysPeriod + "_end" + " > ?))";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the lower bound column prepended with the table alias.
|
||||
*
|
||||
* @param tableAlias the table alias
|
||||
* @param sysPeriod the name of the sys_period column
|
||||
*/
|
||||
@Override
|
||||
public String getSysPeriodLower(String tableAlias, String sysPeriod) {
|
||||
return tableAlias + "." + sysPeriod + "_start";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the upper bound column prepended with the table alias.
|
||||
*
|
||||
* @param tableAlias the table alias
|
||||
* @param sysPeriod the name of the sys_period column
|
||||
*/
|
||||
@Override
|
||||
public String getSysPeriodUpper(String tableAlias, String sysPeriod) {
|
||||
return tableAlias + "." + sysPeriod + "_end";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
/**
|
||||
* Runtime support for @History with H2.
|
||||
*/
|
||||
public class H2HistorySupport extends DbViewHistorySupport {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import org.h2.api.Trigger;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* H2 database trigger used to populate history tables to support the @History feature.
|
||||
*/
|
||||
public class H2HistoryTrigger implements Trigger {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(H2HistoryTrigger.class);
|
||||
|
||||
/**
|
||||
* Hardcoding the column and history table suffix for now. Not sure how to get that
|
||||
* configuration into the trigger instance nicely as it is instantiated by H2.
|
||||
*/
|
||||
private static final String SYS_PERIOD_START = "SYS_PERIOD_START";
|
||||
private static final String SYS_PERIOD_END = "SYS_PERIOD_END";
|
||||
private static final String HISTORY_SUFFIX = "_history";
|
||||
|
||||
/**
|
||||
* SQL to insert into the history table.
|
||||
*/
|
||||
private String insertHistorySql;
|
||||
|
||||
/**
|
||||
* Position of SYS_PERIOD_START column in the Object[].
|
||||
*/
|
||||
private int effectStartPosition;
|
||||
|
||||
/**
|
||||
* Position of SYS_PERIOD_END column in the Object[].
|
||||
*/
|
||||
private int effectEndPosition;
|
||||
|
||||
@Override
|
||||
public void init(Connection conn, String schemaName, String triggerName, String tableName, boolean before, int type) throws SQLException {
|
||||
|
||||
// get the columns for the table
|
||||
ResultSet rs = conn.getMetaData().getColumns(null, schemaName, tableName, null);
|
||||
|
||||
// build the insert into history table SQL
|
||||
StringBuilder insertSql = new StringBuilder(150);
|
||||
insertSql.append("insert into ").append(tableName).append(HISTORY_SUFFIX).append(" (");
|
||||
|
||||
int count = 0;
|
||||
List<String> columns = new ArrayList<String>();
|
||||
while (rs.next()) {
|
||||
if (++count > 1) {
|
||||
insertSql.append(",");
|
||||
}
|
||||
String columnName = rs.getString("COLUMN_NAME");
|
||||
if (columnName.equalsIgnoreCase(SYS_PERIOD_START)) {
|
||||
this.effectStartPosition = count - 1;
|
||||
} else if (columnName.equalsIgnoreCase(SYS_PERIOD_END)) {
|
||||
this.effectEndPosition = count - 1;
|
||||
}
|
||||
insertSql.append(columnName);
|
||||
columns.add(columnName);
|
||||
}
|
||||
insertSql.append(") values (");
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (i > 0) {
|
||||
insertSql.append(",");
|
||||
}
|
||||
insertSql.append("?");
|
||||
}
|
||||
insertSql.append(");");
|
||||
|
||||
this.insertHistorySql = insertSql.toString();
|
||||
logger.debug("History table insert sql: {}", insertHistorySql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fire(Connection connection, Object[] oldRow, Object[] newRow) throws SQLException {
|
||||
|
||||
if (oldRow != null) {
|
||||
// a delete or update event
|
||||
Timestamp now = new Timestamp(System.currentTimeMillis());
|
||||
oldRow[effectEndPosition] = now;
|
||||
if (newRow != null) {
|
||||
// update event. Set the effective start timestamp to now.
|
||||
newRow[effectStartPosition] = now;
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("History insert: {}", Arrays.toString(oldRow));
|
||||
}
|
||||
insertIntoHistory(connection, oldRow);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the data into the history table.
|
||||
*/
|
||||
private void insertIntoHistory(Connection connection, Object[] oldRow) throws SQLException {
|
||||
|
||||
PreparedStatement stmt = connection.prepareStatement(insertHistorySql);
|
||||
try {
|
||||
for (int i = 0; i < oldRow.length; i++) {
|
||||
stmt.setObject(i + 1, oldRow[i]);
|
||||
}
|
||||
stmt.executeUpdate();
|
||||
} finally {
|
||||
stmt.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SQLException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() throws SQLException {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@ public class H2Platform extends DatabasePlatform {
|
||||
this.name = "h2";
|
||||
this.dbEncrypt = new H2DbEncrypt();
|
||||
this.platformDdl = new H2Ddl(this.dbTypeMap, dbIdentity);
|
||||
this.historySupport = new H2HistorySupport();
|
||||
this.nativeUuidType = true;
|
||||
|
||||
// only support getGeneratedKeys with non-batch JDBC
|
||||
// so generally use SEQUENCE instead of IDENTITY for H2
|
||||
|
||||
@@ -3,33 +3,6 @@ package com.avaje.ebean.config.dbplatform;
|
||||
/**
|
||||
* Runtime support for @History with MySql.
|
||||
*/
|
||||
public class MySqlHistorySupport implements DbHistorySupport {
|
||||
public class MySqlHistorySupport extends DbViewHistorySupport {
|
||||
|
||||
@Override
|
||||
public int getBindCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsOfPredicate(String asOfTableAlias, String asOfSysPeriod) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(90);
|
||||
sb.append("(");
|
||||
sb.append(asOfTableAlias).append(".").append(asOfSysPeriod).append("_start").append(" < ? and (");
|
||||
sb.append(asOfTableAlias).append(".").append(asOfSysPeriod).append("_end").append(" is null or ");
|
||||
sb.append(asOfTableAlias).append(".").append(asOfSysPeriod).append("_end").append(" > ?))");
|
||||
|
||||
// (sys_period_start < ? and (sys_period_end is null or sys_period_end > ?));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSysPeriodLower(String tableAlias, String sysPeriod) {
|
||||
return tableAlias+"."+sysPeriod+"_start";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSysPeriodUpper(String tableAlias, String sysPeriod) {
|
||||
return tableAlias+"."+sysPeriod+"_end";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
/**
|
||||
* Since Ebean v2.2.0 Oracle9 is no different from Oracle10.
|
||||
* <p>
|
||||
* This will be removed in the future.
|
||||
* </p>
|
||||
*/
|
||||
public class Oracle9Platform extends Oracle10Platform {
|
||||
|
||||
public Oracle9Platform() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -43,12 +43,12 @@ package com.avaje.ebean.config.dbplatform;
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class Oracle10DbEncrypt extends AbstractDbEncrypt {
|
||||
public class OracleDbEncrypt extends AbstractDbEncrypt {
|
||||
|
||||
/**
|
||||
* Constructs the Oracle10DbEncrypt with default encrypt and decrypt stored procedures.
|
||||
*/
|
||||
public Oracle10DbEncrypt() {
|
||||
public OracleDbEncrypt() {
|
||||
this("eb_encrypt", "eb_decrypt");
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public class Oracle10DbEncrypt extends AbstractDbEncrypt {
|
||||
* @param encryptFunction the encrypt stored procedure
|
||||
* @param decryptFunction the decrypt stored procedure
|
||||
*/
|
||||
public Oracle10DbEncrypt(String encryptFunction, String decryptFunction) {
|
||||
public OracleDbEncrypt(String encryptFunction, String decryptFunction) {
|
||||
|
||||
this.varcharEncryptFunction = new OraVarcharFunction(encryptFunction, decryptFunction);
|
||||
this.dateEncryptFunction = new OraDateFunction(encryptFunction, decryptFunction);
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
/**
|
||||
* Oracle Total recall based history support.
|
||||
*/
|
||||
public class OracleDbHistorySupport extends DbStandardHistorySupport {
|
||||
|
||||
/**
|
||||
* Return the ' as of timestamp ?' clause appended after the table name.
|
||||
*/
|
||||
@Override
|
||||
public String getAsOfViewSuffix(String asOfViewSuffix) {
|
||||
return " as of TIMESTAMP ?";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersionsBetweenSuffix(String asOfViewSuffix) {
|
||||
return " versions between timestamp ? and ?";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Oracle specific effective start column.
|
||||
*/
|
||||
@Override
|
||||
public String getSysPeriodLower(String tableAlias, String sysPeriod) {
|
||||
return "versions_starttime";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Oracle specific effective end column.
|
||||
*/
|
||||
@Override
|
||||
public String getSysPeriodUpper(String tableAlias, String sysPeriod) {
|
||||
return "versions_endtime";
|
||||
}
|
||||
|
||||
}
|
||||
+4
-3
@@ -9,18 +9,19 @@ import java.sql.Types;
|
||||
/**
|
||||
* Oracle10 and greater specific platform.
|
||||
*/
|
||||
public class Oracle10Platform extends DatabasePlatform {
|
||||
public class OraclePlatform extends DatabasePlatform {
|
||||
|
||||
public Oracle10Platform() {
|
||||
public OraclePlatform() {
|
||||
super();
|
||||
this.name = "oracle";
|
||||
this.maxTableNameLength = 30;
|
||||
this.maxConstraintNameLength = 30;
|
||||
// OnQueryOnly.CLOSE as a performance optimisation on Oracle
|
||||
this.onQueryOnly = OnQueryOnly.CLOSE;
|
||||
this.dbEncrypt = new Oracle10DbEncrypt();
|
||||
this.dbEncrypt = new OracleDbEncrypt();
|
||||
this.sqlLimiter = new RownumSqlLimiter();
|
||||
this.platformDdl = new Oracle10Ddl(this.dbTypeMap, this.dbIdentity);
|
||||
this.historySupport = new OracleDbHistorySupport();
|
||||
|
||||
// Not using getGeneratedKeys as instead we will
|
||||
// batch load sequences which enables JDBC batch execution
|
||||
@@ -13,6 +13,7 @@ public class Postgres8Platform extends PostgresPlatform {
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(false);
|
||||
this.dbIdentity.setIdType(IdType.SEQUENCE);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
this.dbIdentity.setSupportsIdentity(true);
|
||||
this.columnAliasPrefix = "as c";
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ package com.avaje.ebean.config.dbplatform;
|
||||
/**
|
||||
* Postgres support for history features.
|
||||
*/
|
||||
public class PostgresHistorySupport implements DbHistorySupport {
|
||||
public class PostgresHistorySupport extends DbViewHistorySupport {
|
||||
|
||||
/**
|
||||
* Return 1 as we are using the range type and hence don't need 2 bind variables.
|
||||
* Return 1 as we are using the postgres range type and hence don't need 2 bind variables.
|
||||
*/
|
||||
@Override
|
||||
public int getBindCount() {
|
||||
@@ -24,19 +24,16 @@ public class PostgresHistorySupport implements DbHistorySupport {
|
||||
|
||||
// for Postgres we are using the 'timestamp with timezone range' data type
|
||||
// as our sys_period column so hence the predicate below
|
||||
//noinspection StringBufferReplaceableByString
|
||||
StringBuilder sb = new StringBuilder(40);
|
||||
sb.append(asOfTableAlias).append(".").append(asOfSysPeriod).append(" @> ?::timestamptz");
|
||||
return sb.toString();
|
||||
return asOfTableAlias + "." + asOfSysPeriod + " @> ?::timestamptz";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSysPeriodLower(String tableAlias, String sysPeriod) {
|
||||
return "lower("+tableAlias+"."+sysPeriod+")";
|
||||
return "lower(" + tableAlias + "." + sysPeriod + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSysPeriodUpper(String tableAlias, String sysPeriod) {
|
||||
return "upper("+tableAlias+"."+sysPeriod+")";
|
||||
return "upper(" + tableAlias + "." + sysPeriod + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,10 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
// OnQueryOnly.CLOSE as a performance optimisation on Postgres
|
||||
this.onQueryOnly = OnQueryOnly.CLOSE;
|
||||
this.likeClause = "like ? escape''";
|
||||
|
||||
this.selectCountWithAlias = true;
|
||||
this.blobDbType = Types.LONGVARBINARY;
|
||||
this.clobDbType = Types.VARCHAR;
|
||||
this.nativeUuidType = true;
|
||||
|
||||
this.dbEncrypt = new PostgresDbEncrypt();
|
||||
this.historySupport = new PostgresHistorySupport();
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.avaje.ebean.config.dbplatform.DbPlatformName;
|
||||
import com.avaje.ebean.config.dbplatform.H2Platform;
|
||||
import com.avaje.ebean.config.dbplatform.MsSqlServer2005Platform;
|
||||
import com.avaje.ebean.config.dbplatform.MySqlPlatform;
|
||||
import com.avaje.ebean.config.dbplatform.Oracle10Platform;
|
||||
import com.avaje.ebean.config.dbplatform.OraclePlatform;
|
||||
import com.avaje.ebean.config.dbplatform.PostgresPlatform;
|
||||
import com.avaje.ebean.config.dbplatform.SQLitePlatform;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
|
||||
@@ -33,7 +33,25 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Generates DB Migration xml and sql scripts.
|
||||
* <p>
|
||||
* Reads the prior migrations and compares with the current model of the EbeanServer
|
||||
* and generates a migration 'diff' in the form of xml document with the logical schema
|
||||
* changes and a series of sql scripts to apply, rollback the applied changes if necessary
|
||||
* and drop objects (drop tables, drop columns).
|
||||
* </p>
|
||||
* <p>
|
||||
* This does not run the migration or ddl scripts but just generates them.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* DbMigration migration = new DbMigration();
|
||||
* migration.setPathToResources("src/main/resources");
|
||||
* migration.setPlatform(DbPlatformName.ORACLE);
|
||||
*
|
||||
* migration.generateMigration();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public class DbMigration {
|
||||
|
||||
@@ -54,18 +72,29 @@ public class DbMigration {
|
||||
protected DbConstraintNaming constraintNaming;
|
||||
|
||||
public DbMigration() {
|
||||
DbOffline.asH2();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the path from the current working directory to the application resources.
|
||||
*
|
||||
* This defaults to maven style 'src/main/resources'.
|
||||
*/
|
||||
public void setPathToResources(String pathToResources) {
|
||||
this.pathToResources = pathToResources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the server to use to determine the current model.
|
||||
* Typically this is not called explicitly.
|
||||
*/
|
||||
public void setServer(EbeanServer ebeanServer) {
|
||||
this.server = (SpiEbeanServer) ebeanServer;
|
||||
setServerConfig(server.getServerConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the serverConfig to use. Typically this is not called explicitly.
|
||||
*/
|
||||
public void setServerConfig(ServerConfig config) {
|
||||
if (this.serverConfig == null) {
|
||||
this.serverConfig = config;
|
||||
@@ -78,10 +107,22 @@ public class DbMigration {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the specific platform to generate DDL for.
|
||||
* <p>
|
||||
* If not set this defaults to the platform of the default server.
|
||||
* </p>
|
||||
*/
|
||||
public void setPlatform(DbPlatformName platform) {
|
||||
setPlatform(getPlatform(platform));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the specific platform to generate DDL for.
|
||||
* <p>
|
||||
* If not set this defaults to the platform of the default server.
|
||||
* </p>
|
||||
*/
|
||||
public void setPlatform(DatabasePlatform databasePlatform) {
|
||||
this.databasePlatform = databasePlatform;
|
||||
DbOffline.setPlatform(databasePlatform.getName());
|
||||
@@ -89,16 +130,49 @@ public class DbMigration {
|
||||
|
||||
/**
|
||||
* Add an additional platform to write the migration DDL.
|
||||
* <p>
|
||||
* Use this when you want to generate sql scripts for multiple database platforms
|
||||
* from the migration (e.g. generate migration sql for MySql, Postgres and Oracle).
|
||||
* </p>
|
||||
*/
|
||||
public void addPlatform(DbPlatformName platform, String prefix) {
|
||||
if (!prefix.endsWith("-")) {
|
||||
prefix+="-";
|
||||
prefix += "-";
|
||||
}
|
||||
platforms.add(new Pair(getPlatform(platform), prefix));
|
||||
}
|
||||
|
||||
|
||||
public void runMigration() throws IOException {
|
||||
/**
|
||||
* Generate the next migration xml file and associated apply and rollback sql scripts.
|
||||
* <p>
|
||||
* This does not run the migration or ddl scripts but just generates them.
|
||||
* </p>
|
||||
* <h3>Example: Run for a single specific platform</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* DbMigration migration = new DbMigration();
|
||||
* migration.setPathToResources("src/main/resources");
|
||||
* migration.setPlatform(DbPlatformName.ORACLE);
|
||||
*
|
||||
* migration.generateMigration();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Example: Run migration generating DDL for multiple platforms</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* DbMigration migration = new DbMigration();
|
||||
* migration.setPathToResources("src/main/resources");
|
||||
*
|
||||
* migration.addPlatform(DbPlatformName.POSTGRES, "pg");
|
||||
* migration.addPlatform(DbPlatformName.MYSQL, "mysql");
|
||||
* migration.addPlatform(DbPlatformName.ORACLE, "mysql");
|
||||
*
|
||||
* migration.generateMigration();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public void generateMigration() throws IOException {
|
||||
|
||||
// use this flag to stop other plugins like full DDL generation
|
||||
DbOffline.setRunningMigration();
|
||||
@@ -158,19 +232,34 @@ public class DbMigration {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the migration xml.
|
||||
*/
|
||||
protected void writeMigrationXml(Migration dbMigration, File resourcePath, int migrationVersion) {
|
||||
File file = new File(resourcePath, "v"+migrationVersion+".0.xml");
|
||||
|
||||
File file = new File(resourcePath, "v"+migrationVersion+".0.xml");
|
||||
MigrationXmlWriter xmlWriter = new MigrationXmlWriter();
|
||||
xmlWriter.write(dbMigration, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default server and platform if necessary.
|
||||
*/
|
||||
protected void setDefaults() {
|
||||
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
|
||||
databasePlatform = server.getDatabasePlatform();
|
||||
logger.debug("set platform to {}", databasePlatform.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file path to write the xml and sql to.
|
||||
*/
|
||||
protected File getWritePath() {
|
||||
|
||||
// path to src/main/resources in typical maven project
|
||||
@@ -188,6 +277,9 @@ public class DbMigration {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DatabasePlatform given the platform key.
|
||||
*/
|
||||
protected DatabasePlatform getPlatform(DbPlatformName platform) {
|
||||
switch (platform) {
|
||||
case H2:
|
||||
@@ -197,7 +289,7 @@ public class DbMigration {
|
||||
case MYSQL:
|
||||
return new MySqlPlatform();
|
||||
case ORACLE:
|
||||
return new Oracle10Platform();
|
||||
return new OraclePlatform();
|
||||
case SQLSERVER:
|
||||
return new MsSqlServer2005Platform();
|
||||
case DB2:
|
||||
|
||||
@@ -5,7 +5,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* Helper to indicate that an EbeanServer should come up offline
|
||||
* typically for DDL generation purposes.
|
||||
*/
|
||||
public class DbOffline {
|
||||
|
||||
@@ -15,28 +16,43 @@ public class DbOffline {
|
||||
|
||||
private static boolean runningMigration;
|
||||
|
||||
/**
|
||||
* Set the platform to use when creating the next EbeanServer instance.
|
||||
*/
|
||||
public static void setPlatform(DbPlatformName dbPlatform) {
|
||||
System.setProperty(KEY, dbPlatform.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the platform to use when creating the next EbeanServer instance.
|
||||
*/
|
||||
public static void setPlatform(String platformName) {
|
||||
System.setProperty(KEY, platformName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the platform to use when creating the next EbeanServer instance.
|
||||
*/
|
||||
public static String getPlatform() {
|
||||
return System.getProperty(KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bring up the next EbeanServer instance using the H2 platform.
|
||||
*/
|
||||
public static void asH2() {
|
||||
setPlatform(DbPlatformName.H2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the offline platform has been set.
|
||||
*/
|
||||
public static boolean isSet() {
|
||||
return getPlatform() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the migration is runing. This typically means don't run the
|
||||
* Return true if the migration is running. This typically means don't run the
|
||||
* plugins like full DDL generation.
|
||||
*/
|
||||
public static boolean isRunningMigration() {
|
||||
|
||||
@@ -263,6 +263,74 @@ public class DdlGenerator implements SpiEbeanPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Local utility used to detect the end of statements / separate statements.
|
||||
* This is often just the semicolon character but for trigger/procedures this
|
||||
* detects the $$ demarcation used in the history DDL generation for MySql and
|
||||
* Postgres.
|
||||
*/
|
||||
static class StatementsSeparator {
|
||||
|
||||
ArrayList<String> statements = new ArrayList<String>();
|
||||
|
||||
boolean trimDelimiter;
|
||||
|
||||
boolean inDbProcedure;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
void lineContainsDollars(String line) {
|
||||
if (inDbProcedure) {
|
||||
if (trimDelimiter) {
|
||||
line = line.replace("$$","");
|
||||
}
|
||||
endOfStatement(line);
|
||||
} else {
|
||||
// MySql style delimiter needs to be trimmed/removed
|
||||
trimDelimiter = line.equals("delimiter $$");
|
||||
if (!trimDelimiter) {
|
||||
sb.append(line).append(" ");
|
||||
}
|
||||
}
|
||||
inDbProcedure = !inDbProcedure;
|
||||
}
|
||||
|
||||
void endOfStatement(String line) {
|
||||
// end of Db procedure
|
||||
sb.append(line);
|
||||
statements.add(sb.toString().trim());
|
||||
sb = new StringBuilder();
|
||||
}
|
||||
|
||||
void nextLine(String line) {
|
||||
|
||||
if (line.contains("$$")) {
|
||||
lineContainsDollars(line);
|
||||
return;
|
||||
}
|
||||
|
||||
if (inDbProcedure) {
|
||||
sb.append(line).append(" ");
|
||||
return;
|
||||
}
|
||||
|
||||
int semiPos = line.indexOf(';');
|
||||
if (semiPos == -1) {
|
||||
sb.append(line).append(" ");
|
||||
|
||||
} else if (semiPos == line.length() - 1) {
|
||||
// semicolon at end of line
|
||||
endOfStatement(line);
|
||||
|
||||
} else {
|
||||
// semicolon in middle of line
|
||||
String preSemi = line.substring(0, semiPos);
|
||||
endOfStatement(preSemi);
|
||||
sb.append(line.substring(semiPos + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Break up the sql in reader into a list of statements using the semi-colon
|
||||
* character;
|
||||
@@ -271,35 +339,16 @@ public class DdlGenerator implements SpiEbeanPlugin {
|
||||
|
||||
try {
|
||||
BufferedReader br = new BufferedReader(reader);
|
||||
StatementsSeparator statements = new StatementsSeparator();
|
||||
|
||||
ArrayList<String> statements = new ArrayList<String>();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String s;
|
||||
while ((s = br.readLine()) != null) {
|
||||
s = s.trim();
|
||||
int semiPos = s.indexOf(';');
|
||||
if (semiPos == -1) {
|
||||
sb.append(s).append(" ");
|
||||
|
||||
} else if (semiPos == s.length() - 1) {
|
||||
// semicolon at end of line
|
||||
sb.append(s);
|
||||
statements.add(sb.toString().trim());
|
||||
sb = new StringBuilder();
|
||||
|
||||
} else {
|
||||
// semicolon in middle of line
|
||||
String preSemi = s.substring(0, semiPos);
|
||||
sb.append(preSemi);
|
||||
statements.add(sb.toString().trim());
|
||||
sb = new StringBuilder();
|
||||
sb.append(s.substring(semiPos + 1));
|
||||
|
||||
}
|
||||
statements.nextLine(s);
|
||||
}
|
||||
|
||||
return statements;
|
||||
return statements.statements;
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ public class BaseDdlBuffer implements DdlBuffer {
|
||||
append(" ");
|
||||
}
|
||||
}
|
||||
append(" ");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+18
-16
@@ -98,7 +98,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
reset();
|
||||
|
||||
String tableName = lowerName(createTable.getName());
|
||||
String tableName = lowerTableName(createTable.getName());
|
||||
List<Column> columns = createTable.getColumn();
|
||||
List<Column> pk = determinePrimaryKeyColumns(columns);
|
||||
|
||||
@@ -248,7 +248,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void writeForeignKey(DdlWrite write, String fkName, String tableName, String[] columns, String refTable, String[] refColumns, String indexName) throws IOException {
|
||||
|
||||
tableName = lowerName(tableName);
|
||||
tableName = lowerTableName(tableName);
|
||||
DdlBuffer fkeyBuffer = write.applyForeignKeys();
|
||||
alterTableAddForeignKey(fkeyBuffer, fkName, tableName, columns, refTable, refColumns);
|
||||
|
||||
@@ -282,7 +282,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
if (i > 0) {
|
||||
buffer.append(",");
|
||||
}
|
||||
buffer.append(lowerName(columns[i].trim()));
|
||||
buffer.append(lowerColumnName(columns[i].trim()));
|
||||
}
|
||||
buffer.append(")");
|
||||
}
|
||||
@@ -324,14 +324,13 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void writeCompoundUniqueConstraints(DdlBuffer apply, CreateTable createTable) throws IOException {
|
||||
|
||||
String tableName = createTable.getName();
|
||||
|
||||
List<UniqueConstraint> uniqueConstraints = createTable.getUniqueConstraint();
|
||||
for (UniqueConstraint uniqueConstraint : uniqueConstraints) {
|
||||
String uqName = uniqueConstraint.getName();
|
||||
String[] columns = toColumnNamesSplit(uniqueConstraint.getColumnNames());
|
||||
apply
|
||||
.append(platformDdl.alterTableAddUniqueConstraint(tableName, uniqueConstraint.getName(), columns))
|
||||
.endOfStatement();
|
||||
apply.append(",").newLine();
|
||||
apply.append(" constraint ").append(uqName).append(" unique");
|
||||
appendColumns(columns, apply);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +367,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
buffer.append(",").newLine();
|
||||
buffer.append(" constraint ").append(uqName).append(" unique ");
|
||||
buffer.append("(");
|
||||
buffer.append(lowerName(column.getName()));
|
||||
buffer.append(lowerColumnName(column.getName()));
|
||||
buffer.append(")");
|
||||
}
|
||||
|
||||
@@ -402,14 +401,17 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the table or column name to lower case.
|
||||
* <p>
|
||||
* This is passed up to the platformDdl to override as desired.
|
||||
* Generally lower case with underscore is a good cross database
|
||||
* choice for column/table names.
|
||||
* Convert the table lower case.
|
||||
*/
|
||||
protected String lowerName(String name) {
|
||||
return naming.lowerName(name);
|
||||
protected String lowerTableName(String name) {
|
||||
return naming.lowerTableName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the column name to lower case.
|
||||
*/
|
||||
protected String lowerColumnName(String name) {
|
||||
return naming.lowerColumnName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -176,7 +176,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
|
||||
String platformType = platformDdl.convert(type, false);
|
||||
buffer.append(" ");
|
||||
buffer.append(platformDdl.lowerName(columnName), 30);
|
||||
buffer.append(platformDdl.lowerColumnName(columnName), 29);
|
||||
buffer.append(platformType);
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
List<String> includedColumns = new ArrayList<String>(columns.size());
|
||||
|
||||
for (MColumn column : columns) {
|
||||
if (!column.isHistoryExclude()) {
|
||||
if (column.isIncludeInHistory()) {
|
||||
includedColumns.add(column.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ public class H2Ddl extends PlatformDdl {
|
||||
|
||||
public H2Ddl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
|
||||
super(platformTypes, dbIdentity);
|
||||
this.historyDdl = new H2HistoryDdl();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import com.avaje.ebean.config.dbplatform.H2HistoryTrigger;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
|
||||
import com.avaje.ebean.dbmigration.model.MTable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* H2 history support using DB triggers to maintain a history table.
|
||||
*/
|
||||
public class H2HistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
|
||||
private static final String TRIGGER_CLASS = H2HistoryTrigger.class.getName();
|
||||
|
||||
public H2HistoryDdl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dropTriggers(DdlBuffer buffer, String baseTable) throws IOException {
|
||||
|
||||
buffer.append("drop trigger ").append(updateTriggerName(baseTable)).endOfStatement();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createTriggers(DdlWrite writer, MTable table) throws IOException {
|
||||
|
||||
String baseTableName = table.getName();
|
||||
String historyTableName = historyTableName(baseTableName);
|
||||
List<String> includedColumns = includedColumnNames(table);
|
||||
|
||||
DdlBuffer apply = writer.applyHistory();
|
||||
|
||||
addCreateTrigger(apply, updateTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void regenerateHistoryTriggers(DdlWrite writer, MTable table, HistoryTableUpdate update) throws IOException {
|
||||
|
||||
String baseTableName = table.getName();
|
||||
String historyTableName = historyTableName(baseTableName);
|
||||
List<String> includedColumns = includedColumnNames(table);
|
||||
|
||||
DdlBuffer apply = writer.applyHistory();
|
||||
|
||||
apply.append("-- Regenerated ").newLine();
|
||||
apply.append("-- changes: ").append(update.description()).newLine();
|
||||
|
||||
dropTriggers(apply, baseTableName);
|
||||
addCreateTrigger(apply, updateTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
|
||||
// put a reverted version into the rollback buffer
|
||||
update.toRevertedColumns(includedColumns);
|
||||
|
||||
DdlBuffer rollback = writer.rollback();
|
||||
rollback.append("-- Revert regenerated ").newLine();
|
||||
rollback.append("-- revert changes: ").append(update.description()).newLine();
|
||||
dropTriggers(rollback, baseTableName);
|
||||
addCreateTrigger(rollback, updateTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
}
|
||||
|
||||
private void addCreateTrigger(DdlBuffer apply, String triggerName, String baseTable, String historyTable, List<String> includedColumns) throws IOException {
|
||||
|
||||
// Note that this does not take into account the historyTable name (excepts _history suffix) and
|
||||
// does not take into account excluded columns (all columns included in history)
|
||||
apply
|
||||
.append("create trigger ").append(triggerName).append(" before update,delete on ").append(baseTable)
|
||||
.append(" for each row call \"" + TRIGGER_CLASS + "\";").newLine();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,6 +53,8 @@ public class PlatformDdl {
|
||||
|
||||
protected String identitySuffix = " auto_increment";
|
||||
|
||||
protected String alterTableIfExists = "";
|
||||
|
||||
protected String dropConstraintIfExists = "drop constraint if exists";
|
||||
|
||||
protected String dropIndexIfExists = "drop index if exists ";
|
||||
@@ -136,7 +138,7 @@ public class PlatformDdl {
|
||||
String platformType = convert(column.getType(), identityColumn);
|
||||
|
||||
buffer.append(" ");
|
||||
buffer.append(lowerName(column.getName()), 30);
|
||||
buffer.append(lowerColumnName(column.getName()), 29);
|
||||
buffer.append(platformType);
|
||||
if (isTrue(column.isNotnull()) || isTrue(column.isPrimaryKey())) {
|
||||
buffer.append(" not null");
|
||||
@@ -150,7 +152,7 @@ public class PlatformDdl {
|
||||
* Return the drop foreign key clause.
|
||||
*/
|
||||
public String alterTableDropForeignKey(String tableName, String fkName) {
|
||||
return "alter table " + tableName + " " + dropConstraintIfExists + " " + fkName;
|
||||
return "alter table " + alterTableIfExists + tableName + " " + dropConstraintIfExists + " " + fkName;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,7 +256,7 @@ public class PlatformDdl {
|
||||
appendColumns(columns, buffer);
|
||||
buffer
|
||||
.append(" references ")
|
||||
.append(lowerName(refTable));
|
||||
.append(lowerTableName(refTable));
|
||||
appendColumns(refColumns, buffer);
|
||||
appendWithSpace(foreignKeyRestrict, buffer);
|
||||
|
||||
@@ -346,7 +348,7 @@ public class PlatformDdl {
|
||||
if (i > 0) {
|
||||
buffer.append(",");
|
||||
}
|
||||
buffer.append(lowerName(columns[i].trim()));
|
||||
buffer.append(lowerColumnName(columns[i].trim()));
|
||||
}
|
||||
buffer.append(")");
|
||||
}
|
||||
@@ -358,16 +360,26 @@ public class PlatformDdl {
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the table or column name to lower case.
|
||||
* Convert the table to lower case.
|
||||
* <p>
|
||||
* This is passed up to the platformDdl to override as desired.
|
||||
* Generally lower case with underscore is a good cross database
|
||||
* Override as desired. Generally lower case with underscore is a good cross database
|
||||
* choice for column/table names.
|
||||
*/
|
||||
protected String lowerName(String name) {
|
||||
return naming.lowerName(name);
|
||||
protected String lowerTableName(String name) {
|
||||
return naming.lowerTableName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the column name to lower case.
|
||||
* <p>
|
||||
* Override as desired. Generally lower case with underscore is a good cross database
|
||||
* choice for column/table names.
|
||||
*/
|
||||
protected String lowerColumnName(String name) {
|
||||
return naming.lowerColumnName(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Null safe Boolean true test.
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,7 @@ public class PostgresDdl extends PlatformDdl {
|
||||
this.historyDdl = new PostgresHistoryDdl();
|
||||
this.dropTableCascade = " cascade";
|
||||
this.columnSetType = "type ";
|
||||
this.alterTableIfExists = "if exists ";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-3
@@ -40,7 +40,7 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
.endOfStatement();
|
||||
|
||||
if (whenCreatedColumn != null) {
|
||||
apply.append("update table ").append(baseTableName).append(" set ")
|
||||
apply.append("update ").append(baseTableName).append(" set ")
|
||||
.append(sysPeriod).append(" = tstzrange(").append(whenCreatedColumn).append(", null)").endOfStatement();
|
||||
}
|
||||
}
|
||||
@@ -81,12 +81,12 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
appendInsertIntoHistory(apply, historyTable, includedColumns);
|
||||
apply
|
||||
.append(" NEW.").append(sysPeriod).append(" = tstzrange(CURRENT_TIMESTAMP,null);").newLine()
|
||||
.append(" return new;").newLine().newLine();
|
||||
.append(" return new;").newLine();
|
||||
apply
|
||||
.append(" elsif (TG_OP = 'DELETE') then").newLine();
|
||||
appendInsertIntoHistory(apply, historyTable, includedColumns);
|
||||
apply
|
||||
.append(" return old;").newLine().newLine();
|
||||
.append(" return old;").newLine();
|
||||
apply
|
||||
.append(" end if;").newLine()
|
||||
.append("end;").newLine()
|
||||
|
||||
@@ -77,6 +77,9 @@ public class CurrentModel {
|
||||
ModelBuildBeanVisitor visitor = new ModelBuildBeanVisitor(context);
|
||||
VisitAllUsing visit = new VisitAllUsing(visitor, server);
|
||||
visit.visitAllBeans();
|
||||
|
||||
// adjust the foreign keys on the 'draft' tables
|
||||
context.adjustDraftReferences();
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ public class MColumn {
|
||||
*/
|
||||
private AlterColumn alterColumn;
|
||||
|
||||
private boolean draftOnly;
|
||||
|
||||
public MColumn(Column column) {
|
||||
this.name = column.getName();
|
||||
this.type = column.getType();
|
||||
@@ -63,6 +65,28 @@ public class MColumn {
|
||||
this.notnull = notnull;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this column used for creating the associated draft table.
|
||||
*/
|
||||
public MColumn copyForDraft() {
|
||||
|
||||
MColumn copy = new MColumn(name, type);
|
||||
copy.draftOnly = draftOnly;
|
||||
copy.checkConstraint = checkConstraint;
|
||||
copy.checkConstraintName = checkConstraintName;
|
||||
copy.defaultValue = defaultValue;
|
||||
copy.references = references;
|
||||
copy.foreignKeyName = foreignKeyName;
|
||||
copy.foreignKeyIndex = foreignKeyIndex;
|
||||
copy.historyExclude = historyExclude;
|
||||
copy.notnull = notnull;
|
||||
copy.primaryKey = primaryKey;
|
||||
copy.identity = identity;
|
||||
copy.unique = unique;
|
||||
copy.uniqueOneToOne = uniqueOneToOne;
|
||||
return copy;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@@ -174,6 +198,28 @@ public class MColumn {
|
||||
return uniqueOneToOne;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the draftOnly status for this column.
|
||||
*/
|
||||
public void setDraftOnly(boolean draftOnly) {
|
||||
this.draftOnly = draftOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the draftOnly status for this column.
|
||||
*/
|
||||
public boolean isDraftOnly() {
|
||||
return draftOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this column should be included in History DB triggers etc.
|
||||
*/
|
||||
public boolean isIncludeInHistory() {
|
||||
return !draftOnly && !historyExclude;
|
||||
}
|
||||
|
||||
public Column createColumn() {
|
||||
|
||||
Column c = new Column();
|
||||
|
||||
@@ -14,7 +14,7 @@ import java.util.List;
|
||||
*/
|
||||
public class MCompoundForeignKey {
|
||||
|
||||
private final String name;
|
||||
private String name;
|
||||
private final String referenceTable;
|
||||
private final List<String> columns = new ArrayList<String>();
|
||||
private final List<String> referenceColumns = new ArrayList<String>();
|
||||
@@ -47,6 +47,28 @@ public class MCompoundForeignKey {
|
||||
return fk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a counter to the foreign key and index names to avoid duplication.
|
||||
*/
|
||||
public void addNameSuffix(int counter) {
|
||||
this.name = name + "_" + counter;
|
||||
this.indexName = indexName + "_" + counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the foreign key name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the index name.
|
||||
*/
|
||||
public String getIndexName() {
|
||||
return indexName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the columns making up the foreign key in order.
|
||||
*/
|
||||
|
||||
@@ -26,6 +26,17 @@ public class MIndex {
|
||||
this.columns.add(columnName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a multi column non unique index.
|
||||
*/
|
||||
public MIndex(String indexName, String tableName, String[] columnNames) {
|
||||
this.tableName = tableName;
|
||||
this.indexName = indexName;
|
||||
for (int i = 0; i < columnNames.length; i++) {
|
||||
this.columns.add(columnNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public MIndex(CreateIndex createIndex) {
|
||||
this.indexName = createIndex.getIndexName();
|
||||
this.tableName = createIndex.getTableName();
|
||||
|
||||
@@ -9,10 +9,12 @@ import com.avaje.ebean.dbmigration.migration.DropColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.DropHistoryTable;
|
||||
import com.avaje.ebean.dbmigration.migration.DropTable;
|
||||
import com.avaje.ebean.dbmigration.migration.IdentityType;
|
||||
import com.avaje.ebean.dbmigration.migration.UniqueConstraint;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
@@ -41,6 +43,17 @@ public class MTable {
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* The associated draft table.
|
||||
*/
|
||||
private MTable draftTable;
|
||||
|
||||
/**
|
||||
* Marked true for draft tables. These need to have their FK references adjusted
|
||||
* after all the draft tables have been identified.
|
||||
*/
|
||||
private boolean draft;
|
||||
|
||||
/**
|
||||
* Primary key name.
|
||||
*/
|
||||
@@ -105,6 +118,27 @@ public class MTable {
|
||||
*/
|
||||
private AddColumn addColumn;
|
||||
|
||||
/**
|
||||
* Create a copy of this table structure as a 'draft' table.
|
||||
*
|
||||
* Note that both tables contain @DraftOnly MColumns and these are filtered out
|
||||
* later when creating the CreateTable object.
|
||||
*/
|
||||
public MTable createDraftTable() {
|
||||
|
||||
draftTable = new MTable(name+"_draft");
|
||||
draftTable.draft = true;
|
||||
draftTable.whenCreatedColumn = whenCreatedColumn;
|
||||
// compoundKeys
|
||||
// compoundUniqueConstraints
|
||||
draftTable.identityType = identityType;
|
||||
|
||||
for (MColumn col: columns.values()) {
|
||||
draftTable.addColumn(col.copyForDraft());
|
||||
}
|
||||
|
||||
return draftTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for migration.
|
||||
@@ -162,13 +196,31 @@ public class MTable {
|
||||
}
|
||||
|
||||
for (MColumn column : this.columns.values()) {
|
||||
createTable.getColumn().add(column.createColumn());
|
||||
// filter out draftOnly columns from the base table
|
||||
if (draft || !column.isDraftOnly()) {
|
||||
createTable.getColumn().add(column.createColumn());
|
||||
}
|
||||
}
|
||||
|
||||
for (MCompoundForeignKey compoundKey : compoundKeys) {
|
||||
createTable.getForeignKey().add(compoundKey.createForeignKey());
|
||||
}
|
||||
|
||||
for (MCompoundUniqueConstraint constraint : compoundUniqueConstraints) {
|
||||
UniqueConstraint uq = new UniqueConstraint();
|
||||
uq.setName(constraint.getName());
|
||||
String[] columns = constraint.getColumns();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(columns[i]);
|
||||
}
|
||||
uq.setColumnNames(sb.toString());
|
||||
createTable.getUniqueConstraint().add(uq);
|
||||
}
|
||||
|
||||
return createTable;
|
||||
}
|
||||
|
||||
@@ -254,6 +306,13 @@ public class MTable {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this table is a 'Draft' table.
|
||||
*/
|
||||
public boolean isDraft() {
|
||||
return draft;
|
||||
}
|
||||
|
||||
public String getPkName() {
|
||||
return pkName;
|
||||
}
|
||||
@@ -466,4 +525,70 @@ public class MTable {
|
||||
return (value == 0) ? null : BigInteger.valueOf(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are duplicate foreign keys.
|
||||
* <p>
|
||||
* This can occur when an ManyToMany relates back to itself.
|
||||
* </p>
|
||||
*/
|
||||
public void checkDuplicateForeignKeys() {
|
||||
|
||||
if (hasDuplicateForeignKeys()) {
|
||||
int counter = 1;
|
||||
for (MCompoundForeignKey fk : compoundKeys) {
|
||||
fk.addNameSuffix(counter++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the foreign key names are not unique.
|
||||
*/
|
||||
private boolean hasDuplicateForeignKeys() {
|
||||
Set<String> fkNames = new HashSet<String>();
|
||||
for (MCompoundForeignKey fk : compoundKeys) {
|
||||
if (!fkNames.add(fk.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the references (FK) if it should relate to a draft table.
|
||||
*/
|
||||
public void adjustReferences(ModelContainer modelContainer) {
|
||||
|
||||
Collection<MColumn> cols = columns.values();
|
||||
for (MColumn col : cols) {
|
||||
String references = col.getReferences();
|
||||
if (references != null) {
|
||||
String baseTable = extractBaseTable(references);
|
||||
MTable refBaseTable = modelContainer.getTable(baseTable);
|
||||
if (refBaseTable.draftTable != null) {
|
||||
// change references to another associated 'draft' table
|
||||
String newReferences = deriveReferences(references, refBaseTable.draftTable.getName());
|
||||
col.setReferences(newReferences);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base table name from references (table.column).
|
||||
*/
|
||||
private String extractBaseTable(String references) {
|
||||
int lastDot = references.lastIndexOf('.');
|
||||
return references.substring(0,lastDot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the new references using the given draftTableName.
|
||||
* (The referenced column is the same as before).
|
||||
*/
|
||||
private String deriveReferences(String references, String draftTableName) {
|
||||
int lastDot = references.lastIndexOf('.');
|
||||
return draftTableName+"."+references.substring(lastDot+1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.avaje.ebean.dbmigration.migration.DropIndex;
|
||||
import com.avaje.ebean.dbmigration.migration.DropTable;
|
||||
import com.avaje.ebean.dbmigration.migration.Migration;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -38,6 +39,18 @@ public class ModelContainer {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the FK references on all the draft tables.
|
||||
*/
|
||||
public void adjustDraftReferences() {
|
||||
Collection<MTable> tables = this.tables.values();
|
||||
for (MTable table : tables) {
|
||||
if (table.isDraft()) {
|
||||
table.adjustReferences(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the map of all the tables.
|
||||
*/
|
||||
@@ -213,8 +226,8 @@ public class ModelContainer {
|
||||
/**
|
||||
* Add a table (typically from reading EbeanServer meta data).
|
||||
*/
|
||||
public void addTable(MTable table) {
|
||||
tables.put(table.getName(), table);
|
||||
public MTable addTable(MTable table) {
|
||||
return tables.put(table.getName(), table);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,4 +237,12 @@ public class ModelContainer {
|
||||
|
||||
indexes.put(indexName, new MIndex(indexName, tableName, columnName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a multi column index.
|
||||
*/
|
||||
public void addIndex(String indexName, String tableName, String[] columnNames) {
|
||||
|
||||
indexes.put(indexName, new MIndex(indexName, tableName, columnNames));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.avaje.ebean.config.dbplatform.IdType;
|
||||
import com.avaje.ebean.dbmigration.migration.IdentityType;
|
||||
import com.avaje.ebean.dbmigration.model.MColumn;
|
||||
import com.avaje.ebean.dbmigration.model.MTable;
|
||||
import com.avaje.ebean.dbmigration.model.visitor.BeanPropertyVisitor;
|
||||
import com.avaje.ebean.dbmigration.model.visitor.BeanVisitor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
@@ -29,7 +28,7 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
|
||||
* This creates an MTable and adds it to the model.
|
||||
* </p>
|
||||
*/
|
||||
public BeanPropertyVisitor visitBean(BeanDescriptor<?> descriptor) {
|
||||
public ModelBuildPropertyVisitor visitBean(BeanDescriptor<?> descriptor) {
|
||||
|
||||
if (!descriptor.isInheritanceRoot()) {
|
||||
return null;
|
||||
@@ -58,7 +57,7 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
|
||||
table.addColumn(new MColumn(discColumn, discDbType, true));
|
||||
}
|
||||
|
||||
return new ModelBuildPropertyVisitor(ctx, table, descriptor.getCompoundUniqueConstraints());
|
||||
return new ModelBuildPropertyVisitor(ctx, table, descriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,11 +3,15 @@ package com.avaje.ebean.dbmigration.model.build;
|
||||
import com.avaje.ebean.config.DbConstraintNaming;
|
||||
import com.avaje.ebean.config.dbplatform.DbType;
|
||||
import com.avaje.ebean.config.dbplatform.DbTypeMap;
|
||||
import com.avaje.ebean.dbmigration.model.MColumn;
|
||||
import com.avaje.ebean.dbmigration.model.MTable;
|
||||
import com.avaje.ebean.dbmigration.model.ModelContainer;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The context used during DDL generation.
|
||||
*/
|
||||
@@ -31,6 +35,14 @@ public class ModelBuildContext {
|
||||
this.maxLength = maxLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the foreign key references on any draft tables (that reference other draft tables).
|
||||
* This is called as a 'second pass' after all the draft tables have been identified.
|
||||
*/
|
||||
public void adjustDraftReferences() {
|
||||
model.adjustDraftReferences();
|
||||
}
|
||||
|
||||
public String primaryKeyName(String tableName) {
|
||||
return maxLength(constraintNaming.primaryKeyName(tableName), 0);
|
||||
}
|
||||
@@ -67,14 +79,18 @@ public class ModelBuildContext {
|
||||
return maxLength(constraintNaming.checkConstraintName(tableName, columnName), checkCount);
|
||||
}
|
||||
|
||||
public void addTable(MTable table) {
|
||||
model.addTable(table);
|
||||
public MTable addTable(MTable table) {
|
||||
return model.addTable(table);
|
||||
}
|
||||
|
||||
public void addIndex(String indexName, String tableName, String columnName) {
|
||||
model.addIndex(indexName, tableName, columnName);
|
||||
}
|
||||
|
||||
public void addIndex(String indexName, String tableName, String[] columnNames) {
|
||||
model.addIndex(indexName, tableName, columnNames);
|
||||
}
|
||||
|
||||
private String maxLength(String constraintName, int indexCount) {
|
||||
return maxLength.maxLength(constraintName, indexCount);
|
||||
}
|
||||
@@ -116,4 +132,49 @@ public class ModelBuildContext {
|
||||
return dbTypeMap.get(dbType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the draft table for a given table.
|
||||
*/
|
||||
public void createDraft(MTable table, boolean draftable) {
|
||||
|
||||
MTable draftTable = table.createDraftTable();
|
||||
draftTable.setPkName(primaryKeyName(draftTable.getName()));
|
||||
|
||||
if (draftable) {
|
||||
// Add a FK from @Draftable live table back to it's draft table)
|
||||
List<MColumn> pkCols = table.primaryKeyColumns();
|
||||
if (pkCols.size() == 1) {
|
||||
// only doing this for single column PK at this stage
|
||||
MColumn pk = pkCols.get(0);
|
||||
pk.setReferences(draftTable.getName() + "." + pk.getName());
|
||||
pk.setForeignKeyName(foreignKeyConstraintName(table.getName(), pk.getName(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
int fkCount = 0;
|
||||
int ixCount = 0;
|
||||
int uqCount = 0;
|
||||
Collection<MColumn> cols = draftTable.getColumns().values();
|
||||
for (MColumn col: cols) {
|
||||
if (col.getForeignKeyName() != null) {
|
||||
// Note that we adjust the 'references' table later in a second pass
|
||||
// after we know all the tables that are 'draftable'
|
||||
//col.setReferences(refTable + "." + refColumn);
|
||||
col.setForeignKeyName(foreignKeyConstraintName(draftTable.getName(), col.getName(), ++fkCount));
|
||||
|
||||
String[] indexCols = {col.getName()};
|
||||
col.setForeignKeyIndex(foreignKeyIndexName(draftTable.getName(), indexCols, ++ixCount));
|
||||
}
|
||||
// adjust the unique constraint names
|
||||
if (col.getUnique() != null){
|
||||
col.setUnique(uniqueConstraintName(draftTable.getName(), col.getName(), ++uqCount));
|
||||
}
|
||||
if (col.getUniqueOneToOne() != null){
|
||||
col.setUniqueOneToOne(uniqueConstraintName(draftTable.getName(), col.getName(), ++uqCount));
|
||||
}
|
||||
}
|
||||
|
||||
addTable(draftTable);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-1
@@ -35,9 +35,18 @@ public class ModelBuildIntersectionTable {
|
||||
public void build() {
|
||||
|
||||
intersectionTable = createTable();
|
||||
ctx.addTable(intersectionTable);
|
||||
MTable existingTable = ctx.addTable(intersectionTable);
|
||||
if (existingTable != null) {
|
||||
throw new IllegalStateException("Property " + manyProp.getFullBeanName() + " has duplicate ManyToMany intersection table " + intersectionTable.getName()
|
||||
+ ". Please use @JoinTable to define unique table to use");
|
||||
}
|
||||
|
||||
buildFkConstraints();
|
||||
|
||||
if (manyProp.getTargetDescriptor().isDraftable()) {
|
||||
ctx.createDraft(intersectionTable, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void buildFkConstraints() {
|
||||
@@ -47,6 +56,8 @@ public class ModelBuildIntersectionTable {
|
||||
|
||||
BeanDescriptor<?> targetDesc = manyProp.getTargetDescriptor();
|
||||
buildFkConstraints(targetDesc, tableJoin.columns(), false);
|
||||
|
||||
intersectionTable.checkDuplicateForeignKeys();
|
||||
}
|
||||
|
||||
|
||||
|
||||
+43
-10
@@ -5,15 +5,17 @@ import com.avaje.ebean.dbmigration.model.MColumn;
|
||||
import com.avaje.ebean.dbmigration.model.MCompoundForeignKey;
|
||||
import com.avaje.ebean.dbmigration.model.MTable;
|
||||
import com.avaje.ebean.dbmigration.model.visitor.BaseTablePropertyVisitor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
|
||||
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint;
|
||||
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueConstraint;
|
||||
import com.avaje.ebeaninternal.server.deploy.TableJoinColumn;
|
||||
import com.avaje.ebeaninternal.server.deploy.id.ImportedId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -26,6 +28,8 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
|
||||
private final MTable table;
|
||||
|
||||
private final BeanDescriptor<?> beanDescriptor;
|
||||
|
||||
private final IndexSet indexSet = new IndexSet();
|
||||
|
||||
private MColumn lastColumn;
|
||||
@@ -36,24 +40,39 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
private int countCheck;
|
||||
|
||||
|
||||
public ModelBuildPropertyVisitor(ModelBuildContext ctx, MTable table, CompoundUniqueContraint[] compoundUniqueConstraints) {
|
||||
public ModelBuildPropertyVisitor(ModelBuildContext ctx, MTable table, BeanDescriptor<?> beanDescriptor) {
|
||||
this.ctx = ctx;
|
||||
this.table = table;
|
||||
|
||||
addCompoundUniqueConstraint(compoundUniqueConstraints);
|
||||
this.beanDescriptor = beanDescriptor;
|
||||
addCompoundUniqueConstraint(beanDescriptor.getCompoundUniqueConstraints());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add unique constraints defined via JPA UniqueConstraint annotations.
|
||||
*/
|
||||
private void addCompoundUniqueConstraint(CompoundUniqueContraint[] compoundUniqueConstraints) {
|
||||
private void addCompoundUniqueConstraint(CompoundUniqueConstraint[] constraints) {
|
||||
|
||||
if (compoundUniqueConstraints != null) {
|
||||
for (int i = 0; i < compoundUniqueConstraints.length; i++) {
|
||||
String[] columns = compoundUniqueConstraints[i].getColumns();
|
||||
String uqName = determineUniqueConstraintName(columns);
|
||||
table.addCompoundUniqueConstraint(columns, false, uqName);
|
||||
if (constraints != null) {
|
||||
for (int i = 0; i < constraints.length; i++) {
|
||||
CompoundUniqueConstraint constraint = constraints[i];
|
||||
String[] columns = constraint.getColumns();
|
||||
indexSet.add(columns);
|
||||
|
||||
if (constraint.isUnique()) {
|
||||
String uqName = constraint.getName();
|
||||
if (uqName == null || uqName.trim().isEmpty()) {
|
||||
uqName = determineUniqueConstraintName(columns);
|
||||
}
|
||||
table.addCompoundUniqueConstraint(columns, false, uqName);
|
||||
|
||||
} else {
|
||||
// 'just' an index (not a unique constraint)
|
||||
String idxName = constraint.getName();
|
||||
if (idxName == null || idxName.trim().isEmpty()) {
|
||||
idxName = determineIndexName(columns);
|
||||
}
|
||||
ctx.addIndex(idxName, table.getName(), columns);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,6 +101,19 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
compoundKey.setIndexName(null);
|
||||
}
|
||||
}
|
||||
|
||||
addDraftTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a 'draft' table that is mostly the same as the base table.
|
||||
* It has @DraftOnly columns and adjusted primary and foreign keys.
|
||||
*/
|
||||
private void addDraftTable() {
|
||||
if (beanDescriptor.isDraftable() || beanDescriptor.isDraftableElement()) {
|
||||
// create a 'Draft' table which looks very similar (change PK, FK etc)
|
||||
ctx.createDraft(table, !beanDescriptor.isDraftableElement());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -198,6 +230,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
}
|
||||
|
||||
MColumn col = new MColumn(p.getDbColumn(), ctx.getColumnDefn(p));
|
||||
col.setDraftOnly(p.isDraftOnly());
|
||||
|
||||
if (p.isId()) {
|
||||
col.setPrimaryKey(true);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.avaje.ebean.dbmigration.model.visitor;
|
||||
|
||||
import com.avaje.ebean.dbmigration.model.build.ModelBuildPropertyVisitor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
/**
|
||||
@@ -12,6 +13,6 @@ public interface BeanVisitor {
|
||||
* Visit a BeanDescriptor and return a PropertyVisitor to use to visit each
|
||||
* property on the entity bean (return null to skip visiting this bean).
|
||||
*/
|
||||
BeanPropertyVisitor visitBean(BeanDescriptor<?> descriptor);
|
||||
ModelBuildPropertyVisitor visitBean(BeanDescriptor<?> descriptor);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Generation DDL migration scripts based on changes to the model.
|
||||
* <p>
|
||||
* You can use <code>DbMigration</code> to compare the entity bean model to its prior state
|
||||
* and generate DDL for the differences - adding tables, columns etc.
|
||||
* </p>
|
||||
*/
|
||||
package com.avaje.ebean.dbmigration;
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.avaje.ebean.event;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.ValuePair;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Holds the information available for a bean persist (insert, update or
|
||||
* delete).
|
||||
@@ -34,9 +34,30 @@ public interface BeanPersistRequest<T> {
|
||||
|
||||
/**
|
||||
* For an update this is the set of properties that where updated.
|
||||
* <p>
|
||||
* Note that hasDirtyProperty() is a more efficient check than this method and
|
||||
* should be preferred if it satisfies the requirement.
|
||||
* </p>
|
||||
*/
|
||||
Set<String> getUpdatedProperties();
|
||||
|
||||
/**
|
||||
* Return true for an update request if at least one of dirty properties is contained
|
||||
* in the given set of property names.
|
||||
* <p>
|
||||
* This method will produce less GC compared with getUpdatedProperties() and should
|
||||
* be preferred if it satisfies the requirement.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that this method is used by the default ChangeLogFilter mechanism for when
|
||||
* the <code>@ChangeLog</code> updatesThatInclude attribute has been specified.
|
||||
* </p>
|
||||
*
|
||||
* @param propertyNames a set of property names which we are checking to see if at least
|
||||
* one of them is dirty.
|
||||
*/
|
||||
boolean hasDirtyProperty(Set<String> propertyNames);
|
||||
|
||||
/**
|
||||
* Returns the bean being inserted updated or deleted.
|
||||
*/
|
||||
@@ -45,6 +66,6 @@ public interface BeanPersistRequest<T> {
|
||||
/**
|
||||
* Returns a map of the properties that have changed and their new and old values.
|
||||
*/
|
||||
Map<String,ValuePair> getUpdatedValues();
|
||||
Map<String, ValuePair> getUpdatedValues();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.avaje.ebean.event.changelog;
|
||||
|
||||
import com.avaje.ebean.ValuePair;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A bean insert, update or delete change sent as part of a ChangeSet.
|
||||
*/
|
||||
public class BeanChange {
|
||||
|
||||
/**
|
||||
* The underling base table name.
|
||||
*/
|
||||
String table;
|
||||
|
||||
/**
|
||||
* The id value.
|
||||
*/
|
||||
Object id;
|
||||
|
||||
/**
|
||||
* The INSERT, UPDATE or DELETE change type.
|
||||
*/
|
||||
ChangeType type;
|
||||
|
||||
/**
|
||||
* The time the bean change was created.
|
||||
*/
|
||||
long eventTime;
|
||||
|
||||
/**
|
||||
* The values for insert or update. Note that null values are not included for insert.
|
||||
*/
|
||||
Map<String, ValuePair> values;
|
||||
|
||||
/**
|
||||
* Construct with all the details.
|
||||
*/
|
||||
public BeanChange(String table, Object id, ChangeType type, Map<String, ValuePair> values) {
|
||||
this.table = table;
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.eventTime = System.currentTimeMillis();
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor for JSON tools.
|
||||
*/
|
||||
public BeanChange() {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "table:" + table + " id:" + id+" values:"+values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object type (typically table name).
|
||||
*/
|
||||
public String getTable() {
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the object type (for JSON tools).
|
||||
*/
|
||||
public void setTable(String table) {
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object id.
|
||||
*/
|
||||
public Object getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bean id (for JSON tools).
|
||||
*/
|
||||
public void setId(Object id) {
|
||||
this.id = this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the change type (INSERT, UPDATE or DELETE).
|
||||
*/
|
||||
public ChangeType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the type (for JSON tools).
|
||||
*/
|
||||
public void setType(ChangeType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the event time in epoch millis.
|
||||
*/
|
||||
public long getEventTime() {
|
||||
return eventTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the event time in epoch millis.
|
||||
*/
|
||||
public void setEventTime(long eventTime) {
|
||||
this.eventTime = eventTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value pairs. For inserts the ValuePair oldValue is always null.
|
||||
*/
|
||||
public Map<String, ValuePair> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value pairs (for JSON tools).
|
||||
*/
|
||||
public void setValues(Map<String, ValuePair> values) {
|
||||
this.values = values;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.avaje.ebean.event.changelog;
|
||||
|
||||
import com.avaje.ebean.event.BeanPersistRequest;
|
||||
|
||||
/**
|
||||
* Used to provide fine grained control over what persist requests are included in the change log.
|
||||
*/
|
||||
public interface ChangeLogFilter {
|
||||
|
||||
/**
|
||||
* Return true if this insert request should be included in the change log.
|
||||
*/
|
||||
boolean includeInsert(BeanPersistRequest<?> insertRequest);
|
||||
|
||||
/**
|
||||
* Return true if this update request should be included in the change log.
|
||||
*/
|
||||
boolean includeUpdate(BeanPersistRequest<?> updateRequest);
|
||||
|
||||
/**
|
||||
* Return true if this delete request should be included in the change log.
|
||||
*/
|
||||
boolean includeDelete(BeanPersistRequest<?> deleteRequest);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.avaje.ebean.event.changelog;
|
||||
|
||||
/**
|
||||
* Listen for changes.
|
||||
* <p>
|
||||
* Implementations can take the changes and store them in a document store for auditing purposes etc.
|
||||
* </p>
|
||||
*/
|
||||
public interface ChangeLogListener {
|
||||
|
||||
/**
|
||||
* Log the batch of changes.
|
||||
* <p>
|
||||
* For small transactions this will be all the changes in the transaction.
|
||||
* For larger/longer transactions this can be a 'batch' of changes made and the actual transaction
|
||||
* has not yet committed or rolled back and a later change set will contain the final changeSet for
|
||||
* the transaction with it's final status of <code>COMMITTED</code> or <code>ROLLBACK</code>.
|
||||
*/
|
||||
void log(ChangeSet changeSet);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.avaje.ebean.event.changelog;
|
||||
|
||||
/**
|
||||
* Listen for changes.
|
||||
* <p>
|
||||
* Implementations can take the changes and store them in a document store for auditing purposes etc.
|
||||
* </p>
|
||||
*/
|
||||
public interface ChangeLogPrepare {
|
||||
|
||||
/**
|
||||
* In the foreground prepare the changeLog for sending.
|
||||
* <p>
|
||||
* This is intended to set extra context information onto the ChangeSet such
|
||||
* as the application user id and client ip address.
|
||||
* </p>
|
||||
* <p>
|
||||
* Returning false means the changeLog is not sent to the log() method in a background thread
|
||||
* and implies that the changeSet should be ignored or that is has been handled in this prepare()
|
||||
* method call.
|
||||
* </p>
|
||||
*
|
||||
* @return true if the changeLog should then be sent to the log method in a background thread.
|
||||
*/
|
||||
boolean prepare(ChangeSet changeSet);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.avaje.ebean.event.changelog;
|
||||
|
||||
/**
|
||||
* Used to assign ChangeLogFilters to bean types.
|
||||
* <p>
|
||||
* Ebean has a built in implementation that uses the ChangeLog annotation to build
|
||||
* appropriate ChangeLogFilters but you can provide an implementation to use instead.
|
||||
* </p>
|
||||
*/
|
||||
public interface ChangeLogRegister {
|
||||
|
||||
/**
|
||||
* For the given bean type return the Change log filter to use.
|
||||
* <p>
|
||||
* This filter provides control over which persist request are included in the change log.
|
||||
*/
|
||||
ChangeLogFilter getChangeFilter(Class<?> beanType);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user