Compare commits

...
Author SHA1 Message Date
rob bygrave 4509c6b1ed [maven-release-plugin] prepare release ebean-11.37.1 2019-04-12 13:38:41 +12:00
rob bygrave d32559baf4 #1674 - For ProfileLocation use in constructors trim label <init> to init .... (for safe label) 2019-04-12 02:25:05 +12:00
rob bygrave 2639e799c5 #1673 - ENH: Support ProfileLocation on DtoQuery including via orm query.asDto() 2019-04-12 02:01:01 +12:00
rob bygrave 46c66b7169 #1672 - ENH: Add ebean.dumpMetricsOnShutdown=true ... for dumping metrics 2019-04-12 00:55:39 +12:00
rob bygrave 30ae435f33 #1672 - ENH: Add ebean.dumpMetricsOnShutdown=true ... for dumping metrics 2019-04-12 00:45:00 +12:00
rob bygrave 4f4743807c #1671 - Use table alias with delete query for Postgres, Oracle, MySql 2019-04-10 01:25:43 +12:00
rob bygrave 7b7a492241 Bump to 11.37.1 2019-04-09 23:04:34 +12:00
rob bygrave 4af8e53315 #1670 - Expose metrics as ServerMetrics interface (improvement over existing BasicMetricVisitor use) 2019-04-09 22:54:16 +12:00
rob bygrave 1c6fa575f2 #1669 - When deleting complex object graphs, entities are deleted multiple times
Fix by not unregistering the deleted bean hash in the transaction so that
the skip occurs on other root level bean traversals (the delete of the
2nd master bean)
2019-04-09 20:30:17 +12:00
rob bygrave 2430404e1f Merge branch 'FOCONIS-bug-delete-with-version' 2019-04-09 20:27:23 +12:00
rob bygrave d433e0ea46 Merge branch 'bug-delete-with-version' of https://github.com/FOCONIS/ebean into FOCONIS-bug-delete-with-version 2019-04-09 20:06:12 +12:00
rob bygrave 42591a5a87 No effective change - reformat line 2019-04-09 20:05:22 +12:00
Roland PramlandRob Bygrave 65a62bf683 Fix check constraint support for MariaDB >= 10.2.1 (#1668)
* fixed the check constraint support for mysql/mariadb

* update migration scripts
2019-04-09 19:38:57 +12:00
Roland Praml 3e3f3c35e7 add test case to show delete with version 2019-04-08 17:21:23 +02:00
Rob BygraveandGitHub f4db7ca0bb Update sponsors in README 2019-04-08 20:49:28 +12:00
rob bygrave b0e487cfbd Add sponsor payintech.com 2019-04-03 23:24:13 +13:00
rob bygrave 23fd29ddf1 #1659 - CVE-2018-12022 - Bump Jackson version from 2.9.5 to 2.9.8 2019-04-01 23:23:02 +13:00
rob bygrave f21cac5d4f [maven-release-plugin] prepare for next development iteration 2019-04-01 23:13:07 +13:00
rob bygrave c2e5619f70 [maven-release-plugin] prepare release ebean-11.36.4 2019-04-01 23:12:53 +13:00
rob bygrave 18e0ff7067 #1665 - Incorrect query execution metrics for ORM query converted to DTO query via asDto() 2019-04-01 23:11:16 +13:00
rob bygrave 4119c02f0d #1663 Additionally trim the OrmQueryDetail.asString() output removing extra whitespace 2019-04-01 23:09:58 +13:00
rob bygrave 8985d37aed Merge branch 'master' of github.com:ebean-orm/ebean 2019-04-01 22:25:24 +13:00
Rob BygraveandGitHub 23e035894f Add foconis.de as sponsor to readme 2019-04-01 22:24:50 +13:00
rob bygrave 32b8fd0e26 #1663 Additionally trim the OrmQueryDetail.asString() output removing extra whitespace 2019-03-29 00:43:32 +13:00
rob bygrave 4971db54c8 #1663 - AutoTune - merge profiling such that "select (orderDate) fetch customer (id)" instead merges to "select (orderDate,customer)" 2019-03-29 00:19:21 +13:00
rob bygrave e417b3cde2 #1661 - AutoTune - callstack filters _ebean_ methods 2019-03-28 20:47:34 +13:00
rob bygrave d23ca31e42 [maven-release-plugin] prepare for next development iteration 2019-03-27 16:44:00 +13:00
rob bygrave c9b71ae373 [maven-release-plugin] prepare release ebean-11.36.3 2019-03-27 16:43:47 +13:00
rob bygrave f06f21cf5e #1660 - Modify MetaOrmQueryMetric getQueryPlanHash to return hash of sql 2019-03-27 16:12:09 +13:00
rob bygrave f8b97ff397 #1658 - ENH: Add label to ProfileLocation and use as default query label
- Extend/propagate the label from the origin query to secondary queries
(appending the relative path).
2019-03-27 12:57:36 +13:00
rob bygrave fc032c632b #1658 - ENH: Add label to ProfileLocation and use as default query label 2019-03-27 09:37:35 +13:00
rob bygrave 54534ea7f3 #1657 - ERROR BeanDescriptorManager - Error in deployment java.lang.RuntimeException: Expecting [ ... ] to start with "select" 2019-03-21 22:30:27 +13:00
rob bygrave 812dcbb4bb #1653 - ENH: Add support for ClickHouse database
With array type conversion for primitive arrays
2019-03-20 23:13:50 +13:00
rob bygrave a3d4b57b94 Merge branch 'feature/1653-clickhouse' 2019-03-20 22:07:42 +13:00
rob bygrave 61479d365a Merge branch 'master' of github.com:ebean-orm/ebean 2019-03-20 22:05:54 +13:00
ooknightandRob Bygrave 3ba9a52b0e add server config : default enum type (#1655)
ENH: add server config : default enum type
2019-03-20 21:45:44 +13:00
rob bygrave 518ba37fb2 No effective change - Bump maven plugin version 2019-03-16 15:28:44 +13:00
rob bygrave fd6a7f64eb [maven-release-plugin] prepare for next development iteration 2019-03-14 21:17:03 +13:00
rob bygrave 8b073ef10a [maven-release-plugin] prepare release ebean-11.36.2 2019-03-14 21:16:47 +13:00
rob bygrave 6a7555055d No effective change - Update tests from Ebean -> DB 2019-03-14 21:11:59 +13:00
rob bygrave eb31f1bfce No effective change - Add more tests for LocalDate pre 1900 etc 2019-03-14 16:20:14 +13:00
rob bygrave 2e4f99318a No effective change - Ebean -> DB in tests 2019-03-14 16:15:47 +13:00
rob bygrave 409c1d06b2 #1652 - Conversion problem between org.joda.time.LocalDate and java.sql.Date field for dates before Jan 2 1900 2019-03-14 16:08:04 +13:00
rob bygrave 01f2881940 #1653 - ENH: Add support for ClickHouse database
With array type conversion etc
2019-03-14 14:44:30 +13:00
rob bygrave aef512a851 #1653 - ENH: Add support for ClickHouse database 2019-03-14 00:08:13 +13:00
rob bygrave 435e1f61fe #1651 - ENH: Support change to io.ebean.Model that supports named databases (other than default database) 2019-03-13 15:05:08 +13:00
rob bygrave 0230e3e253 Reduce logging for CI server 2019-03-12 15:06:26 +13:00
rob bygrave 770364fd1a [maven-release-plugin] prepare for next development iteration 2019-03-12 14:53:24 +13:00
rob bygrave fbc2d70967 [maven-release-plugin] prepare release ebean-11.36.1 2019-03-12 14:53:09 +13:00
rob bygrave 276e865f72 #1647 - Change ServerConfig.updateAllPropertiesInBatch to default to false 2019-03-12 14:49:00 +13:00
rob bygrave 9ded3890d7 #1649 - ENH: Add optional length() attribute to @DbEnumValue 2019-03-12 14:31:44 +13:00
rob bygrave bfeb0cd667 No effective change - update to using "Database" and DB 2019-03-12 14:29:28 +13:00
rob bygrave 119049b696 Javadoc update using "Database" vs "EbeanServer" 2019-03-07 16:53:13 +13:00
rob bygrave 33ff113585 #1648 - Change BeanFinder and BeanRepository to use Database rather than EbeanServer 2019-03-07 16:51:33 +13:00
rob bygrave 17e0953cad Javadoc update - reduce generated packages 2019-03-07 16:26:05 +13:00
rob bygrave e3ca5d2419 Javadoc update using "DB" and hide DB constructor 2019-03-07 12:01:05 +13:00
rob bygrave a5f435dbcf Javadoc update using "DB" 2019-03-07 11:25:34 +13:00
rob bygrave aa1e021501 Javadoc update using "DB" 2019-03-07 11:10:58 +13:00
rob bygrave 7ea6f16207 Javadoc update using "DB" vs "Ebean" 2019-03-07 10:56:56 +13:00
rob bygrave 197852857d #1646 - Ensure that java.sql.Array.free() is called 2019-03-07 09:25:48 +13:00
rob bygrave e7e6e00473 No effective change - Finder javadoc to use "Database" 2019-03-07 09:08:34 +13:00
rob bygrave 933c791d33 No effective change - Finder javadoc to use "Database" 2019-03-06 23:13:24 +13:00
rob bygrave 043c523c2a [maven-release-plugin] prepare for next development iteration 2019-03-05 23:06:29 +13:00
rob bygrave 82247fd4e4 [maven-release-plugin] prepare release ebean-11.35.3 2019-03-05 23:06:14 +13:00
rob bygrave 27f86548a6 #1645 - ENH: Add expression rawOrEmpty() ... as a convenience for conditionally added a raw expression 2019-03-05 23:03:22 +13:00
rob bygrave 7e8424328f [maven-release-plugin] prepare for next development iteration 2019-03-05 22:26:35 +13:00
rob bygrave 923828ce87 [maven-release-plugin] prepare release ebean-11.35.2 2019-03-05 22:26:19 +13:00
rob bygrave e195f20981 #1645 - ENH: Add expression rawOrEmpty() ... as a convenience for conditionally added a raw expression 2019-03-05 22:13:38 +13:00
rob bygrave 86d0216159 #1644 - ENH: Improve batch flush when multiple "top level" (unrelated) types are persisted 2019-03-04 12:42:08 +13:00
rob bygrave a4698ea1b6 #1643 - ENH: Add expression inOrEmpty() ... as a convenience for conditionally added a IN expression 2019-03-01 18:24:00 +13:00
rob bygrave 888384f7db No effective change (change to DB from Ebean for TestSubQuery) 2019-03-01 18:12:36 +13:00
rob bygrave 817c946eeb [maven-release-plugin] prepare for next development iteration 2019-02-24 16:20:36 +13:00
rob bygrave 1749c2cb4c [maven-release-plugin] prepare release ebean-11.35.1 2019-02-24 16:20:21 +13:00
rob bygrave 3afd3c4530 #1640 Additionally add expression eq and in support for Inet and Postgres any
Bump ebean-types to 1.3 with of() helper methods
2019-02-24 16:18:49 +13:00
rob bygrave e5f98f1b7a #1640 Additionally add expression eq and in support for Inet and Postgres any 2019-02-24 15:29:07 +13:00
rob bygrave 2e496c9e1e Bump pom to 11.35.1-SNAPSHOT 2019-02-21 22:15:49 +13:00
Rob BygraveandGitHub 5975694538 #1640 - For Postgres support mapping InetAddress to INET db column (#1641)
* #1640 - For Postgres support mapping InetAddress to INET db column

* #1640 - For Postgres support mapping InetAddress to INET db column

Support for Inet and Cdir types + offlineMigrationGeneration + PlatformConfig.databaseInetAddressVarchar for turning off InetAddress mapping on Postgres
2019-02-21 22:14:21 +13:00
rob bygrave 03a42f7f6e No effective change - update test DefaultProfileHandlerTest to output profiling files to target 2019-02-21 09:37:14 +13:00
rob bygrave 84f1e05eb4 [maven-release-plugin] prepare for next development iteration 2019-02-18 10:58:21 +13:00
rob bygrave da20ced1ed [maven-release-plugin] prepare release ebean-11.34.3 2019-02-18 10:58:08 +13:00
rob bygrave 1f1a6e2c04 #1642 - Bump to ebean-datasource 4.5.1 with offline/online support 2019-02-18 10:50:34 +13:00
Roland PramlandRob Bygrave 6b83800a83 FIX: Proper sort of migration versions (possible regression due #1577) (#1639) 2019-02-17 17:40:07 +13:00
rob bygrave c344551e4d [maven-release-plugin] prepare for next development iteration 2019-02-13 11:57:09 +13:00
rob bygrave 6b4aff7191 [maven-release-plugin] prepare release ebean-11.34.2 2019-02-13 11:56:55 +13:00
Roland PramlandRob Bygrave fc7fae35c9 FIX: When saving multiple m2m beans, only last m2m wins (Regression due #1630) (#1638)
* provide test case, that shows problem on multiple-m2m

* FIX: When saving multiple m2m beans, only last m2m wins (Regression due #1630)
2019-02-12 09:31:04 +13:00
rob bygrave 7929d8c0b8 Fix test data ordering 2019-02-11 23:17:54 +13:00
rob bygrave 058766dd92 No effective change - adjust test to use DB and try with resources 2019-02-11 23:13:57 +13:00
rob bygrave 4fdb8a77b4 Fix test data 2019-02-11 23:08:50 +13:00
Manuel PoloandRob Bygrave 56be1fc047 Fixed missing EBEAN_PROPS_FILE loading (#1635) 2019-02-11 23:02:10 +13:00
rob bygrave 192a946cf6 #1634 ENH: Add convenience expressions "equal to or null" eqOrNull() 2019-02-11 22:59:47 +13:00
rob bygrave aa7b15fd6e #1637 - build on jdk11 2019-02-11 22:48:10 +13:00
Roland PramlandRob Bygrave cb74ef01e0 build on jdk11 (#1637) 2019-02-11 22:42:05 +13:00
rob bygrave 829dfcb013 #1634 - ENH: Add convenience expressions "greater than or null" gtOrNull(), ltOrNull() and inRangeWith() 2019-02-09 00:30:55 +13:00
rob bygrave 25b6d2a5a0 [maven-release-plugin] prepare for next development iteration 2019-02-07 23:43:13 +13:00
215 changed files with 3963 additions and 1508 deletions
+23
View File
@@ -1,6 +1,29 @@
[![Build Status](https://travis-ci.org/ebean-orm/ebean.svg?branch=master)](https://travis-ci.org/ebean-orm/ebean)
[![Maven Central : ebean](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean)
# Sponsors
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://www.foconis.de/" target="_blank">
<img width="222px" src="https://www.foconis.de/templates/yootheme/cache/foconis_logo_322-709da1de.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.payintech.com/" target="_blank">
<img width="222px" src="https://www.payintech.com/wp-content/uploads/2015/10/PayinTech-logo-noir.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.premium-minds.com" target="_blank">
<img width="222px" src="https://ebean.io/images/logo-premium-minds.png">
</a>
</td>
</tr>
</tbody>
</table>
# Need help?
Post questions or issues to the Ebean google group - https://groups.google.com/forum/#!forum/ebean
+33 -11
View File
@@ -9,20 +9,20 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.34.1</version>
<version>11.37.1</version>
<packaging>jar</packaging>
<name>ebean</name>
<url>http://ebean-orm.github.io/</url>
<properties>
<jackson-core.version>2.9.5</jackson-core.version>
<jackson-core.version>2.9.8</jackson-core.version>
<snakeyaml.version>1.21</snakeyaml.version>
</properties>
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-11.34.1</tag>
<tag>ebean-11.37.1</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.5</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.4</version>
<version>4.5.1</version>
</dependency>
<dependency>
@@ -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,14 +243,14 @@
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-agentloader</artifactId>
<version>2.1.2</version>
<version>3.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-agent</artifactId>
<version>11.27.1</version>
<version>11.37.1</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.37.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>
+16 -21
View File
@@ -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);
+14 -14
View File
@@ -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);
+3 -4
View File
@@ -1,10 +1,9 @@
package io.ebean;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
/**
* Provides access to the internal state of an entity bean.
*/
@@ -58,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();
*
@@ -103,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.
*/
+16 -18
View File
@@ -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>
+94 -68
View File
@@ -17,8 +17,58 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
/**
* DB is a registry of {@link Database} by name.
* <p>
* DB additionally provides a convenient way to use the 'default' Database.
* <p>
* <h3>Default database</h3>
* <p>
* One of the Database instances can be registered as the "default database"
* and can be obtained using <code>DB.getDefault()</code>
* </p>
* <pre>{@code
*
* Database database = DB.getDefault();
*
* }</pre>
*
* <h3>Named database</h3>
* <p>
* Multiple database instances can be registered with DB and we can obtain them
* using <code>DB.byName()</code>
* </p>
* <pre>{@code
*
* Database hrDatabase = DB.byName("hr");
*
* }</pre>
*
* <h3>Convenience methods</h3>
* <p>
* DB has methods like {@link #find(Class)} and {@link #save(Object)} which are
* just convenience for using the default database.
* </p>
*
* <pre>{@code
*
* // fetch using the default database
* Order order = DB.find(Order.class, 10);
*
* // is the same as
* Database database = DB.getDefault();
* Order order = database.find(Order.class, 10);
*
* }</pre>
*/
public class DB {
/**
* Hide constructor.
*/
private DB() {
}
/**
* Return the default database.
*/
@@ -106,22 +156,21 @@ public class DB {
* </p>
* <pre>{@code
*
* try (Transaction txn = DB.beginTransaction()) {
* Order order = DB.find(Order.class,10); ...
* try (Transaction transaction = DB.beginTransaction()) {
*
* DB.save(order);
* Order order = DB.find(Order.class, 42);
* order.setStatus(Status.COMPLETE);
* order.save();
*
* txn.commit();
* transaction.commit();
* }
*
* }</pre>
* <p>
* If you want to externalise the transaction management then you should be
* able to do this via Database. Specifically with Database you can pass
* the transaction to the various find() and save() execute() methods. This
* gives you the ability to create the transactions yourself externally from
* Ebean and pass those transactions through to the various methods available
* on Database.
* If we want to externalise the transaction management then we do this via Database.
* With Database we can pass the transaction to the various find(), save() and execute()
* methods. This gives us the ability to create the transactions externally from Ebean
* and use the transaction explicitly via the various methods available on Database.
* </p>
*/
public static Transaction beginTransaction() {
@@ -150,29 +199,24 @@ public class DB {
* // suspend it until this transaction ends
*
* try (Transaction txn = DB.beginTransaction(TxScope.requiresNew())) {
*
* ...
*
* // commit the transaction
* txn.commit();
* }
*
* }</pre>
*
* <h3>REQUIRED example:</h3>
* <pre>{@code
*
* // start a new transaction if there is not a current transaction
*
* try (Transaction txn = DB.beginTransaction(TxScope.required())) {
*
* ...
*
* // commit the transaction if it was created or
* // do nothing if there was already a current transaction
* txn.commit();
*
* }
*
* }</pre>
*/
public static Transaction beginTransaction(TxScope scope) {
@@ -291,18 +335,6 @@ public class DB {
* OneToMany, OneToOne or ManyToMany annotation.
* </p>
* <p>
* In this example below the details property has a CascadeType.ALL set so
* saving an order will also save all its details.
* </p>
* <pre>{@code
* public class Order { ...
*
* @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
* List<OrderDetail> details;
* ...
* }
* }</pre>
* <p>
* When a save cascades via a OneToMany or ManyToMany Ebean will automatically
* set the 'parent' object to the 'detail' object. In the example below in
* saving the order and cascade saving the order details the 'parent' order
@@ -1043,15 +1075,15 @@ public class DB {
* </p>
* <pre>{@code
*
* // set specific transactional scope settings
* TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
* // set specific transactional scope settings
* TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
*
* DB.execute(scope, new TxRunnable() {
* public void run() {
* User u1 = DB.find(User.class, 1);
* ...
* }
* });
* DB.execute(scope, new TxRunnable() {
* public void run() {
* User u1 = DB.find(User.class, 1);
* ...
* }
* });
*
* }</pre>
*/
@@ -1067,19 +1099,17 @@ public class DB {
* </p>
* <pre>{@code
*
* DB.execute(() -> {
* DB.execute(() -> {
*
* User u1 = DB.find(User.class, 1);
* User u2 = DB.find(User.class, 2);
* User u1 = DB.find(User.class, 1);
* User u2 = DB.find(User.class, 2);
*
* u1.setName("u1 mod");
* u2.setName("u2 mod");
*
* DB.save(u1);
* DB.save(u2);
*
* });
* u1.setName("u1 mod");
* u2.setName("u2 mod");
*
* DB.save(u1);
* DB.save(u2);
* });
* }</pre>
*/
public static void execute(Runnable r) {
@@ -1094,17 +1124,16 @@ public class DB {
* </p>
* <pre>{@code
*
* // set specific transactional scope settings
* TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
*
* DB.executeCall(scope, new Callable<String>() {
* public String call() {
* User u1 = DB.find(User.class, 1);
* ...
* return u1.getEmail();
* }
* });
* // set specific transactional scope settings
* TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
*
* DB.executeCall(scope, new Callable<String>() {
* public String call() {
* User u1 = DB.find(User.class, 1);
* ...
* return u1.getEmail();
* }
* });
* }</pre>
*/
public static <T> T executeCall(TxScope scope, Callable<T> c) {
@@ -1123,21 +1152,19 @@ public class DB {
* </p>
* <pre>{@code
*
* DB.executeCall(() -> {
* DB.executeCall(() -> {
*
* User u1 = DB.find(User.class, 1);
* User u2 = DB.find(User.class, 2);
* User u1 = DB.find(User.class, 1);
* User u2 = DB.find(User.class, 2);
*
* u1.setName("u1 mod");
* u2.setName("u2 mod");
* u1.setName("u1 mod");
* u2.setName("u2 mod");
*
* DB.save(u1);
* DB.save(u2);
*
* return u1.getEmail();
*
* });
* DB.save(u1);
* DB.save(u2);
*
* return u1.getEmail();
* });
* }</pre>
*/
public static <T> T executeCall(Callable<T> c) {
@@ -1175,7 +1202,6 @@ public class DB {
* @param deletes true if rows on the table where deleted
*/
public static void externalModification(String tableName, boolean inserts, boolean updates, boolean deletes) {
getDefault().externalModification(tableName, inserts, updates, deletes);
}
+1 -1
View File
@@ -55,7 +55,7 @@ public class DatabaseFactory {
}
/**
* Shutdown gracefully all EbeanServers cleaning up any resources as required.
* 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>
+7 -1
View File
@@ -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();
@@ -109,6 +109,12 @@ public interface DtoQuery<T> {
*/
DtoQuery<T> setLabel(String label);
/**
* Set the profile location of this query. This is used to relate query execution metrics
* back to a location like a specific line of code.
*/
DtoQuery<T> setProfileLocation(ProfileLocation profileLocation);
/**
* Set a timeout on this query.
* <p>
+16 -103
View File
@@ -24,97 +24,11 @@ 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);
@@ -124,13 +38,12 @@ public final class Ebean {
}
/**
* 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>
*
@@ -22,8 +22,8 @@ package io.ebean;
* example.setName("Rob%");
* example.setNotes("%something%");
*
* List&lt;Customer&gt; 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();
+40 -18
View File
@@ -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()
*/
@@ -71,7 +59,6 @@ public class Expr {
* </p>
*/
public static Expression inRange(String propertyName, Object value1, Object value2) {
return Ebean.getExpressionFactory().inRange(propertyName, value1, value2);
}
@@ -79,7 +66,6 @@ public class Expr {
* Between - property between the two given values.
*/
public static Expression between(String propertyName, Object value1, Object value2) {
return Ebean.getExpressionFactory().between(propertyName, value1, value2);
}
@@ -87,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);
}
@@ -253,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.
*/
+78 -3
View File
@@ -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.
*/
@@ -157,6 +162,25 @@ public interface ExpressionFactory {
*/
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.
*/
@@ -167,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.
*/
@@ -178,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.
*/
@@ -285,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.
*/
+142 -26
View File
@@ -321,7 +321,7 @@ public interface ExpressionList<T> {
* <pre>{@code
*
* List<String> names =
* Ebean.find(Customer.class)
* DB.find(Customer.class)
* .select("name")
* .orderBy().asc("name")
* .findSingleAttributeList();
@@ -332,7 +332,7 @@ public interface ExpressionList<T> {
* <pre>{@code
*
* List<String> names =
* Ebean.find(Customer.class)
* DB.find(Customer.class)
* .setDistinct(true)
* .select("name")
* .where().eq("status", Customer.Status.NEW)
@@ -352,7 +352,7 @@ public interface ExpressionList<T> {
* <pre>{@code
*
* String name =
* Ebean.find(Customer.class)
* DB.find(Customer.class)
* .select("name")
* .where().eq("id", 42)
* .findSingleAttribute();
@@ -435,7 +435,7 @@ public interface ExpressionList<T> {
* </p>
* <pre>{@code
*
* PagedList<Order> pagedList = Ebean.find(Order.class)
* PagedList<Order> pagedList = DB.find(Order.class)
* .setFirstRow(50)
* .setMaxRows(20)
* .findPagedList();
@@ -502,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();
@@ -575,7 +575,7 @@ public interface ExpressionList<T> {
*
* List<CountedValue<Order.Status>> orderStatusCount =
*
* Ebean.find(Order.class)
* DB.find(Order.class)
* .select("status")
* .where()
* .gt("orderDate", LocalDate.now().minusMonths(3))
@@ -758,20 +758,6 @@ public interface ExpressionList<T> {
/**
* Add an Expression to the list.
* <p>
* This returns the list so that add() can be chained.
* </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);
@@ -785,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.
*/
@@ -802,6 +793,25 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> ine(String propertyName, String value);
/**
* 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>
@@ -825,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.
@@ -836,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.
*/
@@ -873,11 +893,10 @@ 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>
@@ -892,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>
*/
@@ -969,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>
@@ -1213,6 +1267,68 @@ public interface ExpressionList<T> {
*/
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.
*
+10 -12
View File
@@ -15,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).
@@ -26,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>
*/
@@ -121,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) -> {
@@ -155,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) -> {
@@ -194,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();
*
@@ -272,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();
@@ -301,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();
*
@@ -341,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();
@@ -351,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)
@@ -413,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
+6 -6
View File
@@ -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")
+4 -4
View File
@@ -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);
+22 -17
View File
@@ -13,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
*
@@ -24,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();
@@ -45,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> {
@@ -54,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.
@@ -81,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;
}
/**
@@ -107,12 +115,10 @@ 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 Database db() {
return DB.byName(serverName);
return DB.byName(_$dbName);
}
/**
@@ -120,11 +126,10 @@ public class Finder<I, T> {
* <p>
* This is equivalent to {@link DB#byName(String)}
*
* @param server The name of the Database. 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 Database db(String server) {
return DB.byName(server);
public Database db(String databaseName) {
return DB.byName(databaseName);
}
/**
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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()
+4 -4
View File
@@ -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
+1 -2
View File
@@ -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)
+1 -1
View File
@@ -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.
*/
+44 -88
View File
@@ -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>
@@ -377,7 +359,7 @@ public interface Query<T> {
* <pre>{@code
*
* // fetch a bean with JSON content
* EBasicJsonList bean= Ebean.find(EBasicJsonList.class)
* EBasicJsonList bean= DB.find(EBasicJsonList.class)
* .setId(42)
* .setAllowLoadErrors() // collect errors into bean state if we have invalid JSON
* .findOne();
@@ -436,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")
@@ -465,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")
@@ -481,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();
@@ -551,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();
@@ -573,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();
@@ -637,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();
@@ -658,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
@@ -680,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");
*
@@ -732,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) -> {
@@ -761,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")
@@ -784,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();
*
@@ -801,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();
*
@@ -818,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
@@ -826,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();
*
@@ -843,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();
@@ -854,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)
@@ -875,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();
@@ -934,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();
* ...
@@ -947,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();
@@ -1089,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();
@@ -1114,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>
*
@@ -1136,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>
*
@@ -1158,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();
@@ -1180,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();
* ...
@@ -1196,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%")
@@ -1243,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)
@@ -1260,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);
@@ -1375,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();
@@ -1391,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))
@@ -1447,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>
+3 -3
View File
@@ -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)
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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);
*
+4 -16
View File
@@ -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>
*/
+8 -9
View File
@@ -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,7 +243,7 @@ public interface Transaction extends AutoCloseable {
*
* // assume Customer has L2 bean caching enabled ...
*
* try (Transaction transaction = ebeanServer.beginTransaction()) {
* try (Transaction transaction = DB.beginTransaction()) {
*
* // this uses L2 bean cache as the transaction
* // ... is considered "query only" at this point
@@ -366,13 +365,13 @@ public interface Transaction extends AutoCloseable {
* // inserts into a table called sp_test
* cs.addModification("sp_test", true, false, false);
*
* try (Transaction txn = ebeanServer.beginTransaction()) {
* 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);
* }
*
* // Note: commit implicitly flushes
+1 -1
View File
@@ -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();
*
+6 -12
View File
@@ -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();
+1 -1
View File
@@ -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();
@@ -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.
@@ -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) {
+83 -39
View File
@@ -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;
/**
@@ -350,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.
@@ -521,8 +524,12 @@ public class ServerConfig {
*/
private boolean idGeneratorAutomatic = true;
private boolean dumpMetricsOnShutdown;
private String dumpMetricsOptions;
/**
* Construct a Server Configuration for programmatically creating an EbeanServer.
* Construct a Database Configuration for programmatically creating an Database.
*/
public ServerConfig() {
@@ -693,14 +700,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;
@@ -709,8 +716,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;
@@ -719,8 +726,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;
@@ -760,8 +767,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>
@@ -978,6 +985,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.
*/
@@ -1547,14 +1562,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;
@@ -1954,16 +1969,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>
*/
@@ -2214,7 +2229,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) {
@@ -2328,8 +2343,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
@@ -2337,7 +2351,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"
@@ -2356,9 +2370,6 @@ public class ServerConfig {
* transaction.setSkipCache(true);
* Customer.find.byId(99); // skips l2 bean cache
*
*
* } finally {
* transaction.end();
* }
*
* }</pre>
@@ -2426,7 +2437,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;
@@ -2896,6 +2907,8 @@ public class ServerConfig {
}
loadDocStoreSettings(p);
dumpMetricsOnShutdown = p.getBoolean("dumpMetricsOnShutdown", dumpMetricsOnShutdown);
dumpMetricsOptions = p.get("dumpMetricsOptions", dumpMetricsOptions);
queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds);
slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis);
collectQueryPlans = p.getBoolean("collectQueryPlans", collectQueryPlans);
@@ -2967,6 +2980,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);
@@ -3279,6 +3293,36 @@ public class ServerConfig {
this.collectQueryPlans = collectQueryPlans;
}
/**
* Return true if metrics should be dumped when the server is shutdown.
*/
public boolean isDumpMetricsOnShutdown() {
return dumpMetricsOnShutdown;
}
/**
* Set to true if metrics should be dumped when the server is shutdown.
*/
public void setDumpMetricsOnShutdown(boolean dumpMetricsOnShutdown) {
this.dumpMetricsOnShutdown = dumpMetricsOnShutdown;
}
/**
* Return the options for dumping metrics.
*/
public String getDumpMetricsOptions() {
return dumpMetricsOptions;
}
/**
* Include 'sql' or 'hash' in options such that they are included in the output.
*
* @param dumpMetricsOptions Example "sql,hash", "sql"
*/
public void setDumpMetricsOptions(String dumpMetricsOptions) {
this.dumpMetricsOptions = dumpMetricsOptions;
}
public enum UuidVersion {
VERSION4,
VERSION1,
@@ -49,6 +49,8 @@ public class DatabasePlatform {
*/
protected boolean useExtraTransactionOnIterateSecondaryQueries;
protected boolean supportsDeleteTableAlias;
/**
* The behaviour used when ending a read only transaction at read committed isolation level.
*/
@@ -176,10 +178,10 @@ public class DatabasePlatform {
* findIterate() and findVisit().
*/
protected boolean forwardOnlyHintOnFindIterate;
/**
* If set then use the CONCUR_UPDATABLE hint when creating ResultSets.
*
*
* This is {@code false} for HANA
*/
protected boolean supportsResultSetConcurrencyModeUpdatable = true;
@@ -306,6 +308,13 @@ public class DatabasePlatform {
return supportsNativeIlike;
}
/**
* Return true if the platform supports delete statements with table alias.
*/
public boolean isSupportsDeleteTableAlias() {
return supportsDeleteTableAlias;
}
/**
* Return the maximum table name length.
* <p>
@@ -524,7 +533,7 @@ public class DatabasePlatform {
public void setForwardOnlyHintOnFindIterate(boolean forwardOnlyHintOnFindIterate) {
this.forwardOnlyHintOnFindIterate = forwardOnlyHintOnFindIterate;
}
/**
* Return true if the ResultSet CONCUR_UPDATABLE Hint should be used on
* createNativeSqlTree() PreparedStatements.
@@ -535,7 +544,7 @@ public class DatabasePlatform {
public boolean isSupportsResultSetConcurrencyModeUpdatable() {
return supportsResultSetConcurrencyModeUpdatable;
}
/**
* Set to true if the ResultSet CONCUR_UPDATABLE Hint should be used by default on createNativeSqlTree() PreparedStatements.
*/
@@ -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;
}
}
@@ -23,6 +23,7 @@ public class H2Platform extends DatabasePlatform {
this.dbEncrypt = new H2DbEncrypt();
this.historySupport = new H2HistorySupport();
this.nativeUuidType = true;
this.supportsDeleteTableAlias = true;
this.dbDefaultValue.setNow("now()");
this.columnAliasPrefix = null;
@@ -23,6 +23,7 @@ public class OraclePlatform extends DatabasePlatform {
public OraclePlatform() {
super();
this.platform = Platform.ORACLE;
this.supportsDeleteTableAlias = true;
this.maxTableNameLength = 30;
this.maxConstraintNameLength = 30;
this.dbEncrypt = new OracleDbEncrypt();
@@ -30,6 +30,7 @@ public class PostgresPlatform extends DatabasePlatform {
super();
this.platform = Platform.POSTGRES;
this.supportsNativeIlike = true;
this.supportsDeleteTableAlias = true;
this.selectCountWithAlias = true;
this.blobDbType = Types.LONGVARBINARY;
this.clobDbType = Types.VARCHAR;
@@ -60,6 +61,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));
+2 -2
View File
@@ -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);
}
}
@@ -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);
@@ -6,7 +6,7 @@ import java.util.List;
/**
* A simple MetricVisitor that can collect the desired metrics into lists.
*/
public class BasicMetricVisitor extends AbstractMetricVisitor {
public class BasicMetricVisitor extends AbstractMetricVisitor implements ServerMetrics {
private final List<MetaTimedMetric> timed = new ArrayList<>();
private final List<MetaQueryMetric> dtoQuery = new ArrayList<>();
@@ -29,6 +29,7 @@ public class BasicMetricVisitor extends AbstractMetricVisitor {
/**
* Return timed metrics for Transactions, labelled SqlQuery, labelled SqlUpdate.
*/
@Override
public List<MetaTimedMetric> getTimedMetrics() {
return timed;
}
@@ -36,6 +37,7 @@ public class BasicMetricVisitor extends AbstractMetricVisitor {
/**
* Return the DTO query metrics.
*/
@Override
public List<MetaQueryMetric> getDtoQueryMetrics() {
return dtoQuery;
}
@@ -43,6 +45,7 @@ public class BasicMetricVisitor extends AbstractMetricVisitor {
/**
* Return the ORM query metrics.
*/
@Override
public List<MetaOrmQueryMetric> getOrmQueryMetrics() {
return ormQuery;
}
@@ -3,10 +3,19 @@ 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 {
/**
* Return the metrics for the database instance.
* <p>
* This will reset the metrics (reset counters back to zero etc) and
* will only return the non-empty metrics.
* </p>
*/
ServerMetrics collectMetrics();
/**
* Collect query plans.
*/
@@ -1,7 +1,5 @@
package io.ebean.meta;
import io.ebean.ProfileLocation;
import java.util.List;
/**
@@ -9,11 +7,6 @@ import java.util.List;
*/
public interface MetaOrmQueryMetric extends MetaQueryMetric {
/**
* Return the profile location.
*/
ProfileLocation getProfileLocation();
/**
* Return true if this query plan was tuned by AutoTune.
*/
@@ -1,5 +1,7 @@
package io.ebean.meta;
import io.ebean.ProfileLocation;
/**
* Query execution metrics.
*/
@@ -15,6 +17,11 @@ public interface MetaQueryMetric extends MetaTimedMetric {
*/
String getLabel();
/**
* Return the profile location.
*/
ProfileLocation getProfileLocation();
/**
* The actual SQL of the query.
*/
@@ -0,0 +1,24 @@
package io.ebean.meta;
import java.util.List;
/**
* Metrics of the Database instance.
*/
public interface ServerMetrics {
/**
* Return timed metrics for Transactions, labelled SqlQuery, labelled SqlUpdate.
*/
List<MetaTimedMetric> getTimedMetrics();
/**
* Return the DTO query metrics.
*/
List<MetaQueryMetric> getDtoQueryMetrics();
/**
* Return the ORM query metrics.
*/
List<MetaOrmQueryMetric> getOrmQueryMetrics();
}
@@ -0,0 +1,80 @@
package io.ebean.meta;
import java.util.Comparator;
/**
* Comparator for timed metrics sorted by name and then count.
*/
public class SortMetric {
public static final Comparator<MetaTimedMetric> NAME = new Name();
public static final Comparator<MetaTimedMetric> COUNT = new Count();
public static final Comparator<MetaTimedMetric> TOTAL = new Total();
public static final Comparator<MetaTimedMetric> MEAN = new Mean();
public static final Comparator<MetaTimedMetric> MAX = new Max();
/**
* Sort by name.
*/
public static class Name implements Comparator<MetaTimedMetric> {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
String name = o1.getName();
String name2 = o2.getName();
if (name == null) {
return name2 == null ? 0 : -1;
}
if (name2 == null) {
return 1;
}
int i = name.compareTo(name2);
return i != 0 ? i : Long.compare(o1.getCount(), o2.getCount());
}
}
/**
* Sort by count desc.
*/
public static class Count implements Comparator<MetaTimedMetric> {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
return Long.compare(o2.getCount(), o1.getCount());
}
}
/**
* Sort by total time desc.
*/
public static class Total implements Comparator<MetaTimedMetric> {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
return Long.compare(o2.getTotal(), o1.getTotal());
}
}
/**
* Sort by mean desc.
*/
public static class Mean implements Comparator<MetaTimedMetric> {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
return Long.compare(o2.getMean(), o1.getMean());
}
}
/**
* Sort by max desc.
*/
public static class Max implements Comparator<MetaTimedMetric> {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
return Long.compare(o2.getMax(), o1.getMax());
}
}
}
@@ -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;
@@ -1,5 +1,6 @@
package io.ebean.metric;
import io.ebean.ProfileLocation;
import io.ebean.meta.MetricType;
/**
@@ -27,6 +28,6 @@ public interface MetricFactory {
/**
* Create a Timed metric.
*/
QueryPlanMetric createQueryPlanMetric(MetricType metricType, Class<?> type, String label, String sql);
QueryPlanMetric createQueryPlanMetric(MetricType metricType, Class<?> type, String label, ProfileLocation profileLocation, String sql);
}
+10 -116
View File
@@ -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>
&nbsp;
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>
+22 -60
View File
@@ -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(&quot;HR&quot;);
// 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>
+1 -1
View File
@@ -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 {
+3 -3
View File
@@ -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 {
@@ -20,7 +20,7 @@ import java.util.Locale;
*
* FileReader reader = new FileReader(f);
*
* CsvReader&lt;Customer&gt; 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);
@@ -1,6 +1,7 @@
package io.ebeaninternal.api;
import io.ebean.DtoQuery;
import io.ebean.ProfileLocation;
import io.ebeaninternal.server.dto.DtoMappingRequest;
import io.ebeaninternal.server.dto.DtoQueryPlan;
@@ -37,9 +38,23 @@ public interface SpiDtoQuery<T> extends DtoQuery<T>, SpiSqlBinding {
/**
* Return the label for the query.
*/
@Override
String getLabel();
/**
* Return the label with fallback to profile location label.
*/
String getPlanLabel();
/**
* Obtain the location if necessary.
*/
void obtainLocation();
/**
* Return the profile location.
*/
ProfileLocation getProfileLocation();
/**
* Return the associated DTO bean type.
*/
@@ -54,67 +54,69 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
/**
* Find by Id or unique returning a single bean.
*/
BEAN(FIND_ONE),
BEAN(FIND_ONE, "byId"),
/**
* Find returning a List.
*/
LIST(FIND_MANY),
LIST(FIND_MANY, "findList"),
/**
* Find returning a Set.
*/
SET(FIND_MANY),
SET(FIND_MANY, "findSet"),
/**
* Find returning a Map.
*/
MAP(FIND_MANY),
MAP(FIND_MANY, "findMap"),
/**
* Find iterate type query - findEach(), findIterate() etc.
*/
ITERATE(FIND_ITERATE),
ITERATE(FIND_ITERATE, "findEach"),
/**
* Find the Id's.
*/
ID_LIST(FIND_ID_LIST),
ID_LIST(FIND_ID_LIST, "findIds"),
/**
* Find single attribute.
*/
ATTRIBUTE(FIND_ATTRIBUTE),
ATTRIBUTE(FIND_ATTRIBUTE, "findAttribute"),
/**
* Find rowCount.
*/
COUNT(FIND_COUNT),
COUNT(FIND_COUNT, "findCount"),
/**
* A subquery used as part of a where clause.
*/
SUBQUERY(FIND_SUBQUERY),
SUBQUERY(FIND_SUBQUERY, "subquery"),
/**
* Delete query.
*/
DELETE(FIND_DELETE, true),
DELETE(FIND_DELETE, "delete", true),
/**
* Update query.
*/
UPDATE(FIND_UPDATE, true);
UPDATE(FIND_UPDATE, "update", true);
boolean update;
String profileEventId;
private boolean update;
private String profileEventId;
private String label;
Type(String profileEventId) {
this(profileEventId, false);
Type(String profileEventId, String label) {
this(profileEventId, label, false);
}
Type(String profileEventId, boolean update) {
Type(String profileEventId, String label, boolean update) {
this.profileEventId = profileEventId;
this.label = label;
this.update = update;
}
@@ -128,6 +130,10 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
public String profileEventId() {
return profileEventId;
}
public String label() {
return label;
}
}
enum TemporalMode {
@@ -197,6 +203,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.
*/
@@ -69,11 +69,6 @@ public interface SpiTransaction extends Transaction {
*/
void registerDeleteBean(Integer hash);
/**
* Unregister the hash of the bean.
*/
void unregisterDeleteBean(Integer hash);
/**
* Return true if this is a bean that has already been saved/deleted.
*/
@@ -183,11 +183,6 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
transaction.registerDeleteBean(hash);
}
@Override
public void unregisterDeleteBean(Integer hash) {
transaction.unregisterDeleteBean(hash);
}
@Override
public boolean isRegisteredDeleteBean(Integer hash) {
return transaction.isRegisteredDeleteBean(hash);
@@ -189,7 +189,7 @@ public class DdlGenerator {
runScript(connection, false, createAllContent, getCreateFileName());
if (extraDdl && jaxbPresent) {
if (currentModel.isTablePartitioning()) {
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;
@@ -802,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);
}
}
@@ -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.
*/
@@ -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;
}
@@ -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();
@@ -262,7 +262,6 @@ public class BaseTableDdl implements TableDdl {
DdlBuffer apply = writer.apply();
apply.append(platformDdl.getCreateTableCommandPrefix()).append(" ").append(tableName).append(" (");
writeTableColumns(apply, columns, useIdentity);
writeCheckConstraints(apply, createTable);
writeUniqueConstraints(apply, createTable);
writeCompoundUniqueConstraints(apply, createTable);
if (!pk.isEmpty()) {
@@ -276,6 +275,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 +316,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 +328,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 +366,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 +393,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 +424,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 +432,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 +444,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 +459,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 +475,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 +489,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,43 +502,16 @@ public class BaseTableDdl implements TableDdl {
*/
protected void dropSequence(DdlBuffer buffer, String sequenceName) throws IOException {
buffer.append(platformDdl.dropSequence(sequenceName)).endOfStatement();
}
/**
* Write all the check constraints.
*/
protected void writeCheckConstraints(DdlBuffer apply, CreateTable createTable) throws IOException {
List<Column> columns = createTable.getColumn();
for (Column column : columns) {
String checkConstraint = column.getCheckConstraint();
if (hasValue(checkConstraint)) {
writeCheckConstraint(apply, column, checkConstraint);
}
}
}
/**
* Write a check constraint.
*/
protected void writeCheckConstraint(DdlBuffer buffer, Column column, String checkConstraint) throws IOException {
String ckName = column.getCheckConstraintName();
buffer.append(",").newLine();
buffer.append(" constraint ").append(ckName);
buffer.append(" ").append(checkConstraint);
buffer.appendStatement(platformDdl.dropSequence(sequenceName));
}
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 +608,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 +845,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 +857,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 +903,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 +926,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()));
}
@@ -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,91 @@
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
public String createCheckConstraint(String ckName, String checkConstraint) {
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;
}
}
@@ -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));
}
}
@@ -0,0 +1,28 @@
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
}
}
@@ -14,6 +14,10 @@ import java.util.Collection;
*/
public class MySqlDdl extends PlatformDdl {
// check constraint support is disabled by default. See https://groups.google.com/forum/#!topic/ebean/luFN-2xBkUw
// this flag is for compatibility. Use it with care.
private static final boolean USE_CHECK_CONSTRAINT = Boolean.getBoolean("ebean.mysql.useCheckConstraint");
public MySqlDdl(DatabasePlatform platform) {
super(platform);
this.alterColumn = "modify";
@@ -48,11 +52,40 @@ public class MySqlDdl extends PlatformDdl {
buffer.append("CALL usp_ebean_drop_column('").append(tableName).append("', '").append(columnName).append("')").endOfStatement();
}
@Override
public String createCheckConstraint(String ckName, String checkConstraint) {
if (USE_CHECK_CONSTRAINT) {
return super.createCheckConstraint(ckName, checkConstraint);
} else {
return null;
}
}
@Override
public String alterTableAddCheckConstraint(String tableName, String checkConstraintName, String checkConstraint) {
if (USE_CHECK_CONSTRAINT) {
return super.alterTableAddCheckConstraint(tableName, checkConstraintName, checkConstraint);
} else {
return null;
}
}
@Override
public String alterTableDropConstraint(String tableName, String constraintName) {
// drop constraint not supported in MySQL 5.7 and 8.0 but starting with MariaDB 10.2.1 CHECK is evaluated
// TODO: Implement for MariaDB >= 10.2.1
return null;
// drop constraint not supported in MySQL 5.7 and 8.0 but starting with MariaDB
// 10.2.1 CHECK is evaluated
if (USE_CHECK_CONSTRAINT) {
StringBuilder sb = new StringBuilder();
// statement for MySQL >= 8.0.16
sb.append("/*!80016 alter table ").append(tableName);
sb.append(" drop check ").append(constraintName).append(" */;\n");
// statement for MariaDB >= 10.2.1
sb.append("/*M!100201 ");
sb.append(super.alterTableDropConstraint(tableName, constraintName));
sb.append(" */");
return sb.toString();
} else {
return null;
}
}
@Override
@@ -104,7 +137,6 @@ public class MySqlDdl extends PlatformDdl {
}
buffer.append(String.format(" comment '%s'", comment));
}
}
@Override
@@ -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.
@@ -204,10 +213,21 @@ public class PlatformDdl {
*/
public void writeTableColumns(DdlBuffer apply, List<Column> columns, boolean useIdentity) throws IOException {
for (int i = 0; i < columns.size(); i++) {
if (i > 0) {
apply.append(",");
}
apply.newLine();
writeColumnDefinition(apply, columns.get(i), useIdentity);
if (i < columns.size() - 1) {
apply.append(",");
}
for (Column column : columns) {
String checkConstraint = column.getCheckConstraint();
if (hasValue(checkConstraint)) {
checkConstraint = createCheckConstraint(maxConstraintName(column.getCheckConstraintName()), checkConstraint);
if (hasValue(checkConstraint)) {
apply.append(",").newLine();
apply.append(checkConstraint);
}
}
}
}
@@ -230,13 +250,27 @@ 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");
}
/**
* Returns the check constraint.
*/
public String createCheckConstraint(String ckName, String checkConstraint) {
return " constraint " + ckName + " " + checkConstraint;
}
/**
* Convert the DB column default literal to platform specific.
*/
@@ -409,13 +443,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,16 +503,17 @@ public class PlatformDdl {
if (!onHistoryTable) {
if (isTrue(column.isNotnull())) {
buffer.append(" not null");
writeColumnNotNull(buffer);
}
buffer.append(addColumnSuffix);
buffer.endOfStatement();
// check constraints cannot be added in one statement for h2
if (!StringHelper.isNull(column.getCheckConstraint())) {
String ddl = alterTableAddCheckConstraint(tableName, column.getCheckConstraintName(),
column.getCheckConstraint());
buffer.append(ddl).endOfStatement();
String ddl = alterTableAddCheckConstraint(tableName, column.getCheckConstraintName(), column.getCheckConstraint());
if (hasValue(ddl)) {
buffer.append(ddl).endOfStatement();
}
}
} else {
buffer.append(addColumnSuffix);
@@ -485,7 +524,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();
}
/**
@@ -560,7 +599,7 @@ public class PlatformDdl {
}
protected void appendWithSpace(String content, StringBuilder buffer) {
if (content != null && !content.isEmpty()) {
if (hasValue(content)) {
buffer.append(" ").append(content);
}
}
@@ -593,6 +632,13 @@ public class PlatformDdl {
return updateNullWithDefault;
}
/**
* Return true if null or trimmed string is empty.
*/
protected boolean hasValue(String value) {
return value != null && !value.trim().isEmpty();
}
/**
* Null safe Boolean true test.
*/
@@ -607,6 +653,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 +696,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 +707,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()) {
@@ -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
@@ -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));
}
@@ -10,6 +10,7 @@ import io.ebean.meta.MetaQueryPlan;
import io.ebean.meta.MetaTimedMetric;
import io.ebean.meta.MetricVisitor;
import io.ebean.meta.QueryPlanRequest;
import io.ebean.meta.ServerMetrics;
import java.util.ArrayList;
import java.util.List;
@@ -35,6 +36,11 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
server.visitMetrics(visitor);
}
@Override
public ServerMetrics collectMetrics() {
return visitBasic();
}
@Override
public BasicMetricVisitor visitBasic() {
BasicMetricVisitor basic = new BasicMetricVisitor();
@@ -505,6 +505,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private void shutdownPlugins() {
if (serverConfig.isDumpMetricsOnShutdown()) {
new DumpMetrics(this, serverConfig.getDumpMetricsOptions()).dump();
}
for (Plugin plugin : serverPlugins) {
try {
plugin.shutdown();
@@ -1234,7 +1238,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
SpiOrmQueryRequest<T> request = createQueryRequest(spiQuery, t);
request.profileLocationById();
if (request.isUseDocStore()) {
return docStore().find(request);
}
@@ -1588,7 +1591,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private <T> List<T> findList(Query<T> query, Transaction t, boolean findOne) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, t);
request.profileLocationAll();
request.resetBeanCacheAutoMode(findOne);
Object result = request.getFromQueryCache();
if (result != null) {
@@ -37,6 +37,7 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
super(server, query, null);
this.queryEngine = engine;
this.query = query;
query.obtainLocation();
}
/**
@@ -44,6 +45,7 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
*/
@Override
public void executeSql(Binder binder, SpiQuery.Type type) throws SQLException {
startNano = System.nanoTime();
SpiQuery<?> ormQuery = query.getOrmQuery();
if (ormQuery != null) {
ormQuery.setType(type);
@@ -0,0 +1,174 @@
package io.ebeaninternal.server.core;
import io.ebean.ProfileLocation;
import io.ebean.meta.MetaOrmQueryMetric;
import io.ebean.meta.MetaQueryMetric;
import io.ebean.meta.MetaTimedMetric;
import io.ebean.meta.ServerMetrics;
import io.ebean.meta.SortMetric;
import io.ebeaninternal.api.SpiEbeanServer;
import java.util.Comparator;
import java.util.List;
class DumpMetrics {
private final SpiEbeanServer server;
private final String options;
private final String nameFormat;
private final String nameFormatTimed;
private boolean dumpHash;
private boolean dumpSql;
private boolean dumpLoc;
private Comparator<MetaTimedMetric> sortBy = SortMetric.NAME;
DumpMetrics(SpiEbeanServer server, String options) {
this.server = server;
this.options = options;
int width = 0;
if (options != null) {
dumpLoc = options.contains("loc");
dumpSql = options.contains("sql");
dumpHash = options.contains("hash");
for (int i = 5; i < 10; i++) {
width = Math.max(width, optionWidth(i * 10));
}
for (String option : new String[]{"Total", "Count", "Mean", "Max"}) {
sortOption(option);
}
}
if (width == 0) {
width = 80;
}
nameFormat = "%1$-" + width + "s";
nameFormatTimed = "%1$-" + (width + 6) + "s";
}
private int optionWidth(int check) {
return options.contains("w" + check) ? check : 0;
}
private void sortOption(String option) {
if (options.contains("sort" + option)) {
sortBy = setSortOption(option);
}
}
private Comparator<MetaTimedMetric> setSortOption(String option) {
switch (option.toUpperCase()) {
case "TOTAL":
return SortMetric.TOTAL;
case "COUNT":
return SortMetric.COUNT;
case "MEAN":
return SortMetric.MEAN;
case "MAX":
return SortMetric.MAX;
}
return SortMetric.NAME;
}
void dump() {
out("-- Dumping metrics for " + server.getName() + " -- ");
ServerMetrics serverMetrics = server.getMetaInfoManager().collectMetrics();
for (MetaTimedMetric metric : serverMetrics.getTimedMetrics()) {
log(metric);
}
List<MetaOrmQueryMetric> ormQueryMetrics = serverMetrics.getOrmQueryMetrics();
if (!ormQueryMetrics.isEmpty()) {
out("\n-- ORM queries --");
ormQueryMetrics.sort(sortBy);
for (MetaOrmQueryMetric metric : ormQueryMetrics) {
logQuery(metric);
}
}
List<MetaQueryMetric> dtoQueryMetrics = serverMetrics.getDtoQueryMetrics();
if (!dtoQueryMetrics.isEmpty()) {
out("\n-- DTO queries --");
dtoQueryMetrics.sort(sortBy);
for (MetaQueryMetric metric : dtoQueryMetrics) {
logDtoQuery(metric);
}
}
}
private void out(String sb) {
System.out.println(sb);
}
private void logQuery(MetaOrmQueryMetric metric) {
StringBuilder sb = new StringBuilder();
appendQueryName(metric, sb);
appendCounters(metric, sb);
if (dumpHash) {
sb.append("\n hash:").append(metric.getQueryPlanHash());
}
appendProfileAndSql(metric, sb);
out(sb.toString());
}
private void logDtoQuery(MetaQueryMetric metric) {
StringBuilder sb = new StringBuilder();
appendQueryName(metric, sb);
appendCounters(metric, sb);
appendProfileAndSql(metric, sb);
out(sb.toString());
}
private void appendQueryName(MetaQueryMetric metric, StringBuilder sb) {
sb.append("query:").append(padName(metric.getName())).append(" ");
}
private void appendProfileAndSql(MetaQueryMetric metric, StringBuilder sb) {
ProfileLocation profileLocation = metric.getProfileLocation();
if (dumpLoc && profileLocation != null) {
sb.append("\n loc:").append(profileLocation.shortDescription());
}
if (dumpSql) {
sb.append(" \n\n sql:").append(metric.getSql()).append("\n\n");
}
}
private void log(MetaTimedMetric metric) {
StringBuilder sb = new StringBuilder();
sb.append(padNameTimed(metric.getName())).append(" ");
appendCounters(metric, sb);
out(sb.toString());
}
private void appendCounters(MetaTimedMetric timedMetric, StringBuilder sb) {
sb.append(" count:").append(pad(timedMetric.getCount()))
.append(" total:").append(pad(timedMetric.getTotal()))
.append(" mean:").append(pad(timedMetric.getMean()))
.append(" max:").append(pad(timedMetric.getMax()));
}
private String padName(String name) {
return String.format(nameFormat, name);
}
private String padNameTimed(String name) {
return String.format(nameFormatTimed, name);
}
private String pad(long value) {
return String.format("%1$-8s", value);
}
}

Some files were not shown because too many files have changed in this diff Show More