Compare commits

..
Author SHA1 Message Date
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
rob bygrave 14fbc3a955 [maven-release-plugin] prepare release ebean-11.34.1 2019-02-07 23:42:57 +13:00
rob bygrave e41da2f995 #1632 - ENH: Add bind parameter expansion for SqlUpdate
Fix test with bind type.
2019-02-07 23:35:44 +13:00
rob bygrave ea60e42a4d #1633 - ENH: Add bind parameter expansion for raw() expressions 2019-02-07 23:29:42 +13:00
rob bygrave efce6b57e1 #1632 - ENH: Add bind parameter expansion for SqlUpdate 2019-02-07 23:19:00 +13:00
rob bygrave 988e4c7386 WIP Bind parameter expansion 2019-02-07 21:10:57 +13:00
rob bygrave 9e1d7ab722 #1631 - Change logging for SqlUpdate ... such that it is more consistent with other SQL logging wrt JDBC batch 2019-02-07 17:20:49 +13:00
rob bygrave 0c03a8b925 #1630 - ENH: Improve jdbc batch use when persisting ManyToMany intersection changes 2019-02-05 17:35:58 +13:00
rob bygrave 37a66f1ee2 Playing around with SqlUpdate and bind expansion for ?1 etc 2019-02-04 14:24:47 +13:00
rob bygrave a02673359d No effective change - tidy BindParams 2019-02-04 12:34:02 +13:00
rob bygrave f9d13cbba7 Merge branch 'master' of github.com:ebean-orm/ebean 2019-02-04 11:55:04 +13:00
rob bygrave f787483e30 #1629 - ENH: Add sqlQuery(sql) and sqlUpdate(sql) ... as alias for createSqlQuery(sql) and createSqlUpdate(sql) 2019-02-04 11:54:45 +13:00
rob bygrave 9e33f36808 #1629 - ENH: Add sqlQuery(sql) and sqlUpdate(sql) ... as alias for createSqlQuery(sql) and createSqlUpdate(sql) 2019-02-04 11:14:54 +13:00
Roland PramlandRob Bygrave 6a7e6caa66 Test fixes (#1628)
* FIX: mysql index sizes

* FIX: osolete whitespace in sqlserver tests

* Updated mssql-jdbc, this fixes problems in the batch insert (https://github.com/Microsoft/mssql-jdbc/pull/912)

* fix tablename  (reserved word in mariadb)

* ignored failing tests for sqlserver
2019-02-04 09:20:09 +13:00
rob bygrave 5a4a0e3886 Bump version to 11.34.1-SNAPSHOT 2019-02-03 22:39:23 +13:00
rob bygrave c3f586c461 Merge branch 'feature/1624-Database' 2019-02-03 22:21:36 +13:00
rob bygrave 99f7243d6c #1627 - Bump ebean-datasource to 4.3.4 2019-02-03 22:14:25 +13:00
rob bygrave 7026c00290 [maven-release-plugin] prepare for next development iteration 2019-02-02 22:29:02 +13:00
rob bygrave 448d94a335 [maven-release-plugin] prepare release ebean-11.33.3 2019-02-02 22:28:47 +13:00
rob bygrave 961a7ef7ea #1626 - ENH: Add SqlUpdate.setParams(Object ... ) and SqlQuery.setParams(Object ... ) ... as convenient alternative to many setParameter() calls 2019-02-02 22:17:07 +13:00
rob bygrave 5cef1ad017 #1625 - Populate UUID via database column default value / impossible to disable automatic generation for @Id fields of type UUID. 2019-02-02 20:16:05 +13:00
rob bygrave b814ca6e81 Bump ebean-migration to 11.14.1 with logging improvement 2019-02-02 17:37:21 +13:00
rob bygrave a24e6b6064 #1623 - DbOrderByTrim doesn't remove asc/desc if you're sorting by a function called on a column
This fix removes the word boundary check prior to the blank space for asc and desc (allowing the preceeding char to be a brace or anything)
2019-02-02 00:40:56 +13:00
rob bygrave b751823abb #1624 - ENH: Create io.ebean.DB as alias for Ebean and io.ebean.Database as alias for EbeanServer
Adjust Model and Finder to use DB and Database
2019-02-01 23:20:53 +13:00
rob bygrave f3cb7e2cfd #1624 - ENH: Create io.ebean.DB as alias for Ebean and io.ebean.Database as alias for EbeanServer 2019-02-01 22:53:41 +13:00
rob bygrave 31d7c6fed8 #1622 - ENH: Bump ebean-datasource to 4.3.3 with improved DataSourceInitialiseException 2019-01-30 12:10:16 +13:00
rob bygrave c5fb1dbe23 [maven-release-plugin] prepare for next development iteration 2019-01-29 15:59:24 +13:00
rob bygrave b7770a9617 [maven-release-plugin] prepare release ebean-11.33.2 2019-01-29 15:59:11 +13:00
rob bygrave 41abf34ef6 #1621 - Consider clean up white space in generated SQL (as in remove unneeded extra spaces) 2019-01-25 01:52:23 +13:00
rob bygrave 89beaee0f6 #1621 - Consider clean up white space in generated SQL (as in remove unneeded extra spaces) 2019-01-25 01:47:21 +13:00
rob bygrave 5052afbd07 #1620 - Support case statement in order by clause 2019-01-23 22:28:53 +13:00
rob bygrave d89a58482c no effective change - tidy test order by parse 2019-01-23 21:48:49 +13:00
rob bygrave eb1bca805e #1619 - Support aggregation formula on fetch clause e.g. query.fetch("machineUsage", "sum(totalKms)") 2019-01-23 16:45:47 +13:00
rob bygrave 9cb66b112f [maven-release-plugin] prepare for next development iteration 2019-01-23 00:31:34 +13:00
rob bygrave 210901ee2d [maven-release-plugin] prepare release ebean-11.33.1 2019-01-23 00:31:18 +13:00
rob bygrave 396fb49acd #1618 - ENH: Add @Sum ... as short hand for @Aggregation("sum(<property name>)") 2019-01-23 00:29:51 +13:00
rob bygrave a83896f1ff #1617 - save() on a reference bean causes insert to occur ... results in duplicate key exception
Fix
2019-01-22 21:59:17 +13:00
rob bygrave 3483372ae3 #1617 - save() on a reference bean causes insert to occur ... results in duplicate key exception
Tests broken by fix as the test inserts only included id value in the insert
2019-01-22 21:59:04 +13:00
rob bygrave 4e1f817ade #1617 - save() on a reference bean causes insert to occur ... results in duplicate key exception
Tests that reproduce the issue (supplied by Andre - thanks for that)
2019-01-22 21:54:03 +13:00
rob bygrave d4139aa8ef #1616 - Enhance EQL grammer 1) Allow select without ( and ) 2) support aggregate functions min,max,sum,count,avg 3) support quoting fetch paths (that are keywords)
Fix test for Postgres any()
2019-01-22 21:23:16 +13:00
rob bygrave a6cfed7c44 #1616 - Enhance EQL grammer 1) Allow select without ( and ) 2) support aggregate functions min,max,sum,count,avg 3) support quoting fetch paths (that are keywords) 2019-01-22 21:12:18 +13:00
rob bygrave cc17ea8911 No effective change - improve tests for TestAggregationTopLevel 2019-01-22 20:26:50 +13:00
rob bygrave 141f0a8b16 Testing out concept - aggregation formula on fetch clause 2019-01-22 15:56:20 +13:00
rob bygrave 9dbd903a73 No effective change - Update tests for aggregation queries 2019-01-22 15:53:47 +13:00
rob bygrave b8105190d1 #1615 - findNative() with List/array named parameter throws error ... query plan key doesn't take bind count into account 2019-01-22 01:56:58 +13:00
rob bygrave ef3d911e19 #1614 - Refactor DbMigration.generateMigration() to use System out to log key messages and throw UnknownResourcePathException 2019-01-22 01:08:15 +13:00
rob bygrave 31fd0b7ed5 Bump version to 11.33.1-SNAPSHOT 2019-01-22 01:04:25 +13:00
rob bygrave 26d1288757 #1613 - When using transaction.setPersistCascade(false) ... a Delete query should not cascade 2019-01-22 00:05:19 +13:00
rob bygrave 1d49e5b341 #1613 - When using transaction.setPersistCascade(false) ... a Delete query should not cascade 2019-01-21 22:09:15 +13:00
rob bygrave 4a0806e453 #1612 - query.setMapKey("customer.id") throws NullPointerException (ie. mapKey is a path rather than local property) 2019-01-21 21:57:30 +13:00
rob bygrave ab48f796c6 #1611 - ENH: Add inRange expression (as half open expression) ... (property >= lowerValue and property < upperValue)
Bump antlr4-runtime from 4.7.1 to 4.7.2
2019-01-21 20:30:39 +13:00
rob bygrave 0cb8bb0415 #1611 - ENH: Add inRange expression (as half open expression) ... (property >= lowerValue and property < upperValue)
Add inrange to EQL grammer
2019-01-21 20:28:38 +13:00
rob bygrave 471691c747 #1611 - ENH: Add inRange expression (as half open expression) ... (property >= lowerValue and property < upperValue) 2019-01-21 20:01:57 +13:00
rob bygrave c86e6b3cf9 #1610 - Refactor: Change SQL logging such that jdbc batch use is more obvious 2019-01-21 19:31:28 +13:00
rob bygrave b399713a85 No effective change - update readme 2019-01-17 17:19:35 +13:00
rob bygrave f8d4ad7f33 No effective change - update on Transaction 2019-01-16 15:44:45 +13:00
rob bygrave 7984a9ccd1 No effective change - update test to use try with resources 2019-01-16 11:13:43 +13:00
rob bygrave 27cda09523 Merge branch 'master' of github.com:ebean-orm/ebean 2019-01-15 22:16:15 +13:00
rob bygrave 20fc23c19c No effective change - improve javadoc for ExpressionList 2019-01-15 22:16:03 +13:00
André CamiloandRob Bygrave bac53a5d75 FIX: insert in batch OneToOne relationship (#1609) 2019-01-15 11:56:52 +13:00
rob bygrave 2e0690efa9 No effective change - improve javadoc for Transaction and EbeanServer 2019-01-14 23:10:56 +13:00
rob bygrave fdd9b1905c No effective change - improve javadoc for query.exists() and query.setBaseTable() 2019-01-10 14:52:55 +13:00
rob bygrave fc966618a6 No effective change - improve javadoc for Query.setInheritType() 2019-01-10 14:42:55 +13:00
rob bygrave 669ba7837a [maven-release-plugin] prepare for next development iteration 2019-01-10 03:44:59 +13:00
rob bygrave 0edf90056c [maven-release-plugin] prepare release ebean-11.32.1 2019-01-10 03:44:44 +13:00
Rob BygraveandGitHub 6b27a9d280 Support @ManyToOne inside @Embeddables - fix for #52 (#1608)
* #52 - @ManyToOne support inside @Embeddables

Initial test and work

* #52 - @ManyToOne support inside @Embeddables

Initial test and work

* #52 - @ManyToOne support inside @Embeddables

Initial test and work

* #52 - @ManyToOne support inside @Embeddables

Initial test and work

* #52 - @ManyToOne support inside @Embeddables

Initial test and work

* #52 - @ManyToOne support inside @Embeddables

Initial test and work

* #52 - @ManyToOne support inside @Embeddables

Update test with sql capture

* #52 - @ManyToOne support inside @Embeddables

Update test with cache clearing before hand (such that we hit DB on lazy load)
2019-01-10 03:40:02 +13:00
rob bygrave 2b0669c2ae #1594 - Support loading of beans with invalid JSON content
Adjust to be per query via query.setAllowLoadErrors() rather than a
global config via ServerConfig.
2019-01-09 20:03:26 +13:00
rob bygrave 9fc10e8e04 Merge branch 'FOCONIS-feature/load-error' 2019-01-09 19:26:13 +13:00
rob bygrave fb5eb0cdd2 Merge branch 'feature/load-error' of https://github.com/FOCONIS/ebean into FOCONIS-feature/load-error 2019-01-09 19:25:50 +13:00
rob bygrave 3351882915 #1602 - Adjust test adding sql checking for discriminator 2019-01-09 19:20:05 +13:00
Roland PramlandRob Bygrave 31c6d16e73 A query on inherited beans can be restricted to the subtype of one of its children (#1602)
ENH: A query on inherited beans can be restricted to the subtype of one of its children
2019-01-09 19:15:04 +13:00
rob bygrave cd8c12a9a8 #1566 - ENH: Support query.setBaseTable() ... to query against dynamic table (ala manual table partitioning) 2019-01-09 19:13:35 +13:00
rob bygrave 3207ae1f08 No effective change - tidy test 2019-01-09 19:05:16 +13:00
Roland PramlandRob Bygrave 5fe532c393 Refactor: relpaced system-property "ebean.ignoreExtraDdl" with serverConfig flag (#1597) 2019-01-09 16:17:11 +13:00
rob bygrave 93b9cbf9ac bump to 11.32.1-SNAPSHOT 2019-01-09 14:01:25 +13:00
rob bygrave 8a34d6050b #1607 - ENH: Add query.exists() query ... as a convenience for "does a row exist" check 2019-01-09 13:59:53 +13:00
rob bygrave ab3e6ccbf7 [maven-release-plugin] prepare for next development iteration 2019-01-09 12:43:01 +13:00
Roland Praml 4d93b70bf9 Supports loading of corrupt beans 2019-01-03 11:43:50 +01:00
335 changed files with 13746 additions and 8321 deletions
+5 -3
View File
@@ -1,8 +1,11 @@
[![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)
# Need help?
Post questions or issues to the Ebean google group - https://groups.google.com/forum/#!forum/ebean
# Documentation
Goto [https://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Website")
Goto [https://ebean.io/docs/](https://ebean.io/docs/)
## Maven cental links:
@@ -10,8 +13,7 @@ Goto [https://ebean-orm.github.io/](http://ebean-orm.github.io/ "Ebean ORM's Web
[Maven central - all related projects](http://search.maven.org/#search%7Cga%7C1%7Cebean "maven central all related projects")
## Current versions
* [![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) - ebean
## Other versions
* [![Maven Central : ebean-agent](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent) - ebean-agent
* [![Maven Central : ebean-maven-plugin](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin) - ebean-maven-plugin
+32 -10
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.31.3</version>
<version>11.36.1</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -22,7 +22,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-11.31.3</tag>
<tag>ebean-11.36.1</tag>
</scm>
<profiles>
@@ -117,13 +117,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>4.4</version>
<version>4.6</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-types</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-datasource</artifactId>
<version>4.3.2</version>
<version>4.5.1</version>
</dependency>
<dependency>
@@ -135,13 +141,13 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration</artifactId>
<version>11.13.1</version>
<version>11.14.1</version>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.7.1</version>
<version>4.7.2</version>
</dependency>
<!-- Jackson core used internally by Ebean -->
@@ -188,6 +194,21 @@
<scope>provided</scope>
</dependency>
<!-- Provided scope to compile with java 11 -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.4.0-b180830.0438</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
<scope>provided</scope>
</dependency>
<!-- Provided scope for Postgres JSON/JSONB support -->
<dependency>
<groupId>org.postgresql</groupId>
@@ -222,7 +243,7 @@
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-agentloader</artifactId>
<version>2.1.2</version>
<version>3.0.1</version>
<scope>test</scope>
</dependency>
@@ -259,7 +280,7 @@
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>6.4.0.jre8</version>
<version>7.2.0.jre8</version>
<scope>test</scope>
</dependency>
@@ -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);
+9 -2
View File
@@ -1,5 +1,6 @@
package io.ebean;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.Set;
@@ -56,7 +57,7 @@ public interface BeanState {
*
* // set loaded state on the email property to false so that
* // the email property is not included in a stateless update
* Ebean.getBeanState(user).setPropertyLoaded("email", false);
* DB.getBeanState(user).setPropertyLoaded("email", false);
*
* user.update();
*
@@ -101,7 +102,7 @@ public interface BeanState {
/**
* Advanced - Used to programmatically build a partially or fully loaded
* entity bean. First create an entity bean via
* {@link EbeanServer#createEntityBean(Class)}, then populate its properties
* {@link Database#createEntityBean(Class)}, then populate its properties
* and then call this method specifying which properties where loaded or null
* for a fully loaded entity bean.
*/
@@ -111,4 +112,10 @@ public interface BeanState {
* Reset the bean putting it into NEW state such that a save() results in an insert.
*/
void resetForInsert();
/**
* Returns a map with load erros.
*/
@Nullable
Map<String, Exception> getLoadErrors();
}
+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>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,67 @@
package io.ebean;
import io.ebean.config.ContainerConfig;
import io.ebean.config.DatabaseConfig;
/**
* Creates Database instances.
* <p>
* This uses either DatabaseConfig or properties in the application.properties file to
* configure and create a Database instance.
* </p>
* <p>
* The Database instance can either be registered with the DB singleton or
* not. The DB singleton effectively holds a map of Database by a name.
* If the Database is registered with the DB singleton you can retrieve it
* later via {@link DB#byName(String)}.
* </p>
* <p>
* One Database can be nominated as the 'default/primary' Database. Many
* methods on the DB singleton such as {@link DB#find(Class)} are just a
* convenient way of using the 'default/primary' Database.
* </p>
*/
public class DatabaseFactory {
/**
* Initialise the container with clustering configuration.
* <p>
* Call this prior to creating any Database instances or alternatively set the
* ContainerConfig on the ServerConfig when creating the first Database instance.
*/
public static synchronized void initialiseContainer(ContainerConfig containerConfig) {
EbeanServerFactory.initialiseContainer(containerConfig);
}
/**
* Create using ebean.properties to configure the server.
*/
public static synchronized Database create(String name) {
return EbeanServerFactory.create(name);
}
/**
* Create using the ServerConfig object to configure the server.
*/
public static synchronized Database create(DatabaseConfig config) {
return EbeanServerFactory.create(config);
}
/**
* Create using the ServerConfig additionally specifying a classLoader to use as the context class loader.
*/
public static synchronized Database createWithContextClassLoader(DatabaseConfig config, ClassLoader classLoader) {
return EbeanServerFactory.createWithContextClassLoader(config, classLoader);
}
/**
* Shutdown gracefully all Database instances cleaning up any resources as required.
* <p>
* This is typically invoked via JVM shutdown hook and not explicitly called.
* </p>
*/
public static synchronized void shutdown() {
EbeanServerFactory.shutdown();
}
}
+1 -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();
+17 -104
View File
@@ -24,113 +24,26 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
/**
* This Ebean object is effectively a singleton that holds a map of registered
* {@link EbeanServer}s. It additionally provides a convenient way to use the
* 'default' EbeanServer.
* Ebean is a registry of {@link Database} by name. Ebean has now been renamed to {@link DB}.
* <p>
* If you are using a Dependency Injection framework such as
* <strong>Spring</strong> or <strong>Guice</strong> you will probably
* <strong>NOT</strong> use this Ebean singleton object. Instead you will
* configure and construct EbeanServer instances using {@link ServerConfig} and
* {@link EbeanServerFactory} and inject those EbeanServer instances into your
* data access objects.
* </p>
* Ebean is effectively this is an alias for {@link DB} which is the new and improved name for Ebean.
* <p>
* In documentation "Ebean singleton" refers to this object.
* </p>
* <ul>
* <li>There is one EbeanServer per Database (javax.sql.DataSource).</li>
* <li>EbeanServers can be 'registered' with the Ebean singleton (put into its
* map). Registered EbeanServer's can later be retrieved via
* {@link #getServer(String)}.</li>
* <li>One EbeanServer can be referred to as the 'default' EbeanServer. For
* convenience, the Ebean singleton (this object) provides methods such as
* {@link #find(Class)} that proxy through to the 'default' EbeanServer. This
* can be useful for applications that use a single database.</li>
* </ul>
* <p>
* For developer convenience Ebean has static methods that proxy through to the
* methods on the <em>'default'</em> EbeanServer. These methods are provided for
* developers who are mostly using a single database. Many developers will be
* able to use the methods on Ebean rather than get a EbeanServer.
* </p>
* <p>
* EbeanServers can be created and used without ever needing or using the Ebean
* singleton. Refer to {@link ServerConfig#setRegister(boolean)}.
* </p>
* <p>
* You can either programmatically create/register EbeanServers via
* {@link EbeanServerFactory} or they can automatically be created and
* registered when you first use the Ebean singleton. When EbeanServers are
* created automatically they are configured using information in the
* ebean.properties file.
* </p>
* <pre>{@code
*
* // fetch shipped orders (and also their customer)
* List<Order> list = Ebean.find(Order.class)
* .fetch("customer")
* .where()
* .eq("status.code", Order.Status.SHIPPED)
* .findList();
*
* // read/use the order list ...
* for (Order order : list) {
* Customer customer = order.getCustomer();
* ...
* }
*
* }</pre>
* <pre>{@code
*
* // fetch order 10, modify and save
* Order order = Ebean.find(Order.class, 10);
*
* OrderStatus shipped = Ebean.getReference(OrderStatus.class,"SHIPPED");
* order.setStatus(shipped);
* order.setShippedDate(shippedDate);
* ...
*
* // implicitly creates a transaction and commits
* Ebean.save(order);
*
* }</pre>
* <p>
* When you have multiple databases and need access to a specific one the
* {@link #getServer(String)} method provides access to the EbeanServer for that
* specific database.
* </p>
* <pre>{@code
*
* // Get access to the Human Resources EbeanServer/Database
* EbeanServer hrDb = Ebean.getServer("hr");
*
* // fetch contact 3 from the HR database
* Contact contact = hrDb.find(Contact.class, 3);
*
* contact.setName("I'm going to change");
* ...
*
* // save the contact back to the HR database
* hrDb.save(contact);
*
* }</pre>
* The preference is to use DB and Database rather than Ebean and EbeanServer.
*/
public final class Ebean {
private static final Logger logger = LoggerFactory.getLogger(Ebean.class);
static {
EbeanVersion.getVersion(); // initalizes the version class and logs the version.
EbeanVersion.getVersion(); // initialises the version class and logs the version.
}
/**
* Manages creation and cache of EbeanServers.
* Manages creation and cache of Databases.
*/
private static final Ebean.ServerManager serverMgr = new Ebean.ServerManager();
/**
* Helper class for managing fast and safe access and creation of
* EbeanServers.
* Helper class for managing fast and safe access and creation of Databases.
*/
private static final class ServerManager {
@@ -147,7 +60,7 @@ public final class Ebean {
private final Object monitor = new Object();
/**
* The 'default' EbeanServer.
* The 'default' Database.
*/
private EbeanServer defaultServer;
@@ -166,20 +79,20 @@ public final class Ebean {
throw e;
} catch (DataSourceConfigurationException e) {
String msg = "Configuration error creating DataSource for the default EbeanServer." +
String msg = "Configuration error creating DataSource for the default Database." +
" This typically means a missing application-test.yaml or missing ebean-test-config dependency." +
" See https://ebean.io/docs/trouble-shooting#datasource";
throw new DataSourceConfigurationException(msg, e);
} catch (Throwable e) {
logger.error("Error trying to create the default EbeanServer", e);
logger.error("Error trying to create the default Database", e);
throw new RuntimeException(e);
}
}
private EbeanServer getDefaultServer() {
if (defaultServer == null) {
String msg = "The default EbeanServer has not been defined?";
String msg = "The default Database has not been defined?";
msg += " This is normally set via the ebean.datasource.default property.";
msg += " Otherwise it should be registered programmatically via registerServer()";
throw new PersistenceException(msg);
@@ -201,7 +114,7 @@ public final class Ebean {
}
/**
* Synchronized read, create and put of EbeanServers.
* Synchronized read, create and put of Databases.
*/
private EbeanServer getWithCreate(String name) {
@@ -240,7 +153,7 @@ public final class Ebean {
}
/**
* Get the EbeanServer for a given DataSource. If name is null this will
* Get the Database for a given DataSource. If name is null this will
* return the 'default' EbeanServer.
* <p>
* This is provided to access EbeanServer for databases other than the
@@ -631,7 +544,8 @@ public final class Ebean {
* Customer customer = new Customer();
* customer.setId(7);
* customer.setName("ModifiedNameNoOCC");
* ebeanServer.update(customer);
*
* DB.update(customer);
*
* }</pre>
*
@@ -1131,10 +1045,9 @@ public final class Ebean {
*
* String sql = "select c.id, c.name from customer c where c.name like ? order by c.name";
*
* Query<Customer> query = ebeanServer.findNative(Customer.class, sql);
* query.setParameter(1, "Rob%");
*
* List<Customer> customers = query.findList();
* List<Customer> customers = DB.findNative(Customer.class, sql)
* .setParameter(1, "Rob%")
* .findList()
*
* }</pre>
*
File diff suppressed because it is too large Load Diff
@@ -22,8 +22,8 @@ package io.ebean;
* example.setName("Rob%");
* example.setNotes("%something%");
*
* List&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();
+50 -17
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()
*/
@@ -64,11 +52,20 @@ public class Expr {
return Ebean.getExpressionFactory().ieq(propertyName, value);
}
/**
* In Range - property >= value1 and property < value2.
* <p>
* Unlike Between inRange is "half open" and usually more useful for use with dates or timestamps.
* </p>
*/
public static Expression inRange(String propertyName, Object value1, Object value2) {
return Ebean.getExpressionFactory().inRange(propertyName, value1, value2);
}
/**
* Between - property between the two given values.
*/
public static Expression between(String propertyName, Object value1, Object value2) {
return Ebean.getExpressionFactory().between(propertyName, value1, value2);
}
@@ -76,7 +73,6 @@ public class Expr {
* Between - value between two given properties.
*/
public static Expression between(String lowProperty, String highProperty, Object value) {
return Ebean.getExpressionFactory().betweenProperties(lowProperty, highProperty, value);
}
@@ -242,6 +238,43 @@ public class Expr {
return Ebean.getExpressionFactory().in(propertyName, values);
}
/**
* In where null or empty values means that no predicate is added to the query.
* <p>
* That is, only add the IN predicate if the values are not null or empty.
* <p>
* Without this we typically need to code an <code>if</code> block to only add
* the IN predicate if the collection is not empty like:
* </p>
*
* <h3>Without inOrEmpty()</h3>
* <pre>{@code
*
* query.where() // add some predicates
* .eq("status", Status.NEW);
*
* if (ids != null && !ids.isEmpty()) {
* query.where().in("customer.id", ids);
* }
*
* query.findList();
*
* }</pre>
*
* <h3>Using inOrEmpty()</h3>
* <pre>{@code
*
* query.where()
* .eq("status", Status.NEW)
* .inOrEmpty("customer.id", ids)
* .findList();
*
* }</pre>
*/
public static Expression inOrEmpty(String propertyName, Collection<?> values) {
return Ebean.getExpressionFactory().inOrEmpty(propertyName, values);
}
/**
* Id Equal to - ID property is equal to the value.
*/
+86 -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.
*/
@@ -149,6 +154,33 @@ public interface ExpressionFactory {
*/
Expression ineObject(String propertyName, Object value);
/**
* In Range - property >= value1 and property < value2.
* <p>
* Unlike Between inRange is "half open" and usually more useful for use with dates or timestamps.
* </p>
*/
Expression inRange(String propertyName, Object value1, Object value2);
/**
* Value in Range between 2 properties.
*
* <pre>{@code
*
* .startDate.inRangeWith(endDate, now)
*
* // which equates to
* startDate <= now and (endDate > now or endDate is null)
*
* }</pre>
*
* <p>
* This is a convenience expression combining a number of simple expressions.
* The most common use of this could be called "effective dating" where 2 date or
* timestamp columns represent the date range in which
*/
Expression inRangeWith(String lowProperty, String highProperty, Object value);
/**
* Between - property between the two given values.
*/
@@ -159,6 +191,14 @@ public interface ExpressionFactory {
*/
Expression betweenProperties(String lowProperty, String highProperty, Object value);
/**
* Greater Than Or Null - property greater than the given value or null.
* <p>
* A convenient expression combining GT and Is Null. Most often useful for range
* expressions where the top range value is nullable.
*/
Expression gtOrNull(String propertyName, Object value);
/**
* Greater Than - property greater than the given value.
*/
@@ -170,6 +210,14 @@ public interface ExpressionFactory {
*/
Expression ge(String propertyName, Object value);
/**
* Less Than or Null - property less than the given value or null.
* <p>
* A convenient expression combining LT and Is Null. Most often useful for range
* expressions where the bottom range value is nullable.
*/
Expression ltOrNull(String propertyName, Object value);
/**
* Less Than - property less than the given value.
*/
@@ -277,6 +325,41 @@ public interface ExpressionFactory {
*/
Expression in(String propertyName, Collection<?> values);
/**
* In where null or empty values means that no predicate is added to the query.
* <p>
* That is, only add the IN predicate if the values are not null or empty.
* <p>
* Without this we typically need to code an <code>if</code> block to only add
* the IN predicate if the collection is not empty like:
* </p>
*
* <h3>Without inOrEmpty()</h3>
* <pre>{@code
*
* query.where() // add some predicates
* .eq("status", Status.NEW);
*
* if (ids != null && !ids.isEmpty()) {
* query.where().in("customer.id", ids);
* }
*
* query.findList();
*
* }</pre>
*
* <h3>Using inOrEmpty()</h3>
* <pre>{@code
*
* query.where()
* .eq("status", Status.NEW)
* .inOrEmpty("customer.id", ids)
* .findList();
*
* }</pre>
*/
Expression inOrEmpty(String propertyName, Collection<?> values);
/**
* Not In - property has a value in the array of values.
*/
+212 -55
View File
@@ -226,6 +226,32 @@ public interface ExpressionList<T> {
*/
int update(Transaction transaction);
/**
* Execute the query returning true if a row is found.
* <p>
* The query is executed using max rows of 1 and will only select the id property.
* This method is really just a convenient way to optimise a query to perform a
* 'does a row exist in the db' check.
* </p>
*
* <h2>Example:</h2>
* <pre>{@code
*
* boolean userExists = query().where().eq("email", "rob@foo.com").exists();
*
* }</pre>
*
* <h2>Example using a query bean:</h2>
* <pre>{@code
*
* boolean userExists = new QContact().email.equalTo("rob@foo.com").exists();
*
* }</pre>
*
* @return True if the query finds a matching row in the database
*/
boolean exists();
/**
* Execute the query iterating over the results.
*
@@ -290,23 +316,23 @@ public interface ExpressionList<T> {
/**
* Execute the query returning a list of values for a single property.
* <p>
*
* <h3>Example 1:</h3>
* <pre>{@code
*
* List<String> names =
* Ebean.find(Customer.class)
* DB.find(Customer.class)
* .select("name")
* .orderBy().asc("name")
* .findSingleAttributeList();
*
* }</pre>
* <p>
*
* <h3>Example 2:</h3>
* <pre>{@code
*
* List<String> names =
* Ebean.find(Customer.class)
* DB.find(Customer.class)
* .setDistinct(true)
* .select("name")
* .where().eq("status", Customer.Status.NEW)
@@ -323,11 +349,10 @@ public interface ExpressionList<T> {
/**
* Execute a query returning a single value of a single property/column.
* <p>
* <pre>{@code
*
* String name =
* Ebean.find(Customer.class)
* DB.find(Customer.class)
* .select("name")
* .where().eq("id", 42)
* .findSingleAttribute();
@@ -408,10 +433,9 @@ public interface ExpressionList<T> {
* If maxRows is not set on the query prior to calling findPagedList() then a
* PersistenceException is thrown.
* </p>
* <p>
* <pre>{@code
*
* PagedList<Order> pagedList = Ebean.find(Order.class)
* PagedList<Order> pagedList = DB.find(Order.class)
* .setFirstRow(50)
* .setMaxRows(20)
* .findPagedList();
@@ -478,7 +502,7 @@ public interface ExpressionList<T> {
* <pre>{@code
*
* List<Customer> customers =
* Ebean.find(Customer.class)
* DB.find(Customer.class)
* .setDistinct(true)
* .select("name") // only select the customer name
* .findList();
@@ -547,12 +571,11 @@ public interface ExpressionList<T> {
/**
* Extended version for setDistinct in conjunction with "findSingleAttributeList";
* <p>
* <pre>{@code
*
* List<CountedValue<Order.Status>> orderStatusCount =
*
* Ebean.find(Order.class)
* DB.find(Order.class)
* .select("status")
* .where()
* .gt("orderDate", LocalDate.now().minusMonths(3))
@@ -657,7 +680,6 @@ public interface ExpressionList<T> {
/**
* Equal to expression for the value at the given path in the JSON document.
* <p>
* <pre>{@code
*
* where().jsonEqualTo("content", "path.other", 34)
@@ -672,7 +694,6 @@ public interface ExpressionList<T> {
/**
* Not Equal to - for the given path in a JSON document.
* <p>
* <pre>{@code
*
* where().jsonNotEqualTo("content", "path.other", 34)
@@ -687,7 +708,6 @@ public interface ExpressionList<T> {
/**
* Greater than - for the given path in a JSON document.
* <p>
* <pre>{@code
*
* where().jsonGreaterThan("content", "path.other", 34)
@@ -698,7 +718,6 @@ public interface ExpressionList<T> {
/**
* Greater than or equal to - for the given path in a JSON document.
* <p>
* <pre>{@code
*
* where().jsonGreaterOrEqual("content", "path.other", 34)
@@ -709,7 +728,6 @@ public interface ExpressionList<T> {
/**
* Less than - for the given path in a JSON document.
* <p>
* <pre>{@code
*
* where().jsonLessThan("content", "path.other", 34)
@@ -720,7 +738,6 @@ public interface ExpressionList<T> {
/**
* Less than or equal to - for the given path in a JSON document.
* <p>
* <pre>{@code
*
* where().jsonLessOrEqualTo("content", "path.other", 34)
@@ -731,7 +748,6 @@ public interface ExpressionList<T> {
/**
* Between - for the given path in a JSON document.
* <p>
* <pre>{@code
*
* where().jsonBetween("content", "orderDate", lowerDateTime, upperDateTime)
@@ -742,21 +758,6 @@ public interface ExpressionList<T> {
/**
* Add an Expression to the list.
* <p>
* This returns the list so that add() can be chained.
* </p>
* <p>
* <pre>{@code
*
* Query<Customer> query = Ebean.find(Customer.class);
* query.where()
* .like("name","Rob%")
* .eq("status", Customer.ACTIVE);
*
* List<Customer> list = query.findList();
* ...
*
* }</pre>
*/
ExpressionList<T> add(Expression expr);
@@ -770,6 +771,11 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> eq(String propertyName, Object value);
/**
* Equal To or Null - property is equal to a given value or null.
*/
ExpressionList<T> eqOrNull(String propertyName, Object value);
/**
* Not Equal To - property not equal to the given value.
*/
@@ -788,8 +794,35 @@ public interface ExpressionList<T> {
ExpressionList<T> ine(String propertyName, String value);
/**
* Between - property between the two given values.
* Value in Range between 2 properties.
*
* <pre>{@code
*
* .startDate.inRangeWith(endDate, now)
*
* // which equates to
* startDate <= now and (endDate > now or endDate is null)
*
* }</pre>
*
* <p>
* This is a convenience expression combining a number of simple expressions.
* The most common use of this could be called "effective dating" where 2 date or
* timestamp columns represent the date range in which
*/
ExpressionList<T> inRangeWith(String lowProperty, String highProperty, Object value);
/**
* In Range - property >= value1 and property < value2.
* <p>
* Unlike Between inRange is "half open" and usually more useful for use with dates or timestamps.
* </p>
*/
ExpressionList<T> inRange(String propertyName, Object value1, Object value2);
/**
* Between - property between the two given values.
*/
ExpressionList<T> between(String propertyName, Object value1, Object value2);
/**
@@ -802,6 +835,11 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> gt(String propertyName, Object value);
/**
* Greater Than or Null - property greater than the given value or null.
*/
ExpressionList<T> gtOrNull(String propertyName, Object value);
/**
* Greater Than or Equal to - property greater than or equal to the given
* value.
@@ -813,6 +851,11 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> lt(String propertyName, Object value);
/**
* Less Than or Null - property less than the given value or null.
*/
ExpressionList<T> ltOrNull(String propertyName, Object value);
/**
* Less Than or Equal to - property less than or equal to the given value.
*/
@@ -842,7 +885,6 @@ public interface ExpressionList<T> {
* To get control over the options you can create an ExampleExpression and set
* those options such as case insensitive etc.
* </p>
* <p>
* <pre>{@code
*
* // create an example bean and set the properties
@@ -851,16 +893,15 @@ public interface ExpressionList<T> {
* example.setName("Rob%");
* example.setNotes("%something%");
*
* List&lt;Customer&gt; list = Ebean.find(Customer.class).where()
* // pass the bean into the where() clause
* .exampleLike(example)
* // you can add other expressions to the same query
* .gt("id", 2).findList();
* List<Customer> list =
* DB.find(Customer.class)
* .where().exampleLike(example)
* .findList();
*
* }</pre>
* <p>
* Similarly you can create an ExampleExpression
* <p>
* </p>
* <pre>{@code
*
* Customer example = new Customer();
@@ -870,7 +911,7 @@ public interface ExpressionList<T> {
* // create a ExampleExpression with more control
* ExampleExpression qbe = new ExampleExpression(example, true, LikeType.EQUAL_TO).includeZeros();
*
* List<Customer> list = Ebean.find(Customer.class).where().add(qbe).findList();
* List<Customer> list = DB.find(Customer.class).where().add(qbe).findList();
*
* }</pre>
*/
@@ -947,6 +988,41 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> in(String propertyName, Collection<?> values);
/**
* In where null or empty values means that no predicate is added to the query.
* <p>
* That is, only add the IN predicate if the values are not null or empty.
* <p>
* Without this we typically need to code an <code>if</code> block to only add
* the IN predicate if the collection is not empty like:
* </p>
*
* <h3>Without inOrEmpty()</h3>
* <pre>{@code
*
* query.where() // add some predicates
* .eq("status", Status.NEW);
*
* if (ids != null && !ids.isEmpty()) {
* query.where().in("customer.id", ids);
* }
*
* query.findList();
*
* }</pre>
*
* <h3>Using inOrEmpty()</h3>
* <pre>{@code
*
* query.where()
* .eq("status", Status.NEW)
* .inOrEmpty("customer.id", ids)
* .findList();
*
* }</pre>
*/
ExpressionList<T> inOrEmpty(String propertyName, Collection<?> values);
/**
* In - using a subQuery.
* <p>
@@ -1082,7 +1158,6 @@ public interface ExpressionList<T> {
/**
* Add expression for ALL of the given bit flags to be set.
* <p>
* <pre>{@code
*
* where().bitwiseAll("flags", BwFlags.HAS_BULK + BwFlags.HAS_COLOUR)
@@ -1096,7 +1171,6 @@ public interface ExpressionList<T> {
/**
* Add expression for the given bit flags to be NOT set.
* <p>
* <pre>{@code
*
* where().bitwiseNot("flags", BwFlags.HAS_COLOUR)
@@ -1147,6 +1221,13 @@ public interface ExpressionList<T> {
* raw("add_days(orderDate, 10) < ?", someDate)
*
* }</pre>
*
* <h4>Subquery example:</h4>
* <pre>{@code
*
* .raw("t0.customer_id in (select customer_id from customer_group where group_id = any(?::uuid[]))", groupIds)
*
* }</pre>
*/
ExpressionList<T> raw(String raw, Object value);
@@ -1171,15 +1252,83 @@ public interface ExpressionList<T> {
* then they are not translated. logical property name names (not fully
* qualified) will still be translated to their physical name.
* </p>
* <p>
* <pre>{@code
*
* raw("orderQty < shipQty")
*
* }</pre>
*
* <h4>Subquery example:</h4>
* <pre>{@code
*
* .raw("t0.customer_id in (select customer_id from customer_group where group_id = any(?::uuid[]))", groupIds)
*
* }</pre>
*/
ExpressionList<T> raw(String raw);
/**
* Only add the raw expression if the values is not null or empty.
* <p>
* This is a pure convenience expression to make it nicer to deal with the pattern where we use
* raw() expression with a subquery and only want to add the subquery predicate when the collection
* of values is not empty.
* </p>
* <h3>Without inOrEmpty()</h3>
* <pre>{@code
*
* query.where() // add some predicates
* .eq("status", Status.NEW);
*
* // common pattern - we can use rawOrEmpty() instead
* if (orderIds != null && !orderIds.isEmpty()) {
* query.where().raw("t0.customer_id in (select o.customer_id from orders o where o.id in (?1))", orderIds);
* }
*
* query.findList();
*
* }</pre>
*
* <h3>Using rawOrEmpty()</h3>
* Note that in the example below we use the <code>?1</code> bind parameter to get "parameter expansion"
* for each element in the collection.
*
* <pre>{@code
*
* query.where()
* .eq("status", Status.NEW)
* // only add the expression if orderIds is not empty
* .rawOrEmpty("t0.customer_id in (select o.customer_id from orders o where o.id in (?1))", orderIds);
* .findList();
*
* }</pre>
*
* <h3>Postgres ANY</h3>
* With Postgres we would often use the SQL <code>ANY</code> expression and array parameter binding
* rather than <code>IN</code>.
*
* <pre>{@code
*
* query.where()
* .eq("status", Status.NEW)
* .rawOrEmpty("t0.customer_id in (select o.customer_id from orders o where o.id = any(?))", orderIds);
* .findList();
*
* }</pre>
* <p>
* Note that we need to cast the Postgres array for UUID types like:
* </p>
* <pre>{@code
*
* " ... = any(?::uuid[])"
*
* }</pre>
*
* @param raw The raw expression that is typically a subquery
* @param values The values which is typically a list or set of id values.
*/
ExpressionList<T> rawOrEmpty(String raw, Collection<?> values);
/**
* Add a match expression.
*
@@ -1250,12 +1399,10 @@ public interface ExpressionList<T> {
* typically you only explicitly need to use the and() junction
* when it is nested inside an or() or not() junction.
* </p>
* <p>
* <pre>{@code
*
* // Example: Nested and()
*
* Ebean.find(Customer.class)
* .where()
* .or()
* .and() // nested and
@@ -1276,16 +1423,27 @@ public interface ExpressionList<T> {
* Return a list of expressions that will be joined by OR's.
* This is exactly the same as disjunction();
* <p>
* <p>
* Use endOr() or endJunction() to end the OR junction.
* </p>
* <p>
*
* <pre>{@code
*
* // Example: (status active OR anniversary is null)
*
* .where()
* .or()
* .eq("status", Customer.Status.ACTIVE)
* .isNull("anniversary")
* .orderBy().asc("name")
* .findList();
*
* }</pre>
*
* <pre>{@code
*
* // Example: Use or() to join
* // two nested and() expressions
*
* Ebean.find(Customer.class)
* .where()
* .or()
* .and()
@@ -1309,8 +1467,8 @@ public interface ExpressionList<T> {
* Use endNot() or endJunction() to end expressions being added to the
* NOT expression list.
* </p>
* <p>
* <pre>@{code
*
* <pre>{@code
*
* .where()
* .not()
@@ -1319,12 +1477,11 @@ public interface ExpressionList<T> {
* .endNot()
*
* }</pre>
* <p>
* <pre>@{code
*
* <pre>{@code
*
* // Example: nested not()
*
* Ebean.find(Customer.class)
* .where()
* .eq("status", Customer.Status.ACTIVE)
* .not()
+39 -12
View File
@@ -1,5 +1,8 @@
package io.ebean;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.SpiTransaction;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.NonUniqueResultException;
@@ -12,7 +15,7 @@ import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* The extended API for EbeanServer.
* The extended API for Database.
* <p>
* This provides the finder methods that take an explicit transaction rather than obtaining
* the transaction from the usual mechanism (which is ThreadLocal based).
@@ -23,7 +26,7 @@ import java.util.function.Predicate;
* the transaction to use.
* </p>
* <p>
* Note that in all cases the transaction supplied can be null and in this case the EbeanServer
* Note that in all cases the transaction supplied can be null and in this case the Database
* will use the normal mechanism to obtain the transaction to use.
* </p>
*/
@@ -42,6 +45,32 @@ public interface ExtendedServer {
*/
void setClock(Clock clock);
/**
* Execute the query returning true if a row is found.
* <p>
* The query is executed using max rows of 1 and will only select the id property.
* This method is really just a convenient way to optimise a query to perform a
* 'does a row exist in the db' check.
* </p>
*
* <h2>Example:</h2>
* <pre>{@code
*
* boolean userExists = query().where().eq("email", "rob@foo.com").exists();
*
* }</pre>
*
* <h2>Example using a query bean:</h2>
* <pre>{@code
*
* boolean userExists = new QContact().email.equalTo("rob@foo.com").exists();
*
* }</pre>
*
* @return True if the query finds a matching row in the database
*/
<T> boolean exists(SpiQuery<?> ormQuery, SpiTransaction transaction);
/**
* Return the number of 'top level' or 'root' entities this query should return.
*
@@ -92,7 +121,7 @@ public interface ExtendedServer {
* <p>
* <pre>{@code
*
* ebeanServer.find(Order.class)
* DB.find(Order.class)
* .where().eq("status", Order.Status.NEW)
* .order().asc("id")
* .findEach((Order order) -> {
@@ -126,7 +155,7 @@ public interface ExtendedServer {
* <p>
* <pre>{@code
*
* ebeanServer.find(Order.class)
* DB.find(Order.class)
* .where().eq("status", Order.Status.NEW)
* .order().asc("id")
* .findEachWhile((Order order) -> {
@@ -165,8 +194,7 @@ public interface ExtendedServer {
* <p>
* <pre>{@code
*
* List<Customer> customers =
* ebeanServer.find(Customer.class)
* List<Customer> customers = DB.find(Customer.class)
* .where().ilike("name", "rob%")
* .findList();
*
@@ -243,7 +271,7 @@ public interface ExtendedServer {
* <p>
* <pre>{@code
*
* PagedList<Order> pagedList = Ebean.find(Order.class)
* PagedList<Order> pagedList = DB.find(Order.class)
* .setFirstRow(50)
* .setMaxRows(20)
* .findPagedList();
@@ -272,8 +300,7 @@ public interface ExtendedServer {
* <p>
* <pre>{@code
*
* Set<Customer> customers =
* ebeanServer.find(Customer.class)
* Set<Customer> customers = DB.find(Customer.class)
* .where().ilike("name", "rob%")
* .findSet();
*
@@ -312,7 +339,7 @@ public interface ExtendedServer {
* <pre>{@code
*
* List<String> names =
* Ebean.find(Customer.class)
* DB.find(Customer.class)
* .select("name")
* .orderBy().asc("name")
* .findSingleAttributeList();
@@ -322,7 +349,7 @@ public interface ExtendedServer {
* <pre>{@code
*
* List<String> names =
* Ebean.find(Customer.class)
* DB.find(Customer.class)
* .setDistinct(true)
* .select("name")
* .where().eq("status", Customer.Status.NEW)
@@ -384,7 +411,7 @@ public interface ExtendedServer {
/**
* Execute the update query returning the number of rows updated.
* <p>
* The update query must be created using {@link EbeanServer#update(Class)}.
* The update query must be created using {@link Database#update(Class)}.
* </p>
*
* @param query the update query to execute
+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);
+30 -26
View File
@@ -2,7 +2,6 @@ package io.ebean;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
/**
@@ -14,8 +13,7 @@ import java.util.List;
* </p>
* <h3>Testing</h3>
* <p>
* For testing the mocki-ebean project has the ability to replace the finder implementation
* <p>
* For testing the mocki-ebean project has the ability to replace the finder implementation.
* </p>
* <pre>{@code
*
@@ -25,7 +23,7 @@ import java.util.List;
* super(Customer.class);
* }
*
* // Add your customer finder methods ...
* // Add finder methods ...
*
* public Customer byName(String name) {
* return query().eq("name", name).findOne();
@@ -46,6 +44,15 @@ import java.util.List;
* ...
*
* }</pre>
* <p>
* When the Finder is registered as a field on Customer it can then be used like:
* </p>
* <pre>{@code
*
* Customer rob = Customer.find.byName("Rob");
*
* }</pre>
*
*/
public class Finder<I, T> {
@@ -55,9 +62,9 @@ public class Finder<I, T> {
private final Class<T> type;
/**
* The name of the EbeanServer, null for the default server.
* The name of the database this finder will use, null for the default database.
*/
private final String serverName;
private final String _$dbName;
/**
* Create with the type of the entity bean.
@@ -82,15 +89,15 @@ public class Finder<I, T> {
*/
public Finder(Class<T> type) {
this.type = type;
this.serverName = null;
this._$dbName = null;
}
/**
* Create with the type of the entity bean and specific server name.
* Create with the type of the entity bean and specific database name.
*/
public Finder(Class<T> type, String serverName) {
public Finder(Class<T> type, String databaseName) {
this.type = type;
this.serverName = serverName;
this._$dbName = databaseName;
}
/**
@@ -108,30 +115,27 @@ public class Finder<I, T> {
}
/**
* Return the underlying 'default' EbeanServer.
* <p>
* This provides full access to the API such as explicit transaction demarcation etc.
* Return the Database this finder will use.
*/
public EbeanServer db() {
return Ebean.getServer(serverName);
public Database db() {
return DB.byName(_$dbName);
}
/**
* Return typically a different EbeanServer to the default.
* Return typically a different Database to the default.
* <p>
* This is equivalent to {@link Ebean#getServer(String)}
* This is equivalent to {@link DB#byName(String)}
*
* @param server The name of the EbeanServer. If this is null then the default EbeanServer is
* returned.
* @param databaseName The name of the Database. If this is null then the default database is returned.
*/
public EbeanServer db(String server) {
return Ebean.getServer(server);
public Database db(String databaseName) {
return DB.byName(databaseName);
}
/**
* Creates an entity reference for this ID.
* <p>
* Equivalent to {@link EbeanServer#getReference(Class, Object)}
* Equivalent to {@link Database#getReference(Class, Object)}
*/
@Nonnull
public T ref(I id) {
@@ -141,7 +145,7 @@ public class Finder<I, T> {
/**
* Retrieves an entity by ID.
* <p>
* Equivalent to {@link EbeanServer#find(Class, Object)}
* Equivalent to {@link Database#find(Class, Object)}
*/
@Nullable
public T byId(I id) {
@@ -151,7 +155,7 @@ public class Finder<I, T> {
/**
* Delete a bean by Id.
* <p>
* Equivalent to {@link EbeanServer#delete(Class, Object)}
* Equivalent to {@link Database#delete(Class, Object)}
*/
public void deleteById(I id) {
db().delete(type, id);
@@ -181,7 +185,7 @@ public class Finder<I, T> {
* }</pre>
*
* <p>
* Equivalent to {@link EbeanServer#update(Class)}
* Equivalent to {@link Database#update(Class)}
*/
public UpdateQuery<T> update() {
return db().update(type);
@@ -190,7 +194,7 @@ public class Finder<I, T> {
/**
* Creates a query.
* <p>
* Equivalent to {@link EbeanServer#find(Class)}
* Equivalent to {@link Database#find(Class)}
*/
public Query<T> query() {
return db().find(type);
+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()
+19 -25
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
@@ -76,15 +76,14 @@ import io.ebean.bean.EntityBean;
public abstract class Model {
/**
* Return the underlying 'default' EbeanServer.
* Return the underlying 'default' Database.
* <p>
* This provides full access to the API such as explicit transaction demarcation etc.
* <p>
* Example:
* <pre>{@code
*
* Transaction transaction = Customer.db().beginTransaction();
* try {
* try (Transaction transaction = Customer.db().beginTransaction()) {
*
* // turn off cascade persist for this transaction
* transaction.setPersistCascade(false);
@@ -104,26 +103,21 @@ public abstract class Model {
*
* transaction.commit();
*
* } finally {
* transaction.end();
* }
*
* }</pre>
*/
public static EbeanServer db() {
return Ebean.getDefaultServer();
public static Database db() {
return DB.getDefault();
}
/**
* Return a named EbeanServer that is typically different to the default server.
* <p>
* If you are using multiple databases then each database has a name and maps to a single
* EbeanServer. You can use this method to get an EbeanServer for another database.
* Return a named Database that is typically different to the default database.
*
* @param server The name of the EbeanServer. If this is null then the default EbeanServer is returned.
* @param server The name of the Database. If this is null then the default Database is returned.
*/
public static EbeanServer db(String server) {
return Ebean.getServer(server);
public static Database db(String server) {
return DB.byName(server);
}
/**
@@ -146,7 +140,7 @@ public abstract class Model {
*
* }</pre>
*
* @see EbeanServer#markAsDirty(Object)
* @see Database#markAsDirty(Object)
*/
public void markAsDirty() {
db().markAsDirty(this);
@@ -182,7 +176,7 @@ public abstract class Model {
* Ebean will detect if this is a new bean or a previously fetched bean and perform either an
* insert or an update based on that.
*
* @see EbeanServer#save(Object)
* @see Database#save(Object)
*/
public void save() {
db().save(this);
@@ -202,7 +196,7 @@ public abstract class Model {
/**
* Update this entity.
*
* @see EbeanServer#update(Object)
* @see Database#update(Object)
*/
public void update() {
db().update(this);
@@ -211,7 +205,7 @@ public abstract class Model {
/**
* Insert this entity.
*
* @see EbeanServer#insert(Object)
* @see Database#insert(Object)
*/
public void insert() {
db().insert(this);
@@ -232,7 +226,7 @@ public abstract class Model {
* deleted. Note that, if JDBC batch mode is used then this always returns true.
* </p>
*
* @see EbeanServer#delete(Object)
* @see Database#delete(Object)
*/
public boolean delete() {
return db().delete(this);
@@ -245,7 +239,7 @@ public abstract class Model {
* want to perform a hard/permanent delete.
* </p>
*
* @see EbeanServer#deletePermanent(Object)
* @see Database#deletePermanent(Object)
*/
public boolean deletePermanent() {
return db().deletePermanent(this);
@@ -275,7 +269,7 @@ public abstract class Model {
/**
* Refreshes this entity from the database.
*
* @see EbeanServer#refresh(Object)
* @see Database#refresh(Object)
*/
public void refresh() {
db().refresh(this);
+4 -6
View File
@@ -4,7 +4,6 @@ import io.ebean.util.StringHelper;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -412,15 +411,15 @@ public final class OrderBy<T> implements Serializable {
String[] chunks = orderByClause.split(",");
for (String chunk : chunks) {
String[] pairs = chunk.split(" ");
Property p = parseProperty(pairs);
Property p = parseProperty(chunk);
if (p != null) {
list.add(p);
}
}
}
private Property parseProperty(String[] pairs) {
private Property parseProperty(String chunk) {
String[] pairs = chunk.split(" ");
if (pairs.length == 0) {
return null;
}
@@ -446,8 +445,7 @@ public final class OrderBy<T> implements Serializable {
boolean asc = isAscending(wordList.get(1));
return new Property(wordList.get(0), asc, wordList.get(2), wordList.get(3));
}
String m = "Expecting a 1, 2 or 4 words in [" + Arrays.toString(pairs) + "] but got " + wordList;
throw new RuntimeException(m);
return new Property(chunk.trim(), true);
}
private boolean isAscending(String s) {
+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
+134 -87
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>
@@ -372,6 +354,27 @@ public interface Query<T> {
*/
Query<T> setAutoTune(boolean autoTune);
/**
* Execute the query allowing properties with invalid JSON to be collected and not fail the query.
* <pre>{@code
*
* // fetch a bean with JSON content
* EBasicJsonList bean= DB.find(EBasicJsonList.class)
* .setId(42)
* .setAllowLoadErrors() // collect errors into bean state if we have invalid JSON
* .findOne();
*
*
* // get the invalid JSON errors from the bean state
* Map<String, Exception> errors = server().getBeanState(bean).getLoadErrors();
*
* // If this map is not empty tell we have invalid JSON
* // and should try and fix the JSON content or inform the user
*
* }</pre>
*/
Query<T> setAllowLoadErrors();
/**
* Set the default lazy loading batch size to use.
* <p>
@@ -415,8 +418,7 @@ public interface Query<T> {
* </p>
* <pre>{@code
*
* List<Customer> customers =
* ebeanServer.find(Customer.class)
* List<Customer> customers = DB.find(Customer.class)
* // Only fetch the customer id, name and status.
* // This is described as a "Partial Object"
* .select("name, status")
@@ -444,8 +446,7 @@ public interface Query<T> {
* <pre>{@code
*
* // query orders...
* List<Order> orders =
* ebeanServer.find(Order.class)
* List<Order> orders = DB.find(Order.class)
* // fetch the customer...
* // ... getting the customers name and phone number
* .fetch("customer", "name, phoneNumber")
@@ -460,8 +461,7 @@ public interface Query<T> {
* <pre>{@code
*
* // fetch customers (their id, name and status)
* List<Customer> customers =
* ebeanServer.find(Customer.class)
* List<Customer> customers = DB.find(Customer.class)
* .select("name, status")
* .fetch("contacts", "firstName,lastName,email")
* .findList();
@@ -530,8 +530,7 @@ public interface Query<T> {
* <pre>{@code
*
* // fetch customers (their id, name and status)
* List<Customer> customers =
* ebeanServer.find(Customer.class)
* List<Customer> customers = DB.find(Customer.class)
* .select("name, status")
* .fetch("contacts", "firstName,lastName,email", new FetchConfig().lazy(10))
* .findList();
@@ -552,8 +551,7 @@ public interface Query<T> {
* <pre>{@code
*
* // fetch customers (their id, name and status)
* List<Customer> customers =
* ebeanServer.find(Customer.class)
* List<Customer> customers = DB.find(Customer.class)
* // eager fetch the contacts
* .fetch("contacts")
* .findList();
@@ -616,8 +614,7 @@ public interface Query<T> {
* <pre>{@code
*
* // fetch customers (their id, name and status)
* List<Customer> customers =
* ebeanServer.find(Customer.class)
* List<Customer> customers = DB.find(Customer.class)
* // lazy fetch contacts with a batch size of 100
* .fetch("contacts", new FetchConfig().lazy(100))
* .findList();
@@ -637,7 +634,7 @@ public interface Query<T> {
/**
* Execute the query returning the list of Id's.
* <p>
* This query will execute against the EbeanServer that was used to create it.
* This query will execute against the Database that was used to create it.
* </p>
*/
@Nonnull
@@ -659,12 +656,11 @@ public interface Query<T> {
* the jdbc statement and resultSet are closed at the end of the iteration.
* </p>
* <p>
* This query will execute against the EbeanServer that was used to create it.
* This query will execute against the Database that was used to create it.
* </p>
* <pre>{@code
*
* Query<Customer> query =
* ebeanServer.find(Customer.class)
* Query<Customer> query = DB.find(Customer.class)
* .where().eq("status", Status.NEW)
* .order().asc("id");
*
@@ -711,7 +707,7 @@ public interface Query<T> {
* </p>
* <pre>{@code
*
* ebeanServer.find(Customer.class)
* DB.find(Customer.class)
* .where().eq("status", Status.NEW)
* .order().asc("id")
* .findEach((Customer customer) -> {
@@ -740,7 +736,7 @@ public interface Query<T> {
* </p>
* <pre>{@code
*
* ebeanServer.find(Customer.class)
* DB.find(Customer.class)
* .fetch("contacts", new FetchConfig().query(2))
* .where().eq("status", Status.NEW)
* .order().asc("id")
@@ -763,12 +759,11 @@ public interface Query<T> {
/**
* Execute the query returning the list of objects.
* <p>
* This query will execute against the EbeanServer that was used to create it.
* This query will execute against the Database that was used to create it.
* </p>
* <pre>{@code
*
* List<Customer> customers =
* ebeanServer.find(Customer.class)
* List<Customer> customers = DB.find(Customer.class)
* .where().ilike("name", "rob%")
* .findList();
*
@@ -780,12 +775,11 @@ public interface Query<T> {
/**
* Execute the query returning the set of objects.
* <p>
* This query will execute against the EbeanServer that was used to create it.
* This query will execute against the Database that was used to create it.
* </p>
* <pre>{@code
*
* Set<Customer> customers =
* ebeanServer.find(Customer.class)
* Set<Customer> customers = DB.find(Customer.class)
* .where().ilike("name", "rob%")
* .findSet();
*
@@ -797,7 +791,7 @@ public interface Query<T> {
/**
* Execute the query returning a map of the objects.
* <p>
* This query will execute against the EbeanServer that was used to create it.
* This query will execute against the Database that was used to create it.
* </p>
* <p>
* You can use setMapKey() so specify the property values to be used as keys
@@ -805,8 +799,7 @@ public interface Query<T> {
* </p>
* <pre>{@code
*
* Map<String, Product> map =
* ebeanServer.find(Product.class)
* Map<String, Product> map = DB.find(Product.class)
* .setMapKey("sku")
* .findMap();
*
@@ -822,7 +815,7 @@ public interface Query<T> {
* <pre>{@code
*
* List<String> names =
* Ebean.find(Customer.class)
* DB.find(Customer.class)
* .select("name")
* .orderBy().asc("name")
* .findSingleAttributeList();
@@ -833,7 +826,7 @@ public interface Query<T> {
* <pre>{@code
*
* List<String> names =
* Ebean.find(Customer.class)
* DB.find(Customer.class)
* .setDistinct(true)
* .select("name")
* .where().eq("status", Customer.Status.NEW)
@@ -854,7 +847,7 @@ public interface Query<T> {
* <pre>{@code
*
* String name =
* Ebean.find(Customer.class)
* DB.find(Customer.class)
* .select("name")
* .where().eq("id", 42)
* .findSingleAttribute();
@@ -868,6 +861,37 @@ public interface Query<T> {
*/
boolean isCountDistinct();
/**
* Execute the query returning true if a row is found.
* <p>
* The query is executed using max rows of 1 and will only select the id property.
* This method is really just a convenient way to optimise a query to perform a
* 'does a row exist in the db' check.
* </p>
*
* <h2>Example using a query bean:</h2>
* <pre>{@code
*
* boolean userExists =
* new QContact()
* .email.equalTo("rob@foo.com")
* .exists();
*
* }</pre>
*
* <h2>Example:</h2>
* <pre>{@code
*
* boolean userExists = query()
* .where().eq("email", "rob@foo.com")
* .exists();
*
* }</pre>
*
* @return True if the query finds a matching row in the database
*/
boolean exists();
/**
* Execute the query returning either a single bean or null (if no matching
* bean is found).
@@ -882,8 +906,7 @@ public interface Query<T> {
* <pre>{@code
*
* // assuming the sku of products is unique...
* Product product =
* ebeanServer.find(Product.class)
* Product product = DB.find(Product.class)
* .where().eq("sku", "aa113")
* .findOne();
* ...
@@ -895,8 +918,7 @@ public interface Query<T> {
* <pre>{@code
*
* // Fetch order 1 and additionally fetch join its order details...
* Order order =
* ebeanServer.find(Order.class)
* Order order = DB.find(Order.class)
* .setId(1)
* .fetch("details")
* .findOne();
@@ -1037,7 +1059,7 @@ public interface Query<T> {
* </p>
* <pre>{@code
*
* PagedList<Order> pagedList = Ebean.find(Order.class)
* PagedList<Order> pagedList = DB.find(Order.class)
* .setFirstRow(50)
* .setMaxRows(20)
* .findPagedList();
@@ -1062,11 +1084,9 @@ public interface Query<T> {
* // a query with a named parameter
* String oql = "find order where status = :orderStatus";
*
* Query<Order> query = ebeanServer.find(Order.class, oql);
*
* // bind the named parameter
* query.bind("orderStatus", OrderStatus.NEW);
* List<Order> list = query.findList();
* List<Order> list = DB.find(Order.class, oql)
* .setParameter("orderStatus", OrderStatus.NEW)
* .findList();
*
* }</pre>
*
@@ -1084,12 +1104,9 @@ public interface Query<T> {
* // a query with a positioned parameter
* String oql = "where status = ? order by id desc";
*
* Query<Order> query = ebeanServer.createQuery(Order.class, oql);
*
* // bind the parameter
* query.setParameter(1, OrderStatus.NEW);
*
* List<Order> list = query.findList();
* List<Order> list = DB.createQuery(Order.class, oql)
* .setParameter(1, OrderStatus.NEW)
* .findList();
*
* }</pre>
*
@@ -1106,8 +1123,7 @@ public interface Query<T> {
* </p>
* <pre>{@code
*
* Order order =
* ebeanServer.find(Order.class)
* Order order = DB.find(Order.class)
* .setId(1)
* .fetch("details")
* .findOne();
@@ -1128,8 +1144,7 @@ public interface Query<T> {
* Add a single Expression to the where clause returning the query.
* <pre>{@code
*
* List<Order> newOrders =
* ebeanServer.find(Order.class)
* List<Order> newOrders = DB.find(Order.class)
* .where().eq("status", Order.NEW)
* .findList();
* ...
@@ -1144,8 +1159,7 @@ public interface Query<T> {
* where clause.
* <pre>{@code
*
* List<Order> orders =
* ebeanServer.find(Order.class)
* List<Order> orders = DB.find(Order.class)
* .where()
* .eq("status", Order.NEW)
* .ilike("customer.name","rob%")
@@ -1191,10 +1205,7 @@ public interface Query<T> {
* </p>
* <pre>{@code
*
* List<Customer> list =
* ebeanServer.find(Customer.class)
* // .fetch("orders", new FetchConfig().lazy())
* // .fetch("orders", new FetchConfig().query())
* List<Customer> list = DB.find(Customer.class)
* .fetch("orders")
* .where().ilike("name", "rob%")
* .filterMany("orders").eq("status", Order.Status.NEW).gt("orderDate", lastWeek)
@@ -1208,8 +1219,7 @@ public interface Query<T> {
* </p>
*
* @param propertyName the name of the many property that you want to have a filter on.
* @return the expression list that you add filter expressions for the many
* to.
* @return the expression list that you add filter expressions for the many to.
*/
ExpressionList<T> filterMany(String propertyName);
@@ -1323,7 +1333,7 @@ public interface Query<T> {
* <pre>{@code
*
* List<Customer> customers =
* Ebean.find(Customer.class)
* DB.find(Customer.class)
* .setDistinct(true)
* .select("name")
* .findList();
@@ -1339,7 +1349,7 @@ public interface Query<T> {
*
* List<CountedValue<Order.Status>> orderStatusCount =
*
* Ebean.find(Order.class)
* DB.find(Order.class)
* .select("status")
* .where()
* .gt("orderDate", LocalDate.now().minusMonths(3))
@@ -1395,10 +1405,8 @@ public interface Query<T> {
*
* // Assuming sku is unique for products...
*
* Map<String,Product> productMap =
* ebeanServer.find(Product.class)
* // use sku for keys...
* .setMapKey("sku")
* Map<String,Product> productMap = DB.find(Product.class)
* .setMapKey("sku") // sku map keys...
* .findMap();
*
* }</pre>
@@ -1570,11 +1578,50 @@ public interface Query<T> {
*/
Query<T> alias(String alias);
/**
* Set the base table to use for this query.
* <p>
* Typically this is used when a table has partitioning and we wish to specify a specific
* partition/table to query against.
* </p>
* <pre>{@code
*
* QOrder()
* .setBaseTable("order_2019_05")
* .status.equalTo(Status.NEW)
* .findList();
*
* }</pre>
*/
Query<T> setBaseTable(String baseTable);
/**
* Return the type of beans being queried.
*/
Class<T> getBeanType();
/**
* Restrict the query to only return subtypes of the given inherit type.
*
* <pre>{@code
*
* List<Animal> animals =
* new QAnimal()
* .name.startsWith("Fluffy")
* .setInheritType(Cat.class)
* .findList();
*
* }</pre>
*
* @param type An inheritance subtype of the
*/
Query<T> setInheritType(Class<? extends T> type);
/**
* Returns the inherit type. This is normally the same as getBeanType() returns as long as no other type is set.
*/
Class<? extends T> getInheritType();
/**
* Return the type of query being executed.
*/
+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>
*/
+47 -23
View File
@@ -10,34 +10,30 @@ import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* Query object for performing native SQL queries that return SqlRow's.
* <p>
* Firstly note that you can use your own sql queries with <em>entity beans</em>
* by using the SqlSelect annotation. This should be your first approach when
* wanting to use your own SQL queries.
* </p>
* <p>
* If ORM Mapping is too tight and constraining for your problem then SqlQuery
* could be a good approach.
* </p>
* Query object for performing native SQL queries that return SqlRow or directly read
* ResultSet using a RowMapper.
* <p>
* The returned SqlRow objects are similar to a LinkedHashMap with some type
* conversion support added.
* </p>
* <p>
* Refer to {@link DtoQuery} for native sql queries returning DTO beans.
* </p>
* <p>
* Refer to {@link Database#findNative(Class, String)} for native sql queries returning entity beans.
* </p>
*
* <pre>{@code
*
* // its typically a good idea to use a named query
* // and put the sql in the orm.xml instead of in your code
* // example using named parameters
*
* String sql = "select id, name from customer where name like :name and status_code = :status";
*
* SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
* sqlQuery.setParameter("name", "Acme%");
* sqlQuery.setParameter("status", "ACTIVE");
*
* // execute the query returning a List of MapBean objects
* List<SqlRow> list = sqlQuery.findList();
* List<SqlRow> list =
* DB.sqlQuery(sql)
* .setParameter("name", "Acme%")
* .setParameter("status", "ACTIVE")
* .findList();
*
* }</pre>
*/
@@ -104,7 +100,7 @@ public interface SqlQuery extends Serializable {
*
* String sql = "select id, name, status from customer order by name desc";
*
* Ebean.createSqlQuery(sql)
* DB.sqlQuery(sql)
* .findEachRow((resultSet, rowNum) -> {
*
* // read directly from ResultSet
@@ -134,7 +130,7 @@ public interface SqlQuery extends Serializable {
*
* String sql = "select max(unit_price) from o_order_detail where order_qty > ?";
*
* BigDecimal maxPrice = Ebean.createSqlQuery(sql)
* BigDecimal maxPrice = DB.sqlQuery(sql)
* .setParameter(1, 2)
* .findSingleAttribute(BigDecimal.class);
*
@@ -176,9 +172,10 @@ public interface SqlQuery extends Serializable {
* " order by (unit_price * order_qty) desc";
*
* //
* List<BigDecimal> lineAmounts = Ebean.createSqlQuery(sql)
* .setParameter(1, 3)
* .findSingleAttributeList(BigDecimal.class);
* List<BigDecimal> lineAmounts =
* DB.sqlQuery(sql)
* .setParameter(1, 3)
* .findSingleAttributeList(BigDecimal.class);
*
* }</pre>
*
@@ -195,6 +192,33 @@ public interface SqlQuery extends Serializable {
*/
SqlQuery setParameter(String name, Object value);
/**
* Set one of more positioned parameters.
* <p>
* This is a convenient alternative to multiple calls setParameter().
*
* <pre>{@code
*
* String sql = "select id, name from customer where name like ? and status = ?";
*
* List<SqlRow> list =
* DB.sqlQuery(sql)
* .setParams("Rob", Status.NEW)
* .findList();
*
*
* // is the same as ...
*
* List<SqlRow> list =
* DB.sqlQuery(sql)
* .setParameter(1, "Rob")
* .setParameter(2, "Status.NEW)
* .findList();
*
* }</pre>
*/
SqlQuery setParams(Object... values);
/**
* The same as bind for positioned parameters.
*/
+52 -11
View File
@@ -16,20 +16,33 @@ package io.ebean;
* notify Ebean of external changes and enable Ebean to maintain it's "L2"
* server cache.
* </p>
* <p>
*
* <pre>{@code
*
* // example that uses 'named' parameters
* // example using 'positioned' parameters
*
* String s = "UPDATE f_topic set post_count = :count where id = :id";
* String sql = "insert into audit_log (group, title, description) values (?, ?, ?);
*
* SqlUpdate update = Ebean.createSqlUpdate(s);
* update.setParameter("id", 1);
* update.setParameter("count", 50);
* int rows =
* DB.sqlUpdate(sql)
* .setParams("login", "new user", "user rob was created")
* .executeNow();
*
* int modifiedCount = update.execute();
* }</pre>
*
* String msg = "There were " + modifiedCount + " rows updated";
* <pre>{@code
*
* // example using 'named' parameters
*
* String sql = "update topic set post_count = :count where id = :id";
*
* int rows =
* DB.sqlUpdate(sql)
* .setParameter("id", 1)
* .setParameter("count", 50)
* .execute();
*
* String msg = "There were " + rows + " rows updated";
*
* }</pre>
* <p>
@@ -37,9 +50,10 @@ package io.ebean;
* <pre>{@code
*
* String sql = "insert into audit_log (id, description, modified_description) values (?,?,?)";
* SqlUpdate insert = Ebean.createSqlUpdate(sql);
*
* try (Transaction txn = Ebean.beginTransaction()) {
* SqlUpdate insert = DB.sqlUpdate(sql);
*
* try (Transaction txn = DB.beginTransaction()) {
* txn.setBatchMode(true);
*
* insert.setNextParameter(10000);
@@ -65,7 +79,7 @@ package io.ebean;
* </p>
* <pre>{@code
*
* try (Transaction txn = Ebean.beginTransaction()) {
* try (Transaction txn = DB.beginTransaction()) {
*
* insert.setNextParameter(10000);
* insert.setNextParameter("hello");
@@ -217,6 +231,33 @@ public interface SqlUpdate {
*/
SqlUpdate setTimeout(int secs);
/**
* Set one of more positioned parameters.
* <p>
* This is a convenient alternative to multiple setParameter() calls.
*
* <pre>{@code
*
* String sql = "insert into audit_log (id, name, version) values (?,?,?)";
*
* DB.sqlUpdate(sql)
* .setParams(UUID.randomUUID(), "Hello", 1)
* .executeNow();
*
*
* // is the same as ...
*
* DB.sqlUpdate(sql)
* .setParameter(1, UUID.randomUUID())
* .setParameter(2, "Hello")
* .setParameter(3, 1)
* .executeNow();
*
* }</pre>
*
*/
SqlUpdate setParams(Object... values);
/**
* Set the next positioned parameter.
*
+68 -44
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,8 +243,7 @@ public interface Transaction extends AutoCloseable {
*
* // assume Customer has L2 bean caching enabled ...
*
* Transaction transaction = Ebean.beginTransaction();
* try {
* try (Transaction transaction = DB.beginTransaction()) {
*
* // this uses L2 bean cache as the transaction
* // ... is considered "query only" at this point
@@ -253,7 +251,7 @@ public interface Transaction extends AutoCloseable {
*
* // transaction no longer "query only" once
* // ... a bean has been saved etc
* Ebean.save(someBean);
* someBean.save();
*
* // will NOT use L2 bean cache as the transaction
* // ... is no longer considered "query only"
@@ -273,8 +271,7 @@ public interface Transaction extends AutoCloseable {
* Customer.find.byId(99); // skips l2 bean cache
*
*
* } finally {
* transaction.end();
* transaction.commit();
* }
*
* }</pre>
@@ -290,36 +287,66 @@ public interface Transaction extends AutoCloseable {
boolean isSkipCache();
/**
* Turn on or off statement batching. Statement batching can be transparent
* for drivers and databases that support getGeneratedKeys. Otherwise you may
* wish to specifically control when batching is used via this method.
* <p>
* Refer to <code>java.sql.PreparedStatement.addBatch();</code>
* <p>
* Note that you may also wish to use the setPersistCascade method to stop
* save and delete cascade behaviour. You may do this to have full control
* over the order of execution rather than the normal cascading fashion.
* </p>
* <p>
* Note that the <em>execution order</em> in batch mode may be different from
* non batch mode execution order. Also note that <em>insert behaviour</em>
* may be different depending on the JDBC driver and its support for
* getGeneratedKeys. That is, for JDBC drivers that do not support
* getGeneratedKeys you may not get back the generated IDs (used for inserting
* associated detail beans etc).
* </p>
* Turn on or off use of JDBC statement batching.
* <p>
* Calls to save(), delete(), insert() and execute() all support batch
* processing. This includes normal beans, MapBean, CallableSql and UpdateSql.
* processing. This includes normal beans, CallableSql and UpdateSql.
* </p>
*
* <pre>{@code
*
* try (Transaction transaction = server.beginTransaction()) {
*
* // turn on JDBC batch
* transaction.setBatchMode(true);
*
* // tune the batch size
* transaction.setBatchSize(50);
*
* ...
*
* transaction.commit();
* }
*
* }</pre>
*
* <h3>getGeneratedKeys</h3>
* <p>
* The flushing of the batched statements is automatic but you can call
* batchFlush when you like. Note that flushing occurs when a query is
* executed or when you mix UpdateSql and CallableSql with save and delete of
* Often with large batch inserts we want to turn off getGeneratedKeys. We do
* this via {@link #setBatchGetGeneratedKeys(boolean)}.
* Also note that some JDBC drivers do not support getGeneratedKeys in JDBC batch mode.
* </p>
* <pre>{@code
*
* try (Transaction transaction = server.beginTransaction()) {
*
* transaction.setBatchMode(true);
* transaction.setBatchSize(100);
* // insert but don't bother getting back the generated keys
* transaction.setBatchGetGeneratedKeys(false);
*
*
* // perform lots of inserts ...
* ...
*
* transaction.commit();
* }
*
* }</pre>
*
* <h3>Flush</h3>
* <p>
* The batch is automatically flushed when it hits the batch size and also when we
* execute queries or when we mix UpdateSql and CallableSql with save and delete of
* beans.
* </p>
* <p>
* Example: batch processing executing every 3 rows
* We use {@link #flush()} to explicitly flush the batch and we can use
* {@link #setBatchFlushOnQuery(boolean)} and {@link #setBatchFlushOnMixed(boolean)}
* to control the automatic flushing behaviour.
* </p>
* <p>
* Example: batch processing of CallableSql executing every 10 rows
* </p>
*
* <pre>{@code
@@ -334,24 +361,21 @@ public interface Transaction extends AutoCloseable {
* CallableSql cs = new CallableSql(sql);
* cs.registerOut(2, Types.INTEGER);
*
* // (optional) inform eBean this stored procedure
* // (optional) inform Ebean this stored procedure
* // inserts into a table called sp_test
* cs.addModification("sp_test", true, false, false);
*
* Transaction txn = ebeanServer.beginTransaction();
* txn.setBatchMode(true);
* txn.setBatchSize(3);
* try {
* try (Transaction txn = DB.beginTransaction()) {
* txn.setBatchMode(true);
* txn.setBatchSize(10);
*
* for (int i = 0; i < da.length;) {
* cs.setParameter(1, da[i]);
* ebeanServer.execute(cs);
* DB.execute(cs);
* }
*
* // NB: commit implicitly flushes
* // Note: commit implicitly flushes
* txn.commit();
*
* } finally {
* txn.end();
* }
*
* }</pre>
+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();
@@ -98,6 +98,8 @@ public final class EntityBeanIntercept implements Serializable {
private Object[] origValues;
private Exception[] loadErrors;
private int lazyLoadProperty = -1;
private Object ownerId;
@@ -1115,4 +1117,36 @@ public final class EntityBeanIntercept implements Serializable {
public void setSortOrder(int sortOrder) {
this.sortOrder = sortOrder;
}
/**
* Set the load error that happened on this property.
*/
public void setLoadError(int propertyIndex, Exception t) {
if (loadErrors == null) {
loadErrors = new Exception[owner._ebean_getPropertyNames().length];
}
loadErrors[propertyIndex] = t;
flags[propertyIndex] |= FLAG_LOADED_PROP;
}
/**
* Returns the loadErrors.
*/
public Map<String, Exception> getLoadErrors() {
if (loadErrors == null) {
return null;
}
Map<String, Exception> ret = null;
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
Exception loadError = loadErrors[i];
if (loadError != null) {
if (ret == null) {
ret = new LinkedHashMap<>();
}
ret.put(getProperty(i), loadError);
}
}
return ret;
}
}
@@ -20,7 +20,7 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
protected boolean disableLazyLoad;
/**
* The EbeanServer this is associated with. (used for lazy fetch).
* The Database this is associated with. (used for lazy fetch).
*/
protected transient BeanCollectionLoader loader;
@@ -1,7 +1,7 @@
package io.ebean.config;
/**
* Used to provide some automatic configuration early in the creation of an EbeanServer.
* Used to provide some automatic configuration early in the creation of a Database.
*/
public interface AutoConfigure {
@@ -1,7 +1,7 @@
package io.ebean.config;
/**
* Defines the AutoTune behaviour for a EbeanServer.
* Defines the AutoTune behaviour for a Database.
*/
public class AutoTuneConfig {
@@ -3,7 +3,7 @@ package io.ebean.config;
import java.util.Properties;
/**
* Configuration for the container that holds the EbeanServer instances.
* Configuration for the container that holds the Database instances.
* <p>
* Provides configuration for cluster communication (if clustering is used). The cluster communication is
* used to invalidate appropriate parts of the L2 cache across the cluster.
@@ -0,0 +1,44 @@
package io.ebean.config;
import io.ebean.DatabaseFactory;
/**
* The configuration used for creating a Database.
* <p>
* Used to programmatically construct an Database and optionally register it
* with the DB singleton.
* </p>
* <p>
* If you just use DB thout this programmatic configuration Ebean will read
* the application.properties file and take the configuration from there. This usually
* includes searching the class path and automatically registering any entity
* classes and listeners etc.
* </p>
* <pre>{@code
*
* DatabaseConfig config = new DatabaseConfig();
*
* // read the ebean.properties and load
* // those settings into this serverConfig object
* config.loadFromProperties();
*
* // explicitly register the entity beans to avoid classpath scanning
* config.addClass(Customer.class);
* config.addClass(User.class);
*
* Database db = DatabaseFactory.create(config);
*
* }</pre>
*
* <p>
* Note that ServerConfigProvider provides a standard Java ServiceLoader mechanism that can
* be used to apply configuration to the DatabaseConfig.
* </p>
*
* @author emcgreal
* @author rbygrave
* @see DatabaseFactory
*/
public class DatabaseConfig extends ServerConfig {
}
@@ -140,7 +140,7 @@ public class DbMigrationConfig {
* <p>
* The default of "dbmigration" is reasonable in most cases. You may look to set this
* to be something like "dbmigration/myapp" where myapp gives it a unique resource path
* in the case there are multiple EbeanServer applications in the single classpath.
* in the case there are multiple Database applications in the single classpath.
* </p>
*/
public void setMigrationPath(String migrationPath) {
@@ -47,6 +47,11 @@ public class PlatformConfig {
*/
private DbUuid dbUuid = DbUuid.AUTO_VARCHAR;
/**
* Set to true to force InetAddress to map to Varchar (for Postgres rather than INET)
*/
private boolean databaseInetAddressVarchar;
/**
* Modify the default mapping of standard types such as default precision for DECIMAL etc.
*/
@@ -181,6 +186,20 @@ public class PlatformConfig {
this.idType = idType;
}
/**
* Return true if InetAddress should map to varchar column (rather than Postgres INET).
*/
public boolean isDatabaseInetAddressVarchar() {
return databaseInetAddressVarchar;
}
/**
* Set to true to force InetAddress to map to varchar column.
*/
public void setDatabaseInetAddressVarchar(boolean databaseInetAddressVarchar) {
this.databaseInetAddressVarchar = databaseInetAddressVarchar;
}
/**
* Add a custom type mapping.
* <p>
@@ -235,6 +254,7 @@ public class PlatformConfig {
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize);
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
databaseInetAddressVarchar = p.getBoolean("databaseInetAddressVarchar", databaseInetAddressVarchar);
DbUuid dbUuid = p.getEnum(DbUuid.class, "dbuuid", null);
if (dbUuid != null) {
+55 -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;
@@ -44,30 +44,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 +78,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 +106,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 +150,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;
@@ -252,6 +252,8 @@ public class ServerConfig {
private boolean ddlRun;
private boolean ddlExtra = true;
private boolean ddlCreateOnly;
private String ddlInitSql;
@@ -348,7 +350,7 @@ public class ServerConfig {
/**
* Behaviour of updates in JDBC batch to by default include all properties.
*/
private boolean updateAllPropertiesInBatch = true;
private boolean updateAllPropertiesInBatch;
/**
* Default behaviour for updates when cascade save on a O2M or M2M to delete any missing children.
@@ -520,7 +522,7 @@ public class ServerConfig {
private boolean idGeneratorAutomatic = true;
/**
* Construct a Server Configuration for programmatically creating an EbeanServer.
* Construct a Database Configuration for programmatically creating an Database.
*/
public ServerConfig() {
@@ -691,14 +693,14 @@ public class ServerConfig {
}
/**
* Return the name of the EbeanServer.
* Return the name of the Database.
*/
public String getName() {
return name;
}
/**
* Set the name of the EbeanServer.
* Set the name of the Database.
*/
public void setName(String name) {
this.name = name;
@@ -707,8 +709,8 @@ public class ServerConfig {
/**
* Return the container / clustering configuration.
* <p/>
* The container holds all the EbeanServer instances and provides clustering communication
* services to all the EbeanServer instances.
* The container holds all the Database instances and provides clustering communication
* services to all the Database instances.
*/
public ContainerConfig getContainerConfig() {
return containerConfig;
@@ -717,8 +719,8 @@ public class ServerConfig {
/**
* Set the container / clustering configuration.
* <p/>
* The container holds all the EbeanServer instances and provides clustering communication
* services to all the EbeanServer instances.
* The container holds all the Database instances and provides clustering communication
* services to all the Database instances.
*/
public void setContainerConfig(ContainerConfig containerConfig) {
this.containerConfig = containerConfig;
@@ -758,8 +760,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>
@@ -1545,14 +1547,14 @@ public class ServerConfig {
}
/**
* Return true if this EbeanServer is a Document store only instance (has no JDBC DB).
* Return true if this Database is a Document store only instance (has no JDBC DB).
*/
public boolean isDocStoreOnly() {
return docStoreOnly;
}
/**
* Set to true if this EbeanServer is Document store only instance (has no JDBC DB).
* Set to true if this Database is Document store only instance (has no JDBC DB).
*/
public void setDocStoreOnly(boolean docStoreOnly) {
this.docStoreOnly = docStoreOnly;
@@ -1952,16 +1954,16 @@ public class ServerConfig {
}
/**
* Return true if the EbeanServer instance should be created in offline mode.
* Return true if the Database instance should be created in offline mode.
*/
public boolean isDbOffline() {
return dbOffline;
}
/**
* Set to true if the EbeanServer instance should be created in offline mode.
* Set to true if the Database instance should be created in offline mode.
* <p>
* Typically used to create an EbeanServer instance for DDL Migration generation
* Typically used to create an Database instance for DDL Migration generation
* without requiring a real DataSource / Database to connect to.
* </p>
*/
@@ -2116,6 +2118,16 @@ public class ServerConfig {
this.ddlRun = ddlRun;
}
/**
* Set to false if you not want to run the extra-ddl.xml scripts. (default = true)
* <p>
* Typically we want this on when we are running tests.
*/
public void setDdlExtra(boolean ddlExtra) {
this.ddlExtra = ddlExtra;
}
/**
* Return true if the "drop all ddl" should be skipped.
* <p>
@@ -2186,6 +2198,13 @@ public class ServerConfig {
return ddlRun;
}
/**
* Return true, if extra-ddl.xml should be executed.
*/
public boolean isDdlExtra() {
return ddlExtra;
}
/**
* Return true if the class path search should be disabled.
*/
@@ -2195,7 +2214,7 @@ public class ServerConfig {
/**
* Set to true to disable the class path search even for the case where no entity bean classes
* have been registered. This can be used to start an EbeanServer instance just to use the
* have been registered. This can be used to start an Database instance just to use the
* SQL functions such as SqlQuery, SqlUpdate etc.
*/
public void setDisableClasspathSearch(boolean disableClasspathSearch) {
@@ -2309,8 +2328,7 @@ public class ServerConfig {
*
* // assume Customer has L2 bean caching enabled ...
*
* Transaction transaction = Ebean.beginTransaction();
* try {
* try (Transaction transaction = DB.beginTransaction()) {
*
* // this uses L2 bean cache as the transaction
* // ... is considered "query only" at this point
@@ -2318,7 +2336,7 @@ public class ServerConfig {
*
* // transaction no longer "query only" once
* // ... a bean has been saved etc
* Ebean.save(someBean);
* DB.save(someBean);
*
* // will NOT use L2 bean cache as the transaction
* // ... is no longer considered "query only"
@@ -2337,9 +2355,6 @@ public class ServerConfig {
* transaction.setSkipCache(true);
* Customer.find.byId(99); // skips l2 bean cache
*
*
* } finally {
* transaction.end();
* }
*
* }</pre>
@@ -2407,7 +2422,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;
@@ -2962,6 +2977,7 @@ public class ServerConfig {
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
ddlRun = p.getBoolean("ddl.run", ddlRun);
ddlExtra = p.getBoolean("ddl.extra", ddlExtra);
ddlCreateOnly = p.getBoolean("ddl.createOnly", ddlCreateOnly);
ddlInitSql = p.get("ddl.initSql", ddlInitSql);
ddlSeedSql = p.get("ddl.seedSql", ddlSeedSql);
@@ -27,6 +27,9 @@ public class DbPlatformTypeMapping {
private static final DbPlatformType BOOLEAN_LOGICAL = new BooleanLogicalType();
private static final DbPlatformType INET_NATIVE = new DbPlatformType("inet", false);
private static final DbPlatformType INET_VARCHAR = new DbPlatformType("varchar", 50);
private static final DbPlatformType UUID_NATIVE = new DbPlatformType("uuid", false);
@SuppressWarnings("unused")
private static final DbPlatformType UUID_PLACEHOLDER = new DbPlatformType("uuidPlaceholder");
@@ -106,6 +109,7 @@ public class DbPlatformTypeMapping {
put(DbType.JSONBLOB, new DbPlatformType("jsonblob"));
put(DbType.JSONVARCHAR, new DbPlatformType("jsonvarchar", 1000));
put(DbType.UUID, UUID_NATIVE);
put(DbType.INET, INET_NATIVE);
} else {
put(DbType.VARCHAR, new DbPlatformType("varchar", 255));
@@ -121,6 +125,7 @@ public class DbPlatformTypeMapping {
put(DbType.JSONVARCHAR, JSON_VARCHAR_PLACEHOLDER);
// default to native UUID and override on platform configure()
put(DbType.UUID, UUID_NATIVE);
put(DbType.INET, INET_VARCHAR);
}
}
@@ -35,6 +35,8 @@ public enum DbType {
ARRAY(Types.ARRAY),
UUID(ExtraDbTypes.UUID),
INET(ExtraDbTypes.INET),
CDIR(ExtraDbTypes.CDIR),
POINT(ExtraDbTypes.POINT),
POLYGON(ExtraDbTypes.POLYGON),
@@ -40,6 +40,9 @@ public interface ExtraDbTypes {
*/
int JSONBlob = 5005;
int INET = 5020;
int CDIR = 5021;
/**
* Geo Point
*/
@@ -60,6 +60,7 @@ public class PostgresPlatform extends DatabasePlatform {
DbPlatformType dbBytea = new DbPlatformType("bytea", false);
dbTypeMap.put(DbType.UUID, new DbPlatformType("uuid", false));
dbTypeMap.put(DbType.INET, new DbPlatformType("inet", false));
dbTypeMap.put(DbType.HSTORE, new DbPlatformType("hstore", false));
dbTypeMap.put(DbType.JSON, new DbPlatformType("json", false));
dbTypeMap.put(DbType.JSONB, new DbPlatformType("jsonb", false));
+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);
}
}
@@ -50,6 +50,11 @@ public interface DbMigration {
throw new IllegalStateException("No service implementation found for DbMigration?");
}
/**
* Set to false to suppress logging to System out.
*/
void setLogToSystemOut(boolean logToSystemOut);
/**
* Set the path from the current working directory to the application resources.
* <p>
@@ -9,7 +9,7 @@ package io.ebean.event;
* <p>
* Note that getTransaction() on the PersistRequest returns the transaction used
* for the insert, update, delete or fetch. To explicitly use this same
* transaction you should use this transaction via methods on EbeanServer.
* transaction you should use this transaction via methods on Database.
* </p>
* <pre>{@code
*
@@ -1,6 +1,6 @@
package io.ebean.event;
import io.ebean.EbeanServer;
import io.ebean.Database;
/**
* Fired after a bean is constructed, but not yet loaded from database.
@@ -9,7 +9,7 @@ import io.ebean.EbeanServer;
* properties will get unload. Use {@link BeanPostLoad} instead.
* <p>
* it's intended to do some dependency-injection here.
* If you plan to use this feature you should use {@link EbeanServer#createEntityBean(Class)}
* If you plan to use this feature you should use {@link Database#createEntityBean(Class)}
* to create new beans.
* </p>
*/
@@ -32,7 +32,7 @@ public interface BeanPostConstructListener {
void postConstruct(Object bean);
/**
* Called after {@link EbeanServer#createEntityBean(Class)}. Only for new beans.
* Called after {@link Database#createEntityBean(Class)}. Only for new beans.
* intended to set default values here.
*/
void postCreate(Object bean);
@@ -3,7 +3,7 @@ package io.ebean.meta;
import java.util.List;
/**
* Provides access to the meta data in EbeanServer such as query execution statistics.
* Provides access to the meta data in Database such as query execution statistics.
*/
public interface MetaInfoManager {
@@ -1,4 +1,4 @@
/**
* Meta data that can be retrieved for the EbeanServer.
* Meta data that can be retrieved for a Database instance.
*/
package io.ebean.meta;
+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>
@@ -0,0 +1,14 @@
package io.ebean.plugin;
import io.ebean.bean.EntityBean;
/**
* Errorhandler to handle load errors and may be recover correct value.
*
* @author Roland Praml, FOCONIS AG
*
*/
@FunctionalInterface
public interface LoadErrorHandler {
void handleLoadError(EntityBean bean, Property prop, String fullName, Exception e);
}
+1 -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 {
@@ -6,6 +6,7 @@ package io.ebean.text;
public class TextException extends RuntimeException {
private static final long serialVersionUID = 1601310159486033148L;
private String text;
/**
* Construct with an error message.
@@ -27,4 +28,41 @@ public class TextException extends RuntimeException {
public TextException(Exception e) {
super(e);
}
/**
* Constructor for a detailed exception.
*
* @param message
* the message. The placeholder {} will be replaced by
* <code>text</code>
* @param text
* the fault text.
* @param cause
* the case
*/
public TextException(String message, String text, Exception cause) {
super(message.replace("{}", String.valueOf(text)), cause);
this.text = text;
}
/**
* Constructor for a detailed exception.
*
* @param message
* the message. The placeholder {} will be replaced by
* <code>text</code>
* @param text
* the fault text.
*/
public TextException(String message, String text) {
super(message.replace("{}", String.valueOf(text)));
this.text = text;
}
/**
* Return the text, that caused the error. (e.g. the JSON). May be null.
*/
public String getText() {
return text;
}
}
@@ -20,7 +20,7 @@ import java.util.Locale;
*
* FileReader reader = new FileReader(f);
*
* CsvReader&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);
@@ -41,6 +41,14 @@ public class BindParams implements Serializable {
public BindParams() {
}
/**
* Reset positioned parameters (usually due to bind parameter expansion).
*/
public void reset() {
bindHash = null;
positionedParameters.clear();
}
public int queryBindHash() {
int hc = namedParameters.hashCode();
for (Param positionedParameter : positionedParameters) {
@@ -171,8 +179,7 @@ public class BindParams implements Serializable {
}
private Param getParam(String name) {
Param p = namedParameters.computeIfAbsent(name, k -> new Param());
return p;
return namedParameters.computeIfAbsent(name, k -> new Param());
}
private Param getParam(int position) {
@@ -11,4 +11,8 @@ public interface CQueryPlanKey {
*/
String getPartialKey();
/**
* Return a query plan key taking into account persist cascading.
*/
CQueryPlanKey withDeleteByIds();
}
@@ -439,6 +439,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
*/
void setDelete();
/**
* Set the query to be delete by ids due to cascading delete.
*/
CQueryPlanKey setDeleteByIdsPlan();
/**
* Set the query to select the id property only.
*/
@@ -820,6 +825,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
*/
boolean isCancelled();
/**
* Return the base table to use if user defined on the query.
*/
String getBaseTable();
/**
* Return root table alias set by {@link #alias(String)} command.
*/
@@ -844,4 +854,9 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
* Returns the count distinct order setting.
*/
CountDistinctOrder getCountDistinctOrder();
/**
* Handles load errors.
*/
void handleLoadError(String fullName, Exception e);
}
@@ -4,6 +4,11 @@ import io.ebean.SqlUpdate;
public interface SpiSqlUpdate extends SqlUpdate {
/**
* Return the sql taking into account bind parameter expansion.
*/
String getBaseSql();
/**
* Return the Bind parameters.
*/
@@ -40,6 +40,7 @@ public class DdlGenerator {
private final boolean generateDdl;
private final boolean runDdl;
private final boolean extraDdl;
private final boolean createOnly;
private final boolean jaxbPresent;
private final boolean ddlCommitOnCreateIndex;
@@ -54,6 +55,7 @@ public class DdlGenerator {
this.server = server;
this.jaxbPresent = serverConfig.getClassLoadConfig().isJavaxJAXBPresent();
this.generateDdl = serverConfig.isDdlGenerate();
this.extraDdl = serverConfig.isDdlExtra();
this.createOnly = serverConfig.isDdlCreateOnly();
this.dbSchema = serverConfig.getDbSchema();
if (!serverConfig.getTenantMode().isDdlEnabled() && serverConfig.isDdlRun()) {
@@ -166,8 +168,7 @@ public class DdlGenerator {
protected void runDropSql(Connection connection) throws IOException {
if (!createOnly) {
String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl");
if (!"true".equalsIgnoreCase(ignoreExtraDdl) && jaxbPresent) {
if (extraDdl && jaxbPresent) {
String extraApply = ExtraDdlXmlReader.buildExtra(server.getDatabasePlatform().getName(), true);
if (extraApply != null) {
runScript(connection, false, extraApply, "extra-ddl");
@@ -187,8 +188,7 @@ public class DdlGenerator {
}
runScript(connection, false, createAllContent, getCreateFileName());
String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl");
if (!"true".equalsIgnoreCase(ignoreExtraDdl) && jaxbPresent) {
if (extraDdl && jaxbPresent) {
if (currentModel.isTablePartitioning()) {
String extraPartitioning = ExtraDdlXmlReader.buildPartitioning(server.getDatabasePlatform().getName());
if (extraPartitioning != null && !extraPartitioning.isEmpty()) {
@@ -72,6 +72,8 @@ public class DefaultDbMigration implements DbMigration {
private static final String GENERATED_COMMENT = "THIS IS A GENERATED FILE - DO NOT MODIFY";
private boolean logToSystemOut = true;
/**
* Set to true if DefaultDbMigration run with online EbeanServer instance.
*/
@@ -316,6 +318,11 @@ public class DefaultDbMigration implements DbMigration {
return generateDiff(request);
}
} catch (UnknownResourcePathException e) {
logError("ERROR - " + e.getMessage());
logError("Check the working directory or change dbMigration.setPathToResources() value?");
return null;
} finally {
if (!online) {
DbOffline.reset();
@@ -387,8 +394,7 @@ public class DefaultDbMigration implements DbMigration {
private void writeExtraDdl(File migrationDir, DdlScript script) throws IOException {
String fullName = repeatableMigrationName(script.isInit(), script.getName());
logger.info("writing repeatable script {}", fullName);
logger.debug("writing repeatable script {}", fullName);
File file = new File(migrationDir, fullName);
try (FileWriter writer = new FileWriter(file)) {
@@ -397,6 +403,30 @@ public class DefaultDbMigration implements DbMigration {
}
}
@Override
public void setLogToSystemOut(boolean logToSystemOut) {
this.logToSystemOut = logToSystemOut;
}
private void logError(String message) {
if (logToSystemOut) {
System.out.println("DbMigration> " + message);
} else {
logger.error(message);
}
}
private void logInfo(String message, Object value) {
if (value != null) {
message = String.format(message, value);
}
if (logToSystemOut) {
System.out.println("DbMigration> " + message);
} else {
logger.info(message);
}
}
private String repeatableMigrationName(boolean init, String scriptName) {
StringBuilder sb = new StringBuilder();
if (init) {
@@ -416,12 +446,12 @@ public class DefaultDbMigration implements DbMigration {
List<String> pendingDrops = request.getPendingDrops();
if (!pendingDrops.isEmpty()) {
logger.info("Pending un-applied drops in versions {}", pendingDrops);
logInfo("Pending un-applied drops in versions %s", pendingDrops);
}
Migration migration = request.createDiffMigration();
if (migration == null) {
logger.info("no changes detected - no migration written");
logInfo("no changes detected - no migration written", null);
return null;
} else {
// there were actually changes to write
@@ -440,7 +470,7 @@ public class DefaultDbMigration implements DbMigration {
List<String> pendingDrops = request.getPendingDrops();
if (!pendingDrops.isEmpty()) {
logger.info("... remaining pending un-applied drops in versions {}", pendingDrops);
logInfo("... remaining pending un-applied drops in versions %s", pendingDrops);
}
return version;
}
@@ -520,9 +550,9 @@ public class DefaultDbMigration implements DbMigration {
String fullVersion = getFullVersion(request.nextVersion(), dropsFor);
logger.info("generating migration:{}", fullVersion);
logInfo("generating migration:%s", fullVersion);
if (!request.dbinitMigration && !writeMigrationXml(dbMigration, request.modelDir, fullVersion)) {
logger.warn("migration already exists, not generating DDL");
logError("migration already exists, not generating DDL");
return null;
} else {
if (!platforms.isEmpty()) {
@@ -627,7 +657,6 @@ public class DefaultDbMigration implements DbMigration {
if (vanillaPlatform || databasePlatform == null) {
// not explicitly set so use the platform of the server
databasePlatform = server.getDatabasePlatform();
logger.trace("set platform to {}", databasePlatform.getName());
}
if (migrationConfig != null) {
if (strictMode != null) {
@@ -713,13 +742,17 @@ public class DefaultDbMigration implements DbMigration {
// path to src/main/resources in typical maven project
File resourceRootDir = new File(pathToResources);
if (!resourceRootDir.exists()) {
String msg = String.format("Error - path to resources %s does not exist. Absolute path is %s", pathToResources, resourceRootDir.getAbsolutePath());
throw new UnknownResourcePathException(msg);
}
String resourcePath = migrationConfig.getMigrationPath(dbinitMigration);
// expect to be a path to something like - src/main/resources/dbmigration/model
File path = new File(resourceRootDir, resourcePath);
if (!path.exists()) {
if (!path.mkdirs()) {
logger.debug("Unable to ensure migration directory exists at {}", path.getAbsolutePath());
logInfo("Warning - Unable to ensure migration directory exists at %s", path.getAbsolutePath());
}
}
return path;
@@ -735,7 +768,7 @@ public class DefaultDbMigration implements DbMigration {
}
File modelDir = new File(migrationDirectory, migrationConfig.getModelPath());
if (!modelDir.exists() && !modelDir.mkdirs()) {
logger.debug("Unable to ensure migration model directory exists at {}", modelDir.getAbsolutePath());
logInfo("Warning - Unable to ensure migration model directory exists at %s", modelDir.getAbsolutePath());
}
return modelDir;
}
@@ -33,12 +33,12 @@ class LastMigration {
*/
static String lastVersion(File migDirectory, File modelDir) {
List<String> fileNames = new ArrayList<>();
List<MigrationVersion> versions = new ArrayList<>();
File[] sqlFiles = migDirectory.listFiles(pathname -> includeSqlFile(pathname.getName().toLowerCase()));
if (sqlFiles != null) {
for (File file : sqlFiles) {
fileNames.add(trim(file.getName()));
versions.add(trimAndParse(file.getName()));
}
}
@@ -46,14 +46,14 @@ class LastMigration {
File[] xmlFiles = modelDir.listFiles(pathname -> includeModelFile(pathname.getName().toLowerCase()));
if (xmlFiles != null) {
for (File file : xmlFiles) {
fileNames.add(trim(file.getName()));
versions.add(trimAndParse(file.getName()));
}
}
}
Collections.sort(fileNames);
if (!fileNames.isEmpty()) {
return fileNames.get(fileNames.size() - 1);
Collections.sort(versions);
if (!versions.isEmpty()) {
return versions.get(versions.size() - 1).asString();
}
return null;
}
@@ -69,23 +69,14 @@ class LastMigration {
return lowerFileName.endsWith(MODEL_XML);
}
private static String trim(String name) {
name = name.toLowerCase();
char c = name.charAt(0);
if (c == 'v') {
name = name.substring(1);
}
int p = name.indexOf("__");
if (p > -1) {
name = name.substring(0, p);
}
private static MigrationVersion trimAndParse(String name) {
if (name.endsWith(SQL)) {
name = name.substring(0, name.length() - 4);
}
if (name.endsWith(MODEL_XML)) {
name = name.substring(0, name.length() - 10);
}
return name;
return MigrationVersion.parse(name);
}
}
@@ -0,0 +1,14 @@
package io.ebeaninternal.dbmigration;
/**
* Exception when db migration resource path does not exist.
* <p>
* Typically the working directory or pathToResources is incorrect.
* </p>
*/
public class UnknownResourcePathException extends RuntimeException {
public UnknownResourcePathException(String msg) {
super(msg);
}
}
@@ -147,7 +147,11 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
@Override
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
visitScalar(p);
if (p instanceof BeanPropertyAssocOne) {
visitOneImported((BeanPropertyAssocOne)p);
} else {
visitScalar(p);
}
if (embedded.isId()) {
// compound primary key
lastColumn.setPrimaryKey(true);
@@ -88,4 +88,10 @@ public class DefaultBeanState implements BeanState {
public void resetForInsert() {
intercept.setNew();
}
@Override
public Map<String, Exception> getLoadErrors() {
return intercept.getLoadErrors();
}
}
@@ -1083,15 +1083,25 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
@Override
public SqlQuery createSqlQuery(String sql) {
public SqlQuery sqlQuery(String sql) {
return new DefaultRelationalQuery(this, sql.trim());
}
@Override
public SqlUpdate createSqlUpdate(String sql) {
public SqlQuery createSqlQuery(String sql) {
return sqlQuery(sql);
}
@Override
public SqlUpdate sqlUpdate(String sql) {
return new DefaultSqlUpdate(this, sql.trim());
}
@Override
public SqlUpdate createSqlUpdate(String sql) {
return sqlUpdate(sql);
}
@Override
public CallableSql createCallableSql(String sql) {
return new DefaultCallableSql(this, sql.trim());
@@ -1363,6 +1373,21 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return !ids.isEmpty();
}
@Override
public <T> boolean exists(SpiQuery<?> ormQuery, SpiTransaction transaction) {
ormQuery.setMaxRows(1);
SpiOrmQueryRequest<?> request = createQueryRequest(Type.ID_LIST, ormQuery, transaction);
try {
request.initTransIfRequired();
List<Object> ids = request.findIds();
return !ids.isEmpty();
} finally {
request.endTransIfRequired();
}
}
@Override
public <A, T> List<A> findIds(Query<T> query, Transaction t) {
@@ -1444,10 +1469,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return queryFuture;
}
public <T> FutureRowCount<T> findFutureRowCount(Query<T> q, Transaction t) {
return findFutureCount(q, t);
}
@Override
public <T> FutureIds<T> findFutureIds(Query<T> query, Transaction t) {
@@ -9,6 +9,7 @@ import io.ebeaninternal.api.SpiSqlUpdate;
import io.ebeaninternal.api.SpiTransaction;
import java.io.Serializable;
import java.util.Collection;
/**
* A SQL Update Delete or Insert statement that can be executed. For the times
@@ -35,9 +36,14 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
private final BindParams bindParams;
/**
* The sql update or delete statement.
* The original sql update or delete statement.
*/
private final String sql;
private final String origSql;
/**
* The sql taking into account bind parameter expansion.
*/
private String baseSql;
/**
* The actual sql with named parameters converted.
@@ -66,6 +72,8 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
*/
private int addPos;
private int bindExpansion;
private boolean getGeneratedKeys;
private Object generatedKey;
@@ -89,7 +97,8 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
*/
public DefaultSqlUpdate(SpiEbeanServer server, String sql, BindParams bindParams) {
this.server = server;
this.sql = sql;
this.origSql = sql;
this.baseSql = sql;
this.bindParams = bindParams;
}
@@ -221,11 +230,21 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
@Override
public void setGeneratedSql(String generatedSql) {
this.generatedSql = generatedSql;
this.baseSql = origSql;
if (bindExpansion > 0) {
bindParams.reset();
bindExpansion = 0;
}
}
@Override
public String getSql() {
return sql;
return origSql;
}
@Override
public String getBaseSql() {
return baseSql;
}
@Override
@@ -239,27 +258,61 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
return this;
}
@Override
public SqlUpdate setParams(Object... values) {
for (Object value : values) {
setParameter(++addPos, value);
}
return this;
}
@Override
public SqlUpdate setNextParameter(Object value) {
setParameter(++addPos, value);
return this;
}
private SqlUpdate setParamWithBindExpansion(int position, Collection values, String bindLiteral) {
StringBuilder sqlExpand = new StringBuilder(values.size() * 2);
position = position + bindExpansion;
int offset = 0;
for (Object val : values) {
if (offset > 0) {
sqlExpand.append(",");
}
sqlExpand.append("?");
bindParams.setParameter(position + offset++, val);
}
bindExpansion += (offset - 1);
baseSql = baseSql.replace(bindLiteral, sqlExpand.toString());
return this;
}
@Override
public SqlUpdate setParameter(int position, Object value) {
bindParams.setParameter(position, value);
if (value instanceof Collection) {
String bindLiteral = "?" + position;
int pos = baseSql.indexOf(bindLiteral);
if (pos > -1) {
return setParamWithBindExpansion(position, (Collection) value, bindLiteral);
}
}
bindParams.setParameter(bindExpansion + position, value);
return this;
}
@Override
public SqlUpdate setNull(int position, int jdbcType) {
bindParams.setNullParameter(position, jdbcType);
bindParams.setNullParameter(bindExpansion + position, jdbcType);
return this;
}
@Override
public SqlUpdate setNullParameter(int position, int jdbcType) {
bindParams.setNullParameter(position, jdbcType);
bindParams.setNullParameter(bindExpansion + position, jdbcType);
return this;
}
@@ -118,7 +118,14 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
@Override
public boolean isDeleteByStatement() {
return beanDescriptor.isDeleteByStatement();
if (!transaction.isPersistCascade() || beanDescriptor.isDeleteByStatement()) {
// plain delete by query
return true;
} else {
// delete by ids due to cascading delete needs
queryPlanKey = query.setDeleteByIdsPlan();
return false;
}
}
@Override
@@ -27,6 +27,7 @@ import io.ebeaninternal.server.persist.BatchedSqlException;
import io.ebeaninternal.server.persist.DeleteMode;
import io.ebeaninternal.server.persist.Flags;
import io.ebeaninternal.server.persist.PersistExecute;
import io.ebeaninternal.server.persist.SaveManyBeans;
import io.ebeaninternal.server.transaction.BeanPersistIdMap;
import io.ebeanservice.docstore.api.DocStoreUpdate;
import io.ebeanservice.docstore.api.DocStoreUpdateContext;
@@ -174,7 +175,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
private int pendingPostUpdateNotify;
/**
* Set to true when post execute has occured (so includes batch flush).
* Set to true when post execute has occurred (so includes batch flush).
*/
private boolean postExecute;
@@ -183,6 +184,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
*/
private boolean complete;
/**
* Many to many intersection table changes that are held for later batch processing.
*/
private List<SaveManyBeans> saveManyIntersections;
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
PersistExecute persistExecute, PersistRequest.Type type, int flags) {
@@ -955,6 +961,13 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
if (isLogSummary()) {
logSummary();
}
saveQueuedManyIntersection();
}
private void saveQueuedManyIntersection() {
if (saveManyIntersections != null) {
saveManyIntersections.forEach(SaveManyBeans::saveIntersectionBatch);
}
}
/**
@@ -1119,6 +1132,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
updatedManysOnly = true;
setNotifyCache();
addPostCommitListeners();
saveQueuedManyIntersection();
}
notifyCacheOnComplete();
postUpdateNotify();
@@ -1454,4 +1468,21 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
public String getSelectLastInsertedId() {
return beanDescriptor.getSelectLastInsertedId(publish);
}
/**
* Return true if the intersection table updates should be queued and batched.
*/
public boolean isQueueManyIntersection() {
return !postExecute;
}
/**
* The intersection table updates to the batch executed later on postExecute.
*/
public void addManyIntersection(SaveManyBeans saveManyIntersection) {
if (this.saveManyIntersections == null) {
this.saveManyIntersections = new ArrayList<>();
}
this.saveManyIntersections.add(saveManyIntersection);
}
}
@@ -3,7 +3,10 @@ package io.ebeaninternal.server.core;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiSqlUpdate;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.server.lib.util.Str;
import io.ebeaninternal.server.persist.BatchControl;
import io.ebeaninternal.server.persist.PersistExecute;
import io.ebeaninternal.server.persist.TrimLogSql;
/**
* Persist request specifically for CallableSql.
@@ -24,12 +27,12 @@ public final class PersistRequestUpdateSql extends PersistRequest {
private String tableName;
private String description;
private boolean addBatch;
private boolean forceNoBatch;
private boolean batchThisRequest;
public PersistRequestUpdateSql(SpiEbeanServer server, SpiSqlUpdate sqlUpdate,
SpiTransaction t, PersistExecute persistExecute, boolean forceNoBatch) {
@@ -58,6 +61,25 @@ public final class PersistRequestUpdateSql extends PersistRequest {
return executeOrQueue();
}
/**
* Execute using jdbc batch.
*/
public void executeAddBatch() {
this.addBatch = true;
persistExecute.executeSqlUpdate(this);
}
/**
* Add this request to BatchControl to flush later.
*/
public void addToFlushQueue(boolean early) {
BatchControl control = transaction.getBatchControl();
if (control == null) {
control = persistExecute.createBatchControl(transaction);
}
control.addToFlushQueue(this, early);
}
@Override
public int executeNow() {
return persistExecute.executeSqlUpdate(this);
@@ -104,10 +126,9 @@ public final class PersistRequestUpdateSql extends PersistRequest {
* Specify the type of statement executed. Used to automatically register
* with the transaction event.
*/
public void setType(SqlType sqlType, String tableName, String description) {
public void setType(SqlType sqlType, String tableName) {
this.sqlType = sqlType;
this.tableName = tableName;
this.description = description;
}
/**
@@ -117,6 +138,20 @@ public final class PersistRequestUpdateSql extends PersistRequest {
this.bindLog = bindLog;
}
public void startBind(boolean batchThisRequest) {
this.batchThisRequest = batchThisRequest;
super.startBind(batchThisRequest);
}
/**
* Log the sql bind used with jdbc batch.
*/
public void logSqlBatchBind() {
if (transaction.isLogSql()) {
transaction.logSql(Str.add(" -- bind(", bindLog, ")"));
}
}
/**
* Perform post execute processing.
*/
@@ -125,9 +160,8 @@ public final class PersistRequestUpdateSql extends PersistRequest {
if (startNanos > 0) {
persistExecute.collectSqlUpdate(label, startNanos, rowCount);
}
if (transaction.isLogSummary()) {
String m = description + " table[" + tableName + "] rows[" + rowCount + "] bind[" + bindLog + "]";
transaction.logSummary(m);
if (transaction.isLogSql() && !batchThisRequest) {
transaction.logSql(Str.add(TrimLogSql.trim(updateSql.getGeneratedSql()), "; -- bind(", bindLog, ") rows(", String.valueOf(rowCount), ")"));
}
if (updateSql.isAutoTableMod()) {
@@ -103,6 +103,11 @@ public interface Persister {
*/
void visitMetrics(MetricVisitor visitor);
/**
* Execute or queue the update.
*/
void executeOrQueue(SpiSqlUpdate update, SpiTransaction t, boolean queue);
/**
* Add the statement to JDBC batch for later execution via executeBatch.
*/
@@ -17,9 +17,16 @@ abstract class AssocOneHelp {
protected final BeanDescriptor<?> target;
private final String path;
AssocOneHelp(BeanPropertyAssocOne<?> property) {
this(property, null);
}
AssocOneHelp(BeanPropertyAssocOne<?> property, String embeddedPrefix) {
this.property = property;
this.target = property.targetDescriptor;
this.path = (embeddedPrefix == null) ? property.name : embeddedPrefix + "." + property.name;
}
/**
@@ -67,7 +74,7 @@ abstract class AssocOneHelp {
boolean disableLazyLoading = ctx.isDisableLazyLoading();
Object ref = target.contextRef(pc, ctx.isReadOnly(), disableLazyLoading, id);
if (!disableLazyLoading) {
ctx.register(property.name, ((EntityBean) ref)._ebean_getIntercept());
ctx.register(path, ((EntityBean) ref)._ebean_getIntercept());
}
return ref;
}
@@ -5,8 +5,8 @@ package io.ebeaninternal.server.deploy;
*/
class AssocOneHelpRefSimple extends AssocOneHelp {
AssocOneHelpRefSimple(BeanPropertyAssocOne<?> property) {
super(property);
AssocOneHelpRefSimple(BeanPropertyAssocOne<?> property, String embeddedPrefix) {
super(property, embeddedPrefix);
}
/**
@@ -2,6 +2,8 @@ package io.ebeaninternal.server.deploy;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
import io.ebeaninternal.server.query.CQueryCollectionAdd;
/**
@@ -10,10 +12,10 @@ import io.ebeaninternal.server.core.OrmQueryRequest;
public class BeanCollectionHelpFactory {
@SuppressWarnings("rawtypes")
static final BeanListHelp LIST_HELP = new BeanListHelp();
private static final BeanListHelp LIST_HELP = new BeanListHelp();
@SuppressWarnings("rawtypes")
static final BeanSetHelp SET_HELP = new BeanSetHelp();
private static final BeanSetHelp SET_HELP = new BeanSetHelp();
/**
* Create the helper based on the many property.
@@ -36,7 +38,7 @@ public class BeanCollectionHelpFactory {
}
@SuppressWarnings("unchecked")
public static <T> BeanCollectionHelp<T> create(SpiQuery.Type manyType, OrmQueryRequest<T> request) {
public static <T> CQueryCollectionAdd<T> create(SpiQuery.Type manyType, OrmQueryRequest<T> request) {
if (manyType == SpiQuery.Type.LIST) {
return LIST_HELP;
@@ -44,10 +46,13 @@ public class BeanCollectionHelpFactory {
} else if (manyType == SpiQuery.Type.SET) {
return SET_HELP;
} else {
} else if (manyType == SpiQuery.Type.MAP) {
BeanDescriptor<T> target = request.getBeanDescriptor();
String mapKey = request.getQuery().getMapKey();
return new BeanMapHelp<>(target, mapKey);
ElPropertyValue elProperty = target.getElGetValue(request.getQuery().getMapKey());
return new BeanMapQueryHelp<>(elProperty);
} else {
return null;
}
}
@@ -724,16 +724,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
inheritInfo.setDescriptor(this);
}
if (isEmbedded()) {
// initialise all the properties
for (BeanProperty prop : propertiesAll()) {
prop.initialise(initContext);
}
} else {
// initialise just the Id properties
if (idProperty != null) {
idProperty.initialise(initContext);
}
// initialise just the Id property only
if (idProperty != null) {
idProperty.initialise(initContext);
}
}
@@ -759,14 +752,12 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
}
}
if (!isEmbedded()) {
// initialise all the non-id properties
for (BeanProperty prop : propertiesAll()) {
if (!prop.isId()) {
prop.initialise(initContext);
}
prop.registerColumn(this, null);
// initialise all the non-id properties
for (BeanProperty prop : propertiesAll()) {
if (!prop.isId()) {
prop.initialise(initContext);
}
prop.registerColumn(this, null);
}
if (unidirectional != null) {
@@ -2595,9 +2586,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
/**
* Return a 'dynamic property' used to read a formula.
*/
private STreeProperty findSqlTreeFormula(String formulaExpression) {
return dynamicProperty.computeIfAbsent(formulaExpression, (formula) -> new FormulaPropertyPath(this, formula).build());
private STreeProperty findSqlTreeFormula(String formula, String path) {
String key = formula + "-" + path;
return dynamicProperty.computeIfAbsent(key, (fullKey) -> new FormulaPropertyPath(this, formula, path).build());
}
/**
@@ -2606,9 +2597,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* The property can be a dynamic formula or a well known bean property.
*/
@Override
public STreeProperty findPropertyWithDynamic(String propName) {
public STreeProperty findPropertyWithDynamic(String propName, String path) {
if (propName.indexOf('(') > -1) {
return findSqlTreeFormula(propName);
return findSqlTreeFormula(propName, path);
}
return _findBeanProperty(propName);
}
@@ -8,6 +8,8 @@ class BeanDescriptorInitContext {
private final Map<String, String> draftTables;
private final String asOfViewSuffix;
private String embeddedPrefix;
BeanDescriptorInitContext(Map<String, String> withHistoryTables, Map<String, String> draftTables, String asOfViewSuffix) {
this.withHistoryTables = withHistoryTables;
this.draftTables = draftTables;
@@ -29,4 +31,12 @@ class BeanDescriptorInitContext {
void addDraftIntersection(String intersectionPublishTable, String intersectionDraftTable) {
draftTables.put(intersectionPublishTable, intersectionDraftTable);
}
public void setEmbeddedPrefix(String embeddedPrefix) {
this.embeddedPrefix = embeddedPrefix;
}
public String getEmbeddedPrefix() {
return embeddedPrefix;
}
}
@@ -150,8 +150,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final String serverName;
private Map<Class<?>, DeployBeanInfo<?>> deployInfoMap = new HashMap<>();
private final Map<Class<?>, BeanTable> beanTableMap = new HashMap<>();
private final Map<String, BeanDescriptor<?>> descMap = new HashMap<>();
@@ -196,6 +194,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final int queryPlanTTLSeconds;
// temporary collections used during startup and then cleared
private Map<Class<?>, DeployBeanInfo<?>> deployInfoMap = new HashMap<>();
private Set<Class<?>> embeddedIdTypes = new HashSet<>();
private List<DeployBeanInfo<?>> embeddedBeans = new ArrayList<>();
/**
* Create for a given database dbConfig.
*/
@@ -366,7 +370,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
createListeners();
readEntityDeploymentInitial();
readXmlMapping(mappings);
readEmbeddedDeployment();
readEntityBeanTable();
readEntityDeploymentAssociations();
readInheritedIdGenerators();
@@ -385,7 +388,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
logStatus();
deployInfoMap.clear();
// clear collections we no longer need
embeddedIdTypes = null;
embeddedBeans = null;
deployInfoMap = null;
return asOfTableMap;
@@ -592,8 +597,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
for (BeanDescriptor<?> d : descMap.values()) {
d.initLast();
if (!d.isEmbedded()) {
BeanManager<?> m = beanManagerFactory.create(d);
beanManagerMap.put(d.getFullName(), m);
beanManagerMap.put(d.getFullName(), beanManagerFactory.create(d));
checkForValidEmbeddedId(d);
}
}
@@ -676,11 +680,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
logger.debug("Entities[{}]", entityBeanCount);
}
private <T> BeanDescriptor<T> createEmbedded(Class<T> beanClass) {
DeployBeanInfo<T> info = getDeploy(beanClass);
return new BeanDescriptor<>(this, info.getDescriptor());
}
/**
* Return the bean deploy info for the given class.
*/
@@ -689,24 +688,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
return (DeployBeanInfo<T>) deployInfoMap.get(cls);
}
private void registerBeanDescriptor(BeanDescriptor<?> desc) {
private void registerBeanDescriptor(DeployBeanInfo<?> info) {
BeanDescriptor desc = new BeanDescriptor<>(this, info.getDescriptor());
descMap.put(desc.getBeanType().getName(), desc);
if (desc.isDocStoreMapped()) {
descQueueMap.put(desc.getDocStoreQueueId(), desc);
}
}
/**
* Read deployment information for all the embedded beans.
*/
private void readEmbeddedDeployment() {
List<Class<?>> embeddedClasses = bootupClasses.getEmbeddables();
for (Class<?> embeddedClass : embeddedClasses) {
registerBeanDescriptor(createEmbedded(embeddedClass));
}
}
/**
* Read the initial deployment information for the entities.
* <p>
@@ -719,14 +708,31 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
for (Class<?> entityClass : bootupClasses.getEntities()) {
DeployBeanInfo<?> info = createDeployBeanInfo(entityClass);
deployInfoMap.put(entityClass, info);
Class<?> embeddedIdType = info.getEmbeddedIdType();
if (embeddedIdType != null){
embeddedIdTypes.add(embeddedIdType);
}
}
for (Class<?> entityClass : bootupClasses.getEmbeddables()) {
DeployBeanInfo<?> info = createDeployBeanInfo(entityClass);
readDeployAssociations(info);
deployInfoMap.put(entityClass, info);
if (embeddedIdTypes.contains(entityClass)) {
// register embeddedId types early - scalar properties only
// and needed for creating BeanTables (id properties)
registerEmbeddedBean(info);
} else {
// delay register of other embedded beans until after
// the BeanTables have been created to support ManyToOne
embeddedBeans.add(info);
}
}
}
private void registerEmbeddedBean(DeployBeanInfo<?> info) {
readDeployAssociations(info);
registerBeanDescriptor(info);
}
/**
* Create the BeanTable information which has the base table and id.
* <p>
@@ -739,6 +745,11 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
BeanTable beanTable = createBeanTable(info);
beanTableMap.put(beanTable.getBeanType(), beanTable);
}
// register non-id embedded beans (after bean tables are created)
for (DeployBeanInfo<?> info : embeddedBeans) {
registerEmbeddedBean(info);
}
}
/**
@@ -816,13 +827,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
secondaryPropsJoins(info);
}
// Set inheritance info
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
setInheritanceInfo(info);
}
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
registerBeanDescriptor(new BeanDescriptor(this, info.getDescriptor()));
if (!info.isEmbedded()) {
registerBeanDescriptor(info);
}
}
}
@@ -30,7 +30,7 @@ public class BeanEmbeddedMetaFactory {
String columnPrefix = prop.getColumnPrefix();
Map<String, String> propColMap = prop.getDeployEmbedded().getPropertyColumnMap();
BeanProperty[] sourceProperties = targetDesc.propertiesBaseScalar();
BeanProperty[] sourceProperties = targetDesc.propertiesNonTransient();
BeanProperty[] embeddedProperties = new BeanProperty[sourceProperties.length];
for (int i = 0; i < sourceProperties.length; i++) {
@@ -45,7 +45,11 @@ public class BeanEmbeddedMetaFactory {
}
BeanPropertyOverride overrides = new BeanPropertyOverride(dbColumn);
embeddedProperties[i] = new BeanProperty(sourceProperties[i], overrides);
if (sourceProperties[i] instanceof BeanPropertyAssocOne) {
embeddedProperties[i] = new BeanPropertyAssocOne((BeanPropertyAssocOne)sourceProperties[i], overrides);
} else {
embeddedProperties[i] = new BeanProperty(sourceProperties[i], overrides);
}
}
return new BeanEmbeddedMeta(embeddedProperties);
@@ -24,16 +24,6 @@ public class BeanMapHelp<T> extends BaseCollectionHelp<T> {
private final String propertyName;
private final BeanProperty beanProperty;
/**
* When created for a given query that will return a map.
*/
BeanMapHelp(BeanDescriptor<T> targetDescriptor, String mapKey) {
this.targetDescriptor = targetDescriptor;
this.beanProperty = targetDescriptor.getBeanProperty(mapKey);
this.many = null;
this.propertyName = null;
}
/**
* When help is attached to a specific many property.
*/
@@ -0,0 +1,35 @@
package io.ebeaninternal.server.deploy;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.common.BeanMap;
import io.ebeaninternal.server.el.ElPropertyValue;
import io.ebeaninternal.server.query.CQueryCollectionAdd;
class BeanMapQueryHelp<T> implements CQueryCollectionAdd<T> {
private final ElPropertyValue elGetValue;
/**
* Create for a findMap query.
*/
BeanMapQueryHelp(ElPropertyValue elGetValue) {
this.elGetValue = elGetValue;
}
@Override
public BeanCollection<T> createEmptyNoParent() {
return new BeanMap<>();
}
@Override
public void add(BeanCollection collection, EntityBean bean, boolean withCheck) {
if (bean == null) {
((BeanMap<?, ?>) collection).internalPutNull();
} else {
Object keyValue = elGetValue.pathGet(bean);
BeanMap<?, ?> map = ((BeanMap<?, ?>) collection);
map.internalPutWithCheck(keyValue, bean);
}
}
}
@@ -104,6 +104,22 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
this.fetchPreference = deploy.getFetchPreference();
}
/**
* Copy constructor for ManyToOne inside Embeddable.
*/
public BeanPropertyAssoc(BeanPropertyAssoc source, BeanPropertyOverride override) {
super(source, override);
foreignKey = source.foreignKey;
extraWhere = source.extraWhere;
beanTable = source.beanTable;
mappedBy = source.mappedBy;
docStoreDoc = source.docStoreDoc;
targetType = source.targetType;
cascadeInfo = source.cascadeInfo;
fetchPreference = source.fetchPreference;
tableJoin = source.tableJoin.withOverrideColumn(override.getDbColumn());
}
/**
* Initialise post construction.
*/
@@ -93,14 +93,35 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
}
}
/**
* Copy constructor for ManyToOne inside Embeddable.
*/
public BeanPropertyAssocOne(BeanPropertyAssocOne source, BeanPropertyOverride override) {
super(source, override);
primaryKeyExport = source.primaryKeyExport;
oneToOne = source.oneToOne;
oneToOneExported = source.oneToOneExported;
orphanRemoval = source.orphanRemoval;
embeddedProps = null;
embeddedPropsMap = null;
}
@Override
public void initialise(BeanDescriptorInitContext initContext) {
super.initialise(initContext);
initialiseAssocOne();
initialiseAssocOne(initContext.getEmbeddedPrefix());
if (embedded) {
// initialise ManyToOne importedId
initContext.setEmbeddedPrefix(name);
for (BeanProperty embeddedProp : embeddedProps) {
embeddedProp.initialise(initContext);
}
initContext.setEmbeddedPrefix(null);
}
}
private void initialiseAssocOne() {
localHelp = createHelp(embedded, oneToOneExported);
private void initialiseAssocOne(String embeddedPrefix) {
localHelp = createHelp(embedded, oneToOneExported, embeddedPrefix);
if (!isTransient) {
//noinspection StatementWithEmptyBody
@@ -668,7 +689,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
}
}
private AssocOneHelp createHelp(boolean embedded, boolean oneToOneExported) {
private AssocOneHelp createHelp(boolean embedded, boolean oneToOneExported, String embeddedPrefix) {
if (embedded) {
return new AssocOneHelpEmbedded(this);
} else if (oneToOneExported) {
@@ -677,7 +698,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
if (targetInheritInfo != null) {
return new AssocOneHelpRefInherit(this);
} else {
return new AssocOneHelpRefSimple(this);
return new AssocOneHelpRefSimple(this, embeddedPrefix);
}
}
}
@@ -90,4 +90,10 @@ public interface DbReadContext {
* Return true if this request disables lazy loading.
*/
boolean isDisableLazyLoading();
/**
* Handles a load error on given property.
*/
void handleLoadError(String fullName, Exception e);
}
@@ -49,15 +49,15 @@ class DynamicPropertyAggregationFormula extends DynamicPropertyBase {
@Override
public void load(SqlBeanLoad sqlBeanLoad) {
Object value;
try {
Object value = scalarType.read(sqlBeanLoad.ctx().getDataReader());
if (asTarget != null) {
sqlBeanLoad.load(asTarget, value);
}
value = scalarType.read(sqlBeanLoad.ctx().getDataReader());
} catch (Exception e) {
throw new PersistenceException("Error loading on " + fullName, e);
sqlBeanLoad.ctx().handleLoadError(fullName, e);
return;
}
if (asTarget != null) {
sqlBeanLoad.load(asTarget, value);
}
}
@@ -20,15 +20,17 @@ class FormulaPropertyPath {
private final String internalExpression;
private final String path;
private boolean countDistinct;
private String cast;
private String alias;
FormulaPropertyPath(BeanDescriptor<?> descriptor, String formula) {
FormulaPropertyPath(BeanDescriptor<?> descriptor, String formula, String path) {
this.descriptor = descriptor;
this.formula = formula;
this.path = path;
int openBracket = formula.indexOf('(');
int closeBracket = formula.lastIndexOf(')');
@@ -94,6 +96,11 @@ class FormulaPropertyPath {
DeployPropertyParser parser = descriptor.parser().setCatchFirst(true);
String parsed = parser.parse(internalExpression);
if (path != null) {
// fetch("machineStats", "sum(hours), sum(totalKms)")
parsed = parsed.replace("${}", "${" + path + "}");
}
ElPropertyDeploy firstProp = parser.getFirstProp();
ScalarType<?> scalarType;
@@ -1,8 +1,8 @@
package io.ebeaninternal.server.deploy;
import io.ebean.SqlUpdate;
import io.ebeaninternal.api.BindParams;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiSqlUpdate;
import io.ebeaninternal.server.core.DefaultSqlUpdate;
import io.ebeaninternal.server.expression.DefaultExpressionRequest;
import io.ebeaninternal.server.expression.IdInExpression;
@@ -45,7 +45,7 @@ public class IntersectionRow {
values.put(key, value);
}
public SqlUpdate createInsert(SpiEbeanServer server) {
public SpiSqlUpdate createInsert(SpiEbeanServer server) {
BindParams bindParams = new BindParams();
@@ -73,7 +73,7 @@ public class IntersectionRow {
return new DefaultSqlUpdate(server, sb.toString(), bindParams);
}
public SqlUpdate createDelete(SpiEbeanServer server, DeleteMode deleteMode) {
public SpiSqlUpdate createDelete(SpiEbeanServer server, DeleteMode deleteMode) {
BindParams bindParams = new BindParams();
@@ -108,7 +108,7 @@ public class IntersectionRow {
return new DefaultSqlUpdate(server, sb.toString(), bindParams);
}
public SqlUpdate createDeleteChildren(SpiEbeanServer server) {
public SpiSqlUpdate createDeleteChildren(SpiEbeanServer server) {
BindParams bindParams = new BindParams();
@@ -51,6 +51,15 @@ public final class TableJoin {
this.queryHash = calcQueryHash();
}
private TableJoin(TableJoin source, String overrideColumn) {
this.table = source.table;
this.type = source.type;
this.inheritInfo = source.inheritInfo;
this.columns = new TableJoinColumn[1];
this.columns[0] = source.columns[0].withOverrideColumn(overrideColumn);
this.queryHash = calcQueryHash();
}
/**
* Calculate a hash value for adding to a query plan.
*/
@@ -166,4 +175,11 @@ public final class TableJoin {
sb.append(a2).append(".").append(pair.getForeignDbColumn());
}
}
TableJoin withOverrideColumn(String overrideColumn) {
if (columns.length == 1 && overrideColumn != null && !overrideColumn.equals(columns[0].getLocalDbColumn())) {
return new TableJoin(this, overrideColumn);
}
return this;
}
}

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