mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
331
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36b5b3b817 | ||
|
|
d83a70a155 | ||
|
|
c140574eba | ||
|
|
4ab2f0df3c | ||
|
|
a447361ea0 | ||
|
|
8fc3f2da15 | ||
|
|
a838bc38c5 | ||
|
|
84e853bcfa | ||
|
|
e2a508aa5e | ||
|
|
e9c66abfe6 | ||
|
|
050aad717d | ||
|
|
30e02de6f3 | ||
|
|
b8a7017ffd | ||
|
|
da61136465 | ||
|
|
90ec7bafb3 | ||
|
|
c638ad8d56 | ||
|
|
6a582c0eef | ||
|
|
0551b3066f | ||
|
|
af36c9ec7d | ||
|
|
bd99badb28 | ||
|
|
f7e07633a1 | ||
|
|
ace45a038e | ||
|
|
36dd2bbee7 | ||
|
|
71e7baff01 | ||
|
|
ef093b2c6c | ||
|
|
14de498955 | ||
|
|
bcda8576bc | ||
|
|
0d35c8cb1b | ||
|
|
6362634924 | ||
|
|
676a9c8d6c | ||
|
|
b1b9f68b7e | ||
|
|
961b219e3e | ||
|
|
a998c0be55 | ||
|
|
e0159ccb4f | ||
|
|
423066ada7 | ||
|
|
c9d968b4b0 | ||
|
|
34be7cc1f1 | ||
|
|
54f21836b0 | ||
|
|
4adf5a9eba | ||
|
|
b6a0653a3c | ||
|
|
c0810a375e | ||
|
|
90fe19eb8d | ||
|
|
43ecfe7c19 | ||
|
|
15be5dd4c7 | ||
|
|
d54cffacea | ||
|
|
965de6e84d | ||
|
|
56fd6b90be | ||
|
|
dd85eba3f0 | ||
|
|
d1a40d5676 | ||
|
|
b0462bcda6 | ||
|
|
59428bb214 | ||
|
|
73cf3e8511 | ||
|
|
beff4465b0 | ||
|
|
563cdd3b3b | ||
|
|
1bd7b99235 | ||
|
|
bb9d9f7e17 | ||
|
|
6bb38f2674 | ||
|
|
bb6571f426 | ||
|
|
1ee679c5ae | ||
|
|
e5264ca69e | ||
|
|
3a86be5f3d | ||
|
|
9f71b0df22 | ||
|
|
f271ea8a0d | ||
|
|
1ced574cd8 | ||
|
|
9dd412cd19 | ||
|
|
8237072f5b | ||
|
|
dd30b43d59 | ||
|
|
09f8967459 | ||
|
|
e3bf3629ee | ||
|
|
b6edd33f9a | ||
|
|
3a11a23e01 | ||
|
|
370267ab6f | ||
|
|
fe65a0ccb9 | ||
|
|
2837324a03 | ||
|
|
a6bd614f87 | ||
|
|
dfaf4b2f31 | ||
|
|
0f26160e1b | ||
|
|
cb66aaf66c | ||
|
|
78b1c05de1 | ||
|
|
c70a4f80eb | ||
|
|
cc788e281f | ||
|
|
d55a7b26f8 | ||
|
|
2195d7ecbc | ||
|
|
983391af94 | ||
|
|
478053263f | ||
|
|
1cd31e13b9 | ||
|
|
374d7faae9 | ||
|
|
6a6cff2236 | ||
|
|
d5d6ac675b | ||
|
|
8ea1f51c7f | ||
|
|
6ecb77dcc2 | ||
|
|
216806074c | ||
|
|
65cb2ae94d | ||
|
|
6677a6199c | ||
|
|
55eaa0b484 | ||
|
|
353f662d70 | ||
|
|
c95d76d6f6 | ||
|
|
e04d6979d1 | ||
|
|
b8078083b1 | ||
|
|
07cdb69a5b | ||
|
|
98f6e1e6af | ||
|
|
fd4d757f87 | ||
|
|
b71b0fc8e2 | ||
|
|
4303bcfd13 | ||
|
|
7f30dc7b1e | ||
|
|
4a882b537c | ||
|
|
641385aab5 | ||
|
|
33ab7eb7a5 | ||
|
|
f6674f8e3b | ||
|
|
4e8b3f22de | ||
|
|
55e43ddc36 | ||
|
|
97d75c06a1 | ||
|
|
adcfa939e4 | ||
|
|
8a4d78a3f5 | ||
|
|
04e77d9949 | ||
|
|
76e7b8d199 | ||
|
|
00f85a42f8 | ||
|
|
e649a70509 | ||
|
|
2ec50d2c71 | ||
|
|
eaee3a9c02 | ||
|
|
221272328c | ||
|
|
1677917465 | ||
|
|
bfefdbac41 | ||
|
|
58755a51a9 | ||
|
|
b313809894 | ||
|
|
0f3b1eb812 | ||
|
|
306cc4adf4 | ||
|
|
ed88978173 | ||
|
|
ae266968b4 | ||
|
|
aa86454cc4 | ||
|
|
c99bec4940 | ||
|
|
7426274ef6 | ||
|
|
130b7e3327 | ||
|
|
de201715fc | ||
|
|
241f6f3d6d | ||
|
|
1210aca027 | ||
|
|
e653c34211 | ||
|
|
131750cf85 | ||
|
|
17b16fd5d9 | ||
|
|
1065a84709 | ||
|
|
13791a2e58 | ||
|
|
277703d750 | ||
|
|
637cbc6489 | ||
|
|
5d09ca16d6 | ||
|
|
6ed1f25e22 | ||
|
|
b8ae443074 | ||
|
|
ab9762bbd9 | ||
|
|
2c90b239cd | ||
|
|
13806767a3 | ||
|
|
40e43b9d5f | ||
|
|
29f1e63146 | ||
|
|
5cf008b521 | ||
|
|
3e788d4598 | ||
|
|
8a29454893 | ||
|
|
62c083a6ea | ||
|
|
7f8e4be396 | ||
|
|
2ad39974e8 | ||
|
|
664e95d87b | ||
|
|
5cf668f65b | ||
|
|
31f56621ff | ||
|
|
11a6cd04ea | ||
|
|
fdb573bfe2 | ||
|
|
ae0e1fe691 | ||
|
|
0511e01273 | ||
|
|
f3bd38536c | ||
|
|
139a558165 | ||
|
|
53b40f7ce0 | ||
|
|
e50656871d | ||
|
|
f7ece0f13c | ||
|
|
562c6fafc9 | ||
|
|
63e9f603ec | ||
|
|
0c4fc4eea3 | ||
|
|
2dca1bdf5b | ||
|
|
6403a83e0e | ||
|
|
b57b91ffef | ||
|
|
4ba5702211 | ||
|
|
2e7aeae004 | ||
|
|
3f2676ab49 | ||
|
|
3f29acb25c | ||
|
|
6d1a41f979 | ||
|
|
aee46955a7 | ||
|
|
38a93b9814 | ||
|
|
5f2ad0dc62 | ||
|
|
ece7f481bc | ||
|
|
fa4d0380e5 | ||
|
|
713ba4889d | ||
|
|
0543745e18 | ||
|
|
4e2bc6cce6 | ||
|
|
f2d422ff5a | ||
|
|
176197513d | ||
|
|
1e3035c558 | ||
|
|
17f7143447 | ||
|
|
394dfc96d1 | ||
|
|
452ce78c3d | ||
|
|
4cb13760c2 | ||
|
|
5cc2eff0d4 | ||
|
|
af9a01be77 | ||
|
|
cceb7c035c | ||
|
|
1ddc103582 | ||
|
|
b973cc3b86 | ||
|
|
10f0ea9bae | ||
|
|
c49dd54500 | ||
|
|
bf0944bb2a | ||
|
|
59ba6244c1 | ||
|
|
f88524b34c | ||
|
|
0ceda6406e | ||
|
|
6838b4df4f | ||
|
|
5db7ae504e | ||
|
|
723ae6bffc | ||
|
|
33b173dd55 | ||
|
|
7966d74eb3 | ||
|
|
28cb2e210d | ||
|
|
1109d412e7 | ||
|
|
3e33ece65a | ||
|
|
1dd200fba2 | ||
|
|
f0ea977f91 | ||
|
|
e830d8efc7 | ||
|
|
0b2ba2a82e | ||
|
|
df526b8f3c | ||
|
|
8fe1f32e78 | ||
|
|
c5ea93fb8e | ||
|
|
dd83bc4d26 | ||
|
|
9b4b7a1a01 | ||
|
|
548fa745d6 | ||
|
|
7d83e3f49e | ||
|
|
974284bf8a | ||
|
|
b446f00cd8 | ||
|
|
a4095b4ae5 | ||
|
|
af6dbd0dbe | ||
|
|
db17ddd069 | ||
|
|
b6b85225e7 | ||
|
|
b57abfa732 | ||
|
|
4a4927b1f3 | ||
|
|
6ac34bec97 | ||
|
|
82725e6a98 | ||
|
|
a744c8b375 | ||
|
|
5e2a34de39 | ||
|
|
8a9ab39a01 | ||
|
|
515256abfd | ||
|
|
84e5a6145e | ||
|
|
1f5618d0e3 | ||
|
|
b1de821817 | ||
|
|
f4dceb97b0 | ||
|
|
ad79f175da | ||
|
|
a4b145bc2d | ||
|
|
5491b27528 | ||
|
|
9211c7e6ab | ||
|
|
5bcb6cd116 | ||
|
|
534f2c8b07 | ||
|
|
7ad390934e | ||
|
|
2a651c38e3 | ||
|
|
ddd30f9ea0 | ||
|
|
01d41b7eea | ||
|
|
4cdea33073 | ||
|
|
33d7e10ad8 | ||
|
|
2201f9220e | ||
|
|
b8743a5ef2 | ||
|
|
b587aa8172 | ||
|
|
c37fa83675 | ||
|
|
08602afcff | ||
|
|
5b98d82f58 | ||
|
|
22cd5b0e82 | ||
|
|
93855478dd | ||
|
|
59b86d5b19 | ||
|
|
27afb9ee2e | ||
|
|
a49cd42a0c | ||
|
|
8c8442ec58 | ||
|
|
41f3173adf | ||
|
|
1a4192b40f | ||
|
|
fffda7c789 | ||
|
|
b7f6596bcb | ||
|
|
1ea49dd206 | ||
|
|
32e4f114b4 | ||
|
|
fcbedda597 | ||
|
|
2055349d76 | ||
|
|
44ee64cde5 | ||
|
|
9f916998c9 | ||
|
|
2ddef37632 | ||
|
|
538ebdc899 | ||
|
|
d6e0d661fb | ||
|
|
289deb397a | ||
|
|
68b4aab837 | ||
|
|
563293f7a9 | ||
|
|
5049694fd3 | ||
|
|
a9c8d1960c | ||
|
|
066b409a65 | ||
|
|
54ad2e0ad0 | ||
|
|
77f7bc2a7e | ||
|
|
f773d74597 | ||
|
|
90b23c4985 | ||
|
|
60b4ad410b | ||
|
|
56215aafa3 | ||
|
|
5ba66e4d78 | ||
|
|
0eb7139426 | ||
|
|
d16d92db54 | ||
|
|
dde59db411 | ||
|
|
1a13fc9742 | ||
|
|
e1ec0bcdfe | ||
|
|
27803afe64 | ||
|
|
c011dab3e2 | ||
|
|
fb7abb44e0 | ||
|
|
14034ffaa0 | ||
|
|
75bcbc06e1 | ||
|
|
bc75736780 | ||
|
|
ed0fe7fd57 | ||
|
|
696dabc658 | ||
|
|
89f7d2bace | ||
|
|
4c4e084e43 | ||
|
|
2dccb818a2 | ||
|
|
a7df7da984 | ||
|
|
b012720a5e | ||
|
|
1451ec0084 | ||
|
|
0d2101ca7c | ||
|
|
a22496a57d | ||
|
|
654b10154e | ||
|
|
f4357c2767 | ||
|
|
0d1c9e2883 | ||
|
|
5e2d592c2a | ||
|
|
2344f57f84 | ||
|
|
fc628d336d | ||
|
|
18d9c53ce3 | ||
|
|
811d298355 | ||
|
|
f7a37537db | ||
|
|
0c9d95619b | ||
|
|
96e4b4640f | ||
|
|
a3c8ed88ca | ||
|
|
d8391ba90c | ||
|
|
c4d41ecb00 | ||
|
|
39f5284a76 | ||
|
|
4ec62a5772 | ||
|
|
eb1eafe98c |
@@ -6,4 +6,12 @@
|
||||
target/
|
||||
logs/
|
||||
log/
|
||||
/db
|
||||
/mydb.db
|
||||
|
||||
|
||||
# Intellij project files
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
[](https://waffle.io/ebean-orm/avaje-ebeanorm)
|
||||
avaje-ebeanorm
|
||||
==============
|
||||
|
||||
Main EbeanORM artifact
|
||||
|
||||
Maven Dependency
|
||||
----------------
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>3.3.2</version>
|
||||
<version>4.5.4</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>3.3.2</version>
|
||||
<version>4.5.5</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>avaje-ebeanorm</name>
|
||||
@@ -48,6 +48,18 @@
|
||||
<artifactId>persistence-api</artifactId>
|
||||
<version>1.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.7</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>2.4.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.transaction</groupId>
|
||||
@@ -78,10 +90,10 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.2</version>
|
||||
<scope>provided</scope>
|
||||
<groupId>com.squareup.okhttp</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>2.1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -94,14 +106,14 @@
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-agent</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<version>4.5.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>1.3.153</version>
|
||||
<version>1.4.182</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -136,9 +148,16 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.8.2</version>
|
||||
<version>4.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-integration</artifactId>
|
||||
<version>1.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
@@ -167,23 +186,21 @@
|
||||
<!-- Enhance the meta beans -->
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.2</version>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
|
||||
<version>3.3.2</version>
|
||||
<version>4.5.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>main</id>
|
||||
<phase>process-classes</phase>
|
||||
<configuration>
|
||||
<classSource>target/classes</classSource>
|
||||
<packages>com.avaje.ebean.meta.**</packages>
|
||||
<transformArgs>debug=1</transformArgs>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>enhance</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<!-- Not going to enhance Model bean -->
|
||||
<execution>
|
||||
<id>test</id>
|
||||
<phase>process-test-classes</phase>
|
||||
@@ -213,9 +230,56 @@
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.5</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9.1</version>
|
||||
<configuration>
|
||||
<doctitle>Ebean 4</doctitle>
|
||||
<overview>src/main/java/com/avaje/ebean/overview.html</overview>
|
||||
<!-- <excludePackageNames>com.avaje.ebeaninternal.*:com.avaje.ebean.util</excludePackageNames> -->
|
||||
<!--<additionalparam>-Xdoclint:none</additionalparam>-->
|
||||
<source>1.8</source>
|
||||
<doclet>org.avaje.doclet.PygmentsDoclet</doclet>
|
||||
<excludePackageNames>com.avaje.ebeaninternal.*:com.avaje.ebean.util</excludePackageNames>
|
||||
<docletArtifact>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>pygments-doclet</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</docletArtifact>
|
||||
<additionalparam>
|
||||
-Xdoclint:none
|
||||
-include-basedir ${project.basedir}
|
||||
-attributes "idseparator=-; project_name=${project.name}; \
|
||||
project_version=${project.version}; \
|
||||
project_desc=${project.description}"
|
||||
</additionalparam>
|
||||
<linksource>true</linksource>
|
||||
<overview>src/main/java/com/avaje/ebean/overview.html</overview>
|
||||
|
||||
</configuration>
|
||||
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -46,6 +47,11 @@ public interface BeanState {
|
||||
*/
|
||||
public Set<String> getChangedProps();
|
||||
|
||||
/**
|
||||
* Return a map of the updated properties and their new and old values.
|
||||
*/
|
||||
public Map<String,ValuePair> getDirtyValues();
|
||||
|
||||
/**
|
||||
* Return true if the bean is readOnly.
|
||||
* <p>
|
||||
@@ -69,26 +75,12 @@ public interface BeanState {
|
||||
*/
|
||||
public void removePropertyChangeListener(PropertyChangeListener listener);
|
||||
|
||||
/**
|
||||
* Advanced - Used to programmatically build a reference object.
|
||||
* <p>
|
||||
* You can create a new EntityBean (
|
||||
* {@link EbeanServer#createEntityBean(Class)}, set its Id property and then
|
||||
* call this setReference() method.
|
||||
* </p>
|
||||
*/
|
||||
public void setReference();
|
||||
|
||||
/**
|
||||
* Advanced - Used to programmatically build a partially or fully loaded
|
||||
* entity bean. First create an entity bean via
|
||||
* {@link EbeanServer#createEntityBean(Class)}, then populate its properties
|
||||
* and then call this method specifying which properties where loaded or null
|
||||
* for a fully loaded entity bean.
|
||||
*
|
||||
* @param loadedProperties
|
||||
* the properties that where loaded or null for a fully loaded entity
|
||||
* bean.
|
||||
*/
|
||||
public void setLoaded(Set<String> loadedProperties);
|
||||
public void setLoaded();
|
||||
}
|
||||
@@ -16,8 +16,6 @@ class DRawSqlParser {
|
||||
|
||||
public static final String $_WHERE = "${where}";
|
||||
|
||||
private static final String ORDER_BY = "order by";
|
||||
|
||||
private final SimpleTextParser textParser;
|
||||
|
||||
private String sql;
|
||||
@@ -35,6 +33,7 @@ class DRawSqlParser {
|
||||
private int groupByPos = -1;
|
||||
private int havingPos = -1;
|
||||
private int orderByPos = -1;
|
||||
private int orderByStmtPos = -1;
|
||||
|
||||
private boolean whereExprAnd;
|
||||
private int whereExprPos = -1;
|
||||
@@ -66,12 +65,12 @@ class DRawSqlParser {
|
||||
String preFrom = removeWhitespace(findPreFromSql());
|
||||
String preWhere = removeWhitespace(findPreWhereSql());
|
||||
String preHaving = removeWhitespace(findPreHavingSql());
|
||||
String orderByPrefix = findOrderByPrefixSql();
|
||||
String orderBySql = findOrderBySql();
|
||||
|
||||
preFrom = trimSelectKeyword(preFrom);
|
||||
|
||||
return new Sql(sql.hashCode(), preFrom, preWhere, whereExprAnd, preHaving, havingExprAnd,
|
||||
orderBySql, (distinctPos > -1));
|
||||
return new Sql(sql.hashCode(), preFrom, preWhere, whereExprAnd, preHaving, havingExprAnd, orderByPrefix, orderBySql, (distinctPos > -1));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,12 +141,12 @@ class DRawSqlParser {
|
||||
return preWhereExprSql;
|
||||
}
|
||||
|
||||
private String findOrderByPrefixSql() {
|
||||
return (orderByPos < 1) ? null : sql.substring(orderByPos, orderByStmtPos);
|
||||
}
|
||||
|
||||
private String findOrderBySql() {
|
||||
if (orderByPos > -1) {
|
||||
int pos = orderByPos + ORDER_BY.length();
|
||||
return sql.substring(pos).trim();
|
||||
}
|
||||
return null;
|
||||
return (orderByStmtPos < 1) ? null : sql.substring(orderByStmtPos).trim();
|
||||
}
|
||||
|
||||
private String findPreHavingSql() {
|
||||
@@ -226,6 +225,11 @@ class DRawSqlParser {
|
||||
}
|
||||
|
||||
orderByPos = textParser.findWordLower("order", startOrderBy);
|
||||
if (orderByPos > 1) {
|
||||
// there might be keywords like siblings in between the order
|
||||
// and by so search for the by keyword explicitly
|
||||
orderByStmtPos = 2 + textParser.findWordLower("by", orderByPos);
|
||||
}
|
||||
}
|
||||
|
||||
private int findWhereExprPosition() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,12 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.common.BootupEbeanManager;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebean.config.ContainerConfig;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.util.ClassUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Creates EbeanServer instances.
|
||||
@@ -26,39 +25,46 @@ import org.slf4j.LoggerFactory;
|
||||
* methods on the Ebean singleton such as {@link Ebean#find(Class)} are just a
|
||||
* convenient way of using the 'default/primary' EbeanServer.
|
||||
* </p>
|
||||
*
|
||||
* @author Rob Bygrave
|
||||
*
|
||||
*/
|
||||
public class EbeanServerFactory {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(EbeanServerFactory.class);
|
||||
|
||||
private static BootupEbeanManager serverFactory = createServerFactory();
|
||||
private static BootupEbeanManager bootupEbeanManager;
|
||||
|
||||
/**
|
||||
* Initialise the container with clustering configuration.
|
||||
*
|
||||
* Call this prior to creating any EbeanServer instances or alternatively set the
|
||||
* ContainerConfig on the ServerConfig when creating the first EbeanServer instance.
|
||||
*/
|
||||
public static synchronized void initialiseContainer(ContainerConfig containerConfig) {
|
||||
getServerFactory(containerConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create using ebean.properties to configure the server.
|
||||
*/
|
||||
public static EbeanServer create(String name) {
|
||||
public static synchronized EbeanServer create(String name) {
|
||||
|
||||
EbeanServer server = serverFactory.createServer(name);
|
||||
|
||||
return server;
|
||||
// construct based on loading properties files
|
||||
// and if invoked by Ebean then it handles registration
|
||||
BootupEbeanManager serverFactory = getServerFactory(null);
|
||||
return serverFactory.createServer(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create using the ServerConfig object to configure the server.
|
||||
*/
|
||||
public static EbeanServer create(ServerConfig config) {
|
||||
public static synchronized EbeanServer create(ServerConfig config) {
|
||||
|
||||
if (config.getName() == null) {
|
||||
throw new PersistenceException("The name is null (it is required)");
|
||||
}
|
||||
|
||||
EbeanServer server = serverFactory.createServer(config);
|
||||
EbeanServer server = createInternal(config);
|
||||
|
||||
if (config.isDefaultServer()) {
|
||||
GlobalProperties.setSkipPrimaryServer(true);
|
||||
PrimaryServer.setSkip(true);
|
||||
}
|
||||
if (config.isRegister()) {
|
||||
Ebean.register(server, config.isDefaultServer());
|
||||
@@ -67,30 +73,45 @@ public class EbeanServerFactory {
|
||||
return server;
|
||||
}
|
||||
|
||||
private static BootupEbeanManager createServerFactory() {
|
||||
|
||||
// String d___ =
|
||||
// com.avaje.ebean.server.core.DefaultServerFactory.class.getName();
|
||||
String dflt = "com.avaje.ebeaninternal.server.core.DefaultServerFactory";
|
||||
String implClassName = GlobalProperties.get("ebean.serverfactory", dflt);
|
||||
private static EbeanServer createInternal(ServerConfig config) {
|
||||
|
||||
int delaySecs = GlobalProperties.getInt("ebean.start.delay", 0);
|
||||
if (delaySecs > 0) {
|
||||
try {
|
||||
// perhaps useful to delay the startup to give time to
|
||||
// attach a debugger when running in a server like tomcat.
|
||||
String m = "Ebean sleeping " + delaySecs + " seconds due to ebean.start.delay";
|
||||
logger.info(m);
|
||||
Thread.sleep(delaySecs * 1000);
|
||||
return getServerFactory(config.getContainerConfig()).createServer(config);
|
||||
}
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
String m = "Interrupting debug.start.delay of " + delaySecs;
|
||||
logger.error(m, e);
|
||||
}
|
||||
/**
|
||||
* Get the BootupEbeanManager initialising it if necessary.
|
||||
*
|
||||
* @param containerConfig the configuration controlling clustering communication
|
||||
*/
|
||||
private static BootupEbeanManager getServerFactory(ContainerConfig containerConfig) {
|
||||
|
||||
if (bootupEbeanManager != null) {
|
||||
return bootupEbeanManager;
|
||||
}
|
||||
|
||||
if (containerConfig == null) {
|
||||
// effectively load configuration from ebean.properties
|
||||
Properties properties = PrimaryServer.getProperties();
|
||||
containerConfig = new ContainerConfig();
|
||||
containerConfig.loadFromProperties(properties);
|
||||
}
|
||||
bootupEbeanManager = createServerFactory(containerConfig);
|
||||
return bootupEbeanManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the container instance using the configuration.
|
||||
*/
|
||||
private static BootupEbeanManager createServerFactory(ContainerConfig containerConfig) {
|
||||
|
||||
String dflt = "com.avaje.ebeaninternal.server.core.DefaultServerFactory";
|
||||
String implClassName = System.getProperty("ebean.serverfactory", dflt);
|
||||
|
||||
try {
|
||||
// use a client side implementation?
|
||||
return (BootupEbeanManager) ClassUtil.newInstance(implClassName);
|
||||
Class<?> cls = Class.forName(implClassName);
|
||||
Constructor<?> constructor = cls.getConstructor(ContainerConfig.class);
|
||||
return (BootupEbeanManager) constructor.newInstance(containerConfig);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -87,6 +89,11 @@ public interface ExpressionList<T> extends Serializable {
|
||||
*/
|
||||
public Query<T> setOrderBy(String orderBy);
|
||||
|
||||
/**
|
||||
* Apply the path properties to the query replacing the select and fetch clauses.
|
||||
*/
|
||||
public Query<T> apply(PathProperties pathProperties);
|
||||
|
||||
/**
|
||||
* Execute the query iterating over the results.
|
||||
*
|
||||
@@ -95,9 +102,25 @@ public interface ExpressionList<T> extends Serializable {
|
||||
public QueryIterator<T> findIterate();
|
||||
|
||||
/**
|
||||
* Execute the query visiting the results.
|
||||
*
|
||||
* @see Query#findVisit(QueryResultVisitor)
|
||||
* Execute the query process the beans one at a time.
|
||||
*
|
||||
* @see Query#findEach(QueryEachConsumer)
|
||||
*/
|
||||
public void findEach(QueryEachConsumer<T> consumer);
|
||||
|
||||
/**
|
||||
* Execute the query processing the beans one at a time with the ability to
|
||||
* stop processing before reading all the beans.
|
||||
*
|
||||
* @see Query#findEachWhile(QueryEachWhileConsumer)
|
||||
*/
|
||||
public void findEachWhile(QueryEachWhileConsumer<T> consumer);
|
||||
|
||||
/**
|
||||
* Deprecated in favor of #findEachWhile which is functionally exactly the same
|
||||
* but has a much better name.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public void findVisit(QueryResultVisitor<T> visitor);
|
||||
|
||||
@@ -186,24 +209,29 @@ public interface ExpressionList<T> extends Serializable {
|
||||
public FutureList<T> findFutureList();
|
||||
|
||||
/**
|
||||
* Return a PagingList for this query.
|
||||
* Return a PagedList for this query.
|
||||
* <p>
|
||||
* This can be used to break up a query into multiple queries to fetch the
|
||||
* data a page at a time.
|
||||
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
|
||||
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
|
||||
* {@link Query#findFutureRowCount()} to determine total row count, total page count etc.
|
||||
* </p>
|
||||
* <p>
|
||||
* This typically works by using a query per page and setting
|
||||
* {@link Query#setFirstRow(int)} and and {@link Query#setMaxRows(int)} on the
|
||||
* query. This usually would translate into SQL that uses limit offset, rownum
|
||||
* or row_number function to limit the result set.
|
||||
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
|
||||
* the query. This translates into SQL that uses limit offset, rownum or row_number
|
||||
* function to limit the result set.
|
||||
* </p>
|
||||
*
|
||||
* @param pageIndex
|
||||
* The zero based index of the page.
|
||||
* @param pageSize
|
||||
* the number of beans fetched per Page
|
||||
*
|
||||
* The number of beans to return per page.
|
||||
* @return The PagedList
|
||||
*/
|
||||
public PagingList<T> findPagingList(int pageSize);
|
||||
public PagedList<T> findPagedList(int pageIndex, int pageSize);
|
||||
|
||||
/**
|
||||
* Add some filter predicate expressions to the many property.
|
||||
*/
|
||||
public ExpressionList<T> filterMany(String prop);
|
||||
|
||||
/**
|
||||
@@ -214,22 +242,6 @@ public interface ExpressionList<T> extends Serializable {
|
||||
*/
|
||||
public Query<T> select(String properties);
|
||||
|
||||
/**
|
||||
* Specify a property (associated bean) to join and <em>fetch</em> including
|
||||
* all its properties.
|
||||
*
|
||||
* @see Query#join(String)
|
||||
*/
|
||||
public Query<T> join(String assocProperties);
|
||||
|
||||
/**
|
||||
* Specify a property (associated bean) to join and <em>fetch</em> with its
|
||||
* specific properties to include (aka partial object).
|
||||
*
|
||||
* @see Query#join(String,String)
|
||||
*/
|
||||
public Query<T> join(String assocProperty, String assocProperties);
|
||||
|
||||
/**
|
||||
* Set the first row to fetch.
|
||||
*
|
||||
@@ -244,14 +256,6 @@ public interface ExpressionList<T> extends Serializable {
|
||||
*/
|
||||
public Query<T> setMaxRows(int maxRows);
|
||||
|
||||
/**
|
||||
* Set the number of rows after which the fetching should continue in a
|
||||
* background thread.
|
||||
*
|
||||
* @see Query#setBackgroundFetchAfter(int)
|
||||
*/
|
||||
public Query<T> setBackgroundFetchAfter(int backgroundFetchAfter);
|
||||
|
||||
/**
|
||||
* Set the name of the property which values become the key of a map.
|
||||
*
|
||||
@@ -259,15 +263,6 @@ public interface ExpressionList<T> extends Serializable {
|
||||
*/
|
||||
public Query<T> setMapKey(String mapKey);
|
||||
|
||||
/**
|
||||
* Please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}.
|
||||
* Set a QueryListener for bean by bean processing.
|
||||
*
|
||||
* @see Query#setListener(QueryListener)
|
||||
* @deprecated Migrate to {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
|
||||
*/
|
||||
public Query<T> setListener(QueryListener<T> queryListener);
|
||||
|
||||
/**
|
||||
* Set to true to use the query for executing this query.
|
||||
*
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* FutureList represents the result of a background query execution that will
|
||||
@@ -15,13 +19,13 @@ import java.util.concurrent.Future;
|
||||
* A simple example:
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* // create a query to find all orders
|
||||
* Query<Order> query = Ebean.find(Order.class);
|
||||
* Query<Order> query = Ebean.find(Order.class);
|
||||
*
|
||||
* // execute the query in a background thread
|
||||
* // immediately returning the futureList
|
||||
* FutureList<Order> futureList = query.findFutureList();
|
||||
* FutureList<Order> futureList = query.findFutureList();
|
||||
*
|
||||
* // do something else ...
|
||||
*
|
||||
@@ -35,13 +39,11 @@ import java.util.concurrent.Future;
|
||||
*
|
||||
* if (!futureList.isCancelled()){
|
||||
* // wait for the query to finish and return the list
|
||||
* List<Order> list = futureList.get();
|
||||
* List<Order> list = futureList.get();
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author rbygrave
|
||||
* }</pre>
|
||||
*/
|
||||
public interface FutureList<T> extends Future<List<T>> {
|
||||
|
||||
@@ -50,4 +52,25 @@ public interface FutureList<T> extends Future<List<T>> {
|
||||
*/
|
||||
public Query<T> getQuery();
|
||||
|
||||
/**
|
||||
* Same as {@link #get()} but wraps InterruptedException and ExecutionException in the
|
||||
* unchecked PersistenceException.
|
||||
*
|
||||
* @return The query list result
|
||||
*
|
||||
* @throws PersistenceException when a InterruptedException or ExecutionException occurs.
|
||||
*/
|
||||
public List<T> getUnchecked();
|
||||
|
||||
/**
|
||||
* Same as {@link #get(long, java.util.concurrent.TimeUnit)} but wraps InterruptedException
|
||||
* and ExecutionException in the unchecked PersistenceException.
|
||||
*
|
||||
* @return The query list result
|
||||
*
|
||||
* @throws TimeoutException if the wait timed out
|
||||
* @throws PersistenceException if a InterruptedException or ExecutionException occurs.
|
||||
*/
|
||||
public List<T> getUnchecked(long timeout, TimeUnit unit) throws TimeoutException;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,737 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
/**
|
||||
* A MappedSuperclass base class that provides convenience methods for inserting, updating and
|
||||
* deleting beans.
|
||||
*
|
||||
* <p>
|
||||
* By having your entity beans extend this it provides a 'Active Record' style programming model for
|
||||
* Ebean users.
|
||||
*
|
||||
* <p>
|
||||
* Note that there is a avaje-ebeanorm-mocker project that enables you to use Mockito or similar
|
||||
* tools to still mock out the underlying 'default EbeanServer' for testing purposes.
|
||||
*
|
||||
* <p>
|
||||
* You may choose not use this Model mapped superclass if you don't like the 'Active Record' style
|
||||
* or if you believe it 'pollutes' your entity beans.
|
||||
*
|
||||
* <p>
|
||||
* You can use Dependency Injection like Guice or Spring to construct and wire a EbeanServer instance
|
||||
* and have that same instance used with this Model and Finder. The way that works is that when the
|
||||
* DI container creates the EbeanServer instance it can be registered with the Ebean singleton. In this
|
||||
* way the EbeanServer instance can be injected as per normal Guice / Spring dependency injection and
|
||||
* that same instance also used to support the Model and Finder active record style.
|
||||
*
|
||||
* <p>
|
||||
* If you choose to use the Model mapped superclass you will probably also chose to additionally add
|
||||
* a {@link Finder} as a public static field to complete the active record pattern and provide a
|
||||
* relatively nice clean way to write queries.
|
||||
*
|
||||
* <h3>Typical common @MappedSuperclass</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Typically there is a common base model that has some
|
||||
* // common properties like the ones below
|
||||
*
|
||||
* @MappedSuperclass
|
||||
* public class BaseModel extends Model {
|
||||
*
|
||||
* @Id Long id;
|
||||
*
|
||||
* @Version Long version;
|
||||
*
|
||||
* @CreatedTimestamp Timestamp whenCreated;
|
||||
*
|
||||
* @UpdatedTimestamp Timestamp whenUpdated;
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Extend the Model</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Extend the mappedSuperclass
|
||||
*
|
||||
* @Entity @Table(name="oto_account")
|
||||
* public class Account extends BaseModel {
|
||||
*
|
||||
* // add a static Finder
|
||||
* // ... with Long being the type of our ID property ...
|
||||
*
|
||||
* public static final Finder<Long,Account> find =
|
||||
* new Finder<Long,Account>(Long.class, Account.class);
|
||||
*
|
||||
* String name;
|
||||
*
|
||||
* @OneToOne(mappedBy = "account",optional = true)
|
||||
* User user;
|
||||
*
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Modal: save()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Active record style ... save(), delete() etc
|
||||
* Account account = new Account();
|
||||
* account.setName("AC234");
|
||||
*
|
||||
* // save() method inherited from Model
|
||||
* account.save();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Finder: find byId</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // find byId
|
||||
* Account account = Account.find.byId(42);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Finder: find where</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // find where ...
|
||||
* List<Account> accounts =
|
||||
* Account.find
|
||||
* .where().gt("startDate", lastMonth)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public abstract class Model {
|
||||
|
||||
/**
|
||||
* Return the underlying 'default' EbeanServer.
|
||||
*
|
||||
* <p>
|
||||
* This provides full access to the API such as explicit transaction demarcation etc.
|
||||
*
|
||||
* <p>
|
||||
* Example:
|
||||
* <pre class="code">
|
||||
* Transaction transaction = Customer.db().beginTransaction();
|
||||
* try {
|
||||
*
|
||||
* // turn off cascade persist for this transaction
|
||||
* transaction.setPersistCascade(false);
|
||||
*
|
||||
* // extra control over jdbc batching for this transaction
|
||||
* transaction.setBatchGetGeneratedKeys(false);
|
||||
* transaction.setBatchMode(true);
|
||||
* transaction.setBatchSize(20);
|
||||
*
|
||||
* Customer customer = new Customer();
|
||||
* customer.setName("Roberto");
|
||||
* customer.save();
|
||||
*
|
||||
* Customer otherCustomer = new Customer();
|
||||
* otherCustomer.setName("Franko");
|
||||
* otherCustomer.save();
|
||||
*
|
||||
* transaction.commit();
|
||||
*
|
||||
* } finally {
|
||||
* transaction.end();
|
||||
* }
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
public static EbeanServer db() {
|
||||
return Ebean.getServer(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a named EbeanServer that is typically different to the default server.
|
||||
*
|
||||
* <p>
|
||||
* If you are using multiple databases then each database has a name and maps to a single
|
||||
* EbeanServer. You can use this method to get an EbeanServer for another database.
|
||||
*
|
||||
* @param server
|
||||
* The name of the EbeanServer. If this is null then the default EbeanServer is returned.
|
||||
*/
|
||||
public static EbeanServer db(String server) {
|
||||
return Ebean.getServer(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the entity bean as dirty.
|
||||
* <p>
|
||||
* This is used so that when a bean that is otherwise unmodified is updated the version
|
||||
* property is updated.
|
||||
* <p>
|
||||
* An unmodified bean that is saved or updated is normally skipped and this marks the bean as
|
||||
* dirty so that it is not skipped.
|
||||
*
|
||||
* <pre class="code">
|
||||
*
|
||||
* Customer customer = Customer.find.byId(id);
|
||||
*
|
||||
* // mark the bean as dirty so that a save() or update() will
|
||||
* // increment the version property
|
||||
* customer.markAsDirty();
|
||||
* customer.save();
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @see EbeanServer#markAsDirty(Object)
|
||||
*/
|
||||
public void markAsDirty() {
|
||||
db().markAsDirty(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or update this entity depending on its state.
|
||||
*
|
||||
* <p>
|
||||
* Ebean will detect if this is a new bean or a previously fetched bean and perform either an
|
||||
* insert or an update based on that.
|
||||
*
|
||||
* @see EbeanServer#save(Object)
|
||||
*/
|
||||
public void save() {
|
||||
db().save(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this entity.
|
||||
*
|
||||
* @see EbeanServer#update(Object)
|
||||
*/
|
||||
public void update() {
|
||||
db().update(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert this entity.
|
||||
*
|
||||
* @see EbeanServer#insert(Object)
|
||||
*/
|
||||
public void insert() {
|
||||
db().insert(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete this entity.
|
||||
*
|
||||
* @see EbeanServer#delete(Object)
|
||||
*/
|
||||
public void delete() {
|
||||
db().delete(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an update using this entity against the specified server.
|
||||
*/
|
||||
public void update(String server) {
|
||||
db(server).update(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an insert using this entity against the specified server.
|
||||
*/
|
||||
public void insert(String server) {
|
||||
db(server).insert(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a delete using this entity against the specified server.
|
||||
*/
|
||||
public void delete(String server) {
|
||||
db(server).delete(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes this entity from the database.
|
||||
*
|
||||
* @see EbeanServer#refresh(Object)
|
||||
*/
|
||||
public void refresh() {
|
||||
db().refresh(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper object for performing queries.
|
||||
*
|
||||
* <p>
|
||||
* Typically a Finder is defined as a public static field on an entity bean class to provide a
|
||||
* nice way to write queries.
|
||||
*
|
||||
* @param <I>
|
||||
* The Id type. This is most often a {@link Long} but is also often a {@link UUID} or
|
||||
* {@link String}.
|
||||
*
|
||||
* @param <T>
|
||||
* The bean type
|
||||
*/
|
||||
public static class Finder<I, T> {
|
||||
|
||||
private final Class<I> idType;
|
||||
private final Class<T> type;
|
||||
private final String serverName;
|
||||
|
||||
/**
|
||||
* Creates a finder for entity of type <code>T</code> with ID of type <code>I</code>.
|
||||
*
|
||||
* <p>
|
||||
* Typically you use this constructor to have a static "find" field on each entity bean.
|
||||
*/
|
||||
public Finder(Class<I> idType, Class<T> type) {
|
||||
this(null, idType, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a finder for entity of type <code>T</code> with ID of type <code>I</code>, using a
|
||||
* specific EbeanServer.
|
||||
*
|
||||
* <p>
|
||||
* Typically you don't need to use this method.
|
||||
*/
|
||||
public Finder(String serverName, Class<I> idType, Class<T> type) {
|
||||
this.type = type;
|
||||
this.idType = idType;
|
||||
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 equivilent 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 a Finder for the named EbeanServer.
|
||||
*
|
||||
* <p>
|
||||
* Create and return a new Finder for a different server.
|
||||
*/
|
||||
public Finder<I, T> on(String server) {
|
||||
return new Finder<I, T>(server, idType, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* This is the same as (synonym for) {@link #findList()}
|
||||
*/
|
||||
public List<T> all() {
|
||||
return findList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an entity by ID.
|
||||
*
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#find(Class, Object)}
|
||||
*/
|
||||
public T byId(I id) {
|
||||
return db().find(type, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a filter for sorting and filtering lists of entities locally without going back to
|
||||
* the database.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#filter(Class)}
|
||||
*/
|
||||
public Filter<T> filter() {
|
||||
return db().filter(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#find(Class)}
|
||||
*/
|
||||
public Query<T> query() {
|
||||
return db().find(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query applying the path properties to set the select and fetch clauses.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#apply(com.avaje.ebean.text.PathProperties)}
|
||||
*/
|
||||
public Query<T> apply(PathProperties pathProperties) {
|
||||
return db().find(type).apply(pathProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next identity value.
|
||||
*
|
||||
* @see EbeanServer#nextId(Class)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public I nextId() {
|
||||
return (I) db().nextId(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a query and returns the results as a list of IDs.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#findIds()}
|
||||
*/
|
||||
public List<Object> findIds() {
|
||||
return query().findIds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query consuming each bean one at a time.
|
||||
* <p>
|
||||
* This is generally used to process large queries where unlike findList
|
||||
* you do not want to hold all the results in memory at once but instead
|
||||
* process them one at a time (requiring far less memory).
|
||||
* </p>
|
||||
* Equivalent to {@link Query#findEach(QueryEachConsumer)}
|
||||
*/
|
||||
public void findEach(QueryEachConsumer<T> consumer) {
|
||||
query().findEach(consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query consuming each bean one at a time.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#findEachWhile(QueryEachWhileConsumer)}
|
||||
* <p>
|
||||
* This is similar to #findEach except that you return boolean
|
||||
* true to continue processing beans and return false to stop
|
||||
* processing early.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is generally used to process large queries where unlike findList
|
||||
* you do not want to hold all the results in memory at once but instead
|
||||
* process them one at a time (requiring far less memory).
|
||||
* </p>
|
||||
* Equivalent to {@link Query#findEachWhile(QueryEachWhileConsumer)}
|
||||
*/
|
||||
public void findEachWhile(QueryEachWhileConsumer<T> consumer) {
|
||||
query().findEachWhile(consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all entities of the given type.
|
||||
* <p>
|
||||
* The same as {@link #all()}
|
||||
* <p>
|
||||
* Equivalent to {@link Query#findList()}
|
||||
*/
|
||||
public List<T> findList() {
|
||||
return query().findList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the entities of the given type as a set.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#findSet()}
|
||||
*/
|
||||
public Set<T> findSet() {
|
||||
return query().findSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all entities of the given type as a map of objects.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#findMap()}
|
||||
*/
|
||||
public Map<?, T> findMap() {
|
||||
return query().findMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query and returns the results as a map of the objects specifying the map key
|
||||
* property.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#findMap(String, Class)}
|
||||
*/
|
||||
public <K> Map<K, T> findMap(String keyProperty, Class<K> keyType) {
|
||||
return query().findMap(keyProperty, keyType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a PagedList of all entities of the given type (use where() to specify predicates as
|
||||
* needed).
|
||||
* <p>
|
||||
* Equivalent to {@link Query#findPagedList(int, int)}
|
||||
*/
|
||||
public PagedList<T> findPagedList(int pageIndex, int pageSize) {
|
||||
return query().findPagedList(pageIndex, pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a find row count query in a background thread.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#findFutureRowCount()}
|
||||
*/
|
||||
public FutureRowCount<T> findFutureRowCount() {
|
||||
return query().findFutureRowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of entities for this type. *
|
||||
* <p>
|
||||
* Equivalent to {@link Query#findRowCount()}
|
||||
*/
|
||||
public int findRowCount() {
|
||||
return query().findRowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>ExpressionFactory</code> used by this query.
|
||||
*/
|
||||
public ExpressionFactory getExpressionFactory() {
|
||||
return query().getExpressionFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly sets a comma delimited list of the properties to fetch on the 'main' entity bean,
|
||||
* to load a partial object.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#select(String)}
|
||||
*/
|
||||
public Query<T> select(String fetchProperties) {
|
||||
return query().select(fetchProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a path to load including all its properties.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#fetch(String)}
|
||||
*/
|
||||
public Query<T> fetch(String path) {
|
||||
return query().fetch(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Additionally specifies a <code>FetchConfig</code> to specify a 'query join' and/or define the
|
||||
* lazy loading query.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#fetch(String, FetchConfig)}
|
||||
*/
|
||||
public Query<T> fetch(String path, FetchConfig joinConfig) {
|
||||
return query().fetch(path, joinConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a path to fetch with a specific list properties to include, to load a partial
|
||||
* object.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#fetch(String, String)}
|
||||
*/
|
||||
public Query<T> fetch(String path, String fetchProperties) {
|
||||
return query().fetch(path, fetchProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Additionally specifies a <code>FetchConfig</code> to use a separate query or lazy loading to
|
||||
* load this path.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#fetch(String, String, FetchConfig)}
|
||||
*/
|
||||
public Query<T> fetch(String assocProperty, String fetchProperties, FetchConfig fetchConfig) {
|
||||
return query().fetch(assocProperty, fetchProperties, fetchConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds expressions to the <code>where</code> clause with the ability to chain on the
|
||||
* <code>ExpressionList</code>.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#where()}
|
||||
*/
|
||||
public ExpressionList<T> where() {
|
||||
return query().where();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>order by</code> clause so that you can append an ascending or descending
|
||||
* property to the <code>order by</code> clause.
|
||||
* <p>
|
||||
* This is exactly the same as {@link #orderBy}.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#order()}
|
||||
*/
|
||||
public OrderBy<T> order() {
|
||||
return query().order();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <code>order by</code> clause, replacing the existing <code>order by</code> clause if
|
||||
* there is one.
|
||||
* <p>
|
||||
* This is exactly the same as {@link #orderBy(String)}.
|
||||
*/
|
||||
public Query<T> order(String orderByClause) {
|
||||
return query().order(orderByClause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>order by</code> clause so that you can append an ascending or descending
|
||||
* property to the <code>order by</code> clause.
|
||||
* <p>
|
||||
* This is exactly the same as {@link #order}.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#orderBy()}
|
||||
*/
|
||||
public OrderBy<T> orderBy() {
|
||||
return query().orderBy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the <code>order by</code> clause replacing the existing <code>order by</code> clause if
|
||||
* there is one.
|
||||
* <p>
|
||||
* This is exactly the same as {@link #order(String)}.
|
||||
*/
|
||||
public Query<T> orderBy(String orderByClause) {
|
||||
return query().orderBy(orderByClause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the first row to return for this query.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#setFirstRow(int)}
|
||||
*/
|
||||
public Query<T> setFirstRow(int firstRow) {
|
||||
return query().setFirstRow(firstRow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum number of rows to return in the query.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#setMaxRows(int)}
|
||||
*/
|
||||
public Query<T> setMaxRows(int maxRows) {
|
||||
return query().setMaxRows(maxRows);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ID value to query.
|
||||
*
|
||||
* <p>
|
||||
* Use this to perform a find byId query but with additional control over the query such as
|
||||
* using select and fetch to control what parts of the object graph are returned.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#setId(Object)}
|
||||
*/
|
||||
public Query<T> setId(Object id) {
|
||||
return query().setId(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new query using the OQL.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#createQuery(Class, String)}
|
||||
*/
|
||||
public Query<T> setQuery(String oql) {
|
||||
return db().createQuery(type, oql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new query based on the <code>RawSql</code>.
|
||||
* <p>
|
||||
* Equivalent to {@link Query#setRawSql(RawSql)}
|
||||
*/
|
||||
public Query<T> setRawSql(RawSql rawSql) {
|
||||
return query().setRawSql(rawSql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query with explicit 'Autofetch' use.
|
||||
*/
|
||||
public Query<T> setAutofetch(boolean autofetch) {
|
||||
return query().setAutofetch(autofetch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query with the select with "for update" specified.
|
||||
*
|
||||
* <p>
|
||||
* This will typically create row level database locks on the selected rows.
|
||||
*/
|
||||
public Query<T> setForUpdate(boolean forUpdate) {
|
||||
return query().setForUpdate(forUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query specifying whether the returned beans will be read-only.
|
||||
*/
|
||||
public Query<T> setReadOnly(boolean readOnly) {
|
||||
return query().setReadOnly(readOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query specifying if the beans should be loaded into the L2 cache.
|
||||
*/
|
||||
public Query<T> setLoadBeanCache(boolean loadBeanCache) {
|
||||
return query().setLoadBeanCache(loadBeanCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query specifying if the L2 bean cache should be used.
|
||||
*/
|
||||
public Query<T> setUseCache(boolean useBeanCache) {
|
||||
return query().setUseCache(useBeanCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query specifying if the L2 query cache should be used.
|
||||
*/
|
||||
public Query<T> setUseQueryCache(boolean useQueryCache) {
|
||||
return query().setUseQueryCache(useQueryCache);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a Page of results that is part of a PagingList.
|
||||
* <p>
|
||||
* Typically a Page represents the data that is shown to the user at a single
|
||||
* time - and the user 'pages' through a large list.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*
|
||||
* @param <T>
|
||||
* the entity bean type
|
||||
*
|
||||
* @see Query#findPagingList(int)
|
||||
* @see PagingList
|
||||
*/
|
||||
public interface Page<T> {
|
||||
|
||||
/**
|
||||
* Return the list of entities for this page.
|
||||
*/
|
||||
public List<T> getList();
|
||||
|
||||
/**
|
||||
* Return the total row count for all pages.
|
||||
*/
|
||||
public int getTotalRowCount();
|
||||
|
||||
/**
|
||||
* Return the total number of pages.
|
||||
*/
|
||||
public int getTotalPageCount();
|
||||
|
||||
/**
|
||||
* Return the index position of this page.
|
||||
*/
|
||||
public int getPageIndex();
|
||||
|
||||
/**
|
||||
* Return true if there is a next page.
|
||||
*/
|
||||
public boolean hasNext();
|
||||
|
||||
/**
|
||||
* Return true if there is a previous page.
|
||||
*/
|
||||
public boolean hasPrev();
|
||||
|
||||
/**
|
||||
* Return the next page.
|
||||
*/
|
||||
public Page<T> next();
|
||||
|
||||
/**
|
||||
* Return the previous page.
|
||||
*/
|
||||
public Page<T> prev();
|
||||
|
||||
/**
|
||||
* Helper method to return a "X to Y of Z" string for this page where X is the
|
||||
* first row, Y the last row and Z the total row count.
|
||||
*
|
||||
* @param to
|
||||
* String to put between the first and last row
|
||||
* @param of
|
||||
* String to put between the last row and the total row count
|
||||
*
|
||||
* @return String of the format XtoYofZ.
|
||||
*/
|
||||
public String getDisplayXtoYofZ(String to, String of);
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* Represents a page of results.
|
||||
* <p>
|
||||
* The benefit of using PagedList over just using the normal Query with
|
||||
* {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} is that it additionally wraps
|
||||
* functionality that can call {@link Query#findFutureRowCount()} to determine total row count,
|
||||
* total page count etc.
|
||||
* </p>
|
||||
* <p>
|
||||
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
|
||||
* the query. This translates into SQL that uses limit offset, rownum or row_number function to
|
||||
* limit the result set.
|
||||
* </p>
|
||||
*
|
||||
* <h4>Example: typical use including total row count</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // We want to find the first 100 new orders
|
||||
* // ... 0 means first page
|
||||
* // ... page size is 100
|
||||
*
|
||||
* PagedList<Order> pagedList
|
||||
* = ebeanServer.find(Order.class)
|
||||
* .where().eq("status", Order.Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .findPagedList(0, 100);
|
||||
*
|
||||
* // Optional: initiate the loading of the total
|
||||
* // row count in a background thread
|
||||
* pagedList.loadRowCount();
|
||||
*
|
||||
* // fetch and return the list in the foreground thread
|
||||
* List<Order> orders = pagedList.getList();
|
||||
*
|
||||
* // get the total row count (from the future)
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h4>Example: No total row count required</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // If you are not getting the 'first page' often
|
||||
* // you do not bother getting the total row count again
|
||||
* // so instead just get the page list of data
|
||||
*
|
||||
* // fetch and return the list in the foreground thread
|
||||
* List<Order> orders = pagedList.getList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T>
|
||||
* the entity bean type
|
||||
*
|
||||
* @see Query#findPagedList(int, int)
|
||||
*/
|
||||
public interface PagedList<T> {
|
||||
|
||||
/**
|
||||
* Initiate the loading of the total row count in the background.
|
||||
* <pre>{@code
|
||||
*
|
||||
* // initiate the loading of the total row count
|
||||
* // in a background thread
|
||||
* pagedList.loadRowCount();
|
||||
*
|
||||
* // fetch and return the list in the foreground thread
|
||||
* List<Order> orders = pagedList.getList();
|
||||
*
|
||||
* // get the total row count (from the future)
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Also note that using loadRowCount() and getTotalRowCount() rather than getFutureRowCount()
|
||||
* means that exceptions ExecutionException, InterruptedException, TimeoutException are instead
|
||||
* wrapped in the unchecked PersistenceException (which might be preferrable).
|
||||
* </p>
|
||||
*/
|
||||
public void loadRowCount();
|
||||
|
||||
/**
|
||||
* Return the Future row count. You might get this if you wish to cancel the total row count query
|
||||
* or specify a timeout for the row count query.
|
||||
* <p>
|
||||
* The loadRowCount() & getTotalRowCount() methods internally make use of this getFutureRowCount() method.
|
||||
* Generally I expect people to prefer loadRowCount() & getTotalRowCount() over getFutureRowCount().
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // initiate the row count query in the background thread
|
||||
* Future<Integer> rowCount = pagedList.getFutureRowCount();
|
||||
*
|
||||
* // fetch and return the list in the foreground thread
|
||||
* List<Order> orders = pagedList.getList();
|
||||
*
|
||||
* // now get the total count with a timeout
|
||||
* Integer totalRowCount = rowCount.get(30, TimeUnit.SECONDS);
|
||||
*
|
||||
* // or ge the total count without a timeout
|
||||
* Integer totalRowCountViaFuture = rowCount.get();
|
||||
*
|
||||
* // which is actually the same as ...
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public Future<Integer> getFutureRowCount();
|
||||
|
||||
/**
|
||||
* Return the list of entities for this page.
|
||||
*/
|
||||
public List<T> getList();
|
||||
|
||||
/**
|
||||
* Return the total row count for all pages.
|
||||
* <p>
|
||||
* If loadRowCount() has already been called then the row count query is already executing in a background thread
|
||||
* and this gets the associated Future and gets the value waiting for the future to finish.
|
||||
* </p>
|
||||
* <p>
|
||||
* If loadRowCount() has not been called then this executes the find row count query and returns the result and this
|
||||
* will just occur in the current thread and not use a background thread.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Optional: initiate the loading of the total
|
||||
* // row count in a background thread
|
||||
* pagedList.loadRowCount();
|
||||
*
|
||||
* // fetch and return the list in the foreground thread
|
||||
* List<Order> orders = pagedList.getList();
|
||||
*
|
||||
* // get the total row count (which was being executed
|
||||
* // in a background thread if loadRowCount() was used)
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public int getTotalRowCount();
|
||||
|
||||
/**
|
||||
* Return the total number of pages based on the page size and total row count.
|
||||
* <p>
|
||||
* This method requires that the total row count has been fetched and will invoke
|
||||
* the total row count query if it has not already been invoked.
|
||||
* </p>
|
||||
*/
|
||||
public int getTotalPageCount();
|
||||
|
||||
/**
|
||||
* Return the index position of this page. Zero based.
|
||||
*/
|
||||
public int getPageIndex();
|
||||
|
||||
/**
|
||||
* Return true if there is a next page.
|
||||
* <p>
|
||||
* This method requires that the total row count has been fetched and will invoke
|
||||
* the total row count query if it has not already been invoked.
|
||||
* </p>
|
||||
*/
|
||||
public boolean hasNext();
|
||||
|
||||
/**
|
||||
* Return true if there is a previous page.
|
||||
*/
|
||||
public boolean hasPrev();
|
||||
|
||||
/**
|
||||
* Helper method to return a "X to Y of Z" string for this page where X is the first row, Y the
|
||||
* last row and Z the total row count.
|
||||
* <p>
|
||||
* This method requires that the total row count has been fetched and will invoke
|
||||
* the total row count query if it has not already been invoked.
|
||||
* </p>
|
||||
*
|
||||
* @param to
|
||||
* String to put between the first and last row
|
||||
* @param of
|
||||
* String to put between the last row and the total row count
|
||||
*
|
||||
* @return String of the format XtoYofZ.
|
||||
*/
|
||||
public String getDisplayXtoYofZ(String to, String of);
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* Used to page through a query result rather than fetching all the results in a
|
||||
* single query.
|
||||
* <p>
|
||||
* Has the ability to use background threads to 'fetch ahead' the next page and
|
||||
* get the total row count.
|
||||
* </p>
|
||||
* <p>
|
||||
* If you are building a stateless web application and not keeping the
|
||||
* PagingList over multiple requests then there is not much to be gained in
|
||||
* using PagingList. Instead you can just use {@link Query#setFirstRow(int)} and
|
||||
* {@link Query#setMaxRows(int)}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If you are using PagingList is a stateful web application where the
|
||||
* PagingList is held over multiple requests then PagingList provides the extra
|
||||
* benefits of
|
||||
* <ul>
|
||||
* <li>Fetch ahead - automatically fetching the next page via background query
|
||||
* execution</li>
|
||||
* <li>Automatic propagation of the persistence context</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* So with PagingList when you use Page 2 it can automatically fetch Page 3 data
|
||||
* in the background (using a findFutureList() query). It also automatically
|
||||
* propagates the persistence context so that all the queries executed by the
|
||||
* PagingList all use the same persistence context.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* PagingList<TOne> pagingList =
|
||||
* Ebean.find(TOne.class)
|
||||
* .where().gt("name", "2")
|
||||
* .findPagingList(10);
|
||||
*
|
||||
* // get the row count in the background...
|
||||
* // ... otherwise it is fetched on demand
|
||||
* // ... when getRowCount() or getPageCount()
|
||||
* // ... is called
|
||||
* pagingList.getFutureRowCount();
|
||||
*
|
||||
* // get the first page
|
||||
* Page<TOne> page = pagingList.getPage(0);
|
||||
*
|
||||
* // get the beans from the page as a list
|
||||
* List<TOne> list = page.getList();
|
||||
* </pre>
|
||||
*
|
||||
* @author rbygrave
|
||||
*
|
||||
* @param <T>
|
||||
* the entity bean type
|
||||
*/
|
||||
public interface PagingList<T> {
|
||||
|
||||
/**
|
||||
* Refresh will clear all the pages and row count forcing them to be
|
||||
* re-fetched when next required.
|
||||
*/
|
||||
public void refresh();
|
||||
|
||||
// public void fetchAll();
|
||||
// public String? getOrderBy();
|
||||
// public void setOrderBy(String?);
|
||||
|
||||
/**
|
||||
* By default fetchAhead is true so use this to turn off fetchAhead.
|
||||
* <p>
|
||||
* Set this to false if you don't want to fetch ahead using background
|
||||
* fetching.
|
||||
* <p>
|
||||
* If set to true (or left as to default) then the next page is fetched in the
|
||||
* background as soon as the list is accessed.
|
||||
* </p>
|
||||
*/
|
||||
public PagingList<T> setFetchAhead(boolean fetchAhead);
|
||||
|
||||
/**
|
||||
* Return the Future for getting the total row count.
|
||||
*/
|
||||
public Future<Integer> getFutureRowCount();
|
||||
|
||||
/**
|
||||
* Return the data for all the pages in the form of a single List.
|
||||
* <p>
|
||||
* Iterating through this list will automatically fire the paging queries as
|
||||
* required.
|
||||
* </p>
|
||||
*/
|
||||
public List<T> getAsList();
|
||||
|
||||
/**
|
||||
* Return the page size. This is the number of rows per page.
|
||||
*/
|
||||
public int getPageSize();
|
||||
|
||||
/**
|
||||
* Return the total row count.
|
||||
* <p>
|
||||
* This gets the result from getFutureRowCount and will wait until that query
|
||||
* has completed.
|
||||
* </p>
|
||||
*/
|
||||
public int getTotalRowCount();
|
||||
|
||||
/**
|
||||
* Return the total page count.
|
||||
* <p>
|
||||
* This is based on the total row count. This will wait until the row count
|
||||
* has returned if it has not already.
|
||||
* </p>
|
||||
*/
|
||||
public int getTotalPageCount();
|
||||
|
||||
/**
|
||||
* Return the page for a given page position (starting at 0).
|
||||
*/
|
||||
public Page<T> getPage(int i);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* Defines the scope for PersistenceContext.
|
||||
* <p/>
|
||||
* Ebean has traditionally used Transaction scope for the PersistenceContext. This is used to change the scope to
|
||||
* use (by default) and explicitly set the scope to use for an individual query.
|
||||
*
|
||||
* @see com.avaje.ebean.config.ServerConfig#setPersistenceContextScope(PersistenceContextScope)
|
||||
* @see com.avaje.ebean.Query#setPersistenceContextScope(PersistenceContextScope)
|
||||
*/
|
||||
public enum PersistenceContextScope {
|
||||
|
||||
/**
|
||||
* PersistenceContext is scoped to the transaction.
|
||||
* <p/>
|
||||
* If a transaction spans 2 or more queries that fetch the same bean in terms of same type
|
||||
* and same Id value then they share the same bean instance.
|
||||
* <p/>
|
||||
* You may want to change to use QUERY scope when you want a query executing in a transaction to effectively
|
||||
* ignore beans that have already been loaded (by other queries in the same transaction) and instead get a
|
||||
* 'fresh copy' of the bean.
|
||||
*/
|
||||
TRANSACTION,
|
||||
|
||||
/**
|
||||
* PersistenceContext is scoped to the query.
|
||||
* <p/>
|
||||
* This means that for this query running in an existing transaction then it will effectively ignore any beans
|
||||
* that have already been queried/loaded by prior queries in the same transaction.
|
||||
* <p/>
|
||||
* You may use QUERY scope on a query that is executed in a transaction and you want to get a 'fresh copy' of the bean.
|
||||
*/
|
||||
QUERY
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.config.PropertyMap;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Provides singleton state for the default server.
|
||||
* <p/>
|
||||
* Intended for internal use as part of bootup, construction, registration of the default server.
|
||||
*/
|
||||
class PrimaryServer {
|
||||
|
||||
private static Properties globalProperties;
|
||||
|
||||
private static String defaultServerName;
|
||||
|
||||
private static boolean skip;
|
||||
|
||||
/**
|
||||
* Set whether to skip automatically creating the primary server.
|
||||
*/
|
||||
static synchronized void setSkip(boolean skip) {
|
||||
PrimaryServer.skip = skip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true to skip automatically creating the primary server.
|
||||
*/
|
||||
static synchronized boolean isSkip() {
|
||||
return skip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default server name.
|
||||
*/
|
||||
static synchronized String getPrimaryServerName() {
|
||||
getProperties();
|
||||
return defaultServerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default configuration Properties.
|
||||
*/
|
||||
static synchronized Properties getProperties() {
|
||||
if (globalProperties == null) {
|
||||
globalProperties = PropertyMap.defaultProperties();
|
||||
}
|
||||
defaultServerName = globalProperties.getProperty("datasource.default");
|
||||
if (defaultServerName == null) {
|
||||
defaultServerName = globalProperties.getProperty("ebean.default.datasource");
|
||||
}
|
||||
return globalProperties;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -11,54 +13,57 @@ import java.util.Set;
|
||||
* Example: Create the query using the API.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* List<Order> orderList =
|
||||
* Ebean.find(Order.class)
|
||||
* .fetch("customer")
|
||||
* .fetch("details")
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Order> orderList =
|
||||
* ebeanServer.find(Order.class)
|
||||
* .fetch("customer")
|
||||
* .fetch("details")
|
||||
* .where()
|
||||
* .like("customer.name","rob%")
|
||||
* .gt("orderDate",lastWeek)
|
||||
* .orderBy("customer.id, id desc")
|
||||
* .like("customer.name","rob%")
|
||||
* .gt("orderDate",lastWeek)
|
||||
* .orderBy("customer.id, id desc")
|
||||
* .setMaxRows(50)
|
||||
* .findList();
|
||||
*
|
||||
* ...
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Example: The same query using the query language
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* String oql =
|
||||
* " find order "
|
||||
* +" fetch customer "
|
||||
* +" fetch details "
|
||||
* +" where customer.name like :custName and orderDate > :minOrderDate "
|
||||
* +" order by customer.id, id desc "
|
||||
* +" limit 50 ";
|
||||
* " find order "
|
||||
* +" fetch customer "
|
||||
* +" fetch details "
|
||||
* +" where customer.name like :custName and orderDate > :minOrderDate "
|
||||
* +" order by customer.id, id desc "
|
||||
* +" limit 50 ";
|
||||
*
|
||||
* Query<Order> query = Ebean.createQuery(Order.class, oql);
|
||||
* query.setParameter("custName", "Rob%");
|
||||
* query.setParameter("minOrderDate", lastWeek);
|
||||
* Query<Order> query = ebeanServer.createQuery(Order.class, oql);
|
||||
* query.setParameter("custName", "Rob%");
|
||||
* query.setParameter("minOrderDate", lastWeek);
|
||||
*
|
||||
* List<Order> orderList = query.findList();
|
||||
* List<Order> orderList = query.findList();
|
||||
* ...
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Example: Using a named query called "with.cust.and.details"
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* Query<Order> query = Ebean.createNamedQuery(Order.class,"with.cust.and.details");
|
||||
* query.setParameter("custName", "Rob%");
|
||||
* query.setParameter("minOrderDate", lastWeek);
|
||||
* <pre>{@code
|
||||
*
|
||||
* Query<Order> query = ebeanServer.createNamedQuery(Order.class,"with.cust.and.details");
|
||||
* query.setParameter("custName", "Rob%");
|
||||
* query.setParameter("minOrderDate", lastWeek);
|
||||
*
|
||||
* List<Order> orderList = query.findList();
|
||||
* List<Order> orderList = query.findList();
|
||||
* ...
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Autofetch</h3>
|
||||
* <p>
|
||||
@@ -96,13 +101,13 @@ import java.util.Set;
|
||||
* Refer to "ALL Properties/Columns" mode of Optimistic Concurrency checking.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* [ find {bean type} [ ( * | {fetch properties} ) ] ]
|
||||
* [ fetch {associated bean} [ ( * | {fetch properties} ) ] ]
|
||||
* [ where {predicates} ]
|
||||
* [ order by {order by properties} ]
|
||||
* [ limit {max rows} [ offset {first row} ] ]
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* <b>FIND</b> <b>{bean type}</b> [ ( <i>*</i> | <i>{fetch properties}</i> ) ]
|
||||
@@ -158,17 +163,17 @@ import java.util.Set;
|
||||
* Find orders fetching all its properties
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* find order
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Find orders fetching all its properties
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* find order (*)
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Find orders fetching its id, shipDate and status properties. Note that the id
|
||||
@@ -176,30 +181,30 @@ import java.util.Set;
|
||||
* properties.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* find order (shipDate, status)
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Find orders with a named bind variable (that will need to be bound via
|
||||
* {@link Query#setParameter(String, Object)}).
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* find order
|
||||
* where customer.name like :custLike
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Find orders and also fetch the customer with a named bind parameter. This
|
||||
* will fetch and populate both the order and customer objects.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* find order
|
||||
* fetch customer
|
||||
* where customer.id = :custId
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Find orders and also fetch the customer, customer shippingAddress, order
|
||||
@@ -210,13 +215,13 @@ import java.util.Set;
|
||||
* populated.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* find order
|
||||
* fetch customer (name)
|
||||
* fetch customer.shippingAddress
|
||||
* fetch details
|
||||
* fetch details.product (sku, name)
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Early parsing of the Query</h3>
|
||||
* <p>
|
||||
@@ -299,6 +304,21 @@ public interface Query<T> extends Serializable {
|
||||
*/
|
||||
public Query<T> copy();
|
||||
|
||||
/**
|
||||
* Specify the PersistenceContextScope to use for this query.
|
||||
* <p/>
|
||||
* When this is not set the 'default' configured on {@link com.avaje.ebean.config.ServerConfig#setPersistenceContextScope(PersistenceContextScope)}
|
||||
* is used - this value defaults to {@link com.avaje.ebean.PersistenceContextScope#TRANSACTION}.
|
||||
* <p/>
|
||||
* Note that the same persistence Context is used for subsequent lazy loading and query join queries.
|
||||
* <p/>
|
||||
* Note that #findEach uses a 'per object graph' PersistenceContext so this scope is ignored for
|
||||
* queries executed as #findIterate, #findEach, #findEachWhile.
|
||||
*
|
||||
* @param scope The scope to use for this query and subsequent lazy loading.
|
||||
*/
|
||||
public Query<T> setPersistenceContextScope(PersistenceContextScope scope);
|
||||
|
||||
/**
|
||||
* Return the ExpressionFactory used by this query.
|
||||
*/
|
||||
@@ -323,22 +343,37 @@ public interface Query<T> extends Serializable {
|
||||
*/
|
||||
public Query<T> setAutofetch(boolean autofetch);
|
||||
|
||||
/**
|
||||
* Set the default lazy loading batch size to use.
|
||||
* <p>
|
||||
* When lazy loading is invoked on beans loaded by this query then this sets the
|
||||
* batch size used to load those beans.
|
||||
*
|
||||
* @param lazyLoadBatchSize the number of beans to lazy load in a single batch
|
||||
*/
|
||||
public Query<T> setLazyLoadBatchSize(int lazyLoadBatchSize);
|
||||
|
||||
/**
|
||||
* Explicitly set a comma delimited list of the properties to fetch on the
|
||||
* 'main' entity bean (aka partial object). Note that '*' means all
|
||||
* 'main' root level entity bean (aka partial object). Note that '*' means all
|
||||
* properties.
|
||||
*
|
||||
* <pre class="code">
|
||||
* Query<Customer> query = Ebean.createQuery(Customer.class);
|
||||
*
|
||||
* // Only fetch the customer id, name and status.
|
||||
* // This is described as a "Partial Object"
|
||||
* query.select("name, status");
|
||||
* query.where("lower(name) like :custname").setParameter("custname", "rob%");
|
||||
*
|
||||
* List<Customer> customerList = query.findList();
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* You use {@link #fetch(String, String)} to specify specific properties to fetch
|
||||
* on other non-root level paths of the object graph.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* // Only fetch the customer id, name and status.
|
||||
* // This is described as a "Partial Object"
|
||||
* .select("name, status")
|
||||
* .where.ilike("name", "rob%")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param fetchProperties
|
||||
* the properties to fetch for this bean (* = all properties).
|
||||
*/
|
||||
@@ -356,31 +391,35 @@ public interface Query<T> extends Serializable {
|
||||
* "Partial Object" - a bean that only has some of its properties populated.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* // query orders...
|
||||
* Query<Order> query = Ebean.createQuery(Order.class);
|
||||
* List<Order> orders =
|
||||
* ebeanserver.find(Order.class)
|
||||
* // fetch the customer...
|
||||
* // ... getting the customers name and phone number
|
||||
* .fetch("customer", "name, phoneNumber")
|
||||
*
|
||||
* // fetch the customer...
|
||||
* // ... getting the customer's name and phone number
|
||||
* query.fetch("customer", "name, phNumber");
|
||||
*
|
||||
* // ... also fetch the customers billing address (* = all properties)
|
||||
* query.fetch("customer.billingAddress", "*");
|
||||
* </pre>
|
||||
* // ... also fetch the customers billing address (* = all properties)
|
||||
* .fetch("customer.billingAddress", "*")
|
||||
* .findList();
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* If columns is null or "*" then all columns/properties for that path are
|
||||
* fetched.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch customers (their id, name and status)
|
||||
* Query<Customer> query = Ebean.createQuery(Customer.class);
|
||||
*
|
||||
* // only fetch some of the properties of the customers
|
||||
* query.select("name, status");
|
||||
* List<Customer> list = query.findList();
|
||||
* </pre>
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* .select("name, status")
|
||||
* .fetch("contacts", "firstName,lastName,email")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param path
|
||||
* the path of an associated (1-1,1-M,M-1,M-M) bean.
|
||||
@@ -393,6 +432,17 @@ public interface Query<T> extends Serializable {
|
||||
/**
|
||||
* Additionally specify a FetchConfig to use a separate query or lazy loading
|
||||
* to load this path.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* .select("name, status")
|
||||
* .fetch("contacts", "firstName,lastName,email", new FetchConfig().lazy(10))
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public Query<T> fetch(String assocProperty, String fetchProperties, FetchConfig fetchConfig);
|
||||
|
||||
@@ -401,7 +451,17 @@ public interface Query<T> extends Serializable {
|
||||
* <p>
|
||||
* The same as {@link #fetch(String, String)} with the fetchProperties as "*".
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* // eager fetch the contacts
|
||||
* .fetch("contacts")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param path
|
||||
* the property of an associated (1-1,1-M,M-1,M-M) bean.
|
||||
*/
|
||||
@@ -410,9 +470,29 @@ public interface Query<T> extends Serializable {
|
||||
/**
|
||||
* Additionally specify a JoinConfig to specify a "query join" and or define
|
||||
* the lazy loading query.
|
||||
*
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* // lazy fetch contacts with a batch size of 100
|
||||
* .fetch("contacts", new FetchConfig().lazy(100))
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public Query<T> fetch(String path, FetchConfig joinConfig);
|
||||
|
||||
/**
|
||||
* Apply the path properties replacing the select and fetch clauses.
|
||||
* <p>
|
||||
* This is typically used when the PathProperties is applied to both the query and the JSON output.
|
||||
* </p>
|
||||
*/
|
||||
public Query<T> apply(PathProperties pathProperties);
|
||||
|
||||
/**
|
||||
* Execute the query returning the list of Id's.
|
||||
* <p>
|
||||
@@ -431,49 +511,119 @@ public interface Query<T> extends Serializable {
|
||||
* (typically in a finally block).
|
||||
* </p>
|
||||
* <p>
|
||||
* findEach() and findEachWhile() are preferred to findIterate() as they ensure
|
||||
* the jdbc statement and resultSet are closed at the end of the iteration.
|
||||
* </p>
|
||||
* <p>
|
||||
* This query will execute against the EbeanServer that was used to create it.
|
||||
* </p>
|
||||
*/
|
||||
public QueryIterator<T> findIterate();
|
||||
|
||||
/**
|
||||
* This is deprecated in favor of #findEachWhile.
|
||||
* <p>
|
||||
* This is functionally exactly the same as #findEachWhile. It is
|
||||
* replaced by findEachWhile because the method name is much better.
|
||||
* </p>
|
||||
*
|
||||
* @param visitor
|
||||
* the visitor used to process the queried beans.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public void findVisit(QueryResultVisitor<T> visitor);
|
||||
|
||||
/**
|
||||
* Execute the query processing the beans one at a time.
|
||||
* <p>
|
||||
* This method is appropriate to process very large query results as the
|
||||
* beans are consumed one at a time and do not need to be held in memory
|
||||
* (unlike #findList #findSet etc)
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that internally Ebean can inform the JDBC driver that it is expecting larger
|
||||
* resultSet and specifically for MySQL this hint is required to stop it's JDBC driver
|
||||
* from buffering the entire resultSet. As such, for smaller resultSets findList() is
|
||||
* generally preferable.
|
||||
* </p>
|
||||
* <p>
|
||||
* Compared with #findEachWhile this will always process all the beans where as
|
||||
* #findEachWhile provides a way to stop processing the query result early before
|
||||
* all the beans have been read.
|
||||
* </p>
|
||||
* <p>
|
||||
* This method is functionally equivalent to findIterate() but instead of using an
|
||||
* iterator uses the QueryEachConsumer (SAM) interface which is better suited to use
|
||||
* with Java8 closures.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* ebeanServer.find(Customer.class)
|
||||
* .where().eq("status", Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .findEach((Customer customer) -> {
|
||||
*
|
||||
* // do something with customer
|
||||
* System.out.println("-- visit " + customer);
|
||||
* });
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param consumer
|
||||
* the consumer used to process the queried beans.
|
||||
*/
|
||||
public void findEach(QueryEachConsumer<T> consumer);
|
||||
|
||||
/**
|
||||
* Execute the query using callbacks to a visitor to process the resulting
|
||||
* beans one at a time.
|
||||
* <p>
|
||||
* Similar to findIterate() this query method does not require all the result
|
||||
* beans to be all held in memory at once and as such is useful for processing
|
||||
* large queries.
|
||||
* This method is functionally equivalent to findIterate() but instead of using an
|
||||
* iterator uses the QueryEachWhileConsumer (SAM) interface which is better suited to use
|
||||
* with Java8 closures.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
*
|
||||
* Query<Customer> query = server.find(Customer.class)
|
||||
* .fetch("contacts", new FetchConfig().query(2))
|
||||
* .where().gt("id", 0)
|
||||
* .orderBy("id")
|
||||
* .setMaxRows(2);
|
||||
*
|
||||
* query.findVisit(new QueryResultVisitor<Customer>() {
|
||||
*
|
||||
* public boolean accept(Customer customer) {
|
||||
* // do something with customer
|
||||
* System.out.println("-- visit " + customer);
|
||||
* return true;
|
||||
* }
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param visitor
|
||||
* the visitor used to process the queried beans.
|
||||
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* ebeanServer.find(Customer.class)
|
||||
* .fetch("contacts", new FetchConfig().query(2))
|
||||
* .where().eq("status", Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .setMaxRows(2000)
|
||||
* .findEachWhile((Customer customer) -> {
|
||||
*
|
||||
* // do something with customer
|
||||
* System.out.println("-- visit " + customer);
|
||||
*
|
||||
* // return true to continue processing or false to stop
|
||||
* return (customer.getId() < 40);
|
||||
* });
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param consumer
|
||||
* the consumer used to process the queried beans.
|
||||
*/
|
||||
public void findVisit(QueryResultVisitor<T> visitor);
|
||||
public void findEachWhile(QueryEachWhileConsumer<T> consumer);
|
||||
|
||||
/**
|
||||
* Execute the query returning the list of objects.
|
||||
* <p>
|
||||
* This query will execute against the EbeanServer that was used to create it.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* .where().ilike("name", "rob%")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see EbeanServer#findList(Query, Transaction)
|
||||
*/
|
||||
public List<T> findList();
|
||||
@@ -483,7 +633,16 @@ public interface Query<T> extends Serializable {
|
||||
* <p>
|
||||
* This query will execute against the EbeanServer that was used to create it.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* Set<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* .where().ilike("name", "rob%")
|
||||
* .findSet();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see EbeanServer#findSet(Query, Transaction)
|
||||
*/
|
||||
public Set<T> findSet();
|
||||
@@ -498,11 +657,14 @@ public interface Query<T> extends Serializable {
|
||||
* on the map. If one is not specified then the id property is used.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* Query<Product> query = Ebean.createQuery(Product.class);
|
||||
* query.setMapKey("sku");
|
||||
* Map<?, Product> map = query.findMap();
|
||||
* </pre>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Map<?, Product> map =
|
||||
* ebeanServer.find(Product.class)
|
||||
* .setMapKey("sku")
|
||||
* .findMap();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see EbeanServer#findMap(Query, Transaction)
|
||||
*/
|
||||
@@ -525,32 +687,34 @@ public interface Query<T> extends Serializable {
|
||||
* return 0 or 1 results.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* // assuming the sku of products is unique...
|
||||
* Product product =
|
||||
* Ebean.find(Product.class)
|
||||
* .where("sku = ?")
|
||||
* .set(1, "aa113")
|
||||
* ebeanServer.find(Product.class)
|
||||
* .where().eq("sku", "aa113")
|
||||
* .findUnique();
|
||||
* ...
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* It is also useful with finding objects by their id when you want to specify
|
||||
* further join information.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Fetch order 1 and additionally fetch join its order details...
|
||||
* Order order =
|
||||
* Ebean.find(Order.class)
|
||||
* ebeanServer.find(Order.class)
|
||||
* .setId(1)
|
||||
* .fetch("details")
|
||||
* .fetch("details")
|
||||
* .findUnique();
|
||||
*
|
||||
* List<OrderDetail> details = order.getDetails();
|
||||
*
|
||||
* // the order details were eagerly loaded
|
||||
* List<OrderDetail> details = order.getDetails();
|
||||
* ...
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*/
|
||||
public T findUnique();
|
||||
|
||||
@@ -589,48 +753,75 @@ public interface Query<T> extends Serializable {
|
||||
/**
|
||||
* Execute find list query in a background thread.
|
||||
* <p>
|
||||
* This returns a Future object which can be used to cancel, check the
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
* This query will execute in it's own PersistenceContext and using its own transaction.
|
||||
* What that means is that it will not share any bean instances with other queries.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a Future object for the list result of the query
|
||||
*/
|
||||
public FutureList<T> findFutureList();
|
||||
|
||||
/**
|
||||
* Return a PagingList for this query.
|
||||
* Return a PagedList for this query.
|
||||
* <p>
|
||||
* This can be used to break up a query into multiple queries to fetch the
|
||||
* data a page at a time.
|
||||
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
|
||||
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
|
||||
* {@link Query#findFutureRowCount()} to determine total row count, total page count etc.
|
||||
* </p>
|
||||
* <p>
|
||||
* This typically works by using a query per page and setting
|
||||
* {@link Query#setFirstRow(int)} and and {@link Query#setMaxRows(int)} on the
|
||||
* query. This usually would translate into SQL that uses limit offset, rownum
|
||||
* or row_number function to limit the result set.
|
||||
* Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
|
||||
* the query. This translates into SQL that uses limit offset, rownum or row_number function to
|
||||
* limit the result set.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* <h4>Example: typical use including total row count</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // We want to find the first 100 new orders
|
||||
* // ... 0 means first page
|
||||
* // ... page size is 100
|
||||
*
|
||||
* PagedList<Order> pagedList
|
||||
* = ebeanServer.find(Order.class)
|
||||
* .where().eq("status", Order.Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .findPagedList(0, 100);
|
||||
*
|
||||
* // Optional: initiate the loading of the total
|
||||
* // row count in a background thread
|
||||
* pagedList.loadRowCount();
|
||||
*
|
||||
* // fetch and return the list in the foreground thread
|
||||
* List<Order> orders = pagedList.getList();
|
||||
*
|
||||
* // get the total row count (from the future)
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param pageIndex
|
||||
* The zero based index of the page.
|
||||
* @param pageSize
|
||||
* the number of beans fetched per Page
|
||||
*
|
||||
* The number of beans to return per page.
|
||||
* @return The PagedList
|
||||
*/
|
||||
public PagingList<T> findPagingList(int pageSize);
|
||||
public PagedList<T> findPagedList(int pageIndex, int pageSize);
|
||||
|
||||
/**
|
||||
* Set a named bind parameter. Named parameters have a colon to prefix the
|
||||
* name.
|
||||
* Set a named bind parameter. Named parameters have a colon to prefix the name.
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* // a query with a named parameter
|
||||
* String oql = "find order where status = :orderStatus";
|
||||
* String oql = "find order where status = :orderStatus";
|
||||
*
|
||||
* Query<Order> query = Ebean.createQuery(Order.class, oql);
|
||||
* Query<Order> query = ebeanServer.find(Order.class, oql);
|
||||
*
|
||||
* // bind the named parameter
|
||||
* query.bind("orderStatus", OrderStatus.NEW);
|
||||
* List<Order> list = query.findList();
|
||||
* </pre>
|
||||
* query.bind("orderStatus", OrderStatus.NEW);
|
||||
* List<Order> list = query.findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param name
|
||||
* the parameter name
|
||||
@@ -644,17 +835,19 @@ public interface Query<T> extends Serializable {
|
||||
* position starts at 1 to be consistent with JDBC PreparedStatement. You need
|
||||
* to set a parameter value for each ? you have in the query.
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* // a query with a positioned parameter
|
||||
* String oql = "where status = ? order by id desc";
|
||||
* String oql = "where status = ? order by id desc";
|
||||
*
|
||||
* Query<Order> query = Ebean.createQuery(Order.class, oql);
|
||||
* Query<Order> query = ebeanServer.createQuery(Order.class, oql);
|
||||
*
|
||||
* // bind the parameter
|
||||
* query.setParameter(1, OrderStatus.NEW);
|
||||
*
|
||||
* List<Order> list = query.findList();
|
||||
* </pre>
|
||||
* List<Order> list = query.findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param position
|
||||
* the parameter bind position starting from 1 (not 0)
|
||||
@@ -663,36 +856,6 @@ public interface Query<T> extends Serializable {
|
||||
*/
|
||||
public Query<T> setParameter(int position, Object value);
|
||||
|
||||
/**
|
||||
* Please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
|
||||
* <p>
|
||||
* Set a listener to process the query on a row by row basis.
|
||||
* </p>
|
||||
* <p>
|
||||
* Use this when you want to process a large query and do not want to hold the
|
||||
* entire query result in memory.
|
||||
* </p>
|
||||
* <p>
|
||||
* It this case the rows are not loaded into the persistence context and
|
||||
* instead are processed by the query listener.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* QueryListener<Order> listener = ...;
|
||||
*
|
||||
* Query<Order> query = Ebean.createQuery(Order.class);
|
||||
*
|
||||
* // set the listener that will process each order one at a time
|
||||
* query.setListener(listener);
|
||||
*
|
||||
* // execute the query. Note that the returned
|
||||
* // list (emptyList) will be empty ...
|
||||
* List<Order> emtyList = query.findList();
|
||||
* </pre>
|
||||
* @deprecated Deprecated in favor of {@link #findIterate()} and {@link #findVisit(QueryResultVisitor)}
|
||||
*/
|
||||
public Query<T> setListener(QueryListener<T> queryListener);
|
||||
|
||||
/**
|
||||
* Set the Id value to query. This is used with findUnique().
|
||||
* <p>
|
||||
@@ -700,12 +863,18 @@ public interface Query<T> extends Serializable {
|
||||
* fetch joins.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* Query<Order> query = Ebean.createQuery(Order.class);
|
||||
* Order order = query.setId(1).join("details").findUnique();
|
||||
* List<OrderDetail> details = order.getDetails();
|
||||
* ...
|
||||
* </pre>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Order order =
|
||||
* ebeanServer.find(Order.class)
|
||||
* .setId(1)
|
||||
* .fetch("details")
|
||||
* .findUnique();
|
||||
*
|
||||
* // the order details were eagerly fetched
|
||||
* List<OrderDetail> details = order.getDetails();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public Query<T> setId(Object id);
|
||||
|
||||
@@ -716,15 +885,17 @@ public interface Query<T> extends Serializable {
|
||||
* {@link #setParameter(String, Object)}.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* Query<Order> query = Ebean.createQuery(Order.class, "top");
|
||||
* <pre>{@code
|
||||
*
|
||||
* Query<Order> query = ebeanServer.createQuery(Order.class, "top");
|
||||
* ...
|
||||
* if (...) {
|
||||
* query.where("status = :status and lower(customer.name) like :custName");
|
||||
* query.setParameter("status", Order.NEW);
|
||||
* query.setParameter("custName", "rob%");
|
||||
* query.where("status = :status and lower(customer.name) like :custName");
|
||||
* query.setParameter("status", Order.NEW);
|
||||
* query.setParameter("custName", "rob%");
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Internally the addToWhereClause string is processed by removing named
|
||||
@@ -744,13 +915,15 @@ public interface Query<T> extends Serializable {
|
||||
/**
|
||||
* Add a single Expression to the where clause returning the query.
|
||||
*
|
||||
* <pre class="code">
|
||||
* List<Order> newOrders =
|
||||
* Ebean.find(Order.class)
|
||||
* .where().eq("status", Order.NEW)
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Order> newOrders =
|
||||
* ebeanServer.find(Order.class)
|
||||
* .where().eq("status", Order.NEW)
|
||||
* .findList();
|
||||
* ...
|
||||
* </pre>
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public Query<T> where(Expression expression);
|
||||
|
||||
@@ -759,15 +932,16 @@ public interface Query<T> extends Serializable {
|
||||
* ExpressionList. You can use this for adding multiple expressions to the
|
||||
* where clause.
|
||||
*
|
||||
* <pre class="code">
|
||||
* Query<Order> query = Ebean.createQuery(Order.class, "top");
|
||||
* ...
|
||||
* if (...) {
|
||||
* query.where()
|
||||
* .eq("status", Order.NEW)
|
||||
* .ilike("customer.name","rob%");
|
||||
* }
|
||||
* </pre>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Order> orders =
|
||||
* ebeanServer.find(Order.class)
|
||||
* .where()
|
||||
* .eq("status", Order.NEW)
|
||||
* .ilike("customer.name","rob%")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see Expr
|
||||
* @return The ExpressionList for adding expressions to.
|
||||
@@ -785,17 +959,18 @@ public interface Query<T> extends Serializable {
|
||||
* week. In this case you can use filterMany() to filter the orders.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> list = Ebean
|
||||
* .find(Customer.class)
|
||||
* // .fetch("orders", new FetchConfig().lazy())
|
||||
* // .fetch("orders", new FetchConfig().query())
|
||||
* .fetch("orders").where().ilike("name", "rob%").filterMany("orders")
|
||||
* .eq("status", Order.Status.NEW).gt(
|
||||
* "orderDate", lastWeek).findList();
|
||||
* List<Customer> list =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* // .fetch("orders", new FetchConfig().lazy())
|
||||
* // .fetch("orders", new FetchConfig().query())
|
||||
* .fetch("orders")
|
||||
* .where().ilike("name", "rob%")
|
||||
* .filterMany("orders").eq("status", Order.Status.NEW).gt("orderDate", lastWeek)
|
||||
* .findList();
|
||||
*
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Please note you have to be careful that you add expressions to the correct
|
||||
@@ -833,14 +1008,14 @@ public interface Query<T> extends Serializable {
|
||||
* {@link #setParameter(String, Object)}.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* Query<ReportOrder> query = Ebean.createQuery(ReportOrder.class);
|
||||
* ...
|
||||
* if (...) {
|
||||
* query.having("score > :min");
|
||||
* query.setParameter("min", 1);
|
||||
* }
|
||||
* </pre>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<ReportOrder> query =
|
||||
* ebeanServer.find(ReportOrder.class)
|
||||
* .having("score > :min").setParameter("min", 1)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param addToHavingClause
|
||||
* the clause to append to the having clause which typically contains
|
||||
@@ -966,30 +1141,23 @@ public interface Query<T> extends Serializable {
|
||||
*/
|
||||
public Query<T> setMaxRows(int maxRows);
|
||||
|
||||
/**
|
||||
* Set the rows after which fetching should continue in a background thread.
|
||||
*
|
||||
* @param backgroundFetchAfter
|
||||
*/
|
||||
public Query<T> setBackgroundFetchAfter(int backgroundFetchAfter);
|
||||
|
||||
/**
|
||||
* Set the property to use as keys for a map.
|
||||
* <p>
|
||||
* If no property is set then the id property is used.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Assuming sku is unique for products...
|
||||
*
|
||||
* Query<Product> query = Ebean.createQuery(Product.class);
|
||||
*
|
||||
* // use sku for keys...
|
||||
* query.setMapKey("sku");
|
||||
*
|
||||
* Map<?,Product> productMap = query.findMap();
|
||||
* ...
|
||||
* </pre>
|
||||
* Map<?,Product> productMap =
|
||||
* ebeanServer.find(Product.class)
|
||||
* // use sku for keys...
|
||||
* .setMapKey("sku")
|
||||
* .findMap();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param mapKey
|
||||
* the property to use as keys for a map.
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* Used to process a query result one bean at a time via a callback to this
|
||||
* visitor.
|
||||
* <p>
|
||||
* If you wish to stop further processing return false from the accept method.
|
||||
* </p>
|
||||
* <p>
|
||||
* Unlike findList() and findSet() using a QueryResultVisitor does not require
|
||||
* all the beans in the query result to be held in memory at once. This makes
|
||||
* QueryResultVisitor useful for processing large queries.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* Query<Customer> query = server.find(Customer.class)
|
||||
* .where().eq("status", Status.NEW)
|
||||
* .order().asc("id");
|
||||
*
|
||||
* query.findEach((Customer customer) -> {
|
||||
*
|
||||
* // do something with customer
|
||||
* System.out.println("-- visit " + customer);
|
||||
* });
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T>
|
||||
* the type of entity bean being queried.
|
||||
*/
|
||||
public interface QueryEachConsumer<T> {
|
||||
|
||||
/**
|
||||
* Process the bean.
|
||||
*
|
||||
* @param bean
|
||||
* the entity bean to process
|
||||
*/
|
||||
public void accept(T bean);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* Used to process a query result one bean at a time via a callback to this
|
||||
* visitor.
|
||||
* <p>
|
||||
* If you wish to stop further processing return false from the accept method.
|
||||
* </p>
|
||||
* <p>
|
||||
* Unlike findList() and findSet() using a QueryResultVisitor does not require
|
||||
* all the beans in the query result to be held in memory at once. This makes
|
||||
* QueryResultVisitor useful for processing large queries.
|
||||
* </p>
|
||||
* <p/>
|
||||
* <pre class="code">
|
||||
*
|
||||
* Query<Customer> query = server.find(Customer.class)
|
||||
* .fetch("contacts", new FetchConfig().query(2))
|
||||
* .where().gt("id", 0)
|
||||
* .orderBy("id")
|
||||
* .setMaxRows(2);
|
||||
*
|
||||
* query.findEachWhile((Customer customer) -> {
|
||||
*
|
||||
* // do something with customer
|
||||
* System.out.println("-- visit " + customer);
|
||||
*
|
||||
* // return true to continue processing or false to stop
|
||||
* return (customer.getId() < 40);
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param <T> the type of entity bean being queried.
|
||||
*/
|
||||
public interface QueryEachWhileConsumer<T> {
|
||||
|
||||
/**
|
||||
* Process the bean and return true if you want to continue processing more
|
||||
* beans. Return false if you want to stop processing further.
|
||||
*
|
||||
* @param bean the entity bean to process
|
||||
* @return true to continue processing more beans or false to stop.
|
||||
*/
|
||||
public boolean accept(T bean);
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* Deprecated, please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
|
||||
* <p>
|
||||
* Provides a mechanism for processing a query one bean at a time.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is useful when the query will return a large number of results and you
|
||||
* want to process the beans one at a time rather than whole all of the beans in
|
||||
* memory at once.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* QueryListener<Order> listener = ...;
|
||||
*
|
||||
* Query<Order> query = Ebean.createQuery(Order.class);
|
||||
*
|
||||
* // set the listener that will process each order one at a time
|
||||
* query.setListener(listener);
|
||||
*
|
||||
* // execute the query. Note that the returned
|
||||
* // list will be empty ... so don't bother assigning it
|
||||
* query.findList();
|
||||
* </pre>
|
||||
*
|
||||
* @param <T>
|
||||
* the type of entity bean
|
||||
* @deprecated Please migrate to using {@link #findIterate()} or
|
||||
* {@link #findVisit(QueryResultVisitor)}
|
||||
*/
|
||||
public interface QueryListener<T> {
|
||||
|
||||
/**
|
||||
* Process the bean that has just been read.
|
||||
* <p>
|
||||
* This bean will not be added to the List Set or Map and nor will it be put
|
||||
* into the PersistenceContext. This is what makes this a good way to process
|
||||
* a large result set (which could normally use a lot of memory).
|
||||
* </p>
|
||||
*/
|
||||
public void process(T bean);
|
||||
}
|
||||
@@ -1,12 +1,8 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.*;
|
||||
|
||||
import com.avaje.ebean.util.CamelCaseHelper;
|
||||
|
||||
@@ -21,25 +17,25 @@ import com.avaje.ebean.util.CamelCaseHelper;
|
||||
* <b>Unparsed RawSql:</b>
|
||||
* </p>
|
||||
* <p>
|
||||
* When RawSql is created via RawSqlBuilder.unparsed(sql) then Ebean can not
|
||||
* When RawSql is created via {@link RawSqlBuilder#unparsed(String)} then Ebean can not
|
||||
* modify the SQL at all. It can't add any extra expressions into the SQL.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Parsed RawSql:</b>
|
||||
* </p>
|
||||
* <p>
|
||||
* When RawSql is created via RawSqlBuilder.parse(sql) then Ebean will parse the
|
||||
* When RawSql is created via {@link RawSqlBuilder#parse(String)} then Ebean will parse the
|
||||
* SQL and find places in the SQL where it can add extra where expressions, add
|
||||
* extra having expressions or replace the order by clause. If you want to
|
||||
* explicitly tell Ebean where these insertion points are you can place special
|
||||
* strings into your SQL (${where} or ${andWhere} and ${having} or
|
||||
* ${andHaving}).
|
||||
* strings into your SQL ({@code ${where}} or {@code ${andWhere}} and {@code ${having}} or
|
||||
* {@code ${andHaving})}.
|
||||
* </p>
|
||||
* <p>
|
||||
* If the SQL already includes a WHERE clause put in ${andWhere} in the location
|
||||
* If the SQL already includes a WHERE clause put in {@code ${andWhere}} in the location
|
||||
* you want Ebean to add any extra where expressions. If the SQL doesn't have a
|
||||
* WHERE clause put ${where} in instead. Similarly you can put in ${having} or
|
||||
* ${andHaving} where you want Ebean put add extra having expressions.
|
||||
* WHERE clause put {@code ${where}} in instead. Similarly you can put in {@code ${having}} or
|
||||
* {@code ${andHaving}} where you want Ebean put add extra having expressions.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Aggregates:</b>
|
||||
@@ -55,23 +51,19 @@ import com.avaje.ebean.util.CamelCaseHelper;
|
||||
* to hold the values for the aggregate functions (sum etc) and a @OneToOne
|
||||
* to Order.
|
||||
* </p>
|
||||
* <p>
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Example OrderAggregate</b>
|
||||
* </p>
|
||||
*
|
||||
* <h3>Example OrderAggregate</h3>
|
||||
*
|
||||
* <pre class="code">
|
||||
* <pre>{@code
|
||||
* ...
|
||||
* // @Sql indicates to that this bean
|
||||
* // @Sql indicates to that this bean
|
||||
* // is based on RawSql rather than a table
|
||||
*
|
||||
* @Entity
|
||||
* @Sql
|
||||
* @Entity
|
||||
* @Sql
|
||||
* public class OrderAggregate {
|
||||
*
|
||||
* @OneToOne
|
||||
* @OneToOne
|
||||
* Order order;
|
||||
*
|
||||
* Double totalAmount;
|
||||
@@ -80,35 +72,38 @@ import com.avaje.ebean.util.CamelCaseHelper;
|
||||
*
|
||||
* // getters and setters
|
||||
* ...
|
||||
* </pre>
|
||||
* <p>
|
||||
* <b>Example 1:</b>
|
||||
* </p>
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Example 1:</h3>
|
||||
*
|
||||
* <pre class="code">
|
||||
* String sql = " select order_id, o.status, c.id, c.name, sum(d.order_qty*d.unit_price) as totalAmount"
|
||||
* + " from o_order o"
|
||||
* + " join o_customer c on c.id = o.kcustomer_id "
|
||||
* + " join o_order_detail d on d.order_id = o.id " + " group by order_id, o.status ";
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql = " select order_id, o.status, c.id, c.name, sum(d.order_qty*d.unit_price) as totalAmount"
|
||||
* + " from o_order o"
|
||||
* + " join o_customer c on c.id = o.kcustomer_id "
|
||||
* + " join o_order_detail d on d.order_id = o.id " + " group by order_id, o.status ";
|
||||
*
|
||||
* RawSql rawSql = RawSqlBuilder.parse(sql)
|
||||
* RawSql rawSql = RawSqlBuilder.parse(sql)
|
||||
* // map the sql result columns to bean properties
|
||||
* .columnMapping("order_id", "order.id").columnMapping("o.status", "order.status")
|
||||
* .columnMapping("c.id", "order.customer.id")
|
||||
* .columnMapping("c.name", "order.customer.name")
|
||||
* .columnMapping("order_id", "order.id")
|
||||
* .columnMapping("o.status", "order.status")
|
||||
* .columnMapping("c.id", "order.customer.id")
|
||||
* .columnMapping("c.name", "order.customer.name")
|
||||
* // we don't need to map this one due to the sql column alias
|
||||
* // .columnMapping("sum(d.order_qty*d.unit_price)", "totalAmount")
|
||||
* // .columnMapping("sum(d.order_qty*d.unit_price)", "totalAmount")
|
||||
* .create();
|
||||
*
|
||||
* Query<OrderAggregate> query = Ebean.find(OrderAggregate.class);
|
||||
* query.setRawSql(rawSql).where().gt("order.id", 0).having().gt("totalAmount", 20);
|
||||
* List<OrderAggregate> list = Ebean.find(OrderAggregate.class)
|
||||
* .setRawSql(rawSql)
|
||||
* .where().gt("order.id", 0)
|
||||
* .having().gt("totalAmount", 20)
|
||||
* .findList();
|
||||
*
|
||||
* List<OrderAggregate> list = query.findList();
|
||||
* </pre>
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* <b>Example 2:</b>
|
||||
* </p>
|
||||
* <h3>Example 2:</h3>
|
||||
*
|
||||
* <p>
|
||||
* The following example uses a FetchConfig().query() so that after the initial
|
||||
@@ -116,37 +111,90 @@ import com.avaje.ebean.util.CamelCaseHelper;
|
||||
* associated order status, orderDate along with the customer name.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* String sql = " select order_id, 'ignoreMe', sum(d.order_qty*d.unit_price) as totalAmount "
|
||||
* + " from o_order_detail d"
|
||||
* + " group by order_id ";
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql = " select order_id, 'ignoreMe', sum(d.order_qty*d.unit_price) as totalAmount "
|
||||
* + " from o_order_detail d"
|
||||
* + " group by order_id ";
|
||||
*
|
||||
* RawSql rawSql = RawSqlBuilder.parse(sql).columnMapping("order_id", "order.id")
|
||||
* .columnMappingIgnore("'ignoreMe'").create();
|
||||
* RawSql rawSql = RawSqlBuilder.parse(sql)
|
||||
* .columnMapping("order_id", "order.id")
|
||||
* .columnMappingIgnore("'ignoreMe'")
|
||||
* .create();
|
||||
*
|
||||
* Query<OrderAggregate> query = Ebean.find(OrderAggregate.class);
|
||||
* query.setRawSql(rawSql).fetch("order", "status,orderDate", new FetchConfig().query())
|
||||
* .fetch("order.customer", "name").where()
|
||||
* .gt("order.id", 0).having().gt("totalAmount", 20).order().desc("totalAmount").setMaxRows(10);
|
||||
*
|
||||
* </pre>
|
||||
* List<OrderAggregate> orders = Ebean.find(OrderAggregate.class)
|
||||
* .setRawSql(rawSql)
|
||||
* .fetch("order", "status,orderDate", new FetchConfig().query())
|
||||
* .fetch("order.customer", "name")
|
||||
* .where().gt("order.id", 0)
|
||||
* .having().gt("totalAmount", 20)
|
||||
* .order().desc("totalAmount")
|
||||
* .setMaxRows(10)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*
|
||||
* <h3>Example 3: tableAliasMapping</h3>
|
||||
* <p>
|
||||
* Instead of mapping each column you can map each table alias to a path using tableAliasMapping().
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* String rs = "select o.id, o.status, c.id, c.name, "+
|
||||
* " d.id, d.order_qty, p.id, p.name " +
|
||||
* "from o_order o join o_customer c on c.id = o.kcustomer_id " +
|
||||
* "join o_order_detail d on d.order_id = o.id " +
|
||||
* "join o_product p on p.id = d.product_id " +
|
||||
* "where o.id <= :maxOrderId and p.id = :productId "+
|
||||
* "order by o.id, d.id asc";
|
||||
*
|
||||
* RawSql rawSql = RawSqlBuilder.parse(rs)
|
||||
* .tableAliasMapping("c", "customer")
|
||||
* .tableAliasMapping("d", "details")
|
||||
* .tableAliasMapping("p", "details.product")
|
||||
* .create();
|
||||
*
|
||||
* List<Order> ordersFromRaw = Ebean.find(Order.class)
|
||||
* .setRawSql(rawSql)
|
||||
* .setParameter("maxOrderId", 2)
|
||||
* .setParameter("productId", 1)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Note that lazy loading also works with object graphs built with RawSql.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*
|
||||
*/
|
||||
public final class RawSql implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final ResultSet resultSet;
|
||||
|
||||
private final Sql sql;
|
||||
|
||||
private final ColumnMapping columnMapping;
|
||||
|
||||
protected RawSql(Sql sql, ColumnMapping columnMapping) {
|
||||
/**
|
||||
* Construct with a ResultSet and properties that the columns map to.
|
||||
* <p>
|
||||
* The properties listed in the propertyNames must be in the same order as the columns in the
|
||||
* resultSet.
|
||||
* <p>
|
||||
* When a query executes this RawSql object then it will close the resultSet.
|
||||
*/
|
||||
public RawSql(ResultSet resultSet, String... propertyNames) {
|
||||
this.resultSet = resultSet;
|
||||
this.sql = null;
|
||||
this.columnMapping = new ColumnMapping(propertyNames);
|
||||
}
|
||||
|
||||
protected RawSql(ResultSet resultSet, Sql sql, ColumnMapping columnMapping) {
|
||||
this.resultSet = resultSet;
|
||||
this.sql = sql;
|
||||
this.columnMapping = columnMapping;
|
||||
}
|
||||
@@ -158,6 +206,14 @@ public final class RawSql implements Serializable {
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the resultSet if this is a ResultSet based RawSql.
|
||||
*/
|
||||
public ResultSet getResultSet() {
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the column mapping for the SQL columns to bean properties.
|
||||
*/
|
||||
@@ -169,6 +225,9 @@ public final class RawSql implements Serializable {
|
||||
* Return the hash for this query.
|
||||
*/
|
||||
public int queryHash() {
|
||||
if (resultSet != null) {
|
||||
return 31 * columnMapping.queryHash();
|
||||
}
|
||||
return 31 * sql.queryHash() + columnMapping.queryHash();
|
||||
}
|
||||
|
||||
@@ -195,6 +254,8 @@ public final class RawSql implements Serializable {
|
||||
|
||||
private final boolean andHavingExpr;
|
||||
|
||||
private final String orderByPrefix;
|
||||
|
||||
private final String orderBy;
|
||||
|
||||
private final boolean distinct;
|
||||
@@ -213,6 +274,7 @@ public final class RawSql implements Serializable {
|
||||
this.preWhere = null;
|
||||
this.andHavingExpr = false;
|
||||
this.andWhereExpr = false;
|
||||
this.orderByPrefix = null;
|
||||
this.orderBy = null;
|
||||
this.distinct = false;
|
||||
}
|
||||
@@ -221,8 +283,7 @@ public final class RawSql implements Serializable {
|
||||
* Construct for parsed SQL.
|
||||
*/
|
||||
protected Sql(int queryHashCode, String preFrom, String preWhere, boolean andWhereExpr,
|
||||
String preHaving, boolean andHavingExpr,
|
||||
String orderBy, boolean distinct) {
|
||||
String preHaving, boolean andHavingExpr, String orderByPrefix, String orderBy, boolean distinct) {
|
||||
|
||||
this.queryHashCode = queryHashCode;
|
||||
this.parsed = true;
|
||||
@@ -232,6 +293,7 @@ public final class RawSql implements Serializable {
|
||||
this.preWhere = preWhere;
|
||||
this.andHavingExpr = andHavingExpr;
|
||||
this.andWhereExpr = andWhereExpr;
|
||||
this.orderByPrefix = orderByPrefix;
|
||||
this.orderBy = orderBy;
|
||||
this.distinct = distinct;
|
||||
}
|
||||
@@ -310,6 +372,14 @@ public final class RawSql implements Serializable {
|
||||
return andHavingExpr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the 'order by' keywords.
|
||||
* This can contain additional keywords, for example 'order siblings by' as Oracle syntax.
|
||||
*/
|
||||
public String getOrderByPrefix() {
|
||||
return (orderByPrefix == null) ? "order by" : orderByPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL ORDER BY clause.
|
||||
*/
|
||||
@@ -329,6 +399,7 @@ public final class RawSql implements Serializable {
|
||||
private final LinkedHashMap<String, Column> dbColumnMap;
|
||||
|
||||
private final Map<String, String> propertyMap;
|
||||
|
||||
private final Map<String, Column> propertyColumnMap;
|
||||
|
||||
private final boolean parsed;
|
||||
@@ -364,6 +435,26 @@ public final class RawSql implements Serializable {
|
||||
this.propertyColumnMap = null;
|
||||
this.dbColumnMap = new LinkedHashMap<String, Column>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for ResultSet use.
|
||||
*/
|
||||
protected ColumnMapping(String... propertyNames) {
|
||||
this.immutable = false;
|
||||
this.parsed = false;
|
||||
this.propertyMap = null;
|
||||
//this.propertyColumnMap = null;
|
||||
this.dbColumnMap = new LinkedHashMap<String, Column>();
|
||||
|
||||
int hc = 31;
|
||||
int pos = 0;
|
||||
for (String prop : propertyNames) {
|
||||
hc = 31 * hc + prop.hashCode();
|
||||
dbColumnMap.put(prop, new Column(pos++, prop, null, prop));
|
||||
}
|
||||
propertyColumnMap = dbColumnMap;
|
||||
this.queryHashCode = hc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an immutable ColumnMapping based on collected information.
|
||||
@@ -381,9 +472,8 @@ public final class RawSql implements Serializable {
|
||||
for (Column c : dbColumnMap.values()) {
|
||||
pMap.put(c.getPropertyName(), c.getDbColumn());
|
||||
pcMap.put(c.getPropertyName(), c);
|
||||
|
||||
hc = 31 * hc + c.getPropertyName() == null ? 0 : c.getPropertyName().hashCode();
|
||||
hc = 31 * hc + c.getDbColumn() == null ? 0 : c.getDbColumn().hashCode();
|
||||
hc = 31 * hc + ((c.getPropertyName() == null) ? 0 : c.getPropertyName().hashCode());
|
||||
hc = 31 * hc + ((c.getDbColumn() == null) ? 0 : c.getDbColumn().hashCode());
|
||||
}
|
||||
this.propertyMap = Collections.unmodifiableMap(pMap);
|
||||
this.propertyColumnMap = Collections.unmodifiableMap(pcMap);
|
||||
@@ -483,6 +573,22 @@ public final class RawSql implements Serializable {
|
||||
return dbColumnMap.values().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify any column mappings with the given table alias to have the path prefix.
|
||||
* <p>
|
||||
* For example modify all mappings with table alias "c" to have the path prefix "customer".
|
||||
* </p>
|
||||
*/
|
||||
public void tableAliasMapping(String tableAlias, String path) {
|
||||
|
||||
String startMatch = tableAlias+".";
|
||||
for (Map.Entry<String, Column> entry : dbColumnMap.entrySet()) {
|
||||
if (entry.getKey().startsWith(startMatch)) {
|
||||
entry.getValue().tableAliasMapping(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Column of the RawSql that is mapped to a bean property (or ignored).
|
||||
*/
|
||||
@@ -564,10 +670,23 @@ public final class RawSql implements Serializable {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the property name mapped to this db column.
|
||||
*/
|
||||
private void setPropertyName(String propertyName) {
|
||||
this.propertyName = propertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend the path to the property name.
|
||||
* <p/>
|
||||
* For example if path is "customer" then "name" becomes "customer.name".
|
||||
*/
|
||||
public void tableAliasMapping(String path) {
|
||||
if (path != null) {
|
||||
propertyName = path + "." + propertyName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
|
||||
import com.avaje.ebean.RawSql.ColumnMapping;
|
||||
import com.avaje.ebean.RawSql.Sql;
|
||||
|
||||
@@ -10,8 +12,6 @@ import com.avaje.ebean.RawSql.Sql;
|
||||
* named query.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*
|
||||
* @see RawSql
|
||||
*/
|
||||
public class RawSqlBuilder {
|
||||
@@ -21,10 +21,23 @@ public class RawSqlBuilder {
|
||||
*/
|
||||
public static final String IGNORE_COLUMN = "$$_IGNORE_COLUMN_$$";
|
||||
|
||||
private final ResultSet resultSet;
|
||||
|
||||
private final Sql sql;
|
||||
|
||||
private final ColumnMapping columnMapping;
|
||||
|
||||
/**
|
||||
* Create and return a RawSql object based on the resultSet and list of properties the columns in
|
||||
* the resultSet map to.
|
||||
* <p>
|
||||
* The properties listed in the propertyNames must be in the same order as the columns in the
|
||||
* resultSet.
|
||||
*/
|
||||
public static RawSql resultSet(ResultSet resultSet, String... propertyNames) {
|
||||
return new RawSql(resultSet, propertyNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an unparsed RawSqlBuilder. Unlike a parsed one this query can not be
|
||||
* modified - so no additional WHERE or HAVING expressions can be added to
|
||||
@@ -57,10 +70,11 @@ public class RawSqlBuilder {
|
||||
ColumnMapping mapping = DRawSqlColumnsParser.parse(select);
|
||||
return new RawSqlBuilder(sql2, mapping);
|
||||
}
|
||||
|
||||
|
||||
private RawSqlBuilder(Sql sql, ColumnMapping columnMapping) {
|
||||
this.sql = sql;
|
||||
this.columnMapping = columnMapping;
|
||||
this.resultSet = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,12 +101,23 @@ public class RawSqlBuilder {
|
||||
return columnMapping(dbColumn, IGNORE_COLUMN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify any column mappings with the given table alias to have the path prefix.
|
||||
* <p>
|
||||
* For example modify all mappings with table alias "c" to have the path prefix "customer".
|
||||
* </p>
|
||||
*/
|
||||
public RawSqlBuilder tableAliasMapping(String tableAlias, String path) {
|
||||
columnMapping.tableAliasMapping(tableAlias, path);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the immutable RawSql object. Do this after all the column mapping
|
||||
* has been defined.
|
||||
*/
|
||||
public RawSql create() {
|
||||
return new RawSql(sql, columnMapping.createImmutableCopy());
|
||||
return new RawSql(resultSet, sql, columnMapping.createImmutableCopy());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,4 +126,7 @@ public class RawSqlBuilder {
|
||||
protected Sql getSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
public class SimpleTextParser {
|
||||
class SimpleTextParser {
|
||||
|
||||
private final String oql;
|
||||
private final char[] chars;
|
||||
@@ -10,9 +10,7 @@ public class SimpleTextParser {
|
||||
private String word;
|
||||
private String lowerWord;
|
||||
|
||||
private int openParenthesisCount;
|
||||
|
||||
public SimpleTextParser(String oql) {
|
||||
SimpleTextParser(String oql) {
|
||||
this.oql = oql;
|
||||
this.chars = oql.toCharArray();
|
||||
this.eof = oql.length();
|
||||
@@ -22,10 +20,6 @@ public class SimpleTextParser {
|
||||
return pos;
|
||||
}
|
||||
|
||||
public String getOql() {
|
||||
return oql;
|
||||
}
|
||||
|
||||
public String getWord() {
|
||||
return word;
|
||||
}
|
||||
@@ -104,7 +98,7 @@ public class SimpleTextParser {
|
||||
private void moveToClose() {
|
||||
|
||||
pos++;
|
||||
openParenthesisCount = 0;
|
||||
int openParenthesisCount = 0;
|
||||
|
||||
for (; pos < eof; pos++) {
|
||||
char c = chars[pos];
|
||||
@@ -143,11 +137,7 @@ public class SimpleTextParser {
|
||||
if (isOperator(c)) {
|
||||
return !isOperator;
|
||||
}
|
||||
if (c == '(') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isOperator;
|
||||
return c == '(' || isOperator;
|
||||
}
|
||||
|
||||
private boolean isOperator(char c) {
|
||||
|
||||
@@ -79,6 +79,7 @@ public interface SqlQuery extends Serializable {
|
||||
* </p>
|
||||
*
|
||||
* @return a Future object for the list result of the query
|
||||
* @deprecated
|
||||
*/
|
||||
public SqlFutureList findFutureList();
|
||||
|
||||
|
||||
@@ -91,6 +91,11 @@ public interface SqlUpdate {
|
||||
*/
|
||||
public String getSql();
|
||||
|
||||
/**
|
||||
* Return the generated sql that has named parameters converted to positioned parameters.
|
||||
*/
|
||||
public String getGeneratedSql();
|
||||
|
||||
/**
|
||||
* Return the timeout used to execute this statement.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.config.PersistBatch;
|
||||
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.RollbackException;
|
||||
@@ -35,6 +37,11 @@ public interface Transaction extends Closeable {
|
||||
*/
|
||||
public static final int SERIALIZABLE = java.sql.Connection.TRANSACTION_SERIALIZABLE;
|
||||
|
||||
/**
|
||||
* Register a TransactionCallback with this transaction.
|
||||
*/
|
||||
public void register(TransactionCallback callback);
|
||||
|
||||
/**
|
||||
* Return true if this transaction is read only.
|
||||
*/
|
||||
@@ -123,47 +130,93 @@ public interface Transaction extends Closeable {
|
||||
* Example: batch processing executing every 3 rows
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* String data = "This is a simple test of the batch processing"
|
||||
* + " mode and the transaction execute batch method";
|
||||
* <pre>{@code
|
||||
*
|
||||
* String data = "This is a simple test of the batch processing"
|
||||
* + " mode and the transaction execute batch method";
|
||||
*
|
||||
* String[] da = data.split(" ");
|
||||
* String[] da = data.split(" ");
|
||||
*
|
||||
* String sql = "{call sp_t3(?,?)}";
|
||||
* String sql = "{call sp_t3(?,?)}";
|
||||
*
|
||||
* CallableSql cs = new CallableSql(sql);
|
||||
* cs.registerOut(2, Types.INTEGER);
|
||||
*
|
||||
* // (optional) inform eBean this stored procedure
|
||||
* // inserts into a table called sp_test
|
||||
* cs.addModification("sp_test", true, false, false);
|
||||
* cs.addModification("sp_test", true, false, false);
|
||||
*
|
||||
* Transaction t = Ebean.beginTransaction();
|
||||
* t.setBatchMode(true);
|
||||
* t.setBatchSize(3);
|
||||
* Transaction txn = ebeanServer.beginTransaction();
|
||||
* txn.setBatchMode(true);
|
||||
* txn.setBatchSize(3);
|
||||
* try {
|
||||
* for (int i = 0; i < da.length;) {
|
||||
*
|
||||
* for (int i = 0; i < da.length;) {
|
||||
* cs.setParameter(1, da[i]);
|
||||
* Ebean.execute(cs);
|
||||
* ebeanServer.execute(cs);
|
||||
* }
|
||||
*
|
||||
* // NB: commit implicitly flushes
|
||||
* Ebean.commitTransaction();
|
||||
* txn.commit();
|
||||
*
|
||||
* } finally {
|
||||
* Ebean.endTransaction();
|
||||
* txn.end();
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*/
|
||||
public void setBatchMode(boolean useBatch);
|
||||
|
||||
/**
|
||||
* The JDBC batch mode to use for this transaction.
|
||||
* <p>
|
||||
* If this is NONE then JDBC batch can still be used for each request - save(), insert(), update() or delete()
|
||||
* and this would be useful if the request cascades to detail beans.
|
||||
* </p>
|
||||
*
|
||||
* @param persistBatchMode the batch mode to use for this transaction
|
||||
*
|
||||
* @see com.avaje.ebean.config.ServerConfig#setPersistBatch(com.avaje.ebean.config.PersistBatch)
|
||||
*/
|
||||
public void setBatch(PersistBatch persistBatchMode);
|
||||
|
||||
/**
|
||||
* Return the batch mode at the transaction level.
|
||||
*/
|
||||
public PersistBatch getBatch();
|
||||
|
||||
/**
|
||||
* Set the JDBC batch mode to use for a save() or delete() request.
|
||||
* <p>
|
||||
* This only takes effect when batch mode on the transaction has not already meant that
|
||||
* JDBC batch mode is being used.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is useful when the single save() or delete() cascades. For example, inserting a 'master' cascades
|
||||
* and inserts a collection of 'detail' beans. The detail beans can be inserted using JDBC batch.
|
||||
* </p>
|
||||
*
|
||||
* @param batchOnCascadeMode the batch mode to use per save(), insert(), update() or delete()
|
||||
*
|
||||
* @see com.avaje.ebean.config.ServerConfig#setPersistBatchOnCascade(com.avaje.ebean.config.PersistBatch)
|
||||
*/
|
||||
public void setBatchOnCascade(PersistBatch batchOnCascadeMode);
|
||||
|
||||
/**
|
||||
* Return the batch mode at the request level (for each save(), insert(), update() or delete()).
|
||||
*/
|
||||
public PersistBatch getBatchOnCascade();
|
||||
|
||||
/**
|
||||
* Specify the number of statements before a batch is flushed automatically.
|
||||
*/
|
||||
public void setBatchSize(int batchSize);
|
||||
|
||||
/**
|
||||
* Return the current batch size.
|
||||
*/
|
||||
public int getBatchSize();
|
||||
|
||||
/**
|
||||
* Specify if you want batched inserts to use getGeneratedKeys.
|
||||
* <p>
|
||||
@@ -223,20 +276,11 @@ public interface Transaction extends Closeable {
|
||||
* <li>the batch size is reached</li>
|
||||
* <li>A query is executed on the same transaction</li>
|
||||
* <li>UpdateSql or CallableSql are mixed with bean save and delete</li>
|
||||
* <li>Transaction commit occurs</li>
|
||||
* </ul>
|
||||
*/
|
||||
public void flushBatch() throws PersistenceException, OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Deprecated in favour of {@link #flushBatch()}.
|
||||
* <p>
|
||||
* Exactly the same as flushBatch. Deprecated as a name change.
|
||||
* </p>
|
||||
*
|
||||
* @deprecated Please use flushBatch
|
||||
*/
|
||||
public void batchFlush() throws PersistenceException, OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Return the underlying Connection object.
|
||||
* <p>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* Provides a callback that can be registered with a Transaction.
|
||||
* <p/>
|
||||
* The callback methods are called just prior to and after the transaction performs a commit or rollback.
|
||||
* <p/>
|
||||
* A typical use of TransactionCallback would be to clean up non-transactional resources like files. For example,
|
||||
* when processing files on postCommit/postRollback clean up the associated files. As another example when
|
||||
* on postCommit of a delete remove associated resources from the file system or remote service.
|
||||
*/
|
||||
public interface TransactionCallback {
|
||||
|
||||
/**
|
||||
* Perform processing just prior to the transaction commit.
|
||||
*/
|
||||
void preCommit();
|
||||
|
||||
/**
|
||||
* Perform processing just after the transaction commit.
|
||||
*/
|
||||
void postCommit();
|
||||
|
||||
/**
|
||||
* Perform processing just prior to the transaction rollback.
|
||||
*/
|
||||
void preRollback();
|
||||
|
||||
/**
|
||||
* Perform processing just after the transaction rollback.
|
||||
*/
|
||||
void postRollback();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* Adapter that can be extended for easier implementation of TransactionCallback.
|
||||
* <p/>
|
||||
* Provides 'no operation' implementation for each of the TransactionCallback methods. It is expected that this
|
||||
* class is extended and override the methods you need to.
|
||||
*/
|
||||
public abstract class TransactionCallbackAdapter implements TransactionCallback {
|
||||
|
||||
/**
|
||||
* Perform processing just prior to the transaction commit.
|
||||
*/
|
||||
@Override
|
||||
public void preCommit() {
|
||||
// do nothing - override as necessary
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform processing just after the transaction commit.
|
||||
*/
|
||||
@Override
|
||||
public void postCommit() {
|
||||
// do nothing - override as necessary
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform processing just prior to the transaction rollback.
|
||||
*/
|
||||
@Override
|
||||
public void preRollback() {
|
||||
// do nothing - override as necessary
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform processing just after the transaction rollback.
|
||||
*/
|
||||
@Override
|
||||
public void postRollback() {
|
||||
// do nothing - override as necessary
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.config.PersistBatch;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
@@ -28,12 +30,39 @@ public final class TxScope {
|
||||
|
||||
TxIsolation isolation;
|
||||
|
||||
PersistBatch batch;
|
||||
|
||||
PersistBatch batchOnCascade;
|
||||
|
||||
int batchSize;
|
||||
|
||||
boolean readOnly;
|
||||
|
||||
ArrayList<Class<? extends Throwable>> rollbackFor;
|
||||
|
||||
ArrayList<Class<? extends Throwable>> noRollbackFor;
|
||||
|
||||
/**
|
||||
* Return true if PersistBatch has been set.
|
||||
*/
|
||||
public boolean isBatchSet() {
|
||||
return batch != null && batch != PersistBatch.INHERIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if batch on cascade has been set.
|
||||
*/
|
||||
public boolean isBatchOnCascadeSet() {
|
||||
return batchOnCascade != null && batchOnCascade != PersistBatch.INHERIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if batch size has been set.
|
||||
*/
|
||||
public boolean isBatchSizeSet() {
|
||||
return batchSize > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a TxScope with REQUIRES.
|
||||
*/
|
||||
@@ -114,6 +143,51 @@ public final class TxScope {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch mode.
|
||||
*/
|
||||
public PersistBatch getBatch() {
|
||||
return batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the batch mode to use.
|
||||
*/
|
||||
public TxScope setBatch(PersistBatch batch) {
|
||||
this.batch = batch;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch on cascade mode.
|
||||
*/
|
||||
public PersistBatch getBatchOnCascade() {
|
||||
return batchOnCascade;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the batch on cascade mode.
|
||||
*/
|
||||
public TxScope setBatchOnCascade(PersistBatch batchOnCascade) {
|
||||
this.batchOnCascade = batchOnCascade;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the batch size. 0 means use the default value.
|
||||
*/
|
||||
public int getBatchSize() {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the batch size to use.
|
||||
*/
|
||||
public TxScope setBatchSize(int batchSize) {
|
||||
this.batchSize = batchSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the transaction should be treated as read only.
|
||||
*/
|
||||
|
||||
@@ -5,30 +5,46 @@ package com.avaje.ebean;
|
||||
*/
|
||||
public class ValuePair {
|
||||
|
||||
final Object value1;
|
||||
private final Object newValue;
|
||||
|
||||
final Object value2;
|
||||
private final Object oldValue;
|
||||
|
||||
public ValuePair(Object value1, Object value2) {
|
||||
this.value1 = value1;
|
||||
this.value2 = value2;
|
||||
public ValuePair(Object newValue, Object oldValue) {
|
||||
this.newValue = newValue;
|
||||
this.oldValue = oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first value.
|
||||
* Return the new value.
|
||||
*/
|
||||
public Object getNewValue() {
|
||||
return newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the old value.
|
||||
*/
|
||||
public Object getOldValue() {
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the new value.
|
||||
*/
|
||||
@Deprecated
|
||||
public Object getValue1() {
|
||||
return value1;
|
||||
return newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the second value.
|
||||
* Return the old value.
|
||||
*/
|
||||
@Deprecated
|
||||
public Object getValue2() {
|
||||
return value2;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value1 + "," + value2;
|
||||
return newValue + "," + oldValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface ColumnHstore {
|
||||
|
||||
}
|
||||
@@ -13,10 +13,5 @@ public enum ConcurrencyMode {
|
||||
/**
|
||||
* Use a version column.
|
||||
*/
|
||||
VERSION,
|
||||
|
||||
/**
|
||||
* Use all the columns (except Lobs).
|
||||
*/
|
||||
ALL
|
||||
VERSION
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.avaje.ebean.Query;
|
||||
* You may also put use the Transient annotation with the Formula annotation.
|
||||
* The effect of the Transient annotation in this case is that the formula will
|
||||
* <b>NOT</b> be included in queries by default - you have to explicitly include
|
||||
* it via {@link Query#select(String)} or {@link Query#join(String, String)}.
|
||||
* it via {@link Query#select(String)} or {@link Query#fetch(String, String, com.avaje.ebean.FetchConfig)}.
|
||||
* You may want to do this if the Formula is relatively expensive and only want
|
||||
* it included in the query when you explicitly state it.
|
||||
* </p>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* An annotation for declaring an index on a single column.
|
||||
*
|
||||
* @author rvbiljouw
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Index {
|
||||
|
||||
/**
|
||||
* Name of the index
|
||||
*
|
||||
* @return index name
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
@@ -1,118 +1,128 @@
|
||||
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;
|
||||
|
||||
import com.avaje.ebean.TxIsolation;
|
||||
import com.avaje.ebean.TxType;
|
||||
|
||||
/**
|
||||
* Specify transaction scoping for a method.
|
||||
* <p>
|
||||
* <b><i> This is only supported if "Enhancement" is used via javaagent, ANT
|
||||
* task or IDE enhancement plugin etc. </i></b>
|
||||
* </p>
|
||||
* <p>
|
||||
* Note: Currently there are 3 known annotations that perform this role.
|
||||
* <ul>
|
||||
* <li>EJB's javax.ejb.TransactionAttribute</li>
|
||||
* <li>Spring's org.springframework.transaction.annotation.Transactional</li>
|
||||
* <li>and this one, Ebean's own com.avaje.ebean.annotation.Transactional</li>
|
||||
* </ul>
|
||||
* Spring created their one because the EJB annotation does not support features
|
||||
* such as isolation level and specifying rollbackOn, noRollbackOn exceptions.
|
||||
* This one exists for Ebean because I agree that the standard one is
|
||||
* insufficient and don't want to include a dependency on Spring.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default behaviour of EJB (and hence Spring) is to NOT ROLLBACK on checked
|
||||
* exceptions. I find this very counter-intuitive. Ebean will provide a property
|
||||
* to set the default behaviour to rollback on any exception and optionally
|
||||
* change the setting to be consistent with EJB/Spring if people wish to do so.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
*
|
||||
* // a normal class
|
||||
* public class MySimpleUserService {
|
||||
*
|
||||
* // this method is transactional automatically handling
|
||||
* // transaction begin, commit and rollback etc
|
||||
* @Transactional
|
||||
* public void runInTrans() throws IOException {
|
||||
*
|
||||
* // tasks performed within the transaction
|
||||
* ...
|
||||
* // find some objects
|
||||
* Customer cust = Ebean.find(Customer.class, 1);
|
||||
*
|
||||
* Order order = ...;
|
||||
* ...
|
||||
* // save some objects
|
||||
* Ebean.save(customer);
|
||||
* Ebean.save(order);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* During development and testing you can set a debug level which will log the
|
||||
* transaction begin, commit and rollback events so that you can easily confirm
|
||||
* it is behaving as you would expect.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* ## in ebean.properties file
|
||||
*
|
||||
* ## Log transaction begins and ends etc
|
||||
* ## (0=NoLogging 1=minimal ... 9=logAll)
|
||||
* ebean.debug.transaction=3
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Transactional {
|
||||
|
||||
/**
|
||||
* The type of transaction scoping. Defaults to REQUIRED.
|
||||
*/
|
||||
TxType type() default TxType.REQUIRED;
|
||||
|
||||
/**
|
||||
* The transaction isolation level this transaction should have.
|
||||
* <p>
|
||||
* This will only be used if this scope creates the transaction. If the
|
||||
* transaction has already started then this will currently be ignored (you
|
||||
* could argue that it should throw an exception).
|
||||
* </p>
|
||||
*/
|
||||
TxIsolation isolation() default TxIsolation.DEFAULT;
|
||||
|
||||
/**
|
||||
* Set this to true if the transaction should be only contain queries.
|
||||
*/
|
||||
boolean readOnly() default false;
|
||||
|
||||
/**
|
||||
* The name of the server that you want the transaction to be created from.
|
||||
* <p>
|
||||
* If left blank the 'default' server is used.
|
||||
* </p>
|
||||
*/
|
||||
String serverName() default "";
|
||||
|
||||
// int timeout() default 0;
|
||||
|
||||
/**
|
||||
* The throwable's that will explicitly cause a rollback to occur.
|
||||
*/
|
||||
Class<? extends Throwable>[] rollbackFor() default {};
|
||||
|
||||
/**
|
||||
* The throwable's that will explicitly NOT cause a rollback to occur.
|
||||
*/
|
||||
Class<? extends Throwable>[] noRollbackFor() default {};
|
||||
|
||||
};
|
||||
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;
|
||||
|
||||
import com.avaje.ebean.TxIsolation;
|
||||
import com.avaje.ebean.TxType;
|
||||
import com.avaje.ebean.config.PersistBatch;
|
||||
|
||||
/**
|
||||
* Specify transaction scoping for a method.
|
||||
* <p>
|
||||
* <b><i> This is only supported if "Enhancement" is used via javaagent, ANT
|
||||
* task or IDE enhancement plugin etc. </i></b>
|
||||
* </p>
|
||||
* <p>
|
||||
* Note: Currently there are 3 known annotations that perform this role.
|
||||
* <ul>
|
||||
* <li>EJB's javax.ejb.TransactionAttribute</li>
|
||||
* <li>Spring's org.springframework.transaction.annotation.Transactional</li>
|
||||
* <li>and this one, Ebean's own com.avaje.ebean.annotation.Transactional</li>
|
||||
* </ul>
|
||||
* Spring created their one because the EJB annotation does not support features
|
||||
* such as isolation level and specifying rollbackOn, noRollbackOn exceptions.
|
||||
* This one exists for Ebean because I agree that the standard one is
|
||||
* insufficient and don't want to include a dependency on Spring.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default behaviour of EJB (and hence Spring) is to NOT ROLLBACK on checked
|
||||
* exceptions. I find this very counter-intuitive. Ebean will provide a property
|
||||
* to set the default behaviour to rollback on any exception and optionally
|
||||
* change the setting to be consistent with EJB/Spring if people wish to do so.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // a normal class
|
||||
* public class MySimpleUserService {
|
||||
*
|
||||
* // this method is transactional automatically handling
|
||||
* // transaction begin, commit and rollback etc
|
||||
* @Transactional
|
||||
* public void runInTrans() throws IOException {
|
||||
*
|
||||
* // tasks performed within the transaction
|
||||
* ...
|
||||
* // find some objects
|
||||
* Customer cust = ebeanServer.find(Customer.class, 42);
|
||||
*
|
||||
* Order order = ...;
|
||||
* ...
|
||||
* // save some objects
|
||||
* ebeanServer.save(customer);
|
||||
* ebeanServer.save(order);
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Transactional {
|
||||
|
||||
/**
|
||||
* The type of transaction scoping. Defaults to REQUIRED.
|
||||
*/
|
||||
TxType type() default TxType.REQUIRED;
|
||||
|
||||
/**
|
||||
* Persist batch mode for the transaction.
|
||||
*/
|
||||
PersistBatch batch() default PersistBatch.INHERIT;
|
||||
|
||||
/**
|
||||
* Persist batch mode for the request if not set on the transaction.
|
||||
* <p>
|
||||
* If batch is set to NONE then batchOnCascade can be set to INSERT or ALL
|
||||
* and then each save(), delete(), insert(), update() request that cascades
|
||||
* to child beans can use JDBC batch.
|
||||
* </p>
|
||||
*/
|
||||
PersistBatch batchOnCascade() default PersistBatch.INHERIT;
|
||||
|
||||
/**
|
||||
* The batch size to use when using JDBC batch mode.
|
||||
* <p>
|
||||
* If unset this defaults to the value set in ServerConfig.
|
||||
* </p>
|
||||
*/
|
||||
int batchSize() default 0;
|
||||
|
||||
/**
|
||||
* The transaction isolation level this transaction should have.
|
||||
* <p>
|
||||
* This will only be used if this scope creates the transaction. If the
|
||||
* transaction has already started then this will currently be ignored (you
|
||||
* could argue that it should throw an exception).
|
||||
* </p>
|
||||
*/
|
||||
TxIsolation isolation() default TxIsolation.DEFAULT;
|
||||
|
||||
/**
|
||||
* Set this to true if the transaction should be only contain queries.
|
||||
*/
|
||||
boolean readOnly() default false;
|
||||
|
||||
/**
|
||||
* The name of the server that you want the transaction to be created from.
|
||||
* <p>
|
||||
* If left blank the 'default' server is used.
|
||||
* </p>
|
||||
*/
|
||||
String serverName() default "";
|
||||
|
||||
// int timeout() default 0;
|
||||
|
||||
/**
|
||||
* The Throwable's that will explicitly cause a rollback to occur.
|
||||
*/
|
||||
Class<? extends Throwable>[] rollbackFor() default {};
|
||||
|
||||
/**
|
||||
* The Throwable's that will explicitly NOT cause a rollback to occur.
|
||||
*/
|
||||
Class<? extends Throwable>[] noRollbackFor() default {};
|
||||
|
||||
};
|
||||
|
||||
@@ -3,11 +3,8 @@ package com.avaje.ebean.bean;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.avaje.ebean.ExpressionList;
|
||||
import com.avaje.ebean.Query;
|
||||
|
||||
/**
|
||||
* Lazy loading capable Maps, Lists and Sets.
|
||||
@@ -34,24 +31,29 @@ public interface BeanCollection<E> extends Serializable {
|
||||
ALL
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the collection back to an empty state ready for reloading.
|
||||
* <p>
|
||||
* This is done as part of bean refresh.
|
||||
*/
|
||||
public void reset(EntityBean ownerBean, String propertyName);
|
||||
|
||||
/**
|
||||
* Return true if the collection is empty and untouched. Used to detect if a
|
||||
* collection was 'cleared' deliberately or just un-initialised.
|
||||
*/
|
||||
public boolean isEmptyAndUntouched();
|
||||
|
||||
/**
|
||||
* Return the bean that owns this collection.
|
||||
*/
|
||||
public Object getOwnerBean();
|
||||
public EntityBean getOwnerBean();
|
||||
|
||||
/**
|
||||
* Return the bean property name this collection represents.
|
||||
*/
|
||||
public String getPropertyName();
|
||||
|
||||
/**
|
||||
* Return the index position of this collection in the lazy/query loader.
|
||||
* <p>
|
||||
* Used for batch loading of collections.
|
||||
* </p>
|
||||
*/
|
||||
public int getLoaderIndex();
|
||||
|
||||
/**
|
||||
* Check after the lazy load that the underlying collection is not null
|
||||
* (handle case where join to many not outer).
|
||||
@@ -75,39 +77,23 @@ public interface BeanCollection<E> extends Serializable {
|
||||
*/
|
||||
public void setFilterMany(ExpressionList<?> filterMany);
|
||||
|
||||
/**
|
||||
* Set when this collection is being loaded via a background thread.
|
||||
* <p>
|
||||
* Refer to {@link Query#setBackgroundFetchAfter(int)}
|
||||
* </p>
|
||||
*/
|
||||
public void setBackgroundFetch(Future<Integer> future);
|
||||
|
||||
/**
|
||||
* Wait for the fetch to complete with a given timeout.
|
||||
* <p>
|
||||
* Refer to {@link Query#setBackgroundFetchAfter(int)}
|
||||
* </p>
|
||||
*/
|
||||
public void backgroundFetchWait(long wait, TimeUnit timeUnit);
|
||||
|
||||
/**
|
||||
* Wait for the fetch to complete.
|
||||
* <p>
|
||||
* Refer to {@link Query#setBackgroundFetchAfter(int)}
|
||||
* </p>
|
||||
*/
|
||||
public void backgroundFetchWait();
|
||||
|
||||
/**
|
||||
* Set a listener to be notified when the BeanCollection is first touched.
|
||||
*/
|
||||
public void setBeanCollectionTouched(BeanCollectionTouched notify);
|
||||
|
||||
/**
|
||||
* Set the loader that will be used to lazy/query load this collection.
|
||||
* Return true if the collection has been registered with the batch loading context.
|
||||
*/
|
||||
public void setLoader(int beanLoaderIndex, BeanCollectionLoader beanLoader);
|
||||
public boolean isRegisteredWithLoadContext();
|
||||
|
||||
/**
|
||||
* Set the loader that will be used to lazy/query load this collection.
|
||||
* <p>
|
||||
* This is effectively the batch loading context this collection is registered with.
|
||||
* </p>
|
||||
*/
|
||||
public void setLoader(BeanCollectionLoader beanLoader);
|
||||
|
||||
/**
|
||||
* Set to true if you want the BeanCollection to be treated as read only. This
|
||||
@@ -129,11 +115,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
*/
|
||||
public void internalAdd(Object bean);
|
||||
|
||||
/**
|
||||
* Returns the underlying List Set or Map object.
|
||||
*/
|
||||
public Object getActualCollection();
|
||||
|
||||
/**
|
||||
* Return the number of elements in the List Set or Map.
|
||||
*/
|
||||
@@ -158,34 +139,6 @@ public interface BeanCollection<E> extends Serializable {
|
||||
*/
|
||||
public Collection<?> getActualEntries();
|
||||
|
||||
/**
|
||||
* Set to true if maxRows was hit and there are actually more rows available.
|
||||
* <p>
|
||||
* Can be used by client code that is paging through results using
|
||||
* setFirstRow() setMaxRows(). If this returns true then the client can
|
||||
* display a 'next' button etc.
|
||||
* </p>
|
||||
*/
|
||||
public boolean hasMoreRows();
|
||||
|
||||
/**
|
||||
* Set to true when maxRows is hit but there are actually more rows available.
|
||||
* This is set so that client code knows that there is more data available.
|
||||
*/
|
||||
public void setHasMoreRows(boolean hasMoreRows);
|
||||
|
||||
/**
|
||||
* Returns true if the fetch has finished. False if the fetch is continuing in
|
||||
* a background thread.
|
||||
*/
|
||||
public boolean isFinishedFetch();
|
||||
|
||||
/**
|
||||
* Set to true when a fetch has finished. Used when a fetch continues in the
|
||||
* background.
|
||||
*/
|
||||
public void setFinishedFetch(boolean finishedFetch);
|
||||
|
||||
/**
|
||||
* return true if there are real rows held. Return false is this is using
|
||||
* Deferred fetch to lazy load the rows and the rows have not yet been
|
||||
|
||||
@@ -12,5 +12,5 @@ public interface BeanCollectionAdd {
|
||||
/**
|
||||
* Add a loaded bean to the collection.
|
||||
*/
|
||||
public void addBean(Object bean);
|
||||
public void addBean(EntityBean bean);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@ import java.io.Serializable;
|
||||
*/
|
||||
public interface EntityBean extends Serializable {
|
||||
|
||||
public String[] _ebean_getPropertyNames();
|
||||
|
||||
public String _ebean_getPropertyName(int pos);
|
||||
|
||||
/**
|
||||
* Return the enhancement marker value.
|
||||
* <p>
|
||||
@@ -79,31 +83,19 @@ public interface EntityBean extends Serializable {
|
||||
*/
|
||||
public Object _ebean_createCopy();
|
||||
|
||||
/**
|
||||
* Return the fields in their index order.
|
||||
*/
|
||||
public String[] _ebean_getFieldNames();
|
||||
|
||||
/**
|
||||
* Set the value of a field of an entity bean of this type.
|
||||
* <p>
|
||||
* Note that using this method bypasses any interception that otherwise occurs
|
||||
* on entity beans. That means lazy loading and oldValues creation.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldIndex
|
||||
* the index of the field
|
||||
* @param entityBean
|
||||
* the entityBean of this type to modify
|
||||
* @param value
|
||||
* the value to set
|
||||
*/
|
||||
public void _ebean_setField(int fieldIndex, Object entityBean, Object value);
|
||||
public void _ebean_setField(int fieldIndex, Object value);
|
||||
|
||||
/**
|
||||
* Set the field value with interception.
|
||||
*/
|
||||
public void _ebean_setFieldIntercept(int fieldIndex, Object entityBean, Object value);
|
||||
public void _ebean_setFieldIntercept(int fieldIndex, Object value);
|
||||
|
||||
/**
|
||||
* Return the value of a field from an entity bean of this type.
|
||||
@@ -111,17 +103,12 @@ public interface EntityBean extends Serializable {
|
||||
* Note that using this method bypasses any interception that otherwise occurs
|
||||
* on entity beans. That means lazy loading.
|
||||
* </p>
|
||||
*
|
||||
* @param fieldIndex
|
||||
* the index of the field
|
||||
* @param entityBean
|
||||
* the entityBean to get the value from
|
||||
*/
|
||||
public Object _ebean_getField(int fieldIndex, Object entityBean);
|
||||
public Object _ebean_getField(int fieldIndex);
|
||||
|
||||
/**
|
||||
* Return the field value with interception.
|
||||
*/
|
||||
public Object _ebean_getFieldIntercept(int fieldIndex, Object entityBean);
|
||||
public Object _ebean_getFieldIntercept(int fieldIndex);
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
||||
package com.avaje.ebean.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Identifies a unique node of an object graph.
|
||||
@@ -70,7 +69,7 @@ public final class ObjectGraphNode implements Serializable {
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 31 * originQueryPoint.hashCode();
|
||||
hc = 31 * hc + Objects.hashCode(path);
|
||||
hc = 31 * hc + (path == null ? 0 : path.hashCode());
|
||||
return hc;
|
||||
}
|
||||
|
||||
@@ -83,7 +82,7 @@ public final class ObjectGraphNode implements Serializable {
|
||||
}
|
||||
|
||||
ObjectGraphNode e = (ObjectGraphNode) obj;
|
||||
return Objects.equals(e.path, path)
|
||||
return ((e.path == path) || (e.path != null && e.path.equals(path)))
|
||||
&& e.originQueryPoint.equals(originQueryPoint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.avaje.ebean.common;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
@@ -12,7 +10,6 @@ import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.BeanCollectionTouched;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
|
||||
/**
|
||||
* Base class for List Set and Map implementations of BeanCollection.
|
||||
@@ -32,36 +29,24 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
|
||||
protected transient ExpressionList<?> filterMany;
|
||||
|
||||
protected int loaderIndex;
|
||||
/**
|
||||
* Flag set when registered with the batch loading context.
|
||||
*/
|
||||
protected boolean registeredWithLoadContext;
|
||||
|
||||
protected String ebeanServerName;
|
||||
|
||||
protected transient BeanCollectionTouched beanCollectionTouched;
|
||||
|
||||
protected transient Future<Integer> fetchFuture;
|
||||
|
||||
/**
|
||||
* The owning bean (used for lazy fetch).
|
||||
*/
|
||||
protected final Object ownerBean;
|
||||
protected EntityBean ownerBean;
|
||||
|
||||
/**
|
||||
* The name of this property in the owning bean (used for lazy fetch).
|
||||
*/
|
||||
protected final String propertyName;
|
||||
|
||||
/**
|
||||
* Can be false when a background thread is used to continue the fetch the
|
||||
* rows. It will set this to true when it is finished. If no background thread
|
||||
* is used then this should already be true.
|
||||
*/
|
||||
protected boolean finishedFetch = true;
|
||||
|
||||
/**
|
||||
* Flag set to true if rows are limited by firstRow maxRows and more rows
|
||||
* exist. For use by client to enable 'next' for paging.
|
||||
*/
|
||||
protected boolean hasMoreRows;
|
||||
protected String propertyName;
|
||||
|
||||
protected ModifyHolder<E> modifyHolder;
|
||||
|
||||
@@ -70,30 +55,30 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
protected boolean modifyRemoveListening;
|
||||
protected boolean modifyListening;
|
||||
|
||||
/**
|
||||
* Flag used to tell if empty collections have been cleared etc or just
|
||||
* uninitialised.
|
||||
*/
|
||||
protected boolean touched;
|
||||
|
||||
/**
|
||||
* Constructor not non-lazy loading collection.
|
||||
*/
|
||||
public AbstractBeanCollection() {
|
||||
this.ownerBean = null;
|
||||
this.propertyName = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to create deferred fetch proxy.
|
||||
*/
|
||||
public AbstractBeanCollection(BeanCollectionLoader loader, Object ownerBean, String propertyName) {
|
||||
public AbstractBeanCollection(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
|
||||
this.loader = loader;
|
||||
this.ebeanServerName = loader.getName();
|
||||
this.ownerBean = ownerBean;
|
||||
this.propertyName = propertyName;
|
||||
|
||||
if (ownerBean instanceof EntityBean) {
|
||||
EntityBeanIntercept ebi = ((EntityBean) ownerBean)._ebean_getIntercept();
|
||||
this.readOnly = ebi.isReadOnly();
|
||||
}
|
||||
this.readOnly = ownerBean._ebean_getIntercept().isReadOnly();
|
||||
}
|
||||
|
||||
public Object getOwnerBean() {
|
||||
public EntityBean getOwnerBean() {
|
||||
return ownerBean;
|
||||
}
|
||||
|
||||
@@ -101,10 +86,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
public int getLoaderIndex() {
|
||||
return loaderIndex;
|
||||
}
|
||||
|
||||
public ExpressionList<?> getFilterMany() {
|
||||
return filterMany;
|
||||
}
|
||||
@@ -128,7 +109,14 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
checkEmptyLazyLoad();
|
||||
}
|
||||
|
||||
protected void touched() {
|
||||
/**
|
||||
* Set touched. If setFlag is false then typically an isEmpty() call and still
|
||||
* considering that to be untouched.
|
||||
*/
|
||||
protected void touched(boolean setFlag) {
|
||||
if (setFlag) {
|
||||
touched = true;
|
||||
}
|
||||
if (beanCollectionTouched != null) {
|
||||
// only call this once
|
||||
beanCollectionTouched.notifyTouched(this);
|
||||
@@ -140,8 +128,12 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
this.beanCollectionTouched = notify;
|
||||
}
|
||||
|
||||
public void setLoader(int beanLoaderIndex, BeanCollectionLoader loader) {
|
||||
this.loaderIndex = beanLoaderIndex;
|
||||
public boolean isRegisteredWithLoadContext() {
|
||||
return registeredWithLoadContext;
|
||||
}
|
||||
|
||||
public void setLoader(BeanCollectionLoader loader) {
|
||||
this.registeredWithLoadContext = true;
|
||||
this.loader = loader;
|
||||
this.ebeanServerName = loader.getName();
|
||||
}
|
||||
@@ -154,66 +146,6 @@ public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if maxRows was hit and there are actually more rows available.
|
||||
* <p>
|
||||
* Can be used by client code that is paging through results using
|
||||
* setFirstRow() setMaxRows(). If this returns true then the client can
|
||||
* display a 'next' button etc.
|
||||
* </p>
|
||||
*/
|
||||
public boolean hasMoreRows() {
|
||||
return hasMoreRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true when maxRows is hit but there are actually more rows available.
|
||||
* This is set so that client code knows that there is more data available.
|
||||
*/
|
||||
public void setHasMoreRows(boolean hasMoreRows) {
|
||||
this.hasMoreRows = hasMoreRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the fetch has finished. False if the fetch is continuing in
|
||||
* a background thread.
|
||||
*/
|
||||
public boolean isFinishedFetch() {
|
||||
return finishedFetch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true when a fetch has finished. Used when a fetch continues in the
|
||||
* background.
|
||||
*/
|
||||
public void setFinishedFetch(boolean finishedFetch) {
|
||||
this.finishedFetch = finishedFetch;
|
||||
}
|
||||
|
||||
public void setBackgroundFetch(Future<Integer> fetchFuture) {
|
||||
this.fetchFuture = fetchFuture;
|
||||
}
|
||||
|
||||
public void backgroundFetchWait(long wait, TimeUnit timeUnit) {
|
||||
if (fetchFuture != null) {
|
||||
try {
|
||||
fetchFuture.get(wait, timeUnit);
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void backgroundFetchWait() {
|
||||
if (fetchFuture != null) {
|
||||
try {
|
||||
fetchFuture.get();
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkReadOnly() {
|
||||
if (readOnly) {
|
||||
String msg = "This collection is in ReadOnly mode";
|
||||
|
||||
@@ -10,13 +10,15 @@ import java.util.ListIterator;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollectionAdd;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
|
||||
/**
|
||||
* List capable of lazy loading.
|
||||
*/
|
||||
public final class BeanList<E> extends AbstractBeanCollection<E> implements List<E>,
|
||||
BeanCollectionAdd {
|
||||
public final class BeanList<E> extends AbstractBeanCollection<E> implements List<E>, BeanCollectionAdd {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* The underlying List implementation.
|
||||
*/
|
||||
@@ -40,12 +42,25 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
/**
|
||||
* Used to create deferred fetch proxy.
|
||||
*/
|
||||
public BeanList(BeanCollectionLoader loader, Object ownerBean, String propertyName) {
|
||||
public BeanList(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
|
||||
super(loader, ownerBean, propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(EntityBean ownerBean, String propertyName) {
|
||||
this.ownerBean = ownerBean;
|
||||
this.propertyName = propertyName;
|
||||
this.list = null;
|
||||
this.touched = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmptyAndUntouched() {
|
||||
return !touched && (list == null || list.isEmpty());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addBean(Object bean) {
|
||||
public void addBean(EntityBean bean) {
|
||||
list.add((E) bean);
|
||||
}
|
||||
|
||||
@@ -75,16 +90,24 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
list = new ArrayList<E>();
|
||||
}
|
||||
}
|
||||
touched();
|
||||
touched(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void initAsUntouched() {
|
||||
init(false);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
init(true);
|
||||
}
|
||||
|
||||
private void init(boolean setTouched) {
|
||||
synchronized (this) {
|
||||
if (list == null) {
|
||||
lazyLoadCollection(false);
|
||||
}
|
||||
touched();
|
||||
touched(setTouched);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,13 +138,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying list.
|
||||
*/
|
||||
public Object getActualCollection() {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the underlying list is populated.
|
||||
*/
|
||||
@@ -138,7 +154,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuffer sb = new StringBuffer(50);
|
||||
sb.append("BeanList ");
|
||||
if (isReadOnly()) {
|
||||
sb.append("readOnly ");
|
||||
@@ -148,7 +164,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
|
||||
} else {
|
||||
sb.append("size[").append(list.size()).append("] ");
|
||||
sb.append("hasMoreRows[").append(hasMoreRows).append("] ");
|
||||
sb.append("list").append(list).append("");
|
||||
}
|
||||
return sb.toString();
|
||||
@@ -257,7 +272,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
init();
|
||||
initAsUntouched();
|
||||
return list.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,12 +8,15 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
|
||||
/**
|
||||
* Map capable of lazy loading.
|
||||
*/
|
||||
public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Map<K, E> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* The underlying map implementation.
|
||||
*/
|
||||
@@ -33,10 +36,22 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
this(new LinkedHashMap<K, E>());
|
||||
}
|
||||
|
||||
public BeanMap(BeanCollectionLoader ebeanServer, Object ownerBean, String propertyName) {
|
||||
public BeanMap(BeanCollectionLoader ebeanServer, EntityBean ownerBean, String propertyName) {
|
||||
super(ebeanServer, ownerBean, propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(EntityBean ownerBean, String propertyName) {
|
||||
this.ownerBean = ownerBean;
|
||||
this.propertyName = propertyName;
|
||||
this.map = null;
|
||||
this.touched = false;
|
||||
}
|
||||
|
||||
public boolean isEmptyAndUntouched() {
|
||||
return !touched && (map == null || map.isEmpty());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void internalPut(Object key, Object bean) {
|
||||
if (map == null) {
|
||||
@@ -83,16 +98,24 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
map = new LinkedHashMap<K, E>();
|
||||
}
|
||||
}
|
||||
touched();
|
||||
touched(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void initAsUntouched() {
|
||||
init(false);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
init(true);
|
||||
}
|
||||
|
||||
private void init(boolean setTouched) {
|
||||
synchronized (this) {
|
||||
if (map == null) {
|
||||
lazyLoadCollection(false);
|
||||
}
|
||||
touched();
|
||||
touched(setTouched);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,15 +154,8 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying map.
|
||||
*/
|
||||
public Object getActualCollection() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuilder sb = new StringBuilder(50);
|
||||
sb.append("BeanMap ");
|
||||
if (isReadOnly()) {
|
||||
sb.append("readOnly ");
|
||||
@@ -149,7 +165,6 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
|
||||
} else {
|
||||
sb.append("size[").append(map.size()).append("]");
|
||||
sb.append(" hasMoreRows[").append(hasMoreRows).append("]");
|
||||
sb.append(" map").append(map);
|
||||
}
|
||||
return sb.toString();
|
||||
@@ -209,7 +224,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
init();
|
||||
initAsUntouched();
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,12 +8,15 @@ import java.util.Set;
|
||||
|
||||
import com.avaje.ebean.bean.BeanCollectionAdd;
|
||||
import com.avaje.ebean.bean.BeanCollectionLoader;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
|
||||
/**
|
||||
* Set capable of lazy loading.
|
||||
*/
|
||||
public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E>, BeanCollectionAdd {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* The underlying Set implementation.
|
||||
*/
|
||||
@@ -33,12 +36,24 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
this(new LinkedHashSet<E>());
|
||||
}
|
||||
|
||||
public BeanSet(BeanCollectionLoader loader, Object ownerBean, String propertyName) {
|
||||
public BeanSet(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
|
||||
super(loader, ownerBean, propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(EntityBean ownerBean, String propertyName) {
|
||||
this.ownerBean = ownerBean;
|
||||
this.propertyName = propertyName;
|
||||
this.set = null;
|
||||
this.touched = false;
|
||||
}
|
||||
|
||||
public boolean isEmptyAndUntouched() {
|
||||
return !touched && (set == null || set.isEmpty());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addBean(Object bean) {
|
||||
public void addBean(EntityBean bean) {
|
||||
set.add((E) bean);
|
||||
}
|
||||
|
||||
@@ -83,16 +98,24 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
set = new LinkedHashSet<E>();
|
||||
}
|
||||
}
|
||||
touched();
|
||||
touched(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void initAsUntouched() {
|
||||
init(false);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
init(true);
|
||||
}
|
||||
|
||||
private void init(boolean setTouched) {
|
||||
synchronized (this) {
|
||||
if (set == null) {
|
||||
lazyLoadCollection(true);
|
||||
}
|
||||
touched();
|
||||
touched(setTouched);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +151,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuffer sb = new StringBuffer(50);
|
||||
sb.append("BeanSet ");
|
||||
if (isReadOnly()) {
|
||||
sb.append("readOnly ");
|
||||
@@ -138,7 +161,6 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
|
||||
} else {
|
||||
sb.append("size[").append(set.size()).append("]");
|
||||
sb.append(" hasMoreRows[").append(hasMoreRows).append("]");
|
||||
sb.append(" set").append(set);
|
||||
}
|
||||
return sb.toString();
|
||||
@@ -217,7 +239,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
init();
|
||||
initAsUntouched();
|
||||
return set.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Provides some base implementation for NamingConventions.
|
||||
*
|
||||
*
|
||||
* @author emcgreal
|
||||
*/
|
||||
public abstract class AbstractNamingConvention implements NamingConvention {
|
||||
@@ -41,7 +41,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
|
||||
/** Used to trim off extra prefix for M2M. */
|
||||
protected int rhsPrefixLength = 3;
|
||||
|
||||
protected boolean useForeignKeyPrefix = true;
|
||||
protected boolean useForeignKeyPrefix;
|
||||
|
||||
/**
|
||||
* Construct with a sequence format and useForeignKeyPrefix setting.
|
||||
@@ -53,12 +53,12 @@ public abstract class AbstractNamingConvention implements NamingConvention {
|
||||
|
||||
/**
|
||||
* Construct with a sequence format.
|
||||
*
|
||||
* @param sequenceFormat
|
||||
* the sequence format
|
||||
*
|
||||
* @param sequenceFormat the sequence format
|
||||
*/
|
||||
public AbstractNamingConvention(String sequenceFormat) {
|
||||
this.sequenceFormat = sequenceFormat;
|
||||
this.useForeignKeyPrefix = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,10 +125,9 @@ public abstract class AbstractNamingConvention implements NamingConvention {
|
||||
* The format should include "{table}". When generating the sequence name
|
||||
* {table} is replaced with the actual table name.
|
||||
* </p>
|
||||
*
|
||||
* @param sequenceFormat
|
||||
* string containing "{table}" which is replaced with the actual
|
||||
* table name to generate the sequence name.
|
||||
*
|
||||
* @param sequenceFormat string containing "{table}" which is replaced with the actual
|
||||
* table name to generate the sequence name.
|
||||
*/
|
||||
public void setSequenceFormat(String sequenceFormat) {
|
||||
this.sequenceFormat = sequenceFormat;
|
||||
@@ -173,7 +172,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
|
||||
* This first checks for the @Table annotation and if not present uses the
|
||||
* naming convention to define the table name.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @see #getTableNameFromAnnotation(Class)
|
||||
* @see #getTableNameByConvention(Class)
|
||||
*/
|
||||
@@ -240,8 +239,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
|
||||
if (t != null && !isEmpty(t.name())) {
|
||||
// Note: empty catalog and schema are converted to null
|
||||
// Only need to convert quoted identifiers from annotations
|
||||
return new TableName(quoteIdentifiers(t.catalog()), quoteIdentifiers(t.schema()),
|
||||
quoteIdentifiers(t.name()));
|
||||
return new TableName(quoteIdentifiers(t.catalog()), quoteIdentifiers(t.schema()), quoteIdentifiers(t.name()));
|
||||
}
|
||||
|
||||
// No annotation
|
||||
@@ -279,4 +277,16 @@ public abstract class AbstractNamingConvention implements NamingConvention {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings from properties.
|
||||
*/
|
||||
@Override
|
||||
public void loadFromProperties(PropertiesWrapper properties) {
|
||||
|
||||
useForeignKeyPrefix = properties.getBoolean("namingConvention.useForeignKeyPrefix", useForeignKeyPrefix);
|
||||
sequenceFormat = properties.get("namingConvention.sequenceFormat", sequenceFormat);
|
||||
schema = properties.get("namingConvention.schema", schema);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -155,14 +155,6 @@ public class AutofetchConfig {
|
||||
return logDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the log directory substituting any expressions such as
|
||||
* ${catalina.base} etc.
|
||||
*/
|
||||
public String getLogDirectoryWithEval() {
|
||||
return GlobalProperties.evaluateExpressions(logDirectory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the directory to put the autofetch log in.
|
||||
*/
|
||||
@@ -234,22 +226,20 @@ public class AutofetchConfig {
|
||||
/**
|
||||
* Load the settings from the properties file.
|
||||
*/
|
||||
public void loadSettings(GlobalProperties.PropertySource p) {
|
||||
public void loadSettings(PropertiesWrapper p) {
|
||||
|
||||
logDirectory = p.get("autofetch.logDirectory", null);
|
||||
queryTuning = p.getBoolean("autofetch.querytuning", false);
|
||||
queryTuningAddVersion = p.getBoolean("autofetch.queryTuningAddVersion", false);
|
||||
garbageCollectionOnShutdown = p.getBoolean("autofetch.garbageCollectionOnShutdown", false);
|
||||
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", false);
|
||||
mode = p.getEnum(AutofetchMode.class, "autofetch.implicitmode", AutofetchMode.DEFAULT_ONIFEMPTY);
|
||||
profiling = p.getBoolean("autofetch.profiling", profiling);
|
||||
mode = p.getEnum(AutofetchMode.class, "autofetch.implicitmode", mode);
|
||||
|
||||
profilingMin = p.getInt("autofetch.profiling.min", 1);
|
||||
profilingBase = p.getInt("autofetch.profiling.base", 10);
|
||||
profilingMin = p.getInt("autofetch.profiling.min", profilingMin);
|
||||
profilingBase = p.getInt("autofetch.profiling.base", profilingBase);
|
||||
|
||||
String rate = p.get("autofetch.profiling.rate", "0.05");
|
||||
profilingRate = Double.parseDouble(rate);
|
||||
|
||||
profileUpdateFrequency = p.getInt("autofetch.profiling.updatefrequency", 60);
|
||||
profilingRate = p.getDouble("autofetch.profiling.rate", profilingRate);
|
||||
profileUpdateFrequency = p.getInt("autofetch.profiling.updatefrequency", profileUpdateFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
import com.avaje.ebean.config.GlobalProperties.PropertySource;
|
||||
|
||||
/**
|
||||
* Helper to read server specific properties from ebean.properties.
|
||||
*/
|
||||
class ConfigPropertyMap implements PropertySource {
|
||||
|
||||
private final String serverName;
|
||||
|
||||
public ConfigPropertyMap(String serverName) {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
|
||||
public String get(String key, String defaultValue) {
|
||||
String namedKey = "ebean." + serverName + "." + key;
|
||||
String inheritKey = "ebean." + key;
|
||||
String value = GlobalProperties.get(namedKey, null);
|
||||
if (value == null) {
|
||||
value = GlobalProperties.get(inheritKey, null);
|
||||
}
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public int getInt(String key, int defaultValue) {
|
||||
|
||||
String value = get(key, String.valueOf(defaultValue));
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public boolean getBoolean(String key, boolean defaultValue) {
|
||||
|
||||
String value = get(key, String.valueOf(defaultValue));
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
public <T extends Enum<T>> T getEnum(Class<T> enumType, String key, T defaultValue) {
|
||||
String level = get(key, defaultValue.name());
|
||||
return Enum.valueOf(enumType, level.toUpperCase());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,464 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Configuration for the container that holds the EbeanServer instances.
|
||||
* <p>
|
||||
* Provides configuration for cluster communication (if clustering is used). The cluster communication is
|
||||
* used to invalidate appropriate parts of the L2 cache across the cluster.
|
||||
*/
|
||||
public class ContainerConfig {
|
||||
|
||||
/**
|
||||
* Communication mode used for clustering.
|
||||
*/
|
||||
public enum ClusterMode {
|
||||
|
||||
/**
|
||||
* No clustering.
|
||||
*/
|
||||
NONE,
|
||||
|
||||
/**
|
||||
* Use Multicast networking for cluster wide communication.
|
||||
*/
|
||||
MULTICAST,
|
||||
|
||||
/**
|
||||
* Use TCP Sockets for cluster wide communication.
|
||||
*/
|
||||
SOCKET
|
||||
}
|
||||
|
||||
/**
|
||||
* The cluster mode to use.
|
||||
*/
|
||||
ClusterMode mode = ClusterMode.NONE;
|
||||
|
||||
/**
|
||||
* Configuration if using TCP sockets for clustering communication.
|
||||
*/
|
||||
SocketConfig socketConfig = new SocketConfig();
|
||||
|
||||
/**
|
||||
* Configuration if using Multicast for clustering communication.
|
||||
*/
|
||||
MulticastConfig multicastConfig = new MulticastConfig();
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// MulticastConfig
|
||||
|
||||
/**
|
||||
* The configuration for clustering using Multicast networking.
|
||||
*/
|
||||
public static class MulticastConfig {
|
||||
|
||||
int managerSleepMillis = 80;
|
||||
int lastSendTimeFreqSecs = 300;//5mins
|
||||
int lastStatusTimeFreqSecs = 600;//10mins
|
||||
int maxResendOutgoingAttempts = 200;
|
||||
int maxResendIncomingRequests = 50;
|
||||
|
||||
int listenPort;
|
||||
String listenAddress;
|
||||
int sendPort;
|
||||
String sendAddress;
|
||||
|
||||
// Note 1500 is Ethernet MTU and this must be less than UDP max packet size of 65507
|
||||
int maxSendPacketSize = 1500;
|
||||
|
||||
// Whether to send packets even when there are no other members online
|
||||
boolean sendWithNoMembers = true;
|
||||
|
||||
// When multiple instances are on same box you need to broadcast back locally
|
||||
boolean disableLoopback;
|
||||
int listenTimeToLive = -1;
|
||||
int listenTimeout = 1000;
|
||||
int listenBufferSize = 65500;
|
||||
// For multihomed environment the address the listener should bind to
|
||||
String listenBindAddress;
|
||||
|
||||
/**
|
||||
* Return the manager sleep millis.
|
||||
*/
|
||||
public int getManagerSleepMillis() {
|
||||
return managerSleepMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the manager sleep millis.
|
||||
*/
|
||||
public void setManagerSleepMillis(int managerSleepMillis) {
|
||||
this.managerSleepMillis = managerSleepMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last send time frequency.
|
||||
*/
|
||||
public int getLastSendTimeFreqSecs() {
|
||||
return lastSendTimeFreqSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last send time frequency.
|
||||
*/
|
||||
public void setLastSendTimeFreqSecs(int lastSendTimeFreqSecs) {
|
||||
this.lastSendTimeFreqSecs = lastSendTimeFreqSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last status time frequency.
|
||||
*/
|
||||
public int getLastStatusTimeFreqSecs() {
|
||||
return lastStatusTimeFreqSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last status time frequency.
|
||||
*/
|
||||
public void setLastStatusTimeFreqSecs(int lastStatusTimeFreqSecs) {
|
||||
this.lastStatusTimeFreqSecs = lastStatusTimeFreqSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum number of times we will try to re-send a given packet before giving up sending
|
||||
*/
|
||||
public int getMaxResendOutgoingAttempts() {
|
||||
return maxResendOutgoingAttempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum retry attempts for outgoing messages.
|
||||
*/
|
||||
public void setMaxResendOutgoingAttempts(int maxResendOutgoingAttempts) {
|
||||
this.maxResendOutgoingAttempts = maxResendOutgoingAttempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum number of times we will ask for a packet to be resent to us before giving up asking.
|
||||
*/
|
||||
public int getMaxResendIncomingRequests() {
|
||||
return maxResendIncomingRequests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum retry attempts for incoming messages.
|
||||
*/
|
||||
public void setMaxResendIncomingRequests(int maxResendIncomingRequests) {
|
||||
this.maxResendIncomingRequests = maxResendIncomingRequests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the listen port.
|
||||
*/
|
||||
public int getListenPort() {
|
||||
return listenPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listen port.
|
||||
*/
|
||||
public void setListenPort(int port) {
|
||||
this.listenPort = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the listen address.
|
||||
*/
|
||||
public String getListenAddress() {
|
||||
return listenAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listen address.
|
||||
*/
|
||||
public void setListenAddress(String listenAddress) {
|
||||
this.listenAddress = listenAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the send port.
|
||||
*/
|
||||
public int getSendPort() {
|
||||
return sendPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the send port.
|
||||
*/
|
||||
public void setSendPort(int sendPort) {
|
||||
this.sendPort = sendPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the send address.
|
||||
*/
|
||||
public String getSendAddress() {
|
||||
return sendAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the send address.
|
||||
*/
|
||||
public void setSendAddress(String sendAddress) {
|
||||
this.sendAddress = sendAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum send packet size.
|
||||
*/
|
||||
public int getMaxSendPacketSize() {
|
||||
return maxSendPacketSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum send packet size. Note 1500 is Ethernet MTU and this must be less than UDP max packet size of 65507.
|
||||
*/
|
||||
public void setMaxSendPacketSize(int maxSendPacketSize) {
|
||||
this.maxSendPacketSize = maxSendPacketSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if send messages when no other members in the cluster are up.
|
||||
*/
|
||||
public boolean isSendWithNoMembers() {
|
||||
return sendWithNoMembers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set true if send messages when no other members in the cluster are up.
|
||||
*/
|
||||
public void setSendWithNoMembers(boolean sendWithNoMembers) {
|
||||
this.sendWithNoMembers = sendWithNoMembers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if loopback is disabled. When multiple instances are on same box you need to broadcast back locally.
|
||||
*/
|
||||
public boolean isDisableLoopback() {
|
||||
return disableLoopback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if loopback is disabled. When multiple instances are on same box you need to broadcast back locally.
|
||||
*/
|
||||
public void setDisableLoopback(boolean disableLoopback) {
|
||||
this.disableLoopback = disableLoopback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the listen time to live.
|
||||
*/
|
||||
public int getListenTimeToLive() {
|
||||
return listenTimeToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listen time to live.
|
||||
*/
|
||||
public void setListenTimeToLive(int listenTimeToLive) {
|
||||
this.listenTimeToLive = listenTimeToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the listen timeout.
|
||||
*/
|
||||
public int getListenTimeout() {
|
||||
return listenTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the listen timeout.
|
||||
*/
|
||||
public void setListenTimeout(int listenTimeout) {
|
||||
this.listenTimeout = listenTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the listen buffer size.
|
||||
*/
|
||||
public int getListenBufferSize() {
|
||||
return listenBufferSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listen buffer size.
|
||||
*/
|
||||
public void setListenBufferSize(int listenBufferSize) {
|
||||
this.listenBufferSize = listenBufferSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the listener bind address (optional). For multihomed environment the address the listener should bind to.
|
||||
*/
|
||||
public String getListenBindAddress() {
|
||||
return listenBindAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listener bind address (optional). For multihomed environment the address the listener should bind to.
|
||||
*/
|
||||
public void setListenBindAddress(String listenBindAddress) {
|
||||
this.listenBindAddress = listenBindAddress;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// SocketConfig
|
||||
|
||||
/**
|
||||
* Configuration for clustering using TCP sockets.
|
||||
* <p>
|
||||
* This is good for when there are relatively small number of cluster members.
|
||||
*/
|
||||
public static class SocketConfig {
|
||||
|
||||
/**
|
||||
* This local server in host:port format.
|
||||
*/
|
||||
String localHostPort;
|
||||
|
||||
/**
|
||||
* All the cluster members in host:port format.
|
||||
*/
|
||||
List<String> members = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* core threads for the associated thread pool.
|
||||
*/
|
||||
int coreThreads = 2;
|
||||
|
||||
/**
|
||||
* Max threads for the associated thread pool.
|
||||
*/
|
||||
int maxThreads = 16;
|
||||
|
||||
String threadPoolName = "EbeanCluster";
|
||||
|
||||
/**
|
||||
* Return the host and port for this server instance.
|
||||
*/
|
||||
public String getLocalHostPort() {
|
||||
return localHostPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the host and port for this server instance.
|
||||
*/
|
||||
public void setLocalHostPort(String localHostPort) {
|
||||
this.localHostPort = localHostPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the host and port for all the members of the cluster.
|
||||
*/
|
||||
public List<String> getMembers() {
|
||||
return members;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all the host and port for all the members of the cluster.
|
||||
*/
|
||||
public void setMembers(List<String> members) {
|
||||
this.members = members;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of core threads to use.
|
||||
*/
|
||||
public int getCoreThreads() {
|
||||
return coreThreads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of core threads to use.
|
||||
*/
|
||||
public void setCoreThreads(int coreThreads) {
|
||||
this.coreThreads = coreThreads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of max threads to use.
|
||||
*/
|
||||
public int getMaxThreads() {
|
||||
return maxThreads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of max threads to use.
|
||||
*/
|
||||
public void setMaxThreads(int maxThreads) {
|
||||
this.maxThreads = maxThreads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the thread pool name.
|
||||
*/
|
||||
public String getThreadPoolName() {
|
||||
return threadPoolName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the thread pool name.
|
||||
*/
|
||||
public void setThreadPoolName(String threadPoolName) {
|
||||
this.threadPoolName = threadPoolName;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// Members
|
||||
|
||||
/**
|
||||
* Load the settings from properties.
|
||||
*/
|
||||
public void loadFromProperties(Properties properties) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cluster mode.
|
||||
*/
|
||||
public ClusterMode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cluster mode.
|
||||
*/
|
||||
public void setMode(ClusterMode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the socket communication configuration.
|
||||
*/
|
||||
public SocketConfig getSocketConfig() {
|
||||
return socketConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the socket communication configuration.
|
||||
*/
|
||||
public void setSocketConfig(SocketConfig socketConfig) {
|
||||
this.socketConfig = socketConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the multicast communication configuration.
|
||||
*/
|
||||
public MulticastConfig getMulticastConfig() {
|
||||
return multicastConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the multicast communication configuration.
|
||||
*/
|
||||
public void setMulticastConfig(MulticastConfig multicastConfig) {
|
||||
this.multicastConfig = multicastConfig;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.avaje.ebean.config;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.util.StringHelper;
|
||||
@@ -32,6 +33,8 @@ public class DataSourceConfig {
|
||||
|
||||
private int isolationLevel = Transaction.READ_COMMITTED;
|
||||
|
||||
private boolean autoCommit;
|
||||
|
||||
private String heartbeatSql;
|
||||
|
||||
private int heartbeatFreqSecs = 30;
|
||||
@@ -131,6 +134,20 @@ public class DataSourceConfig {
|
||||
public void setIsolationLevel(int isolationLevel) {
|
||||
this.isolationLevel = isolationLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return autoCommit setting.
|
||||
*/
|
||||
public boolean isAutoCommit() {
|
||||
return autoCommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to turn on autoCommit.
|
||||
*/
|
||||
public void setAutoCommit(boolean autoCommit) {
|
||||
this.autoCommit = autoCommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum number of connections the pool should maintain.
|
||||
@@ -432,47 +449,63 @@ public class DataSourceConfig {
|
||||
this.customProperties = customProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the settings by reading the ebean.properties file.
|
||||
*
|
||||
* @param serverName name of the server
|
||||
*/
|
||||
public void loadSettings(String serverName) {
|
||||
loadSettingsCustomPrefix("datasource." + serverName + ".", new GlobalProperties.DelegatedGlobalPropertySource(serverName));
|
||||
loadSettings(new PropertiesWrapper("datasource", serverName, PropertyMap.defaultProperties()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the settings from ebean.properties.
|
||||
* Load the settings from the properties supplied.
|
||||
* <p>
|
||||
* You can use this when you have your own properties to use for configuration.
|
||||
* </p>
|
||||
*
|
||||
* @param properties the properties to configure the datasource
|
||||
* @param serverName the name of the specific datasource (optional)
|
||||
*/
|
||||
public void loadSettingsCustomPrefix(String prefix, GlobalProperties.PropertySource properties) {
|
||||
public void loadSettings(Properties properties, String serverName) {
|
||||
PropertiesWrapper dbProps = new PropertiesWrapper("datasource", serverName, properties);
|
||||
loadSettings(dbProps);
|
||||
}
|
||||
|
||||
this.username = properties.get(prefix + "username", null);
|
||||
this.password = properties.get(prefix + "password", null);
|
||||
/**
|
||||
* Load the settings from the PropertiesWrapper.
|
||||
*/
|
||||
public void loadSettings(PropertiesWrapper properties) {
|
||||
|
||||
String dbDriver = properties.get(prefix + "databaseDriver", null);
|
||||
this.driver = properties.get(prefix + "driver", dbDriver);
|
||||
username = properties.get("username", username);
|
||||
password = properties.get("password", password);
|
||||
driver = properties.get("driver", properties.get("databaseDriver", driver));
|
||||
url = properties.get("url", properties.get("databaseUrl", url));
|
||||
|
||||
String dbUrl = properties.get(prefix + "databaseUrl", null);
|
||||
this.url = properties.get(prefix + "url", dbUrl);
|
||||
autoCommit = properties.getBoolean("autoCommit", autoCommit);
|
||||
captureStackTrace = properties.getBoolean("captureStackTrace", captureStackTrace);
|
||||
maxStackTraceSize = properties.getInt("maxStackTraceSize", maxStackTraceSize);
|
||||
leakTimeMinutes = properties.getInt("leakTimeMinutes", leakTimeMinutes);
|
||||
maxInactiveTimeSecs = properties.getInt("maxInactiveTimeSecs", maxInactiveTimeSecs);
|
||||
trimPoolFreqSecs = properties.getInt("trimPoolFreqSecs", trimPoolFreqSecs);
|
||||
maxAgeMinutes = properties.getInt("maxAgeMinutes", maxAgeMinutes);
|
||||
|
||||
this.captureStackTrace = properties.getBoolean(prefix + "captureStackTrace", false);
|
||||
this.maxStackTraceSize = properties.getInt(prefix + "maxStackTraceSize", 5);
|
||||
this.leakTimeMinutes = properties.getInt(prefix + "leakTimeMinutes", 30);
|
||||
this.maxInactiveTimeSecs = properties.getInt(prefix + "maxInactiveTimeSecs", 720);
|
||||
this.trimPoolFreqSecs = properties.getInt(prefix + "trimPoolFreqSecs", 59);
|
||||
this.maxAgeMinutes = properties.getInt(prefix + "maxAgeMinutes", 0);
|
||||
minConnections = properties.getInt("minConnections", minConnections);
|
||||
maxConnections = properties.getInt("maxConnections", maxConnections);
|
||||
pstmtCacheSize = properties.getInt("pstmtCacheSize", pstmtCacheSize);
|
||||
cstmtCacheSize = properties.getInt("cstmtCacheSize", cstmtCacheSize);
|
||||
|
||||
this.minConnections = properties.getInt(prefix + "minConnections", 0);
|
||||
this.maxConnections = properties.getInt(prefix + "maxConnections", 20);
|
||||
this.pstmtCacheSize = properties.getInt(prefix + "pstmtCacheSize", 20);
|
||||
this.cstmtCacheSize = properties.getInt(prefix + "cstmtCacheSize", 20);
|
||||
waitTimeoutMillis = properties.getInt("waitTimeout", waitTimeoutMillis);
|
||||
|
||||
this.waitTimeoutMillis = properties.getInt(prefix + "waitTimeout", 1000);
|
||||
heartbeatSql = properties.get("heartbeatSql", heartbeatSql);
|
||||
heartbeatTimeoutSeconds = properties.getInt("heartbeatTimeoutSeconds", heartbeatTimeoutSeconds);
|
||||
poolListener = properties.get("poolListener", poolListener);
|
||||
offline = properties.getBoolean("offline", offline);
|
||||
|
||||
this.heartbeatSql = properties.get(prefix + "heartbeatSql", null);
|
||||
this.heartbeatTimeoutSeconds = properties.getInt(prefix + "heartbeatTimeoutSeconds", 3);
|
||||
this.poolListener = properties.get(prefix + "poolListener", null);
|
||||
this.offline = properties.getBoolean(prefix + "offline", false);
|
||||
|
||||
String isoLevel = properties.get(prefix + "isolationlevel", "READ_COMMITTED");
|
||||
String isoLevel = properties.get("isolationlevel", getTransactionIsolationLevel(isolationLevel));
|
||||
this.isolationLevel = getTransactionIsolationLevel(isoLevel);
|
||||
|
||||
String customProperties = properties.get(prefix + "customProperties", null);
|
||||
String customProperties = properties.get("customProperties", null);
|
||||
if (customProperties != null && customProperties.length() > 0) {
|
||||
Map<String, String> custProps = StringHelper.delimitedToMap(customProperties, ";", "=");
|
||||
this.customProperties = custProps;
|
||||
@@ -481,7 +514,21 @@ public class DataSourceConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* return the isolation level for a given string description.
|
||||
* Return the isolation level description from the associated Connection int value.
|
||||
*/
|
||||
public String getTransactionIsolationLevel(int level) {
|
||||
switch (level) {
|
||||
case Connection.TRANSACTION_NONE : return "NONE";
|
||||
case Connection.TRANSACTION_READ_COMMITTED : return "READ_COMMITTED";
|
||||
case Connection.TRANSACTION_READ_UNCOMMITTED : return "READ_UNCOMMITTED";
|
||||
case Connection.TRANSACTION_REPEATABLE_READ : return "REPEATABLE_READ";
|
||||
case Connection.TRANSACTION_SERIALIZABLE : return "SERIALIZABLE";
|
||||
default: throw new RuntimeException("Transaction Isolation level [" + level + "] is not known.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the isolation level for a given string description.
|
||||
*/
|
||||
public int getTransactionIsolationLevel(String level) {
|
||||
level = level.toUpperCase();
|
||||
@@ -505,6 +552,6 @@ public class DataSourceConfig {
|
||||
return Connection.TRANSACTION_SERIALIZABLE;
|
||||
}
|
||||
|
||||
throw new RuntimeException("Transaction Isolaction level [" + level + "] is not known.");
|
||||
throw new RuntimeException("Transaction Isolation level [" + level + "] is not known.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import com.avaje.ebean.util.ClassUtil;
|
||||
|
||||
/**
|
||||
* Provides access to properties loaded from the ebean.properties file.
|
||||
*/
|
||||
public final class GlobalProperties {
|
||||
|
||||
private static volatile PropertyMap globalMap;
|
||||
|
||||
private static boolean skipPrimaryServer;
|
||||
|
||||
/**
|
||||
* Set whether to skip automatically creating the primary server.
|
||||
*/
|
||||
public static synchronized void setSkipPrimaryServer(boolean skip) {
|
||||
skipPrimaryServer = skip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true to skip automatically creating the primary server.
|
||||
*/
|
||||
public static synchronized boolean isSkipPrimaryServer() {
|
||||
return skipPrimaryServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the string replacing any expressions like ${catalina.base}.
|
||||
* <p>
|
||||
* This will evaluate expressions using first environment variables, than java
|
||||
* system variables and lastly properties in ebean.properties - in that order.
|
||||
* </p>
|
||||
* <p>
|
||||
* Expressions start with "${" and end with "}".
|
||||
* </p>
|
||||
*/
|
||||
public static String evaluateExpressions(String val) {
|
||||
return getPropertyMap().eval(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and evaluate any expressions that have not already been evaluated.
|
||||
*/
|
||||
public static synchronized void evaluateExpressions() {
|
||||
getPropertyMap().evaluateProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* In a servlet container environment this will additionally look in WEB-INF
|
||||
* for the ebean.properties file.
|
||||
*/
|
||||
public static synchronized void setServletContext(ServletContext servletContext) {
|
||||
|
||||
PropertyMapLoader.setServletContext(servletContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ServletContext (if setup in a servlet container environment).
|
||||
*/
|
||||
public static synchronized ServletContext getServletContext() {
|
||||
|
||||
return PropertyMapLoader.getServletContext();
|
||||
}
|
||||
|
||||
private static void initPropertyMap() {
|
||||
|
||||
String fileName = System.getenv("EBEAN_PROPS_FILE");
|
||||
if (fileName == null) {
|
||||
fileName = System.getProperty("ebean.props.file");
|
||||
if (fileName == null) {
|
||||
fileName = "ebean.properties";
|
||||
}
|
||||
}
|
||||
|
||||
globalMap = PropertyMapLoader.load(null, fileName);
|
||||
if (globalMap == null) {
|
||||
// ebean.properties file was not found... but that
|
||||
// is ok because we are likely doing programmatic config
|
||||
globalMap = new PropertyMap();
|
||||
}
|
||||
|
||||
String loaderCn = globalMap.get("ebean.properties.loader");
|
||||
if (loaderCn != null) {
|
||||
// a Runnable that can be used to customise the initialisation
|
||||
// of the GlobalProperties
|
||||
try {
|
||||
Runnable r = (Runnable) ClassUtil.newInstance(loaderCn);
|
||||
r.run();
|
||||
} catch (Exception e) {
|
||||
String m = "Error creating or running properties loader " + loaderCn;
|
||||
throw new RuntimeException(m, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property map loading it if required.
|
||||
*/
|
||||
private static synchronized PropertyMap getPropertyMap() {
|
||||
|
||||
if (globalMap == null) {
|
||||
initPropertyMap();
|
||||
}
|
||||
|
||||
return globalMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a String property with a default value.
|
||||
*/
|
||||
public static synchronized String get(String key, String defaultValue) {
|
||||
return getPropertyMap().get(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a int property with a default value.
|
||||
*/
|
||||
public static synchronized int getInt(String key, int defaultValue) {
|
||||
return getPropertyMap().getInt(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a boolean property with a default value.
|
||||
*/
|
||||
public static synchronized boolean getBoolean(String key, boolean defaultValue) {
|
||||
return getPropertyMap().getBoolean(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a property return the previous value. This will evaluate any
|
||||
* expressions in the value.
|
||||
*/
|
||||
public static synchronized String put(String key, String value) {
|
||||
return getPropertyMap().putEval(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a Map of key value properties.
|
||||
*/
|
||||
public static synchronized void putAll(Map<String, String> keyValueMap) {
|
||||
for (Entry<String, String> e : keyValueMap.entrySet()) {
|
||||
getPropertyMap().putEval(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public static PropertySource getPropertySource(String name) {
|
||||
return new ConfigPropertyMap(name);
|
||||
}
|
||||
|
||||
public static interface PropertySource {
|
||||
|
||||
/**
|
||||
* Return the name of the server. This is also the dataSource name.
|
||||
*/
|
||||
public String getServerName();
|
||||
|
||||
/**
|
||||
* Get a property. This will prepend "ebean" and the server name to lookup
|
||||
* the value.
|
||||
*/
|
||||
public String get(String key, String defaultValue);
|
||||
|
||||
public int getInt(String key, int defaultValue);
|
||||
|
||||
public boolean getBoolean(String key, boolean defaultValue);
|
||||
|
||||
public <T extends Enum<T>> T getEnum(Class<T> enumType, String key, T defaultValue);
|
||||
|
||||
}
|
||||
|
||||
public static class DelegatedGlobalPropertySource implements PropertySource {
|
||||
|
||||
private String serverName;
|
||||
|
||||
public DelegatedGlobalPropertySource(String serverName) {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String key, String defaultValue) {
|
||||
return GlobalProperties.get(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(String key, int defaultValue) {
|
||||
return GlobalProperties.getInt(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String key, boolean defaultValue) {
|
||||
return GlobalProperties.getBoolean(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Enum<T>> T getEnum(Class<T> enumType, String key, T defaultValue) {
|
||||
String level = get(key, defaultValue.name());
|
||||
return Enum.valueOf(enumType, level.toUpperCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
/**
|
||||
* Configuration for JSON features.
|
||||
*/
|
||||
public abstract class JsonConfig {
|
||||
|
||||
/**
|
||||
* Defined the format used for DateTime types.
|
||||
*/
|
||||
public enum DateTime {
|
||||
|
||||
/**
|
||||
* Format as epoch millis.
|
||||
*/
|
||||
MILLIS,
|
||||
|
||||
/**
|
||||
* Format as epoch with nanos.
|
||||
*/
|
||||
NANOS,
|
||||
|
||||
/**
|
||||
* Format as ISO-8601 date format.
|
||||
*/
|
||||
ISO8601
|
||||
}
|
||||
|
||||
}
|
||||
@@ -112,4 +112,9 @@ public interface NamingConvention {
|
||||
*/
|
||||
public boolean isUseForeignKeyPrefix();
|
||||
|
||||
/**
|
||||
* Load setting from properties.
|
||||
*/
|
||||
public void loadFromProperties(PropertiesWrapper properties);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
/**
|
||||
* Defines the mode for JDBC batch processing.
|
||||
* <p>
|
||||
* Used both at a per transaction basis and per request basis.
|
||||
* </p>
|
||||
*
|
||||
* @see com.avaje.ebean.config.ServerConfig#setPersistBatch(PersistBatch)
|
||||
* @see com.avaje.ebean.config.ServerConfig#setPersistBatchOnCascade(PersistBatch)
|
||||
*
|
||||
* @see com.avaje.ebean.Transaction#setBatch(PersistBatch)
|
||||
* @see com.avaje.ebean.Transaction#setBatchOnCascade(PersistBatch)
|
||||
*/
|
||||
public enum PersistBatch {
|
||||
|
||||
/**
|
||||
* Do not use JDBC Batch mode.
|
||||
*/
|
||||
NONE(false),
|
||||
|
||||
/**
|
||||
* Use JDBC Batch mode on Inserts.
|
||||
*/
|
||||
INSERT(true),
|
||||
|
||||
/**
|
||||
* Use JDBC Batch mode on Inserts, Updates and Deletes.
|
||||
*/
|
||||
ALL(true),
|
||||
|
||||
/**
|
||||
* You should not use this value explicitly. It should only used on the Transactional annotation
|
||||
* to indicate that the value should inherit from the ServerConfig setting.
|
||||
*/
|
||||
INHERIT(false);
|
||||
|
||||
|
||||
boolean forInsert;
|
||||
|
||||
PersistBatch(boolean forInsert) {
|
||||
this.forInsert = forInsert;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if persist cascade should use JDBC batch for inserts.
|
||||
*/
|
||||
public boolean forInsert() {
|
||||
return forInsert;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
public class PropertiesWrapper {
|
||||
|
||||
protected final Properties properties;
|
||||
|
||||
protected final String prefix;
|
||||
|
||||
protected final String serverName;
|
||||
|
||||
protected final PropertyMap propertyMap;
|
||||
|
||||
/**
|
||||
* Construct with a prefix, serverName and properties.
|
||||
*/
|
||||
public PropertiesWrapper(String prefix, String serverName, Properties properties) {
|
||||
this.serverName = serverName;
|
||||
this.prefix = prefix;
|
||||
this.propertyMap = PropertyMapLoader.load(null, properties);
|
||||
this.properties = propertyMap.asProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct without prefix of serverName.
|
||||
*/
|
||||
public PropertiesWrapper(Properties properties) {
|
||||
this(null, null, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal copy constructor when changing prefix.
|
||||
*/
|
||||
protected PropertiesWrapper(String prefix, String serverName, PropertyMap propertyMap, Properties properties) {
|
||||
this.serverName = serverName;
|
||||
this.prefix = prefix;
|
||||
this.propertyMap = propertyMap;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a PropertiesWrapper instance with a different prefix but same underlying properties.
|
||||
* <p/>
|
||||
* Used when wanting to use "datasource" as the prefix rather than "ebean".
|
||||
* <p/>
|
||||
* The returning instance should only be used in a read only fashion.
|
||||
*/
|
||||
public PropertiesWrapper withPrefix(String prefix) {
|
||||
return new PropertiesWrapper(prefix, serverName, propertyMap, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the serverName (optional).
|
||||
*/
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return as Properties with lower case keys and after evaluation and additional properties loading has occurred.
|
||||
* <p>
|
||||
* Ebean has historically ignored the case of keys hence returning the Properties with all the keys lower cased.
|
||||
* </p>
|
||||
*/
|
||||
public Properties asPropertiesLowerCase() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a property with no default value.
|
||||
*/
|
||||
public String get(String key) {
|
||||
return get(key, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a property with a default value.
|
||||
* <p>
|
||||
* This performs a search using the prefix and server name (if supplied) to search for the property
|
||||
* value in order based on:
|
||||
* <pre>{@code
|
||||
* prefix.serverName.key
|
||||
* prefix.key
|
||||
* key
|
||||
* }</pre>
|
||||
* </p>
|
||||
*/
|
||||
public String get(String key, String defaultValue) {
|
||||
|
||||
String value = null;
|
||||
if (serverName != null && prefix != null) {
|
||||
value = propertyMap.get(prefix + "." + serverName + "." + key, null);
|
||||
}
|
||||
if (value == null && prefix != null) {
|
||||
value = propertyMap.get(prefix + "." + key, null);
|
||||
}
|
||||
if (value == null) {
|
||||
value = propertyMap.get(key, null);
|
||||
}
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a double property value.
|
||||
*/
|
||||
public double getDouble(String key, double defaultValue) {
|
||||
|
||||
String value = get(key, String.valueOf(defaultValue));
|
||||
return Double.parseDouble(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an int property value.
|
||||
*/
|
||||
public int getInt(String key, int defaultValue) {
|
||||
|
||||
String value = get(key, String.valueOf(defaultValue));
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a long property value.
|
||||
*/
|
||||
public long getLong(String key, long defaultValue) {
|
||||
|
||||
String value = get(key, String.valueOf(defaultValue));
|
||||
return Long.parseLong(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a boolean property value.
|
||||
*/
|
||||
public boolean getBoolean(String key, boolean defaultValue) {
|
||||
|
||||
String value = get(key, String.valueOf(defaultValue));
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Enum property value.
|
||||
*/
|
||||
public <T extends Enum<T>> T getEnum(Class<T> enumType, String key, T defaultValue) {
|
||||
String level = get(key, defaultValue.name());
|
||||
return Enum.valueOf(enumType, level.toUpperCase());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +1,43 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A map like structure of properties.
|
||||
* <p/>
|
||||
* Handles evaluation of expressions like ${home} and provides convenience methods for int, long and boolean.
|
||||
*/
|
||||
final class PropertyMap implements Serializable {
|
||||
public final class PropertyMap implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
|
||||
|
||||
public static Properties defaultProperties() {
|
||||
PropertyMap propertyMap = PropertyMapLoader.loadGlobalProperties();
|
||||
return (propertyMap == null) ? new Properties() : propertyMap.asProperties();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return map.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return as standard Properties.
|
||||
*/
|
||||
public Properties asProperties() {
|
||||
Properties properties = new Properties();
|
||||
for (Entry<String, String> e : entrySet()) {
|
||||
properties.put(e.getKey(), e.getValue());
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through all the properties and evaluate any expressions that have not
|
||||
* been resolved.
|
||||
@@ -36,10 +54,16 @@ final class PropertyMap implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value with expressions like ${home} evaluated using system properties and environment variables.
|
||||
*/
|
||||
public synchronized String eval(String val) {
|
||||
return PropertyExpression.eval(val, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the boolean property value with a given default.
|
||||
*/
|
||||
public synchronized boolean getBoolean(String key, boolean defaultValue) {
|
||||
String value = get(key);
|
||||
if (value == null) {
|
||||
@@ -49,6 +73,9 @@ final class PropertyMap implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the int property value with a given default.
|
||||
*/
|
||||
public synchronized int getInt(String key, int defaultValue) {
|
||||
String value = get(key);
|
||||
if (value == null) {
|
||||
@@ -58,37 +85,69 @@ final class PropertyMap implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the long property value with a given default.
|
||||
*/
|
||||
public synchronized long getLong(String key, long defaultValue) {
|
||||
String value = get(key);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return Long.parseLong(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the string property value with a given default.
|
||||
*/
|
||||
public synchronized String get(String key, String defaultValue) {
|
||||
String value = map.get(key.toLowerCase());
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property value returning null if there is no value defined.
|
||||
*/
|
||||
public synchronized String get(String key) {
|
||||
return map.get(key.toLowerCase());
|
||||
}
|
||||
|
||||
synchronized void putAll(Map<String, String> keyValueMap) {
|
||||
Iterator<Entry<String, String>> it = keyValueMap.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<String, String> entry = it.next();
|
||||
put(entry.getKey(), entry.getValue());
|
||||
/**
|
||||
* Put all evaluating any expressions in the values.
|
||||
*/
|
||||
public synchronized void putEvalAll(Map<String, String> keyValueMap) {
|
||||
|
||||
for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
|
||||
putEval(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
synchronized String putEval(String key, String value) {
|
||||
/**
|
||||
* Put a single key value evaluating any expressions in the value.
|
||||
*/
|
||||
public synchronized String putEval(String key, String value) {
|
||||
value = PropertyExpression.eval(value, this);
|
||||
return map.put(key.toLowerCase(), value);
|
||||
}
|
||||
|
||||
synchronized String put(String key, String value) {
|
||||
/**
|
||||
* Put a single key value with no expression evaluation.
|
||||
*/
|
||||
public synchronized String put(String key, String value) {
|
||||
return map.put(key.toLowerCase(), value);
|
||||
}
|
||||
|
||||
synchronized String remove(String key) {
|
||||
/**
|
||||
* Remove an entry.
|
||||
*/
|
||||
public synchronized String remove(String key) {
|
||||
return map.remove(key.toLowerCase());
|
||||
}
|
||||
|
||||
synchronized Set<Entry<String, String>> entrySet() {
|
||||
/**
|
||||
* Return the entries.
|
||||
*/
|
||||
public synchronized Set<Entry<String, String>> entrySet() {
|
||||
return map.entrySet();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,40 +3,28 @@ package com.avaje.ebean.config;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Iterator;
|
||||
import java.io.*;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
/**
|
||||
* Helper used to load the PropertyMap.
|
||||
* Helper used to load the ebean.properties into a PropertyMap.
|
||||
*/
|
||||
final class PropertyMapLoader {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PropertyMapLoader.class);
|
||||
|
||||
private static ServletContext servletContext;
|
||||
public static PropertyMap loadGlobalProperties() {
|
||||
|
||||
/**
|
||||
* Return the servlet context when in a web environment.
|
||||
*/
|
||||
public static ServletContext getServletContext() {
|
||||
return servletContext;
|
||||
}
|
||||
String fileName = System.getenv("EBEAN_PROPS_FILE");
|
||||
if (fileName == null) {
|
||||
fileName = System.getProperty("ebean.props.file");
|
||||
if (fileName == null) {
|
||||
fileName = "ebean.properties";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ServletContext for when ebean.properties is in WEB-INF in a web
|
||||
* application environment.
|
||||
*/
|
||||
public static void setServletContext(ServletContext servletContext) {
|
||||
PropertyMapLoader.servletContext = servletContext;
|
||||
return load(null, fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,24 +54,27 @@ final class PropertyMapLoader {
|
||||
* @param in
|
||||
* the InputStream of the properties file to load.
|
||||
*/
|
||||
private static PropertyMap load(PropertyMap p, InputStream in) {
|
||||
public static PropertyMap load(PropertyMap p, InputStream in) {
|
||||
|
||||
Properties props = new Properties();
|
||||
try {
|
||||
props.load(in);
|
||||
in.close();
|
||||
return load(p, props);
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static PropertyMap load(PropertyMap p, Properties props) {
|
||||
|
||||
if (p == null) {
|
||||
p = new PropertyMap();
|
||||
}
|
||||
|
||||
// put values in initially without any evaluation
|
||||
Iterator<Entry<Object, Object>> it = props.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<Object, Object> entry = it.next();
|
||||
for (Map.Entry<Object, Object> entry : props.entrySet()) {
|
||||
String key = ((String) entry.getKey()).toLowerCase();
|
||||
String val = ((String) entry.getValue());
|
||||
if (val != null) {
|
||||
@@ -121,18 +112,6 @@ final class PropertyMapLoader {
|
||||
throw new NullPointerException("fileName is null?");
|
||||
}
|
||||
|
||||
if (servletContext == null) {
|
||||
logger.debug("No servletContext so not looking in WEB-INF for " + fileName);
|
||||
|
||||
} else {
|
||||
// first look in WEB-INF ...
|
||||
InputStream in = servletContext.getResourceAsStream("/WEB-INF/" + fileName);
|
||||
if (in != null) {
|
||||
logger.debug(fileName + " found in WEB-INF");
|
||||
return in;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
File f = new File(fileName);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -57,6 +57,13 @@ public final class TableName {
|
||||
this.name = split[len - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a qualifiedTableName that might include a catalog and schema and just return the table name.
|
||||
*/
|
||||
public static String parse(String qualifiedTableName) {
|
||||
return new TableName(qualifiedTableName).getName();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getQualifiedName();
|
||||
}
|
||||
@@ -121,6 +128,18 @@ public final class TableName {
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a catalog and schema prefix if they exist to the string builder.
|
||||
*/
|
||||
public void appendCatalogAndSchema(StringBuilder buffer) {
|
||||
if (catalog != null) {
|
||||
buffer.append(catalog).append(".");
|
||||
}
|
||||
if (schema != null) {
|
||||
buffer.append(schema).append(".");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if is table name is valid i.e. it has at least a name.
|
||||
*
|
||||
|
||||
@@ -1,302 +1,427 @@
|
||||
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 org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Database platform specific settings.
|
||||
*/
|
||||
public class DatabasePlatform {
|
||||
|
||||
/** The Constant logger. */
|
||||
private static final Logger logger = LoggerFactory.getLogger(DatabasePlatform.class);
|
||||
|
||||
/** The open quote used by quoted identifiers. */
|
||||
protected String openQuote = "\"";
|
||||
|
||||
/** The close quote used by quoted identifiers. */
|
||||
protected String closeQuote = "\"";
|
||||
|
||||
/** For limit/offset, row_number etc limiting of SQL queries. */
|
||||
protected SqlLimiter sqlLimiter = new LimitOffsetSqlLimiter();
|
||||
|
||||
/** Mapping of JDBC to Database types. */
|
||||
protected DbTypeMap dbTypeMap = new DbTypeMap();
|
||||
|
||||
/** DB specific DDL syntax. */
|
||||
protected DbDdlSyntax dbDdlSyntax = new DbDdlSyntax();
|
||||
|
||||
/** Defines DB identity/sequence features. */
|
||||
protected DbIdentity dbIdentity = new DbIdentity();
|
||||
|
||||
/** The JDBC type to map booleans to (by default). */
|
||||
protected int booleanDbType = Types.BOOLEAN;
|
||||
|
||||
/** The JDBC type to map Blob to. */
|
||||
protected int blobDbType = Types.BLOB;
|
||||
|
||||
/** The JDBC type to map Clob to. */
|
||||
protected int clobDbType = Types.CLOB;
|
||||
|
||||
/** For Oracle treat empty strings as null. */
|
||||
protected boolean treatEmptyStringsAsNull;
|
||||
|
||||
/** The name. */
|
||||
protected String name = "generic";
|
||||
|
||||
/**
|
||||
* Use a BackTick ` at the beginning and end of table or column names that you
|
||||
* want to use quoted identifiers for. The backticks get converted to the
|
||||
* appropriate characters in convertQuotedIdentifiers
|
||||
*/
|
||||
private static final char BACK_TICK = '`';
|
||||
|
||||
protected DbEncrypt dbEncrypt;
|
||||
|
||||
protected boolean idInExpandedForm;
|
||||
|
||||
protected boolean selectCountWithAlias;
|
||||
|
||||
/**
|
||||
* If set then use the FORWARD ONLY hint when creating ResultSets for
|
||||
* findIterate() and findVisit().
|
||||
*/
|
||||
protected boolean forwardOnlyHintOnFindIterate;
|
||||
|
||||
/**
|
||||
* Instantiates a new database platform.
|
||||
*/
|
||||
public DatabasePlatform() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the DatabasePlatform.
|
||||
* <p>
|
||||
* "generic" is returned when no specific database platform has been set or
|
||||
* found.
|
||||
* </p>
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public IdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds,
|
||||
String seqName, int batchSize) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DbEncrypt handler for this DB platform.
|
||||
*/
|
||||
public DbEncrypt getDbEncrypt() {
|
||||
return dbEncrypt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DbEncrypt handler for this DB platform.
|
||||
*/
|
||||
public void setDbEncrypt(DbEncrypt dbEncrypt) {
|
||||
this.dbEncrypt = dbEncrypt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mapping of JDBC to DB types.
|
||||
*
|
||||
* @return the db type map
|
||||
*/
|
||||
public DbTypeMap getDbTypeMap() {
|
||||
return dbTypeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DDL syntax for this platform.
|
||||
*
|
||||
* @return the db ddl syntax
|
||||
*/
|
||||
public DbDdlSyntax getDbDdlSyntax() {
|
||||
return dbDdlSyntax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the close quote for quoted identifiers.
|
||||
*
|
||||
* @return the close quote
|
||||
*/
|
||||
public String getCloseQuote() {
|
||||
return closeQuote;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the open quote for quoted identifiers.
|
||||
*
|
||||
* @return the open quote
|
||||
*/
|
||||
public String getOpenQuote() {
|
||||
return openQuote;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JDBC type used to store booleans.
|
||||
*
|
||||
* @return the boolean db type
|
||||
*/
|
||||
public int getBooleanDbType() {
|
||||
return booleanDbType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the data type that should be used for Blob.
|
||||
* <p>
|
||||
* This is typically Types.BLOB but for Postgres is Types.LONGVARBINARY for
|
||||
* example.
|
||||
* </p>
|
||||
*/
|
||||
public int getBlobDbType() {
|
||||
return blobDbType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the data type that should be used for Clob.
|
||||
* <p>
|
||||
* This is typically Types.CLOB but for Postgres is Types.VARCHAR.
|
||||
* </p>
|
||||
*/
|
||||
public int getClobDbType() {
|
||||
return clobDbType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if empty strings should be treated as null.
|
||||
*
|
||||
* @return true, if checks if is treat empty strings as null
|
||||
*/
|
||||
public boolean isTreatEmptyStringsAsNull() {
|
||||
return treatEmptyStringsAsNull;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a compound ID in (...) type expression needs to be in
|
||||
* expanded form of (a=? and b=?) or (a=? and b=?) or ... rather than (a,b) in
|
||||
* ((?,?),(?,?),...);
|
||||
*/
|
||||
public boolean isIdInExpandedForm() {
|
||||
return idInExpandedForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the ResultSet TYPE_FORWARD_ONLY Hint should be used on
|
||||
* findIterate() and findVisit() PreparedStatements.
|
||||
* <p>
|
||||
* This specifically is required for MySql when processing large results.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isForwardOnlyHintOnFindIterate() {
|
||||
return forwardOnlyHintOnFindIterate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if the ResultSet TYPE_FORWARD_ONLY Hint should be used by default on findIterate PreparedStatements.
|
||||
*/
|
||||
public void setForwardOnlyHintOnFindIterate(boolean forwardOnlyHintOnFindIterate) {
|
||||
this.forwardOnlyHintOnFindIterate = forwardOnlyHintOnFindIterate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DB identity/sequence features for this platform.
|
||||
*
|
||||
* @return the db identity
|
||||
*/
|
||||
public DbIdentity getDbIdentity() {
|
||||
return dbIdentity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SqlLimiter used to apply additional sql around a query to limit
|
||||
* its results.
|
||||
* <p>
|
||||
* Basically add the clauses for limit/offset, rownum, row_number().
|
||||
* </p>
|
||||
*
|
||||
* @return the sql limiter
|
||||
*/
|
||||
public SqlLimiter getSqlLimiter() {
|
||||
return sqlLimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
public String convertQuotedIdentifiers(String dbName) {
|
||||
// Ignore null values e.g. schema name or catalog
|
||||
if (dbName != null && dbName.length() > 0) {
|
||||
if (dbName.charAt(0) == BACK_TICK) {
|
||||
if (dbName.charAt(dbName.length() - 1) == BACK_TICK) {
|
||||
|
||||
String quotedName = getOpenQuote();
|
||||
quotedName += dbName.substring(1, dbName.length() - 1);
|
||||
quotedName += getCloseQuote();
|
||||
|
||||
return quotedName;
|
||||
|
||||
} else {
|
||||
logger.error("Missing backquote on [" + dbName + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
return dbName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if select count against anonymous view requires an alias.
|
||||
*/
|
||||
public boolean isSelectCountWithAlias() {
|
||||
return selectCountWithAlias;
|
||||
}
|
||||
|
||||
public String completeSql(String sql, Query<?> query) {
|
||||
if (Boolean.TRUE.equals(query.isForUpdate())) {
|
||||
sql = withForUpdate(sql);
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
protected String withForUpdate(String sql) {
|
||||
// silently assume the database does not support the "for update" clause.
|
||||
logger.info("it seems your database does not support the 'for update' clause");
|
||||
|
||||
return sql;
|
||||
}
|
||||
}
|
||||
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 org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Database platform specific settings.
|
||||
*/
|
||||
public class DatabasePlatform {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DatabasePlatform.class);
|
||||
|
||||
/**
|
||||
* Behavior used when ending a query only transaction (at read committed isolation level).
|
||||
*/
|
||||
public enum OnQueryOnly {
|
||||
|
||||
/**
|
||||
* Rollback the transaction.
|
||||
*/
|
||||
ROLLBACK,
|
||||
|
||||
/**
|
||||
* Just close the transaction. Valid at READ_COMMITTED isolation and preferred on some Databases
|
||||
* as a performance optimisation.
|
||||
*/
|
||||
CLOSE,
|
||||
|
||||
/**
|
||||
* Commit the transaction
|
||||
*/
|
||||
COMMIT
|
||||
}
|
||||
|
||||
/**
|
||||
* The behaviour used when ending a read only transaction at read committed isolation level.
|
||||
*/
|
||||
protected OnQueryOnly onQueryOnly = OnQueryOnly.ROLLBACK;
|
||||
|
||||
/**
|
||||
* The open quote used by quoted identifiers.
|
||||
*/
|
||||
protected String openQuote = "\"";
|
||||
|
||||
/**
|
||||
* The close quote used by quoted identifiers.
|
||||
*/
|
||||
protected String closeQuote = "\"";
|
||||
|
||||
/**
|
||||
* For limit/offset, row_number etc limiting of SQL queries.
|
||||
*/
|
||||
protected SqlLimiter sqlLimiter = new LimitOffsetSqlLimiter();
|
||||
|
||||
/**
|
||||
* Mapping of JDBC to Database types.
|
||||
*/
|
||||
protected DbTypeMap dbTypeMap = new DbTypeMap();
|
||||
|
||||
/**
|
||||
* DB specific DDL syntax.
|
||||
*/
|
||||
protected DbDdlSyntax dbDdlSyntax = new DbDdlSyntax();
|
||||
|
||||
/**
|
||||
* Defines DB identity/sequence features.
|
||||
*/
|
||||
protected DbIdentity dbIdentity = new DbIdentity();
|
||||
|
||||
/**
|
||||
* The JDBC type to map booleans to (by default).
|
||||
*/
|
||||
protected int booleanDbType = Types.BOOLEAN;
|
||||
|
||||
/**
|
||||
* The JDBC type to map Blob to.
|
||||
*/
|
||||
protected int blobDbType = Types.BLOB;
|
||||
|
||||
/**
|
||||
* The JDBC type to map Clob to.
|
||||
*/
|
||||
protected int clobDbType = Types.CLOB;
|
||||
|
||||
/**
|
||||
* For Oracle treat empty strings as null.
|
||||
*/
|
||||
protected boolean treatEmptyStringsAsNull;
|
||||
|
||||
/**
|
||||
* The database platform name.
|
||||
*/
|
||||
protected String name = "generic";
|
||||
|
||||
protected String columnAliasPrefix = "c";
|
||||
|
||||
protected String tableAliasPlaceHolder = "${ta}";
|
||||
|
||||
/**
|
||||
* Use a BackTick ` at the beginning and end of table or column names that you
|
||||
* want to use quoted identifiers for. The backticks get converted to the
|
||||
* appropriate characters in convertQuotedIdentifiers
|
||||
*/
|
||||
private static final char BACK_TICK = '`';
|
||||
|
||||
/**
|
||||
* The like clause. Can be overridden to disable default escape character.
|
||||
*/
|
||||
protected String likeClause = "like ?";
|
||||
|
||||
protected DbEncrypt dbEncrypt;
|
||||
|
||||
protected boolean idInExpandedForm;
|
||||
|
||||
protected boolean selectCountWithAlias;
|
||||
|
||||
/**
|
||||
* If set then use the FORWARD ONLY hint when creating ResultSets for
|
||||
* findIterate() and findVisit().
|
||||
*/
|
||||
protected boolean forwardOnlyHintOnFindIterate;
|
||||
|
||||
/**
|
||||
* Flag set for SQL Server due to lack of support of getGeneratedKeys in
|
||||
* batch mode (meaning for batch inserts you should explicitly turn off
|
||||
* getGeneratedKeys - joy).
|
||||
*/
|
||||
protected boolean disallowBatchOnCascade;
|
||||
|
||||
/**
|
||||
* Instantiates a new database platform.
|
||||
*/
|
||||
public DatabasePlatform() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the DatabasePlatform.
|
||||
* <p>
|
||||
* "generic" is returned when no specific database platform has been set or
|
||||
* found.
|
||||
* </p>
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public IdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the behaviour to use when ending a read only transaction.
|
||||
*/
|
||||
public OnQueryOnly getOnQueryOnly() {
|
||||
return onQueryOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the behaviour to use when ending a read only transaction.
|
||||
*/
|
||||
public void setOnQueryOnly(OnQueryOnly onQueryOnly) {
|
||||
this.onQueryOnly = onQueryOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DbEncrypt handler for this DB platform.
|
||||
*/
|
||||
public DbEncrypt getDbEncrypt() {
|
||||
return dbEncrypt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DbEncrypt handler for this DB platform.
|
||||
*/
|
||||
public void setDbEncrypt(DbEncrypt dbEncrypt) {
|
||||
this.dbEncrypt = dbEncrypt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mapping of JDBC to DB types.
|
||||
*
|
||||
* @return the db type map
|
||||
*/
|
||||
public DbTypeMap getDbTypeMap() {
|
||||
return dbTypeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DDL syntax for this platform.
|
||||
*
|
||||
* @return the db ddl syntax
|
||||
*/
|
||||
public DbDdlSyntax getDbDdlSyntax() {
|
||||
return dbDdlSyntax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the column alias prefix.
|
||||
*/
|
||||
public String getColumnAliasPrefix() {
|
||||
return columnAliasPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the column alias prefix.
|
||||
*/
|
||||
public void setColumnAliasPrefix(String columnAliasPrefix) {
|
||||
this.columnAliasPrefix = columnAliasPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the table alias placeholder.
|
||||
*/
|
||||
public String getTableAliasPlaceHolder() {
|
||||
return tableAliasPlaceHolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the table alias placeholder.
|
||||
*/
|
||||
public void setTableAliasPlaceHolder(String tableAliasPlaceHolder) {
|
||||
this.tableAliasPlaceHolder = tableAliasPlaceHolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the close quote for quoted identifiers.
|
||||
*
|
||||
* @return the close quote
|
||||
*/
|
||||
public String getCloseQuote() {
|
||||
return closeQuote;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the open quote for quoted identifiers.
|
||||
*
|
||||
* @return the open quote
|
||||
*/
|
||||
public String getOpenQuote() {
|
||||
return openQuote;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JDBC type used to store booleans.
|
||||
*
|
||||
* @return the boolean db type
|
||||
*/
|
||||
public int getBooleanDbType() {
|
||||
return booleanDbType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the data type that should be used for Blob.
|
||||
* <p>
|
||||
* This is typically Types.BLOB but for Postgres is Types.LONGVARBINARY for
|
||||
* example.
|
||||
* </p>
|
||||
*/
|
||||
public int getBlobDbType() {
|
||||
return blobDbType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the data type that should be used for Clob.
|
||||
* <p>
|
||||
* This is typically Types.CLOB but for Postgres is Types.VARCHAR.
|
||||
* </p>
|
||||
*/
|
||||
public int getClobDbType() {
|
||||
return clobDbType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if empty strings should be treated as null.
|
||||
*
|
||||
* @return true, if checks if is treat empty strings as null
|
||||
*/
|
||||
public boolean isTreatEmptyStringsAsNull() {
|
||||
return treatEmptyStringsAsNull;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a compound ID in (...) type expression needs to be in
|
||||
* expanded form of (a=? and b=?) or (a=? and b=?) or ... rather than (a,b) in
|
||||
* ((?,?),(?,?),...);
|
||||
*/
|
||||
public boolean isIdInExpandedForm() {
|
||||
return idInExpandedForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the ResultSet TYPE_FORWARD_ONLY Hint should be used on
|
||||
* findIterate() and findVisit() PreparedStatements.
|
||||
* <p>
|
||||
* This specifically is required for MySql when processing large results.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isForwardOnlyHintOnFindIterate() {
|
||||
return forwardOnlyHintOnFindIterate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if the ResultSet TYPE_FORWARD_ONLY Hint should be used by default on findIterate PreparedStatements.
|
||||
*/
|
||||
public void setForwardOnlyHintOnFindIterate(boolean forwardOnlyHintOnFindIterate) {
|
||||
this.forwardOnlyHintOnFindIterate = forwardOnlyHintOnFindIterate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DB identity/sequence features for this platform.
|
||||
*
|
||||
* @return the db identity
|
||||
*/
|
||||
public DbIdentity getDbIdentity() {
|
||||
return dbIdentity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SqlLimiter used to apply additional sql around a query to limit
|
||||
* its results.
|
||||
* <p>
|
||||
* Basically add the clauses for limit/offset, rownum, row_number().
|
||||
* </p>
|
||||
*
|
||||
* @return the sql limiter
|
||||
*/
|
||||
public SqlLimiter getSqlLimiter() {
|
||||
return sqlLimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
public String convertQuotedIdentifiers(String dbName) {
|
||||
// Ignore null values e.g. schema name or catalog
|
||||
if (dbName != null && dbName.length() > 0) {
|
||||
if (dbName.charAt(0) == BACK_TICK) {
|
||||
if (dbName.charAt(dbName.length() - 1) == BACK_TICK) {
|
||||
|
||||
String quotedName = getOpenQuote();
|
||||
quotedName += dbName.substring(1, dbName.length() - 1);
|
||||
quotedName += getCloseQuote();
|
||||
|
||||
return quotedName;
|
||||
|
||||
} else {
|
||||
logger.error("Missing backquote on [" + dbName + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
return dbName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if select count against anonymous view requires an alias.
|
||||
*/
|
||||
public boolean isSelectCountWithAlias() {
|
||||
return selectCountWithAlias;
|
||||
}
|
||||
|
||||
public String completeSql(String sql, Query<?> query) {
|
||||
if (Boolean.TRUE.equals(query.isForUpdate())) {
|
||||
sql = withForUpdate(sql);
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
protected String withForUpdate(String sql) {
|
||||
// silently assume the database does not support the "for update" clause.
|
||||
logger.info("it seems your database does not support the 'for update' clause");
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the like clause used by this database platform.
|
||||
* <p>
|
||||
* This may include an escape clause to disable a default escape character.
|
||||
*/
|
||||
public String getLikeClause() {
|
||||
return likeClause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the persistBatchOnCascade setting should be ignored.
|
||||
* <p>
|
||||
* This is primarily for SQL Server which does not support getGeneratedKeys with jdbc batch mode
|
||||
* so can't really be transparently used.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isDisallowBatchOnCascade() {
|
||||
return disallowBatchOnCascade;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,41 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import com.avaje.ebean.config.TableName;
|
||||
|
||||
/**
|
||||
* Used to support DB specific syntax for DDL generation.
|
||||
*/
|
||||
public class DbDdlSyntax {
|
||||
|
||||
private boolean renderIndexForFkey = true;
|
||||
protected boolean renderIndexForFkey = true;
|
||||
|
||||
private boolean inlinePrimaryKeyConstraint = false;
|
||||
protected boolean inlinePrimaryKeyConstraint = false;
|
||||
|
||||
private boolean addOneToOneUniqueContraint = false;
|
||||
protected boolean addOneToOneUniqueContraint = true;
|
||||
|
||||
private int maxConstraintNameLength = 32;
|
||||
protected int maxConstraintNameLength = 32;
|
||||
|
||||
private int columnNameWidth = 25;
|
||||
protected int columnNameWidth = 25;
|
||||
|
||||
private String dropTableCascade;
|
||||
private String dropIfExists;
|
||||
/**
|
||||
* Flag set when we want to disable constraints on each table (rather than globally).
|
||||
*/
|
||||
protected boolean dropKeyConstraints;
|
||||
|
||||
private String newLine = "\n";
|
||||
protected String dropTableCascade;
|
||||
protected String dropIfExists;
|
||||
|
||||
private String identity = "auto_increment";
|
||||
private String identitySuffix = "";
|
||||
protected String newLine = "\n";
|
||||
|
||||
private String pkPrefix = "pk_";
|
||||
protected String identity = "auto_increment";
|
||||
protected String identitySuffix = "";
|
||||
|
||||
private String disableReferentialIntegrity;
|
||||
private String enableReferentialIntegrity;
|
||||
protected String pkPrefix = "pk_";
|
||||
|
||||
private String foreignKeySuffix;
|
||||
protected String disableReferentialIntegrity;
|
||||
protected String enableReferentialIntegrity;
|
||||
|
||||
protected String foreignKeySuffix;
|
||||
|
||||
/**
|
||||
* Return the primary key name for a given bean descriptor.
|
||||
@@ -43,6 +50,17 @@ public class DbDdlSyntax {
|
||||
return pk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the column definition for an identity column.
|
||||
*/
|
||||
public String getIdentityColumnDefn(String columnDefn) {
|
||||
String identity = getIdentity();
|
||||
if (identity != null && identity.length() > 0) {
|
||||
return columnDefn+" "+identity;
|
||||
}
|
||||
return columnDefn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the identity clause for DB's that have identities.
|
||||
*/
|
||||
@@ -145,6 +163,20 @@ public class DbDdlSyntax {
|
||||
this.enableReferentialIntegrity = enableReferentialIntegrity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true to is constraints are disabled on each table.
|
||||
*/
|
||||
public boolean isDropKeyConstraints() {
|
||||
return dropKeyConstraints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return some DDL to disable constraints on the given table.
|
||||
*/
|
||||
public String dropKeyConstraintPrefix(String tableName, String fkName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if indexes should be created for the foreign keys.
|
||||
*/
|
||||
@@ -175,6 +207,13 @@ public class DbDdlSyntax {
|
||||
this.dropIfExists = dropIfExists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return prefix text that goes before drop table.
|
||||
*/
|
||||
public String dropTablePrefix(String tableName) {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cascade option for the drop table command.
|
||||
*/
|
||||
@@ -248,26 +287,29 @@ public class DbDdlSyntax {
|
||||
this.inlinePrimaryKeyConstraint = inlinePrimaryKeyConstraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a fully index name.
|
||||
*/
|
||||
public String getIndexName(String table, String propName, int ixCount) {
|
||||
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append("ix_");
|
||||
buffer.append(table);
|
||||
buffer.append("_");
|
||||
buffer.append(propName);
|
||||
|
||||
StringBuilder buffer = new StringBuilder(30);
|
||||
buffer.append("ix_").append(TableName.parse(table));
|
||||
buffer.append("_").append(propName);
|
||||
|
||||
addSuffix(buffer, ixCount);
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a fully qualified foreign key constraint name.
|
||||
*/
|
||||
public String getForeignKeyName(String table, String propName, int fkCount) {
|
||||
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append("fk_");
|
||||
buffer.append(table);
|
||||
buffer.append("_");
|
||||
buffer.append(propName);
|
||||
StringBuilder buffer = new StringBuilder(30);
|
||||
buffer.append("fk_").append(TableName.parse(table));
|
||||
buffer.append("_").append(propName);
|
||||
|
||||
addSuffix(buffer, fkCount);
|
||||
|
||||
@@ -276,15 +318,10 @@ public class DbDdlSyntax {
|
||||
|
||||
/**
|
||||
* Adds the suffix.
|
||||
*
|
||||
* @param buffer
|
||||
* the buffer
|
||||
* @param count
|
||||
* the count
|
||||
*/
|
||||
protected void addSuffix(StringBuilder buffer, int count) {
|
||||
final String suffixNr = Integer.toString(count);
|
||||
final int suffixLen = suffixNr.length() + 1;
|
||||
String suffixNr = Integer.toString(count);
|
||||
int suffixLen = suffixNr.length() + 1;
|
||||
|
||||
if (buffer.length() + suffixLen > maxConstraintNameLength) {
|
||||
buffer.setLength(maxConstraintNameLength - suffixLen);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@@ -14,14 +13,13 @@ public class H2Platform extends DatabasePlatform {
|
||||
super();
|
||||
this.name = "h2";
|
||||
this.dbEncrypt = new H2DbEncrypt();
|
||||
// like ? escape'' not working in the latest version H2 so just using no
|
||||
// escape clause for now noting that backslash is an escape char for like in H2
|
||||
this.likeClause = "like ?";
|
||||
|
||||
// only support getGeneratedKeys with non-batch JDBC
|
||||
// so generally use SEQUENCE instead of IDENTITY for H2
|
||||
boolean useIdentity = GlobalProperties.getBoolean("ebean.h2platform.useIdentity", false);
|
||||
|
||||
IdType idType = useIdentity ? IdType.IDENTITY : IdType.SEQUENCE;
|
||||
this.dbIdentity.setIdType(idType);
|
||||
|
||||
this.dbIdentity.setIdType(IdType.SEQUENCE);
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
this.dbIdentity.setSupportsIdentity(true);
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import java.sql.Types;
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* H2 specific platform.
|
||||
@@ -16,14 +14,7 @@ public class HsqldbPlatform extends DatabasePlatform {
|
||||
super();
|
||||
this.name = "hsqldb";
|
||||
this.dbEncrypt = new H2DbEncrypt();
|
||||
|
||||
// only support getGeneratedKeys with non-batch JDBC
|
||||
// so generally use SEQUENCE instead of IDENTITY for H2
|
||||
boolean useIdentity = GlobalProperties.getBoolean("ebean.hsqldb.useIdentity", true);
|
||||
|
||||
IdType idType = useIdentity ? IdType.IDENTITY : IdType.SEQUENCE;
|
||||
this.dbIdentity.setIdType(idType);
|
||||
|
||||
this.dbIdentity.setIdType(IdType.IDENTITY);
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
this.dbIdentity.setSupportsIdentity(true);
|
||||
|
||||
@@ -17,22 +17,21 @@ public class LimitOffsetSqlLimiter implements SqlLimiter {
|
||||
|
||||
public SqlLimitResponse limit(SqlLimitRequest request) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(512);
|
||||
String dbSql = request.getDbSql();
|
||||
|
||||
StringBuilder sb = new StringBuilder(50 + dbSql.length());
|
||||
sb.append("select ");
|
||||
if (request.isDistinct()) {
|
||||
sb.append("distinct ");
|
||||
}
|
||||
|
||||
sb.append(request.getDbSql());
|
||||
sb.append(dbSql);
|
||||
|
||||
int firstRow = request.getFirstRow();
|
||||
int maxRows = request.getMaxRows();
|
||||
if (maxRows > 0) {
|
||||
maxRows = maxRows + 1;
|
||||
}
|
||||
|
||||
if (maxRows > 0 || firstRow > 0) {
|
||||
sb.append(" ").append(NEW_LINE).append(LIMIT).append(" ").append(maxRows);
|
||||
sb.append(" ").append(LIMIT).append(" ").append(maxRows);
|
||||
if (firstRow > 0) {
|
||||
sb.append(" ").append(OFFSET).append(" ");
|
||||
sb.append(firstRow);
|
||||
|
||||
@@ -17,8 +17,12 @@ public class MsSqlServer2005Platform extends DatabasePlatform {
|
||||
public MsSqlServer2005Platform() {
|
||||
super();
|
||||
this.name = "mssqlserver2005";
|
||||
// effectively disable persistBatchOnCascade mode for SQL Server
|
||||
// due to lack of support for getGeneratedKeys in batch mode
|
||||
this.disallowBatchOnCascade = true;
|
||||
this.idInExpandedForm = true;
|
||||
this.sqlLimiter = new MsSqlServer2005SqlLimiter();
|
||||
this.dbDdlSyntax.setIdentity("identity(1,1)");
|
||||
this.dbDdlSyntax = new MsDdlSyntax();
|
||||
this.dbIdentity.setIdType(IdType.IDENTITY);
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsIdentity(true);
|
||||
@@ -28,6 +32,7 @@ public class MsSqlServer2005Platform extends DatabasePlatform {
|
||||
|
||||
dbTypeMap.put(Types.BOOLEAN, new DbType("bit default 0"));
|
||||
|
||||
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
|
||||
dbTypeMap.put(Types.BIGINT, new DbType("numeric", 19));
|
||||
dbTypeMap.put(Types.REAL, new DbType("float(16)"));
|
||||
dbTypeMap.put(Types.DOUBLE, new DbType("float(32)"));
|
||||
@@ -45,4 +50,30 @@ public class MsSqlServer2005Platform extends DatabasePlatform {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* MS SQL Server specific DDL Syntax.
|
||||
*/
|
||||
public class MsDdlSyntax extends DbDdlSyntax {
|
||||
|
||||
MsDdlSyntax() {
|
||||
this.identity = "identity(1,1)";
|
||||
this.dropKeyConstraints = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return some DDL to disable constraints on the given table.
|
||||
*/
|
||||
public String dropKeyConstraintPrefix(String tableName, String fkName) {
|
||||
return "IF OBJECT_ID('"+fkName+"', 'F') IS NOT NULL";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return prefix text that goes before drop table.
|
||||
*/
|
||||
public String dropTablePrefix(String tableName) {
|
||||
return "IF OBJECT_ID('"+tableName+"', 'U') IS NOT NULL ";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,9 +26,7 @@ public class MsSqlServer2005SqlLimiter implements SqlLimiter {
|
||||
|
||||
int lastRow = request.getMaxRows();
|
||||
if (lastRow > 0) {
|
||||
// fetch 1 more than we return so that
|
||||
// we know if more rows are available
|
||||
lastRow = lastRow + firstRow + 1;
|
||||
lastRow = lastRow + firstRow;
|
||||
}
|
||||
|
||||
if (firstRow < 1) {
|
||||
|
||||
@@ -20,6 +20,7 @@ public class MySqlPlatform extends DatabasePlatform {
|
||||
public MySqlPlatform() {
|
||||
super();
|
||||
this.name = "mysql";
|
||||
this.likeClause = "like ? escape''";
|
||||
this.selectCountWithAlias = true;
|
||||
this.dbEncrypt = new MySqlDbEncrypt();
|
||||
|
||||
@@ -36,7 +37,7 @@ public class MySqlPlatform extends DatabasePlatform {
|
||||
|
||||
dbTypeMap.put(Types.BIT, new DbType("tinyint(1) default 0"));
|
||||
dbTypeMap.put(Types.BOOLEAN, new DbType("tinyint(1) default 0"));
|
||||
dbTypeMap.put(Types.TIMESTAMP, new DbType("datetime"));
|
||||
dbTypeMap.put(Types.TIMESTAMP, new DbType("datetime(6)"));
|
||||
dbTypeMap.put(Types.CLOB, new MySqlClob());
|
||||
dbTypeMap.put(Types.BLOB, new MySqlBlob());
|
||||
dbTypeMap.put(Types.BINARY, new DbType("binary", 255));
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
|
||||
/**
|
||||
* Oracle encryption support.
|
||||
*
|
||||
@@ -48,12 +46,19 @@ import com.avaje.ebean.config.GlobalProperties;
|
||||
public class Oracle10DbEncrypt extends AbstractDbEncrypt {
|
||||
|
||||
/**
|
||||
* Constructs the Oracle10DbEncrypt.
|
||||
* Constructs the Oracle10DbEncrypt with default encrypt and decrypt stored procedures.
|
||||
*/
|
||||
public Oracle10DbEncrypt() {
|
||||
this("eb_encrypt", "eb_decrypt");
|
||||
}
|
||||
|
||||
String encryptfunction = GlobalProperties.get("ebean.oracle.encryptfunction", "eb_encrypt");
|
||||
String decryptfunction = GlobalProperties.get("ebean.oracle.decryptfunction", "eb_decrypt");
|
||||
/**
|
||||
* Constructs the Oracle10DbEncrypt specifying encrypt and decrypt stored procedures.
|
||||
*
|
||||
* @param encryptfunction the encrypt stored procedure
|
||||
* @param decryptfunction the decrypt stored procedure
|
||||
*/
|
||||
public Oracle10DbEncrypt(String encryptfunction, String decryptfunction) {
|
||||
|
||||
this.varcharEncryptFunction = new OraVarcharFunction(encryptfunction, decryptfunction);
|
||||
this.dateEncryptFunction = new OraDateFunction(encryptfunction, decryptfunction);
|
||||
|
||||
@@ -1,67 +1,68 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* Oracle10 and greater specific platform.
|
||||
*/
|
||||
public class Oracle10Platform extends DatabasePlatform {
|
||||
|
||||
public Oracle10Platform() {
|
||||
super();
|
||||
this.name = "oracle";
|
||||
this.dbEncrypt = new Oracle10DbEncrypt();
|
||||
|
||||
this.sqlLimiter = new RownumSqlLimiter();
|
||||
|
||||
// Not using getGeneratedKeys as instead we will
|
||||
// batch load sequences which enables JDBC batch execution
|
||||
dbIdentity.setSupportsGetGeneratedKeys(false);
|
||||
dbIdentity.setIdType(IdType.SEQUENCE);
|
||||
dbIdentity.setSupportsSequence(true);
|
||||
|
||||
this.treatEmptyStringsAsNull = true;
|
||||
|
||||
this.openQuote = "\"";
|
||||
this.closeQuote = "\"";
|
||||
|
||||
booleanDbType = Types.INTEGER;
|
||||
dbTypeMap.put(Types.BOOLEAN, new DbType("number(1) default 0"));
|
||||
|
||||
dbTypeMap.put(Types.INTEGER, new DbType("number", 10));
|
||||
dbTypeMap.put(Types.BIGINT, new DbType("number", 19));
|
||||
dbTypeMap.put(Types.REAL, new DbType("number", 19, 4));
|
||||
dbTypeMap.put(Types.DOUBLE, new DbType("number", 19, 4));
|
||||
dbTypeMap.put(Types.SMALLINT, new DbType("number", 5));
|
||||
dbTypeMap.put(Types.TINYINT, new DbType("number", 3));
|
||||
dbTypeMap.put(Types.DECIMAL, new DbType("number", 38));
|
||||
|
||||
dbTypeMap.put(Types.VARCHAR, new DbType("varchar2", 255));
|
||||
|
||||
dbTypeMap.put(Types.LONGVARBINARY, new DbType("blob"));
|
||||
dbTypeMap.put(Types.LONGVARCHAR, new DbType("clob"));
|
||||
dbTypeMap.put(Types.VARBINARY, new DbType("raw", 255));
|
||||
dbTypeMap.put(Types.BINARY, new DbType("raw", 255));
|
||||
|
||||
dbTypeMap.put(Types.TIME, new DbType("timestamp"));
|
||||
|
||||
dbDdlSyntax.setDropTableCascade("cascade constraints purge");
|
||||
dbDdlSyntax.setIdentity(null);
|
||||
dbDdlSyntax.setMaxConstraintNameLength(30);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds,
|
||||
String seqName, int batchSize) {
|
||||
|
||||
return new OracleSequenceIdGenerator(be, ds, seqName, batchSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String withForUpdate(String sql) {
|
||||
return sql + " for update";
|
||||
}
|
||||
}
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* Oracle10 and greater specific platform.
|
||||
*/
|
||||
public class Oracle10Platform extends DatabasePlatform {
|
||||
|
||||
public Oracle10Platform() {
|
||||
super();
|
||||
this.name = "oracle";
|
||||
// OnQueryOnly.CLOSE as a performance optimisation on Oracle
|
||||
this.onQueryOnly = OnQueryOnly.CLOSE;
|
||||
this.dbEncrypt = new Oracle10DbEncrypt();
|
||||
this.sqlLimiter = new RownumSqlLimiter();
|
||||
|
||||
// Not using getGeneratedKeys as instead we will
|
||||
// batch load sequences which enables JDBC batch execution
|
||||
dbIdentity.setSupportsGetGeneratedKeys(false);
|
||||
dbIdentity.setIdType(IdType.SEQUENCE);
|
||||
dbIdentity.setSupportsSequence(true);
|
||||
|
||||
this.treatEmptyStringsAsNull = true;
|
||||
|
||||
this.openQuote = "\"";
|
||||
this.closeQuote = "\"";
|
||||
|
||||
booleanDbType = Types.INTEGER;
|
||||
dbTypeMap.put(Types.BOOLEAN, new DbType("number(1) default 0"));
|
||||
|
||||
dbTypeMap.put(Types.INTEGER, new DbType("number", 10));
|
||||
dbTypeMap.put(Types.BIGINT, new DbType("number", 19));
|
||||
dbTypeMap.put(Types.REAL, new DbType("number", 19, 4));
|
||||
dbTypeMap.put(Types.DOUBLE, new DbType("number", 19, 4));
|
||||
dbTypeMap.put(Types.SMALLINT, new DbType("number", 5));
|
||||
dbTypeMap.put(Types.TINYINT, new DbType("number", 3));
|
||||
dbTypeMap.put(Types.DECIMAL, new DbType("number", 38));
|
||||
|
||||
dbTypeMap.put(Types.VARCHAR, new DbType("varchar2", 255));
|
||||
|
||||
dbTypeMap.put(Types.LONGVARBINARY, new DbType("blob"));
|
||||
dbTypeMap.put(Types.LONGVARCHAR, new DbType("clob"));
|
||||
dbTypeMap.put(Types.VARBINARY, new DbType("raw", 255));
|
||||
dbTypeMap.put(Types.BINARY, new DbType("raw", 255));
|
||||
|
||||
dbTypeMap.put(Types.TIME, new DbType("timestamp"));
|
||||
|
||||
dbDdlSyntax.setDropTableCascade("cascade constraints purge");
|
||||
dbDdlSyntax.setIdentity(null);
|
||||
dbDdlSyntax.setMaxConstraintNameLength(30);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds,
|
||||
String seqName, int batchSize) {
|
||||
|
||||
return new OracleSequenceIdGenerator(be, ds, seqName, batchSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String withForUpdate(String sql) {
|
||||
return sql + " for update";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* Postgres v8.3 specific platform.
|
||||
* <p>
|
||||
* No support for getGeneratedKeys.
|
||||
* </p>
|
||||
*/
|
||||
public class Postgres8Platform extends DatabasePlatform {
|
||||
|
||||
public Postgres8Platform() {
|
||||
super();
|
||||
this.name = "postgres";
|
||||
this.selectCountWithAlias = true;
|
||||
this.blobDbType = Types.LONGVARBINARY;
|
||||
this.clobDbType = Types.VARCHAR;
|
||||
|
||||
this.dbEncrypt = new PostgresDbEncrypt();
|
||||
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(false);
|
||||
this.dbIdentity.setIdType(IdType.SEQUENCE);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
|
||||
this.columnAliasPrefix = "as c";
|
||||
this.openQuote = "\"";
|
||||
this.closeQuote = "\"";
|
||||
|
||||
// dbTypeMap.put(Types.BOOLEAN, new DbType("bit default 0"));
|
||||
|
||||
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
|
||||
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
|
||||
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
|
||||
dbTypeMap.put(Types.DECIMAL, new DbType("decimal", 38));
|
||||
|
||||
dbTypeMap.put(Types.BINARY, new DbType("bytea", false));
|
||||
dbTypeMap.put(Types.VARBINARY, new DbType("bytea", false));
|
||||
|
||||
dbTypeMap.put(Types.BLOB, new DbType("bytea", false));
|
||||
dbTypeMap.put(Types.CLOB, new DbType("text"));
|
||||
dbTypeMap.put(Types.LONGVARBINARY, new DbType("bytea", false));
|
||||
dbTypeMap.put(Types.LONGVARCHAR, new DbType("text"));
|
||||
|
||||
dbDdlSyntax.setDropTableCascade("cascade");
|
||||
dbDdlSyntax.setDropIfExists("if exists");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Postgres specific sequence IdGenerator.
|
||||
*/
|
||||
@Override
|
||||
public IdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds,
|
||||
String seqName, int batchSize) {
|
||||
|
||||
return new PostgresSequenceIdGenerator(be, ds, seqName, batchSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String withForUpdate(String sql) {
|
||||
return sql + " for update";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
public class PostgresDdlSyntax extends DbDdlSyntax {
|
||||
|
||||
/**
|
||||
* Map bigint, integer and smallint into their equivilent serial types.
|
||||
*/
|
||||
@Override
|
||||
public String getIdentityColumnDefn(String columnDefn) {
|
||||
|
||||
//smallserial, serial and bigserial
|
||||
if ("bigint".equalsIgnoreCase(columnDefn)) {
|
||||
return "bigserial";
|
||||
}
|
||||
if ("integer".equalsIgnoreCase(columnDefn)) {
|
||||
return "serial";
|
||||
}
|
||||
if ("smallint".equalsIgnoreCase(columnDefn)) {
|
||||
return "smallserial";
|
||||
}
|
||||
|
||||
return columnDefn;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,73 +1,80 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* Postgres v8.3 specific platform.
|
||||
* <p>
|
||||
* No support for getGeneratedKeys.
|
||||
* </p>
|
||||
*/
|
||||
public class PostgresPlatform extends DatabasePlatform {
|
||||
|
||||
public PostgresPlatform() {
|
||||
super();
|
||||
this.name = "postgres";
|
||||
this.selectCountWithAlias = true;
|
||||
this.blobDbType = Types.LONGVARBINARY;
|
||||
this.clobDbType = Types.VARCHAR;
|
||||
|
||||
this.dbEncrypt = new PostgresDbEncrypt();
|
||||
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(false);
|
||||
this.dbIdentity.setIdType(IdType.SEQUENCE);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
|
||||
String colAlias = GlobalProperties.get("ebean.columnAliasPrefix", null);
|
||||
if (colAlias == null) {
|
||||
// Postgres requires the "as" keyword for column alias
|
||||
GlobalProperties.put("ebean.columnAliasPrefix", "as c");
|
||||
}
|
||||
|
||||
this.openQuote = "\"";
|
||||
this.closeQuote = "\"";
|
||||
|
||||
// dbTypeMap.put(Types.BOOLEAN, new DbType("bit default 0"));
|
||||
|
||||
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
|
||||
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
|
||||
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
|
||||
dbTypeMap.put(Types.DECIMAL, new DbType("decimal", 38));
|
||||
|
||||
dbTypeMap.put(Types.BINARY, new DbType("bytea", false));
|
||||
dbTypeMap.put(Types.VARBINARY, new DbType("bytea", false));
|
||||
|
||||
dbTypeMap.put(Types.BLOB, new DbType("bytea", false));
|
||||
dbTypeMap.put(Types.CLOB, new DbType("text"));
|
||||
dbTypeMap.put(Types.LONGVARBINARY, new DbType("bytea", false));
|
||||
dbTypeMap.put(Types.LONGVARCHAR, new DbType("text"));
|
||||
|
||||
dbDdlSyntax.setDropTableCascade("cascade");
|
||||
dbDdlSyntax.setDropIfExists("if exists");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Postgres specific sequence IdGenerator.
|
||||
*/
|
||||
@Override
|
||||
public IdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds,
|
||||
String seqName, int batchSize) {
|
||||
|
||||
return new PostgresSequenceIdGenerator(be, ds, seqName, batchSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String withForUpdate(String sql) {
|
||||
return sql + " for update";
|
||||
}
|
||||
}
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* Postgres v9 specific platform.
|
||||
* <p>
|
||||
* Uses serial types and getGeneratedKeys.
|
||||
* </p>
|
||||
*/
|
||||
public class PostgresPlatform extends DatabasePlatform {
|
||||
|
||||
/**
|
||||
* Unique jdbc type id defined for hstore type.
|
||||
*/
|
||||
public static final int TYPE_HSTORE = 4001;
|
||||
|
||||
public PostgresPlatform() {
|
||||
super();
|
||||
this.name = "postgres";
|
||||
// OnQueryOnly.CLOSE as a performance optimisation on Postgres
|
||||
this.onQueryOnly = OnQueryOnly.CLOSE;
|
||||
this.likeClause = "like ? escape''";
|
||||
|
||||
this.dbDdlSyntax = new PostgresDdlSyntax();
|
||||
|
||||
this.selectCountWithAlias = true;
|
||||
this.blobDbType = Types.LONGVARBINARY;
|
||||
this.clobDbType = Types.VARCHAR;
|
||||
|
||||
this.dbEncrypt = new PostgresDbEncrypt();
|
||||
|
||||
// Use Identity and getGeneratedKeys
|
||||
this.dbIdentity.setIdType(IdType.IDENTITY);
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(true);
|
||||
this.dbIdentity.setSupportsSequence(true);
|
||||
|
||||
this.columnAliasPrefix = "as c";
|
||||
|
||||
this.openQuote = "\"";
|
||||
this.closeQuote = "\"";
|
||||
|
||||
dbTypeMap.put(TYPE_HSTORE, new DbType("hstore"));
|
||||
|
||||
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
|
||||
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
|
||||
dbTypeMap.put(Types.TINYINT, new DbType("smallint"));
|
||||
dbTypeMap.put(Types.DECIMAL, new DbType("decimal", 38));
|
||||
|
||||
dbTypeMap.put(Types.BINARY, new DbType("bytea", false));
|
||||
dbTypeMap.put(Types.VARBINARY, new DbType("bytea", false));
|
||||
|
||||
dbTypeMap.put(Types.BLOB, new DbType("bytea", false));
|
||||
dbTypeMap.put(Types.CLOB, new DbType("text"));
|
||||
dbTypeMap.put(Types.LONGVARBINARY, new DbType("bytea", false));
|
||||
dbTypeMap.put(Types.LONGVARCHAR, new DbType("text"));
|
||||
|
||||
dbDdlSyntax.setDropTableCascade("cascade");
|
||||
dbDdlSyntax.setDropIfExists("if exists");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Postgres specific sequence IdGenerator.
|
||||
*/
|
||||
@Override
|
||||
public IdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds,
|
||||
String seqName, int batchSize) {
|
||||
|
||||
return new PostgresSequenceIdGenerator(be, ds, seqName, batchSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String withForUpdate(String sql) {
|
||||
return sql + " for update";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public class RowNumberSqlLimiter implements SqlLimiter {
|
||||
*/
|
||||
private static final String ROW_NUMBER_AS = ") as rn, ";
|
||||
|
||||
final String rowNumberWindowAlias;
|
||||
private final String rowNumberWindowAlias;
|
||||
|
||||
/**
|
||||
* Specify the name of the rowNumberWindowAlias.
|
||||
@@ -30,16 +30,18 @@ public class RowNumberSqlLimiter implements SqlLimiter {
|
||||
|
||||
public SqlLimitResponse limit(SqlLimitRequest request) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(500);
|
||||
String dbSql = request.getDbSql();
|
||||
|
||||
StringBuilder sb = new StringBuilder(60 + dbSql.length());
|
||||
|
||||
int firstRow = request.getFirstRow();
|
||||
|
||||
int lastRow = request.getMaxRows();
|
||||
if (lastRow > 0) {
|
||||
lastRow = lastRow + firstRow + 1;
|
||||
lastRow = lastRow + firstRow;
|
||||
}
|
||||
|
||||
sb.append("select * from (").append(NEW_LINE);
|
||||
sb.append("select * from ( ");
|
||||
|
||||
sb.append("select ");
|
||||
if (request.isDistinct()) {
|
||||
@@ -50,9 +52,9 @@ public class RowNumberSqlLimiter implements SqlLimiter {
|
||||
sb.append(request.getDbOrderBy());
|
||||
sb.append(ROW_NUMBER_AS);
|
||||
|
||||
sb.append(request.getDbSql());
|
||||
sb.append(dbSql);
|
||||
|
||||
sb.append(NEW_LINE).append(") ");
|
||||
sb.append(" ) ");
|
||||
sb.append(rowNumberWindowAlias);
|
||||
sb.append(" where ");
|
||||
if (firstRow > 0) {
|
||||
|
||||
@@ -35,38 +35,40 @@ public class RownumSqlLimiter implements SqlLimiter {
|
||||
// :MAX_ROW_TO_FETCH )
|
||||
// where rnum >= :MIN_ROW_TO_FETCH;
|
||||
|
||||
StringBuilder sb = new StringBuilder(500);
|
||||
String dbSql = request.getDbSql();
|
||||
|
||||
StringBuilder sb = new StringBuilder(60 + dbSql.length());
|
||||
|
||||
int firstRow = request.getFirstRow();
|
||||
|
||||
int lastRow = request.getMaxRows();
|
||||
if (lastRow > 0) {
|
||||
lastRow = lastRow + firstRow + 1;
|
||||
lastRow = lastRow + firstRow;
|
||||
}
|
||||
|
||||
sb.append("select * ").append(NEW_LINE).append("from ( ");
|
||||
sb.append("select * from ( ");
|
||||
|
||||
sb.append("select ");
|
||||
if (useFirstRowsHint && request.getMaxRows() > 0) {
|
||||
sb.append("/*+ FIRST_ROWS(").append(request.getMaxRows() + 1).append(") */ ");
|
||||
sb.append("/*+ FIRST_ROWS(").append(request.getMaxRows()).append(") */ ");
|
||||
}
|
||||
|
||||
sb.append("rownum ").append(rnum).append(", a.* ").append(NEW_LINE);
|
||||
sb.append(" from (");//
|
||||
sb.append("rownum ").append(rnum).append(", a.* ");
|
||||
sb.append(" from (");
|
||||
|
||||
sb.append(" select ");
|
||||
if (request.isDistinct()) {
|
||||
sb.append("distinct ");
|
||||
}
|
||||
sb.append(request.getDbSql());
|
||||
sb.append(dbSql);
|
||||
|
||||
sb.append(NEW_LINE).append(" ) a ");
|
||||
sb.append(NEW_LINE).append(" ) a ");
|
||||
if (lastRow > 0) {
|
||||
sb.append(NEW_LINE).append(" where rownum <= ").append(lastRow);
|
||||
sb.append(" where rownum <= ").append(lastRow);
|
||||
}
|
||||
sb.append(NEW_LINE).append(" ) ");
|
||||
sb.append(" ) ");
|
||||
if (firstRow > 0) {
|
||||
sb.append(NEW_LINE).append("where ");
|
||||
sb.append(" where ");
|
||||
sb.append(rnum).append(" > ").append(firstRow);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ public abstract class SequenceIdGenerator implements IdGenerator {
|
||||
|
||||
protected final BackgroundExecutor backgroundExecutor;
|
||||
|
||||
protected final ArrayList<Integer> idList = new ArrayList<Integer>(50);
|
||||
protected final ArrayList<Long> idList = new ArrayList<Long>(50);
|
||||
|
||||
protected int batchSize;
|
||||
|
||||
@@ -117,7 +117,7 @@ public abstract class SequenceIdGenerator implements IdGenerator {
|
||||
if (idList.size() == 0) {
|
||||
loadMoreIds(batchSize, t);
|
||||
}
|
||||
Integer nextId = idList.remove(0);
|
||||
Long nextId = idList.remove(0);
|
||||
|
||||
if (batchSize > 1) {
|
||||
if (idList.size() <= batchSize / 2) {
|
||||
@@ -158,7 +158,7 @@ public abstract class SequenceIdGenerator implements IdGenerator {
|
||||
|
||||
protected void loadMoreIds(final int numberToLoad, Transaction t) {
|
||||
|
||||
ArrayList<Integer> newIds = getMoreIds(numberToLoad, t);
|
||||
ArrayList<Long> newIds = getMoreIds(numberToLoad, t);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("... seq:" + seqName + " loaded:" + numberToLoad + " ids:" + newIds);
|
||||
@@ -174,11 +174,11 @@ public abstract class SequenceIdGenerator implements IdGenerator {
|
||||
/**
|
||||
* Get more Id's by executing a query and reading the Id's returned.
|
||||
*/
|
||||
protected ArrayList<Integer> getMoreIds(int loadSize, Transaction t) {
|
||||
protected ArrayList<Long> getMoreIds(int loadSize, Transaction t) {
|
||||
|
||||
String sql = getSql(loadSize);
|
||||
|
||||
ArrayList<Integer> newIds = new ArrayList<Integer>(loadSize);
|
||||
ArrayList<Long> newIds = new ArrayList<Long>(loadSize);
|
||||
|
||||
boolean useTxnConnection = t != null;
|
||||
|
||||
@@ -191,12 +191,10 @@ public abstract class SequenceIdGenerator implements IdGenerator {
|
||||
pstmt = c.prepareStatement(sql);
|
||||
rset = pstmt.executeQuery();
|
||||
while (rset.next()) {
|
||||
int val = rset.getInt(1);
|
||||
newIds.add(Integer.valueOf(val));
|
||||
newIds.add(rset.getLong(1));
|
||||
}
|
||||
if (newIds.size() == 0) {
|
||||
String m = "Always expecting more than 1 row from " + sql;
|
||||
throw new PersistenceException(m);
|
||||
throw new PersistenceException("Always expecting more than 1 row from " + sql);
|
||||
}
|
||||
|
||||
return newIds;
|
||||
|
||||
@@ -8,15 +8,12 @@ public class SqlAnywhereLimiter implements SqlLimiter {
|
||||
|
||||
public SqlLimitResponse limit(SqlLimitRequest request) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(500);
|
||||
String dbSql = request.getDbSql();
|
||||
|
||||
StringBuilder sb = new StringBuilder(60 + dbSql.length());
|
||||
|
||||
int firstRow = request.getFirstRow();
|
||||
int maxRows = request.getMaxRows();
|
||||
if (maxRows > 0) {
|
||||
// fetch 1 more than we return so that
|
||||
// we know if more rows are available
|
||||
maxRows = maxRows + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* SELECT TOP xx START AT xx ... FROM ...
|
||||
@@ -31,7 +28,7 @@ public class SqlAnywhereLimiter implements SqlLimiter {
|
||||
if (firstRow > 0) {
|
||||
sb.append("start at ").append(firstRow + 1).append(" ");
|
||||
}
|
||||
sb.append(request.getDbSql());
|
||||
sb.append(dbSql);
|
||||
|
||||
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ import com.avaje.ebean.Query;
|
||||
/**
|
||||
* The request object for the query that can have sql limiting applied to it
|
||||
* (such as a LIMIT OFFSET clause).
|
||||
*
|
||||
* @author rob
|
||||
*/
|
||||
public interface SqlLimitRequest {
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.avaje.ebean.event;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Provides a base implementation of BeanPersistListener.
|
||||
* <p>
|
||||
* Objects extending this should override the methods then are interested in.
|
||||
* The default inserted() updated() and deleted() methods return false and as such
|
||||
* means other servers in the cluster are not notified.
|
||||
* </p>
|
||||
*/
|
||||
public abstract class AbstractBeanPersistListener implements BeanPersistListener {
|
||||
|
||||
/**
|
||||
* Notified that a bean has been inserted locally. Return true if you want the
|
||||
* cluster to be notified of the event.
|
||||
*
|
||||
* @param bean The bean that was inserted.
|
||||
*/
|
||||
@Override
|
||||
public boolean inserted(Object bean) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notified that a bean has been updated locally. Return true if you want the
|
||||
* cluster to be notified of the event.
|
||||
*
|
||||
* @param bean The bean that was updated.
|
||||
* @param updatedProperties The properties that were modified by this update.
|
||||
*/
|
||||
@Override
|
||||
public boolean updated(Object bean, Set<String> updatedProperties) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notified that a bean has been deleted locally. Return true if you want the
|
||||
* cluster to be notified of the event.
|
||||
*
|
||||
* @param bean The bean that was deleted.
|
||||
*/
|
||||
@Override
|
||||
public boolean deleted(Object bean) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify that a bean was inserted on another node of the cluster.
|
||||
*
|
||||
* @param id the id value of the inserted bean
|
||||
*/
|
||||
@Override
|
||||
public void remoteInsert(Object id) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify that a bean was updated on another node of the cluster.
|
||||
*
|
||||
* @param id the id value of the updated bean.
|
||||
*/
|
||||
@Override
|
||||
public void remoteUpdate(Object id) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify that a bean was deleted on another node of the cluster.
|
||||
*
|
||||
* @param id the id value of the deleted bean.
|
||||
*/
|
||||
@Override
|
||||
public void remoteDelete(Object id) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import java.util.Set;
|
||||
/**
|
||||
* Used to enhance or override the default bean persistence mechanism.
|
||||
* <p>
|
||||
* Note that if want to totally change the finding, you need to use a BeanFinder
|
||||
* Note that if want to totally change the finding, you need to use a BeanQueryAdapter
|
||||
* rather than using postLoad().
|
||||
* </p>
|
||||
* <p>
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.avaje.ebean.event;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
|
||||
/**
|
||||
* Listens for committed bean events.
|
||||
* <p>
|
||||
@@ -31,10 +33,17 @@ import java.util.Set;
|
||||
* </p>
|
||||
* <p>
|
||||
* A BeanPersistListener is either found automatically via class path search or
|
||||
* can be added programmatically via ServerConfiguration.addEntity().
|
||||
* can be added programmatically via {@link ServerConfig#add(BeanPersistListener)}}.
|
||||
* </p>
|
||||
* @see ServerConfig#add(BeanPersistListener)
|
||||
*/
|
||||
public interface BeanPersistListener<T> {
|
||||
public interface BeanPersistListener {
|
||||
|
||||
/**
|
||||
* Return true if this BeanPersistListener should be registered for events
|
||||
* on this entity type.
|
||||
*/
|
||||
public boolean isRegisterFor(Class<?> cls);
|
||||
|
||||
/**
|
||||
* Notified that a bean has been inserted locally. Return true if you want the
|
||||
@@ -43,7 +52,7 @@ public interface BeanPersistListener<T> {
|
||||
* @param bean
|
||||
* The bean that was inserted.
|
||||
*/
|
||||
public boolean inserted(T bean);
|
||||
public boolean inserted(Object bean);
|
||||
|
||||
/**
|
||||
* Notified that a bean has been updated locally. Return true if you want the
|
||||
@@ -52,9 +61,9 @@ public interface BeanPersistListener<T> {
|
||||
* @param bean
|
||||
* The bean that was updated.
|
||||
* @param updatedProperties
|
||||
* the properties on the bean that where updated
|
||||
* The properties that were modified by this update.
|
||||
*/
|
||||
public boolean updated(T bean, Set<String> updatedProperties);
|
||||
public boolean updated(Object bean, Set<String> updatedProperties);
|
||||
|
||||
/**
|
||||
* Notified that a bean has been deleted locally. Return true if you want the
|
||||
@@ -63,7 +72,7 @@ public interface BeanPersistListener<T> {
|
||||
* @param bean
|
||||
* The bean that was deleted.
|
||||
*/
|
||||
public boolean deleted(T bean);
|
||||
public boolean deleted(Object bean);
|
||||
|
||||
/**
|
||||
* Notify that a bean was inserted on another node of the cluster.
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* Holds the information available for a bean persist (insert, update or
|
||||
@@ -41,12 +43,8 @@ public interface BeanPersistRequest<T> {
|
||||
public T getBean();
|
||||
|
||||
/**
|
||||
* Returns a bean containing the original values prior to the bean being
|
||||
* modified.
|
||||
* <p>
|
||||
* This is for updates only.
|
||||
* </p>
|
||||
* Returns a map of the properties that have changed and their new and old values.
|
||||
*/
|
||||
public T getOldValues();
|
||||
public Map<String,ValuePair> getUpdatedValues();
|
||||
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<title>Ebean API</title>
|
||||
</head>
|
||||
<body BGCOLOR="#ffffff">
|
||||
Ebean Object Relational Mapping (start at <a href='com/avaje/ebean/Ebean.html'>Ebean</a>
|
||||
or <a href='com/avaje/ebean/EbeanServer.html'>EbeanServer</a>).
|
||||
Ebean Object Relational Mapping (start at
|
||||
<a href='com/avaje/ebean/EbeanServer.html'>EbeanServer</a> or <a href='com/avaje/ebean/Ebean.html'>Ebean</a>).
|
||||
|
||||
|
||||
<h3>Ebean</h3>
|
||||
@@ -21,15 +21,15 @@ For a full description of the query language refer to <a href="com/avaje/ebean/Q
|
||||
<h3>
|
||||
EXAMPLE 1: Simple fetch
|
||||
</h3>
|
||||
<pre class="code">
|
||||
<pre>{@code
|
||||
// fetch order 10
|
||||
Order order = Ebean.find(Order.class, 10);
|
||||
</pre>
|
||||
}</pre>
|
||||
|
||||
<h3>
|
||||
EXAMPLE 2: Fetch an Object with associations
|
||||
</h3>
|
||||
<pre class="code">
|
||||
<pre>{@code
|
||||
// fetch Customer 7 including their billing and shipping addresses
|
||||
Customer customer = Ebean.find(Customer.class)
|
||||
.fetch("billingAddress");
|
||||
@@ -40,19 +40,19 @@ Customer customer = Ebean.find(Customer.class)
|
||||
|
||||
Address billAddr = customer.getBillingAddress();
|
||||
Address shipAddr = customer.getShippingAddress();
|
||||
</pre>
|
||||
}</pre>
|
||||
|
||||
<h3>
|
||||
EXAMPLE 3: Fetch a list of Objects with associations
|
||||
</h3>
|
||||
<pre class="code">
|
||||
<pre>{@code
|
||||
// Note: This example shows a "Partial Object".
|
||||
// For the product objects associated with the
|
||||
// order details only the product id and name is
|
||||
// fetched (the product objects are partially populated).
|
||||
|
||||
// fetch orders for customer.id = 2
|
||||
List<Order> orderList = Ebean.find(Order.class);
|
||||
List<Order> orderList = Ebean.find(Order.class);
|
||||
.fetch("customer")
|
||||
.fetch("customer.shippingAddress")
|
||||
.fetch("details")
|
||||
@@ -72,17 +72,17 @@ Order order = orderList.get(0);
|
||||
Customer customer = order.getCustomer();
|
||||
Address shipAddr = customer.getShippingAddress();
|
||||
|
||||
List<OrderDetail> details = order.getDetails();
|
||||
List<OrderDetail> details = order.getDetails();
|
||||
OrderDetail detail = details.get(0);
|
||||
Product product = detail.getProduct();
|
||||
String productName = product.getName();
|
||||
|
||||
</pre>
|
||||
}</pre>
|
||||
|
||||
<h3>
|
||||
EXAMPLE 4: Create and save an Order
|
||||
</h3>
|
||||
<pre class="code">
|
||||
<pre>{@code
|
||||
// get a Customer reference so we don't hit the database
|
||||
Customer custRef = Ebean.getReference(Customer.class, 7);
|
||||
|
||||
@@ -107,14 +107,14 @@ orderLines.add(line);
|
||||
// NB: assumes CascadeType.PERSIST is set on the order lines association
|
||||
Ebean.save(newOrder);
|
||||
|
||||
</pre>
|
||||
}</pre>
|
||||
|
||||
<h3>
|
||||
EXAMPLE 5: Use another database
|
||||
</h3>
|
||||
<pre class="code">
|
||||
<pre>{@code
|
||||
// Get access to the Human Resources EbeanServer/Database
|
||||
EbeanServer hrServer = Ebean.getServer("HR");
|
||||
EbeanServer hrServer = Ebean.getServer("HR");
|
||||
|
||||
|
||||
// fetch contact 3 from the HR database
|
||||
@@ -125,7 +125,7 @@ contact.setStatus(Contact.Status.INACTIVE);
|
||||
|
||||
// save the contact back to the HR database
|
||||
hrServer.save(contact);
|
||||
</pre>
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -19,9 +19,6 @@ import com.avaje.ebean.Query;
|
||||
* properties and applying that to both what to fetch (ORM query) and what to
|
||||
* render (JAX-RS JSON / XML).
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*
|
||||
*/
|
||||
public class PathProperties {
|
||||
|
||||
|
||||
@@ -53,6 +53,9 @@ class PathPropertiesParser {
|
||||
case '(':
|
||||
return currentWord();
|
||||
default:
|
||||
if (pos == 1) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
} while (pos < eof);
|
||||
throw new RuntimeException("Hit EOF while reading sectionTitle from " + startPos);
|
||||
@@ -91,6 +94,10 @@ class PathPropertiesParser {
|
||||
}
|
||||
|
||||
} while (pos < eof);
|
||||
if (startPos < pos) {
|
||||
String currentWord = source.substring(startPos, pos);
|
||||
currentPathProps.addProperty(currentWord);
|
||||
}
|
||||
}
|
||||
|
||||
private void addSubpath() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.avaje.ebean.text;
|
||||
|
||||
/**
|
||||
* An exception occured typically in processing CSV, JSON or XML.
|
||||
* An exception occurred typically in processing CSV, JSON or XML.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
|
||||
@@ -32,7 +32,6 @@ import com.avaje.ebean.text.StringParser;
|
||||
* csvReader.addDateTime("anniversary", "dd-MMM-yyyy");
|
||||
* csvReader.addProperty("billingAddress.line1");
|
||||
* csvReader.addProperty("billingAddress.city");
|
||||
* csvReader.addReference("billingAddress.country.code");
|
||||
*
|
||||
* csvReader.process(reader);
|
||||
*
|
||||
@@ -41,8 +40,6 @@ import com.avaje.ebean.text.StringParser;
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author rbygrave
|
||||
*
|
||||
* @param <T>
|
||||
* the entity bean type
|
||||
*/
|
||||
@@ -129,13 +126,6 @@ public interface CsvReader<T> {
|
||||
*/
|
||||
public void addProperty(String propertyName);
|
||||
|
||||
/**
|
||||
* Define the next property to be a reference. This effectively means it
|
||||
* represents a foreign key. For example, with an Address object a Country
|
||||
* Code could be a reference.
|
||||
*/
|
||||
public void addReference(String propertyName);
|
||||
|
||||
/**
|
||||
* Define the next property and use a custom StringParser to convert the
|
||||
* string content into the appropriate type for the property.
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
|
||||
/**
|
||||
* Utility that converts between JSON content and simple java Maps/Lists.
|
||||
*/
|
||||
public class EJson {
|
||||
|
||||
/**
|
||||
* Write the nested Map/List as json.
|
||||
*/
|
||||
public static String write(Object object) throws IOException {
|
||||
return EJsonWriter.write(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the nested Map/List as json to the writer.
|
||||
*/
|
||||
public static void write(Object object, Writer writer) throws IOException {
|
||||
EJsonWriter.write(object, writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the nested Map/List as json to the jsonGenerator.
|
||||
*/
|
||||
public static void write(Object object, JsonGenerator jsonGenerator) throws IOException {
|
||||
EJsonWriter.write(object, jsonGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a Map.
|
||||
*/
|
||||
public static Map<String,Object> parseObject(String json) throws IOException {
|
||||
return EJsonReader.parseObject(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a Map taking a reader.
|
||||
*/
|
||||
public static Map<String,Object> parseObject(Reader reader) throws IOException {
|
||||
return EJsonReader.parseObject(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a Map taking a JsonParser.
|
||||
*/
|
||||
public static Map<String,Object> parseObject(JsonParser parser) throws IOException {
|
||||
return EJsonReader.parseObject(parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a Map taking a JsonParser and a starting token.
|
||||
* <p>
|
||||
* Used when the first token is checked to see if the value is null prior to calling this.
|
||||
* </p>
|
||||
*/
|
||||
public static Map<String,Object> parseObject(JsonParser parser, JsonToken token) throws IOException {
|
||||
return EJsonReader.parseObject(parser, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static List<Object> parseList(String json) throws IOException {
|
||||
return EJsonReader.parseList(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List taking a Reader.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static List<Object> parseList(Reader reader) throws IOException {
|
||||
return EJsonReader.parseList(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List taking a JsonParser.
|
||||
*/
|
||||
public static List<Object> parseList(JsonParser parser) throws IOException {
|
||||
return EJsonReader.parseList(parser);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List or Map.
|
||||
*/
|
||||
public static Object parse(String json) throws IOException {
|
||||
return EJsonReader.parse(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List or Map.
|
||||
*/
|
||||
public static Object parse(Reader reader) throws IOException {
|
||||
return EJsonReader.parse(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List or Map.
|
||||
*/
|
||||
public static Object parse(JsonParser parser) throws IOException {
|
||||
return EJsonReader.parse(parser);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
|
||||
class EJsonReader {
|
||||
|
||||
static JsonFactory json = new JsonFactory();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Object> parseObject(String json) throws IOException {
|
||||
return (Map<String, Object>) parse(json);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Object> parseObject(Reader reader) throws IOException {
|
||||
return (Map<String, Object>) parse(reader);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Object> parseObject(JsonParser parser) throws IOException {
|
||||
return (Map<String, Object>) parse(parser);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Object> parseObject(JsonParser parser, JsonToken token) throws IOException {
|
||||
return (Map<String, Object>)parse(parser, token);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static List<Object> parseList(String json) throws IOException {
|
||||
return (List<Object>) parse(json);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static List<Object> parseList(Reader reader) throws IOException {
|
||||
return (List<Object>) parse(reader);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static List<Object> parseList(JsonParser parser) throws IOException {
|
||||
return (List<Object>) parse(parser);
|
||||
}
|
||||
|
||||
static Object parse(String json) throws IOException {
|
||||
return parse(new StringReader(json));
|
||||
}
|
||||
|
||||
static Object parse(Reader reader) throws IOException {
|
||||
return parse(json.createParser(reader));
|
||||
}
|
||||
|
||||
static Object parse(JsonParser parser) throws IOException {
|
||||
return parse(parser, null);
|
||||
}
|
||||
|
||||
static Object parse(JsonParser parser, JsonToken token) throws IOException {
|
||||
return new EJsonReader(parser).parseJson(token);
|
||||
}
|
||||
|
||||
private final JsonParser parser;
|
||||
|
||||
private int depth;
|
||||
|
||||
private Stack stack;
|
||||
|
||||
private Context currentContext;
|
||||
|
||||
EJsonReader(JsonParser parser) {
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
private void startArray() {
|
||||
depth++;
|
||||
stack.push(currentContext);
|
||||
currentContext = new ArrayContext();
|
||||
}
|
||||
|
||||
private void startObject() {
|
||||
depth++;
|
||||
stack.push(currentContext);
|
||||
currentContext = new ObjectContext();
|
||||
}
|
||||
|
||||
private void endArray() {
|
||||
end();
|
||||
}
|
||||
|
||||
private void endObject() {
|
||||
end();
|
||||
}
|
||||
|
||||
private void end() {
|
||||
depth--;
|
||||
if (!stack.isEmpty()) {
|
||||
currentContext = stack.pop(currentContext);
|
||||
}
|
||||
}
|
||||
|
||||
private void setValue(Object value) {
|
||||
currentContext.setValue(value);
|
||||
}
|
||||
|
||||
private void setValueNull() {
|
||||
currentContext.setValueNull();
|
||||
}
|
||||
|
||||
private Object parseJson(JsonToken token) throws IOException {
|
||||
|
||||
if (token == null) {
|
||||
token = parser.nextToken();
|
||||
// if it is a simple value just return it
|
||||
switch (token) {
|
||||
case VALUE_NULL: return null;
|
||||
case VALUE_FALSE: return Boolean.FALSE;
|
||||
case VALUE_TRUE: return Boolean.TRUE;
|
||||
case VALUE_STRING: return parser.getText();
|
||||
case VALUE_NUMBER_INT: return parser.getLongValue();
|
||||
case VALUE_NUMBER_FLOAT: return parser.getDecimalValue();
|
||||
}
|
||||
}
|
||||
|
||||
// it is a object or array, process the first JsonToken
|
||||
stack = new Stack();
|
||||
processJsonToken(token);
|
||||
|
||||
// process the rest of the object or array
|
||||
while (depth > 0) {
|
||||
token = parser.nextToken();
|
||||
processJsonToken(token);
|
||||
}
|
||||
|
||||
return currentContext.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the JsonToken for objects and arrays.
|
||||
*/
|
||||
private void processJsonToken(JsonToken token) throws IOException {
|
||||
switch (token) {
|
||||
|
||||
case START_ARRAY:
|
||||
startArray();
|
||||
break;
|
||||
|
||||
case START_OBJECT:
|
||||
startObject();
|
||||
break;
|
||||
|
||||
case FIELD_NAME:
|
||||
currentContext.setKey(parser.getCurrentName());
|
||||
break;
|
||||
|
||||
case VALUE_STRING:
|
||||
setValue(parser.getValueAsString());
|
||||
break;
|
||||
|
||||
case VALUE_NUMBER_INT:
|
||||
setValue(parser.getLongValue());
|
||||
break;
|
||||
|
||||
case VALUE_NUMBER_FLOAT:
|
||||
setValue(parser.getDecimalValue());
|
||||
break;
|
||||
|
||||
case VALUE_TRUE:
|
||||
setValue(Boolean.TRUE);
|
||||
break;
|
||||
|
||||
case VALUE_FALSE:
|
||||
setValue(Boolean.FALSE);
|
||||
break;
|
||||
|
||||
case VALUE_NULL:
|
||||
setValueNull();
|
||||
break;
|
||||
|
||||
case END_OBJECT:
|
||||
endObject();
|
||||
break;
|
||||
|
||||
case END_ARRAY:
|
||||
endArray();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Stack {
|
||||
|
||||
private Context head;
|
||||
|
||||
private void push(Context context) {
|
||||
if (context != null) {
|
||||
context.next = head;
|
||||
head = context;
|
||||
}
|
||||
}
|
||||
|
||||
private Context pop(Context endingContext) {
|
||||
if (head == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
Context temp = head;
|
||||
head = head.next;
|
||||
temp.popContext(endingContext);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private boolean isEmpty() {
|
||||
return head == null;
|
||||
}
|
||||
}
|
||||
|
||||
private static abstract class Context {
|
||||
Context next;
|
||||
abstract void popContext(Context temp);
|
||||
abstract Object getValue();
|
||||
abstract void setKey(String key);
|
||||
abstract void setValue(Object value);
|
||||
abstract void setValueNull();
|
||||
}
|
||||
|
||||
private static class ObjectContext extends Context {
|
||||
|
||||
private final Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
|
||||
private String key;
|
||||
|
||||
public void popContext(Context temp) {
|
||||
setValue(temp.getValue());
|
||||
}
|
||||
|
||||
Object getValue() {
|
||||
return map;
|
||||
}
|
||||
|
||||
void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
void setValue(Object value) {
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
void setValueNull() {
|
||||
map.put(key, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ArrayContext extends Context {
|
||||
|
||||
private final List<Object> values = new ArrayList<Object>();
|
||||
|
||||
public void popContext(Context temp) {
|
||||
values.add(temp.getValue());
|
||||
}
|
||||
|
||||
Object getValue() {
|
||||
return values;
|
||||
}
|
||||
|
||||
void setValue(Object value) {
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
void setValueNull() {
|
||||
// ignore
|
||||
}
|
||||
void setKey(String key) {
|
||||
// not expected
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
|
||||
class EJsonWriter {
|
||||
|
||||
/**
|
||||
* Base jsonFactory implementation used when it is not passed in.
|
||||
*/
|
||||
static JsonFactory jsonFactory = new JsonFactory();
|
||||
|
||||
static String write(Object object) throws IOException {
|
||||
StringWriter writer = new StringWriter(200);
|
||||
write(object, writer);
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
static void write(Object object, Writer writer) throws IOException {
|
||||
JsonGenerator generator = jsonFactory.createGenerator(writer);
|
||||
write(object, generator);
|
||||
generator.close();
|
||||
}
|
||||
|
||||
static void write(Object object, JsonGenerator jsonGenerator) {
|
||||
new EJsonWriter(jsonGenerator).writeJson(object);
|
||||
}
|
||||
|
||||
private final JsonGenerator jsonGenerator;
|
||||
|
||||
private EJsonWriter(JsonGenerator jsonGenerator) {
|
||||
this.jsonGenerator = jsonGenerator;
|
||||
}
|
||||
|
||||
private void writeJson(Object object) {
|
||||
writeJson(null, object);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void writeJson(String name, Object object) {
|
||||
try {
|
||||
if (object == null) {
|
||||
writeNull(name);
|
||||
|
||||
} else if (object instanceof Map) {
|
||||
writeMap(name, (Map<Object, Object>) object);
|
||||
|
||||
} else if (object instanceof Collection) {
|
||||
writeCollection(name, (Collection<Object>) object);
|
||||
|
||||
} else if (object instanceof Boolean) {
|
||||
writeBoolean(name, (Boolean) object);
|
||||
|
||||
} else if (object instanceof Number) {
|
||||
writeNumber(name, (Number) object);
|
||||
|
||||
} else if (object instanceof Date) {
|
||||
writeDate(name, (Date) object);
|
||||
|
||||
} else if (object instanceof String) {
|
||||
writeString(name, (String) object);
|
||||
|
||||
} else if (object instanceof Map.Entry<?, ?>) {
|
||||
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) object;
|
||||
writeJson(entry.getKey().toString(), entry.getValue());
|
||||
|
||||
} else {
|
||||
writeString(name, object.toString());
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeBoolean(String name, Boolean object) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeBoolean(object);
|
||||
} else {
|
||||
jsonGenerator.writeBooleanField(name, object);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeDate(String name, Date object) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeNumber(object.getTime());
|
||||
} else {
|
||||
jsonGenerator.writeNumberField(name, object.getTime());
|
||||
}
|
||||
}
|
||||
|
||||
private void writeNumber(String name, Number object) throws IOException {
|
||||
|
||||
if (object instanceof Long) {
|
||||
writeLong(name, object);
|
||||
|
||||
} else if (object instanceof Integer) {
|
||||
writeInteger(name, object);
|
||||
|
||||
} else if (object instanceof Double) {
|
||||
writeDouble(name, object);
|
||||
|
||||
} else if (object instanceof BigDecimal) {
|
||||
writeBigDecimal(name, object);
|
||||
|
||||
} else if (object instanceof BigInteger) {
|
||||
writeBigInteger(name, object);
|
||||
|
||||
} else {
|
||||
writeGeneralNumber(name, object);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeGeneralNumber(String name, Number object) throws IOException {
|
||||
writeBigDecimal(name, new BigDecimal(object.toString()));
|
||||
}
|
||||
|
||||
private void writeBigDecimal(String name, Number object) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeNumber((BigDecimal) object);
|
||||
} else {
|
||||
jsonGenerator.writeNumberField(name, (BigDecimal) object);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeBigInteger(String name, Number object) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeNumber((BigInteger) object);
|
||||
} else {
|
||||
jsonGenerator.writeNumberField(name, object.longValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void writeDouble(String name, Number object) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeNumber((Double) object);
|
||||
} else {
|
||||
jsonGenerator.writeNumberField(name, (Double) object);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeLong(String name, Number object) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeNumber((Long) object);
|
||||
} else {
|
||||
jsonGenerator.writeNumberField(name, (Long) object);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeInteger(String name, Number object) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeNumber((Integer) object);
|
||||
} else {
|
||||
jsonGenerator.writeNumberField(name, (Integer) object);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeNull(String name) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeNull();
|
||||
} else {
|
||||
jsonGenerator.writeNullField(name);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeString(String name, String object) throws IOException {
|
||||
if (name == null) {
|
||||
jsonGenerator.writeString(object);
|
||||
} else {
|
||||
jsonGenerator.writeStringField(name, object);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeCollection(String name, Collection<Object> collection) throws IOException {
|
||||
if (name != null) {
|
||||
jsonGenerator.writeFieldName(name);
|
||||
}
|
||||
jsonGenerator.writeStartArray();
|
||||
for (Object object : collection) {
|
||||
writeJson(null, object);
|
||||
}
|
||||
jsonGenerator.writeEndArray();
|
||||
}
|
||||
|
||||
private void writeMap(String name, Map<Object, Object> map) throws IOException {
|
||||
|
||||
if (name != null) {
|
||||
jsonGenerator.writeFieldName(name);
|
||||
}
|
||||
jsonGenerator.writeStartObject();
|
||||
Set<Entry<Object, Object>> entrySet = map.entrySet();
|
||||
for (Entry<Object, Object> entry : entrySet) {
|
||||
writeJson(entry.getKey().toString(), entry.getValue());
|
||||
}
|
||||
jsonGenerator.writeEndObject();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
@@ -7,125 +11,156 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* Converts objects to and from JSON format.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public interface JsonContext {
|
||||
|
||||
/**
|
||||
* Convert json string input into a Bean of a specific type.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public <T> T toBean(Class<T> rootType, String json);
|
||||
public <T> T toBean(Class<T> rootType, String json) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Convert json reader input into a Bean of a specific type.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public <T> T toBean(Class<T> rootType, Reader json);
|
||||
public <T> T toBean(Class<T> rootType, Reader json) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Convert json string input into a Bean of a specific type with options.
|
||||
* Convert json parser input into a Bean of a specific type.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public <T> T toBean(Class<T> rootType, String json, JsonReadOptions options);
|
||||
|
||||
/**
|
||||
* Convert json reader input into a Bean of a specific type with options.
|
||||
*/
|
||||
public <T> T toBean(Class<T> rootType, Reader json, JsonReadOptions options);
|
||||
public <T> T toBean(Class<T> cls, JsonParser parser) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Convert json string input into a list of beans of a specific type.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public <T> List<T> toList(Class<T> rootType, String json);
|
||||
|
||||
/**
|
||||
* Convert json string input into a list of beans of a specific type with
|
||||
* options.
|
||||
*/
|
||||
public <T> List<T> toList(Class<T> rootType, String json, JsonReadOptions options);
|
||||
public <T> List<T> toList(Class<T> rootType, String json) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Convert json reader input into a list of beans of a specific type.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public <T> List<T> toList(Class<T> rootType, Reader json);
|
||||
public <T> List<T> toList(Class<T> rootType, Reader json) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Convert json reader input into a list of beans of a specific type with
|
||||
* options.
|
||||
* Convert json parser input into a list of beans of a specific type.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public <T> List<T> toList(Class<T> rootType, Reader json, JsonReadOptions options);
|
||||
public <T> List<T> toList(Class<T> cls, JsonParser src) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Use the genericType to determine if this should be converted into a List or
|
||||
* bean.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public Object toObject(Type genericType, Reader json, JsonReadOptions options);
|
||||
public Object toObject(Type genericType, Reader json) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Use the genericType to determine if this should be converted into a List or
|
||||
* bean.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public Object toObject(Type genericType, String json, JsonReadOptions options);
|
||||
public Object toObject(Type genericType, String json) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Write the bean or collection in JSON format to the writer with default
|
||||
* options.
|
||||
*
|
||||
* @param o
|
||||
* the bean or collection of beans to write
|
||||
* @param writer
|
||||
* used to write the json output to
|
||||
* Use the genericType to determine if this should be converted into a List or
|
||||
* bean.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public void toJsonWriter(Object o, Writer writer);
|
||||
public Object toObject(Type genericType, JsonParser jsonParser) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* With additional pretty output option.
|
||||
* Return the bean or collection as JSON string.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public void toJsonWriter(Object o, Writer writer, boolean pretty);
|
||||
public String toJson(Object value) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* With additional options to specify JsonValueAdapter and
|
||||
* JsonWriteBeanVisitor's.
|
||||
*
|
||||
* @param o
|
||||
* the bean or collection of beans to write
|
||||
* @param writer
|
||||
* used to write the json output to
|
||||
* @param options
|
||||
* additional options to control the JSON output
|
||||
* Write the bean or collection in JSON format to the writer.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options);
|
||||
public void toJson(Object value, Writer writer) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* With additional JSONP callback function.
|
||||
* Write the bean or collection to the JsonGenerator.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options,
|
||||
String callback);
|
||||
public void toJson(Object value, JsonGenerator generator) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Convert a bean or collection to json string using default options.
|
||||
* Return the bean or collection as JSON string using PathProperties.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public String toJsonString(Object o);
|
||||
public String toJson(Object value, PathProperties pathProperties) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Convert a bean or collection to json string with pretty format using
|
||||
* default options.
|
||||
* Write the bean or collection as json to the writer using the PathProperties.
|
||||
*/
|
||||
public String toJsonString(Object o, boolean pretty);
|
||||
public void toJson(Object value, Writer writer, PathProperties pathProperties) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Convert a bean or collection to json string using options.
|
||||
* Write the bean or collection to the JsonGenerator using the PathProperties.
|
||||
*/
|
||||
public String toJsonString(Object o, boolean pretty, JsonWriteOptions options);
|
||||
public void toJson(Object value, JsonGenerator generator, PathProperties pathProperties) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Convert a bean or collection to json string using a JSONP callback.
|
||||
* Deprecated in favour of using PathProperties by itself.
|
||||
* Write json to the JsonGenerator using the JsonWriteOptions.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public String toJsonString(Object o, boolean pretty, JsonWriteOptions options, String callback);
|
||||
public void toJson(Object value, JsonGenerator generator, JsonWriteOptions options) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Return true if the type is known as an Entity or Xml type or a List Set or
|
||||
* Map of known bean types.
|
||||
* Deprecated in favour of using PathProperties by itself.
|
||||
* With additional options.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
* @deprecated
|
||||
*/
|
||||
public void toJson(Object value, Writer writer, JsonWriteOptions options) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Deprecated in favour of using PathProperties by itself.
|
||||
* Convert a bean or collection to json string.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
* @deprecated
|
||||
*/
|
||||
public String toJson(Object value, JsonWriteOptions options) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Return true if the type is known as an Entity bean or a List Set or
|
||||
* Map of entity beans.
|
||||
*/
|
||||
public boolean isSupportedType(Type genericType);
|
||||
|
||||
/**
|
||||
* Create and return a new JsonGenerator for the given writer.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public JsonGenerator createGenerator(Writer writer) throws JsonIOException;
|
||||
|
||||
/**
|
||||
* Create and return a new JsonParser for the given reader.
|
||||
*
|
||||
* @throws JsonIOException When IOException occurs
|
||||
*/
|
||||
public JsonParser createParser(Reader reader) throws JsonIOException;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
/**
|
||||
* Marker interface for all the Raw JSON types.
|
||||
* <p>
|
||||
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
|
||||
* The JSON elements that are not mapped to a bean property are made available
|
||||
* to the JsonReadBeanVisitor.
|
||||
* </p>
|
||||
*
|
||||
* @see JsonReadBeanVisitor
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public interface JsonElement {
|
||||
|
||||
/**
|
||||
* Return true if this is a JSON primitive type (null, boolean, number or
|
||||
* string).
|
||||
*/
|
||||
public boolean isPrimitive();
|
||||
|
||||
/**
|
||||
* Return the string value of this primitive JSON element.
|
||||
* <p>
|
||||
* This can not be used for JsonElementObject or JsonElementArray.
|
||||
* </p>
|
||||
*/
|
||||
public String toPrimitiveString();
|
||||
|
||||
public Object eval(String exp);
|
||||
|
||||
public int evalInt(String exp);
|
||||
|
||||
public String evalString(String exp);
|
||||
|
||||
public boolean evalBoolean(String exp);
|
||||
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* JSON Array element.
|
||||
* <p>
|
||||
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
|
||||
* The JSON elements that are not mapped to a bean property are made available
|
||||
* to the JsonReadBeanVisitor.
|
||||
* </p>
|
||||
*
|
||||
* @see JsonReadBeanVisitor
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class JsonElementArray implements JsonElement {
|
||||
|
||||
private final List<JsonElement> values = new ArrayList<JsonElement>();
|
||||
|
||||
public List<JsonElement> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public void add(JsonElement value) {
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return values.toString();
|
||||
}
|
||||
|
||||
public boolean isPrimitive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String toPrimitiveString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String[] split(String exp) {
|
||||
int pos = exp.indexOf('.');
|
||||
if (pos == -1) {
|
||||
return new String[] { exp, null };
|
||||
}
|
||||
String exp0 = exp.substring(0, pos);
|
||||
String exp1 = exp.substring(pos + 1);
|
||||
return new String[] { exp0, exp1 };
|
||||
}
|
||||
|
||||
public Object eval(String exp) {
|
||||
String[] e = split(exp);
|
||||
return eval(e[0], e[1]);
|
||||
}
|
||||
|
||||
public int evalInt(String exp) {
|
||||
String[] e = split(exp);
|
||||
return evalInt(e[0], e[1]);
|
||||
}
|
||||
|
||||
public String evalString(String exp) {
|
||||
String[] e = split(exp);
|
||||
return evalString(e[0], e[1]);
|
||||
}
|
||||
|
||||
public boolean evalBoolean(String exp) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
private Object eval(String exp0, String exp1) {
|
||||
if ("size".equals(exp0)) {
|
||||
return values.size();
|
||||
}
|
||||
if ("isEmpty".equals(exp0)) {
|
||||
return values.isEmpty();
|
||||
}
|
||||
int idx = Integer.parseInt(exp0);
|
||||
JsonElement element = values.get(idx);
|
||||
return element.eval(exp1);
|
||||
}
|
||||
|
||||
private int evalInt(String exp0, String exp1) {
|
||||
if ("size".equals(exp0)) {
|
||||
return values.size();
|
||||
}
|
||||
if ("isEmpty".equals(exp0)) {
|
||||
return values.isEmpty() ? 1 : 0;
|
||||
}
|
||||
int idx = Integer.parseInt(exp0);
|
||||
JsonElement element = values.get(idx);
|
||||
return element.evalInt(exp1);
|
||||
}
|
||||
|
||||
private String evalString(String exp0, String exp1) {
|
||||
if ("size".equals(exp0)) {
|
||||
return String.valueOf(values.size());
|
||||
}
|
||||
if ("isEmpty".equals(exp0)) {
|
||||
return String.valueOf(values.isEmpty());
|
||||
}
|
||||
int idx = Integer.parseInt(exp0);
|
||||
JsonElement element = values.get(idx);
|
||||
return element.evalString(exp1);
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
/**
|
||||
* JSON boolean element.
|
||||
* <p>
|
||||
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
|
||||
* The JSON elements that are not mapped to a bean property are made available
|
||||
* to the JsonReadBeanVisitor.
|
||||
* </p>
|
||||
*
|
||||
* @see JsonReadBeanVisitor
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
|
||||
public class JsonElementBoolean implements JsonElement {
|
||||
|
||||
public static final JsonElementBoolean TRUE = new JsonElementBoolean(true);
|
||||
|
||||
public static final JsonElementBoolean FALSE = new JsonElementBoolean(false);
|
||||
|
||||
private final Boolean value;
|
||||
|
||||
private JsonElementBoolean(Boolean value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Boolean getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return Boolean.toString(value);
|
||||
}
|
||||
|
||||
public boolean isPrimitive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toPrimitiveString() {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
public Object eval(String exp) {
|
||||
if (exp != null) {
|
||||
throw new IllegalArgumentException("expression [" + exp + "] not allowed on boolean");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public int evalInt(String exp) {
|
||||
return value ? 1 : 0;
|
||||
}
|
||||
|
||||
public String evalString(String exp) {
|
||||
return toString();
|
||||
}
|
||||
|
||||
public boolean evalBoolean(String exp) {
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
/**
|
||||
* JSON null element.
|
||||
* <p>
|
||||
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
|
||||
* The JSON elements that are not mapped to a bean property are made available
|
||||
* to the JsonReadBeanVisitor.
|
||||
* </p>
|
||||
*
|
||||
* @see JsonReadBeanVisitor
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class JsonElementNull implements JsonElement {
|
||||
|
||||
public static final JsonElementNull NULL = new JsonElementNull();
|
||||
|
||||
private JsonElementNull() {
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return "null";
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "json null";
|
||||
}
|
||||
|
||||
public boolean isPrimitive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toPrimitiveString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object eval(String exp) {
|
||||
if (exp != null) {
|
||||
throw new IllegalArgumentException("expression [" + exp + "] not allowed on null");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int evalInt(String exp) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String evalString(String exp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean evalBoolean(String exp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
/**
|
||||
* JSON number element.
|
||||
* <p>
|
||||
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
|
||||
* The JSON elements that are not mapped to a bean property are made available
|
||||
* to the JsonReadBeanVisitor.
|
||||
* </p>
|
||||
*
|
||||
* @see JsonReadBeanVisitor
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class JsonElementNumber implements JsonElement {
|
||||
|
||||
private final String value;
|
||||
|
||||
public JsonElementNumber(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean isPrimitive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toPrimitiveString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Object eval(String exp) {
|
||||
if (exp != null) {
|
||||
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
|
||||
}
|
||||
return Double.parseDouble(value);
|
||||
}
|
||||
|
||||
public int evalInt(String exp) {
|
||||
if (exp != null) {
|
||||
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
|
||||
}
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public String evalString(String exp) {
|
||||
if (exp != null) {
|
||||
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean evalBoolean(String exp) {
|
||||
if (exp != null) {
|
||||
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
|
||||
}
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* JSON Object element.
|
||||
* <p>
|
||||
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
|
||||
* The JSON elements that are not mapped to a bean property are made available
|
||||
* to the JsonReadBeanVisitor.
|
||||
* </p>
|
||||
*
|
||||
* @see JsonReadBeanVisitor
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class JsonElementObject implements JsonElement {
|
||||
|
||||
private final Map<String, JsonElement> map = new LinkedHashMap<String, JsonElement>();
|
||||
|
||||
public void put(String key, JsonElement value) {
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
private String[] split(String exp) {
|
||||
int pos = exp.indexOf('.');
|
||||
if (pos == -1) {
|
||||
return new String[] { exp, null };
|
||||
}
|
||||
String exp0 = exp.substring(0, pos);
|
||||
String exp1 = exp.substring(pos + 1);
|
||||
return new String[] { exp0, exp1 };
|
||||
}
|
||||
|
||||
public Object eval(String exp) {
|
||||
String[] e = split(exp);
|
||||
return eval(e[0], e[1]);
|
||||
}
|
||||
|
||||
public int evalInt(String exp) {
|
||||
String[] e = split(exp);
|
||||
return evalInt(e[0], e[1]);
|
||||
}
|
||||
|
||||
public String evalString(String exp) {
|
||||
if (exp == null) {
|
||||
return map.toString();
|
||||
}
|
||||
String[] e = split(exp);
|
||||
return evalString(e[0], e[1]);
|
||||
}
|
||||
|
||||
public boolean evalBoolean(String exp) {
|
||||
String[] e = split(exp);
|
||||
return evalBoolean(e[0], e[1]);
|
||||
}
|
||||
|
||||
private Object eval(String exp0, String exp1) {
|
||||
JsonElement e = map.get(exp0);
|
||||
return e == null ? null : e.eval(exp1);
|
||||
}
|
||||
|
||||
private int evalInt(String exp0, String exp1) {
|
||||
JsonElement e = map.get(exp0);
|
||||
return e == null ? 0 : e.evalInt(exp1);
|
||||
}
|
||||
|
||||
private String evalString(String exp0, String exp1) {
|
||||
JsonElement e = map.get(exp0);
|
||||
return e == null ? "" : e.evalString(exp1);
|
||||
}
|
||||
|
||||
private boolean evalBoolean(String exp0, String exp1) {
|
||||
JsonElement e = map.get(exp0);
|
||||
return e == null ? false : e.evalBoolean(exp1);
|
||||
}
|
||||
|
||||
public JsonElement get(String key) {
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
public JsonElement getValue(String key) {
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
public Set<String> keySet() {
|
||||
return map.keySet();
|
||||
}
|
||||
|
||||
public Set<Map.Entry<String, JsonElement>> entrySet() {
|
||||
return map.entrySet();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return map.toString();
|
||||
}
|
||||
|
||||
public boolean isPrimitive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String toPrimitiveString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
/**
|
||||
* JSON string element.
|
||||
* <p>
|
||||
* You will only use the JsonElements when you register a JsonReadBeanVisitor.
|
||||
* The JSON elements that are not mapped to a bean property are made available
|
||||
* to the JsonReadBeanVisitor.
|
||||
* </p>
|
||||
*
|
||||
* @see JsonReadBeanVisitor
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class JsonElementString implements JsonElement {
|
||||
|
||||
private final String value;
|
||||
|
||||
public JsonElementString(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean isPrimitive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toPrimitiveString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Object eval(String exp) {
|
||||
if (exp != null) {
|
||||
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public int evalInt(String exp) {
|
||||
if (exp != null) {
|
||||
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public String evalString(String exp) {
|
||||
if (exp != null) {
|
||||
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean evalBoolean(String exp) {
|
||||
if (exp != null) {
|
||||
throw new IllegalArgumentException("expression [" + exp + "] not allowed on number");
|
||||
}
|
||||
return Boolean.parseBoolean(exp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
/**
|
||||
* Unchecked exception thrown when an IOException occurs in json processing.
|
||||
* <p>
|
||||
* Typically wraps the checked IOException.
|
||||
* </p>
|
||||
*/
|
||||
public class JsonIOException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Construct with an underlying cause.
|
||||
*/
|
||||
public JsonIOException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct with a message.
|
||||
*/
|
||||
public JsonIOException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Provides for some custom handling of json content as it is read.
|
||||
* <p>
|
||||
* This visit method is called after all the known properties of the bean have
|
||||
* been processed. Any JSON elements that could not be mapped to known bean
|
||||
* properties are available in the unmapped Map.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*
|
||||
* @param <T>
|
||||
* The type of entity bean
|
||||
*/
|
||||
public interface JsonReadBeanVisitor<T> {
|
||||
|
||||
/**
|
||||
* Visit the bean that has just been processed.
|
||||
* <p>
|
||||
* This provides a method of customising the bean and processing any custom
|
||||
* JSON content.
|
||||
* </p>
|
||||
*
|
||||
* @param bean
|
||||
* the bean being processed
|
||||
* @param unmapped
|
||||
* Map of any JSON elements that didn't map to known bean properties
|
||||
*/
|
||||
public void visit(T bean, Map<String, JsonElement> unmapped);
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Provides the ability to customise the reading of JSON content.
|
||||
* <p>
|
||||
* You can optionally provide a custom JsonValueAdapter to handle specific
|
||||
* formatting for Date and DateTime types.
|
||||
* </p>
|
||||
* <p>
|
||||
* You can optionally register JsonReadBeanVisitors to customise the processing
|
||||
* of the beans as they are processed and handle any custom JSON elements that
|
||||
* could not be mapped to bean properties.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*
|
||||
*/
|
||||
public class JsonReadOptions {
|
||||
|
||||
protected JsonValueAdapter valueAdapter;
|
||||
|
||||
protected Map<String, JsonReadBeanVisitor<?>> visitorMap;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public JsonReadOptions() {
|
||||
this.visitorMap = new LinkedHashMap<String, JsonReadBeanVisitor<?>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JsonValueAdapter.
|
||||
*/
|
||||
public JsonValueAdapter getValueAdapter() {
|
||||
return valueAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the map of JsonReadBeanVisitor's.
|
||||
*/
|
||||
public Map<String, JsonReadBeanVisitor<?>> getVisitorMap() {
|
||||
return visitorMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a JsonValueAdapter for custom DateTime and Date formatting.
|
||||
*/
|
||||
public JsonReadOptions setValueAdapter(JsonValueAdapter valueAdapter) {
|
||||
this.valueAdapter = valueAdapter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a JsonReadBeanVisitor for the root level.
|
||||
*/
|
||||
public JsonReadOptions addRootVisitor(JsonReadBeanVisitor<?> visitor) {
|
||||
return addVisitor(null, visitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a JsonReadBeanVisitor for a given path.
|
||||
*/
|
||||
public JsonReadOptions addVisitor(String path, JsonReadBeanVisitor<?> visitor) {
|
||||
visitorMap.put(path, visitor);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009 Authors
|
||||
*
|
||||
* This file is part of Ebean.
|
||||
*
|
||||
* Ebean is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Ebean is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package com.avaje.ebean.text.json;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/**
|
||||
* Allows you to customise the Date and Timestamp formats.
|
||||
* <p>
|
||||
* There is not a standard JSON format for Date or Timestamp types. By default
|
||||
* Ebean uses ISO8601 "yyyy-MM-dd'T'HH:mm:ss.SSSZ" and "yyyy-MM-dd".
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that Ebean will convert Joda types to either of the Date or Timestamp
|
||||
* types and back for you.
|
||||
* </p>
|
||||
*
|
||||
* @see JsonReadOptions
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public interface JsonValueAdapter {
|
||||
|
||||
/**
|
||||
* Convert the Date to json string.
|
||||
*/
|
||||
public String jsonFromDate(java.sql.Date date);
|
||||
|
||||
/**
|
||||
* Convert the DateTime to json string.
|
||||
*/
|
||||
public String jsonFromTimestamp(java.sql.Timestamp date);
|
||||
|
||||
/**
|
||||
* Parse the JSON string into a Date.
|
||||
*/
|
||||
public java.sql.Date jsonToDate(String jsonDate);
|
||||
|
||||
/**
|
||||
* Parse the JSON DateTime into a Timestamp.
|
||||
*/
|
||||
public Timestamp jsonToTimestamp(String jsonDateTime);
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user