mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
157
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2e5619f70 | ||
|
|
18e0ff7067 | ||
|
|
4119c02f0d | ||
|
|
8985d37aed | ||
|
|
23e035894f | ||
|
|
32b8fd0e26 | ||
|
|
4971db54c8 | ||
|
|
e417b3cde2 | ||
|
|
d23ca31e42 | ||
|
|
c9b71ae373 | ||
|
|
f06f21cf5e | ||
|
|
f8b97ff397 | ||
|
|
fc032c632b | ||
|
|
54534ea7f3 | ||
|
|
812dcbb4bb | ||
|
|
a3d4b57b94 | ||
|
|
61479d365a | ||
|
|
3ba9a52b0e | ||
|
|
518ba37fb2 | ||
|
|
fd6a7f64eb | ||
|
|
8b073ef10a | ||
|
|
6a7555055d | ||
|
|
eb31f1bfce | ||
|
|
2e4f99318a | ||
|
|
409c1d06b2 | ||
|
|
01f2881940 | ||
|
|
aef512a851 | ||
|
|
435e1f61fe | ||
|
|
0230e3e253 | ||
|
|
770364fd1a | ||
|
|
fbc2d70967 | ||
|
|
276e865f72 | ||
|
|
9ded3890d7 | ||
|
|
bfeb0cd667 | ||
|
|
119049b696 | ||
|
|
33ff113585 | ||
|
|
17e0953cad | ||
|
|
e3ca5d2419 | ||
|
|
a5f435dbcf | ||
|
|
aa1e021501 | ||
|
|
7ea6f16207 | ||
|
|
197852857d | ||
|
|
e7e6e00473 | ||
|
|
933c791d33 | ||
|
|
043c523c2a | ||
|
|
82247fd4e4 | ||
|
|
27f86548a6 | ||
|
|
7e8424328f | ||
|
|
923828ce87 | ||
|
|
e195f20981 | ||
|
|
86d0216159 | ||
|
|
a4698ea1b6 | ||
|
|
888384f7db | ||
|
|
817c946eeb | ||
|
|
1749c2cb4c | ||
|
|
3afd3c4530 | ||
|
|
e5f98f1b7a | ||
|
|
2e496c9e1e | ||
|
|
5975694538 | ||
|
|
03a42f7f6e | ||
|
|
84f1e05eb4 | ||
|
|
da20ced1ed | ||
|
|
1f1a6e2c04 | ||
|
|
6b83800a83 | ||
|
|
c344551e4d | ||
|
|
6b4aff7191 | ||
|
|
fc7fae35c9 | ||
|
|
7929d8c0b8 | ||
|
|
058766dd92 | ||
|
|
4fdb8a77b4 | ||
|
|
56be1fc047 | ||
|
|
192a946cf6 | ||
|
|
aa7b15fd6e | ||
|
|
cb74ef01e0 | ||
|
|
829dfcb013 | ||
|
|
25b6d2a5a0 | ||
|
|
14fbc3a955 | ||
|
|
e41da2f995 | ||
|
|
ea60e42a4d | ||
|
|
efce6b57e1 | ||
|
|
988e4c7386 | ||
|
|
9e1d7ab722 | ||
|
|
0c03a8b925 | ||
|
|
37a66f1ee2 | ||
|
|
a02673359d | ||
|
|
f9d13cbba7 | ||
|
|
f787483e30 | ||
|
|
9e33f36808 | ||
|
|
6a7e6caa66 | ||
|
|
5a4a0e3886 | ||
|
|
c3f586c461 | ||
|
|
99f7243d6c | ||
|
|
7026c00290 | ||
|
|
448d94a335 | ||
|
|
961a7ef7ea | ||
|
|
5cef1ad017 | ||
|
|
b814ca6e81 | ||
|
|
a24e6b6064 | ||
|
|
b751823abb | ||
|
|
f3cb7e2cfd | ||
|
|
31d7c6fed8 | ||
|
|
c5fb1dbe23 | ||
|
|
b7770a9617 | ||
|
|
41abf34ef6 | ||
|
|
89beaee0f6 | ||
|
|
5052afbd07 | ||
|
|
d89a58482c | ||
|
|
eb1bca805e | ||
|
|
9cb66b112f | ||
|
|
210901ee2d | ||
|
|
396fb49acd | ||
|
|
a83896f1ff | ||
|
|
3483372ae3 | ||
|
|
4e1f817ade | ||
|
|
d4139aa8ef | ||
|
|
a6cfed7c44 | ||
|
|
cc17ea8911 | ||
|
|
141f0a8b16 | ||
|
|
9dbd903a73 | ||
|
|
b8105190d1 | ||
|
|
ef3d911e19 | ||
|
|
31fd0b7ed5 | ||
|
|
26d1288757 | ||
|
|
1d49e5b341 | ||
|
|
4a0806e453 | ||
|
|
ab48f796c6 | ||
|
|
0cb8bb0415 | ||
|
|
471691c747 | ||
|
|
c86e6b3cf9 | ||
|
|
b399713a85 | ||
|
|
f8d4ad7f33 | ||
|
|
7984a9ccd1 | ||
|
|
27cda09523 | ||
|
|
20fc23c19c | ||
|
|
bac53a5d75 | ||
|
|
2e0690efa9 | ||
|
|
fdd9b1905c | ||
|
|
fc966618a6 | ||
|
|
669ba7837a | ||
|
|
0edf90056c | ||
|
|
6b27a9d280 | ||
|
|
2b0669c2ae | ||
|
|
9fc10e8e04 | ||
|
|
fb5eb0cdd2 | ||
|
|
3351882915 | ||
|
|
31c6d16e73 | ||
|
|
cd8c12a9a8 | ||
|
|
3207ae1f08 | ||
|
|
5fe532c393 | ||
|
|
93b9cbf9ac | ||
|
|
8a34d6050b | ||
|
|
ab3e6ccbf7 | ||
|
|
cc58fc8195 | ||
|
|
1c34c09f3c | ||
|
|
8544712448 | ||
|
|
5e22079366 | ||
|
|
4d93b70bf9 |
@@ -1,8 +1,17 @@
|
||||
[](https://travis-ci.org/ebean-orm/ebean)
|
||||
[](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean)
|
||||
|
||||
# Sponsors
|
||||
[1]: https://www.foconis.de/
|
||||
[2]: https://www.foconis.de/templates/yootheme/cache/foconis_logo_322-709da1de.png
|
||||
|
||||
[![Foconis.de][2]][1]
|
||||
|
||||
# Need help?
|
||||
Post questions or issues to the Ebean google group - https://groups.google.com/forum/#!forum/ebean
|
||||
|
||||
# Documentation
|
||||
Goto [https://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Website")
|
||||
Goto [https://ebean.io/docs/](https://ebean.io/docs/)
|
||||
|
||||
|
||||
## Maven cental links:
|
||||
@@ -10,8 +19,7 @@ Goto [https://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Web
|
||||
|
||||
[Maven central - all related projects](http://search.maven.org/#search%7Cga%7C1%7Cebean "maven central all related projects")
|
||||
|
||||
## Current versions
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean) - ebean
|
||||
## Other versions
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent) - ebean-agent
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin) - ebean-maven-plugin
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>11.31.2</version>
|
||||
<version>11.36.4</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ebean</name>
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-11.31.2</tag>
|
||||
<tag>ebean-11.36.4</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -111,19 +111,25 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>persistence-api</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<version>2.2.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-annotation</artifactId>
|
||||
<version>4.4</version>
|
||||
<version>4.7</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-types</artifactId>
|
||||
<version>1.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-datasource</artifactId>
|
||||
<version>4.3.2</version>
|
||||
<version>4.5.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -135,13 +141,13 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-migration</artifactId>
|
||||
<version>11.13.1</version>
|
||||
<version>11.14.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-runtime</artifactId>
|
||||
<version>4.7.1</version>
|
||||
<version>4.7.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Jackson core used internally by Ebean -->
|
||||
@@ -188,6 +194,21 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Provided scope to compile with java 11 -->
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jaxb</groupId>
|
||||
<artifactId>jaxb-runtime</artifactId>
|
||||
<version>2.4.0-b180830.0438</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
<version>1.3.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Provided scope for Postgres JSON/JSONB support -->
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
@@ -222,7 +243,7 @@
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-agentloader</artifactId>
|
||||
<version>2.1.2</version>
|
||||
<version>3.0.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -259,7 +280,7 @@
|
||||
<dependency>
|
||||
<groupId>com.microsoft.sqlserver</groupId>
|
||||
<artifactId>mssql-jdbc</artifactId>
|
||||
<version>6.4.0.jre8</version>
|
||||
<version>7.2.0.jre8</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -309,7 +330,7 @@
|
||||
<plugin>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-maven-plugin</artifactId>
|
||||
<version>11.27.1</version>
|
||||
<version>11.35.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
@@ -327,9 +348,10 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.5</version>
|
||||
<version>2.22.1</version>
|
||||
<configuration>
|
||||
<useSystemClassLoader>false</useSystemClassLoader>
|
||||
<trimStackTrace>false</trimStackTrace>
|
||||
<failIfNoTests>false</failIfNoTests>
|
||||
<includes>
|
||||
<include>**/Test*.java</include>
|
||||
@@ -371,7 +393,7 @@
|
||||
<overview>src/main/java/io/ebean/overview.html</overview>
|
||||
<source>1.8</source>
|
||||
<doclet>org.avaje.doclet.PygmentsDoclet</doclet>
|
||||
<excludePackageNames>io.ebeaninternal.*:com.avaje.ebean.util</excludePackageNames>
|
||||
<excludePackageNames>io.ebeaninternal.*:io.ebeanservice:io.ebean.common:io.ebean.bean:io.ebean.service:io.ebean.metric:io.ebean.util:io.ebean.config.properties:io.ebean.config.dbplatform*</excludePackageNames>
|
||||
<docletArtifact>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>pygments-doclet</artifactId>
|
||||
|
||||
@@ -15,8 +15,8 @@ import java.util.Optional;
|
||||
* public class CustomerFinder extends BeanFinder<Long,Customer> {
|
||||
*
|
||||
* @Inject
|
||||
* public CustomerFinder(EbeanServer server) {
|
||||
* super(Customer.class, server);
|
||||
* public CustomerFinder(Database database) {
|
||||
* super(Customer.class, database);
|
||||
* }
|
||||
*
|
||||
* // ... add customer specific finders
|
||||
@@ -29,25 +29,25 @@ import java.util.Optional;
|
||||
*/
|
||||
public abstract class BeanFinder<I,T> {
|
||||
|
||||
protected final EbeanServer server;
|
||||
protected final Database server;
|
||||
|
||||
protected final Class<T> type;
|
||||
|
||||
/**
|
||||
* Create with the given bean type and EbeanServer instance.
|
||||
* Create with the given bean type and Database instance.
|
||||
*
|
||||
* @param type The bean type
|
||||
* @param server The EbeanServer instance typically created via Spring factory or equivalent.
|
||||
* @param server The Database instance typically created via Spring factory or equivalent.
|
||||
*/
|
||||
protected BeanFinder(Class<T> type, EbeanServer server) {
|
||||
protected BeanFinder(Class<T> type, Database server) {
|
||||
this.type = type;
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the EbeanServer to use.
|
||||
* Return the Database to use.
|
||||
*/
|
||||
public EbeanServer db() {
|
||||
public Database db() {
|
||||
return server;
|
||||
}
|
||||
|
||||
@@ -66,21 +66,20 @@ public abstract class BeanFinder<I,T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return typically a different EbeanServer to the default.
|
||||
* Return typically a different Database to the default.
|
||||
* <p>
|
||||
* This is equivalent to {@link Ebean#getServer(String)}
|
||||
* This is equivalent to {@link DB#byName(String)}
|
||||
*
|
||||
* @param server The name of the EbeanServer. If this is null then the default EbeanServer is
|
||||
* returned.
|
||||
* @param server The name of the Database. If this is null then the default Database is returned.
|
||||
*/
|
||||
public EbeanServer db(String server) {
|
||||
return Ebean.getServer(server);
|
||||
public Database db(String server) {
|
||||
return DB.byName(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an entity reference for this ID.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#getReference(Class, Object)}
|
||||
* Equivalent to {@link Database#getReference(Class, Object)}
|
||||
*/
|
||||
@Nonnull
|
||||
public T ref(I id) {
|
||||
@@ -89,8 +88,6 @@ public abstract class BeanFinder<I,T> {
|
||||
|
||||
/**
|
||||
* Retrieves an entity by ID.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#find(Class, Object)}
|
||||
*/
|
||||
@Nullable
|
||||
public T findById(I id) {
|
||||
@@ -107,8 +104,6 @@ public abstract class BeanFinder<I,T> {
|
||||
|
||||
/**
|
||||
* Delete a bean by Id.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#delete(Class, Object)}
|
||||
*/
|
||||
public void deleteById(I id) {
|
||||
db().delete(type, id);
|
||||
@@ -138,7 +133,7 @@ public abstract class BeanFinder<I,T> {
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#update(Class)}
|
||||
* Equivalent to {@link Database#update(Class)}
|
||||
*/
|
||||
protected UpdateQuery<T> updateQuery() {
|
||||
return db().update(type);
|
||||
@@ -147,7 +142,7 @@ public abstract class BeanFinder<I,T> {
|
||||
/**
|
||||
* Creates a query.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#find(Class)}
|
||||
* Equivalent to {@link Database#find(Class)}
|
||||
*/
|
||||
protected Query<T> query() {
|
||||
return db().find(type);
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.util.Collection;
|
||||
* public class CustomerRepository extends BeanRepository<Long,Customer> {
|
||||
*
|
||||
* @Inject
|
||||
* public CustomerRepository(EbeanServer server) {
|
||||
* public CustomerRepository(Database server) {
|
||||
* super(Customer.class, server);
|
||||
* }
|
||||
*
|
||||
@@ -34,23 +34,23 @@ import java.util.Collection;
|
||||
public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
|
||||
|
||||
/**
|
||||
* Create with the given bean type and EbeanServer instance.
|
||||
* Create with the given bean type and Database instance.
|
||||
* <p>
|
||||
* Typically users would extend BeanRepository rather than BeanFinder.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* @Inject
|
||||
* public CustomerRepository(EbeanServer server) {
|
||||
* public CustomerRepository(Database server) {
|
||||
* super(Customer.class, server);
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param type The bean type
|
||||
* @param server The EbeanServer instance typically created via Spring factory or equivalent
|
||||
* @param server The Database instance typically created via Spring factory or equivalent
|
||||
*/
|
||||
protected BeanRepository(Class<T> type, EbeanServer server) {
|
||||
protected BeanRepository(Class<T> type, Database server) {
|
||||
super(type, server);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see EbeanServer#markAsDirty(Object)
|
||||
* @see Database#markAsDirty(Object)
|
||||
*/
|
||||
public void markAsDirty(T bean) {
|
||||
db().markAsDirty(bean);
|
||||
@@ -111,7 +111,7 @@ public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
|
||||
* 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)
|
||||
* @see Database#save(Object)
|
||||
*/
|
||||
public void save(T bean) {
|
||||
db().save(bean);
|
||||
@@ -127,7 +127,7 @@ public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
|
||||
/**
|
||||
* Update this entity.
|
||||
*
|
||||
* @see EbeanServer#update(Object)
|
||||
* @see Database#update(Object)
|
||||
*/
|
||||
public void update(T bean) {
|
||||
db().update(bean);
|
||||
@@ -136,7 +136,7 @@ public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
|
||||
/**
|
||||
* Insert this entity.
|
||||
*
|
||||
* @see EbeanServer#insert(Object)
|
||||
* @see Database#insert(Object)
|
||||
*/
|
||||
public void insert(T bean) {
|
||||
db().insert(bean);
|
||||
@@ -157,7 +157,7 @@ public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
|
||||
* deleted. Note that, if JDBC batch mode is used then this always returns true.
|
||||
* </p>
|
||||
*
|
||||
* @see EbeanServer#delete(Object)
|
||||
* @see Database#delete(Object)
|
||||
*/
|
||||
public boolean delete(T bean) {
|
||||
return db().delete(bean);
|
||||
@@ -177,7 +177,7 @@ public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
|
||||
* want to perform a hard/permanent delete.
|
||||
* </p>
|
||||
*
|
||||
* @see EbeanServer#deletePermanent(Object)
|
||||
* @see Database#deletePermanent(Object)
|
||||
*/
|
||||
public boolean deletePermanent(T bean) {
|
||||
return db().deletePermanent(bean);
|
||||
@@ -189,7 +189,7 @@ public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
|
||||
* Ebean will detect if this is a new bean or a previously fetched bean and perform either an
|
||||
* insert or an update based on that.
|
||||
*
|
||||
* @see EbeanServer#merge(Object)
|
||||
* @see Database#merge(Object)
|
||||
*/
|
||||
public void merge(T bean) {
|
||||
db().merge(bean);
|
||||
@@ -201,7 +201,7 @@ public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
|
||||
* Ebean will detect if this is a new bean or a previously fetched bean and perform either an
|
||||
* insert or an update based on that.
|
||||
*
|
||||
* @see EbeanServer#merge(Object, MergeOptions)
|
||||
* @see Database#merge(Object, MergeOptions)
|
||||
*/
|
||||
public void merge(T bean, MergeOptions options) {
|
||||
db().merge(bean, options);
|
||||
@@ -210,7 +210,7 @@ public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
|
||||
/**
|
||||
* Refreshes this entity from the database.
|
||||
*
|
||||
* @see EbeanServer#refresh(Object)
|
||||
* @see Database#refresh(Object)
|
||||
*/
|
||||
public void refresh(T bean) {
|
||||
db().refresh(bean);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebean;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -56,7 +57,7 @@ public interface BeanState {
|
||||
*
|
||||
* // set loaded state on the email property to false so that
|
||||
* // the email property is not included in a stateless update
|
||||
* Ebean.getBeanState(user).setPropertyLoaded("email", false);
|
||||
* DB.getBeanState(user).setPropertyLoaded("email", false);
|
||||
*
|
||||
* user.update();
|
||||
*
|
||||
@@ -101,7 +102,7 @@ public interface BeanState {
|
||||
/**
|
||||
* 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
|
||||
* {@link Database#createEntityBean(Class)}, then populate its properties
|
||||
* and then call this method specifying which properties where loaded or null
|
||||
* for a fully loaded entity bean.
|
||||
*/
|
||||
@@ -111,4 +112,10 @@ public interface BeanState {
|
||||
* Reset the bean putting it into NEW state such that a save() results in an insert.
|
||||
*/
|
||||
void resetForInsert();
|
||||
|
||||
/**
|
||||
* Returns a map with load erros.
|
||||
*/
|
||||
@Nullable
|
||||
Map<String, Exception> getLoadErrors();
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@ import java.sql.SQLException;
|
||||
*
|
||||
* String sql = "{call sp_order_mod(?,?)}";
|
||||
*
|
||||
* CallableSql cs = Ebean.createCallableSql(sql);
|
||||
* CallableSql cs = DB.createCallableSql(sql);
|
||||
* cs.setParameter(1, "turbo");
|
||||
* cs.registerOut(2, Types.INTEGER);
|
||||
*
|
||||
* Ebean.execute(cs);
|
||||
* DB.execute(cs);
|
||||
*
|
||||
* // read the out parameter
|
||||
* Integer returnValue = (Integer) cs.getObject(2);
|
||||
@@ -38,7 +38,7 @@ import java.sql.SQLException;
|
||||
*
|
||||
* String sql = "{call sp_insert_order(?,?)}";
|
||||
*
|
||||
* CallableSql cs = Ebean.createCallableSql(sql);
|
||||
* CallableSql cs = DB.createCallableSql(sql);
|
||||
*
|
||||
* // Inform Ebean this stored procedure inserts into the
|
||||
* // oe_order table and inserts + updates the oe_order_detail table.
|
||||
@@ -46,35 +46,33 @@ import java.sql.SQLException;
|
||||
* cs.addModification("oe_order", true, false, false);
|
||||
* cs.addModification("oe_order_detail", true, true, false);
|
||||
*
|
||||
* Transaction t = Ebean.startTransaction();
|
||||
*
|
||||
* // execute using JDBC batching 10 statements at a time
|
||||
* t.setBatchMode(true);
|
||||
* t.setBatchSize(10);
|
||||
* try {
|
||||
* try (Transaction t = DB.beginTransaction()) {
|
||||
*
|
||||
* // execute using JDBC batching 10 statements at a time
|
||||
* t.setBatchMode(true);
|
||||
* t.setBatchSize(10);
|
||||
*
|
||||
* cs.setParameter(1, "Was");
|
||||
* cs.setParameter(2, "Banana");
|
||||
* Ebean.execute(cs);
|
||||
* DB.execute(cs);
|
||||
*
|
||||
* cs.setParameter(1, "Here");
|
||||
* cs.setParameter(2, "Kumera");
|
||||
* Ebean.execute(cs);
|
||||
* DB.execute(cs);
|
||||
*
|
||||
* cs.setParameter(1, "More");
|
||||
* cs.setParameter(2, "Apple");
|
||||
* Ebean.execute(cs);
|
||||
* DB.execute(cs);
|
||||
*
|
||||
* // Ebean.externalModification("oe_order",true,false,false);
|
||||
* // Ebean.externalModification("oe_order_detail",true,true,false);
|
||||
* Ebean.commitTransaction();
|
||||
* // DB.externalModification("oe_order",true,false,false);
|
||||
* // DB.externalModification("oe_order_detail",true,true,false);
|
||||
* t.commit();
|
||||
*
|
||||
* } finally {
|
||||
* Ebean.endTransaction();
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @see SqlUpdate
|
||||
* @see Ebean#execute(CallableSql)
|
||||
*/
|
||||
public interface CallableSql {
|
||||
|
||||
@@ -173,7 +171,7 @@ public interface CallableSql {
|
||||
* Add table modification information to the TransactionEvent.
|
||||
* <p>
|
||||
* This would be similar to using the
|
||||
* <code>Ebean.externalModification()</code> method. It may be easier and make
|
||||
* <code>DB.externalModification()</code> method. It may be easier and make
|
||||
* more sense to set it here with the CallableSql.
|
||||
* </p>
|
||||
* <p>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,67 @@
|
||||
package io.ebean;
|
||||
|
||||
import io.ebean.config.ContainerConfig;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
|
||||
/**
|
||||
* Creates Database instances.
|
||||
* <p>
|
||||
* This uses either DatabaseConfig or properties in the application.properties file to
|
||||
* configure and create a Database instance.
|
||||
* </p>
|
||||
* <p>
|
||||
* The Database instance can either be registered with the DB singleton or
|
||||
* not. The DB singleton effectively holds a map of Database by a name.
|
||||
* If the Database is registered with the DB singleton you can retrieve it
|
||||
* later via {@link DB#byName(String)}.
|
||||
* </p>
|
||||
* <p>
|
||||
* One Database can be nominated as the 'default/primary' Database. Many
|
||||
* methods on the DB singleton such as {@link DB#find(Class)} are just a
|
||||
* convenient way of using the 'default/primary' Database.
|
||||
* </p>
|
||||
*/
|
||||
public class DatabaseFactory {
|
||||
|
||||
/**
|
||||
* Initialise the container with clustering configuration.
|
||||
* <p>
|
||||
* Call this prior to creating any Database instances or alternatively set the
|
||||
* ContainerConfig on the ServerConfig when creating the first Database instance.
|
||||
*/
|
||||
public static synchronized void initialiseContainer(ContainerConfig containerConfig) {
|
||||
EbeanServerFactory.initialiseContainer(containerConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create using ebean.properties to configure the server.
|
||||
*/
|
||||
public static synchronized Database create(String name) {
|
||||
return EbeanServerFactory.create(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create using the ServerConfig object to configure the server.
|
||||
*/
|
||||
public static synchronized Database create(DatabaseConfig config) {
|
||||
return EbeanServerFactory.create(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create using the ServerConfig additionally specifying a classLoader to use as the context class loader.
|
||||
*/
|
||||
public static synchronized Database createWithContextClassLoader(DatabaseConfig config, ClassLoader classLoader) {
|
||||
return EbeanServerFactory.createWithContextClassLoader(config, classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown gracefully all Database instances cleaning up any resources as required.
|
||||
* <p>
|
||||
* This is typically invoked via JVM shutdown hook and not explicitly called.
|
||||
* </p>
|
||||
*/
|
||||
public static synchronized void shutdown() {
|
||||
EbeanServerFactory.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,7 +30,7 @@ import java.util.function.Predicate;
|
||||
* String sql = "select id, name from customer where name like :name and status_code = :status";
|
||||
*
|
||||
* List<CustomerDto> beans =
|
||||
* Ebean.findDto(CustomerDto.class, sql)
|
||||
* DB.findDto(CustomerDto.class, sql)
|
||||
* .setParameter("name", "Acme%")
|
||||
* .setParameter("status", "ACTIVE")
|
||||
* .findList();
|
||||
|
||||
@@ -24,113 +24,26 @@ import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* This Ebean object is effectively a singleton that holds a map of registered
|
||||
* {@link EbeanServer}s. It additionally provides a convenient way to use the
|
||||
* 'default' EbeanServer.
|
||||
* Ebean is a registry of {@link Database} by name. Ebean has now been renamed to {@link DB}.
|
||||
* <p>
|
||||
* If you are using a Dependency Injection framework such as
|
||||
* <strong>Spring</strong> or <strong>Guice</strong> you will probably
|
||||
* <strong>NOT</strong> use this Ebean singleton object. Instead you will
|
||||
* configure and construct EbeanServer instances using {@link ServerConfig} and
|
||||
* {@link EbeanServerFactory} and inject those EbeanServer instances into your
|
||||
* data access objects.
|
||||
* </p>
|
||||
* Ebean is effectively this is an alias for {@link DB} which is the new and improved name for Ebean.
|
||||
* <p>
|
||||
* In documentation "Ebean singleton" refers to this object.
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>There is one EbeanServer per Database (javax.sql.DataSource).</li>
|
||||
* <li>EbeanServers can be 'registered' with the Ebean singleton (put into its
|
||||
* map). Registered EbeanServer's can later be retrieved via
|
||||
* {@link #getServer(String)}.</li>
|
||||
* <li>One EbeanServer can be referred to as the 'default' EbeanServer. For
|
||||
* convenience, the Ebean singleton (this object) provides methods such as
|
||||
* {@link #find(Class)} that proxy through to the 'default' EbeanServer. This
|
||||
* can be useful for applications that use a single database.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* For developer convenience Ebean has static methods that proxy through to the
|
||||
* methods on the <em>'default'</em> EbeanServer. These methods are provided for
|
||||
* developers who are mostly using a single database. Many developers will be
|
||||
* able to use the methods on Ebean rather than get a EbeanServer.
|
||||
* </p>
|
||||
* <p>
|
||||
* EbeanServers can be created and used without ever needing or using the Ebean
|
||||
* singleton. Refer to {@link ServerConfig#setRegister(boolean)}.
|
||||
* </p>
|
||||
* <p>
|
||||
* You can either programmatically create/register EbeanServers via
|
||||
* {@link EbeanServerFactory} or they can automatically be created and
|
||||
* registered when you first use the Ebean singleton. When EbeanServers are
|
||||
* created automatically they are configured using information in the
|
||||
* ebean.properties file.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch shipped orders (and also their customer)
|
||||
* List<Order> list = Ebean.find(Order.class)
|
||||
* .fetch("customer")
|
||||
* .where()
|
||||
* .eq("status.code", Order.Status.SHIPPED)
|
||||
* .findList();
|
||||
*
|
||||
* // read/use the order list ...
|
||||
* for (Order order : list) {
|
||||
* Customer customer = order.getCustomer();
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch order 10, modify and save
|
||||
* Order order = Ebean.find(Order.class, 10);
|
||||
*
|
||||
* OrderStatus shipped = Ebean.getReference(OrderStatus.class,"SHIPPED");
|
||||
* order.setStatus(shipped);
|
||||
* order.setShippedDate(shippedDate);
|
||||
* ...
|
||||
*
|
||||
* // implicitly creates a transaction and commits
|
||||
* Ebean.save(order);
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* When you have multiple databases and need access to a specific one the
|
||||
* {@link #getServer(String)} method provides access to the EbeanServer for that
|
||||
* specific database.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Get access to the Human Resources EbeanServer/Database
|
||||
* EbeanServer hrDb = Ebean.getServer("hr");
|
||||
*
|
||||
* // fetch contact 3 from the HR database
|
||||
* Contact contact = hrDb.find(Contact.class, 3);
|
||||
*
|
||||
* contact.setName("I'm going to change");
|
||||
* ...
|
||||
*
|
||||
* // save the contact back to the HR database
|
||||
* hrDb.save(contact);
|
||||
*
|
||||
* }</pre>
|
||||
* The preference is to use DB and Database rather than Ebean and EbeanServer.
|
||||
*/
|
||||
public final class Ebean {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Ebean.class);
|
||||
|
||||
static {
|
||||
EbeanVersion.getVersion(); // initalizes the version class and logs the version.
|
||||
EbeanVersion.getVersion(); // initialises the version class and logs the version.
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages creation and cache of EbeanServers.
|
||||
* Manages creation and cache of Databases.
|
||||
*/
|
||||
private static final Ebean.ServerManager serverMgr = new Ebean.ServerManager();
|
||||
|
||||
/**
|
||||
* Helper class for managing fast and safe access and creation of
|
||||
* EbeanServers.
|
||||
* Helper class for managing fast and safe access and creation of Databases.
|
||||
*/
|
||||
private static final class ServerManager {
|
||||
|
||||
@@ -147,7 +60,7 @@ public final class Ebean {
|
||||
private final Object monitor = new Object();
|
||||
|
||||
/**
|
||||
* The 'default' EbeanServer.
|
||||
* The 'default' Database.
|
||||
*/
|
||||
private EbeanServer defaultServer;
|
||||
|
||||
@@ -166,20 +79,20 @@ public final class Ebean {
|
||||
throw e;
|
||||
|
||||
} catch (DataSourceConfigurationException e) {
|
||||
String msg = "Configuration error creating DataSource for the default EbeanServer." +
|
||||
String msg = "Configuration error creating DataSource for the default Database." +
|
||||
" This typically means a missing application-test.yaml or missing ebean-test-config dependency." +
|
||||
" See https://ebean.io/docs/trouble-shooting#datasource";
|
||||
throw new DataSourceConfigurationException(msg, e);
|
||||
|
||||
} catch (Throwable e) {
|
||||
logger.error("Error trying to create the default EbeanServer", e);
|
||||
logger.error("Error trying to create the default Database", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private EbeanServer getDefaultServer() {
|
||||
if (defaultServer == null) {
|
||||
String msg = "The default EbeanServer has not been defined?";
|
||||
String msg = "The default Database has not been defined?";
|
||||
msg += " This is normally set via the ebean.datasource.default property.";
|
||||
msg += " Otherwise it should be registered programmatically via registerServer()";
|
||||
throw new PersistenceException(msg);
|
||||
@@ -201,7 +114,7 @@ public final class Ebean {
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronized read, create and put of EbeanServers.
|
||||
* Synchronized read, create and put of Databases.
|
||||
*/
|
||||
private EbeanServer getWithCreate(String name) {
|
||||
|
||||
@@ -240,7 +153,7 @@ public final class Ebean {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the EbeanServer for a given DataSource. If name is null this will
|
||||
* Get the Database for a given DataSource. If name is null this will
|
||||
* return the 'default' EbeanServer.
|
||||
* <p>
|
||||
* This is provided to access EbeanServer for databases other than the
|
||||
@@ -631,7 +544,8 @@ public final class Ebean {
|
||||
* Customer customer = new Customer();
|
||||
* customer.setId(7);
|
||||
* customer.setName("ModifiedNameNoOCC");
|
||||
* ebeanServer.update(customer);
|
||||
*
|
||||
* DB.update(customer);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
@@ -1131,10 +1045,9 @@ public final class Ebean {
|
||||
*
|
||||
* String sql = "select c.id, c.name from customer c where c.name like ? order by c.name";
|
||||
*
|
||||
* Query<Customer> query = ebeanServer.findNative(Customer.class, sql);
|
||||
* query.setParameter(1, "Rob%");
|
||||
*
|
||||
* List<Customer> customers = query.findList();
|
||||
* List<Customer> customers = DB.findNative(Customer.class, sql)
|
||||
* .setParameter(1, "Rob%")
|
||||
* .findList()
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,8 +22,8 @@ package io.ebean;
|
||||
* example.setName("Rob%");
|
||||
* example.setNotes("%something%");
|
||||
*
|
||||
* List<Customer> list =
|
||||
* Ebean.find(Customer.class)
|
||||
* List<Customer> list =
|
||||
* DB.find(Customer.class)
|
||||
* .where()
|
||||
* // pass the bean into the where() clause
|
||||
* .exampleLike(example)
|
||||
@@ -46,7 +46,7 @@ package io.ebean;
|
||||
* .includeZeros();
|
||||
*
|
||||
* List<Customer> list =
|
||||
* Ebean.find(Customer.class)
|
||||
* DB.find(Customer.class)
|
||||
* .where()
|
||||
* .add(qbe)
|
||||
* .findList();
|
||||
|
||||
@@ -13,27 +13,15 @@ import java.util.Map;
|
||||
* {@link Query#where()}.
|
||||
* </p>
|
||||
* <p>
|
||||
* This provides a convenient way to create expressions for the 'Default'
|
||||
* server. It is actually a short cut for using the ExpressionFactory of the
|
||||
* 'default' EbeanServer.
|
||||
* This provides a convenient way to create expressions for the default
|
||||
* database.
|
||||
* <p>
|
||||
* See also {@link Ebean#getExpressionFactory()}
|
||||
* See also {@link DB#getExpressionFactory()}
|
||||
* </p>
|
||||
* <p>
|
||||
* Creates standard common expressions for using in a Query Where or Having
|
||||
* clause.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Example: Using an Expr.or() method
|
||||
* Query<Order> query = Ebean.createQuery(Order.class);
|
||||
* query.where(
|
||||
* Expr.or(Expr.eq("status", Order.NEW),
|
||||
* Expr.gt("orderDate", lastWeek));
|
||||
*
|
||||
* List<Order> list = query.findList();
|
||||
* ...
|
||||
* }</pre>
|
||||
*
|
||||
* @see Query#where()
|
||||
*/
|
||||
@@ -64,11 +52,20 @@ public class Expr {
|
||||
return Ebean.getExpressionFactory().ieq(propertyName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* In Range - property >= value1 and property < value2.
|
||||
* <p>
|
||||
* Unlike Between inRange is "half open" and usually more useful for use with dates or timestamps.
|
||||
* </p>
|
||||
*/
|
||||
public static Expression inRange(String propertyName, Object value1, Object value2) {
|
||||
return Ebean.getExpressionFactory().inRange(propertyName, value1, value2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Between - property between the two given values.
|
||||
*/
|
||||
public static Expression between(String propertyName, Object value1, Object value2) {
|
||||
|
||||
return Ebean.getExpressionFactory().between(propertyName, value1, value2);
|
||||
}
|
||||
|
||||
@@ -76,7 +73,6 @@ public class Expr {
|
||||
* Between - value between two given properties.
|
||||
*/
|
||||
public static Expression between(String lowProperty, String highProperty, Object value) {
|
||||
|
||||
return Ebean.getExpressionFactory().betweenProperties(lowProperty, highProperty, value);
|
||||
}
|
||||
|
||||
@@ -242,6 +238,43 @@ public class Expr {
|
||||
return Ebean.getExpressionFactory().in(propertyName, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* In where null or empty values means that no predicate is added to the query.
|
||||
* <p>
|
||||
* That is, only add the IN predicate if the values are not null or empty.
|
||||
* <p>
|
||||
* Without this we typically need to code an <code>if</code> block to only add
|
||||
* the IN predicate if the collection is not empty like:
|
||||
* </p>
|
||||
*
|
||||
* <h3>Without inOrEmpty()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where() // add some predicates
|
||||
* .eq("status", Status.NEW);
|
||||
*
|
||||
* if (ids != null && !ids.isEmpty()) {
|
||||
* query.where().in("customer.id", ids);
|
||||
* }
|
||||
*
|
||||
* query.findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Using inOrEmpty()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where()
|
||||
* .eq("status", Status.NEW)
|
||||
* .inOrEmpty("customer.id", ids)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public static Expression inOrEmpty(String propertyName, Collection<?> values) {
|
||||
return Ebean.getExpressionFactory().inOrEmpty(propertyName, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Id Equal to - ID property is equal to the value.
|
||||
*/
|
||||
|
||||
@@ -30,9 +30,9 @@ import java.util.Map;
|
||||
* Expr.or(Expr.eq("status", Order.Status.NEW),
|
||||
* Expr.gt("orderDate", lastWeek));
|
||||
*
|
||||
* Query<Order> query = Ebean.createQuery(Order.class);
|
||||
* query.where().add(newOrLastWeek);
|
||||
* List<Order> list = query.findList();
|
||||
* List<Order> list = DB.find(Order.class)
|
||||
* .where().add(newOrLastWeek)
|
||||
* .findList();
|
||||
* ...
|
||||
* }</pre>
|
||||
*
|
||||
@@ -122,6 +122,11 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression eq(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Equal To or Null - property equal to the given value or null.
|
||||
*/
|
||||
Expression eqOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Not Equal To - property not equal to the given value.
|
||||
*/
|
||||
@@ -149,6 +154,33 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression ineObject(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* In Range - property >= value1 and property < value2.
|
||||
* <p>
|
||||
* Unlike Between inRange is "half open" and usually more useful for use with dates or timestamps.
|
||||
* </p>
|
||||
*/
|
||||
Expression inRange(String propertyName, Object value1, Object value2);
|
||||
|
||||
/**
|
||||
* Value in Range between 2 properties.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* .startDate.inRangeWith(endDate, now)
|
||||
*
|
||||
* // which equates to
|
||||
* startDate <= now and (endDate > now or endDate is null)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* This is a convenience expression combining a number of simple expressions.
|
||||
* The most common use of this could be called "effective dating" where 2 date or
|
||||
* timestamp columns represent the date range in which
|
||||
*/
|
||||
Expression inRangeWith(String lowProperty, String highProperty, Object value);
|
||||
|
||||
/**
|
||||
* Between - property between the two given values.
|
||||
*/
|
||||
@@ -159,6 +191,14 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression betweenProperties(String lowProperty, String highProperty, Object value);
|
||||
|
||||
/**
|
||||
* Greater Than Or Null - property greater than the given value or null.
|
||||
* <p>
|
||||
* A convenient expression combining GT and Is Null. Most often useful for range
|
||||
* expressions where the top range value is nullable.
|
||||
*/
|
||||
Expression gtOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Greater Than - property greater than the given value.
|
||||
*/
|
||||
@@ -170,6 +210,14 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression ge(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Less Than or Null - property less than the given value or null.
|
||||
* <p>
|
||||
* A convenient expression combining LT and Is Null. Most often useful for range
|
||||
* expressions where the bottom range value is nullable.
|
||||
*/
|
||||
Expression ltOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Less Than - property less than the given value.
|
||||
*/
|
||||
@@ -277,6 +325,41 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression in(String propertyName, Collection<?> values);
|
||||
|
||||
/**
|
||||
* In where null or empty values means that no predicate is added to the query.
|
||||
* <p>
|
||||
* That is, only add the IN predicate if the values are not null or empty.
|
||||
* <p>
|
||||
* Without this we typically need to code an <code>if</code> block to only add
|
||||
* the IN predicate if the collection is not empty like:
|
||||
* </p>
|
||||
*
|
||||
* <h3>Without inOrEmpty()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where() // add some predicates
|
||||
* .eq("status", Status.NEW);
|
||||
*
|
||||
* if (ids != null && !ids.isEmpty()) {
|
||||
* query.where().in("customer.id", ids);
|
||||
* }
|
||||
*
|
||||
* query.findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Using inOrEmpty()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where()
|
||||
* .eq("status", Status.NEW)
|
||||
* .inOrEmpty("customer.id", ids)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
Expression inOrEmpty(String propertyName, Collection<?> values);
|
||||
|
||||
/**
|
||||
* Not In - property has a value in the array of values.
|
||||
*/
|
||||
|
||||
@@ -226,6 +226,32 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
int update(Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query returning true if a row is found.
|
||||
* <p>
|
||||
* The query is executed using max rows of 1 and will only select the id property.
|
||||
* This method is really just a convenient way to optimise a query to perform a
|
||||
* 'does a row exist in the db' check.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Example:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists = query().where().eq("email", "rob@foo.com").exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h2>Example using a query bean:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists = new QContact().email.equalTo("rob@foo.com").exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return True if the query finds a matching row in the database
|
||||
*/
|
||||
boolean exists();
|
||||
|
||||
/**
|
||||
* Execute the query iterating over the results.
|
||||
*
|
||||
@@ -290,23 +316,23 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Execute the query returning a list of values for a single property.
|
||||
* <p>
|
||||
*
|
||||
* <h3>Example 1:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<String> names =
|
||||
* Ebean.find(Customer.class)
|
||||
* DB.find(Customer.class)
|
||||
* .select("name")
|
||||
* .orderBy().asc("name")
|
||||
* .findSingleAttributeList();
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
*
|
||||
* <h3>Example 2:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<String> names =
|
||||
* Ebean.find(Customer.class)
|
||||
* DB.find(Customer.class)
|
||||
* .setDistinct(true)
|
||||
* .select("name")
|
||||
* .where().eq("status", Customer.Status.NEW)
|
||||
@@ -323,11 +349,10 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Execute a query returning a single value of a single property/column.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* String name =
|
||||
* Ebean.find(Customer.class)
|
||||
* DB.find(Customer.class)
|
||||
* .select("name")
|
||||
* .where().eq("id", 42)
|
||||
* .findSingleAttribute();
|
||||
@@ -408,10 +433,9 @@ public interface ExpressionList<T> {
|
||||
* If maxRows is not set on the query prior to calling findPagedList() then a
|
||||
* PersistenceException is thrown.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* PagedList<Order> pagedList = Ebean.find(Order.class)
|
||||
* PagedList<Order> pagedList = DB.find(Order.class)
|
||||
* .setFirstRow(50)
|
||||
* .setMaxRows(20)
|
||||
* .findPagedList();
|
||||
@@ -478,7 +502,7 @@ public interface ExpressionList<T> {
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* Ebean.find(Customer.class)
|
||||
* DB.find(Customer.class)
|
||||
* .setDistinct(true)
|
||||
* .select("name") // only select the customer name
|
||||
* .findList();
|
||||
@@ -547,12 +571,11 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Extended version for setDistinct in conjunction with "findSingleAttributeList";
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<CountedValue<Order.Status>> orderStatusCount =
|
||||
*
|
||||
* Ebean.find(Order.class)
|
||||
* DB.find(Order.class)
|
||||
* .select("status")
|
||||
* .where()
|
||||
* .gt("orderDate", LocalDate.now().minusMonths(3))
|
||||
@@ -657,7 +680,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Equal to expression for the value at the given path in the JSON document.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonEqualTo("content", "path.other", 34)
|
||||
@@ -672,7 +694,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Not Equal to - for the given path in a JSON document.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonNotEqualTo("content", "path.other", 34)
|
||||
@@ -687,7 +708,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Greater than - for the given path in a JSON document.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonGreaterThan("content", "path.other", 34)
|
||||
@@ -698,7 +718,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Greater than or equal to - for the given path in a JSON document.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonGreaterOrEqual("content", "path.other", 34)
|
||||
@@ -709,7 +728,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Less than - for the given path in a JSON document.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonLessThan("content", "path.other", 34)
|
||||
@@ -720,7 +738,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Less than or equal to - for the given path in a JSON document.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonLessOrEqualTo("content", "path.other", 34)
|
||||
@@ -731,7 +748,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Between - for the given path in a JSON document.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().jsonBetween("content", "orderDate", lowerDateTime, upperDateTime)
|
||||
@@ -742,21 +758,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Add an Expression to the list.
|
||||
* <p>
|
||||
* This returns the list so that add() can be chained.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Query<Customer> query = Ebean.find(Customer.class);
|
||||
* query.where()
|
||||
* .like("name","Rob%")
|
||||
* .eq("status", Customer.ACTIVE);
|
||||
*
|
||||
* List<Customer> list = query.findList();
|
||||
* ...
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> add(Expression expr);
|
||||
|
||||
@@ -770,6 +771,11 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> eq(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Equal To or Null - property is equal to a given value or null.
|
||||
*/
|
||||
ExpressionList<T> eqOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Not Equal To - property not equal to the given value.
|
||||
*/
|
||||
@@ -788,8 +794,35 @@ public interface ExpressionList<T> {
|
||||
ExpressionList<T> ine(String propertyName, String value);
|
||||
|
||||
/**
|
||||
* Between - property between the two given values.
|
||||
* Value in Range between 2 properties.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* .startDate.inRangeWith(endDate, now)
|
||||
*
|
||||
* // which equates to
|
||||
* startDate <= now and (endDate > now or endDate is null)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* This is a convenience expression combining a number of simple expressions.
|
||||
* The most common use of this could be called "effective dating" where 2 date or
|
||||
* timestamp columns represent the date range in which
|
||||
*/
|
||||
ExpressionList<T> inRangeWith(String lowProperty, String highProperty, Object value);
|
||||
|
||||
/**
|
||||
* In Range - property >= value1 and property < value2.
|
||||
* <p>
|
||||
* Unlike Between inRange is "half open" and usually more useful for use with dates or timestamps.
|
||||
* </p>
|
||||
*/
|
||||
ExpressionList<T> inRange(String propertyName, Object value1, Object value2);
|
||||
|
||||
/**
|
||||
* Between - property between the two given values.
|
||||
*/
|
||||
ExpressionList<T> between(String propertyName, Object value1, Object value2);
|
||||
|
||||
/**
|
||||
@@ -802,6 +835,11 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> gt(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Greater Than or Null - property greater than the given value or null.
|
||||
*/
|
||||
ExpressionList<T> gtOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Greater Than or Equal to - property greater than or equal to the given
|
||||
* value.
|
||||
@@ -813,6 +851,11 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> lt(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Less Than or Null - property less than the given value or null.
|
||||
*/
|
||||
ExpressionList<T> ltOrNull(String propertyName, Object value);
|
||||
|
||||
/**
|
||||
* Less Than or Equal to - property less than or equal to the given value.
|
||||
*/
|
||||
@@ -842,7 +885,6 @@ public interface ExpressionList<T> {
|
||||
* To get control over the options you can create an ExampleExpression and set
|
||||
* those options such as case insensitive etc.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // create an example bean and set the properties
|
||||
@@ -851,16 +893,15 @@ public interface ExpressionList<T> {
|
||||
* example.setName("Rob%");
|
||||
* example.setNotes("%something%");
|
||||
*
|
||||
* List<Customer> list = Ebean.find(Customer.class).where()
|
||||
* // pass the bean into the where() clause
|
||||
* .exampleLike(example)
|
||||
* // you can add other expressions to the same query
|
||||
* .gt("id", 2).findList();
|
||||
* List<Customer> list =
|
||||
* DB.find(Customer.class)
|
||||
* .where().exampleLike(example)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* Similarly you can create an ExampleExpression
|
||||
* <p>
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Customer example = new Customer();
|
||||
@@ -870,7 +911,7 @@ public interface ExpressionList<T> {
|
||||
* // create a ExampleExpression with more control
|
||||
* ExampleExpression qbe = new ExampleExpression(example, true, LikeType.EQUAL_TO).includeZeros();
|
||||
*
|
||||
* List<Customer> list = Ebean.find(Customer.class).where().add(qbe).findList();
|
||||
* List<Customer> list = DB.find(Customer.class).where().add(qbe).findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@@ -947,6 +988,41 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
ExpressionList<T> in(String propertyName, Collection<?> values);
|
||||
|
||||
/**
|
||||
* In where null or empty values means that no predicate is added to the query.
|
||||
* <p>
|
||||
* That is, only add the IN predicate if the values are not null or empty.
|
||||
* <p>
|
||||
* Without this we typically need to code an <code>if</code> block to only add
|
||||
* the IN predicate if the collection is not empty like:
|
||||
* </p>
|
||||
*
|
||||
* <h3>Without inOrEmpty()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where() // add some predicates
|
||||
* .eq("status", Status.NEW);
|
||||
*
|
||||
* if (ids != null && !ids.isEmpty()) {
|
||||
* query.where().in("customer.id", ids);
|
||||
* }
|
||||
*
|
||||
* query.findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Using inOrEmpty()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where()
|
||||
* .eq("status", Status.NEW)
|
||||
* .inOrEmpty("customer.id", ids)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> inOrEmpty(String propertyName, Collection<?> values);
|
||||
|
||||
/**
|
||||
* In - using a subQuery.
|
||||
* <p>
|
||||
@@ -1082,7 +1158,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Add expression for ALL of the given bit flags to be set.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().bitwiseAll("flags", BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
|
||||
@@ -1096,7 +1171,6 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Add expression for the given bit flags to be NOT set.
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* where().bitwiseNot("flags", BwFlags.HAS_COLOUR)
|
||||
@@ -1147,6 +1221,13 @@ public interface ExpressionList<T> {
|
||||
* raw("add_days(orderDate, 10) < ?", someDate)
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h4>Subquery example:</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* .raw("t0.customer_id in (select customer_id from customer_group where group_id = any(?::uuid[]))", groupIds)
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> raw(String raw, Object value);
|
||||
|
||||
@@ -1171,15 +1252,83 @@ public interface ExpressionList<T> {
|
||||
* then they are not translated. logical property name names (not fully
|
||||
* qualified) will still be translated to their physical name.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* raw("orderQty < shipQty")
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h4>Subquery example:</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* .raw("t0.customer_id in (select customer_id from customer_group where group_id = any(?::uuid[]))", groupIds)
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ExpressionList<T> raw(String raw);
|
||||
|
||||
/**
|
||||
* Only add the raw expression if the values is not null or empty.
|
||||
* <p>
|
||||
* This is a pure convenience expression to make it nicer to deal with the pattern where we use
|
||||
* raw() expression with a subquery and only want to add the subquery predicate when the collection
|
||||
* of values is not empty.
|
||||
* </p>
|
||||
* <h3>Without inOrEmpty()</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where() // add some predicates
|
||||
* .eq("status", Status.NEW);
|
||||
*
|
||||
* // common pattern - we can use rawOrEmpty() instead
|
||||
* if (orderIds != null && !orderIds.isEmpty()) {
|
||||
* query.where().raw("t0.customer_id in (select o.customer_id from orders o where o.id in (?1))", orderIds);
|
||||
* }
|
||||
*
|
||||
* query.findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Using rawOrEmpty()</h3>
|
||||
* Note that in the example below we use the <code>?1</code> bind parameter to get "parameter expansion"
|
||||
* for each element in the collection.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where()
|
||||
* .eq("status", Status.NEW)
|
||||
* // only add the expression if orderIds is not empty
|
||||
* .rawOrEmpty("t0.customer_id in (select o.customer_id from orders o where o.id in (?1))", orderIds);
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Postgres ANY</h3>
|
||||
* With Postgres we would often use the SQL <code>ANY</code> expression and array parameter binding
|
||||
* rather than <code>IN</code>.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* query.where()
|
||||
* .eq("status", Status.NEW)
|
||||
* .rawOrEmpty("t0.customer_id in (select o.customer_id from orders o where o.id = any(?))", orderIds);
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* Note that we need to cast the Postgres array for UUID types like:
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* " ... = any(?::uuid[])"
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param raw The raw expression that is typically a subquery
|
||||
* @param values The values which is typically a list or set of id values.
|
||||
*/
|
||||
ExpressionList<T> rawOrEmpty(String raw, Collection<?> values);
|
||||
|
||||
/**
|
||||
* Add a match expression.
|
||||
*
|
||||
@@ -1250,12 +1399,10 @@ public interface ExpressionList<T> {
|
||||
* typically you only explicitly need to use the and() junction
|
||||
* when it is nested inside an or() or not() junction.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Example: Nested and()
|
||||
*
|
||||
* Ebean.find(Customer.class)
|
||||
* .where()
|
||||
* .or()
|
||||
* .and() // nested and
|
||||
@@ -1276,16 +1423,27 @@ public interface ExpressionList<T> {
|
||||
* Return a list of expressions that will be joined by OR's.
|
||||
* This is exactly the same as disjunction();
|
||||
* <p>
|
||||
* <p>
|
||||
* Use endOr() or endJunction() to end the OR junction.
|
||||
* </p>
|
||||
* <p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Example: (status active OR anniversary is null)
|
||||
*
|
||||
* .where()
|
||||
* .or()
|
||||
* .eq("status", Customer.Status.ACTIVE)
|
||||
* .isNull("anniversary")
|
||||
* .orderBy().asc("name")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Example: Use or() to join
|
||||
* // two nested and() expressions
|
||||
*
|
||||
* Ebean.find(Customer.class)
|
||||
* .where()
|
||||
* .or()
|
||||
* .and()
|
||||
@@ -1309,8 +1467,8 @@ public interface ExpressionList<T> {
|
||||
* Use endNot() or endJunction() to end expressions being added to the
|
||||
* NOT expression list.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>@{code
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* .where()
|
||||
* .not()
|
||||
@@ -1319,12 +1477,11 @@ public interface ExpressionList<T> {
|
||||
* .endNot()
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* <pre>@{code
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Example: nested not()
|
||||
*
|
||||
* Ebean.find(Customer.class)
|
||||
* .where()
|
||||
* .eq("status", Customer.Status.ACTIVE)
|
||||
* .not()
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package io.ebean;
|
||||
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.NonUniqueResultException;
|
||||
@@ -12,7 +15,7 @@ import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* The extended API for EbeanServer.
|
||||
* The extended API for Database.
|
||||
* <p>
|
||||
* This provides the finder methods that take an explicit transaction rather than obtaining
|
||||
* the transaction from the usual mechanism (which is ThreadLocal based).
|
||||
@@ -23,7 +26,7 @@ import java.util.function.Predicate;
|
||||
* the transaction to use.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that in all cases the transaction supplied can be null and in this case the EbeanServer
|
||||
* Note that in all cases the transaction supplied can be null and in this case the Database
|
||||
* will use the normal mechanism to obtain the transaction to use.
|
||||
* </p>
|
||||
*/
|
||||
@@ -42,6 +45,32 @@ public interface ExtendedServer {
|
||||
*/
|
||||
void setClock(Clock clock);
|
||||
|
||||
/**
|
||||
* Execute the query returning true if a row is found.
|
||||
* <p>
|
||||
* The query is executed using max rows of 1 and will only select the id property.
|
||||
* This method is really just a convenient way to optimise a query to perform a
|
||||
* 'does a row exist in the db' check.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Example:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists = query().where().eq("email", "rob@foo.com").exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h2>Example using a query bean:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists = new QContact().email.equalTo("rob@foo.com").exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return True if the query finds a matching row in the database
|
||||
*/
|
||||
<T> boolean exists(SpiQuery<?> ormQuery, SpiTransaction transaction);
|
||||
|
||||
/**
|
||||
* Return the number of 'top level' or 'root' entities this query should return.
|
||||
*
|
||||
@@ -92,7 +121,7 @@ public interface ExtendedServer {
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* ebeanServer.find(Order.class)
|
||||
* DB.find(Order.class)
|
||||
* .where().eq("status", Order.Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .findEach((Order order) -> {
|
||||
@@ -126,7 +155,7 @@ public interface ExtendedServer {
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* ebeanServer.find(Order.class)
|
||||
* DB.find(Order.class)
|
||||
* .where().eq("status", Order.Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .findEachWhile((Order order) -> {
|
||||
@@ -165,8 +194,7 @@ public interface ExtendedServer {
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* List<Customer> customers = DB.find(Customer.class)
|
||||
* .where().ilike("name", "rob%")
|
||||
* .findList();
|
||||
*
|
||||
@@ -243,7 +271,7 @@ public interface ExtendedServer {
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* PagedList<Order> pagedList = Ebean.find(Order.class)
|
||||
* PagedList<Order> pagedList = DB.find(Order.class)
|
||||
* .setFirstRow(50)
|
||||
* .setMaxRows(20)
|
||||
* .findPagedList();
|
||||
@@ -272,8 +300,7 @@ public interface ExtendedServer {
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Set<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* Set<Customer> customers = DB.find(Customer.class)
|
||||
* .where().ilike("name", "rob%")
|
||||
* .findSet();
|
||||
*
|
||||
@@ -312,7 +339,7 @@ public interface ExtendedServer {
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<String> names =
|
||||
* Ebean.find(Customer.class)
|
||||
* DB.find(Customer.class)
|
||||
* .select("name")
|
||||
* .orderBy().asc("name")
|
||||
* .findSingleAttributeList();
|
||||
@@ -322,7 +349,7 @@ public interface ExtendedServer {
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<String> names =
|
||||
* Ebean.find(Customer.class)
|
||||
* DB.find(Customer.class)
|
||||
* .setDistinct(true)
|
||||
* .select("name")
|
||||
* .where().eq("status", Customer.Status.NEW)
|
||||
@@ -384,7 +411,7 @@ public interface ExtendedServer {
|
||||
/**
|
||||
* Execute the update query returning the number of rows updated.
|
||||
* <p>
|
||||
* The update query must be created using {@link EbeanServer#update(Class)}.
|
||||
* The update query must be created using {@link Database#update(Class)}.
|
||||
* </p>
|
||||
*
|
||||
* @param query the update query to execute
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.io.Serializable;
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
* // Normal fetch join results in a single SQL query
|
||||
* List<Order> list = Ebean.find(Order.class).fetch("details").findList();
|
||||
* List<Order> list = DB.find(Order.class).fetch("details").findList();
|
||||
*
|
||||
* // Find Orders join details using a single SQL query
|
||||
* }</pre>
|
||||
@@ -36,7 +36,7 @@ import java.io.Serializable;
|
||||
*
|
||||
* // This will use 2 SQL queries to build this object graph
|
||||
* List<Order> list =
|
||||
* Ebean.find(Order.class)
|
||||
* DB.find(Order.class)
|
||||
* .fetch("details", new FetchConfig().query())
|
||||
* .findList();
|
||||
*
|
||||
@@ -52,7 +52,7 @@ import java.io.Serializable;
|
||||
*
|
||||
* // This will use 3 SQL queries to build this object graph
|
||||
* List<Order> list =
|
||||
* Ebean.find(Order.class)
|
||||
* DB.find(Order.class)
|
||||
* .fetch("details", new FetchConfig().query())
|
||||
* .fetch("customer", new FetchConfig().queryFirst(5))
|
||||
* .findList();
|
||||
@@ -70,7 +70,7 @@ import java.io.Serializable;
|
||||
* <pre>{@code
|
||||
* // This will use 3 SQL queries to build this object graph
|
||||
* List<Order> list =
|
||||
* Ebean.find(Order.class)
|
||||
* DB.find(Order.class)
|
||||
* .select("status, shipDate")
|
||||
* .fetch("details", "quantity, price", new FetchConfig().query())
|
||||
* .fetch("details.product", "sku, name")
|
||||
@@ -100,7 +100,7 @@ import java.io.Serializable;
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Order> list =
|
||||
* Ebean.find(Order.class)
|
||||
* DB.find(Order.class)
|
||||
* .fetch("customer", new FetchConfig().query(10).lazy(5))
|
||||
* .findList();
|
||||
*
|
||||
@@ -121,7 +121,7 @@ import java.io.Serializable;
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Order> list = Ebean.find(Order.class)
|
||||
* List<Order> list = DB.find(Order.class)
|
||||
* .fetch("customer","name", new FetchConfig().lazy(5))
|
||||
* .fetch("customer.contacts","contactName, phone, email")
|
||||
* .fetch("customer.shippingAddress")
|
||||
|
||||
@@ -25,14 +25,14 @@ import java.util.Set;
|
||||
* // get a list of entities (query execution statistics in this case)
|
||||
*
|
||||
* List<MetaQueryStatistic> list =
|
||||
* Ebean.find(MetaQueryStatistic.class).findList();
|
||||
* DB.find(MetaQueryStatistic.class).findList();
|
||||
*
|
||||
* long nowMinus24Hrs = System.currentTimeMillis() - 24 * (1000 * 60 * 60);
|
||||
*
|
||||
* // sort and filter the list returning a filtered list...
|
||||
*
|
||||
* List<MetaQueryStatistic> filteredList =
|
||||
* Ebean.filter(MetaQueryStatistic.class)
|
||||
* DB.filter(MetaQueryStatistic.class)
|
||||
* .sort("avgTimeMicros desc")
|
||||
* .gt("executionCount", 0)
|
||||
* .gt("lastQueryTime", nowMinus24Hrs)
|
||||
@@ -63,12 +63,12 @@ import java.util.Set;
|
||||
* // get a list of entities (query execution statistics)
|
||||
*
|
||||
* List<Order> orders =
|
||||
* Ebean.find(Order.class).findList();
|
||||
* DB.find(Order.class).findList();
|
||||
*
|
||||
* // Apply a filter...
|
||||
*
|
||||
* List<Order> filteredOrders =
|
||||
* Ebean.filter(Order.class)
|
||||
* DB.filter(Order.class)
|
||||
* .startsWith("customer.name", "Rob")
|
||||
* .eq("customer.shippingAddress.city", "Auckland")
|
||||
* .filter(orders);
|
||||
|
||||
@@ -2,7 +2,6 @@ package io.ebean;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -14,8 +13,7 @@ import java.util.List;
|
||||
* </p>
|
||||
* <h3>Testing</h3>
|
||||
* <p>
|
||||
* For testing the mocki-ebean project has the ability to replace the finder implementation
|
||||
* <p>
|
||||
* For testing the mocki-ebean project has the ability to replace the finder implementation.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
@@ -25,7 +23,7 @@ import java.util.List;
|
||||
* super(Customer.class);
|
||||
* }
|
||||
*
|
||||
* // Add your customer finder methods ...
|
||||
* // Add finder methods ...
|
||||
*
|
||||
* public Customer byName(String name) {
|
||||
* return query().eq("name", name).findOne();
|
||||
@@ -46,6 +44,15 @@ import java.util.List;
|
||||
* ...
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* When the Finder is registered as a field on Customer it can then be used like:
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Customer rob = Customer.find.byName("Rob");
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*/
|
||||
public class Finder<I, T> {
|
||||
|
||||
@@ -55,9 +62,9 @@ public class Finder<I, T> {
|
||||
private final Class<T> type;
|
||||
|
||||
/**
|
||||
* The name of the EbeanServer, null for the default server.
|
||||
* The name of the database this finder will use, null for the default database.
|
||||
*/
|
||||
private final String serverName;
|
||||
private final String _$dbName;
|
||||
|
||||
/**
|
||||
* Create with the type of the entity bean.
|
||||
@@ -82,15 +89,15 @@ public class Finder<I, T> {
|
||||
*/
|
||||
public Finder(Class<T> type) {
|
||||
this.type = type;
|
||||
this.serverName = null;
|
||||
this._$dbName = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create with the type of the entity bean and specific server name.
|
||||
* Create with the type of the entity bean and specific database name.
|
||||
*/
|
||||
public Finder(Class<T> type, String serverName) {
|
||||
public Finder(Class<T> type, String databaseName) {
|
||||
this.type = type;
|
||||
this.serverName = serverName;
|
||||
this._$dbName = databaseName;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,30 +115,27 @@ public class Finder<I, T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying 'default' EbeanServer.
|
||||
* <p>
|
||||
* This provides full access to the API such as explicit transaction demarcation etc.
|
||||
* Return the Database this finder will use.
|
||||
*/
|
||||
public EbeanServer db() {
|
||||
return Ebean.getServer(serverName);
|
||||
public Database db() {
|
||||
return DB.byName(_$dbName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return typically a different EbeanServer to the default.
|
||||
* Return typically a different Database to the default.
|
||||
* <p>
|
||||
* This is equivalent to {@link Ebean#getServer(String)}
|
||||
* This is equivalent to {@link DB#byName(String)}
|
||||
*
|
||||
* @param server The name of the EbeanServer. If this is null then the default EbeanServer is
|
||||
* returned.
|
||||
* @param databaseName The name of the Database. If this is null then the default database is returned.
|
||||
*/
|
||||
public EbeanServer db(String server) {
|
||||
return Ebean.getServer(server);
|
||||
public Database db(String databaseName) {
|
||||
return DB.byName(databaseName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an entity reference for this ID.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#getReference(Class, Object)}
|
||||
* Equivalent to {@link Database#getReference(Class, Object)}
|
||||
*/
|
||||
@Nonnull
|
||||
public T ref(I id) {
|
||||
@@ -141,7 +145,7 @@ public class Finder<I, T> {
|
||||
/**
|
||||
* Retrieves an entity by ID.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#find(Class, Object)}
|
||||
* Equivalent to {@link Database#find(Class, Object)}
|
||||
*/
|
||||
@Nullable
|
||||
public T byId(I id) {
|
||||
@@ -151,7 +155,7 @@ public class Finder<I, T> {
|
||||
/**
|
||||
* Delete a bean by Id.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#delete(Class, Object)}
|
||||
* Equivalent to {@link Database#delete(Class, Object)}
|
||||
*/
|
||||
public void deleteById(I id) {
|
||||
db().delete(type, id);
|
||||
@@ -181,7 +185,7 @@ public class Finder<I, T> {
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#update(Class)}
|
||||
* Equivalent to {@link Database#update(Class)}
|
||||
*/
|
||||
public UpdateQuery<T> update() {
|
||||
return db().update(type);
|
||||
@@ -190,7 +194,7 @@ public class Finder<I, T> {
|
||||
/**
|
||||
* Creates a query.
|
||||
* <p>
|
||||
* Equivalent to {@link EbeanServer#find(Class)}
|
||||
* Equivalent to {@link Database#find(Class)}
|
||||
*/
|
||||
public Query<T> query() {
|
||||
return db().find(type);
|
||||
|
||||
@@ -20,7 +20,7 @@ import java.util.concurrent.TimeoutException;
|
||||
* <pre>{@code
|
||||
*
|
||||
* // create a query to find all orders
|
||||
* Query<Order> query = Ebean.find(Order.class);
|
||||
* Query<Order> query = DB.find(Order.class);
|
||||
*
|
||||
* // execute the query in a background thread
|
||||
* // immediately returning the futureList
|
||||
|
||||
@@ -11,7 +11,7 @@ package io.ebean;
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
* Query q =
|
||||
* Ebean.find(Person.class)
|
||||
* DB.find(Person.class)
|
||||
* .where()
|
||||
* .or()
|
||||
* .like("name", "Rob%")
|
||||
@@ -30,7 +30,7 @@ package io.ebean;
|
||||
* <pre>{@code
|
||||
*
|
||||
* Query q =
|
||||
* Ebean.find(Person.class)
|
||||
* DB.find(Person.class)
|
||||
* .where()
|
||||
* .or()
|
||||
* .like("name", "Rob%")
|
||||
@@ -50,7 +50,7 @@ package io.ebean;
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
* Query<Customer> q =
|
||||
* Ebean.find(Customer.class)
|
||||
* DB.find(Customer.class)
|
||||
* .where()
|
||||
* .or()
|
||||
* .and()
|
||||
|
||||
@@ -11,15 +11,15 @@ import io.ebean.bean.EntityBean;
|
||||
* Ebean users.
|
||||
* <p>
|
||||
* Note that there is a ebean-mocker project that enables you to use Mockito or similar
|
||||
* tools to still mock out the underlying 'default EbeanServer' for testing purposes.
|
||||
* tools to still mock out the underlying 'default Database' 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
|
||||
* You can use Dependency Injection like Guice or Spring to construct and wire a Database 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
|
||||
* DI container creates the Database instance it can be registered with DB. In this
|
||||
* way the Database 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
|
||||
@@ -76,15 +76,14 @@ import io.ebean.bean.EntityBean;
|
||||
public abstract class Model {
|
||||
|
||||
/**
|
||||
* Return the underlying 'default' EbeanServer.
|
||||
* Return the underlying 'default' Database.
|
||||
* <p>
|
||||
* This provides full access to the API such as explicit transaction demarcation etc.
|
||||
* <p>
|
||||
* Example:
|
||||
* <pre>{@code
|
||||
*
|
||||
* Transaction transaction = Customer.db().beginTransaction();
|
||||
* try {
|
||||
* try (Transaction transaction = Customer.db().beginTransaction()) {
|
||||
*
|
||||
* // turn off cascade persist for this transaction
|
||||
* transaction.setPersistCascade(false);
|
||||
@@ -104,26 +103,21 @@ public abstract class Model {
|
||||
*
|
||||
* transaction.commit();
|
||||
*
|
||||
* } finally {
|
||||
* transaction.end();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public static EbeanServer db() {
|
||||
return Ebean.getDefaultServer();
|
||||
public static Database db() {
|
||||
return DB.getDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Return a named Database that is typically different to the default database.
|
||||
*
|
||||
* @param server The name of the EbeanServer. If this is null then the default EbeanServer is returned.
|
||||
* @param server The name of the Database. If this is null then the default Database is returned.
|
||||
*/
|
||||
public static EbeanServer db(String server) {
|
||||
return Ebean.getServer(server);
|
||||
public static Database db(String server) {
|
||||
return DB.byName(server);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,7 +140,7 @@ public abstract class Model {
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see EbeanServer#markAsDirty(Object)
|
||||
* @see Database#markAsDirty(Object)
|
||||
*/
|
||||
public void markAsDirty() {
|
||||
db().markAsDirty(this);
|
||||
@@ -182,7 +176,7 @@ public abstract class Model {
|
||||
* 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)
|
||||
* @see Database#save(Object)
|
||||
*/
|
||||
public void save() {
|
||||
db().save(this);
|
||||
@@ -202,7 +196,7 @@ public abstract class Model {
|
||||
/**
|
||||
* Update this entity.
|
||||
*
|
||||
* @see EbeanServer#update(Object)
|
||||
* @see Database#update(Object)
|
||||
*/
|
||||
public void update() {
|
||||
db().update(this);
|
||||
@@ -211,7 +205,7 @@ public abstract class Model {
|
||||
/**
|
||||
* Insert this entity.
|
||||
*
|
||||
* @see EbeanServer#insert(Object)
|
||||
* @see Database#insert(Object)
|
||||
*/
|
||||
public void insert() {
|
||||
db().insert(this);
|
||||
@@ -232,7 +226,7 @@ public abstract class Model {
|
||||
* deleted. Note that, if JDBC batch mode is used then this always returns true.
|
||||
* </p>
|
||||
*
|
||||
* @see EbeanServer#delete(Object)
|
||||
* @see Database#delete(Object)
|
||||
*/
|
||||
public boolean delete() {
|
||||
return db().delete(this);
|
||||
@@ -245,7 +239,7 @@ public abstract class Model {
|
||||
* want to perform a hard/permanent delete.
|
||||
* </p>
|
||||
*
|
||||
* @see EbeanServer#deletePermanent(Object)
|
||||
* @see Database#deletePermanent(Object)
|
||||
*/
|
||||
public boolean deletePermanent() {
|
||||
return db().deletePermanent(this);
|
||||
@@ -275,7 +269,7 @@ public abstract class Model {
|
||||
/**
|
||||
* Refreshes this entity from the database.
|
||||
*
|
||||
* @see EbeanServer#refresh(Object)
|
||||
* @see Database#refresh(Object)
|
||||
*/
|
||||
public void refresh() {
|
||||
db().refresh(this);
|
||||
|
||||
@@ -4,7 +4,6 @@ import io.ebean.util.StringHelper;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -412,15 +411,15 @@ public final class OrderBy<T> implements Serializable {
|
||||
|
||||
String[] chunks = orderByClause.split(",");
|
||||
for (String chunk : chunks) {
|
||||
String[] pairs = chunk.split(" ");
|
||||
Property p = parseProperty(pairs);
|
||||
Property p = parseProperty(chunk);
|
||||
if (p != null) {
|
||||
list.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Property parseProperty(String[] pairs) {
|
||||
private Property parseProperty(String chunk) {
|
||||
String[] pairs = chunk.split(" ");
|
||||
if (pairs.length == 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -446,8 +445,7 @@ public final class OrderBy<T> implements Serializable {
|
||||
boolean asc = isAscending(wordList.get(1));
|
||||
return new Property(wordList.get(0), asc, wordList.get(2), wordList.get(3));
|
||||
}
|
||||
String m = "Expecting a 1, 2 or 4 words in [" + Arrays.toString(pairs) + "] but got " + wordList;
|
||||
throw new RuntimeException(m);
|
||||
return new Property(chunk.trim(), true);
|
||||
}
|
||||
|
||||
private boolean isAscending(String s) {
|
||||
|
||||
@@ -24,8 +24,7 @@ import java.util.concurrent.Future;
|
||||
* // We want to find the first 50 new orders
|
||||
* // ... so we don't really need setFirstRow(0)
|
||||
*
|
||||
* PagedList<Order> pagedList
|
||||
* = ebeanServer.find(Order.class)
|
||||
* PagedList<Order> pagedList = DB.find(Order.class)
|
||||
* .where().eq("status", Order.Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .setFirstRow(0)
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.util.List;
|
||||
* pairs.add("sj2", 1001);
|
||||
* pairs.add("pf3", 1000);
|
||||
*
|
||||
* List<OCachedNatKeyBean3> list = Ebean.find(OCachedNatKeyBean3.class)
|
||||
* List<OCachedNatKeyBean3> list = DB.find(OCachedNatKeyBean3.class)
|
||||
* .where()
|
||||
* .eq("store", "def")
|
||||
* .inPairs(pairs) // IN clause with 'pairs' of values
|
||||
|
||||
@@ -40,6 +40,11 @@ public interface ProfileLocation {
|
||||
*/
|
||||
String shortDescription();
|
||||
|
||||
/**
|
||||
* Return the short label.
|
||||
*/
|
||||
String label();
|
||||
|
||||
/**
|
||||
* Add execution time.
|
||||
*/
|
||||
|
||||
@@ -19,10 +19,7 @@ import java.util.function.Predicate;
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Order> orderList =
|
||||
* ebeanServer.find(Order.class)
|
||||
* .fetch("customer")
|
||||
* .fetch("details")
|
||||
* List<Order> orderList = DB.find(Order.class)
|
||||
* .where()
|
||||
* .like("customer.name","rob%")
|
||||
* .gt("orderDate",lastWeek)
|
||||
@@ -38,29 +35,14 @@ import java.util.function.Predicate;
|
||||
* <pre>{@code
|
||||
*
|
||||
* String oql =
|
||||
* +" fetch customer "
|
||||
* +" fetch details "
|
||||
* +" where customer.name like :custName and orderDate > :minOrderDate "
|
||||
* +" order by customer.id, id desc "
|
||||
* +" limit 50 ";
|
||||
*
|
||||
* Query<Order> query = ebeanServer.createQuery(Order.class, oql);
|
||||
* query.setParameter("custName", "Rob%");
|
||||
* query.setParameter("minOrderDate", lastWeek);
|
||||
*
|
||||
* List<Order> orderList = query.findList();
|
||||
* ...
|
||||
* }</pre>
|
||||
* <p>
|
||||
* Example: Using a named query called "with.cust.and.details"
|
||||
* </p>
|
||||
* <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 = DB.createQuery(Order.class, oql)
|
||||
* .setParameter("custName", "Rob%")
|
||||
* .setParameter("minOrderDate", lastWeek)
|
||||
* .findList();
|
||||
* ...
|
||||
* }</pre>
|
||||
* <h3>AutoTune</h3>
|
||||
@@ -372,6 +354,27 @@ public interface Query<T> {
|
||||
*/
|
||||
Query<T> setAutoTune(boolean autoTune);
|
||||
|
||||
/**
|
||||
* Execute the query allowing properties with invalid JSON to be collected and not fail the query.
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch a bean with JSON content
|
||||
* EBasicJsonList bean= DB.find(EBasicJsonList.class)
|
||||
* .setId(42)
|
||||
* .setAllowLoadErrors() // collect errors into bean state if we have invalid JSON
|
||||
* .findOne();
|
||||
*
|
||||
*
|
||||
* // get the invalid JSON errors from the bean state
|
||||
* Map<String, Exception> errors = server().getBeanState(bean).getLoadErrors();
|
||||
*
|
||||
* // If this map is not empty tell we have invalid JSON
|
||||
* // and should try and fix the JSON content or inform the user
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
Query<T> setAllowLoadErrors();
|
||||
|
||||
/**
|
||||
* Set the default lazy loading batch size to use.
|
||||
* <p>
|
||||
@@ -415,8 +418,7 @@ public interface Query<T> {
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* List<Customer> customers = DB.find(Customer.class)
|
||||
* // Only fetch the customer id, name and status.
|
||||
* // This is described as a "Partial Object"
|
||||
* .select("name, status")
|
||||
@@ -444,8 +446,7 @@ public interface Query<T> {
|
||||
* <pre>{@code
|
||||
*
|
||||
* // query orders...
|
||||
* List<Order> orders =
|
||||
* ebeanServer.find(Order.class)
|
||||
* List<Order> orders = DB.find(Order.class)
|
||||
* // fetch the customer...
|
||||
* // ... getting the customers name and phone number
|
||||
* .fetch("customer", "name, phoneNumber")
|
||||
@@ -460,8 +461,7 @@ public interface Query<T> {
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* List<Customer> customers = DB.find(Customer.class)
|
||||
* .select("name, status")
|
||||
* .fetch("contacts", "firstName,lastName,email")
|
||||
* .findList();
|
||||
@@ -530,8 +530,7 @@ public interface Query<T> {
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* List<Customer> customers = DB.find(Customer.class)
|
||||
* .select("name, status")
|
||||
* .fetch("contacts", "firstName,lastName,email", new FetchConfig().lazy(10))
|
||||
* .findList();
|
||||
@@ -552,8 +551,7 @@ public interface Query<T> {
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* List<Customer> customers = DB.find(Customer.class)
|
||||
* // eager fetch the contacts
|
||||
* .fetch("contacts")
|
||||
* .findList();
|
||||
@@ -616,8 +614,7 @@ public interface Query<T> {
|
||||
* <pre>{@code
|
||||
*
|
||||
* // fetch customers (their id, name and status)
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* List<Customer> customers = DB.find(Customer.class)
|
||||
* // lazy fetch contacts with a batch size of 100
|
||||
* .fetch("contacts", new FetchConfig().lazy(100))
|
||||
* .findList();
|
||||
@@ -637,7 +634,7 @@ public interface Query<T> {
|
||||
/**
|
||||
* Execute the query returning the list of Id's.
|
||||
* <p>
|
||||
* This query will execute against the EbeanServer that was used to create it.
|
||||
* This query will execute against the Database that was used to create it.
|
||||
* </p>
|
||||
*/
|
||||
@Nonnull
|
||||
@@ -659,12 +656,11 @@ public interface Query<T> {
|
||||
* 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.
|
||||
* This query will execute against the Database that was used to create it.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Query<Customer> query =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* Query<Customer> query = DB.find(Customer.class)
|
||||
* .where().eq("status", Status.NEW)
|
||||
* .order().asc("id");
|
||||
*
|
||||
@@ -711,7 +707,7 @@ public interface Query<T> {
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* ebeanServer.find(Customer.class)
|
||||
* DB.find(Customer.class)
|
||||
* .where().eq("status", Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .findEach((Customer customer) -> {
|
||||
@@ -740,7 +736,7 @@ public interface Query<T> {
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* ebeanServer.find(Customer.class)
|
||||
* DB.find(Customer.class)
|
||||
* .fetch("contacts", new FetchConfig().query(2))
|
||||
* .where().eq("status", Status.NEW)
|
||||
* .order().asc("id")
|
||||
@@ -763,12 +759,11 @@ public interface Query<T> {
|
||||
/**
|
||||
* Execute the query returning the list of objects.
|
||||
* <p>
|
||||
* This query will execute against the EbeanServer that was used to create it.
|
||||
* This query will execute against the Database that was used to create it.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* List<Customer> customers = DB.find(Customer.class)
|
||||
* .where().ilike("name", "rob%")
|
||||
* .findList();
|
||||
*
|
||||
@@ -780,12 +775,11 @@ public interface Query<T> {
|
||||
/**
|
||||
* Execute the query returning the set of objects.
|
||||
* <p>
|
||||
* This query will execute against the EbeanServer that was used to create it.
|
||||
* This query will execute against the Database that was used to create it.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Set<Customer> customers =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* Set<Customer> customers = DB.find(Customer.class)
|
||||
* .where().ilike("name", "rob%")
|
||||
* .findSet();
|
||||
*
|
||||
@@ -797,7 +791,7 @@ public interface Query<T> {
|
||||
/**
|
||||
* Execute the query returning a map of the objects.
|
||||
* <p>
|
||||
* This query will execute against the EbeanServer that was used to create it.
|
||||
* This query will execute against the Database that was used to create it.
|
||||
* </p>
|
||||
* <p>
|
||||
* You can use setMapKey() so specify the property values to be used as keys
|
||||
@@ -805,8 +799,7 @@ public interface Query<T> {
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Map<String, Product> map =
|
||||
* ebeanServer.find(Product.class)
|
||||
* Map<String, Product> map = DB.find(Product.class)
|
||||
* .setMapKey("sku")
|
||||
* .findMap();
|
||||
*
|
||||
@@ -822,7 +815,7 @@ public interface Query<T> {
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<String> names =
|
||||
* Ebean.find(Customer.class)
|
||||
* DB.find(Customer.class)
|
||||
* .select("name")
|
||||
* .orderBy().asc("name")
|
||||
* .findSingleAttributeList();
|
||||
@@ -833,7 +826,7 @@ public interface Query<T> {
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<String> names =
|
||||
* Ebean.find(Customer.class)
|
||||
* DB.find(Customer.class)
|
||||
* .setDistinct(true)
|
||||
* .select("name")
|
||||
* .where().eq("status", Customer.Status.NEW)
|
||||
@@ -854,7 +847,7 @@ public interface Query<T> {
|
||||
* <pre>{@code
|
||||
*
|
||||
* String name =
|
||||
* Ebean.find(Customer.class)
|
||||
* DB.find(Customer.class)
|
||||
* .select("name")
|
||||
* .where().eq("id", 42)
|
||||
* .findSingleAttribute();
|
||||
@@ -868,6 +861,37 @@ public interface Query<T> {
|
||||
*/
|
||||
boolean isCountDistinct();
|
||||
|
||||
/**
|
||||
* Execute the query returning true if a row is found.
|
||||
* <p>
|
||||
* The query is executed using max rows of 1 and will only select the id property.
|
||||
* This method is really just a convenient way to optimise a query to perform a
|
||||
* 'does a row exist in the db' check.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Example using a query bean:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists =
|
||||
* new QContact()
|
||||
* .email.equalTo("rob@foo.com")
|
||||
* .exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h2>Example:</h2>
|
||||
* <pre>{@code
|
||||
*
|
||||
* boolean userExists = query()
|
||||
* .where().eq("email", "rob@foo.com")
|
||||
* .exists();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return True if the query finds a matching row in the database
|
||||
*/
|
||||
boolean exists();
|
||||
|
||||
/**
|
||||
* Execute the query returning either a single bean or null (if no matching
|
||||
* bean is found).
|
||||
@@ -882,8 +906,7 @@ public interface Query<T> {
|
||||
* <pre>{@code
|
||||
*
|
||||
* // assuming the sku of products is unique...
|
||||
* Product product =
|
||||
* ebeanServer.find(Product.class)
|
||||
* Product product = DB.find(Product.class)
|
||||
* .where().eq("sku", "aa113")
|
||||
* .findOne();
|
||||
* ...
|
||||
@@ -895,8 +918,7 @@ public interface Query<T> {
|
||||
* <pre>{@code
|
||||
*
|
||||
* // Fetch order 1 and additionally fetch join its order details...
|
||||
* Order order =
|
||||
* ebeanServer.find(Order.class)
|
||||
* Order order = DB.find(Order.class)
|
||||
* .setId(1)
|
||||
* .fetch("details")
|
||||
* .findOne();
|
||||
@@ -1037,7 +1059,7 @@ public interface Query<T> {
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* PagedList<Order> pagedList = Ebean.find(Order.class)
|
||||
* PagedList<Order> pagedList = DB.find(Order.class)
|
||||
* .setFirstRow(50)
|
||||
* .setMaxRows(20)
|
||||
* .findPagedList();
|
||||
@@ -1062,11 +1084,9 @@ public interface Query<T> {
|
||||
* // a query with a named parameter
|
||||
* String oql = "find order where status = :orderStatus";
|
||||
*
|
||||
* Query<Order> query = ebeanServer.find(Order.class, oql);
|
||||
*
|
||||
* // bind the named parameter
|
||||
* query.bind("orderStatus", OrderStatus.NEW);
|
||||
* List<Order> list = query.findList();
|
||||
* List<Order> list = DB.find(Order.class, oql)
|
||||
* .setParameter("orderStatus", OrderStatus.NEW)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
@@ -1084,12 +1104,9 @@ public interface Query<T> {
|
||||
* // a query with a positioned parameter
|
||||
* String oql = "where status = ? order by id desc";
|
||||
*
|
||||
* Query<Order> query = ebeanServer.createQuery(Order.class, oql);
|
||||
*
|
||||
* // bind the parameter
|
||||
* query.setParameter(1, OrderStatus.NEW);
|
||||
*
|
||||
* List<Order> list = query.findList();
|
||||
* List<Order> list = DB.createQuery(Order.class, oql)
|
||||
* .setParameter(1, OrderStatus.NEW)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
@@ -1106,8 +1123,7 @@ public interface Query<T> {
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Order order =
|
||||
* ebeanServer.find(Order.class)
|
||||
* Order order = DB.find(Order.class)
|
||||
* .setId(1)
|
||||
* .fetch("details")
|
||||
* .findOne();
|
||||
@@ -1128,8 +1144,7 @@ public interface Query<T> {
|
||||
* Add a single Expression to the where clause returning the query.
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Order> newOrders =
|
||||
* ebeanServer.find(Order.class)
|
||||
* List<Order> newOrders = DB.find(Order.class)
|
||||
* .where().eq("status", Order.NEW)
|
||||
* .findList();
|
||||
* ...
|
||||
@@ -1144,8 +1159,7 @@ public interface Query<T> {
|
||||
* where clause.
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Order> orders =
|
||||
* ebeanServer.find(Order.class)
|
||||
* List<Order> orders = DB.find(Order.class)
|
||||
* .where()
|
||||
* .eq("status", Order.NEW)
|
||||
* .ilike("customer.name","rob%")
|
||||
@@ -1191,10 +1205,7 @@ public interface Query<T> {
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> list =
|
||||
* ebeanServer.find(Customer.class)
|
||||
* // .fetch("orders", new FetchConfig().lazy())
|
||||
* // .fetch("orders", new FetchConfig().query())
|
||||
* List<Customer> list = DB.find(Customer.class)
|
||||
* .fetch("orders")
|
||||
* .where().ilike("name", "rob%")
|
||||
* .filterMany("orders").eq("status", Order.Status.NEW).gt("orderDate", lastWeek)
|
||||
@@ -1208,8 +1219,7 @@ public interface Query<T> {
|
||||
* </p>
|
||||
*
|
||||
* @param propertyName the name of the many property that you want to have a filter on.
|
||||
* @return the expression list that you add filter expressions for the many
|
||||
* to.
|
||||
* @return the expression list that you add filter expressions for the many to.
|
||||
*/
|
||||
ExpressionList<T> filterMany(String propertyName);
|
||||
|
||||
@@ -1323,7 +1333,7 @@ public interface Query<T> {
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Customer> customers =
|
||||
* Ebean.find(Customer.class)
|
||||
* DB.find(Customer.class)
|
||||
* .setDistinct(true)
|
||||
* .select("name")
|
||||
* .findList();
|
||||
@@ -1339,7 +1349,7 @@ public interface Query<T> {
|
||||
*
|
||||
* List<CountedValue<Order.Status>> orderStatusCount =
|
||||
*
|
||||
* Ebean.find(Order.class)
|
||||
* DB.find(Order.class)
|
||||
* .select("status")
|
||||
* .where()
|
||||
* .gt("orderDate", LocalDate.now().minusMonths(3))
|
||||
@@ -1395,10 +1405,8 @@ public interface Query<T> {
|
||||
*
|
||||
* // Assuming sku is unique for products...
|
||||
*
|
||||
* Map<String,Product> productMap =
|
||||
* ebeanServer.find(Product.class)
|
||||
* // use sku for keys...
|
||||
* .setMapKey("sku")
|
||||
* Map<String,Product> productMap = DB.find(Product.class)
|
||||
* .setMapKey("sku") // sku map keys...
|
||||
* .findMap();
|
||||
*
|
||||
* }</pre>
|
||||
@@ -1570,11 +1578,50 @@ public interface Query<T> {
|
||||
*/
|
||||
Query<T> alias(String alias);
|
||||
|
||||
/**
|
||||
* Set the base table to use for this query.
|
||||
* <p>
|
||||
* Typically this is used when a table has partitioning and we wish to specify a specific
|
||||
* partition/table to query against.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* QOrder()
|
||||
* .setBaseTable("order_2019_05")
|
||||
* .status.equalTo(Status.NEW)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
Query<T> setBaseTable(String baseTable);
|
||||
|
||||
/**
|
||||
* Return the type of beans being queried.
|
||||
*/
|
||||
Class<T> getBeanType();
|
||||
|
||||
/**
|
||||
* Restrict the query to only return subtypes of the given inherit type.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<Animal> animals =
|
||||
* new QAnimal()
|
||||
* .name.startsWith("Fluffy")
|
||||
* .setInheritType(Cat.class)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param type An inheritance subtype of the
|
||||
*/
|
||||
Query<T> setInheritType(Class<? extends T> type);
|
||||
|
||||
/**
|
||||
* Returns the inherit type. This is normally the same as getBeanType() returns as long as no other type is set.
|
||||
*/
|
||||
Class<? extends T> getInheritType();
|
||||
|
||||
/**
|
||||
* Return the type of query being executed.
|
||||
*/
|
||||
|
||||
@@ -87,7 +87,7 @@ package io.ebean;
|
||||
* // .columnMapping("sum(d.order_qty*d.unit_price)", "totalAmount")
|
||||
* .create();
|
||||
*
|
||||
* List<OrderAggregate> list = Ebean.find(OrderAggregate.class)
|
||||
* List<OrderAggregate> list = DB.find(OrderAggregate.class)
|
||||
* .setRawSql(rawSql)
|
||||
* .where().gt("order.id", 0)
|
||||
* .having().gt("totalAmount", 20)
|
||||
@@ -114,7 +114,7 @@ package io.ebean;
|
||||
* .columnMappingIgnore("'ignoreMe'")
|
||||
* .create();
|
||||
*
|
||||
* List<OrderAggregate> orders = Ebean.find(OrderAggregate.class)
|
||||
* List<OrderAggregate> orders = DB.find(OrderAggregate.class)
|
||||
* .setRawSql(rawSql)
|
||||
* .fetch("order", "status,orderDate", new FetchConfig().query())
|
||||
* .fetch("order.customer", "name")
|
||||
@@ -146,7 +146,7 @@ package io.ebean;
|
||||
* .tableAliasMapping("p", "details.product")
|
||||
* .create();
|
||||
*
|
||||
* List<Order> ordersFromRaw = Ebean.find(Order.class)
|
||||
* List<Order> ordersFromRaw = DB.find(Order.class)
|
||||
* .setRawSql(rawSql)
|
||||
* .setParameter("maxOrderId", 2)
|
||||
* .setParameter("productId", 1)
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.sql.SQLException;
|
||||
*
|
||||
* String sql = "select id, name, status from o_customer order by name desc";
|
||||
*
|
||||
* Ebean.createSqlQuery(sql)
|
||||
* DB.sqlQuery(sql)
|
||||
* .findEachRow((resultSet, rowNum) -> {
|
||||
*
|
||||
* // read directly from ResultSet
|
||||
|
||||
@@ -40,7 +40,7 @@ import java.sql.SQLException;
|
||||
*
|
||||
* String sql = "select id, name, status from o_customer where name = ?";
|
||||
*
|
||||
* CustomerDto rob = Ebean.createSqlQuery(sql)
|
||||
* CustomerDto rob = DB.sqlQuery(sql)
|
||||
* .setParameter(1, "Rob")
|
||||
* .findOne(CUSTOMER_MAPPER);
|
||||
*
|
||||
|
||||
@@ -13,20 +13,8 @@ import java.util.Map;
|
||||
* <h3>Example of simple use</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* EbeanServer server = Ebean.getDefaultServer();
|
||||
* server.script().run("/scripts/test-script.sql");
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*
|
||||
* <h3>Example using place holders in the script</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Map<String,String> placeholders = new HashMap<>();
|
||||
* placeholders.put("tableName", "e_basic");
|
||||
*
|
||||
* EbeanServer server = Ebean.getDefaultServer();
|
||||
* server.script().run("/scripts/test-script.sql");
|
||||
* Database database = DB.getDefault();
|
||||
* database.script().run("/scripts/test-script.sql");
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@@ -45,8 +33,8 @@ public interface ScriptRunner {
|
||||
* Map<String,String> placeholders = new HashMap<>();
|
||||
* placeholders.put("tableName", "e_basic");
|
||||
*
|
||||
* EbeanServer server = Ebean.getDefaultServer();
|
||||
* server.script().run("/scripts/test-script.sql");
|
||||
* Database database = DB.getDefault();
|
||||
* database.script().run("/scripts/test-script.sql", placeholders);
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
|
||||
@@ -10,34 +10,30 @@ import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Query object for performing native SQL queries that return SqlRow's.
|
||||
* <p>
|
||||
* Firstly note that you can use your own sql queries with <em>entity beans</em>
|
||||
* by using the SqlSelect annotation. This should be your first approach when
|
||||
* wanting to use your own SQL queries.
|
||||
* </p>
|
||||
* <p>
|
||||
* If ORM Mapping is too tight and constraining for your problem then SqlQuery
|
||||
* could be a good approach.
|
||||
* </p>
|
||||
* Query object for performing native SQL queries that return SqlRow or directly read
|
||||
* ResultSet using a RowMapper.
|
||||
* <p>
|
||||
* The returned SqlRow objects are similar to a LinkedHashMap with some type
|
||||
* conversion support added.
|
||||
* </p>
|
||||
* <p>
|
||||
* Refer to {@link DtoQuery} for native sql queries returning DTO beans.
|
||||
* </p>
|
||||
* <p>
|
||||
* Refer to {@link Database#findNative(Class, String)} for native sql queries returning entity beans.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // its typically a good idea to use a named query
|
||||
* // and put the sql in the orm.xml instead of in your code
|
||||
* // example using named parameters
|
||||
*
|
||||
* String sql = "select id, name from customer where name like :name and status_code = :status";
|
||||
*
|
||||
* SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
|
||||
* sqlQuery.setParameter("name", "Acme%");
|
||||
* sqlQuery.setParameter("status", "ACTIVE");
|
||||
*
|
||||
* // execute the query returning a List of MapBean objects
|
||||
* List<SqlRow> list = sqlQuery.findList();
|
||||
* List<SqlRow> list =
|
||||
* DB.sqlQuery(sql)
|
||||
* .setParameter("name", "Acme%")
|
||||
* .setParameter("status", "ACTIVE")
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@@ -104,7 +100,7 @@ public interface SqlQuery extends Serializable {
|
||||
*
|
||||
* String sql = "select id, name, status from customer order by name desc";
|
||||
*
|
||||
* Ebean.createSqlQuery(sql)
|
||||
* DB.sqlQuery(sql)
|
||||
* .findEachRow((resultSet, rowNum) -> {
|
||||
*
|
||||
* // read directly from ResultSet
|
||||
@@ -134,7 +130,7 @@ public interface SqlQuery extends Serializable {
|
||||
*
|
||||
* String sql = "select max(unit_price) from o_order_detail where order_qty > ?";
|
||||
*
|
||||
* BigDecimal maxPrice = Ebean.createSqlQuery(sql)
|
||||
* BigDecimal maxPrice = DB.sqlQuery(sql)
|
||||
* .setParameter(1, 2)
|
||||
* .findSingleAttribute(BigDecimal.class);
|
||||
*
|
||||
@@ -176,9 +172,10 @@ public interface SqlQuery extends Serializable {
|
||||
* " order by (unit_price * order_qty) desc";
|
||||
*
|
||||
* //
|
||||
* List<BigDecimal> lineAmounts = Ebean.createSqlQuery(sql)
|
||||
* .setParameter(1, 3)
|
||||
* .findSingleAttributeList(BigDecimal.class);
|
||||
* List<BigDecimal> lineAmounts =
|
||||
* DB.sqlQuery(sql)
|
||||
* .setParameter(1, 3)
|
||||
* .findSingleAttributeList(BigDecimal.class);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
@@ -195,6 +192,33 @@ public interface SqlQuery extends Serializable {
|
||||
*/
|
||||
SqlQuery setParameter(String name, Object value);
|
||||
|
||||
/**
|
||||
* Set one of more positioned parameters.
|
||||
* <p>
|
||||
* This is a convenient alternative to multiple calls setParameter().
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql = "select id, name from customer where name like ? and status = ?";
|
||||
*
|
||||
* List<SqlRow> list =
|
||||
* DB.sqlQuery(sql)
|
||||
* .setParams("Rob", Status.NEW)
|
||||
* .findList();
|
||||
*
|
||||
*
|
||||
* // is the same as ...
|
||||
*
|
||||
* List<SqlRow> list =
|
||||
* DB.sqlQuery(sql)
|
||||
* .setParameter(1, "Rob")
|
||||
* .setParameter(2, "Status.NEW)
|
||||
* .findList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
SqlQuery setParams(Object... values);
|
||||
|
||||
/**
|
||||
* The same as bind for positioned parameters.
|
||||
*/
|
||||
|
||||
@@ -16,20 +16,33 @@ package io.ebean;
|
||||
* notify Ebean of external changes and enable Ebean to maintain it's "L2"
|
||||
* server cache.
|
||||
* </p>
|
||||
* <p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // example that uses 'named' parameters
|
||||
* // example using 'positioned' parameters
|
||||
*
|
||||
* String s = "UPDATE f_topic set post_count = :count where id = :id";
|
||||
* String sql = "insert into audit_log (group, title, description) values (?, ?, ?);
|
||||
*
|
||||
* SqlUpdate update = Ebean.createSqlUpdate(s);
|
||||
* update.setParameter("id", 1);
|
||||
* update.setParameter("count", 50);
|
||||
* int rows =
|
||||
* DB.sqlUpdate(sql)
|
||||
* .setParams("login", "new user", "user rob was created")
|
||||
* .executeNow();
|
||||
*
|
||||
* int modifiedCount = update.execute();
|
||||
* }</pre>
|
||||
*
|
||||
* String msg = "There were " + modifiedCount + " rows updated";
|
||||
* <pre>{@code
|
||||
*
|
||||
* // example using 'named' parameters
|
||||
*
|
||||
* String sql = "update topic set post_count = :count where id = :id";
|
||||
*
|
||||
* int rows =
|
||||
* DB.sqlUpdate(sql)
|
||||
* .setParameter("id", 1)
|
||||
* .setParameter("count", 50)
|
||||
* .execute();
|
||||
*
|
||||
* String msg = "There were " + rows + " rows updated";
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
@@ -37,9 +50,10 @@ package io.ebean;
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql = "insert into audit_log (id, description, modified_description) values (?,?,?)";
|
||||
* SqlUpdate insert = Ebean.createSqlUpdate(sql);
|
||||
*
|
||||
* try (Transaction txn = Ebean.beginTransaction()) {
|
||||
* SqlUpdate insert = DB.sqlUpdate(sql);
|
||||
*
|
||||
* try (Transaction txn = DB.beginTransaction()) {
|
||||
* txn.setBatchMode(true);
|
||||
*
|
||||
* insert.setNextParameter(10000);
|
||||
@@ -65,7 +79,7 @@ package io.ebean;
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* try (Transaction txn = Ebean.beginTransaction()) {
|
||||
* try (Transaction txn = DB.beginTransaction()) {
|
||||
*
|
||||
* insert.setNextParameter(10000);
|
||||
* insert.setNextParameter("hello");
|
||||
@@ -217,6 +231,33 @@ public interface SqlUpdate {
|
||||
*/
|
||||
SqlUpdate setTimeout(int secs);
|
||||
|
||||
/**
|
||||
* Set one of more positioned parameters.
|
||||
* <p>
|
||||
* This is a convenient alternative to multiple setParameter() calls.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql = "insert into audit_log (id, name, version) values (?,?,?)";
|
||||
*
|
||||
* DB.sqlUpdate(sql)
|
||||
* .setParams(UUID.randomUUID(), "Hello", 1)
|
||||
* .executeNow();
|
||||
*
|
||||
*
|
||||
* // is the same as ...
|
||||
*
|
||||
* DB.sqlUpdate(sql)
|
||||
* .setParameter(1, UUID.randomUUID())
|
||||
* .setParameter(2, "Hello")
|
||||
* .setParameter(3, 1)
|
||||
* .executeNow();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*/
|
||||
SqlUpdate setParams(Object... values);
|
||||
|
||||
/**
|
||||
* Set the next positioned parameter.
|
||||
*
|
||||
|
||||
@@ -14,18 +14,17 @@ import java.sql.Connection;
|
||||
public interface Transaction extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* Return the current transaction (of the default server) or null if there is
|
||||
* Return the current transaction (of the default database) or null if there is
|
||||
* no current transaction in scope.
|
||||
* <p>
|
||||
* This is the same as <code>Ebean.currentTransaction()</code>
|
||||
* This is the same as <code>DB.currentTransaction()</code>
|
||||
* </p>
|
||||
* <p>
|
||||
* This returns the current transaction for the 'default server'. If you are using
|
||||
* multiple EbeanServer's then use {@link EbeanServer#currentTransaction()}.
|
||||
* This returns the current transaction for the default database.
|
||||
* </p>
|
||||
*
|
||||
* @see Ebean#currentTransaction()
|
||||
* @see EbeanServer#currentTransaction()
|
||||
* @see DB#currentTransaction()
|
||||
* @see Database#currentTransaction()
|
||||
*/
|
||||
static Transaction current() {
|
||||
return Ebean.currentTransaction();
|
||||
@@ -244,8 +243,7 @@ public interface Transaction extends AutoCloseable {
|
||||
*
|
||||
* // assume Customer has L2 bean caching enabled ...
|
||||
*
|
||||
* Transaction transaction = Ebean.beginTransaction();
|
||||
* try {
|
||||
* try (Transaction transaction = DB.beginTransaction()) {
|
||||
*
|
||||
* // this uses L2 bean cache as the transaction
|
||||
* // ... is considered "query only" at this point
|
||||
@@ -253,7 +251,7 @@ public interface Transaction extends AutoCloseable {
|
||||
*
|
||||
* // transaction no longer "query only" once
|
||||
* // ... a bean has been saved etc
|
||||
* Ebean.save(someBean);
|
||||
* someBean.save();
|
||||
*
|
||||
* // will NOT use L2 bean cache as the transaction
|
||||
* // ... is no longer considered "query only"
|
||||
@@ -273,8 +271,7 @@ public interface Transaction extends AutoCloseable {
|
||||
* Customer.find.byId(99); // skips l2 bean cache
|
||||
*
|
||||
*
|
||||
* } finally {
|
||||
* transaction.end();
|
||||
* transaction.commit();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
@@ -290,36 +287,66 @@ public interface Transaction extends AutoCloseable {
|
||||
boolean isSkipCache();
|
||||
|
||||
/**
|
||||
* Turn on or off statement batching. Statement batching can be transparent
|
||||
* for drivers and databases that support getGeneratedKeys. Otherwise you may
|
||||
* wish to specifically control when batching is used via this method.
|
||||
* <p>
|
||||
* Refer to <code>java.sql.PreparedStatement.addBatch();</code>
|
||||
* <p>
|
||||
* Note that you may also wish to use the setPersistCascade method to stop
|
||||
* save and delete cascade behaviour. You may do this to have full control
|
||||
* over the order of execution rather than the normal cascading fashion.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that the <em>execution order</em> in batch mode may be different from
|
||||
* non batch mode execution order. Also note that <em>insert behaviour</em>
|
||||
* may be different depending on the JDBC driver and its support for
|
||||
* getGeneratedKeys. That is, for JDBC drivers that do not support
|
||||
* getGeneratedKeys you may not get back the generated IDs (used for inserting
|
||||
* associated detail beans etc).
|
||||
* </p>
|
||||
* Turn on or off use of JDBC statement batching.
|
||||
* <p>
|
||||
* Calls to save(), delete(), insert() and execute() all support batch
|
||||
* processing. This includes normal beans, MapBean, CallableSql and UpdateSql.
|
||||
* processing. This includes normal beans, CallableSql and UpdateSql.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* try (Transaction transaction = server.beginTransaction()) {
|
||||
*
|
||||
* // turn on JDBC batch
|
||||
* transaction.setBatchMode(true);
|
||||
*
|
||||
* // tune the batch size
|
||||
* transaction.setBatchSize(50);
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* transaction.commit();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>getGeneratedKeys</h3>
|
||||
* <p>
|
||||
* The flushing of the batched statements is automatic but you can call
|
||||
* batchFlush when you like. Note that flushing occurs when a query is
|
||||
* executed or when you mix UpdateSql and CallableSql with save and delete of
|
||||
* Often with large batch inserts we want to turn off getGeneratedKeys. We do
|
||||
* this via {@link #setBatchGetGeneratedKeys(boolean)}.
|
||||
* Also note that some JDBC drivers do not support getGeneratedKeys in JDBC batch mode.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* try (Transaction transaction = server.beginTransaction()) {
|
||||
*
|
||||
* transaction.setBatchMode(true);
|
||||
* transaction.setBatchSize(100);
|
||||
* // insert but don't bother getting back the generated keys
|
||||
* transaction.setBatchGetGeneratedKeys(false);
|
||||
*
|
||||
*
|
||||
* // perform lots of inserts ...
|
||||
* ...
|
||||
*
|
||||
* transaction.commit();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Flush</h3>
|
||||
* <p>
|
||||
* The batch is automatically flushed when it hits the batch size and also when we
|
||||
* execute queries or when we mix UpdateSql and CallableSql with save and delete of
|
||||
* beans.
|
||||
* </p>
|
||||
* <p>
|
||||
* Example: batch processing executing every 3 rows
|
||||
* We use {@link #flush()} to explicitly flush the batch and we can use
|
||||
* {@link #setBatchFlushOnQuery(boolean)} and {@link #setBatchFlushOnMixed(boolean)}
|
||||
* to control the automatic flushing behaviour.
|
||||
* </p>
|
||||
* <p>
|
||||
* Example: batch processing of CallableSql executing every 10 rows
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
@@ -334,24 +361,21 @@ public interface Transaction extends AutoCloseable {
|
||||
* CallableSql cs = new CallableSql(sql);
|
||||
* cs.registerOut(2, Types.INTEGER);
|
||||
*
|
||||
* // (optional) inform eBean this stored procedure
|
||||
* // (optional) inform Ebean this stored procedure
|
||||
* // inserts into a table called sp_test
|
||||
* cs.addModification("sp_test", true, false, false);
|
||||
*
|
||||
* Transaction txn = ebeanServer.beginTransaction();
|
||||
* txn.setBatchMode(true);
|
||||
* txn.setBatchSize(3);
|
||||
* try {
|
||||
* try (Transaction txn = DB.beginTransaction()) {
|
||||
* txn.setBatchMode(true);
|
||||
* txn.setBatchSize(10);
|
||||
*
|
||||
* for (int i = 0; i < da.length;) {
|
||||
* cs.setParameter(1, da[i]);
|
||||
* ebeanServer.execute(cs);
|
||||
* DB.execute(cs);
|
||||
* }
|
||||
*
|
||||
* // NB: commit implicitly flushes
|
||||
* // Note: commit implicitly flushes
|
||||
* txn.commit();
|
||||
*
|
||||
* } finally {
|
||||
* txn.end();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
|
||||
@@ -41,7 +41,7 @@ package io.ebean;
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* Update<Topic> update = Ebean.createUpdate(Topic.class, "incrementPostCount");
|
||||
* Update<Topic> update = DB.createUpdate(Topic.class, "incrementPostCount");
|
||||
* update.setParameter("id", 1);
|
||||
* int rows = update.execute();
|
||||
*
|
||||
|
||||
@@ -12,8 +12,7 @@ package io.ebean;
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* int rows = DB.update(Customer.class)
|
||||
* .set("status", Customer.Status.ACTIVE)
|
||||
* .set("updtime", new Timestamp(System.currentTimeMillis()))
|
||||
* .where()
|
||||
@@ -39,8 +38,7 @@ package io.ebean;
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* int rows = DB.update(Customer.class)
|
||||
* .set("status", Customer.Status.ACTIVE)
|
||||
* .set("updtime", new Timestamp(System.currentTimeMillis()))
|
||||
* .where()
|
||||
@@ -73,8 +71,7 @@ public interface UpdateQuery<T> {
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* int rows = DB.update(Customer.class)
|
||||
* .set("status", Customer.Status.ACTIVE)
|
||||
* .set("updtime", new Timestamp(System.currentTimeMillis()))
|
||||
* .where()
|
||||
@@ -93,8 +90,7 @@ public interface UpdateQuery<T> {
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* int rows = DB.update(Customer.class)
|
||||
* .setNull("notes")
|
||||
* .where()
|
||||
* .gt("id", 1000)
|
||||
@@ -114,8 +110,7 @@ public interface UpdateQuery<T> {
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* int rows = DB.update(Customer.class)
|
||||
* .setRaw("status = coalesce(status, 'A')")
|
||||
* .where()
|
||||
* .gt("id", 1000)
|
||||
@@ -134,8 +129,7 @@ public interface UpdateQuery<T> {
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* int rows = ebeanServer
|
||||
* .update(Customer.class)
|
||||
* int rows = DB.update(Customer.class)
|
||||
* .setRaw("status = coalesce(status, ?)", Customer.Status.ACTIVE)
|
||||
* .where()
|
||||
* .gt("id", 1000)
|
||||
|
||||
@@ -9,7 +9,7 @@ package io.ebean.bean;
|
||||
public interface BeanCollectionLoader {
|
||||
|
||||
/**
|
||||
* Return the name of the associated EbeanServer.
|
||||
* Return the name of the associated Database.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ package io.ebean.bean;
|
||||
public interface BeanLoader {
|
||||
|
||||
/**
|
||||
* Return the name of the associated EbeanServer.
|
||||
* Return the name of the associated Database.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
|
||||
@@ -98,6 +98,8 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
|
||||
private Object[] origValues;
|
||||
|
||||
private Exception[] loadErrors;
|
||||
|
||||
private int lazyLoadProperty = -1;
|
||||
|
||||
private Object ownerId;
|
||||
@@ -1115,4 +1117,36 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
public void setSortOrder(int sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the load error that happened on this property.
|
||||
*/
|
||||
public void setLoadError(int propertyIndex, Exception t) {
|
||||
if (loadErrors == null) {
|
||||
loadErrors = new Exception[owner._ebean_getPropertyNames().length];
|
||||
}
|
||||
loadErrors[propertyIndex] = t;
|
||||
flags[propertyIndex] |= FLAG_LOADED_PROP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the loadErrors.
|
||||
*/
|
||||
public Map<String, Exception> getLoadErrors() {
|
||||
if (loadErrors == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Exception> ret = null;
|
||||
int len = getPropertyLength();
|
||||
for (int i = 0; i < len; i++) {
|
||||
Exception loadError = loadErrors[i];
|
||||
if (loadError != null) {
|
||||
if (ret == null) {
|
||||
ret = new LinkedHashMap<>();
|
||||
}
|
||||
ret.put(getProperty(i), loadError);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
protected boolean disableLazyLoad;
|
||||
|
||||
/**
|
||||
* The EbeanServer this is associated with. (used for lazy fetch).
|
||||
* The Database this is associated with. (used for lazy fetch).
|
||||
*/
|
||||
protected transient BeanCollectionLoader loader;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebean.config;
|
||||
|
||||
/**
|
||||
* Used to provide some automatic configuration early in the creation of an EbeanServer.
|
||||
* Used to provide some automatic configuration early in the creation of a Database.
|
||||
*/
|
||||
public interface AutoConfigure {
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package io.ebean.config;
|
||||
|
||||
/**
|
||||
* Defines the AutoTune behaviour for a EbeanServer.
|
||||
* Defines the AutoTune behaviour for a Database.
|
||||
*/
|
||||
public class AutoTuneConfig {
|
||||
|
||||
private AutoTuneMode mode = AutoTuneMode.DEFAULT_ON;
|
||||
private AutoTuneMode mode = AutoTuneMode.DEFAULT_OFF;
|
||||
|
||||
private String queryTuningFile = "ebean-autotune.xml";
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.ebean.config;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Configuration for the container that holds the EbeanServer instances.
|
||||
* Configuration for the container that holds the Database 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.
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package io.ebean.config;
|
||||
|
||||
import io.ebean.DatabaseFactory;
|
||||
|
||||
/**
|
||||
* The configuration used for creating a Database.
|
||||
* <p>
|
||||
* Used to programmatically construct an Database and optionally register it
|
||||
* with the DB singleton.
|
||||
* </p>
|
||||
* <p>
|
||||
* If you just use DB thout this programmatic configuration Ebean will read
|
||||
* the application.properties file and take the configuration from there. This usually
|
||||
* includes searching the class path and automatically registering any entity
|
||||
* classes and listeners etc.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* DatabaseConfig config = new DatabaseConfig();
|
||||
*
|
||||
* // read the ebean.properties and load
|
||||
* // those settings into this serverConfig object
|
||||
* config.loadFromProperties();
|
||||
*
|
||||
* // explicitly register the entity beans to avoid classpath scanning
|
||||
* config.addClass(Customer.class);
|
||||
* config.addClass(User.class);
|
||||
*
|
||||
* Database db = DatabaseFactory.create(config);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Note that ServerConfigProvider provides a standard Java ServiceLoader mechanism that can
|
||||
* be used to apply configuration to the DatabaseConfig.
|
||||
* </p>
|
||||
*
|
||||
* @author emcgreal
|
||||
* @author rbygrave
|
||||
* @see DatabaseFactory
|
||||
*/
|
||||
public class DatabaseConfig extends ServerConfig {
|
||||
|
||||
}
|
||||
@@ -140,7 +140,7 @@ public class DbMigrationConfig {
|
||||
* <p>
|
||||
* The default of "dbmigration" is reasonable in most cases. You may look to set this
|
||||
* to be something like "dbmigration/myapp" where myapp gives it a unique resource path
|
||||
* in the case there are multiple EbeanServer applications in the single classpath.
|
||||
* in the case there are multiple Database applications in the single classpath.
|
||||
* </p>
|
||||
*/
|
||||
public void setMigrationPath(String migrationPath) {
|
||||
|
||||
@@ -47,6 +47,11 @@ public class PlatformConfig {
|
||||
*/
|
||||
private DbUuid dbUuid = DbUuid.AUTO_VARCHAR;
|
||||
|
||||
/**
|
||||
* Set to true to force InetAddress to map to Varchar (for Postgres rather than INET)
|
||||
*/
|
||||
private boolean databaseInetAddressVarchar;
|
||||
|
||||
/**
|
||||
* Modify the default mapping of standard types such as default precision for DECIMAL etc.
|
||||
*/
|
||||
@@ -181,6 +186,20 @@ public class PlatformConfig {
|
||||
this.idType = idType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if InetAddress should map to varchar column (rather than Postgres INET).
|
||||
*/
|
||||
public boolean isDatabaseInetAddressVarchar() {
|
||||
return databaseInetAddressVarchar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to force InetAddress to map to varchar column.
|
||||
*/
|
||||
public void setDatabaseInetAddressVarchar(boolean databaseInetAddressVarchar) {
|
||||
this.databaseInetAddressVarchar = databaseInetAddressVarchar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom type mapping.
|
||||
* <p>
|
||||
@@ -235,6 +254,7 @@ public class PlatformConfig {
|
||||
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize);
|
||||
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
|
||||
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
|
||||
databaseInetAddressVarchar = p.getBoolean("databaseInetAddressVarchar", databaseInetAddressVarchar);
|
||||
|
||||
DbUuid dbUuid = p.getEnum(DbUuid.class, "dbuuid", null);
|
||||
if (dbUuid != null) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebean.config;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import io.ebean.EbeanServerFactory;
|
||||
import io.ebean.DatabaseFactory;
|
||||
import io.ebean.PersistenceContextScope;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.Transaction;
|
||||
@@ -32,6 +32,7 @@ import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.migration.MigrationRunner;
|
||||
import io.ebean.util.StringHelper;
|
||||
|
||||
import javax.persistence.EnumType;
|
||||
import javax.sql.DataSource;
|
||||
import java.time.Clock;
|
||||
import java.util.ArrayList;
|
||||
@@ -44,30 +45,30 @@ import java.util.Properties;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* The configuration used for creating a EbeanServer.
|
||||
* The configuration used for creating a Database.
|
||||
* <p>
|
||||
* Used to programmatically construct an EbeanServer and optionally register it
|
||||
* with the Ebean singleton.
|
||||
* Used to programmatically construct a Database and optionally register it
|
||||
* with the DB singleton.
|
||||
* </p>
|
||||
* <p>
|
||||
* If you just use Ebean without this programmatic configuration Ebean will read
|
||||
* the ebean.properties file and take the configuration from there. This usually
|
||||
* If you just use DB without this programmatic configuration DB will read
|
||||
* the application.properties file and take the configuration from there. This usually
|
||||
* includes searching the class path and automatically registering any entity
|
||||
* classes and listeners etc.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* ServerConfig c = new ServerConfig();
|
||||
* ServerConfig config = new ServerConfig();
|
||||
*
|
||||
* // read the ebean.properties and load
|
||||
* // those settings into this serverConfig object
|
||||
* c.loadFromProperties();
|
||||
* config.loadFromProperties();
|
||||
*
|
||||
* // explicitly register the entity beans to avoid classpath scanning
|
||||
* c.addClass(Customer.class);
|
||||
* c.addClass(User.class);
|
||||
* config.addClass(Customer.class);
|
||||
* config.addClass(User.class);
|
||||
*
|
||||
* EbeanServer server = EbeanServerFactory.create(c);
|
||||
* Database database = DatabaseFactory.create(config);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
@@ -78,12 +79,12 @@ import java.util.ServiceLoader;
|
||||
*
|
||||
* @author emcgreal
|
||||
* @author rbygrave
|
||||
* @see EbeanServerFactory
|
||||
* @see DatabaseFactory
|
||||
*/
|
||||
public class ServerConfig {
|
||||
|
||||
/**
|
||||
* The EbeanServer name.
|
||||
* The Database name.
|
||||
*/
|
||||
private String name = "db";
|
||||
|
||||
@@ -106,12 +107,12 @@ public class ServerConfig {
|
||||
private String resourceDirectory;
|
||||
|
||||
/**
|
||||
* Set to true to register this EbeanServer with the Ebean singleton.
|
||||
* Set to true to register this Database with the DB singleton.
|
||||
*/
|
||||
private boolean register = true;
|
||||
|
||||
/**
|
||||
* Set to true if this is the default/primary server.
|
||||
* Set to true if this is the default/primary database.
|
||||
*/
|
||||
private boolean defaultServer = true;
|
||||
|
||||
@@ -150,7 +151,7 @@ public class ServerConfig {
|
||||
private DocStoreConfig docStoreConfig = new DocStoreConfig();
|
||||
|
||||
/**
|
||||
* Set to true when the EbeanServer only uses Document store.
|
||||
* Set to true when the Database only uses Document store.
|
||||
*/
|
||||
private boolean docStoreOnly;
|
||||
|
||||
@@ -229,6 +230,8 @@ public class ServerConfig {
|
||||
|
||||
private int persistBatchSize = 20;
|
||||
|
||||
private EnumType defaultEnumType = EnumType.ORDINAL;
|
||||
|
||||
private boolean disableLazyLoading;
|
||||
|
||||
/**
|
||||
@@ -252,6 +255,8 @@ public class ServerConfig {
|
||||
|
||||
private boolean ddlRun;
|
||||
|
||||
private boolean ddlExtra = true;
|
||||
|
||||
private boolean ddlCreateOnly;
|
||||
|
||||
private String ddlInitSql;
|
||||
@@ -348,7 +353,7 @@ public class ServerConfig {
|
||||
/**
|
||||
* Behaviour of updates in JDBC batch to by default include all properties.
|
||||
*/
|
||||
private boolean updateAllPropertiesInBatch = true;
|
||||
private boolean updateAllPropertiesInBatch;
|
||||
|
||||
/**
|
||||
* Default behaviour for updates when cascade save on a O2M or M2M to delete any missing children.
|
||||
@@ -520,7 +525,7 @@ public class ServerConfig {
|
||||
private boolean idGeneratorAutomatic = true;
|
||||
|
||||
/**
|
||||
* Construct a Server Configuration for programmatically creating an EbeanServer.
|
||||
* Construct a Database Configuration for programmatically creating an Database.
|
||||
*/
|
||||
public ServerConfig() {
|
||||
|
||||
@@ -691,14 +696,14 @@ public class ServerConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the EbeanServer.
|
||||
* Return the name of the Database.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the EbeanServer.
|
||||
* Set the name of the Database.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
@@ -707,8 +712,8 @@ public class ServerConfig {
|
||||
/**
|
||||
* Return the container / clustering configuration.
|
||||
* <p/>
|
||||
* The container holds all the EbeanServer instances and provides clustering communication
|
||||
* services to all the EbeanServer instances.
|
||||
* The container holds all the Database instances and provides clustering communication
|
||||
* services to all the Database instances.
|
||||
*/
|
||||
public ContainerConfig getContainerConfig() {
|
||||
return containerConfig;
|
||||
@@ -717,8 +722,8 @@ public class ServerConfig {
|
||||
/**
|
||||
* Set the container / clustering configuration.
|
||||
* <p/>
|
||||
* The container holds all the EbeanServer instances and provides clustering communication
|
||||
* services to all the EbeanServer instances.
|
||||
* The container holds all the Database instances and provides clustering communication
|
||||
* services to all the Database instances.
|
||||
*/
|
||||
public void setContainerConfig(ContainerConfig containerConfig) {
|
||||
this.containerConfig = containerConfig;
|
||||
@@ -758,8 +763,8 @@ public class ServerConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set false if you do not want this EbeanServer to be registered as the "default" server
|
||||
* with the Ebean singleton.
|
||||
* Set false if you do not want this Database to be registered as the "default" database
|
||||
* with the DB singleton.
|
||||
* <p>
|
||||
* This is only used when {@link #setRegister(boolean)} is also true.
|
||||
* </p>
|
||||
@@ -976,6 +981,14 @@ public class ServerConfig {
|
||||
this.queryBatchSize = queryBatchSize;
|
||||
}
|
||||
|
||||
public EnumType getDefaultEnumType() {
|
||||
return defaultEnumType;
|
||||
}
|
||||
|
||||
public void setDefaultEnumType(EnumType defaultEnumType) {
|
||||
this.defaultEnumType = defaultEnumType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if lazy loading is disabled on queries by default.
|
||||
*/
|
||||
@@ -1545,14 +1558,14 @@ public class ServerConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this EbeanServer is a Document store only instance (has no JDBC DB).
|
||||
* Return true if this Database is a Document store only instance (has no JDBC DB).
|
||||
*/
|
||||
public boolean isDocStoreOnly() {
|
||||
return docStoreOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if this EbeanServer is Document store only instance (has no JDBC DB).
|
||||
* Set to true if this Database is Document store only instance (has no JDBC DB).
|
||||
*/
|
||||
public void setDocStoreOnly(boolean docStoreOnly) {
|
||||
this.docStoreOnly = docStoreOnly;
|
||||
@@ -1952,16 +1965,16 @@ public class ServerConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the EbeanServer instance should be created in offline mode.
|
||||
* Return true if the Database instance should be created in offline mode.
|
||||
*/
|
||||
public boolean isDbOffline() {
|
||||
return dbOffline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if the EbeanServer instance should be created in offline mode.
|
||||
* Set to true if the Database instance should be created in offline mode.
|
||||
* <p>
|
||||
* Typically used to create an EbeanServer instance for DDL Migration generation
|
||||
* Typically used to create an Database instance for DDL Migration generation
|
||||
* without requiring a real DataSource / Database to connect to.
|
||||
* </p>
|
||||
*/
|
||||
@@ -2116,6 +2129,16 @@ public class ServerConfig {
|
||||
this.ddlRun = ddlRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to false if you not want to run the extra-ddl.xml scripts. (default = true)
|
||||
* <p>
|
||||
* Typically we want this on when we are running tests.
|
||||
*/
|
||||
public void setDdlExtra(boolean ddlExtra) {
|
||||
this.ddlExtra = ddlExtra;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if the "drop all ddl" should be skipped.
|
||||
* <p>
|
||||
@@ -2186,6 +2209,13 @@ public class ServerConfig {
|
||||
return ddlRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true, if extra-ddl.xml should be executed.
|
||||
*/
|
||||
public boolean isDdlExtra() {
|
||||
return ddlExtra;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the class path search should be disabled.
|
||||
*/
|
||||
@@ -2195,7 +2225,7 @@ public class ServerConfig {
|
||||
|
||||
/**
|
||||
* Set to true to disable the class path search even for the case where no entity bean classes
|
||||
* have been registered. This can be used to start an EbeanServer instance just to use the
|
||||
* have been registered. This can be used to start an Database instance just to use the
|
||||
* SQL functions such as SqlQuery, SqlUpdate etc.
|
||||
*/
|
||||
public void setDisableClasspathSearch(boolean disableClasspathSearch) {
|
||||
@@ -2309,8 +2339,7 @@ public class ServerConfig {
|
||||
*
|
||||
* // assume Customer has L2 bean caching enabled ...
|
||||
*
|
||||
* Transaction transaction = Ebean.beginTransaction();
|
||||
* try {
|
||||
* try (Transaction transaction = DB.beginTransaction()) {
|
||||
*
|
||||
* // this uses L2 bean cache as the transaction
|
||||
* // ... is considered "query only" at this point
|
||||
@@ -2318,7 +2347,7 @@ public class ServerConfig {
|
||||
*
|
||||
* // transaction no longer "query only" once
|
||||
* // ... a bean has been saved etc
|
||||
* Ebean.save(someBean);
|
||||
* DB.save(someBean);
|
||||
*
|
||||
* // will NOT use L2 bean cache as the transaction
|
||||
* // ... is no longer considered "query only"
|
||||
@@ -2337,9 +2366,6 @@ public class ServerConfig {
|
||||
* transaction.setSkipCache(true);
|
||||
* Customer.find.byId(99); // skips l2 bean cache
|
||||
*
|
||||
*
|
||||
* } finally {
|
||||
* transaction.end();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
@@ -2407,7 +2433,7 @@ public class ServerConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the ebeanServer should collection query statistics by ObjectGraphNode.
|
||||
* Return true if query statistics should be collected by ObjectGraphNode.
|
||||
*/
|
||||
public boolean isCollectQueryStatsByNode() {
|
||||
return collectQueryStatsByNode;
|
||||
@@ -2948,6 +2974,7 @@ public class ServerConfig {
|
||||
localTimeWithNanos = p.getBoolean("localTimeWithNanos", localTimeWithNanos);
|
||||
jodaLocalTimeMode = p.get("jodaLocalTimeMode", jodaLocalTimeMode);
|
||||
|
||||
defaultEnumType = p.getEnum(EnumType.class, "defaultEnumType", defaultEnumType);
|
||||
disableLazyLoading = p.getBoolean("disableLazyLoading", disableLazyLoading);
|
||||
lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", lazyLoadBatchSize);
|
||||
queryBatchSize = p.getInt("queryBatchSize", queryBatchSize);
|
||||
@@ -2962,6 +2989,7 @@ public class ServerConfig {
|
||||
|
||||
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
|
||||
ddlRun = p.getBoolean("ddl.run", ddlRun);
|
||||
ddlExtra = p.getBoolean("ddl.extra", ddlExtra);
|
||||
ddlCreateOnly = p.getBoolean("ddl.createOnly", ddlCreateOnly);
|
||||
ddlInitSql = p.get("ddl.initSql", ddlInitSql);
|
||||
ddlSeedSql = p.get("ddl.seedSql", ddlSeedSql);
|
||||
|
||||
@@ -27,6 +27,9 @@ public class DbPlatformTypeMapping {
|
||||
|
||||
private static final DbPlatformType BOOLEAN_LOGICAL = new BooleanLogicalType();
|
||||
|
||||
private static final DbPlatformType INET_NATIVE = new DbPlatformType("inet", false);
|
||||
private static final DbPlatformType INET_VARCHAR = new DbPlatformType("varchar", 50);
|
||||
|
||||
private static final DbPlatformType UUID_NATIVE = new DbPlatformType("uuid", false);
|
||||
@SuppressWarnings("unused")
|
||||
private static final DbPlatformType UUID_PLACEHOLDER = new DbPlatformType("uuidPlaceholder");
|
||||
@@ -106,6 +109,7 @@ public class DbPlatformTypeMapping {
|
||||
put(DbType.JSONBLOB, new DbPlatformType("jsonblob"));
|
||||
put(DbType.JSONVARCHAR, new DbPlatformType("jsonvarchar", 1000));
|
||||
put(DbType.UUID, UUID_NATIVE);
|
||||
put(DbType.INET, INET_NATIVE);
|
||||
|
||||
} else {
|
||||
put(DbType.VARCHAR, new DbPlatformType("varchar", 255));
|
||||
@@ -121,6 +125,7 @@ public class DbPlatformTypeMapping {
|
||||
put(DbType.JSONVARCHAR, JSON_VARCHAR_PLACEHOLDER);
|
||||
// default to native UUID and override on platform configure()
|
||||
put(DbType.UUID, UUID_NATIVE);
|
||||
put(DbType.INET, INET_VARCHAR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ public enum DbType {
|
||||
ARRAY(Types.ARRAY),
|
||||
|
||||
UUID(ExtraDbTypes.UUID),
|
||||
INET(ExtraDbTypes.INET),
|
||||
CDIR(ExtraDbTypes.CDIR),
|
||||
|
||||
POINT(ExtraDbTypes.POINT),
|
||||
POLYGON(ExtraDbTypes.POLYGON),
|
||||
|
||||
@@ -40,6 +40,9 @@ public interface ExtraDbTypes {
|
||||
*/
|
||||
int JSONBlob = 5005;
|
||||
|
||||
int INET = 5020;
|
||||
int CDIR = 5021;
|
||||
|
||||
/**
|
||||
* Geo Point
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package io.ebean.config.dbplatform.clickhouse;
|
||||
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.config.dbplatform.DbType;
|
||||
import io.ebean.config.dbplatform.IdType;
|
||||
|
||||
import java.sql.Types;
|
||||
|
||||
public class ClickHousePlatform extends DatabasePlatform {
|
||||
|
||||
public ClickHousePlatform() {
|
||||
super();
|
||||
this.platform = Platform.CLICKHOUSE;
|
||||
//this.dbEncrypt =
|
||||
//this.historySupport =
|
||||
//this.exceptionTranslator =
|
||||
|
||||
this.nativeUuidType = true;
|
||||
this.dbDefaultValue.setNow("now()");
|
||||
this.columnAliasPrefix = null;
|
||||
|
||||
this.dbIdentity.setIdType(IdType.IDENTITY);
|
||||
this.dbIdentity.setSupportsGetGeneratedKeys(false);
|
||||
this.dbIdentity.setSupportsSequence(false);
|
||||
this.dbIdentity.setSupportsIdentity(true);
|
||||
|
||||
this.booleanDbType = Types.INTEGER;
|
||||
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("UInt8"));
|
||||
|
||||
// using unsigned as default types ...
|
||||
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("UInt8", false));
|
||||
dbTypeMap.put(DbType.SMALLINT, new DbPlatformType("UInt16", false));
|
||||
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("UInt32", false));
|
||||
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("UInt64", false));
|
||||
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("Decimal", 16,4));
|
||||
dbTypeMap.put(DbType.DOUBLE, new DbPlatformType("Float64", false));
|
||||
|
||||
dbTypeMap.put(DbType.DATE, new DbPlatformType("Date", false));
|
||||
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("DateTime", false));
|
||||
|
||||
dbTypeMap.put(DbType.VARCHAR, new DbPlatformType("String", false));
|
||||
dbTypeMap.put(DbType.LONGVARCHAR, new DbPlatformType("String", false));
|
||||
dbTypeMap.put(DbType.CLOB, new DbPlatformType("String", false));
|
||||
dbTypeMap.put(DbType.JSONVARCHAR, new DbPlatformType("String", false));
|
||||
|
||||
dbTypeMap.put(DbType.UUID, new DbPlatformType("UUID", false));
|
||||
dbTypeMap.put(DbType.INET, new DbPlatformType("String", false));
|
||||
dbTypeMap.put(DbType.CDIR, new DbPlatformType("String", false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNativeArrayType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -60,6 +60,7 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
DbPlatformType dbBytea = new DbPlatformType("bytea", false);
|
||||
|
||||
dbTypeMap.put(DbType.UUID, new DbPlatformType("uuid", false));
|
||||
dbTypeMap.put(DbType.INET, new DbPlatformType("inet", false));
|
||||
dbTypeMap.put(DbType.HSTORE, new DbPlatformType("hstore", false));
|
||||
dbTypeMap.put(DbType.JSON, new DbPlatformType("json", false));
|
||||
dbTypeMap.put(DbType.JSONB, new DbPlatformType("jsonb", false));
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
|
||||
<TITLE>Configuration settings for EbeanServer construction</TITLE>
|
||||
<TITLE>Configuration settings for Database construction</TITLE>
|
||||
</HEAD>
|
||||
<Body BGCOLOR="#ffffff">
|
||||
Configuration settings for EbeanServer construction
|
||||
Configuration settings for Database construction
|
||||
|
||||
</Body>
|
||||
</HTML>
|
||||
|
||||
@@ -97,6 +97,8 @@ class Loader {
|
||||
if (fileName != null) {
|
||||
loadWithExtensionCheck(fileName);
|
||||
}
|
||||
} else {
|
||||
loadWithExtensionCheck(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,11 @@ public interface DbMigration {
|
||||
throw new IllegalStateException("No service implementation found for DbMigration?");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to false to suppress logging to System out.
|
||||
*/
|
||||
void setLogToSystemOut(boolean logToSystemOut);
|
||||
|
||||
/**
|
||||
* Set the path from the current working directory to the application resources.
|
||||
* <p>
|
||||
|
||||
@@ -9,7 +9,7 @@ package io.ebean.event;
|
||||
* <p>
|
||||
* Note that getTransaction() on the PersistRequest returns the transaction used
|
||||
* for the insert, update, delete or fetch. To explicitly use this same
|
||||
* transaction you should use this transaction via methods on EbeanServer.
|
||||
* transaction you should use this transaction via methods on Database.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebean.event;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.Database;
|
||||
|
||||
/**
|
||||
* Fired after a bean is constructed, but not yet loaded from database.
|
||||
@@ -9,7 +9,7 @@ import io.ebean.EbeanServer;
|
||||
* properties will get unload. Use {@link BeanPostLoad} instead.
|
||||
* <p>
|
||||
* it's intended to do some dependency-injection here.
|
||||
* If you plan to use this feature you should use {@link EbeanServer#createEntityBean(Class)}
|
||||
* If you plan to use this feature you should use {@link Database#createEntityBean(Class)}
|
||||
* to create new beans.
|
||||
* </p>
|
||||
*/
|
||||
@@ -32,7 +32,7 @@ public interface BeanPostConstructListener {
|
||||
void postConstruct(Object bean);
|
||||
|
||||
/**
|
||||
* Called after {@link EbeanServer#createEntityBean(Class)}. Only for new beans.
|
||||
* Called after {@link Database#createEntityBean(Class)}. Only for new beans.
|
||||
* intended to set default values here.
|
||||
*/
|
||||
void postCreate(Object bean);
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.ebean.meta;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides access to the meta data in EbeanServer such as query execution statistics.
|
||||
* Provides access to the meta data in Database such as query execution statistics.
|
||||
*/
|
||||
public interface MetaInfoManager {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
* Meta data that can be retrieved for the EbeanServer.
|
||||
* Meta data that can be retrieved for a Database instance.
|
||||
*/
|
||||
package io.ebean.meta;
|
||||
|
||||
@@ -3,130 +3,24 @@
|
||||
<title>Ebean API</title>
|
||||
</head>
|
||||
<body BGCOLOR="#ffffff">
|
||||
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>).
|
||||
Ebean Object Relational Mapping -
|
||||
<a href='io/ebean/Database.html'>Database</a> or <a href='io/ebean/DB.html'>DB</a>).
|
||||
|
||||
|
||||
<h3>Ebean</h3>
|
||||
<h4><a href='io/ebean/Database.html'>Database</a></h4>
|
||||
<p>
|
||||
Provides the main API for fetching and persisting beans with Ebean.
|
||||
Database provides the main API for fetching and persisting beans.
|
||||
</p>
|
||||
|
||||
<h4><a href='io/ebean/DB.html'>DB</a></h4>
|
||||
<p>
|
||||
For a full description of the query language refer to <a href="com/avaje/ebean/Query.html">Query</a>.
|
||||
DB holds a registry of Database instances by name.
|
||||
</p>
|
||||
|
||||
<h4><a href='https://ebean.io/docs/query'>Query</a></h4>
|
||||
<p>
|
||||
|
||||
Review the documentation for the query capabilities.
|
||||
</p>
|
||||
<div id="overviewexamples">
|
||||
<h3>
|
||||
EXAMPLE 1: Simple fetch
|
||||
</h3>
|
||||
<pre>{@code
|
||||
// fetch order 10
|
||||
Order order = Ebean.find(Order.class, 10);
|
||||
}</pre>
|
||||
|
||||
<h3>
|
||||
EXAMPLE 2: Fetch an Object with associations
|
||||
</h3>
|
||||
<pre>{@code
|
||||
// fetch Customer 7 including their billing and shipping addresses
|
||||
Customer customer = Ebean.find(Customer.class)
|
||||
.fetch("billingAddress");
|
||||
.fetch("shippingAddress");
|
||||
.setId(7)
|
||||
.findOne();
|
||||
|
||||
|
||||
Address billAddr = customer.getBillingAddress();
|
||||
Address shipAddr = customer.getShippingAddress();
|
||||
}</pre>
|
||||
|
||||
<h3>
|
||||
EXAMPLE 3: Fetch a list of Objects with associations
|
||||
</h3>
|
||||
<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);
|
||||
.fetch("customer")
|
||||
.fetch("customer.shippingAddress")
|
||||
.fetch("details")
|
||||
.fetch("details.product","name")
|
||||
.where().eq("customer.id",2)
|
||||
.findList();
|
||||
|
||||
|
||||
// Note: Only the product id and name is fetched for the
|
||||
// product details. This is referred to as a
|
||||
// "Partial Object" (one that is partially populated).
|
||||
|
||||
|
||||
// code that traverses the object graph...
|
||||
|
||||
Order order = orderList.get(0);
|
||||
Customer customer = order.getCustomer();
|
||||
Address shipAddr = customer.getShippingAddress();
|
||||
|
||||
List<OrderDetail> details = order.getDetails();
|
||||
OrderDetail detail = details.get(0);
|
||||
Product product = detail.getProduct();
|
||||
String productName = product.getName();
|
||||
|
||||
}</pre>
|
||||
|
||||
<h3>
|
||||
EXAMPLE 4: Create and save an Order
|
||||
</h3>
|
||||
<pre>{@code
|
||||
// get a Customer reference so we don't hit the database
|
||||
Customer custRef = Ebean.getReference(Customer.class, 7);
|
||||
|
||||
// create a new Order object
|
||||
Order newOrder = new Order();
|
||||
newOrder.setStatus(Order.Status.NEW);
|
||||
newOrder.setCustomer(custRef);
|
||||
|
||||
ArrayList orderLines = new ArrayList();
|
||||
newOrder.setLines(orderLines);
|
||||
...
|
||||
|
||||
// add a line to the order
|
||||
Product prodRef = Ebean.getReference(Product.class, 41);
|
||||
OrderLine line = new OrderLine();
|
||||
line.setProduct(prodRef);
|
||||
line.setQuantity(10);
|
||||
orderLines.add(line);
|
||||
...
|
||||
|
||||
// save the order and its lines in a single transaction
|
||||
// NB: assumes CascadeType.PERSIST is set on the order lines association
|
||||
Ebean.save(newOrder);
|
||||
|
||||
}</pre>
|
||||
|
||||
<h3>
|
||||
EXAMPLE 5: Use another database
|
||||
</h3>
|
||||
<pre>{@code
|
||||
// Get access to the Human Resources EbeanServer/Database
|
||||
EbeanServer hrServer = Ebean.getServer("HR");
|
||||
|
||||
|
||||
// fetch contact 3 from the HR database
|
||||
Contact contact = hrServer.find(Contact.class, 3);
|
||||
|
||||
contact.setStatus(Contact.Status.INACTIVE);
|
||||
...
|
||||
|
||||
// save the contact back to the HR database
|
||||
hrServer.save(contact);
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
@@ -3,29 +3,38 @@
|
||||
<TITLE>Ebean core API</TITLE>
|
||||
</HEAD>
|
||||
<Body BGCOLOR="#ffffff">
|
||||
Core API (see <a href="EbeanServer.html">EbeanServer</a> and <a href="Ebean.html">Ebean</a>).
|
||||
Core API (see <a href="Database.html">Database</a>, <a href="DB.html">DB</a> and <a href="Query.html">Query</a>).
|
||||
|
||||
<h3>Ebean</h3>
|
||||
<h3>Database</h3>
|
||||
<p>
|
||||
Provides the main API for fetching and persisting beans with eBean.
|
||||
Provides the main API for fetching and persisting beans. We can obtain the "default database"
|
||||
via <code>DB.getDefault()</code> or just use methods on DB.
|
||||
</p>
|
||||
|
||||
<pre>{@code
|
||||
// EXAMPLE 1: Simple fetch
|
||||
//========================
|
||||
|
||||
// fetch order 10
|
||||
Order order = Ebean.find(Order.class, 10);
|
||||
// Example find by id
|
||||
|
||||
Order order = DB.find(Order.class, 10);
|
||||
|
||||
|
||||
// Example save
|
||||
|
||||
// EXAMPLE 2: Fetch an Object with associations
|
||||
//=============================================
|
||||
Customer customer = DB.getReference(Customer.class, 42);
|
||||
|
||||
// fetch Customer 7 including their billing and shipping addresses
|
||||
Customer customer =
|
||||
Ebean.find(Customer.class)
|
||||
.setId(7)
|
||||
Order newOrder = new Order();
|
||||
newOrder.setStatus(Order.Status.NEW);
|
||||
newOrder.setCustomer(customer);
|
||||
...
|
||||
|
||||
DB.save(newOrder);
|
||||
|
||||
|
||||
// Example: Eagerly fetching associations
|
||||
|
||||
// fetch Customer 42 including their billing and shipping addresses
|
||||
Customer customer = DB.find(Customer.class)
|
||||
.setId(42)
|
||||
.fetch("billingAddress")
|
||||
.fetch("shippingAddress")
|
||||
.findOne();
|
||||
@@ -33,53 +42,6 @@ Customer customer =
|
||||
Address billAddr = customer.getBillingAddress();
|
||||
Address shipAddr = customer.getShippingAddress();
|
||||
|
||||
|
||||
|
||||
|
||||
// EXAMPLE 3: Create and save an Order
|
||||
//=====================================
|
||||
|
||||
// get a Customer reference so we don't hit the database
|
||||
Customer custRef = Ebean.getReference(Customer.class, 7);
|
||||
|
||||
// create a new Order object
|
||||
Order newOrder = new Order();
|
||||
newOrder.setStatus(Order.Status.NEW);
|
||||
newOrder.setCustomer(custRef);
|
||||
|
||||
ArrayList orderLines = new ArrayList();
|
||||
newOrder.setLines(orderLines);
|
||||
...
|
||||
|
||||
// add a line to the order
|
||||
Product prodRef = Ebean.getReference(Product.class, 41);
|
||||
OrderLine line = new OrderLine();
|
||||
line.setProduct(prodRef);
|
||||
line.setQuantity(10);
|
||||
orderLines.add(line);
|
||||
...
|
||||
|
||||
// save the order and its lines in a single transaction
|
||||
// NB: assumes CascadeType.PERSIST is set on the order lines association
|
||||
Ebean.save(newOrder);
|
||||
|
||||
|
||||
|
||||
// EXAMPLE 4: Use another database
|
||||
//=================================
|
||||
|
||||
// Get access to the Human Resources EbeanServer/Database
|
||||
EbeanServer hrServer = Ebean.getServer("HR");
|
||||
|
||||
|
||||
// fetch contact 3 from the HR database
|
||||
Contact contact = hrServer.find(Contact.class, 3);
|
||||
|
||||
contact.setStatus(Contact.Status.INACTIVE);
|
||||
...
|
||||
|
||||
// save the contact back to the HR database
|
||||
hrServer.save(contact);
|
||||
}</pre>
|
||||
|
||||
</Body>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.ebean.plugin;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
|
||||
/**
|
||||
* Errorhandler to handle load errors and may be recover correct value.
|
||||
*
|
||||
* @author Roland Praml, FOCONIS AG
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface LoadErrorHandler {
|
||||
void handleLoadError(EntityBean bean, Property prop, String fullName, Exception e);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebean.plugin;
|
||||
|
||||
/**
|
||||
* A 'plugin' that wants to be configured on startup so it can use features of the EbeanServer itself.
|
||||
* A 'plugin' that wants to be configured on startup so it can use features of the Database itself.
|
||||
*/
|
||||
public interface Plugin {
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import javax.sql.DataSource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Extensions to EbeanServer API made available to plugins.
|
||||
* Extensions to Database API made available to plugins.
|
||||
*/
|
||||
public interface SpiServer extends EbeanServer {
|
||||
|
||||
@@ -43,12 +43,12 @@ public interface SpiServer extends EbeanServer {
|
||||
BeanType<?> getBeanTypeForQueueId(String queueId);
|
||||
|
||||
/**
|
||||
* Return the associated DataSource for this EbeanServer instance.
|
||||
* Return the associated DataSource for this Database instance.
|
||||
*/
|
||||
DataSource getDataSource();
|
||||
|
||||
/**
|
||||
* Return the associated read only DataSource for this EbeanServer instance (can be null).
|
||||
* Return the associated read only DataSource for this Database instance (can be null).
|
||||
*/
|
||||
DataSource getReadOnlyDataSource();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Provides a API for plugins.
|
||||
* <p>
|
||||
* Plugins can get and read meta data about the entity beans and enhance or utilise the
|
||||
* functionality of the EbeanServer.
|
||||
* functionality of the Database.
|
||||
* </p>
|
||||
*/
|
||||
package io.ebean.plugin;
|
||||
|
||||
@@ -4,7 +4,7 @@ import io.ebean.EbeanServer;
|
||||
import io.ebean.config.ServerConfig;
|
||||
|
||||
/**
|
||||
* Creates the EbeanServer implementations. This is used internally by the EbeanServerFactory and is not currently
|
||||
* Creates the Database implementations. This is used internally by the EbeanServerFactory and is not currently
|
||||
* exposed as public API.
|
||||
*/
|
||||
public interface SpiContainer {
|
||||
|
||||
@@ -6,6 +6,7 @@ package io.ebean.text;
|
||||
public class TextException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1601310159486033148L;
|
||||
private String text;
|
||||
|
||||
/**
|
||||
* Construct with an error message.
|
||||
@@ -27,4 +28,41 @@ public class TextException extends RuntimeException {
|
||||
public TextException(Exception e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a detailed exception.
|
||||
*
|
||||
* @param message
|
||||
* the message. The placeholder {} will be replaced by
|
||||
* <code>text</code>
|
||||
* @param text
|
||||
* the fault text.
|
||||
* @param cause
|
||||
* the case
|
||||
*/
|
||||
public TextException(String message, String text, Exception cause) {
|
||||
super(message.replace("{}", String.valueOf(text)), cause);
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a detailed exception.
|
||||
*
|
||||
* @param message
|
||||
* the message. The placeholder {} will be replaced by
|
||||
* <code>text</code>
|
||||
* @param text
|
||||
* the fault text.
|
||||
*/
|
||||
public TextException(String message, String text) {
|
||||
super(message.replace("{}", String.valueOf(text)));
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text, that caused the error. (e.g. the JSON). May be null.
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import java.util.Locale;
|
||||
*
|
||||
* FileReader reader = new FileReader(f);
|
||||
*
|
||||
* CsvReader<Customer> csvReader = Ebean.createCsvReader(Customer.class);
|
||||
* CsvReader<Customer> csvReader = DB.createCsvReader(Customer.class);
|
||||
*
|
||||
* csvReader.setPersistBatchSize(20);
|
||||
*
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* <pre>{@code
|
||||
* // find some customers ...
|
||||
*
|
||||
* List<Customer> list = Ebean.find(Customer.class)
|
||||
* List<Customer> list = DB.find(Customer.class)
|
||||
* .select("id, name, status, shippingAddress")
|
||||
* .fetch("billingAddress","line1, city")
|
||||
* .fetch("billingAddress.country", "*")
|
||||
@@ -18,7 +18,7 @@
|
||||
* .order().desc("id")
|
||||
* .findList();
|
||||
*
|
||||
* JsonContext json = Ebean.json();
|
||||
* JsonContext json = DB.json();
|
||||
*
|
||||
* // output as a JSON string
|
||||
* String jsonOutput = json.toJson(list);
|
||||
|
||||
@@ -41,6 +41,14 @@ public class BindParams implements Serializable {
|
||||
public BindParams() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset positioned parameters (usually due to bind parameter expansion).
|
||||
*/
|
||||
public void reset() {
|
||||
bindHash = null;
|
||||
positionedParameters.clear();
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
int hc = namedParameters.hashCode();
|
||||
for (Param positionedParameter : positionedParameters) {
|
||||
@@ -171,8 +179,7 @@ public class BindParams implements Serializable {
|
||||
}
|
||||
|
||||
private Param getParam(String name) {
|
||||
Param p = namedParameters.computeIfAbsent(name, k -> new Param());
|
||||
return p;
|
||||
return namedParameters.computeIfAbsent(name, k -> new Param());
|
||||
}
|
||||
|
||||
private Param getParam(int position) {
|
||||
|
||||
@@ -11,4 +11,8 @@ public interface CQueryPlanKey {
|
||||
*/
|
||||
String getPartialKey();
|
||||
|
||||
/**
|
||||
* Return a query plan key taking into account persist cascading.
|
||||
*/
|
||||
CQueryPlanKey withDeleteByIds();
|
||||
}
|
||||
|
||||
@@ -197,6 +197,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* Return the label manually set on the query or from the profile location.
|
||||
*/
|
||||
String getPlanLabel();
|
||||
|
||||
/**
|
||||
* Return true if this is a "find by id" query. This includes a check for a single "equal to" expression for the Id.
|
||||
*/
|
||||
@@ -439,6 +444,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
*/
|
||||
void setDelete();
|
||||
|
||||
/**
|
||||
* Set the query to be delete by ids due to cascading delete.
|
||||
*/
|
||||
CQueryPlanKey setDeleteByIdsPlan();
|
||||
|
||||
/**
|
||||
* Set the query to select the id property only.
|
||||
*/
|
||||
@@ -820,6 +830,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
*/
|
||||
boolean isCancelled();
|
||||
|
||||
/**
|
||||
* Return the base table to use if user defined on the query.
|
||||
*/
|
||||
String getBaseTable();
|
||||
|
||||
/**
|
||||
* Return root table alias set by {@link #alias(String)} command.
|
||||
*/
|
||||
@@ -844,4 +859,9 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
* Returns the count distinct order setting.
|
||||
*/
|
||||
CountDistinctOrder getCountDistinctOrder();
|
||||
|
||||
/**
|
||||
* Handles load errors.
|
||||
*/
|
||||
void handleLoadError(String fullName, Exception e);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,11 @@ import io.ebean.SqlUpdate;
|
||||
|
||||
public interface SpiSqlUpdate extends SqlUpdate {
|
||||
|
||||
/**
|
||||
* Return the sql taking into account bind parameter expansion.
|
||||
*/
|
||||
String getBaseSql();
|
||||
|
||||
/**
|
||||
* Return the Bind parameters.
|
||||
*/
|
||||
|
||||
@@ -40,6 +40,7 @@ public class DdlGenerator {
|
||||
|
||||
private final boolean generateDdl;
|
||||
private final boolean runDdl;
|
||||
private final boolean extraDdl;
|
||||
private final boolean createOnly;
|
||||
private final boolean jaxbPresent;
|
||||
private final boolean ddlCommitOnCreateIndex;
|
||||
@@ -54,6 +55,7 @@ public class DdlGenerator {
|
||||
this.server = server;
|
||||
this.jaxbPresent = serverConfig.getClassLoadConfig().isJavaxJAXBPresent();
|
||||
this.generateDdl = serverConfig.isDdlGenerate();
|
||||
this.extraDdl = serverConfig.isDdlExtra();
|
||||
this.createOnly = serverConfig.isDdlCreateOnly();
|
||||
this.dbSchema = serverConfig.getDbSchema();
|
||||
if (!serverConfig.getTenantMode().isDdlEnabled() && serverConfig.isDdlRun()) {
|
||||
@@ -166,8 +168,7 @@ public class DdlGenerator {
|
||||
|
||||
protected void runDropSql(Connection connection) throws IOException {
|
||||
if (!createOnly) {
|
||||
String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl");
|
||||
if (!"true".equalsIgnoreCase(ignoreExtraDdl) && jaxbPresent) {
|
||||
if (extraDdl && jaxbPresent) {
|
||||
String extraApply = ExtraDdlXmlReader.buildExtra(server.getDatabasePlatform().getName(), true);
|
||||
if (extraApply != null) {
|
||||
runScript(connection, false, extraApply, "extra-ddl");
|
||||
@@ -187,9 +188,8 @@ public class DdlGenerator {
|
||||
}
|
||||
runScript(connection, false, createAllContent, getCreateFileName());
|
||||
|
||||
String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl");
|
||||
if (!"true".equalsIgnoreCase(ignoreExtraDdl) && jaxbPresent) {
|
||||
if (currentModel.isTablePartitioning()) {
|
||||
if (extraDdl && jaxbPresent) {
|
||||
if (currentModel().isTablePartitioning()) {
|
||||
String extraPartitioning = ExtraDdlXmlReader.buildPartitioning(server.getDatabasePlatform().getName());
|
||||
if (extraPartitioning != null && !extraPartitioning.isEmpty()) {
|
||||
runScript(connection, false, extraPartitioning, "builtin-partitioning-ddl");
|
||||
@@ -201,7 +201,7 @@ public class DdlGenerator {
|
||||
runScript(connection, false, extraApply, "extra-ddl");
|
||||
}
|
||||
|
||||
if (currentModel.isTablePartitioning()) {
|
||||
if (currentModel().isTablePartitioning()) {
|
||||
checkInitialTablePartitions(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import io.ebean.config.DbMigrationConfig;
|
||||
import io.ebean.config.PlatformConfig;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.clickhouse.ClickHousePlatform;
|
||||
import io.ebean.config.dbplatform.cockroach.CockroachPlatform;
|
||||
import io.ebean.config.dbplatform.db2.DB2Platform;
|
||||
import io.ebean.config.dbplatform.h2.H2Platform;
|
||||
import io.ebean.config.dbplatform.hana.HanaPlatform;
|
||||
@@ -72,6 +74,8 @@ public class DefaultDbMigration implements DbMigration {
|
||||
|
||||
private static final String GENERATED_COMMENT = "THIS IS A GENERATED FILE - DO NOT MODIFY";
|
||||
|
||||
private boolean logToSystemOut = true;
|
||||
|
||||
/**
|
||||
* Set to true if DefaultDbMigration run with online EbeanServer instance.
|
||||
*/
|
||||
@@ -316,6 +320,11 @@ public class DefaultDbMigration implements DbMigration {
|
||||
return generateDiff(request);
|
||||
}
|
||||
|
||||
} catch (UnknownResourcePathException e) {
|
||||
logError("ERROR - " + e.getMessage());
|
||||
logError("Check the working directory or change dbMigration.setPathToResources() value?");
|
||||
return null;
|
||||
|
||||
} finally {
|
||||
if (!online) {
|
||||
DbOffline.reset();
|
||||
@@ -387,8 +396,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
private void writeExtraDdl(File migrationDir, DdlScript script) throws IOException {
|
||||
|
||||
String fullName = repeatableMigrationName(script.isInit(), script.getName());
|
||||
|
||||
logger.info("writing repeatable script {}", fullName);
|
||||
logger.debug("writing repeatable script {}", fullName);
|
||||
|
||||
File file = new File(migrationDir, fullName);
|
||||
try (FileWriter writer = new FileWriter(file)) {
|
||||
@@ -397,6 +405,30 @@ public class DefaultDbMigration implements DbMigration {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogToSystemOut(boolean logToSystemOut) {
|
||||
this.logToSystemOut = logToSystemOut;
|
||||
}
|
||||
|
||||
private void logError(String message) {
|
||||
if (logToSystemOut) {
|
||||
System.out.println("DbMigration> " + message);
|
||||
} else {
|
||||
logger.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
private void logInfo(String message, Object value) {
|
||||
if (value != null) {
|
||||
message = String.format(message, value);
|
||||
}
|
||||
if (logToSystemOut) {
|
||||
System.out.println("DbMigration> " + message);
|
||||
} else {
|
||||
logger.info(message);
|
||||
}
|
||||
}
|
||||
|
||||
private String repeatableMigrationName(boolean init, String scriptName) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (init) {
|
||||
@@ -416,12 +448,12 @@ public class DefaultDbMigration implements DbMigration {
|
||||
|
||||
List<String> pendingDrops = request.getPendingDrops();
|
||||
if (!pendingDrops.isEmpty()) {
|
||||
logger.info("Pending un-applied drops in versions {}", pendingDrops);
|
||||
logInfo("Pending un-applied drops in versions %s", pendingDrops);
|
||||
}
|
||||
|
||||
Migration migration = request.createDiffMigration();
|
||||
if (migration == null) {
|
||||
logger.info("no changes detected - no migration written");
|
||||
logInfo("no changes detected - no migration written", null);
|
||||
return null;
|
||||
} else {
|
||||
// there were actually changes to write
|
||||
@@ -440,7 +472,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
|
||||
List<String> pendingDrops = request.getPendingDrops();
|
||||
if (!pendingDrops.isEmpty()) {
|
||||
logger.info("... remaining pending un-applied drops in versions {}", pendingDrops);
|
||||
logInfo("... remaining pending un-applied drops in versions %s", pendingDrops);
|
||||
}
|
||||
return version;
|
||||
}
|
||||
@@ -520,9 +552,9 @@ public class DefaultDbMigration implements DbMigration {
|
||||
|
||||
String fullVersion = getFullVersion(request.nextVersion(), dropsFor);
|
||||
|
||||
logger.info("generating migration:{}", fullVersion);
|
||||
logInfo("generating migration:%s", fullVersion);
|
||||
if (!request.dbinitMigration && !writeMigrationXml(dbMigration, request.modelDir, fullVersion)) {
|
||||
logger.warn("migration already exists, not generating DDL");
|
||||
logError("migration already exists, not generating DDL");
|
||||
return null;
|
||||
} else {
|
||||
if (!platforms.isEmpty()) {
|
||||
@@ -627,7 +659,6 @@ public class DefaultDbMigration implements DbMigration {
|
||||
if (vanillaPlatform || databasePlatform == null) {
|
||||
// not explicitly set so use the platform of the server
|
||||
databasePlatform = server.getDatabasePlatform();
|
||||
logger.trace("set platform to {}", databasePlatform.getName());
|
||||
}
|
||||
if (migrationConfig != null) {
|
||||
if (strictMode != null) {
|
||||
@@ -713,13 +744,17 @@ public class DefaultDbMigration implements DbMigration {
|
||||
|
||||
// path to src/main/resources in typical maven project
|
||||
File resourceRootDir = new File(pathToResources);
|
||||
if (!resourceRootDir.exists()) {
|
||||
String msg = String.format("Error - path to resources %s does not exist. Absolute path is %s", pathToResources, resourceRootDir.getAbsolutePath());
|
||||
throw new UnknownResourcePathException(msg);
|
||||
}
|
||||
String resourcePath = migrationConfig.getMigrationPath(dbinitMigration);
|
||||
|
||||
// expect to be a path to something like - src/main/resources/dbmigration/model
|
||||
File path = new File(resourceRootDir, resourcePath);
|
||||
if (!path.exists()) {
|
||||
if (!path.mkdirs()) {
|
||||
logger.debug("Unable to ensure migration directory exists at {}", path.getAbsolutePath());
|
||||
logInfo("Warning - Unable to ensure migration directory exists at %s", path.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
return path;
|
||||
@@ -735,7 +770,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
}
|
||||
File modelDir = new File(migrationDirectory, migrationConfig.getModelPath());
|
||||
if (!modelDir.exists() && !modelDir.mkdirs()) {
|
||||
logger.debug("Unable to ensure migration model directory exists at {}", modelDir.getAbsolutePath());
|
||||
logInfo("Warning - Unable to ensure migration model directory exists at %s", modelDir.getAbsolutePath());
|
||||
}
|
||||
return modelDir;
|
||||
}
|
||||
@@ -769,6 +804,11 @@ public class DefaultDbMigration implements DbMigration {
|
||||
return new SQLitePlatform();
|
||||
case HANA:
|
||||
return new HanaPlatform();
|
||||
case COCKROACH:
|
||||
return new CockroachPlatform();
|
||||
case CLICKHOUSE:
|
||||
return new ClickHousePlatform();
|
||||
|
||||
case GENERIC:
|
||||
return new DatabasePlatform();
|
||||
|
||||
|
||||
@@ -33,12 +33,12 @@ class LastMigration {
|
||||
*/
|
||||
static String lastVersion(File migDirectory, File modelDir) {
|
||||
|
||||
List<String> fileNames = new ArrayList<>();
|
||||
List<MigrationVersion> versions = new ArrayList<>();
|
||||
|
||||
File[] sqlFiles = migDirectory.listFiles(pathname -> includeSqlFile(pathname.getName().toLowerCase()));
|
||||
if (sqlFiles != null) {
|
||||
for (File file : sqlFiles) {
|
||||
fileNames.add(trim(file.getName()));
|
||||
versions.add(trimAndParse(file.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,14 +46,14 @@ class LastMigration {
|
||||
File[] xmlFiles = modelDir.listFiles(pathname -> includeModelFile(pathname.getName().toLowerCase()));
|
||||
if (xmlFiles != null) {
|
||||
for (File file : xmlFiles) {
|
||||
fileNames.add(trim(file.getName()));
|
||||
versions.add(trimAndParse(file.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(fileNames);
|
||||
if (!fileNames.isEmpty()) {
|
||||
return fileNames.get(fileNames.size() - 1);
|
||||
Collections.sort(versions);
|
||||
if (!versions.isEmpty()) {
|
||||
return versions.get(versions.size() - 1).asString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -69,23 +69,14 @@ class LastMigration {
|
||||
return lowerFileName.endsWith(MODEL_XML);
|
||||
}
|
||||
|
||||
private static String trim(String name) {
|
||||
name = name.toLowerCase();
|
||||
char c = name.charAt(0);
|
||||
if (c == 'v') {
|
||||
name = name.substring(1);
|
||||
}
|
||||
int p = name.indexOf("__");
|
||||
if (p > -1) {
|
||||
name = name.substring(0, p);
|
||||
}
|
||||
private static MigrationVersion trimAndParse(String name) {
|
||||
if (name.endsWith(SQL)) {
|
||||
name = name.substring(0, name.length() - 4);
|
||||
}
|
||||
if (name.endsWith(MODEL_XML)) {
|
||||
name = name.substring(0, name.length() - 10);
|
||||
}
|
||||
return name;
|
||||
return MigrationVersion.parse(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.ebeaninternal.dbmigration;
|
||||
|
||||
/**
|
||||
* Exception when db migration resource path does not exist.
|
||||
* <p>
|
||||
* Typically the working directory or pathToResources is incorrect.
|
||||
* </p>
|
||||
*/
|
||||
public class UnknownResourcePathException extends RuntimeException {
|
||||
|
||||
public UnknownResourcePathException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,11 @@ public interface DdlBuffer {
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Append a statement allowing for null or empty statements.
|
||||
*/
|
||||
DdlBuffer appendStatement(String content) throws IOException;
|
||||
|
||||
/**
|
||||
* Append DDL content to the buffer.
|
||||
*/
|
||||
|
||||
+12
-1
@@ -38,6 +38,15 @@ public class BaseDdlBuffer implements DdlBuffer {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DdlBuffer appendStatement(String content) throws IOException {
|
||||
if (content != null && !content.isEmpty()) {
|
||||
writer.append(content);
|
||||
endOfStatement();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DdlBuffer append(String content) throws IOException {
|
||||
writer.append(content);
|
||||
@@ -73,7 +82,9 @@ public class BaseDdlBuffer implements DdlBuffer {
|
||||
*/
|
||||
@Override
|
||||
public DdlBuffer end() throws IOException {
|
||||
writer.append("\n");
|
||||
if (!isEmpty()) {
|
||||
writer.append("\n");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
+62
-118
@@ -37,6 +37,8 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.ebeaninternal.dbmigration.ddlgeneration.platform.SplitColumns.split;
|
||||
|
||||
/**
|
||||
* Base implementation for 'create table' and 'alter table' statements.
|
||||
*/
|
||||
@@ -148,8 +150,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
buffer.append("-- NOTE: table has @History - special migration may be necessary").newLine();
|
||||
}
|
||||
for (String ddlScript : before) {
|
||||
buffer.append(translate(ddlScript, tableName, columnName, this.defaultValue));
|
||||
buffer.endOfStatement();
|
||||
buffer.appendStatement(translate(ddlScript, tableName, columnName, this.defaultValue));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,8 +160,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
// here we run post migration scripts
|
||||
for (String ddlScript : after) {
|
||||
buffer.append(translate(ddlScript, tableName, columnName, defaultValue));
|
||||
buffer.endOfStatement();
|
||||
buffer.appendStatement(translate(ddlScript, tableName, columnName, defaultValue));
|
||||
}
|
||||
if (!after.isEmpty()) {
|
||||
buffer.end();
|
||||
@@ -276,6 +276,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
|
||||
apply.newLine().append(")");
|
||||
addTableStorageEngine(apply, createTable);
|
||||
addTableCommentInline(apply, createTable);
|
||||
if (partitionMode != null) {
|
||||
platformDdl.addTablePartition(apply, partitionMode, createTable.getPartitionColumn());
|
||||
@@ -316,12 +317,11 @@ public class BaseTableDdl implements TableDdl {
|
||||
private void addComments(DdlBuffer apply, CreateTable createTable) throws IOException {
|
||||
if (!platformDdl.isInlineComments()) {
|
||||
String tableComment = createTable.getComment();
|
||||
if (!StringHelper.isNull(tableComment)) {
|
||||
if (hasValue(tableComment)) {
|
||||
platformDdl.addTableComment(apply, createTable.getName(), tableComment);
|
||||
}
|
||||
|
||||
List<Column> columns = createTable.getColumn();
|
||||
for (Column column : columns) {
|
||||
for (Column column : createTable.getColumn()) {
|
||||
if (!StringHelper.isNull(column.getComment())) {
|
||||
platformDdl.addColumnComment(apply, createTable.getName(), column.getName(), column.getComment());
|
||||
}
|
||||
@@ -329,6 +329,15 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the table storage engine clause.
|
||||
*/
|
||||
private void addTableStorageEngine(DdlBuffer apply, CreateTable createTable) throws IOException {
|
||||
if (platformDdl.isIncludeStorageEngine()) {
|
||||
platformDdl.tableStorageEngine(apply, createTable.getStorageEngine());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the table comment inline with the create table statement.
|
||||
*/
|
||||
@@ -358,26 +367,17 @@ public class BaseTableDdl implements TableDdl {
|
||||
uqName = col.getUnique();
|
||||
}
|
||||
String[] columnNames = {col.getName()};
|
||||
write.apply()
|
||||
.append(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, Boolean.TRUE.equals(col.isNotnull()) ? null : columnNames))
|
||||
.endOfStatement();
|
||||
|
||||
write.dropAllForeignKeys()
|
||||
.append(platformDdl.dropIndex(uqName, tableName))
|
||||
.endOfStatement();
|
||||
write.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, Boolean.TRUE.equals(col.isNotnull()) ? null : columnNames));
|
||||
write.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(uqName, tableName));
|
||||
}
|
||||
|
||||
for (UniqueConstraint constraint : externalCompoundUnique) {
|
||||
String uqName = constraint.getName();
|
||||
String[] columnNames = SplitColumns.split(constraint.getColumnNames());
|
||||
String[] nullableColumns = SplitColumns.split(constraint.getNullableColumns());
|
||||
write.apply()
|
||||
.append(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, nullableColumns))
|
||||
.endOfStatement();
|
||||
String[] columnNames = split(constraint.getColumnNames());
|
||||
String[] nullableColumns = split(constraint.getNullableColumns());
|
||||
|
||||
write.dropAllForeignKeys()
|
||||
.append(platformDdl.dropIndex(uqName, tableName))
|
||||
.endOfStatement();
|
||||
write.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, nullableColumns));
|
||||
write.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(uqName, tableName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,9 +394,9 @@ public class BaseTableDdl implements TableDdl {
|
||||
}
|
||||
|
||||
String createSeq = platformDdl.createSequence(seqName, initial, allocate);
|
||||
if (createSeq != null) {
|
||||
if (hasValue(createSeq)) {
|
||||
writer.apply().append(createSeq).newLine();
|
||||
writer.dropAll().append(platformDdl.dropSequence(seqName)).endOfStatement();
|
||||
writer.dropAll().appendStatement(platformDdl.dropSequence(seqName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,8 +425,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void writeInlineCompoundForeignKeys(DdlWrite write, CreateTable createTable) throws IOException {
|
||||
|
||||
List<ForeignKey> foreignKey = createTable.getForeignKey();
|
||||
for (ForeignKey key : foreignKey) {
|
||||
for (ForeignKey key : createTable.getForeignKey()) {
|
||||
String fkConstraint = platformDdl.tableInlineForeignKey(new WriteForeignKey(null, key));
|
||||
write.apply().append(",").newLine().append(" ").append(fkConstraint);
|
||||
}
|
||||
@@ -434,12 +433,10 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void writeAddForeignKeys(DdlWrite write, CreateTable createTable) throws IOException {
|
||||
|
||||
String tableName = createTable.getName();
|
||||
List<Column> columns = createTable.getColumn();
|
||||
for (Column column : columns) {
|
||||
for (Column column : createTable.getColumn()) {
|
||||
String references = column.getReferences();
|
||||
if (hasValue(references)) {
|
||||
writeForeignKey(write, tableName, column);
|
||||
writeForeignKey(write, createTable.getName(), column);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,11 +445,8 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void writeAddCompoundForeignKeys(DdlWrite write, CreateTable createTable) throws IOException {
|
||||
|
||||
String tableName = createTable.getName();
|
||||
|
||||
List<ForeignKey> foreignKey = createTable.getForeignKey();
|
||||
for (ForeignKey key : foreignKey) {
|
||||
writeForeignKey(write, new WriteForeignKey(tableName, key));
|
||||
for (ForeignKey key : createTable.getForeignKey()) {
|
||||
writeForeignKey(write, new WriteForeignKey(createTable.getName(), key));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,19 +460,15 @@ public class BaseTableDdl implements TableDdl {
|
||||
String tableName = lowerTableName(request.table());
|
||||
if (request.indexName() != null) {
|
||||
// no matching unique constraint so add the index
|
||||
fkeyBuffer.append(platformDdl.createIndex(request.indexName(), tableName, request.cols())).endOfStatement();
|
||||
fkeyBuffer.appendStatement(platformDdl.createIndex(request.indexName(), tableName, request.cols()));
|
||||
}
|
||||
|
||||
alterTableAddForeignKey(fkeyBuffer, request);
|
||||
|
||||
fkeyBuffer.end();
|
||||
|
||||
write.dropAllForeignKeys()
|
||||
.append(platformDdl.alterTableDropForeignKey(tableName, request.fkName())).endOfStatement();
|
||||
|
||||
if (request.indexName() != null) {
|
||||
write.dropAllForeignKeys()
|
||||
.append(platformDdl.dropIndex(request.indexName(), tableName)).endOfStatement();
|
||||
write.dropAllForeignKeys().appendStatement(platformDdl.alterTableDropForeignKey(tableName, request.fkName()));
|
||||
if (hasValue(request.indexName())) {
|
||||
write.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(request.indexName(), tableName));
|
||||
}
|
||||
|
||||
write.dropAllForeignKeys().end();
|
||||
@@ -486,10 +476,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void alterTableAddForeignKey(DdlBuffer buffer, WriteForeignKey request) throws IOException {
|
||||
|
||||
String fkConstraint = platformDdl.alterTableAddForeignKey(request);
|
||||
if (fkConstraint != null && !fkConstraint.isEmpty()) {
|
||||
buffer.append(fkConstraint).endOfStatement();
|
||||
}
|
||||
buffer.appendStatement(platformDdl.alterTableAddForeignKey(request));
|
||||
}
|
||||
|
||||
protected void appendColumns(String[] columns, DdlBuffer buffer) throws IOException {
|
||||
@@ -503,13 +490,12 @@ public class BaseTableDdl implements TableDdl {
|
||||
buffer.append(")");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add 'drop table' statement to the buffer.
|
||||
*/
|
||||
protected void dropTable(DdlBuffer buffer, String tableName) throws IOException {
|
||||
|
||||
buffer.append(platformDdl.dropTable(tableName)).endOfStatement();
|
||||
buffer.appendStatement(platformDdl.dropTable(tableName));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -517,7 +503,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
*/
|
||||
protected void dropSequence(DdlBuffer buffer, String sequenceName) throws IOException {
|
||||
|
||||
buffer.append(platformDdl.dropSequence(sequenceName)).endOfStatement();
|
||||
buffer.appendStatement(platformDdl.dropSequence(sequenceName));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -525,8 +511,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
*/
|
||||
protected void writeCheckConstraints(DdlBuffer apply, CreateTable createTable) throws IOException {
|
||||
|
||||
List<Column> columns = createTable.getColumn();
|
||||
for (Column column : columns) {
|
||||
for (Column column : createTable.getColumn()) {
|
||||
String checkConstraint = column.getCheckConstraint();
|
||||
if (hasValue(checkConstraint)) {
|
||||
writeCheckConstraint(apply, column, checkConstraint);
|
||||
@@ -548,12 +533,11 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void writeCompoundUniqueConstraints(DdlBuffer apply, CreateTable createTable) throws IOException {
|
||||
|
||||
List<UniqueConstraint> uniqueConstraints = createTable.getUniqueConstraint();
|
||||
boolean inlineUniqueWhenNull = platformDdl.isInlineUniqueWhenNullable();
|
||||
for (UniqueConstraint uniqueConstraint : uniqueConstraints) {
|
||||
for (UniqueConstraint uniqueConstraint : createTable.getUniqueConstraint()) {
|
||||
if (inlineUniqueWhenNull) {
|
||||
String uqName = uniqueConstraint.getName();
|
||||
String[] columns = SplitColumns.split(uniqueConstraint.getColumnNames());
|
||||
String[] columns = split(uniqueConstraint.getColumnNames());
|
||||
apply.append(",").newLine();
|
||||
apply.append(" constraint ").append(uqName).append(" unique");
|
||||
appendColumns(columns, apply);
|
||||
@@ -650,54 +634,34 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
@Override
|
||||
public void generate(DdlWrite writer, CreateIndex createIndex) throws IOException {
|
||||
|
||||
String[] cols = SplitColumns.split(createIndex.getColumns());
|
||||
writer.apply()
|
||||
.append(platformDdl.createIndex(createIndex.getIndexName(), createIndex.getTableName(), cols))
|
||||
.endOfStatement();
|
||||
|
||||
writer.dropAll()
|
||||
.append(platformDdl.dropIndex(createIndex.getIndexName(), createIndex.getTableName()))
|
||||
.endOfStatement();
|
||||
writer.apply().appendStatement(platformDdl.createIndex(createIndex.getIndexName(), createIndex.getTableName(), split(createIndex.getColumns())));
|
||||
writer.dropAll().appendStatement(platformDdl.dropIndex(createIndex.getIndexName(), createIndex.getTableName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(DdlWrite writer, DropIndex dropIndex) throws IOException {
|
||||
|
||||
writer.apply()
|
||||
.append(platformDdl.dropIndex(dropIndex.getIndexName(), dropIndex.getTableName()))
|
||||
.endOfStatement();
|
||||
writer.apply().appendStatement(platformDdl.dropIndex(dropIndex.getIndexName(), dropIndex.getTableName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(DdlWrite writer, AddUniqueConstraint constraint) throws IOException {
|
||||
|
||||
if (DdlHelp.isDropConstraint(constraint.getColumnNames())) {
|
||||
String ddl = platformDdl.alterTableDropUniqueConstraint(constraint.getTableName(), constraint.getConstraintName());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropUniqueConstraint(constraint.getTableName(), constraint.getConstraintName()));
|
||||
|
||||
} else {
|
||||
String[] cols = SplitColumns.split(constraint.getColumnNames());
|
||||
String[] nullableColumns = SplitColumns.split(constraint.getNullableColumns());
|
||||
String ddl = platformDdl.alterTableAddUniqueConstraint(constraint.getTableName(), constraint.getConstraintName(), cols, nullableColumns);
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
String[] cols = split(constraint.getColumnNames());
|
||||
String[] nullableColumns = split(constraint.getNullableColumns());
|
||||
writer.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(constraint.getTableName(), constraint.getConstraintName(), cols, nullableColumns));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(DdlWrite writer, AlterForeignKey alterForeignKey) throws IOException {
|
||||
if (DdlHelp.isDropForeignKey(alterForeignKey.getColumnNames())) {
|
||||
String ddl = platformDdl.alterTableDropForeignKey(alterForeignKey.getTableName(), alterForeignKey.getName());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropForeignKey(alterForeignKey.getTableName(), alterForeignKey.getName()));
|
||||
} else {
|
||||
String ddl = platformDdl.alterTableAddForeignKey(new WriteForeignKey(alterForeignKey));
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterTableAddForeignKey(new WriteForeignKey(alterForeignKey)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -907,7 +871,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
String ddl = platformDdl.alterColumnBaseAttributes(alter);
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
writer.apply().appendStatement(ddl);
|
||||
|
||||
if (isTrue(alter.isWithHistory()) && alter.getType() != null && historySupport == HistorySupport.TRIGGER_BASED) {
|
||||
// mysql and sql server column type change allowing nulls in the history table column
|
||||
@@ -919,53 +883,41 @@ public class BaseTableDdl implements TableDdl {
|
||||
String histColumnDdl = platformDdl.alterColumnBaseAttributes(alterHistoryColumn);
|
||||
|
||||
// write the apply to history table
|
||||
writer.apply().append(histColumnDdl).endOfStatement();
|
||||
writer.apply().appendStatement(histColumnDdl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void alterColumnDefaultValue(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterColumnDefaultValue(alter.getTableName(), alter.getColumnName(), alter.getDefaultValue());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterColumnDefaultValue(alter.getTableName(), alter.getColumnName(), alter.getDefaultValue()));
|
||||
}
|
||||
|
||||
protected void dropCheckConstraint(DdlWrite writer, AlterColumn alter, String constraintName) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterTableDropConstraint(alter.getTableName(), constraintName);
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropConstraint(alter.getTableName(), constraintName));
|
||||
}
|
||||
|
||||
protected void addCheckConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterTableAddCheckConstraint(alter.getTableName(), alter.getCheckConstraintName(), alter.getCheckConstraint());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterTableAddCheckConstraint(alter.getTableName(), alter.getCheckConstraintName(), alter.getCheckConstraint()));
|
||||
}
|
||||
|
||||
protected void alterColumnNotnull(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterColumnNotnull(alter.getTableName(), alter.getColumnName(), alter.isNotnull());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
}
|
||||
writer.apply().appendStatement(platformDdl.alterColumnNotnull(alter.getTableName(), alter.getColumnName(), alter.isNotnull()));
|
||||
}
|
||||
|
||||
protected void alterColumnType(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
String ddl = platformDdl.alterColumnType(alter.getTableName(), alter.getColumnName(), alter.getType());
|
||||
if (hasValue(ddl)) {
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
writer.apply().appendStatement(ddl);
|
||||
if (isTrue(alter.isWithHistory()) && historySupport == HistorySupport.TRIGGER_BASED) {
|
||||
regenerateHistoryTriggers(alter.getTableName(), HistoryTableUpdate.Change.ALTER, alter.getColumnName());
|
||||
// apply same type change to matching column in the history table
|
||||
ddl = platformDdl.alterColumnType(historyTable(alter.getTableName()), alter.getColumnName(), alter.getType());
|
||||
writer.apply().append(ddl).endOfStatement();
|
||||
writer.apply().appendStatement(ddl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -977,17 +929,13 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void alterColumnDropForeignKey(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
writer.apply()
|
||||
.append(platformDdl.alterTableDropForeignKey(alter.getTableName(), alter.getDropForeignKey()))
|
||||
.endOfStatement();
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropForeignKey(alter.getTableName(), alter.getDropForeignKey()));
|
||||
}
|
||||
|
||||
|
||||
protected void alterColumnDropUniqueConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
writer.apply()
|
||||
.append(platformDdl.alterTableDropUniqueConstraint(alter.getTableName(), alter.getDropUnique()))
|
||||
.endOfStatement();
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropUniqueConstraint(alter.getTableName(), alter.getDropUnique()));
|
||||
}
|
||||
|
||||
protected void alterColumnAddUniqueOneToOneConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
@@ -1004,13 +952,9 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
String[] cols = {alter.getColumnName()};
|
||||
boolean notNull = alter.isNotnull() != null ? alter.isNotnull() : Boolean.TRUE.equals(alter.isNotnull());
|
||||
writer.apply()
|
||||
.append(platformDdl.alterTableAddUniqueConstraint(alter.getTableName(), uqName, cols, notNull ? null : cols))
|
||||
.endOfStatement();
|
||||
writer.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(alter.getTableName(), uqName, cols, notNull ? null : cols));
|
||||
|
||||
writer.dropAllForeignKeys()
|
||||
.append(platformDdl.dropIndex(uqName, alter.getTableName()))
|
||||
.endOfStatement();
|
||||
writer.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(uqName, alter.getTableName()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Logical array type to ClickHouse array type conversion.
|
||||
*/
|
||||
class ClickHouseDbArray {
|
||||
|
||||
private static final Map<String, String> mapping = new HashMap<>();
|
||||
static {
|
||||
mapping.put("uuid[]", "Array(UUID)");
|
||||
mapping.put("varchar[]", "Array(String)");
|
||||
mapping.put("integer[]", "Array(UInt32)");
|
||||
mapping.put("bigint[]", "Array(UInt64)");
|
||||
}
|
||||
|
||||
/**
|
||||
* Covert the 'logical' array type to a native one (for Postgres and Cockroach).
|
||||
*/
|
||||
static String logicalToNative(String logicalArrayType) {
|
||||
int colonPos = logicalArrayType.indexOf(':');
|
||||
if (colonPos > -1) {
|
||||
logicalArrayType = logicalArrayType.substring(0, colonPos);
|
||||
}
|
||||
String clickHouseType = mapping.get(logicalArrayType);
|
||||
if (clickHouseType == null) {
|
||||
throw new IllegalStateException("No mapping for logical array type " + logicalArrayType);
|
||||
}
|
||||
return clickHouseType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ClickHouseDdl extends PlatformDdl {
|
||||
|
||||
private static final String LOG_TABLE = "ENGINE = Log()";
|
||||
|
||||
public ClickHouseDdl(DatabasePlatform platform) {
|
||||
super(platform);
|
||||
this.includeStorageEngine = true;
|
||||
this.identitySuffix = "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public DdlHandler createDdlHandler(ServerConfig serverConfig) {
|
||||
return new ClickHouseDdlHandler(serverConfig, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String convertArrayType(String logicalArrayType) {
|
||||
return ClickHouseDbArray.logicalToNative(logicalArrayType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an table storage engine to the create table statement.
|
||||
*/
|
||||
@Override
|
||||
public void tableStorageEngine(DdlBuffer apply, String storageEngine) throws IOException {
|
||||
if (storageEngine == null) {
|
||||
// default to Log() table but really should all be explicit (need arguments for MergeTree etc)
|
||||
storageEngine = LOG_TABLE;
|
||||
}
|
||||
apply.append(" ").append(storageEngine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String alterTableAddForeignKey(WriteForeignKey request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String alterTableDropForeignKey(String tableName, String fkName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tableInlineForeignKey(WriteForeignKey request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dropIndex(String indexName, String tableName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createIndex(String indexName, String tableName, String[] columns) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeColumnNotNull(DdlBuffer buffer) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTableComment(DdlBuffer apply, String tableName, String tableComment) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addColumnComment(DdlBuffer apply, String table, String column, String comment) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInlineComments() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.BaseDdlHandler;
|
||||
|
||||
public class ClickHouseDdlHandler extends BaseDdlHandler {
|
||||
|
||||
public ClickHouseDdlHandler(ServerConfig serverConfig, PlatformDdl platformDdl) {
|
||||
super(serverConfig, platformDdl, new ClickHouseTableDdl(serverConfig, platformDdl));
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import io.ebeaninternal.dbmigration.migration.CreateTable;
|
||||
|
||||
public class ClickHouseTableDdl extends BaseTableDdl {
|
||||
|
||||
public ClickHouseTableDdl(ServerConfig serverConfig, PlatformDdl platformDdl) {
|
||||
super(serverConfig, platformDdl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writePrimaryKeyConstraint(DdlBuffer buffer, String pkName, String[] pkColumns) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeCompoundUniqueConstraints(DdlBuffer apply, CreateTable createTable) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeUniqueConstraints(DdlBuffer apply, CreateTable createTable) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void writeCheckConstraints(DdlBuffer apply, CreateTable createTable) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}
|
||||
@@ -104,7 +104,6 @@ public class MySqlDdl extends PlatformDdl {
|
||||
}
|
||||
buffer.append(String.format(" comment '%s'", comment));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+40
-13
@@ -118,6 +118,8 @@ public class PlatformDdl {
|
||||
*/
|
||||
protected boolean inlineForeignKeys;
|
||||
|
||||
protected boolean includeStorageEngine;
|
||||
|
||||
protected final DbDefaultValue dbDefaultValue;
|
||||
|
||||
protected String fallbackArrayType = "varchar(1000)";
|
||||
@@ -191,6 +193,13 @@ public class PlatformDdl {
|
||||
return inlineComments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the platform includes storage engine clause.
|
||||
*/
|
||||
public boolean isIncludeStorageEngine() {
|
||||
return includeStorageEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if foreign key reference constraints need to inlined with create table.
|
||||
* Ideally we don't do this as then the constraints are not named. Do this for SQLite.
|
||||
@@ -230,13 +239,20 @@ public class PlatformDdl {
|
||||
}
|
||||
}
|
||||
if (isTrue(column.isNotnull()) || isTrue(column.isPrimaryKey())) {
|
||||
buffer.append(" not null");
|
||||
writeColumnNotNull(buffer);
|
||||
}
|
||||
|
||||
// add check constraints later as we really want to give them a nice name
|
||||
// so that the database can potentially provide a nice SQL error
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow for platform overriding (e.g. ClickHouse).
|
||||
*/
|
||||
protected void writeColumnNotNull(DdlBuffer buffer) throws IOException {
|
||||
buffer.append(" not null");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the DB column default literal to platform specific.
|
||||
*/
|
||||
@@ -409,13 +425,17 @@ public class PlatformDdl {
|
||||
}
|
||||
|
||||
protected String translate(ConstraintMode mode) {
|
||||
switch(mode) {
|
||||
case SET_NULL: return "set null";
|
||||
case SET_DEFAULT: return "set default";
|
||||
case RESTRICT: return "restrict";
|
||||
case CASCADE: return "cascade";
|
||||
switch (mode) {
|
||||
case SET_NULL:
|
||||
return "set null";
|
||||
case SET_DEFAULT:
|
||||
return "set default";
|
||||
case RESTRICT:
|
||||
return "restrict";
|
||||
case CASCADE:
|
||||
return "cascade";
|
||||
default:
|
||||
throw new IllegalStateException("Unknown mode "+mode);
|
||||
throw new IllegalStateException("Unknown mode " + mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,7 +485,7 @@ public class PlatformDdl {
|
||||
|
||||
if (!onHistoryTable) {
|
||||
if (isTrue(column.isNotnull())) {
|
||||
buffer.append(" not null");
|
||||
writeColumnNotNull(buffer);
|
||||
}
|
||||
buffer.append(addColumnSuffix);
|
||||
buffer.endOfStatement();
|
||||
@@ -473,7 +493,7 @@ public class PlatformDdl {
|
||||
// check constraints cannot be added in one statement for h2
|
||||
if (!StringHelper.isNull(column.getCheckConstraint())) {
|
||||
String ddl = alterTableAddCheckConstraint(tableName, column.getCheckConstraintName(),
|
||||
column.getCheckConstraint());
|
||||
column.getCheckConstraint());
|
||||
buffer.append(ddl).endOfStatement();
|
||||
}
|
||||
} else {
|
||||
@@ -485,7 +505,7 @@ public class PlatformDdl {
|
||||
|
||||
public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException {
|
||||
buffer.append("alter table ").append(tableName).append(" ").append(dropColumn).append(" ").append(columnName)
|
||||
.append(dropColumnSuffix).endOfStatement();
|
||||
.append(dropColumnSuffix).endOfStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -607,6 +627,13 @@ public class PlatformDdl {
|
||||
// do nothing by default (MySql only)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an table storage engine to the create table statement.
|
||||
*/
|
||||
public void tableStorageEngine(DdlBuffer apply, String storageEngine) throws IOException {
|
||||
// do nothing by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Add table comment as a separate statement (from the create table statement).
|
||||
*/
|
||||
@@ -643,10 +670,10 @@ public class PlatformDdl {
|
||||
|
||||
/**
|
||||
* Shortens the given name to the maximum constraint name length of the platform in a deterministic way.
|
||||
*
|
||||
* <p>
|
||||
* First, all vowels are removed, If the string is still to long, 31 bits are taken from the hash code
|
||||
* of the string and base36 encoded (10 digits and 26 chars) string.
|
||||
*
|
||||
* <p>
|
||||
* As 36^6 > 31^2, the resulting string is never longer as 6 chars.
|
||||
*/
|
||||
protected String maxConstraintName(String name) {
|
||||
@@ -654,7 +681,7 @@ public class PlatformDdl {
|
||||
int hash = name.hashCode() & 0x7FFFFFFF;
|
||||
name = VowelRemover.trim(name, 4);
|
||||
if (name.length() > platform.getMaxConstraintNameLength()) {
|
||||
return name.substring(0, platform.getMaxConstraintNameLength()-7) + "_" + Integer.toString(hash, 36);
|
||||
return name.substring(0, platform.getMaxConstraintNameLength() - 7) + "_" + Integer.toString(hash, 36);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
|
||||
@@ -77,6 +77,8 @@ public class CreateTable {
|
||||
protected BigInteger sequenceAllocate;
|
||||
@XmlAttribute(name = "pkName")
|
||||
protected String pkName;
|
||||
@XmlAttribute(name = "storageEngine")
|
||||
protected String storageEngine;
|
||||
@XmlAttribute(name = "tablespace")
|
||||
protected String tablespace;
|
||||
@XmlAttribute(name = "indexTablespace")
|
||||
@@ -365,6 +367,26 @@ public class CreateTable {
|
||||
this.pkName = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the storageEngine property.
|
||||
*
|
||||
* @return possible object is
|
||||
* {@link String }
|
||||
*/
|
||||
public String getStorageEngine() {
|
||||
return storageEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the storageEngine property.
|
||||
*
|
||||
* @param value allowed object is
|
||||
* {@link String }
|
||||
*/
|
||||
public void setStorageEngine(String value) {
|
||||
this.storageEngine = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the tablespace property.
|
||||
*
|
||||
|
||||
@@ -67,14 +67,14 @@ public class CurrentModel {
|
||||
* Return true if the model contains tables that are partitioned.
|
||||
*/
|
||||
public boolean isTablePartitioning() {
|
||||
return model.isTablePartitioning();
|
||||
return read().isTablePartitioning();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the tables that have partitioning.
|
||||
*/
|
||||
public List<MTable> getPartitionedTables() {
|
||||
return model.getPartitionedTables();
|
||||
return read().getPartitionedTables();
|
||||
}
|
||||
|
||||
private static DbConstraintNaming.MaxLength maxLength(SpiEbeanServer server, DbConstraintNaming naming) {
|
||||
|
||||
@@ -79,6 +79,8 @@ public class MTable {
|
||||
*/
|
||||
private String tablespace;
|
||||
|
||||
private String storageEngine;
|
||||
|
||||
/**
|
||||
* Tablespace to use for indexes on this table.
|
||||
*/
|
||||
@@ -159,6 +161,7 @@ public class MTable {
|
||||
this.name = createTable.getName();
|
||||
this.pkName = createTable.getPkName();
|
||||
this.comment = createTable.getComment();
|
||||
this.storageEngine = createTable.getStorageEngine();
|
||||
this.tablespace = createTable.getTablespace();
|
||||
this.indexTablespace = createTable.getIndexTablespace();
|
||||
this.withHistory = Boolean.TRUE.equals(createTable.isWithHistory());
|
||||
@@ -244,6 +247,7 @@ public class MTable {
|
||||
createTable.setPartitionMode(partitionMeta.getMode().name());
|
||||
createTable.setPartitionColumn(partitionMeta.getProperty());
|
||||
}
|
||||
createTable.setStorageEngine(storageEngine);
|
||||
createTable.setTablespace(tablespace);
|
||||
createTable.setIndexTablespace(indexTablespace);
|
||||
createTable.setSequenceName(sequenceName);
|
||||
@@ -452,6 +456,10 @@ public class MTable {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
public void setStorageEngine(String storageEngine) {
|
||||
this.storageEngine = storageEngine;
|
||||
}
|
||||
|
||||
public String getTablespace() {
|
||||
return tablespace;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
|
||||
}
|
||||
|
||||
MTable table = new MTable(descriptor.getBaseTable());
|
||||
table.setStorageEngine(descriptor.getStorageEngine());
|
||||
table.setPartitionMeta(descriptor.getPartitionMeta());
|
||||
table.setComment(descriptor.getDbComment());
|
||||
if (descriptor.isHistorySupport()) {
|
||||
|
||||
+5
-1
@@ -147,7 +147,11 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
@Override
|
||||
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
|
||||
|
||||
visitScalar(p);
|
||||
if (p instanceof BeanPropertyAssocOne) {
|
||||
visitOneImported((BeanPropertyAssocOne)p);
|
||||
} else {
|
||||
visitScalar(p);
|
||||
}
|
||||
if (embedded.isId()) {
|
||||
// compound primary key
|
||||
lastColumn.setPrimaryKey(true);
|
||||
|
||||
@@ -41,7 +41,7 @@ public class BaseQueryTuner {
|
||||
*/
|
||||
private final boolean skipAll;
|
||||
|
||||
public BaseQueryTuner(AutoTuneConfig config, SpiEbeanServer server, ProfilingListener profilingListener) {
|
||||
BaseQueryTuner(AutoTuneConfig config, SpiEbeanServer server, ProfilingListener profilingListener) {
|
||||
this.server = server;
|
||||
this.profilingListener = profilingListener;
|
||||
this.mode = config.getMode();
|
||||
@@ -84,12 +84,17 @@ public class BaseQueryTuner {
|
||||
/**
|
||||
* Auto tune the query and enable profiling.
|
||||
*/
|
||||
public boolean tuneQuery(SpiQuery<?> query) {
|
||||
boolean tuneQuery(SpiQuery<?> query) {
|
||||
|
||||
if (skipAll || !tunableQuery(query)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (query.getProfilingListener() != null) {
|
||||
// profiling secondary query
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!useTuning(query)) {
|
||||
if (profiling) {
|
||||
profiling(query, server.createCallStack());
|
||||
@@ -138,6 +143,7 @@ public class BaseQueryTuner {
|
||||
switch (type) {
|
||||
case COUNT:
|
||||
case ID_LIST:
|
||||
case UPDATE:
|
||||
case DELETE:
|
||||
case SUBQUERY:
|
||||
return false;
|
||||
|
||||
@@ -103,8 +103,7 @@ public class ProfileManager implements ProfilingListener {
|
||||
|
||||
private ProfileOrigin getProfileOrigin(ObjectGraphOrigin originQueryPoint) {
|
||||
synchronized (monitor) {
|
||||
ProfileOrigin stats = profileMap.computeIfAbsent(originQueryPoint.getKey(), k -> new ProfileOrigin(originQueryPoint, queryTuningAddVersion, profilingBase, profilingRate));
|
||||
return stats;
|
||||
return profileMap.computeIfAbsent(originQueryPoint.getKey(), k -> new ProfileOrigin(originQueryPoint, queryTuningAddVersion, profilingBase, profilingRate));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ public class ProfileOrigin {
|
||||
}
|
||||
}
|
||||
|
||||
private OrmQueryDetail buildDetail(BeanDescriptor<?> rootDesc) {
|
||||
OrmQueryDetail buildDetail(BeanDescriptor<?> rootDesc) {
|
||||
|
||||
PathProperties pathProps = new PathProperties();
|
||||
for (ProfileOriginNodeUsage statsNode : nodeUsageMap.values()) {
|
||||
@@ -107,8 +107,7 @@ public class ProfileOrigin {
|
||||
|
||||
OrmQueryDetail detail = new OrmQueryDetail();
|
||||
|
||||
Collection<Props> pathProperties = pathProps.getPathProps();
|
||||
for (Props props : pathProperties) {
|
||||
for (Props props : pathProps.getPathProps()) {
|
||||
if (!props.isEmpty()) {
|
||||
detail.fetch(props.getPath(), props.getPropertiesAsString(), null);
|
||||
}
|
||||
@@ -151,12 +150,8 @@ public class ProfileOrigin {
|
||||
* Collect the usage information for from a instance for this node.
|
||||
*/
|
||||
public void collectUsageInfo(NodeUsageCollector profile) {
|
||||
|
||||
//logger.info("COLLECT USAGE {}", profile.toString());
|
||||
|
||||
if (!profile.isEmpty()) {
|
||||
ProfileOriginNodeUsage nodeStats = getNodeStats(profile.getNode().getPath());
|
||||
nodeStats.collectUsageInfo(profile);
|
||||
getNodeStats(profile.getNode().getPath()).collectUsageInfo(profile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,23 +55,27 @@ public class ProfileOriginNodeUsage {
|
||||
}
|
||||
}
|
||||
|
||||
BeanProperty toOneIdProperty = null;
|
||||
boolean addedToPath = false;
|
||||
|
||||
for (String propName : aggregateUsed) {
|
||||
BeanProperty beanProp = desc.findPropertyFromPath(propName);
|
||||
if (beanProp == null) {
|
||||
logger.warn("AutoTune: Can't find property[" + propName + "] for " + desc.getName());
|
||||
|
||||
} else {
|
||||
if (beanProp instanceof BeanPropertyAssoc<?>) {
|
||||
BeanPropertyAssoc<?> assocProp = (BeanPropertyAssoc<?>) beanProp;
|
||||
String targetIdProp = assocProp.getTargetIdProperty();
|
||||
String manyPath = SplitName.add(path, assocProp.getName());
|
||||
pathProps.addToPath(manyPath, targetIdProp);
|
||||
if (beanProp.isId()) {
|
||||
// remember and maybe add ToOne property to parent path
|
||||
toOneIdProperty = beanProp;
|
||||
} else if (beanProp instanceof BeanPropertyAssoc<?>) {
|
||||
// intentionally skip
|
||||
} else {
|
||||
//noinspection StatementWithEmptyBody
|
||||
if (beanProp.isLob() && !beanProp.isFetchEager()) {
|
||||
// AutoTune will not include Lob's marked FetchLazy
|
||||
// (which is the default for Lob's so typical).
|
||||
} else {
|
||||
addedToPath = true;
|
||||
pathProps.addToPath(path, beanProp.getName());
|
||||
}
|
||||
}
|
||||
@@ -81,9 +85,16 @@ public class ProfileOriginNodeUsage {
|
||||
if ((modified || addVersionProperty) && desc != null) {
|
||||
BeanProperty versionProp = desc.getVersionProperty();
|
||||
if (versionProp != null) {
|
||||
addedToPath = true;
|
||||
pathProps.addToPath(path, versionProp.getName());
|
||||
}
|
||||
}
|
||||
|
||||
if (toOneIdProperty != null && !addedToPath) {
|
||||
// add ToOne property to parent path
|
||||
ElPropertyValue assocOne = rootDesc.getElGetValue(path);
|
||||
pathProps.addToPath(SplitName.parent(path), assocOne.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -144,15 +144,13 @@ public abstract class AbstractSqlQueryRequest {
|
||||
* Prepare and execute the SQL using the Binder.
|
||||
*/
|
||||
public void executeSql(Binder binder, SpiQuery.Type type) throws SQLException {
|
||||
startNano = System.nanoTime();
|
||||
executeAsSql(binder);
|
||||
}
|
||||
|
||||
protected void executeAsSql(Binder binder) throws SQLException {
|
||||
|
||||
startNano = System.nanoTime();
|
||||
|
||||
prepareSql();
|
||||
|
||||
Connection conn = trans.getInternalConnection();
|
||||
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.clickhouse.ClickHousePlatform;
|
||||
import io.ebean.config.dbplatform.cockroach.CockroachPlatform;
|
||||
import io.ebean.config.dbplatform.db2.DB2Platform;
|
||||
import io.ebean.config.dbplatform.h2.H2Platform;
|
||||
@@ -104,6 +105,9 @@ public class DatabasePlatformFactory {
|
||||
if (dbName.equals("db2")) {
|
||||
return new DB2Platform();
|
||||
}
|
||||
if (dbName.equals("clickhouse")) {
|
||||
return new ClickHousePlatform();
|
||||
}
|
||||
if (dbName.equals("sqlite")) {
|
||||
return new SQLitePlatform();
|
||||
}
|
||||
@@ -162,6 +166,8 @@ public class DatabasePlatformFactory {
|
||||
return new SqlAnywherePlatform();
|
||||
} else if (dbProductName.contains("hdb")) {
|
||||
return new HanaPlatform();
|
||||
} else if (dbProductName.contains("clickhouse")) {
|
||||
return new ClickHousePlatform();
|
||||
}
|
||||
|
||||
// use the standard one
|
||||
|
||||
@@ -88,4 +88,10 @@ public class DefaultBeanState implements BeanState {
|
||||
public void resetForInsert() {
|
||||
intercept.setNew();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Exception> getLoadErrors() {
|
||||
return intercept.getLoadErrors();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public class DefaultCallStackFactory implements CallStackFactory {
|
||||
|
||||
// find the first non-avaje stackElement
|
||||
for (; startIndex < stackTrace.length; startIndex++) {
|
||||
if (!stackTrace[startIndex].getClassName().startsWith(IO_EBEAN)) {
|
||||
if (!ignore(stackTrace[startIndex])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,16 @@ public class DefaultCallStackFactory implements CallStackFactory {
|
||||
return createCallStack(finalTrace);
|
||||
}
|
||||
|
||||
private boolean ignore(StackTraceElement element) {
|
||||
if (element.getClassName().startsWith(IO_EBEAN)) {
|
||||
return true;
|
||||
}
|
||||
if (element.getMethodName().startsWith("_ebean_")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private CallStack createCallStack(StackTraceElement[] finalTrace) {
|
||||
return new CallStack(finalTrace, finalTrace[0].hashCode(), pathHash(finalTrace));
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user