Compare commits

..
Author SHA1 Message Date
rob bygrave e197bb2900 [maven-release-plugin] prepare release ebean-10.4.3 2017-08-31 01:50:06 +12:00
rob bygrave b54b8815d9 Merge branch 'master' of github.com:ebean-orm/ebean 2017-08-30 21:59:08 +12:00
rob bygrave 1d1fde61be #1101 - findNative() on a bean with an associated @OneToOne (with the PK being derived from the parent) maps incorrectly 2017-08-30 21:58:33 +12:00
rob bygrave 683186cbe9 No effective change - pom whitespace only 2017-08-30 21:14:20 +12:00
Roy de BokxandRob Bygrave 5e6be40454 Only throwing warning for autocommit when server is not set to use autocommit-friendly TransactionManager. (#1085) 2017-08-30 20:57:49 +12:00
Roland PramlandRob Bygrave c00febd95b ENH: Abort all tests if connection to db could not be esablished (#1099) 2017-08-30 20:04:50 +12:00
Roland PramlandRob Bygrave 4c8e1113bf FIX: deadlock in TestCommitAndContiune for sqlserver & hsqldb (#1098) 2017-08-30 20:03:41 +12:00
Roland PramlandRob Bygrave 6f854da70b Fix/corrected error codes (#1097)
* FIX: db2plaform error codes and dbtypes

* FIX: H2: Add error code for locking

* FIX: Mysql: Add DataIntegrity-error code
2017-08-30 20:02:34 +12:00
Roland PramlandRob Bygrave 3f5a162a4e Fix/connection to different platforms (#1096)
* ADD: Profile for oracle and db2 to pom file. Downgraded mysql driver

* FIX: connection string for several platforms

* ADD: Documentation
2017-08-30 20:00:58 +12:00
Roland PramlandRob Bygrave d14529b602 FIX: optimized unique constraint handling for platforms that does support them on nullable columns (#1095) 2017-08-30 19:54:53 +12:00
Roland PramlandRob Bygrave f471961f06 FIX: extra-ddl modified to be compatible with different platfoms (#1094) 2017-08-30 17:51:41 +12:00
Roland PramlandRob Bygrave 457c1356a4 Fix/shortened tablenames (#1093)
* FIX: Shortened tablename (will fail on oracle)

* FIX: renamed table, otherwise, in db2 he_link_doc & hi_link_ are both generating the same index ix_h_lnk_dc_h_dc
2017-08-30 17:50:40 +12:00
Roland PramlandRob Bygrave 419ac2bf50 FIX: Possible NPE in StringHelper (#1092) 2017-08-30 17:49:34 +12:00
Roland PramlandRob Bygrave 727d088577 FIX: add MAX-DATE as default value for ROW END (#1091) 2017-08-30 17:48:36 +12:00
Roland PramlandRob Bygrave 972d2dcc6b FIX: nvarchar(max) length specified differently, otherwise this leads to a ddl like this "nvarchar(max)(700)" (#1090) 2017-08-30 17:47:21 +12:00
Roland PramlandRob Bygrave a5bcd49ecb FIX: Clean up before running tests - otherwise tests will fail if a previous test did not clean up well. (This fixes test failures in Eclipse where tests are executed in different order than in maven) (#1089) 2017-08-30 17:44:30 +12:00
Roland PramlandRob Bygrave ff496ad2d3 FIX: EnumToDbIntegerMap did not set 'integerType' - this leads to an incompatible quoting. Constraints like "check in ('1','2','3')" were not accepted by hsqldb on integer columns. (#1088) 2017-08-30 17:42:25 +12:00
Roland PramlandRob Bygrave 24d438ba23 FIX: H2HistoryTrigger honors schema (#1087) 2017-08-30 17:38:52 +12:00
Roland PramlandRob Bygrave 591067e39e FIX: Typo in classname (#1086) 2017-08-30 17:37:45 +12:00
rob bygrave 38a95dc5c4 No effective change - Reformat code - ScalarTypeEnumStandard 2017-08-24 22:37:41 +12:00
icodeandRob Bygrave 53426d8493 use jackson configuration [SD]eserialization enum (#1079)
* use jackson configuration [SD]eserialization enum

https://github.com/FasterXML/jackson-databind/wiki/Serialization-features 
https://github.com/FasterXML/jackson-databind/wiki/Deserialization-Features

keep jackson and ebean [SD]eserialization same
`objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_INDEX);`

* Update ScalarTypeEnumStandard.java

* Update ScalarTypeEnumStandard.java

* Update ScalarTypeEnumStandard.java

* Update ScalarTypeEnumStandard.java
2017-08-24 22:35:36 +12:00
rob bygrave a06238558e No effective change - Jackson WRITE_ENUMS_USING_INDEX test 2017-08-24 22:34:57 +12:00
Yuan XueguiandRob Bygrave b3a3b19d5d Fix: query table entity or sql entity has no id (#1082)
When query table entity or sql entity,include NamedQuery which modified via code and RawSqlQuery which modified via code.
2017-08-24 22:18:46 +12:00
rob bygrave 57231c80a8 #1084 - NPE When DB Migration generation run with @WhoCreated mapping 2017-08-24 22:15:01 +12:00
rob bygrave dc96fb9ad5 No effective change - update test, better asserts 2017-08-15 21:47:21 +12:00
rob bygrave 567847b47a #1081 - Update jackson-core dependency to 2.9.0 2017-08-05 00:11:53 +12:00
rob bygrave b74fed7073 Bump to 10.4.1 agent (@Transient properties included in mapped superclass enhancement detection) 2017-08-04 22:59:53 +12:00
rob bygrave cdaf8ee6d5 [maven-release-plugin] prepare for next development iteration 2017-08-04 21:33:33 +12:00
rob bygrave c6c5073e47 [maven-release-plugin] prepare release ebean-10.4.2 2017-08-04 21:33:19 +12:00
rob bygrave 2a58b954e9 #1062 - Multi-tenant - Update statements should include tenantId in where clause 2017-08-04 21:16:14 +12:00
Koen De GrooteandRob Bygrave 9772623880 Added the Serializable interface to classes that extend/implement a Comparator with an anonymous/typed class. (#1078) 2017-08-03 23:29:56 +12:00
rob bygrave 27b6183aad Merge branch 'master' of github.com:ebean-orm/ebean 2017-08-03 23:28:35 +12:00
David HofmannandRob Bygrave 55e19618f8 fix connection leak in JTA managed transactions (#1077) 2017-08-03 23:28:21 +12:00
rob bygrave 5c60ba4d7f Update test for WriteJson to just use PathProperties 2017-08-03 23:01:22 +12:00
rob bygrave b9cc584086 #1080 - NOT IN expression using entity beans / reference beans uses "in" 2017-08-03 20:31:18 +12:00
Koen De GrooteandRob Bygrave bf5a8c2fad Changed some boolean checks to use the recommended method of comparing floating point values in Java. (#1075) 2017-07-22 13:00:24 +12:00
rob bygrave 66138e5ea2 #1070 Fix for - Create test for bean cache not being cleared with delete or update queries 2017-07-21 23:31:23 +12:00
Rien Nentjes 75fb89ef76 Create test for bean cache not being cleared with delete or update queries 2017-07-14 11:04:43 +02:00
rob bygrave a2cb468222 No effective change - add test resource ebean.mf to hint to enhancement agent packages to enhance 2017-07-11 21:11:54 +12:00
rob bygrave 166bcd7c45 #1061 - findCount query is not cached in L2 query cache 2017-07-06 21:51:52 +12:00
rob bygrave 95aed6af54 #1060 - findSingleAttributeList doesn't store result in L2 query cache 2017-07-06 21:35:20 +12:00
rob bygrave 7a697c12af No effective change - moved mssql-jdbc down with other test scope dependencies 2017-07-06 21:11:40 +12:00
rob bygrave 23278ce30b #1058 - Refactor - PlatformPrefix isn't actually used in PlatformDddlWriter, removing. 2017-07-06 20:30:20 +12:00
Koen De GrooteandRob Bygrave 36a5610e25 Using Regular expression objects for splitting and replacing in strings. (#1059)
* Where possible, replace .split() and .replace() methods with compiled Regex patterns.
This prevents a regular expression from being compiled multiple times during runtime.

* Forgot making one instance `private static final`.
2017-07-06 20:14:51 +12:00
rob bygrave 08263c4588 [maven-release-plugin] prepare for next development iteration 2017-07-05 21:41:12 +12:00
rob bygrave 1e88a52208 [maven-release-plugin] prepare release ebean-10.4.1 2017-07-05 21:40:58 +12:00
rob bygrave e770a42175 Bump to 10.4.1-SNAPSHOT (due to ServerConfig h2ProductionMode removal) 2017-07-05 21:39:12 +12:00
Thibault MeyerandRob Bygrave 35b467c04f JSON simple implementation for MSSQL (#1057)
- Use JSON_VALUE
  - Use NVARCHAR type

Signed-off-by: Thibault Meyer <meyer.thibault@gmail.com>
2017-07-03 20:45:48 +12:00
Roland PramlandRob Bygrave 2001539755 Sqlserver support (#1053)
* made some tests sqlserver ready (No effective code change in src/main)

* FIX enhance packages and let all testcases inherit from BaseTestCase, so that they can be launched directly from eclipse

* ADD sqldriver in pom and datasource

* FIX: DDL-generation for sqlserver - not yet all sqlserver tests passing
mvn verify -Ddatasource.default=mssql => Tests run: 1980, Failures: 22, Errors: 44, Skipped: 16

* BaseDdlHandlerTest checks against correct sqlserver-ddl now
2017-07-03 20:27:37 +12:00
rob bygrave e49c4bfe5a #867 - ENH: Add ability to globally use quoted identifiers on all tables and columns 2017-07-02 21:07:58 +12:00
rob bygrave 15be31fd5f #1051 - Remove ServerConfig h2ProductionMode ... means for testing with h2 explicitly set ddlGenerate and ddlRun 2017-07-02 11:42:56 +12:00
rob bygrave 4777ec46be #1050 - Bump db-migration to 10.1.10 2017-07-02 11:12:03 +12:00
rob bygrave 80ab731aec Use older postgres jdbc driver 2017-06-25 22:56:21 +12:00
rob bygrave 080bafe18f #1048 - Fix connection leak on failed findIterate() 2017-06-25 22:39:04 +12:00
rob bygrave e913f88f52 Fix test - TestCommitAndContinue ... close extra transactions used in asserts 2017-06-25 21:43:37 +12:00
rob bygrave e94c5484a0 #1047 - Fix connection leak on failed beginTransaction() 2017-06-25 21:42:51 +12:00
rob bygrave 4af00c51d3 travis - dist: trusty ... to get PG 9.6 2017-06-25 19:57:31 +12:00
rob bygrave 23d1256dbf Update to postgres 9.6 for travis test runs 2017-06-25 19:43:05 +12:00
rob bygrave ed0ef46d2e No effective change - update postgresql jdbc driver 2017-06-25 19:03:07 +12:00
rob bygrave 1860ded735 [maven-release-plugin] prepare for next development iteration 2017-06-25 16:01:50 +12:00
rob bygrave d432854b82 [maven-release-plugin] prepare release ebean-10.3.2 2017-06-25 16:01:37 +12:00
rob bygrave 93582c8ea3 Downgrade postgres jdbc driver for commit and continue test 2017-06-25 15:54:14 +12:00
rob bygrave 520d35b1d6 #963 - NullPointerException on no version provided (when setVersion(null) is used) 2017-06-25 14:44:13 +12:00
rob bygrave 8ca3599cbf #1046 Bump DB migration to 10.1.9 - fix for Checksum mismatch does not stop migration 2017-06-25 14:29:46 +12:00
rob bygrave 283ae74219 Update javadoc on ScalarTypeConverter. Should look to officially deprecate this in favor of AttributeConverter. 2017-06-24 11:35:58 +12:00
rob bygrave cebd3f92a4 #1045 - MySql - DbMigration generate wrong comment syntax - should be alter table comment = syntax 2017-06-24 11:23:21 +12:00
rob bygrave 4b2481d3ea #1039 Fix for - reset of dirty flag on L2 cached entities after lazy load 2017-06-23 23:20:17 +12:00
rob bygrave f1f2a416d3 No effective change - reformat only 2017-06-23 23:19:22 +12:00
rob bygrave 74dd97a55f No effective change 2017-06-23 23:18:57 +12:00
rob bygrave 3f04f7f363 Merge branch 'master' of https://github.com/SamhammerAG/ebean into SamhammerAG-master 2017-06-23 19:52:55 +12:00
rob bygrave 800f854a12 #1043 Additional test for RawSql with upper case SQL 2017-06-23 19:46:47 +12:00
rob bygrave bef9e6d54f Merge branch 'master' of github.com:ebean-orm/ebean 2017-06-23 19:43:45 +12:00
Tigran_DadaiantsandRob Bygrave 0e4c0ae6c3 #1043 RawSql should automatically map upper-case column alias to lower-case bean property (#1044) 2017-06-23 19:43:15 +12:00
rob bygrave a0ea9887df No effective change - remove unused imports 2017-06-23 19:37:24 +12:00
Roland PramlandRob Bygrave 0413c22e6e no effective code change - fixed warnings about serialization, override, raw-types (#1032) 2017-06-23 19:33:03 +12:00
rob bygrave 989ae44a6d Merge branch 'pr/id-in-as-collection' of https://github.com/FOCONIS/ebean into FOCONIS-pr/id-in-as-collection 2017-06-23 19:28:25 +12:00
rob bygrave 100d314e63 #1035 Update for - FIX: deleting and adding same bean in beanCollection leads to deletion in DB
If use Modify mode we need to listen for both adds and removes to support sorting (can't rely on undoDelete() due to ordering)
2017-06-19 23:34:44 +12:00
rob bygrave dbf4e99187 #1035 tests for - FIX: deleting and adding same bean in beanCollection leads to deletion in DB 2017-06-19 23:32:58 +12:00
Roland PramlandRob Bygrave 7b8a85106c FIX: deleting and adding same bean in beanCollection leads to deletion in DB (#1035) 2017-06-19 22:50:20 +12:00
rob bygrave 58c5f01e2b #1042 - JtaTransaction is TenantMode.DB aware 2017-06-19 22:28:15 +12:00
Roland PramlandRob Bygrave bc74d35651 FIX: correct behavior of "startsWith, "contains" etc (#1031)
* FIX: correct behavior of "startsWith" if string contains "like"-wildcards

* Removed likeClasues with disabled escaping in database platforms as bind values are properly escaped now.
2017-06-19 22:21:26 +12:00
Roland PramlandRob Bygrave a1f95bf25e Pr/id in as collection2 (#1034)
* ENH: Changed  idIn(List) to idIn(Collection)

* ADD: sanity checks in IdBinder & special case if ID-list is empty
2017-06-19 22:05:56 +12:00
rob bygrave c454e1f8a7 No effective change - add test 2017-06-19 21:53:35 +12:00
Koen De GrooteandRob Bygrave 5a3669e1db Noticed that this boolean parameter was ignored. (#1041)
After some quick analysis, implemented that way it probably should be used.

Of course, this could have been on purpose, please advise.
2017-06-19 21:39:58 +12:00
rob bygrave 7a2e0b3695 Bump to the later agent 2017-06-19 21:34:18 +12:00
AWoelfel ce6d6269cd Step to reproduce reset of dirty flag on L2 cached entities after lazy load 2017-06-08 15:07:32 +02:00
Roland Praml 7d800b7080 ENH: Changed idIn(List) to idIn(Collection) 2017-05-29 15:22:48 +02:00
rob bygrave 136154e247 [maven-release-plugin] prepare for next development iteration 2017-05-21 21:56:29 +12:00
rob bygrave 0a5ce1bee8 [maven-release-plugin] prepare release ebean-10.3.1 2017-05-21 21:56:16 +12:00
Koen De GrooteandRob Bygrave b4e2c89e2d Moved a few variables closer to the point where they are actually used, but only in cases where this means moving them past a (potential) return/throw statement. (#1029) 2017-05-21 21:51:58 +12:00
Koen De GrooteandRob Bygrave 9e2923f571 For debug and trace levels, either added isEnabled check or made arguments parameterized. (#1028) 2017-05-21 21:50:13 +12:00
rob bygrave d0c1ae76f4 #955 - Migration "add column" DDL with check constraint is missing constraint name 2017-05-21 01:23:55 +12:00
rob bygrave fe9500e6d0 #1011 - Create valid migration scripts for non-Postgres databases and @DbArray columns.
Merge branch 'test/dbarray-create-migration' of https://github.com/FOCONIS/ebean into FOCONIS-test/dbarray-create-migration

commit 3a8f32b173
Author: Michael Benz <michael.benz@foconis.de>
Date:   Wed Apr 19 18:16:51 2017 +0200

    adding dbmigration ddlgeneration test for Ebean @DbArray extension.
2017-05-20 23:40:02 +12:00
rob bygrave 79f7c73983 #999 - Refactor tidy for ... @DiscriminatorColumn columnDefinition is ignored for DDL generation 2017-05-20 15:22:06 +12:00
rob bygrave d04954e46f #999 - @DiscriminatorColumn columnDefinition is ignored for DDL generation 2017-05-20 15:03:21 +12:00
rob bygrave 88c6ec358c #981 Additional refactor to 981 - use composed key in multi tenant environment - Expose TenantAwareKey handling for plugins 2017-05-20 13:53:46 +12:00
Roland PramlandRob Bygrave d3ea01b76e use composed key in multi tenant environment instead a cache per tenant (#981) 2017-05-20 12:35:34 +12:00
rob bygrave 0c1a9786a3 #996 Add findOne() and findOneOrEmpty() - SqlQuery 2017-05-19 23:48:41 +12:00
rob bygrave db298a349e #996 Add findOne() and findOneOrEmpty() 2017-05-19 23:38:40 +12:00
rob bygrave c1bed9f578 #996 Add findOne() and findOneOrEmpty() 2017-05-19 23:38:24 +12:00
rob bygrave c7cbe4ead8 Read the new @Length annotation 2017-05-19 23:00:10 +12:00
rob bygrave 4064aaf714 #1004 - Ebean migrations can not be executed if they contain ';' into a string 2017-05-15 22:18:24 +12:00
rob bygrave 6ad808003a Refactor - remove unused ebeandeploy.dtd 2017-05-14 21:11:00 +12:00
rob bygrave 70de6101db Refactor, remove unused SqlReservedWords 2017-05-14 21:10:24 +12:00
Roland PramlandRob Bygrave 933595ceea ADD: Testcases for "fetchDistinct" (#1024)
* ADD: Testcases for "fetchDistinct"

* Updated the tests for #1024
2017-05-14 17:39:07 +12:00
rob bygrave 461c2aef60 #946 - Treat primitive numbers (long, int, short, float, double) as NOT NULL wrt DDL generation 2017-05-14 01:31:28 +12:00
Rob Bygrave 0c49b6514c [maven-release-plugin] prepare for next development iteration 2017-05-11 23:54:47 +12:00
268 changed files with 3207 additions and 1033 deletions
+2 -1
View File
@@ -1,3 +1,4 @@
dist: trusty
sudo: required
language: java
jdk:
@@ -7,7 +8,7 @@ git:
depth: 3
addons:
postgresql: "9.4"
postgresql: "9.6"
services:
- postgresql
+152
View File
@@ -0,0 +1,152 @@
This is a short description how to run tests
============================================
Run tests from maven
--------------------
Just type `mvn test` - it will run the whole test suite with the settings
from `src/test/resources/ebean.properties`.
Run tests from eclipse
----------------------
Running tests requires, that the classes are enhanced. By default, the
`BaseTestCase` tries to load the enhancer-agent in a very early stage. This
also works for most test cases, but it does not work if you try to run a
complete test-suite, as classes that are loaded before the enhancer-agent
kicks in will not get enhanced. To solve this problem, you need either the
[Eclipse-Enhancer-Plugin](https://github.com/ebean-orm-tools/ebean-eclipse-enhancer)
or add the agent to your JVM arguments (Preferences->Installed JREs) or to
your run configuration:
`-javaagent:<USER_HOME>/.m2/repository/io/ebean/ebean-agent/<VERSION>/ebean-agent-<VERSION>.jar=debug=0`
Platform Tests
==============
By default, the `h2` platform is used. To test a different platform, add
`-Ddatasource.default=xxx` to your maven command or JVM arguments.
'h2' platform
-------------
Reqires no setup
run: `mvn clean test`
Current status: PASS
Tests run: 1986, Failures: 0, Errors: 0, Skipped: 16
'pg' platform
-------------
Reqirements
- a locally installed PostgreSQL server with PostGis.
- Create a user "unit" with password "unit" and a database "unit"
- Give the user all permissions to that db
run: `mvn clean test -Ddatasource.default=pg`
Current status: PASS
Tests run: 1986, Failures: 0, Errors: 0, Skipped: 16
'mysql' platform
-------------
Reqirements
- a locally installed MySQL server.
- Create a user "unit" with password "unit" and a database "unit"
- Give the user all permissions to that db
run: `mvn clean test -Ddatasource.default=mysql`
Current status: FAIL
Tests run: 1986, Failures: 2, Errors: 4, Skipped: 16
'sqlserver' platform
--------------------
Reqires an installed sqlserver - e.g. https://hub.docker.com/r/microsoft/mssql-server-linux/
- Create a user "ebean" with password "ebean" and a database "ebean_unittest"
- Adjust the connection string and/or username in ebean.properties
run: `mvn clean test -Ddatasource.default=mssql`
Current status: FAIL
Tests run: 1986, Failures: 19, Errors: 43, Skipped: 16
'oracle' platform
-----------------
Reqires a locally installed Oracle server.
- Create a user "unit" with password "unit" with all permissions
- The test wirtes directly to the "xe" SID - so do not use a productive server!
- As the Oracle JDBC driver is only available on an oracle repository,
you need a special maven setup as described [here](http://docs.oracle.com/middleware/1213/core/MAVEN/config_maven_repo.htm#MAVEN9010)
run: `mvn clean test -Ddatasource.default=ora -Poracle`
Current status: FAIL
Tests run: 1986, Failures: 17, Errors: 34, Skipped: 16
'db2' platform
--------------
Reqires a locally installed DB2 Express-C server.
- Create a user "unit" with password "unit" with all permissions
- Install the db2jcc4 driver (see pom.xml)
run: `mvn clean test -Ddatasource.default=db2 -Pdb2`
Current status: FAIL
Tests run: 1986, Failures: 33, Errors: 26, Skipped: 16
'sqlite' platform
-----------------
Reqires no setup
run: `mvn clean test -Ddatasource.default=sqlite`
Current status: FAIL
Tests run: 1986, Failures: 24, Errors: 86, Skipped: 16
After some time, the db locks up, every test takes 3 seconds and fails with
"the database file is locked"
'hsqldb' platform
-----------------
Reqires no setup
run: `mvn clean test -Ddatasource.default=hsqldb`
Current status: FAIL
Tests run: 1980, Failures: 3, Errors: 657, Skipped: 16
@Rob FYI
The problem is https://sourceforge.net/p/hsqldb/bugs/1364/
In `InsertHandler.getPstmt` the `meta.getIdentityDbColumns()` contains
'id' in lowercase. Changing that to uppercase gives me this result:
`Tests run: 1986, Failures: 2, Errors: 18, Skipped: 16`
(as quick hack I added temporary a toUpperCase to BindableIdScalar.getIdentityColumn)
'sqlanywhere' platform
----------------------
TODO: not yet tested
+67 -9
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>10.2.3</version>
<version>10.4.3</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -17,9 +17,59 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-10.2.3</tag>
<tag>ebean-10.4.3</tag>
</scm>
<profiles>
<profile>
<!-- Note: to use this profile, you need credentials for the oracle repository in your
settings.xml as described here:
http://docs.oracle.com/middleware/1213/core/MAVEN/config_maven_repo.htm#MAVEN9010 -->
<id>oracle</id>
<dependencies>
<dependency>
<groupId>com.oracle.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>12.2.0.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>maven.oracle.com</id>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
<url>https://maven.oracle.com</url>
<layout>default</layout>
</repository>
</repositories>
</profile>
<profile>
<!-- Note: to use this profile, you need to download manually the db2jcc4 driver.
After that, install it into your local maven repository:
mvn install:install-file \
-Dfile=db2jcc4.jar \
-DgroupId=com.ibm.jdbc \
-DartifactId=db2jcc4 \
-Dversion=4.23.42 \
-Dpackaging=jar
-->
<id>db2</id>
<dependencies>
<dependency>
<groupId>com.ibm.jdbc</groupId>
<artifactId>db2jcc4</artifactId>
<version>4.23.42</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
</profiles>
<dependencies>
<dependency>
@@ -67,7 +117,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-dbmigration</artifactId>
<version>10.1.6</version>
<version>10.1.10</version>
</dependency>
<dependency>
@@ -86,14 +136,14 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.5</version>
<version>2.9.0</version>
</dependency>
<!-- provided scope for JsonNode support -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.5</version>
<version>2.9.0</version>
<scope>provided</scope>
</dependency>
@@ -130,7 +180,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4.1212.jre7</version>
<version>9.4.1212</version>
<scope>provided</scope>
</dependency>
@@ -160,7 +210,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-agent</artifactId>
<version>10.1.7</version>
<version>10.4.1</version>
<scope>test</scope>
</dependency>
@@ -187,10 +237,18 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>6.1.3.jre8-preview</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.5</version>
<!-- Do not upgrade to 6.0.6: https://bugs.mysql.com/bug.php?id=82896 -->
<version>5.1.43</version>
<scope>test</scope>
</dependency>
@@ -225,7 +283,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>10.1.7</version>
<version>10.4.1</version>
<executions>
<execution>
<id>test</id>
@@ -10,6 +10,7 @@ import javax.persistence.PessimisticLockException;
* </p>
*/
public class AcquireLockException extends PessimisticLockException {
private static final long serialVersionUID = -8585962352965876691L;
/**
* Create with a message and cause.
@@ -2,6 +2,7 @@ package io.ebean;
import io.ebean.RawSql.ColumnMapping;
import java.util.regex.Pattern;
import javax.persistence.PersistenceException;
import java.util.ArrayList;
@@ -10,6 +11,8 @@ import java.util.ArrayList;
*/
final class DRawSqlColumnsParser {
private static final Pattern COLINFO_SPLIT = Pattern.compile("\\s(?=[^\\)]*(?:\\(|$))");
private final int end;
private final String sqlSelect;
@@ -44,7 +47,7 @@ final class DRawSqlColumnsParser {
String colInfo = sqlSelect.substring(start, pos++);
colInfo = colInfo.trim();
String[] split = colInfo.split("\\s(?=[^\\)]*(?:\\(|$))");
String[] split = COLINFO_SPLIT.split(colInfo);
if (split.length > 1) {
ArrayList<String> tmp = new ArrayList<>(split.length);
for (String aSplit : split) {
@@ -6,6 +6,7 @@ import javax.persistence.PersistenceException;
* Thrown when a foreign key constraint is enforced.
*/
public class DataIntegrityException extends PersistenceException {
private static final long serialVersionUID = -6740171949170180970L;
/**
* Create with a message and cause.
@@ -4,6 +4,7 @@ package io.ebean;
* Thrown when a duplicate is attempted on a unique constraint.
*/
public class DuplicateKeyException extends DataIntegrityException {
private static final long serialVersionUID = -4771932723285724817L;
/**
* Create with a message and cause.
+1 -1
View File
@@ -154,7 +154,7 @@ public final class Ebean {
} else {
// look to see if there is a default server defined
String defaultName = PrimaryServer.getDefaultServerName();
logger.debug("defaultName:" + defaultName);
logger.debug("defaultName:{}", defaultName);
if (defaultName != null && !defaultName.trim().isEmpty()) {
defaultServer = getWithCreate(defaultName.trim());
}
+28 -2
View File
@@ -14,6 +14,7 @@ import javax.persistence.PersistenceException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -1066,7 +1067,7 @@ public interface EbeanServer {
* This will throw a NonUniqueResultException if the query finds more than one result.
* </p>
* <p>
* Generally you are able to use {@link Query#findUnique()} rather than
* Generally you are able to use {@link Query#findOne()} rather than
* explicitly calling this method. You could use this method if you wish to
* explicitly control the transaction used for the query.
* </p>
@@ -1076,7 +1077,22 @@ public interface EbeanServer {
* @param transaction the transaction to use (can be null).
* @return the list of fetched beans.
* @throws NonUniqueResultException if more than one result was found
* @see Query#findUnique()
* @see Query#findOne()
*/
@Nullable
<T> T findOne(Query<T> query, Transaction transaction);
/**
* Similar to findOne() but returns an Optional (rather than nullable).
*/
<T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction);
/**
* Synonym for findOne().
* <p>
* This proceeded findOne which was introduced to better match spring data.
* This will be deprecated at some future point.
* </p>
*/
@Nullable
<T> T findUnique(Query<T> query, Transaction transaction);
@@ -1161,6 +1177,16 @@ public interface EbeanServer {
* @see SqlQuery#findUnique()
*/
@Nullable
SqlRow findOne(SqlQuery query, Transaction transaction);
/**
* Synonym for findOne().
* <p>
* This proceeded findOne which was introduced to better match spring data.
* This will be deprecated at some future point.
* </p>
*/
@Nullable
SqlRow findUnique(SqlQuery query, Transaction transaction);
/**
@@ -7,7 +7,6 @@ import io.ebean.search.TextQueryString;
import io.ebean.search.TextSimple;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
@@ -308,9 +307,9 @@ public interface ExpressionFactory {
Expression idIn(Object... idValues);
/**
* Id IN a list of Id values.
* Id IN a collection of Id values.
*/
Expression idIn(List<?> idList);
Expression idIn(Collection<?> idCollection);
/**
* All Equal - Map containing property names and their values.
+18 -2
View File
@@ -12,6 +12,7 @@ import java.sql.Timestamp;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -266,6 +267,21 @@ public interface ExpressionList<T> {
* @see Query#findUnique()
*/
@Nullable
T findOne();
/**
* Execute the query returning an optional bean.
*/
Optional<T> findOneOrEmpty();
/**
* Synonym for findOne().
* <p>
* This proceeded findOne which was introduced to better match spring data.
* This will be deprecated at some future point.
* </p>
*/
@Nullable
T findUnique();
/**
@@ -820,9 +836,9 @@ public interface ExpressionList<T> {
ExpressionList<T> idIn(Object... idValues);
/**
* Id IN a list of id values.
* Id IN a collection of id values.
*/
ExpressionList<T> idIn(List<?> idValues);
ExpressionList<T> idIn(Collection<?> idValues);
/**
* Id Equal to - ID property is equal to the value.
+4 -4
View File
@@ -6,22 +6,22 @@ package io.ebean;
public enum LikeType {
/**
* You need to put in your own wildcards.
* You need to put in your own wildcards - no escaping is performed.
*/
RAW,
/**
* The % wildcard is added to the end of the search word.
* The % wildcard is added to the end of the search word and search word is escaped.
*/
STARTS_WITH,
/**
* The % wildcard is added to the beginning of the search word.
* The % wildcard is added to the beginning of the search word and search word is escaped.
*/
ENDS_WITH,
/**
* The % wildcard is added to the beginning and end of the search word.
* The % wildcard is added to the beginning and end of the search word and search word is escaped.
*/
CONTAINS,
+16
View File
@@ -6,6 +6,7 @@ import javax.persistence.NonUniqueResultException;
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -854,6 +855,21 @@ public interface Query<T> {
* @throws NonUniqueResultException if more than one result was found
*/
@Nullable
T findOne();
/**
* Execute the query returning an optional bean.
*/
Optional<T> findOneOrEmpty();
/**
* Synonym for findOne().
* <p>
* This proceeded findOne which was introduced to better match spring data.
* This will be deprecated at some future point.
* </p>
*/
@Nullable
T findUnique();
/**
+11
View File
@@ -2,6 +2,7 @@ package io.ebean;
import java.io.Serializable;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -70,6 +71,16 @@ public interface SqlQuery extends Serializable {
* PersistenceException.
* </p>
*/
SqlRow findOne();
/**
* Execute the query returning an optional row.
*/
Optional<SqlRow> findOneOrEmpty();
/**
* Synonym for findOne().
*/
SqlRow findUnique();
/**
+1
View File
@@ -129,6 +129,7 @@ public interface Transaction extends AutoCloseable {
/**
* Synonym for end() to support AutoClosable.
*/
@Override
void close();
/**
@@ -1027,7 +1027,7 @@ public final class EntityBeanIntercept implements Serializable {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
} else if (Double.compare(oldValue, newValue) != 0) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
@@ -1042,7 +1042,7 @@ public final class EntityBeanIntercept implements Serializable {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
} else if (Float.compare(oldValue, newValue) != 0) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
} else {
return null;
+3 -1
View File
@@ -1,5 +1,7 @@
package io.ebean.cache;
import io.ebean.config.CurrentTenantProvider;
/**
* Defines method for constructing caches for beans and queries.
*/
@@ -8,6 +10,6 @@ public interface ServerCacheFactory {
/**
* Create the cache for the given type with options.
*/
ServerCache createCache(ServerCacheType type, String cacheKey, ServerCacheOptions cacheOptions);
ServerCache createCache(ServerCacheType type, String cacheKey, CurrentTenantProvider tenantProvider, ServerCacheOptions cacheOptions);
}
+77
View File
@@ -0,0 +1,77 @@
package io.ebean.cache;
import io.ebean.config.CurrentTenantProvider;
import java.io.Serializable;
import java.util.Objects;
/**
* Tenant aware handling for caching.
*/
public class TenantAwareKey {
private final CurrentTenantProvider tenantProvider;
/**
* Construct with a tenant provider than can be null.
*/
public TenantAwareKey(CurrentTenantProvider tenantProvider) {
this.tenantProvider = tenantProvider;
}
/**
* Return a tenant aware key.
*/
public Object key(Object key) {
if (tenantProvider != null) {
return new CacheKey(key, tenantProvider.currentId());
} else {
return key;
}
}
/**
* We use a combined key, if this serverCache is per tenant.
*/
public static final class CacheKey implements Serializable {
private static final long serialVersionUID = 1L;
final Object key;
final Object tenantId;
/**
* Create with optional tenantId.
*/
public CacheKey(Object key, Object tenantId) {
this.key = key;
this.tenantId = tenantId;
}
@Override
public int hashCode() {
int result = key.hashCode();
result = 92821 * result + Objects.hashCode(tenantId);
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof CacheKey) {
CacheKey that = (CacheKey) obj;
return Objects.equals(that.key, this.key)
&& Objects.equals(that.tenantId, this.tenantId);
}
return false;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(key.toString());
if (tenantId != null) {
sb.append(":").append(tenantId);
}
return sb.toString();
}
}
}
@@ -47,8 +47,7 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
protected ModifyHolder<E> modifyHolder;
protected ModifyListenMode modifyListenMode;
protected boolean modifyAddListening;
protected boolean modifyRemoveListening;
protected boolean modifyListening;
/**
@@ -148,9 +147,7 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
public void setModifyListening(ModifyListenMode mode) {
this.modifyListenMode = mode;
this.modifyAddListening = ModifyListenMode.ALL.equals(mode);
this.modifyRemoveListening = modifyAddListening || ModifyListenMode.REMOVALS.equals(mode);
this.modifyListening = modifyRemoveListening || modifyAddListening;
this.modifyListening = mode != null && ModifyListenMode.NONE != mode;
if (modifyListening) {
// lose any existing modifications
modifyHolder = null;
@@ -173,14 +170,14 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
@Override
public void modifyAddition(E bean) {
if (modifyAddListening) {
if (modifyListening) {
getModifyHolder().modifyAddition(bean);
}
}
@Override
public void modifyRemoval(Object bean) {
if (modifyRemoveListening) {
if (modifyListening) {
getModifyHolder().modifyRemoval(bean);
}
}
+9 -9
View File
@@ -232,7 +232,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
public void add(int index, E element) {
checkReadOnly();
init();
if (modifyAddListening) {
if (modifyListening) {
modifyAddition(element);
}
list.add(index, element);
@@ -247,7 +247,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
public boolean add(E o) {
checkReadOnly();
init();
if (modifyAddListening) {
if (modifyListening) {
if (list.add(o)) {
modifyAddition(o);
return true;
@@ -262,7 +262,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
public boolean addAll(Collection<? extends E> c) {
checkReadOnly();
init();
if (modifyAddListening) {
if (modifyListening) {
// all elements in c are added (no contains checking)
getModifyHolder().modifyAdditionAll(c);
}
@@ -273,7 +273,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
public boolean addAll(int index, Collection<? extends E> c) {
checkReadOnly();
init();
if (modifyAddListening) {
if (modifyListening) {
// all elements in c are added (no contains checking)
getModifyHolder().modifyAdditionAll(c);
}
@@ -286,7 +286,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
// TODO: when clear() and not initialised could be more clever
// and fetch just the Id's
initClear();
if (modifyRemoveListening) {
if (modifyListening) {
for (E aList : list) {
getModifyHolder().modifyRemoval(aList);
}
@@ -380,7 +380,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
public E remove(int index) {
checkReadOnly();
init();
if (modifyRemoveListening) {
if (modifyListening) {
E o = list.remove(index);
modifyRemoval(o);
return o;
@@ -392,7 +392,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
public boolean remove(Object o) {
checkReadOnly();
init();
if (modifyRemoveListening) {
if (modifyListening) {
boolean isRemove = list.remove(o);
if (isRemove) {
modifyRemoval(o);
@@ -406,7 +406,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
public boolean removeAll(Collection<?> beans) {
checkReadOnly();
init();
if (modifyRemoveListening) {
if (modifyListening) {
boolean changed = false;
for (Object bean : beans) {
if (list.remove(bean)) {
@@ -424,7 +424,7 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
public boolean retainAll(Collection<?> retainBeans) {
checkReadOnly();
init();
if (modifyRemoveListening) {
if (modifyListening) {
boolean changed = false;
Iterator<E> it = list.iterator();
while (it.hasNext()) {
+2 -2
View File
@@ -211,7 +211,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
public void clear() {
checkReadOnly();
initClear();
if (modifyRemoveListening) {
if (modifyListening) {
// add all beans to the removal list
for (E bean : map.values()) {
modifyRemoval(bean);
@@ -313,7 +313,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
public E remove(Object key) {
checkReadOnly();
init();
if (modifyRemoveListening) {
if (modifyListening) {
E o = map.remove(key);
modifyRemoval(o);
return o;
+6 -6
View File
@@ -210,7 +210,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
public boolean add(E o) {
checkReadOnly();
init();
if (modifyAddListening) {
if (modifyListening) {
if (set.add(o)) {
modifyAddition(o);
return true;
@@ -225,7 +225,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
public boolean addAll(Collection<? extends E> addCollection) {
checkReadOnly();
init();
if (modifyAddListening) {
if (modifyListening) {
boolean changed = false;
for (E bean : addCollection) {
if (set.add(bean)) {
@@ -243,7 +243,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
public void clear() {
checkReadOnly();
initClear();
if (modifyRemoveListening) {
if (modifyListening) {
for (E bean : set) {
modifyRemoval(bean);
}
@@ -285,7 +285,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
public boolean remove(Object o) {
checkReadOnly();
init();
if (modifyRemoveListening) {
if (modifyListening) {
if (set.remove(o)) {
modifyRemoval(o);
return true;
@@ -299,7 +299,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
public boolean removeAll(Collection<?> beans) {
checkReadOnly();
init();
if (modifyRemoveListening) {
if (modifyListening) {
boolean changed = false;
for (Object bean : beans) {
if (set.remove(bean)) {
@@ -316,7 +316,7 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
public boolean retainAll(Collection<?> beans) {
checkReadOnly();
init();
if (modifyRemoveListening) {
if (modifyListening) {
boolean changed = false;
Iterator<?> it = set.iterator();
while (it.hasNext()) {
@@ -44,21 +44,29 @@ class ModifyHolder<E> implements Serializable {
}
}
private boolean undoDeletion(E bean) {
return (bean != null) && modifyDeletions.remove(bean);
}
void modifyAddition(E bean) {
if (bean != null) {
// If it is to delete then just remove the deletion
if (!modifyDeletions.remove(bean)) {
if (!undoDeletion(bean)) {
// Insert
modifyAdditions.add(bean);
}
}
}
private boolean undoAddition(Object bean) {
return (bean != null) && modifyAdditions.remove(bean);
}
@SuppressWarnings("unchecked")
void modifyRemoval(Object bean) {
if (bean != null) {
// If it is to be added then just remove the addition
if (!modifyAdditions.remove(bean)) {
if (!undoAddition(bean)) {
modifyDeletions.add((E) bean);
}
}
@@ -2,6 +2,8 @@ package io.ebean.config;
import io.ebean.config.dbplatform.DatabasePlatform;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Inheritance;
import javax.persistence.Table;
@@ -14,16 +16,15 @@ import java.lang.annotation.Annotation;
*/
public abstract class AbstractNamingConvention implements NamingConvention {
private static final Pattern TABLE_REPLACE = Pattern.compile("{table}", Pattern.LITERAL);
private static final Pattern COLUMN_REPLACE = Pattern.compile("{column}", Pattern.LITERAL);
/**
* The Constant DEFAULT_SEQ_FORMAT.
*/
public static final String DEFAULT_SEQ_FORMAT = "{table}_seq";
/**
* Sequence Format that includes the Primary Key column
*/
public static final String TABLE_PKCOLUMN_SEQ_FORMAT = "{table}_{column}_seq";
/**
* The catalog.
*/
@@ -83,11 +84,11 @@ public abstract class AbstractNamingConvention implements NamingConvention {
@Override
public String getSequenceName(String tableName, String pkColumn) {
String s = sequenceFormat.replace("{table}", tableName);
String s = TABLE_REPLACE.matcher(sequenceFormat).replaceAll(Matcher.quoteReplacement(tableName));
if (pkColumn == null) {
pkColumn = "";
}
return s.replace("{column}", pkColumn);
return COLUMN_REPLACE.matcher(s).replaceAll(Matcher.quoteReplacement(pkColumn));
}
/**
@@ -7,6 +7,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Properties;
/**
* Configuration for the DB migration processing.
@@ -443,7 +444,7 @@ public class DbMigrationConfig {
/**
* Create the MigrationRunner to run migrations if necessary.
*/
public MigrationRunner createRunner(ClassLoader classLoader) {
public MigrationRunner createRunner(ClassLoader classLoader, Properties properties) {
MigrationConfig runnerConfig = new MigrationConfig();
runnerConfig.setMetaTable(metaTable);
@@ -454,6 +455,9 @@ public class DbMigrationConfig {
runnerConfig.setDbUsername(getDbUsername());
runnerConfig.setDbPassword(getDbPassword());
runnerConfig.setClassLoader(classLoader);
if (properties != null) {
runnerConfig.load(properties);
}
return new MigrationRunner(runnerConfig);
}
}
@@ -31,7 +31,7 @@ public class MatchingNamingConvention extends AbstractNamingConvention {
@Override
public String getColumnFromProperty(Class<?> beanClass, String propertyName) {
return propertyName;
return quoteIdentifiers(propertyName);
}
@Override
@@ -47,8 +47,11 @@ public class MatchingNamingConvention extends AbstractNamingConvention {
@Override
public String getForeignKey(String prefix, String fkProperty) {
prefix = databasePlatform.unQuote(prefix);
fkProperty = databasePlatform.unQuote(fkProperty);
// add fkProperty as init caps
return prefix + fkProperty.substring(0, 1).toUpperCase() + fkProperty.substring(1);
String fullName = prefix + fkProperty.substring(0, 1).toUpperCase() + fkProperty.substring(1);
return quoteIdentifiers(fullName);
}
}
@@ -91,7 +91,7 @@ final class PropertyExpression {
// in which properties are being set/loaded. You can use
// GlobalProperties.evaluateExpressions() to get any unresolved
// expressions to be evaluated
logger.debug("Unable to evaluate expression [" + exp + "]");
logger.debug("Unable to evaluate expression [{}]", exp);
return null;
}
}
@@ -1,5 +1,7 @@
package io.ebean.config;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -18,6 +20,8 @@ final class PropertyMapLoader {
private static final Logger logger = LoggerFactory.getLogger(PropertyMapLoader.class);
private static final Pattern OTHER_PROPS_REPLACE = Pattern.compile("\\", Pattern.LITERAL);
/**
* Load the <code>test-ebean.properties</code>.
*/
@@ -109,10 +113,10 @@ final class PropertyMapLoader {
otherProps = p.remove("load.properties.override");
}
if (otherProps != null) {
otherProps = otherProps.replace("\\", "/");
otherProps = OTHER_PROPS_REPLACE.matcher(otherProps).replaceAll(Matcher.quoteReplacement("/"));
InputStream is = findInputStream(otherProps);
if (is != null) {
logger.debug("loading properties from " + otherProps);
logger.debug("loading properties from {}", otherProps);
load(p, is);
} else {
logger.error("load.properties " + otherProps + " not found.");
@@ -135,12 +139,12 @@ final class PropertyMapLoader {
File f = new File(fileName);
if (f.exists()) {
logger.debug(fileName + " found in file system");
logger.debug("{} found in file system", fileName);
return new FileInputStream(f);
} else {
InputStream in = findInClassPath(fileName);
if (in != null) {
logger.debug(fileName + " found in classpath");
logger.debug("{} found in classpath", fileName);
}
return in;
}
@@ -1,27 +1,24 @@
package io.ebean.config;
/**
* Matches the functionality of javax.persistence.AttributeConverter
* <p>
* In general AttributeConverter should be used in preference to this
* ScalarTypeConverter as it is JPA standard and offers the same functionality.
* </p>
* <p>
* For Ebean we will look to deprecate this interface in preference to AttributeConverter.
* </p>
* <p>
* Used to convert between a value object and a known scalar type. The value
* object is the logical type used in your application and the scalar type is
* the value used to persist than to the DB.
* </p>
* <p>
* The Value object should be immutable and scalar (aka not compound) and
* converts to and from a known scalar type which Ebean will use to persist the
* value.
* </p>
* <p>
* This is an easier alternative to implementing the
* io.ebean.server.type.ScalarType interface.
* </p>
* <p>
* Note that Ebean will automatically try to detect Immutable Scalar Value
* Objects and automatically support them via reflection. This however would not
* be appropriate when the logical type is different from the type you wish to
* use for persistence - for example, if the logical type was long and you
* wanted to use java.sql.Timestamp for persistence. In this case you would want
* to implement this interface rather than let Ebean automatically support that
* type via reflection.
* </p>
*
* @param <B> The value object type.
* @param <S> The scalar object type that is used to persist the value object.
+42 -36
View File
@@ -12,6 +12,7 @@ import io.ebean.cache.ServerCachePlugin;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbEncrypt;
import io.ebean.config.dbplatform.DbType;
import io.ebean.dbmigration.DbOffline;
import io.ebean.dbmigration.MigrationRunner;
import io.ebean.event.BeanFindController;
import io.ebean.event.BeanPersistController;
@@ -27,6 +28,7 @@ import io.ebean.event.changelog.ChangeLogRegister;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.meta.MetaInfoManager;
import java.util.regex.Pattern;
import org.avaje.datasource.DataSourceConfig;
import javax.sql.DataSource;
@@ -75,17 +77,13 @@ import java.util.ServiceLoader;
*/
public class ServerConfig {
private static final Pattern NAMES_SPLIT = Pattern.compile("[ ,;]");
/**
* The EbeanServer name.
*/
private String name = "db";
/**
* When false (default) H2 automatically uses DDL generate and run
* (i.e. assumes we are running tests using in memory h2).
*/
private boolean h2ProductionMode;
/**
* Typically configuration type objects that are passed by this ServerConfig
* to plugins. For example - IgniteConfiguration passed to Ignite plugin.
@@ -319,6 +317,8 @@ public class ServerConfig {
*/
private String databaseBooleanFalse;
private boolean allQuotedIdentifiers;
/**
* The naming convention.
*/
@@ -527,27 +527,6 @@ public class ServerConfig {
this.name = name;
}
/**
* Return true if H2 should be used in production mode.
* <p>
* Otherwise it is assumed we are using H2 for testing and DDL generate and run is turned on.
* </p>
*/
public boolean isH2ProductionMode() {
return h2ProductionMode;
}
/**
* Set to true for H2 to be used in production mode.
* <p>
* Do this when we want to use H2 and not have the DDL generation and run automatically turned on.
* Otherwise it is assumed we are using H2 for testing purposes.
* </p>
*/
public void setH2ProductionMode(boolean h2ProductionMode) {
this.h2ProductionMode = h2ProductionMode;
}
/**
* Return the container / clustering configuration.
* <p/>
@@ -1301,6 +1280,24 @@ public class ServerConfig {
this.namingConvention = namingConvention;
}
/**
* Return true if all DB column and table names should use quoted identifiers.
*/
public boolean isAllQuotedIdentifiers() {
return allQuotedIdentifiers;
}
/**
* Set to true if all DB column and table names should use quoted identifiers.
*/
public void setAllQuotedIdentifiers(boolean allQuotedIdentifiers) {
this.allQuotedIdentifiers = allQuotedIdentifiers;
if (allQuotedIdentifiers && namingConvention instanceof UnderscoreNamingConvention) {
// we need to use matching naming convention
this.namingConvention = new MatchingNamingConvention();
}
}
/**
* Return true if this EbeanServer is a Document store only instance (has no JDBC DB).
*/
@@ -1567,11 +1564,6 @@ public class ServerConfig {
*/
public void setDatabasePlatform(DatabasePlatform databasePlatform) {
this.databasePlatform = databasePlatform;
if (!h2ProductionMode && databasePlatform != null && databasePlatform.isPlatform(Platform.H2)) {
// we are using H2 to run tests so turn on DDL generation and run
this.ddlGenerate = true;
this.ddlRun = true;
}
}
/**
@@ -1641,6 +1633,16 @@ public class ServerConfig {
this.encryptor = encryptor;
}
/**
* Return true if the instance is coming up in offline mode.
* <p>
* Offline mode is mostly used when generating DB migration.
* </p>
*/
public boolean isOfflineMode() {
return dbOffline || DbOffline.isSet();
}
/**
* Return true if the EbeanServer instance should be created in offline mode.
*/
@@ -2504,6 +2506,11 @@ public class ServerConfig {
migrationConfig.loadSettings(p, name);
boolean quotedIdentifiers = p.getBoolean("allQuotedIdentifiers", allQuotedIdentifiers);
if (quotedIdentifiers != allQuotedIdentifiers) {
// potentially also set to use matching naming convention
setAllQuotedIdentifiers(quotedIdentifiers);
}
namingConvention = createNamingConvention(p, namingConvention);
if (namingConvention != null) {
namingConvention.loadFromProperties(p);
@@ -2610,7 +2617,6 @@ public class ServerConfig {
jsonDateTime = JsonConfig.DateTime.MILLIS;
}
h2ProductionMode = p.getBoolean("h2ProductionMode", h2ProductionMode);
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
ddlRun = p.getBoolean("ddl.run", ddlRun);
ddlCreateOnly = p.getBoolean("ddl.createOnly", ddlCreateOnly);
@@ -2641,7 +2647,7 @@ public class ServerConfig {
List<Class<?>> classes = new ArrayList<>();
String[] split = classNames.split("[ ,;]");
String[] split = NAMES_SPLIT.split(classNames);
for (String aSplit : split) {
String cn = aSplit.trim();
if (!cn.isEmpty() && !"class".equalsIgnoreCase(cn)) {
@@ -2662,7 +2668,7 @@ public class ServerConfig {
if (searchPackages != null) {
String[] entries = searchPackages.split("[ ,;]");
String[] entries = NAMES_SPLIT.split(searchPackages);
for (String entry : entries) {
hitList.add(entry.trim());
}
@@ -2755,7 +2761,7 @@ public class ServerConfig {
*/
public DataSource runDbMigration(DataSource dataSource) {
if (migrationConfig.isRunMigration()) {
MigrationRunner runner = migrationConfig.createRunner(getClassLoadConfig().getClassLoader());
MigrationRunner runner = migrationConfig.createRunner(getClassLoadConfig().getClassLoader(), properties);
runner.run(dataSource);
}
return dataSource;
@@ -64,6 +64,11 @@ public class DatabasePlatform {
*/
protected String closeQuote = "\"";
/**
* When set to true all db column names and table names use quoted identifiers.
*/
protected boolean allQuotedIdentifiers;
/**
* For limit/offset, row_number etc limiting of SQL queries.
*/
@@ -175,6 +180,8 @@ public class DatabasePlatform {
protected SqlExceptionTranslator exceptionTranslator = new SqlCodeTranslator();
protected char[] specialLikeCharacters = { '%', '_' };
/**
* Instantiates a new database platform.
*/
@@ -191,7 +198,8 @@ public class DatabasePlatform {
/**
* Configure UUID Storage etc based on ServerConfig settings.
*/
public void configure(DbTypeConfig config) {
public void configure(DbTypeConfig config, boolean allQuotedIdentifiers) {
this.allQuotedIdentifiers = allQuotedIdentifiers;
addGeoTypes(config.getGeometrySRID());
configureIdType(config.getIdType());
dbTypeMap.config(nativeUuidType, config.getDbUuid());
@@ -533,24 +541,33 @@ public class DatabasePlatform {
* naming rules.
* </p>
*
* @param dbName the db name
* @return the string
* @param dbName the db table or column name
* @return the db table or column name with potentially platform specific quoted identifiers
*/
public String convertQuotedIdentifiers(String dbName) {
// Ignore null values e.g. schema name or catalog
if (dbName != null && !dbName.isEmpty()) {
if (dbName.charAt(0) == BACK_TICK) {
if (dbName.charAt(dbName.length() - 1) == BACK_TICK) {
String quotedName = getOpenQuote();
quotedName += dbName.substring(1, dbName.length() - 1);
quotedName += getCloseQuote();
return quotedName;
return openQuote + dbName.substring(1, dbName.length() - 1) + closeQuote;
} else {
logger.error("Missing backquote on [" + dbName + "]");
}
} else if (allQuotedIdentifiers) {
return openQuote + dbName + closeQuote;
}
}
return dbName;
}
/**
* Remove quoted identifier quotes from the table or column name if present.
*/
public String unQuote(String dbName) {
if (dbName != null && !dbName.isEmpty()) {
if (dbName.startsWith(openQuote)) {
// trim off the open and close quotes
return dbName.substring(1, dbName.length()-1);
}
}
return dbName;
@@ -617,4 +634,37 @@ public class DatabasePlatform {
logger.error("Error closing resultSet", e);
}
}
/**
* Escapes the like string for this DB-Platform
*/
public String escapeLikeString(String value) {
StringBuilder sb = null;
for (int i = 0; i < value.length(); i++) {
char ch = value.charAt(i);
boolean escaped = false;
for (char escapeChar: specialLikeCharacters) {
if (ch == escapeChar) {
if (sb == null) {
sb = new StringBuilder(value.substring(0, i));
}
escapeLikeCharacter(escapeChar, sb);
escaped = true;
break;
}
}
if (!escaped && sb != null) {
sb.append(ch);
}
}
if (sb == null) {
return value;
} else {
return sb.toString();
}
}
protected void escapeLikeCharacter(char ch, StringBuilder sb) {
sb.append('\\').append(ch);
}
}
@@ -1,12 +1,16 @@
package io.ebean.config.dbplatform;
import io.ebean.dbmigration.migration.IdentityType;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Defines the identity/sequence behaviour for the database.
*/
public class DbIdentity {
private static final Pattern TABLE_REPLACE = Pattern.compile("{table}", Pattern.LITERAL);
/**
* Set if this DB supports sequences. Note some DB's support both Sequences
* and Identity.
@@ -50,7 +54,8 @@ public class DbIdentity {
if (selectLastInsertedIdTemplate == null) {
return null;
}
return selectLastInsertedIdTemplate.replace("{table}", table);
return TABLE_REPLACE.matcher(selectLastInsertedIdTemplate).replaceAll(Matcher.quoteReplacement(table));
}
/**
@@ -130,7 +130,9 @@ public class DbPlatformType implements ExtraDbTypes {
if (canHaveLength || !strict) {
// see if there is a precision/scale to add (or not)
int len = deployLength != 0 ? deployLength : defaultLength;
if (len > 0) {
if (len == Integer.MAX_VALUE) {
sb.append("(max)"); // TODO: this is sqlserver specific
} else if (len > 0) {
sb.append("(");
sb.append(len);
int scale = deployScale != 0 ? deployScale : defaultScale;
@@ -30,7 +30,8 @@ class DbPlatformTypeParser {
} else {
String type = columnDefinition.substring(0, openPos);
int scale = Integer.parseInt(columnDefinition.substring(openPos + 1, closePos));
String strScale = columnDefinition.substring(openPos + 1, closePos);
int scale = strScale.equalsIgnoreCase("max") ? Integer.MAX_VALUE : Integer.parseInt(strScale);
return new DbPlatformType(type, scale);
}
} catch (RuntimeException e) {
@@ -1,6 +1,7 @@
package io.ebean.config.dbplatform.db2;
import io.ebean.BackgroundExecutor;
import io.ebean.PersistBatch;
import io.ebean.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
@@ -30,15 +31,21 @@ public class DB2Platform extends DatabasePlatform {
this.exceptionTranslator =
new SqlErrorCodes()
//.addAcquireLock("")
.addDuplicateKey("-803")
.addDataIntegrity("-407","-530","-531","-532","-543","-544","-545","-603","-667")
.addAcquireLock("40001","57033") // key -911/-913
.addDuplicateKey("23505") // -803
// .addDataIntegrity("-407","-530","-531","-532","-543","-544","-545","-603","-667")
// we need SQLState, not code: https://www.ibm.com/support/knowledgecenter/en/SSEPEK_10.0.0/codes/src/tpc/db2z_n.html
.addDataIntegrity("23502","23503","23504","23511","23512","23511","42917","23515")
.build();
booleanDbType = Types.BOOLEAN;
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint", false));
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
dbTypeMap.put(DbType.BIGINT, new DbPlatformType("bigint", false));
dbTypeMap.put(DbType.REAL, new DbPlatformType("real"));
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint"));
dbTypeMap.put(DbType.DECIMAL, new DbPlatformType("decimal", 15));
persistBatchOnCascade = PersistBatch.NONE;
}
/**
@@ -49,7 +49,7 @@ public class H2HistoryTrigger implements Trigger {
// build the insert into history table SQL
StringBuilder insertSql = new StringBuilder(150);
insertSql.append("insert into ").append(tableName).append(HISTORY_SUFFIX).append(" (");
insertSql.append("insert into ").append(schemaName).append(".").append(tableName).append(HISTORY_SUFFIX).append(" (");
int count = 0;
while (rs.next()) {
@@ -30,7 +30,7 @@ public class H2Platform extends DatabasePlatform {
this.exceptionTranslator =
new SqlErrorCodes()
.addAcquireLock("50200")
.addAcquireLock("50200","HYT00")
.addDuplicateKey("23001","23505")
.addDataIntegrity("22001","22003","22012","22018","22025","23000","23002","23003","23502","23503","23506","23507","23513")
.build();
@@ -40,10 +40,6 @@ public class H2Platform extends DatabasePlatform {
this.dbIdentity.setSupportsSequence(true);
this.dbIdentity.setSupportsIdentity(true);
// like ? escape'' not working in the latest version H2 so just using no
// escape clause for now noting that backslash is an escape char for like in H2
this.likeClause = "like ?";
dbTypeMap.put(DbType.UUID, new DbPlatformType("uuid", false));
}
@@ -30,7 +30,6 @@ public class MySqlPlatform extends DatabasePlatform {
super();
this.platform = Platform.MYSQL;
this.useExtraTransactionOnIterateSecondaryQueries = true;
this.likeClause = "like ? escape''";
this.selectCountWithAlias = true;
this.dbEncrypt = new MySqlDbEncrypt();
this.platformDdl = new MySqlDdl(this);
@@ -46,7 +45,7 @@ public class MySqlPlatform extends DatabasePlatform {
new SqlErrorCodes()
.addAcquireLock("1205")
.addDuplicateKey("1062")
.addDataIntegrity("630","839","840","893","1169","1215","1216","1217","1364","1451","1452","1557")
.addDataIntegrity("630","839","840","893","1169","1215","1216","1217","1364","1451","1452","1557","23000")
.build();
this.openQuote = "`";
@@ -28,7 +28,6 @@ public class PostgresPlatform extends DatabasePlatform {
super();
this.platform = Platform.POSTGRES;
this.supportsNativeIlike = true;
this.likeClause = "like ? escape''";
this.selectCountWithAlias = true;
this.blobDbType = Types.LONGVARBINARY;
this.clobDbType = Types.VARCHAR;
@@ -8,7 +8,6 @@ import io.ebean.config.dbplatform.DbType;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.SqlErrorCodes;
import io.ebean.dbmigration.ddlgeneration.platform.SqlServerDdl;
import java.sql.Types;
/**
@@ -35,12 +34,14 @@ public class SqlServerPlatform extends DatabasePlatform {
this.exceptionTranslator =
new SqlErrorCodes()
.addAcquireLock("1222")
.addDuplicateKey("2601","2627")
.addDataIntegrity("544","8114","8115")
.addDuplicateKey("2601", "2627")
.addDataIntegrity("544", "8114", "8115")
.build();
this.openQuote = "[";
this.closeQuote = "]";
this.specialLikeCharacters = new char[]{'%', '_', '['};
this.likeClause = "like ? COLLATE Latin1_General_BIN";
booleanDbType = Types.INTEGER;
dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("bit default 0"));
@@ -61,6 +62,13 @@ public class SqlServerPlatform extends DatabasePlatform {
dbTypeMap.put(DbType.TIME, new DbPlatformType("time"));
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime2"));
dbTypeMap.put(DbType.JSON, new DbPlatformType("nvarchar", Integer.MAX_VALUE));
dbTypeMap.put(DbType.JSONB, new DbPlatformType("nvarchar", Integer.MAX_VALUE));
}
@Override
protected void escapeLikeCharacter(char ch, StringBuilder sb) {
sb.append('[').append(ch).append(']');
}
}
@@ -374,7 +374,7 @@ public class DbMigration {
// writer needs the current model to provide table/column details for
// history ddl generation (triggers, history tables etc)
DdlWrite write = new DdlWrite(new MConfiguration(), request.current);
PlatformDdlWriter writer = createDdlWriter(databasePlatform, "");
PlatformDdlWriter writer = createDdlWriter(databasePlatform);
writer.processMigration(dbMigration, write, request.migrationDir, fullVersion);
}
}
@@ -431,7 +431,7 @@ public class DbMigration {
for (Pair pair : platforms) {
DdlWrite platformBuffer = new DdlWrite(new MConfiguration(), currentModel.read());
PlatformDdlWriter platformWriter = createDdlWriter(pair);
PlatformDdlWriter platformWriter = createDdlWriter(pair.platform);
File subPath = platformWriter.subPath(writePath, pair.prefix);
platformWriter.processMigration(dbMigration, platformBuffer, subPath, fullVersion);
@@ -439,12 +439,8 @@ public class DbMigration {
}
}
private PlatformDdlWriter createDdlWriter(Pair pair) {
return createDdlWriter(pair.platform, pair.prefix);
}
private PlatformDdlWriter createDdlWriter(DatabasePlatform platform, String prefix) {
return new PlatformDdlWriter(platform, serverConfig, prefix, migrationConfig);
private PlatformDdlWriter createDdlWriter(DatabasePlatform platform) {
return new PlatformDdlWriter(platform, serverConfig, migrationConfig);
}
/**
@@ -54,6 +54,8 @@ public class BaseTableDdl implements TableDdl {
* Used when unique constraints specifically for OneToOne can't be created normally (MsSqlServer).
*/
protected List<Column> externalUnique = new ArrayList<>();
protected List<UniqueConstraint> externalCompoundUnique = new ArrayList<>();
// counters used when constraint names are truncated due to maximum length
// and these counters are used to keep the constraint name unique
@@ -85,6 +87,7 @@ public class BaseTableDdl implements TableDdl {
protected void reset() {
indexSet.clear();
externalUnique.clear();
externalCompoundUnique.clear();
countCheck = 0;
countUnique = 0;
countForeignKey = 0;
@@ -204,9 +207,24 @@ public class BaseTableDdl implements TableDdl {
String tableName = createTable.getName();
for (Column col : externalUnique) {
String uqName = col.getUniqueOneToOne();
if (uqName == null) {
uqName = col.getUnique();
}
String[] columnNames = {col.getName()};
write.apply()
.append(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames))
.append(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, Boolean.TRUE.equals(col.isNotnull())))
.endOfStatement();
write.dropAllForeignKeys()
.append(platformDdl.dropIndex(uqName, tableName))
.endOfStatement();
}
for (UniqueConstraint constraint : externalCompoundUnique) {
String uqName = constraint.getName();
String[] columnNames = StringHelper.delimitedToArray(constraint.getColumnNames(), ",", false);
write.apply()
.append(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, false)) // TODO: check if nullable
.endOfStatement();
write.dropAllForeignKeys()
@@ -406,12 +424,17 @@ public class BaseTableDdl implements TableDdl {
protected void writeCompoundUniqueConstraints(DdlBuffer apply, CreateTable createTable) throws IOException {
List<UniqueConstraint> uniqueConstraints = createTable.getUniqueConstraint();
boolean inlineUniqueWhenNull = platformDdl.isInlineUniqueWhenNullable();
for (UniqueConstraint uniqueConstraint : uniqueConstraints) {
String uqName = uniqueConstraint.getName();
String[] columns = toColumnNamesSplit(uniqueConstraint.getColumnNames());
apply.append(",").newLine();
apply.append(" constraint ").append(uqName).append(" unique");
appendColumns(columns, apply);
if (inlineUniqueWhenNull) {
String uqName = uniqueConstraint.getName();
String[] columns = toColumnNamesSplit(uniqueConstraint.getColumnNames());
apply.append(",").newLine();
apply.append(" constraint ").append(uqName).append(" unique");
appendColumns(columns, apply);
} else {
externalCompoundUnique.add(uniqueConstraint);
}
}
}
@@ -420,17 +443,18 @@ public class BaseTableDdl implements TableDdl {
*/
protected void writeUniqueConstraints(DdlBuffer apply, CreateTable createTable) throws IOException {
boolean inlineUniqueOneToOne = platformDdl.isInlineUniqueOneToOne();
boolean inlineUniqueWhenNullable = platformDdl.isInlineUniqueWhenNullable();
List<Column> columns = createTable.getColumn();
for (Column column : columns) {
if (hasValue(column.getUnique()) || (inlineUniqueOneToOne && hasValue(column.getUniqueOneToOne()))) {
// normal mechanism for adding unique constraint
inlineUniqueConstraintSingle(apply, column);
} else if (!inlineUniqueOneToOne && hasValue(column.getUniqueOneToOne())) {
// MsSqlServer specific mechanism for adding unique constraints (that allow nulls)
externalUnique.add(column);
if (hasValue(column.getUnique()) || hasValue(column.getUniqueOneToOne())) {
if (Boolean.TRUE.equals(column.isNotnull()) || inlineUniqueWhenNullable) {
// normal mechanism for adding unique constraint
inlineUniqueConstraintSingle(apply, column);
} else {
// MsSqlServer & DB2 specific mechanism for adding unique constraints (that allow nulls)
externalUnique.add(column);
}
}
}
}
@@ -814,9 +838,9 @@ public class BaseTableDdl implements TableDdl {
protected void addUniqueConstraint(DdlWrite writer, AlterColumn alter, String uqName) throws IOException {
String[] cols = {alter.getColumnName()};
boolean notNull = alter.isNotnull() != null ? alter.isNotnull() : Boolean.TRUE.equals(alter.isNotnull());
writer.apply()
.append(platformDdl.alterTableAddUniqueConstraint(alter.getTableName(), uqName, cols))
.append(platformDdl.alterTableAddUniqueConstraint(alter.getTableName(), uqName, cols, notNull))
.endOfStatement();
writer.dropAllForeignKeys()
@@ -832,21 +856,11 @@ public class BaseTableDdl implements TableDdl {
}
protected void alterTableAddColumn(DdlBuffer buffer, String tableName, Column column, boolean onHistoryTable) throws IOException {
String convertedType = platformDdl.convert(column.getType(), false);
buffer.append("alter table ").append(tableName)
.append(" add column ").append(column.getName())
.append(" ").append(convertedType);
if (!onHistoryTable) {
if (isTrue(column.isNotnull())) {
buffer.append(" not null");
}
if (hasValue(column.getCheckConstraint())) {
buffer.append(" ").append(column.getCheckConstraint());
}
String ddl = platformDdl.alterTableAddColumn(tableName, column, onHistoryTable);
if (hasValue(ddl)) {
buffer.append(ddl);
buffer.endOfStatement();
}
buffer.endOfStatement();
}
protected boolean isFalse(Boolean value) {
@@ -9,7 +9,21 @@ public class DB2Ddl extends PlatformDdl {
public DB2Ddl(DatabasePlatform platform) {
super(platform);
this.dropTableIfExists = "drop table ";
this.dropSequenceIfExists = "drop sequence ";
this.dropConstraintIfExists = "drop constraint";
this.dropIndexIfExists = "drop index ";
this.identitySuffix = " generated by default as identity";
this.inlineUniqueWhenNullable = false;
}
public String alterTableAddUniqueConstraint(String tableName, String uqName, String[] columns, boolean notNull) {
if (notNull) {
return super.alterTableAddUniqueConstraint(tableName, uqName, columns, true);
} else {
// Hmm: Complex workaround: https://www.ibm.com/developerworks/mydeveloperworks/blogs/SQLTips4DB2LUW/entry/unique_where_not_null_indexes26?lang=en
return "-- NOT SUPPORTED " + super.alterTableAddUniqueConstraint(tableName, uqName, columns, true);
}
}
}
@@ -12,4 +12,8 @@ public class H2Ddl extends PlatformDdl {
this.historyDdl = new H2HistoryDdl();
}
@Override
protected String convertArrayType(String logicalArrayType) {
return "array";
}
}
@@ -101,4 +101,11 @@ public class MySqlDdl extends PlatformDdl {
apply.append(" comment='").append(tableComment).append("'");
}
/**
* Add table comment as a separate statement (from the create table statement).
*/
@Override
public void addTableComment(DdlBuffer apply, String tableName, String tableComment) throws IOException {
apply.append(String.format("alter table %s comment = '%s'", tableName, tableComment)).endOfStatement();
}
}
@@ -17,6 +17,7 @@ import io.ebean.dbmigration.migration.Column;
import io.ebean.dbmigration.migration.DropHistoryTable;
import io.ebean.dbmigration.migration.IdentityType;
import io.ebean.dbmigration.model.MTable;
import io.ebean.util.StringHelper;
import java.io.IOException;
import java.util.List;
@@ -74,6 +75,8 @@ public class PlatformDdl {
protected String dropUniqueConstraint = "drop constraint";
protected String addConstraint = "add constraint";
protected String addColumn = "add column";
protected String columnSetType = "";
@@ -88,7 +91,7 @@ public class PlatformDdl {
/**
* Set false for MsSqlServer to allow multiple nulls for OneToOne mapping.
*/
protected boolean inlineUniqueOneToOne = true;
protected boolean inlineUniqueWhenNullable = true;
protected DbConstraintNaming naming;
@@ -99,6 +102,8 @@ public class PlatformDdl {
protected final DbDefaultValue dbDefaultValue;
protected String fallbackArrayType = "varchar(1000)";
public PlatformDdl(DatabasePlatform platform) {
this.platform = platform;
this.dbIdentity = platform.getDbIdentity();
@@ -176,7 +181,7 @@ public class PlatformDdl {
buffer.append(" ");
buffer.append(lowerColumnName(column.getName()), 29);
buffer.append(platformType);
if (!typeContainsDefault(platformType)) {
if (!Boolean.TRUE.equals(column.isPrimaryKey()) && !typeContainsDefault(platformType)) {
String defaultValue = convertDefaultValue(column.getDefaultValue());
if (defaultValue != null) {
buffer.append(" default ").append(defaultValue);
@@ -215,10 +220,24 @@ public class PlatformDdl {
* Convert the standard type to the platform specific type.
*/
public String convert(String type, boolean identity) {
if (type.contains("[]")) {
return convertArrayType(type);
}
String platformType = typeConverter.convert(type);
return identity ? asIdentityColumn(platformType) : platformType;
}
/**
* Convert the logical array type to a db platform specific type to support the array data.
*/
protected String convertArrayType(String logicalArrayType) {
if (logicalArrayType.endsWith("]")) {
return fallbackArrayType;
}
int colonPos = logicalArrayType.lastIndexOf(']');
return "varchar" + logicalArrayType.substring(colonPos + 1);
}
/**
* Add history support to this table using the platform specific mechanism.
*/
@@ -352,20 +371,42 @@ public class PlatformDdl {
* <p>
* Overridden by MsSqlServer for specific null handling on unique constraints.
*/
public String alterTableAddUniqueConstraint(String tableName, String uqName, String[] columns) {
public String alterTableAddUniqueConstraint(String tableName, String uqName, String[] columns, boolean notNull) {
StringBuilder buffer = new StringBuilder(90);
buffer.append("alter table ").append(tableName).append(" add constraint ").append(uqName).append(" unique ");
appendColumns(columns, buffer);
return buffer.toString();
}
public String alterTableAddColumn(String tableName, Column column, boolean onHistoryTable) throws IOException {
String convertedType = convert(column.getType(), false);
StringBuilder buffer = new StringBuilder(90);
buffer.append("alter table ").append(tableName)
.append(' ').append(addColumn).append(' ').append(column.getName())
.append(' ').append(convertedType);
if (!onHistoryTable) {
if (isTrue(column.isNotnull())) {
buffer.append(" not null");
}
if (!StringHelper.isNull(column.getCheckConstraint())) {
buffer.append(" constraint ").append(column.getCheckConstraintName());
buffer.append(" ").append(column.getCheckConstraint());
}
}
return buffer.toString();
}
/**
* Return true if unique constraints for OneToOne can be inlined as normal.
* Returns false for MsSqlServer due to it's null handling for unique constraints.
* Return true if unique constraints for nullable columns can be inlined as normal.
* Returns false for MsSqlServer & DB2 due to it's not possible to to put a constraint
* on a nullable column
*/
public boolean isInlineUniqueOneToOne() {
return inlineUniqueOneToOne;
public boolean isInlineUniqueWhenNullable() {
return inlineUniqueWhenNullable;
}
/**
@@ -16,6 +16,17 @@ public class PostgresDdl extends PlatformDdl {
this.columnSetNull = "drop not null";
}
@Override
protected String convertArrayType(String logicalArrayType) {
int colonPos = logicalArrayType.lastIndexOf(']');
if (colonPos == -1) {
return logicalArrayType;
} else {
// trim of the fallback varchar length
return logicalArrayType.substring(0, colonPos + 1);
}
}
/**
* Map bigint, integer and smallint into their equivalent serial types.
*/
@@ -15,7 +15,9 @@ public class SqlServerDdl extends PlatformDdl {
super(platform);
this.identitySuffix = " identity(1,1)";
this.foreignKeyRestrict = "";
this.inlineUniqueOneToOne = false;
this.alterTableIfExists = "";
this.addColumn = "add";
this.inlineUniqueWhenNullable = false;
this.columnSetDefault = "add default";
this.dropConstraintIfExists = "drop constraint";
this.historyDdl = new SqlServerHistoryDdl();
@@ -28,15 +30,34 @@ public class SqlServerDdl extends PlatformDdl {
@Override
public String alterTableDropForeignKey(String tableName, String fkName) {
return "IF OBJECT_ID('" + fkName + "', 'F') IS NOT NULL " + super.alterTableDropForeignKey(tableName, fkName);
int pos = tableName.lastIndexOf('.');
String objectId = fkName;
if (pos != -1) {
objectId = tableName.substring(0, pos + 1) + fkName;
}
return "IF OBJECT_ID('" + objectId + "', 'F') IS NOT NULL " + super.alterTableDropForeignKey(tableName, fkName);
}
@Override
public String dropSequence(String sequenceName) {
return "IF OBJECT_ID('" + sequenceName + "', 'SO') IS NOT NULL drop sequence " + sequenceName;
}
@Override
public String dropIndex(String indexName, String tableName) {
return "IF EXISTS (SELECT name FROM sys.indexes WHERE object_id = OBJECT_ID('" + tableName +"','U') AND name = '" + indexName + "') drop index " + indexName + " ON " + tableName;
}
/**
* MsSqlServer specific null handling on unique constraints.
*/
@Override
public String alterTableAddUniqueConstraint(String tableName, String uqName, String[] columns) {
public String alterTableAddUniqueConstraint(String tableName, String uqName, String[] columns, boolean notNull) {
if (notNull) {
return super.alterTableAddUniqueConstraint(tableName, uqName, columns, notNull);
}
if (uqName == null) {
throw new NullPointerException();
}
// issues#233
String start = "create unique nonclustered index " + uqName + " on " + tableName + "(";
StringBuilder sb = new StringBuilder(start);
@@ -48,12 +69,37 @@ public class SqlServerDdl extends PlatformDdl {
sb.append(columns[i]);
}
sb.append(") where");
String sep = " ";
for (String column : columns) {
sb.append(" ").append(column).append(" is not null");
sb.append(sep).append(column).append(" is not null");
sep = " and ";
}
return sb.toString();
}
/**
* Generate and return the create sequence DDL.
*/
@Override
public String createSequence(String sequenceName, int initialValue, int allocationSize) {
StringBuilder sb = new StringBuilder("create sequence ");
sb.append(sequenceName);
sb.append(" as bigint ");
if (initialValue > 1) {
sb.append(" start with ").append(initialValue);
} else {
sb.append(" start with 1 ");
}
if (allocationSize > 0 && allocationSize != 50) {
// at this stage ignoring allocationSize 50 as this is the 'default' and
// not consistent with the way Ebean batch fetches sequence values
sb.append(" increment by ").append(allocationSize);
}
sb.append(";");
return sb.toString();
}
@Override
public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) {
@@ -74,7 +120,7 @@ public class SqlServerDdl extends PlatformDdl {
boolean notnull = (alter.isNotnull() != null) ? alter.isNotnull() : Boolean.TRUE.equals(alter.isCurrentNotnull());
String notnullClause = notnull ? " not null" : "";
return "alter table " + tableName + " alter column " + columnName + " " + type + notnullClause;
return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + type + notnullClause;
}
@Override
@@ -33,17 +33,26 @@ public class SqlServerHistoryDdl implements PlatformHistoryDdl {
DdlBuffer apply = writer.applyHistory();
apply.append("alter table ").append(baseTable).newLine()
.append(" add ").append(systemPeriodStart).append(" datetime2 GENERATED ALWAYS AS ROW START NOT NULL DEFAULT SYSUTCDATETIME(),").newLine()
.append(" ").append(systemPeriodEnd).append(" datetime2 GENERATED ALWAYS AS ROW END NOT NULL,").newLine()
.append("period for system_time (").append(systemPeriodStart).append("From, ").append(systemPeriodEnd).append("To)").endOfStatement();
.append(" ").append(systemPeriodEnd).append(" datetime2 GENERATED ALWAYS AS ROW END NOT NULL DEFAULT '9999-12-31T23:59:59.9999999',").newLine()
.append("period for system_time (").append(systemPeriodStart).append(", ").append(systemPeriodEnd).append(")").endOfStatement();
apply.append("alter table ").append(baseTable).append("set (system_versioning = on)").endOfStatement();
String historyTable = baseTable + "_history"; // history must contain schema, otherwise you'll get
// Setting SYSTEM_VERSIONING to ON failed because history table 'xxx_history' is not specified in two-part name format.
if (historyTable.indexOf('.') == -1) {
historyTable = "dbo." +historyTable; // so add the default schema, if none was specified.
}
apply.append("alter table ").append(baseTable).append(" set (system_versioning = on (history_table=").append(historyTable).append("))").endOfStatement();
DdlBuffer drop = writer.dropAll();
drop.append("IF OBJECT_ID('").append(baseTable).append("', 'U') IS NOT NULL alter table ").append(baseTable).append(" set (system_versioning = off)").endOfStatement();
drop.append("IF OBJECT_ID('").append(baseTable).append("_history', 'U') IS NOT NULL drop table ").append(baseTable).append("_history").endOfStatement();
}
@Override
public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException {
String baseTable = dropHistoryTable.getBaseTable();
DdlBuffer apply = writer.applyHistory();
apply.append("alter table ").append(baseTable).append("set (system_versioning = off)").endOfStatement();
apply.append("alter table ").append(baseTable).append(" set (system_versioning = off)").endOfStatement();
apply.append("alter table ").append(baseTable).append(" drop column ").append(systemPeriodStart).endOfStatement();
apply.append("alter table ").append(baseTable).append(" drop column ").append(systemPeriodEnd).endOfStatement();
}
@@ -1,12 +1,15 @@
package io.ebean.dbmigration.model;
import java.util.Arrays;
import java.util.regex.Pattern;
/**
* The version of a migration used so that migrations are processed in order.
*/
public class MigrationVersion implements Comparable<MigrationVersion> {
private static final Pattern SECTION_SPLITTER = Pattern.compile("[\\.-]");
private static final int[] REPEAT_ORDERING = {Integer.MAX_VALUE};
private static final boolean[] REPEAT_UNDERSCORES = {false};
@@ -169,7 +172,7 @@ public class MigrationVersion implements Comparable<MigrationVersion> {
value = value.replace('_', '.');
String[] sections = value.split("[\\.-]");
String[] sections = SECTION_SPLITTER.split(value);
if ("r".equalsIgnoreCase(sections[0])) {
// a "repeatable" version (does not have a version number)
@@ -29,14 +29,11 @@ public class PlatformDdlWriter {
private final DatabasePlatform platform;
private final String platformPrefix;
private final DbMigrationConfig config;
public PlatformDdlWriter(DatabasePlatform platform, ServerConfig serverConfig, String platformPrefix, DbMigrationConfig config) {
public PlatformDdlWriter(DatabasePlatform platform, ServerConfig serverConfig, DbMigrationConfig config) {
this.platform = platform;
this.serverConfig = serverConfig;
this.platformPrefix = platformPrefix;
this.config = config;
}
@@ -53,10 +53,12 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
if (inheritInfo != null && inheritInfo.isRoot()) {
// add the discriminator column
String discColumn = inheritInfo.getDiscriminatorColumn();
DbPlatformType dbType = ctx.getDbTypeMap().get(inheritInfo.getDiscriminatorType());
String discDbType = dbType.renderType(inheritInfo.getDiscriminatorLength(), 0);
table.addColumn(new MColumn(discColumn, discDbType, true));
String columnDefn = inheritInfo.getColumnDefn();
if (columnDefn == null || columnDefn.isEmpty()) {
DbPlatformType dbType = ctx.getDbTypeMap().get(inheritInfo.getDiscriminatorType());
columnDefn = dbType.renderType(inheritInfo.getColumnLength(), 0);
}
table.addColumn(new MColumn(discColumn, columnDefn, true));
}
return new ModelBuildPropertyVisitor(ctx, table, descriptor);
@@ -221,7 +221,6 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
// using non-strict mode to render the DB type such that we have a
// "logical" type like jsonb(200) that can map to JSONB or VARCHAR(200)
MColumn col = new MColumn(p.getDbColumn(), ctx.getColumnDefn(p, false));
col.setDefaultValue(p.getDbColumnDefault());
col.setComment(p.getDbComment());
col.setDraftOnly(p.isDraftOnly());
col.setHistoryExclude(p.isExcludedFromHistory());
@@ -231,8 +230,11 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
if (p.getBeanDescriptor().isUseIdGenerator()) {
col.setIdentity(true);
}
} else if (!p.isNullable() || p.isDDLNotNull()) {
col.setNotnull(true);
} else {
col.setDefaultValue(p.getDbColumnDefault());
if (!p.isNullable() || p.isDDLNotNull()) {
col.setNotnull(true);
}
}
if (p.isUnique() && !p.isId()) {
@@ -27,16 +27,17 @@ public final class TimeStringParser implements StringParser {
}
String s = value.trim();
int minute;
int second;
int firstColon = s.indexOf(':');
int secondColon = s.indexOf(':', firstColon + 1);
if (firstColon == -1) {
throw new java.lang.IllegalArgumentException("No ':' in value [" + s + "]");
}
try {
int second;
int minute;
int hour = Integer.parseInt(s.substring(0, firstColon));
int secondColon = s.indexOf(':', firstColon + 1);
if (secondColon == -1) {
minute = Integer.parseInt(s.substring(firstColon + 1, s.length()));
second = 0;
+6 -1
View File
@@ -158,7 +158,12 @@ public class EJson {
if (list == null) {
return null;
}
return ((ModifyAwareList) list).asSet();
if (modifyAware) {
return ((ModifyAwareList) list).asSet();
} else {
return new LinkedHashSet<>(list);
}
}
/**
@@ -12,7 +12,7 @@ public class CamelCaseHelper {
String[] vals = underscore.split("_");
if (vals.length == 1) {
return underscore;
return isUpperCase(underscore) ? underscore.toLowerCase() : underscore;
}
StringBuilder result = new StringBuilder();
@@ -29,4 +29,13 @@ public class CamelCaseHelper {
return result.toString();
}
private static boolean isUpperCase(String underscore) {
for (int i = 0; i < underscore.length(); i++) {
if (Character.isLowerCase(underscore.charAt(i))) {
return false;
}
}
return true;
}
}
@@ -430,6 +430,9 @@ public class StringHelper {
* }</pre>
*/
public static String replaceStringMulti(String source, String[] match, String replace) {
if (source == null) {
return null;
}
return replaceStringMulti(source, match, replace, 30, 0, source.length());
}
@@ -443,7 +446,9 @@ public class StringHelper {
*/
public static String replaceStringMulti(String source, String[] match, String replace,
int additionalSize, int startPos, int endPos) {
if (source == null) {
return null;
}
int shortestMatch = match[0].length();
char[] match0 = new char[match.length];
@@ -65,4 +65,9 @@ public interface SpiExpressionRequest {
* Append a DB Like clause.
*/
void appendLike();
/**
* Escapes a string to use it as exact match in Like clause.
*/
String escapeLikeString(String value);
}
@@ -161,6 +161,7 @@ public interface SpiQuery<T> extends Query<T> {
/**
* Return the ForUpdate mode.
*/
@Override
ForUpdate getForUpdateMode();
/**
@@ -1,5 +1,6 @@
package io.ebeaninternal.extraddl.model;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -15,6 +16,8 @@ public class ExtraDdlXmlReader {
private static final Logger logger = LoggerFactory.getLogger(ExtraDdlXmlReader.class);
private static final Pattern PLATFORM_REGEX_SPLIT = Pattern.compile("[,;]");
/**
* Return the combined extra DDL that should be run given the platform name.
*/
@@ -44,7 +47,8 @@ public class ExtraDdlXmlReader {
if (platforms == null || platforms.trim().isEmpty()) {
return true;
}
String[] names = platforms.split("[,;]");
String[] names = PLATFORM_REGEX_SPLIT.split(platforms);
for (String name : names) {
if (name.trim().toLowerCase().contains(platformName)) {
return true;
@@ -24,28 +24,28 @@ public class DefaultCacheAdapter implements ServerCacheManager {
@Override
public ServerCache getNaturalKeyCache(Class<?> beanType) {
return cacheManager.getNaturalKeyCache(beanType).get();
return cacheManager.getNaturalKeyCache(beanType);
}
@Override
public ServerCache getBeanCache(Class<?> beanType) {
return cacheManager.getBeanCache(beanType).get();
return cacheManager.getBeanCache(beanType);
}
@Override
public ServerCache getCollectionIdsCache(Class<?> beanType, String propertyName) {
return cacheManager.getCollectionIdsCache(beanType, propertyName).get();
return cacheManager.getCollectionIdsCache(beanType, propertyName);
}
@Override
public ServerCache getQueryCache(Class<?> beanType) {
return cacheManager.getQueryCache(beanType).get();
return cacheManager.getQueryCache(beanType);
}
@Override
public void clear(Class<?> beanType) {
cacheManager.getBeanCache(beanType).get().clear();
cacheManager.getQueryCache(beanType).get().clear();
cacheManager.getBeanCache(beanType).clear();
cacheManager.getQueryCache(beanType).clear();
}
@Override
@@ -9,7 +9,6 @@ import io.ebean.cache.ServerCacheType;
import io.ebean.config.CurrentTenantProvider;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
/**
* Manages the construction of caches.
@@ -39,12 +38,10 @@ class DefaultCacheHolder {
this.tenantProvider = tenantProvider;
}
Supplier<ServerCache> getCache(Class<?> beanType, String cacheKey, ServerCacheType type) {
ServerCache getCache(Class<?> beanType, String cacheKey, ServerCacheType type) {
return getCacheInternal(beanType, cacheKey, type);
if (tenantProvider == null) {
return new SimpleSupplier(getCacheInternal(beanType, cacheKey, type));
}
return new TenantSupplier(beanType, cacheKey, type);
}
private String key(String cacheKey, ServerCacheType type) {
@@ -62,7 +59,8 @@ class DefaultCacheHolder {
private ServerCache createCache(Class<?> beanType, ServerCacheType type, String key) {
ServerCacheOptions options = getCacheOptions(beanType, type);
return cacheFactory.createCache(type, key, options);
return cacheFactory.createCache(type, key, tenantProvider, options);
}
void clearAll() {
@@ -99,40 +97,4 @@ class DefaultCacheHolder {
return beanDefault.copy();
}
/**
* Multi-Tenant based cache supplier.
*/
private class TenantSupplier implements Supplier<ServerCache> {
final Class<?> beanType;
final String key;
final ServerCacheType type;
private TenantSupplier(Class<?> beanType, String key, ServerCacheType type) {
this.beanType = beanType;
this.key = key;
this.type = type;
}
@Override
public ServerCache get() {
String fullKey = key + "_" + tenantProvider.currentId();
return getCacheInternal(beanType, fullKey, type);
}
}
private static class SimpleSupplier implements Supplier<ServerCache> {
final ServerCache underlying;
private SimpleSupplier(ServerCache underlying) {
this.underlying = underlying;
}
@Override
public ServerCache get() {
return underlying;
}
}
}
@@ -4,6 +4,9 @@ import io.ebean.BackgroundExecutor;
import io.ebean.cache.ServerCache;
import io.ebean.cache.ServerCacheOptions;
import io.ebean.cache.ServerCacheStatistics;
import io.ebean.cache.TenantAwareKey;
import io.ebean.config.CurrentTenantProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -51,8 +54,6 @@ public class DefaultServerCache implements ServerCache {
protected final LongAdder evictCount = new LongAdder();
protected final LongAdder evictMicros = new LongAdder();
protected final Object monitor = new Object();
protected final String name;
protected int maxSize;
@@ -63,27 +64,30 @@ public class DefaultServerCache implements ServerCache {
protected int maxSecsToLive;
protected TenantAwareKey tenantAwareKey;
/**
* Construct using a ConcurrentHashMap and cache options.
*/
public DefaultServerCache(String name, ServerCacheOptions options) {
this(name, new ConcurrentHashMap<>(), options);
public DefaultServerCache(String name, CurrentTenantProvider tenantProvider, ServerCacheOptions options) {
this(name, new ConcurrentHashMap<>(), tenantProvider, options);
}
/**
* Construct passing in name, map and base eviction controls as ServerCacheOptions.
*/
public DefaultServerCache(String name, Map<Object, CacheEntry> map, ServerCacheOptions options) {
this(name, map, options.getMaxSize(), options.getMaxIdleSecs(), options.getMaxSecsToLive(), options.getTrimFrequency());
public DefaultServerCache(String name, Map<Object, CacheEntry> map, CurrentTenantProvider tenantProvider, ServerCacheOptions options) {
this(name, map, tenantProvider, options.getMaxSize(), options.getMaxIdleSecs(), options.getMaxSecsToLive(), options.getTrimFrequency());
}
/**
* Construct passing in name, map and base eviction controls.
*/
public DefaultServerCache(String name, Map<Object, CacheEntry> map, int maxSize, int maxIdleSecs, int maxSecsToLive, int trimFrequency) {
public DefaultServerCache(String name, Map<Object, CacheEntry> map, CurrentTenantProvider tenantProvider, int maxSize, int maxIdleSecs, int maxSecsToLive, int trimFrequency) {
this.name = name;
this.map = map;
this.maxSize = maxSize;
this.tenantAwareKey = new TenantAwareKey(tenantProvider);
this.maxIdleSecs = maxIdleSecs;
this.maxSecsToLive = maxSecsToLive;
this.trimFrequency = determineTrim(maxIdleSecs, maxSecsToLive, trimFrequency);
@@ -187,13 +191,20 @@ public class DefaultServerCache implements ServerCache {
map.clear();
}
/**
* Return the tenant aware key.
*/
private Object key(Object id) {
return tenantAwareKey.key(id);
}
/**
* Return a value from the cache.
*/
@Override
public Object get(Object key) {
public Object get(Object id) {
CacheEntry entry = map.get(key);
CacheEntry entry = map.get(key(id));
if (entry == null) {
missCount.increment();
return null;
@@ -210,7 +221,9 @@ public class DefaultServerCache implements ServerCache {
* Put a value into the cache.
*/
@Override
public Object put(Object key, Object value) {
public Object put(Object id, Object value) {
Object key = key(id);
CacheEntry entry = map.put(key, new CacheEntry(key, value));
if (entry == null) {
insertCount.increment();
@@ -225,8 +238,9 @@ public class DefaultServerCache implements ServerCache {
* Remove an entry from the cache.
*/
@Override
public Object remove(Object key) {
CacheEntry entry = map.remove(key);
public Object remove(Object id) {
CacheEntry entry = map.remove(key(id));
if (entry == null) {
return null;
} else {
@@ -5,6 +5,7 @@ import io.ebean.cache.ServerCache;
import io.ebean.cache.ServerCacheFactory;
import io.ebean.cache.ServerCacheOptions;
import io.ebean.cache.ServerCacheType;
import io.ebean.config.CurrentTenantProvider;
/**
@@ -29,9 +30,9 @@ class DefaultServerCacheFactory implements ServerCacheFactory {
}
@Override
public ServerCache createCache(ServerCacheType type, String cacheKey, ServerCacheOptions cacheOptions) {
public ServerCache createCache(ServerCacheType type, String cacheKey, CurrentTenantProvider tenantProvider, ServerCacheOptions cacheOptions) {
DefaultServerCache cache = new DefaultServerCache(cacheKey, cacheOptions);
DefaultServerCache cache = new DefaultServerCache(cacheKey, tenantProvider, cacheOptions);
if (executor != null) {
cache.periodicTrim(executor);
}
@@ -6,9 +6,6 @@ import io.ebean.cache.ServerCacheOptions;
import io.ebean.cache.ServerCacheType;
import io.ebean.config.CurrentTenantProvider;
import java.util.function.Supplier;
/**
* Manages the bean and query caches.
*/
@@ -48,12 +45,12 @@ public class DefaultServerCacheManager implements SpiCacheManager {
}
@Override
public Supplier<ServerCache> getCollectionIdsCache(Class<?> beanType, String propertyName) {
public ServerCache getCollectionIdsCache(Class<?> beanType, String propertyName) {
return cacheHolder.getCache(beanType, name(beanType) + "." + propertyName, ServerCacheType.COLLECTION_IDS);
}
@Override
public Supplier<ServerCache> getNaturalKeyCache(Class<?> beanType) {
public ServerCache getNaturalKeyCache(Class<?> beanType) {
return cacheHolder.getCache(beanType, name(beanType), ServerCacheType.NATURAL_KEY);
}
@@ -61,7 +58,7 @@ public class DefaultServerCacheManager implements SpiCacheManager {
* Return the query cache for a given bean type.
*/
@Override
public Supplier<ServerCache> getQueryCache(Class<?> beanType) {
public ServerCache getQueryCache(Class<?> beanType) {
return cacheHolder.getCache(beanType, name(beanType), ServerCacheType.QUERY);
}
@@ -69,7 +66,7 @@ public class DefaultServerCacheManager implements SpiCacheManager {
* Return the bean cache for a given bean type.
*/
@Override
public Supplier<ServerCache> getBeanCache(Class<?> beanType) {
public ServerCache getBeanCache(Class<?> beanType) {
return cacheHolder.getCache(beanType, name(beanType), ServerCacheType.BEAN);
}
@@ -2,8 +2,6 @@ package io.ebeaninternal.server.cache;
import io.ebean.cache.ServerCache;
import java.util.function.Supplier;
/**
* The cache service for server side caching of beans and query results.
*/
@@ -21,22 +19,22 @@ public interface SpiCacheManager {
/**
* Return the cache for mapping natural keys to id values.
*/
Supplier<ServerCache> getNaturalKeyCache(Class<?> beanType);
ServerCache getNaturalKeyCache(Class<?> beanType);
/**
* Return the cache for beans of a particular type.
*/
Supplier<ServerCache> getBeanCache(Class<?> beanType);
ServerCache getBeanCache(Class<?> beanType);
/**
* Return the cache for associated many properties of a bean type.
*/
Supplier<ServerCache> getCollectionIdsCache(Class<?> beanType, String propertyName);
ServerCache getCollectionIdsCache(Class<?> beanType, String propertyName);
/**
* Return the cache for query results of a particular type of bean.
*/
Supplier<ServerCache> getQueryCache(Class<?> beanType);
ServerCache getQueryCache(Class<?> beanType);
/**
* Clear all the caches.
@@ -244,17 +244,17 @@ public class DefaultContainer implements SpiContainer {
*/
private void setDatabasePlatform(ServerConfig config) {
DatabasePlatform dbPlatform = config.getDatabasePlatform();
if (dbPlatform == null) {
DatabasePlatform platform = config.getDatabasePlatform();
if (platform == null) {
if (config.getTenantMode().isDynamicDataSource()) {
throw new IllegalStateException("DatabasePlatform must be explicitly set on ServerConfig for TenantMode "+config.getTenantMode());
}
DatabasePlatformFactory factory = new DatabasePlatformFactory();
DatabasePlatform db = factory.create(config);
db.configure(config.getDbTypeConfig());
config.setDatabasePlatform(db);
logger.info("DatabasePlatform name:{} platform:{}", config.getName(), db.getName());
// automatically determine the platform
platform = new DatabasePlatformFactory().create(config);
config.setDatabasePlatform(platform);
}
logger.info("DatabasePlatform name:{} platform:{}", config.getName(), platform.getName());
platform.configure(config.getDbTypeConfig(), config.isAllQuotedIdentifiers());
}
/**
@@ -268,7 +268,7 @@ public class DefaultContainer implements SpiContainer {
private DataSource getDataSourceFromConfig(ServerConfig config) {
if (config.isDbOffline() || DbOffline.isSet()) {
if (config.isOfflineMode()) {
logger.debug("... DbOffline using platform [{}]", DbOffline.getPlatform());
return null;
}
@@ -334,7 +334,7 @@ public class DefaultContainer implements SpiContainer {
*/
private boolean checkDataSource(ServerConfig serverConfig) {
if (serverConfig.isDbOffline() || DbOffline.isSet()) {
if (serverConfig.isOfflineMode()) {
return false;
}
@@ -349,7 +349,7 @@ public class DefaultContainer implements SpiContainer {
Connection c = null;
try {
c = serverConfig.getDataSource().getConnection();
if (c.getAutoCommit()) {
if (!serverConfig.isAutoCommitMode() && c.getAutoCommit()) {
logger.warn("DataSource [{}] has autoCommit defaulting to true!", serverConfig.getName());
}
return true;
@@ -104,6 +104,7 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@@ -827,7 +828,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
public Transaction beginTransaction(TxIsolation isolation) {
// start an explicit transaction
SpiTransaction t = transactionManager.createTransaction(true, isolation.getLevel());
transactionScopeManager.set(t);
try {
transactionScopeManager.set(t);
} catch (PersistenceException existingTransactionError) {
t.end();
throw existingTransactionError;
}
return t;
}
@@ -1175,6 +1181,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public <T> T findUnique(Query<T> query, Transaction transaction) {
return findOne(query, transaction);
}
@Override
public <T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction) {
return Optional.ofNullable(findOne(query, transaction));
}
@Override
public <T> T findOne(Query<T> query, Transaction transaction) {
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
spiQuery.checkIdEqualTo();
@@ -1284,6 +1300,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
public <T> int findCountWithCopy(Query<T> query, Transaction t) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.COUNT, query, t);
Integer result = request.getFromQueryCache();
if (result != null) {
return result;
}
try {
request.initTransIfRequired();
return request.findCount();
@@ -1492,6 +1512,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public SqlRow findUnique(SqlQuery query, Transaction t) {
return findOne(query, t);
}
@Override
public SqlRow findOne(SqlQuery query, Transaction t) {
// no findId() method for SqlQuery...
// a query that is expected to return either 0 or 1 rows
@@ -23,6 +23,7 @@ public final class InternString {
return null;
}
//noinspection SynchronizationOnStaticField
synchronized (map) {
String v = map.get(s);
if (v != null) {
@@ -248,19 +248,20 @@ public class InternalConfiguration {
* Return the JSON expression handler for the given database platform.
*/
private DbExpressionHandler getDbExpressionHandler(DatabasePlatform databasePlatform) {
Platform platform = databasePlatform.getPlatform();
if (platform == Platform.POSTGRES) {
return new PostgresJsonExpression();
final Platform platform = databasePlatform.getPlatform();
switch (platform) {
case POSTGRES:
return new PostgresJsonExpression();
case ORACLE:
return new OracleDbExpression();
case SQLSERVER:
return new SqlServerJsonExpression();
default:
return new NotSupportedDbExpression();
}
if (platform == Platform.ORACLE) {
return new OracleDbExpression();
}
return new NotSupportedDbExpression();
}
public JsonContext createJsonContext(SpiEbeanServer server) {
return new DJsonContext(server, jsonFactory, typeManager);
}
@@ -97,6 +97,14 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
return ebeanServer.getDatabasePlatform().getLikeClause();
}
/**
* Return the database platform escaped like string.
*/
@Override
public String escapeLikeString(String value) {
return ebeanServer.getDatabasePlatform().escapeLikeString(value);
}
@Override
public void executeSecondaryQueries(boolean forEach) {
// disable lazy loading leaves loadContext null
@@ -293,7 +301,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
*/
@Override
public int delete() {
return queryEngine.delete(this);
return notifyCache(queryEngine.delete(this), false);
}
/**
@@ -301,7 +309,14 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
*/
@Override
public int update() {
return queryEngine.update(this);
return notifyCache(queryEngine.update(this), true);
}
private int notifyCache(int rows, boolean update) {
if (rows > 0 && beanDescriptor.isCaching()) {
transaction.getEvent().add(beanDescriptor.getBaseTable(), false, update, !update);
}
return rows;
}
/**
@@ -453,7 +468,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
* Try to get the query result from the query cache.
*/
@Override
public BeanCollection<T> getFromQueryCache() {
@SuppressWarnings("unchecked")
public Object getFromQueryCache() {
if (!query.isUseQueryCache()) {
return null;
@@ -461,16 +477,18 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
cacheKey = query.queryHash();
BeanCollection<T> cached = beanDescriptor.queryCacheGet(cacheKey);
Object cached = beanDescriptor.queryCacheGet(cacheKey);
if (cached != null && isAuditReads() && readAuditQueryType()) {
// raw sql can't use L2 cache so normal queries only in here
Collection<T> actualDetails = cached.getActualDetails();
List<Object> ids = new ArrayList<>(actualDetails.size());
for (T bean : actualDetails) {
ids.add(beanDescriptor.getIdForJson(bean));
if (cached instanceof BeanCollection) {
// raw sql can't use L2 cache so normal queries only in here
Collection<T> actualDetails = ((BeanCollection<T>)cached).getActualDetails();
List<Object> ids = new ArrayList<>(actualDetails.size());
for (T bean : actualDetails) {
ids.add(beanDescriptor.getIdForJson(bean));
}
beanDescriptor.readAuditMany(queryPlanKey.getPartialKey(), "l2-query-cache", ids);
}
beanDescriptor.readAuditMany(queryPlanKey.getPartialKey(), "l2-query-cache", ids);
}
return cached;
@@ -494,7 +512,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
}
}
public void putToQueryCache(BeanCollection<T> queryResult) {
public void putToQueryCache(Object queryResult) {
beanDescriptor.queryCachePut(cacheKey, queryResult);
}
@@ -535,7 +553,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
* </p>
*/
public boolean isAuditReads() {
return !query.isDisableReadAudit() && beanDescriptor.isReadAuditing();
return beanDescriptor.isReadAuditing() && !query.isDisableReadAudit();
}
/**
@@ -524,7 +524,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
/**
* Prepare the update after potential modifications in a BeanPersistController.
*/
public void postControllerPrepareUpdate() {
private void postControllerPrepareUpdate() {
if (intercept.isNew() && controller != null) {
// 'stateless update' - set dirty properties modified in controller preUpdate
intercept.setNewBeanForUpdate();
@@ -985,6 +985,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
docStoreUpdates.queueIndex(beanDescriptor.getDocStoreQueueId(), idValue);
}
}
break;
default:
break;
}
@@ -1071,31 +1072,38 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
return version;
}
public void executeInsert() {
private void setTenantId() {
Object tenantId = transaction.getTenantId();
if (tenantId != null) {
beanDescriptor.setTenantId(entityBean, tenantId);
}
}
private void executeInsert() {
setTenantId();
if (controller == null || controller.preInsert(this)) {
beanManager.getBeanPersister().insert(this);
}
}
public void executeUpdate() {
private void executeUpdate() {
setTenantId();
if (controller == null || controller.preUpdate(this)) {
postControllerPrepareUpdate();
beanManager.getBeanPersister().update(this);
}
}
public void executeSoftDelete() {
private void executeSoftDelete() {
setTenantId();
if (controller == null || controller.preSoftDelete(this)) {
postControllerPrepareUpdate();
beanManager.getBeanPersister().update(this);
}
}
public int executeDelete() {
private int executeDelete() {
setTenantId();
if (controller == null || controller.preDelete(this)) {
return beanManager.getBeanPersister().delete(this);
}
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.core;
import io.ebean.QueryIterator;
import io.ebean.Version;
import io.ebean.bean.BeanCollection;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeanservice.docstore.api.DocQueryRequest;
@@ -117,13 +116,18 @@ public interface SpiOrmQueryRequest<T> extends DocQueryRequest<T> {
/**
* Try to get the query result from the query cache.
*/
BeanCollection<T> getFromQueryCache();
<A> A getFromQueryCache();
/**
* Return the Database platform like clause.
*/
String getDBLikeClause();
/**
* Escapes a string to use it as exact match in Like clause.
*/
String escapeLikeString(String value);
/**
* Mark the underlying transaction as not being query only.
*/
@@ -0,0 +1,28 @@
package io.ebeaninternal.server.core;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.expression.Op;
/**
* Microsoft SQL Server JSON. ARRAY expressions not supported.
*/
public class SqlServerJsonExpression implements DbExpressionHandler {
@Override
public void json(final SpiExpressionRequest request, final String propName,
final String path, final Op operator, final Object value) {
request.append("json_value(").append(propName).append(", '$.").append(path).append("')");
request.append(operator.bind());
}
@Override
public void arrayContains(final SpiExpressionRequest request, final String propName,
final boolean contains, final Object... values) {
throw new RuntimeException("ARRAY expressions not supported on Microsoft SQL Server");
}
@Override
public void arrayIsEmpty(final SpiExpressionRequest request, final String propName, final boolean empty) {
throw new RuntimeException("ARRAY expressions not supported on Microsoft SQL Server");
}
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.core.bootup;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -21,6 +22,8 @@ class ManifestReader {
private final Set<String> packageSet = new HashSet<>();
private static final Pattern PACKAGE_DELIMITER = Pattern.compile("[,; ]");
/**
* Read the packages from ebean.mf manifest files found as resources.
*/
@@ -73,7 +76,7 @@ class ManifestReader {
* Collect each individual package splitting by delimiters.
*/
private void add(String packages) {
String[] split = packages.split(",|;| ");
String[] split = PACKAGE_DELIMITER.split(packages);
for (String aSplit : split) {
String pkg = aSplit.trim();
if (!pkg.isEmpty()) {
@@ -713,7 +713,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
void registerColumn(String dbColumn, String path) {
columnPath.put(dbColumn.toLowerCase(), path);
String key = dbColumn.toLowerCase();
// check for clash with imported OneToOne PK
if (!columnPath.containsKey(key)) {
columnPath.put(key, path);
}
}
void registerTable(String baseTable, BeanPropertyAssoc<?> assocProperty) {
@@ -1116,6 +1120,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
}
/**
* Return true if there is bean or query caching for this type.
*/
public boolean isCaching() {
return cacheHelp.isCaching();
}
/**
* Return true if there is currently bean caching for this type of bean.
*/
@@ -1163,15 +1174,15 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
/**
* Get a query result from the query cache.
*/
public BeanCollection<T> queryCacheGet(Object id) {
public Object queryCacheGet(Object id) {
return cacheHelp.queryCacheGet(id);
}
/**
* Put a query result into the query cache.
*/
public void queryCachePut(Object id, BeanCollection<T> query) {
cacheHelp.queryCachePut(id, query);
public void queryCachePut(Object id, Object queryResult) {
cacheHelp.queryCachePut(id, queryResult);
}
/**
@@ -2176,12 +2187,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
if (basePos > -1) {
// nested or embedded property
String baseName = propName.substring(0, basePos);
String remainder = propName.substring(basePos + 1);
BeanProperty assocProp = _findBeanProperty(baseName);
if (assocProp == null) {
return null;
}
String remainder = propName.substring(basePos + 1);
return assocProp.buildElPropertyValue(propName, remainder, chain, propertyDeploy);
}
@@ -3006,6 +3018,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
return versionProperty;
}
/**
* Return the tenant property when multi-tenant partitioning support is used.
*/
public BeanProperty getTenantProperty() {
return tenant;
}
/**
* Scalar properties without the unique id or secondary table properties.
*/
@@ -26,7 +26,6 @@ import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
/**
* Helper for BeanDescriptor that manages the bean, query and collection caches.
@@ -60,9 +59,9 @@ final class BeanDescriptorCacheHelp<T> {
private final BeanPropertyAssocOne<?>[] propertiesOneImported;
private final String naturalKeyProperty;
private final Supplier<ServerCache> beanCache;
private final Supplier<ServerCache> naturalKeyCache;
private final Supplier<ServerCache> queryCache;
private final ServerCache beanCache;
private final ServerCache naturalKeyCache;
private final ServerCache queryCache;
/**
* Set to true if all persist changes need to notify the cache.
@@ -156,6 +155,13 @@ final class BeanDescriptorCacheHelp<T> {
return beanCache != null;
}
/**
* Return true if there is bean or query caching on this type.
*/
boolean isCaching() {
return beanCache != null || queryCache != null;
}
CacheOptions getCacheOptions() {
return cacheOptions;
}
@@ -168,7 +174,7 @@ final class BeanDescriptorCacheHelp<T> {
if (queryLog.isDebugEnabled()) {
queryLog.debug(" CLEAR {}", cacheName);
}
queryCache.get().clear();
queryCache.clear();
}
}
@@ -184,38 +190,37 @@ final class BeanDescriptorCacheHelp<T> {
/**
* Get a query result from the query cache.
*/
@SuppressWarnings("unchecked")
BeanCollection<T> queryCacheGet(Object id) {
Object queryCacheGet(Object id) {
if (queryCache == null) {
throw new IllegalStateException("No query cache enabled on " + desc + ". Need explicit @Cache(enableQueryCache=true)");
}
BeanCollection<T> list = (BeanCollection<T>) queryCache.get().get(id);
Object queryResult = queryCache.get(id);
if (queryLog.isDebugEnabled()) {
if (list == null) {
if (queryResult == null) {
queryLog.debug(" GET {}({}) - cache miss", cacheName, id);
} else {
queryLog.debug(" GET {}({}) - hit", cacheName, id);
}
}
return list;
return queryResult;
}
/**
* Put a query result into the query cache.
*/
void queryCachePut(Object id, BeanCollection<T> query) {
void queryCachePut(Object id, Object queryResult) {
if (queryCache == null) {
throw new IllegalStateException("No query cache enabled on " + desc + ". Need explicit @Cache(enableQueryCache=true)");
}
if (queryLog.isDebugEnabled()) {
queryLog.debug(" PUT {}({})", cacheName, id);
}
queryCache.get().put(id, query);
queryCache.put(id, queryResult);
}
void manyPropRemove(String propertyName, Object parentId) {
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName).get();
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName);
if (manyLog.isTraceEnabled()) {
manyLog.trace(" REMOVE {}({}).{}", cacheName, parentId, propertyName);
}
@@ -223,7 +228,7 @@ final class BeanDescriptorCacheHelp<T> {
}
void manyPropClear(String propertyName) {
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName).get();
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName);
if (manyLog.isDebugEnabled()) {
manyLog.debug(" CLEAR {}(*).{} ", cacheName, propertyName);
}
@@ -234,7 +239,7 @@ final class BeanDescriptorCacheHelp<T> {
* Return the CachedManyIds for a given bean many property. Returns null if not in the cache.
*/
private CachedManyIds manyPropGet(Object parentId, String propertyName) {
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName).get();
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName);
CachedManyIds entry = (CachedManyIds) collectionIdsCache.get(parentId);
if (entry == null) {
if (manyLog.isTraceEnabled()) {
@@ -285,7 +290,7 @@ final class BeanDescriptorCacheHelp<T> {
void cachePutManyIds(Object parentId, String manyName, CachedManyIds entry) {
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, manyName).get();
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, manyName);
if (manyLog.isDebugEnabled()) {
manyLog.debug(" PUT {}({}).{} - ids:{}", cacheName, parentId, manyName, entry);
}
@@ -326,7 +331,7 @@ final class BeanDescriptorCacheHelp<T> {
}
// try to lookup the id using the natural key
Object id = naturalKeyCache.get().get(keyBindParam.getValue());
Object id = naturalKeyCache.get(keyBindParam.getValue());
if (natLog.isTraceEnabled()) {
natLog.trace(" LOOKUP {}({}) - id:{}", cacheName, keyBindParam.getValue(), id);
}
@@ -365,7 +370,7 @@ final class BeanDescriptorCacheHelp<T> {
if (beanCache == null) {
throw new IllegalStateException("No bean cache enabled for " + desc + ". Add the @Cache annotation.");
}
return beanCache.get();
return beanCache;
}
/**
@@ -376,7 +381,7 @@ final class BeanDescriptorCacheHelp<T> {
if (beanLog.isDebugEnabled()) {
beanLog.debug(" CLEAR {}", cacheName);
}
beanCache.get().clear();
beanCache.clear();
}
}
@@ -415,7 +420,7 @@ final class BeanDescriptorCacheHelp<T> {
if (natLog.isDebugEnabled()) {
natLog.debug(" PUT {}({}, {})", cacheName, naturalKey, id);
}
naturalKeyCache.get().put(naturalKey, id);
naturalKeyCache.put(naturalKey, id);
}
}
}
@@ -544,7 +549,7 @@ final class BeanDescriptorCacheHelp<T> {
if (beanLog.isDebugEnabled()) {
beanLog.debug(" REMOVE {}({})", cacheName, id);
}
beanCache.get().remove(id);
beanCache.remove(id);
}
for (BeanPropertyAssocOne<?> aPropertiesOneImported : propertiesOneImported) {
aPropertiesOneImported.cacheClear();
@@ -677,7 +682,7 @@ final class BeanDescriptorCacheHelp<T> {
void cacheNaturalKeyPut(Object id, Object newKey) {
if (newKey != null) {
naturalKeyCache.get().put(newKey, id);
naturalKeyCache.put(newKey, id);
}
}
@@ -712,7 +717,7 @@ final class BeanDescriptorCacheHelp<T> {
if (natLog.isDebugEnabled()) {
natLog.debug(".. update {} REMOVE({}) - old key for ({})", cacheName, oldKey, id);
}
naturalKeyCache.get().remove(oldKey);
naturalKeyCache.remove(oldKey);
}
}
}
@@ -626,7 +626,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
int lc = persistListenerManager.getRegisterCount();
int fc = beanFinderManager.getRegisterCount();
logger.debug("BeanPersistControllers[" + cc + "] BeanFinders[" + fc + "] BeanPersistListeners[" + lc + "] BeanQueryAdapters[" + qa + "] BeanPostLoaders[" + pl + "] BeanPostConstructors[" + pc + "]");
logger.debug("BeanPersistControllers[{}] BeanFinders[{}] BeanPersistListeners[{}] BeanQueryAdapters[{}] BeanPostLoaders[{}] BeanPostConstructors[{}]", cc, fc, lc, qa, pl, pc);
}
private void logStatus() {
@@ -1239,7 +1239,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
if (desc.getIdType() == null) {
if (desc.isPrimaryKeyCompoundOrNonNumeric()) {
// assuming that this is a user supplied key like ISO country code or ISO currency code or lookup table code
logger.debug("Expecting user defined identity on " + desc.getFullName() + " - not using db sequence or autoincrement");
logger.debug("Expecting user defined identity on {} - not using db sequence or autoincrement", desc.getFullName());
desc.setIdType(IdType.EXTERNAL);
return;
}
@@ -1263,7 +1263,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
String seqName = desc.getIdGeneratorName();
if (seqName != null) {
logger.debug("explicit sequence " + seqName + " on " + desc.getFullName());
logger.debug("explicit sequence {} on {}", seqName, desc.getFullName());
} else {
String primaryKeyColumn = desc.getSinglePrimaryKeyColumn();
// use namingConvention to define sequence name
@@ -32,7 +32,7 @@ public class BeanFinderManager {
for (BeanFindController c : list) {
if (c.isRegisterFor(deployDesc.getBeanType())) {
logger.debug("BeanFindController on[" + deployDesc.getFullName() + "] " + c.getClass().getName());
logger.debug("BeanFindController on[{}] {}", deployDesc.getFullName(), c.getClass().getName());
deployDesc.setBeanFinder(c);
}
}
@@ -33,7 +33,7 @@ public class BeanQueryAdapterManager {
for (BeanQueryAdapter c : list) {
if (c.isRegisterFor(deployDesc.getBeanType())) {
logger.debug("BeanQueryAdapter on[" + deployDesc.getFullName() + "] " + c.getClass().getName());
logger.debug("BeanQueryAdapter on[{}] {}", deployDesc.getFullName(), c.getClass().getName());
deployDesc.addQueryAdapter(c);
}
}
@@ -60,7 +60,7 @@ public class DeployDocPropertyOptions {
}
private void setBoost(float boost) {
if (boost != 1) {
if (Float.compare(boost, 1.0F) != 0) {
mapping.setBoost(boost);
}
}
@@ -26,6 +26,8 @@ public class InheritInfo {
private final int discriminatorLength;
private final String columnDefn;
private final String where;
private final Class<?> type;
@@ -52,12 +54,13 @@ public class InheritInfo {
this.parent = parent;
this.type = deploy.getType();
this.discriminatorColumn = InternString.intern(deploy.getDiscriminatorColumn(parent));
this.discriminatorColumn = InternString.intern(deploy.getColumnName(parent));
this.discriminatorValue = deploy.getDiscriminatorObjectValue();
this.discriminatorStringValue = deploy.getDiscriminatorStringValue();
this.discriminatorType = deploy.getDiscriminatorType(parent);
this.discriminatorLength = deploy.getDiscriminatorLength(parent);
this.discriminatorLength = deploy.getColumnLength(parent);
this.columnDefn = deploy.getColumnDefn();
this.where = InternString.intern(deploy.getWhere());
if (r == null) {
@@ -164,14 +167,14 @@ public class InheritInfo {
public BeanProperty[] localProperties() {
return descriptor.propertiesLocal();
}
/**
* Return the children.
*/
public ArrayList<InheritInfo> getChildren() {
return children;
}
/**
* Get the bean property additionally looking in the sub types.
*/
@@ -338,10 +341,17 @@ public class InheritInfo {
/**
* Return the length of the discriminator column.
*/
public int getDiscriminatorLength() {
public int getColumnLength() {
return discriminatorLength;
}
/**
* Return the explicit column definition.
*/
public String getColumnDefn() {
return columnDefn;
}
/**
* Return the discriminator value for this node.
*/
@@ -16,8 +16,10 @@ public enum ManyType {
private final boolean map;
@SuppressWarnings("rawtypes")
private final Class<? extends Collection> type;
@SuppressWarnings("rawtypes")
ManyType(boolean map, Class<? extends Collection> type) {
this.map = map;
this.type = type;
@@ -31,6 +33,7 @@ public enum ManyType {
* Returns List.class or Set.class and null for Map.
* Not intended to be called for maps.
*/
@SuppressWarnings("rawtypes")
public Class<? extends Collection> getCollectionType() {
return type;
}
@@ -33,7 +33,7 @@ public class PersistControllerManager {
for (BeanPersistController c : list) {
if (c.isRegisterFor(deployDesc.getBeanType())) {
logger.debug("BeanPersistController on[" + deployDesc.getFullName() + "] " + c.getClass().getName());
logger.debug("BeanPersistController on[{}] {}", deployDesc.getFullName(), c.getClass().getName());
deployDesc.addPersistController(c);
}
}
@@ -32,7 +32,7 @@ public class PostConstructManager {
for (BeanPostConstructListener c : list) {
if (c.isRegisterFor(deployDesc.getBeanType())) {
logger.debug("BeanPostLoad on[" + deployDesc.getFullName() + "] " + c.getClass().getName());
logger.debug("BeanPostLoad on[{}] {}", deployDesc.getFullName(), c.getClass().getName());
deployDesc.addPostConstructListener(c);
}
}
@@ -32,7 +32,7 @@ public class PostLoadManager {
for (BeanPostLoad c : list) {
if (c.isRegisterFor(deployDesc.getBeanType())) {
logger.debug("BeanPostLoad on[" + deployDesc.getFullName() + "] " + c.getClass().getName());
logger.debug("BeanPostLoad on[{}] {}", deployDesc.getFullName(), c.getClass().getName());
deployDesc.addPostLoad(c);
}
}
@@ -29,6 +29,9 @@ public class GeneratedCounter implements GeneratedProperty {
@Override
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
Number currVal = (Number) prop.getValue(bean);
if (currVal == null) {
throw new IllegalStateException("version property has been set to null on bean: " + bean);
}
Integer nextVal = currVal.intValue() + 1;
return BasicTypeConverter.convert(nextVal, numberType);
}
@@ -25,8 +25,11 @@ public class GeneratedCounterInteger implements GeneratedProperty {
*/
@Override
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
Integer i = (Integer) prop.getValue(bean);
return i + 1;
Integer val = (Integer) prop.getValue(bean);
if (val == null) {
throw new IllegalStateException("version property has been set to null on bean: " + bean);
}
return val + 1;
}
/**
@@ -25,8 +25,11 @@ public class GeneratedCounterLong implements GeneratedProperty {
*/
@Override
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
Long i = (Long) prop.getValue(bean);
return i + 1;
Long val = (Long) prop.getValue(bean);
if (val == null) {
throw new IllegalStateException("version property has been set to null on bean: " + bean);
}
return val + 1;
}
/**
@@ -45,6 +45,10 @@ public class GeneratedPropertyFactory {
if (currentUserProvider != null) {
generatedWhoCreated = new GeneratedWhoCreated(currentUserProvider);
generatedWhoModified = new GeneratedWhoModified(currentUserProvider);
} else if (serverConfig.isOfflineMode()) {
currentUserProvider = new DummyCurrentUser();
generatedWhoCreated = new GeneratedWhoCreated(currentUserProvider);
generatedWhoModified = new GeneratedWhoModified(currentUserProvider);
} else {
generatedWhoCreated = null;
generatedWhoModified = null;
@@ -150,4 +154,11 @@ public class GeneratedPropertyFactory {
// do nothing
}
}
private static class DummyCurrentUser implements CurrentUserProvider {
@Override
public Object currentUser() {
throw new RuntimeException("never called");
}
}
}
@@ -172,6 +172,9 @@ public final class IdBinderEmbedded implements IdBinder {
@Override
public String getIdInValueExprDelete(int size) {
if (size <= 0) {
throw new IndexOutOfBoundsException("The size must be at least 1");
}
if (!idInExpandedForm) {
return getIdInValueExpr(size);
}
@@ -199,7 +202,9 @@ public final class IdBinderEmbedded implements IdBinder {
@Override
public String getIdInValueExpr(int size) {
if (size <= 0) {
throw new IndexOutOfBoundsException("The size must be at least 1");
}
StringBuilder sb = new StringBuilder();
if (!idInExpandedForm) {
@@ -130,6 +130,9 @@ public final class IdBinderSimple implements IdBinder {
@Override
public String getIdInValueExpr(int size) {
if (size <= 0) {
throw new IndexOutOfBoundsException("The size must be at least 1");
}
StringBuilder sb = new StringBuilder(2 * size + 10);
sb.append(" in");
sb.append(" (?");
@@ -34,6 +34,8 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.sql.Types;
import java.util.HashSet;
import java.util.Set;
/**
* Description of a property of a bean. Includes its deployment information such
@@ -45,6 +47,14 @@ public class DeployBeanProperty {
private static final int UNIDIRECTIONAL_ORDER = 100000;
private static final int AUDITCOLUMN_ORDER = -1000000;
private static final int VERSIONCOLUMN_ORDER = -1000000;
private static final Set<Class<?>> PRIMITIVE_NUMBER_TYPES = new HashSet<>();
static {
PRIMITIVE_NUMBER_TYPES.add(float.class);
PRIMITIVE_NUMBER_TYPES.add(double.class);
PRIMITIVE_NUMBER_TYPES.add(long.class);
PRIMITIVE_NUMBER_TYPES.add(int.class);
PRIMITIVE_NUMBER_TYPES.add(short.class);
}
/**
* Flag to mark this at part of the unique id.
@@ -940,6 +950,9 @@ public class DeployBeanProperty {
if (boolean.class.equals(propertyType) && !softDelete) {
this.nullable = false;
this.dbColumnDefault = DbDefaultValue.FALSE;
} else if (!id && !versionColumn && PRIMITIVE_NUMBER_TYPES.contains(propertyType)) {
this.nullable = false;
}
}
@@ -1,34 +1,6 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.annotation.Aggregation;
import io.ebean.annotation.CreatedTimestamp;
import io.ebean.annotation.DbArray;
import io.ebean.annotation.DbComment;
import io.ebean.annotation.DbHstore;
import io.ebean.annotation.DbJson;
import io.ebean.annotation.DbJsonB;
import io.ebean.annotation.DocCode;
import io.ebean.annotation.DocEmbedded;
import io.ebean.annotation.DocProperty;
import io.ebean.annotation.DocSortable;
import io.ebean.annotation.Draft;
import io.ebean.annotation.DraftDirty;
import io.ebean.annotation.DraftOnly;
import io.ebean.annotation.DraftReset;
import io.ebean.annotation.Encrypted;
import io.ebean.annotation.Expose;
import io.ebean.annotation.Formula;
import io.ebean.annotation.HistoryExclude;
import io.ebean.annotation.Index;
import io.ebean.annotation.JsonIgnore;
import io.ebean.annotation.SoftDelete;
import io.ebean.annotation.TenantId;
import io.ebean.annotation.UnmappedJson;
import io.ebean.annotation.UpdatedTimestamp;
import io.ebean.annotation.WhenCreated;
import io.ebean.annotation.WhenModified;
import io.ebean.annotation.WhoCreated;
import io.ebean.annotation.WhoModified;
import io.ebean.annotation.*;
import io.ebean.config.EncryptDeploy;
import io.ebean.config.EncryptDeploy.Mode;
import io.ebean.config.dbplatform.DbEncrypt;
@@ -315,7 +287,10 @@ public class AnnotationFields extends AnnotationParser {
if (get(prop, HistoryExclude.class) != null) {
prop.setExcludedFromHistory();
}
Length length = get(prop, Length.class);
if (length != null) {
prop.setDbLength(length.value());
}
io.ebean.annotation.NotNull nonNull = get(prop, io.ebean.annotation.NotNull.class);
if (nonNull != null) {
prop.setNullable(false);
@@ -373,7 +348,7 @@ public class AnnotationFields extends AnnotationParser {
private void addIndex(DeployBeanProperty prop, Index index) {
String[] columnNames;
if (index.columnNames() == null || index.columnNames().length == 0) {
if (index.columnNames().length == 0) {
columnNames = new String[]{prop.getDbColumn()};
} else {
columnNames = new String[index.columnNames().length];
@@ -5,11 +5,9 @@ import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Inheritance;
import java.lang.annotation.Annotation;
import java.sql.Types;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -118,14 +116,10 @@ public class DeployInherit {
DiscriminatorColumn da = AnnotationBase.findAnnotation(cls, DiscriminatorColumn.class);
if (da != null) {
// lowercase the discriminator column for RawSql and JSON
info.setDiscriminatorColumn(da.name().toLowerCase());
DiscriminatorType discriminatorType = da.discriminatorType();
if (discriminatorType.equals(DiscriminatorType.INTEGER)) {
info.setDiscriminatorType(Types.INTEGER);
} else {
info.setDiscriminatorType(Types.VARCHAR);
}
info.setDiscriminatorLength(da.length());
info.setColumnName(da.name().toLowerCase());
info.setColumnType(da.discriminatorType());
info.setColumnLength(da.length());
info.setColumnDefn(da.columnDefinition());
}
DiscriminatorValue dv = AnnotationBase.findAnnotation(cls, DiscriminatorValue.class);
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.deploy.parse;
import io.ebeaninternal.server.deploy.InheritInfo;
import javax.persistence.DiscriminatorType;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
@@ -15,18 +16,15 @@ public class DeployInheritInfo {
/**
* the default discriminator column according to the JPA 1.0 spec.
*/
private static final String JPA_DEFAULT_DISCRIM_COLUMN = "dtype";
private int discriminatorLength;
private int discriminatorType;
private static final String DEFAULT_COLUMN_NAME = "dtype";
private String discriminatorStringValue;
private Object discriminatorObjectValue;
private String discriminatorColumn;
private String discriminatorWhere;
private int columnType;
private String columnName;
private int columnLength;
private String columnDefn;
private final Class<?> type;
@@ -37,7 +35,7 @@ public class DeployInheritInfo {
/**
* Create for a given type.
*/
public DeployInheritInfo(Class<?> type) {
DeployInheritInfo(Class<?> type) {
this.type = type;
}
@@ -90,85 +88,82 @@ public class DeployInheritInfo {
children.add(childInfo);
}
/**
* Return the derived where for the discriminator.
*/
public String getDiscriminatorWhere() {
return discriminatorWhere;
}
/**
* Set the derived where for the discriminator.
*/
public void setDiscriminatorWhere(String discriminatorWhere) {
this.discriminatorWhere = discriminatorWhere;
}
/**
* Return the column name of the discriminator.
*/
public String getDiscriminatorColumn(InheritInfo parent) {
if (discriminatorColumn == null) {
public String getColumnName(InheritInfo parent) {
if (columnName == null) {
if (parent == null) {
discriminatorColumn = JPA_DEFAULT_DISCRIM_COLUMN;
columnName = DEFAULT_COLUMN_NAME;
} else {
discriminatorColumn = parent.getDiscriminatorColumn();
columnName = parent.getDiscriminatorColumn();
}
}
return discriminatorColumn;
return columnName;
}
/**
* Set the column name of the discriminator.
*/
public void setDiscriminatorColumn(String discriminatorColumn) {
this.discriminatorColumn = discriminatorColumn;
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public int getDiscriminatorLength(InheritInfo parent) {
if (discriminatorLength == 0) {
public int getColumnLength(InheritInfo parent) {
if (columnLength == 0) {
if (parent == null) {
discriminatorLength = 10;
columnLength = 10;
} else {
discriminatorLength = parent.getDiscriminatorLength();
columnLength = parent.getColumnLength();
}
}
return discriminatorLength;
return columnLength;
}
/**
* Return the sql type of the discriminator value.
*/
public int getDiscriminatorType(InheritInfo parent) {
if (discriminatorType == 0) {
if (columnType == 0) {
if (parent == null) {
discriminatorType = Types.VARCHAR;
columnType = Types.VARCHAR;
} else {
discriminatorType = parent.getDiscriminatorType();
columnType = parent.getDiscriminatorType();
}
}
return discriminatorType;
return columnType;
}
/**
* Set the sql type of the discriminator.
*/
public void setDiscriminatorType(int discriminatorType) {
this.discriminatorType = discriminatorType;
public void setColumnType(DiscriminatorType type) {
if (type.equals(DiscriminatorType.INTEGER)) {
this.columnType = Types.INTEGER;
} else {
this.columnType = Types.VARCHAR;
}
}
/**
* Return the length of the discriminator column.
* Set explicit column definition (ddl).
*/
public int getDiscriminatorLength() {
return discriminatorLength;
void setColumnDefn(String columnDefn) {
this.columnDefn = columnDefn;
}
/**
* Return the explicit column definition.
*/
public String getColumnDefn() {
return columnDefn;
}
/**
* Set the length of the discriminator column.
*/
public void setDiscriminatorLength(int discriminatorLength) {
this.discriminatorLength = discriminatorLength;
void setColumnLength(int columnLength) {
this.columnLength = columnLength;
}
/**
@@ -185,13 +180,13 @@ public class DeployInheritInfo {
/**
* Set the discriminator value for this node.
*/
public void setDiscriminatorValue(String value) {
void setDiscriminatorValue(String value) {
if (value != null) {
value = value.trim();
if (!value.isEmpty()) {
discriminatorStringValue = value;
// convert the value if desired
if (discriminatorType == Types.INTEGER) {
if (columnType == Types.INTEGER) {
this.discriminatorObjectValue = Integer.valueOf(value);
} else {
this.discriminatorObjectValue = value;
@@ -224,7 +219,7 @@ public class DeployInheritInfo {
return "";
}
StringBuilder sb = new StringBuilder();
sb.append(discriminatorColumn);
sb.append(columnName);
if (size == 1) {
sb.append(" = ");
} else {
@@ -202,7 +202,7 @@ public class DeployUtil {
} else {
// this is ok...
logger.trace("... transient property " + msg);
logger.trace("... transient property {}", msg);
return null;
}
} catch (IllegalArgumentException e) {
@@ -245,13 +245,15 @@ public class DeployUtil {
prop.setDbType(dbType);
prop.setScalarType(scalarType);
if (scalarType instanceof ScalarTypeArray) {
prop.setDbColumnDefn(((ScalarTypeArray) scalarType).getDbColumnDefn());
}
if (dbType == Types.VARCHAR) {
// determine the db column size
int dbLength = dbArray.length();
int columnLength = (dbLength > 0) ? dbLength : DEFAULT_ARRAY_VARCHAR_LENGTH;
prop.setDbLength(columnLength);
String columnDefn = ((ScalarTypeArray) scalarType).getDbColumnDefn();
if (dbArray.length() > 0) {
// fallback varchar column length when ARRAY not support by DB
columnDefn += "(" + dbArray.length() + ")";
}
prop.setDbLength(dbArray.length());
prop.setDbColumnDefn(columnDefn);
} else {
throw new RuntimeException("Not mapped to ScalarTypeArray? " + scalarType.getClass());
}
}
@@ -1,79 +0,0 @@
package io.ebeaninternal.server.deploy.parse;
import java.util.HashSet;
/**
* Keywords deemed inappropriate for use as table alias' column or table name.
* <p>
* That is, they might cause sql syntax errors if used.
* </p>
*/
public class SqlReservedWords {
private static final String baseKeyWords =
"ALIAS,ALTER,ADD,ALL,ARE,AND,ANY,ARRAY"
+ ",AS,ASC,AT"
+ ",AVG,BEGIN,BETWEEN,BIGINT,BINARY,BIT,BIT_LENGTH,BLOB,BOOLEAN"
+ ",BOTH,BY,CALL,CALLED,CASCADE,CASCADED,CASE,CAST,CATALOG,CHAR"
+ ",CHARACTER,CHECK,CLOB,CLOSE,COALESCE,COLLATE"
+ ",COLLATION,COLUMN,COMMIT,CONDITION,CONNECT,CONNECTION,CONSTRAINT"
+ ",CONSTRAINTS,CONSTRUCTOR,CONTAINS,CONTINUE,CONVERT"
+ ",COUNT,CREATE,CROSS,CURRENT_DATE"
+ ",CURRENT_PATH,CURRENT_ROLE,CURRENT_TIME,CURRENT_TIMESTAMP"
+ ",CURRENT_USER,CURSOR,DATE"
+ ",DAY,DEC,DECIMAL,DECLARE,DEFAULT,DELETE"
+ ",DESC"
+ ",DISTINCT,DO,DOUBLE,DROP,ELSE,ELSEIF,END,EQUALS"
+ ",EXEC,EXIT,EXISTS,EXTRACT"
+ ",FLOAT,FROM,FOR,FREE"
+ ",GET,GLOBAL,GO,GOTO,GRANT,GROUP,HAVING,HOUR"
+ ",IF,IN,INNER,INOUT,INSERT"
+ ",INT,INTEGER,INTO,IS,JOIN"
+ ",LAST,LIKE,LIMIT"
+ ",MAX,MIN"
+ ",NCHAR,NCLOB,NOT"
+ ",NULL,NULLIF,NUMERIC,"
+ ",OR,ORDER,OUTER"
+ ",REAL,REF,REFERENCES"
+ ",RETURN,RETURNS"
+ ",SELECT,SSL"
+ ",SMALLINT,"
+ ",SYSTEM,SYSTEM_USER,TABLE"
+ ",TO"
+ ",TRIGGER,UNION,UNIQUE"
+ ",UPDATE,USER,VARCHAR,VIEW,WHEN"
+ ",WHERE,WITH";
private static final HashSet<String> keywords = new HashSet<>();
static {
String[] initialKeywords = baseKeyWords.split(",");
for (String initialKeyword : initialKeywords) {
keywords.add(initialKeyword.trim());
}
}
/**
* Return true if the tableAlias is a keyword.
*/
public static synchronized boolean isKeyword(String keyword) {
String s = keyword.trim().toUpperCase();
return keywords.contains(s);
}
/**
* Add a sql keyword to the known set.
*/
public static synchronized void addKeyword(String keyword) {
if (keyword != null) {
keyword = keyword.trim().toUpperCase();
if (!keyword.isEmpty()) {
keywords.add(keyword);
}
}
}
}
@@ -1,16 +0,0 @@
<!ELEMENT ebean (table | iudextender | findextender | datasource | primarykeys | property)* >
<!ELEMENT table EMPTY >
<!ATTLIST table name CDATA #REQUIRED>
<!ELEMENT iudextender EMPTY >
<!ATTLIST iudextender classname CDATA #REQUIRED>
<!ELEMENT findextender EMPTY >
<!ATTLIST findextender classname CDATA #REQUIRED>
<!ELEMENT datasource EMPTY >
<!ATTLIST datasource name CDATA #REQUIRED>
<!ELEMENT primarykeys EMPTY >
<!ATTLIST primarykeys keys CDATA #REQUIRED>
<!ELEMENT property EMPTY >
<!ATTLIST property name CDATA #REQUIRED
datatype CDATA #REQUIRED
deploydatatype CDATA #IMPLIED
deployname CDATA #IMPLIED >
@@ -1,11 +1,12 @@
package io.ebeaninternal.server.el;
import java.io.Serializable;
import java.util.Comparator;
/**
* Comparator for use with the expression objects.
*/
public interface ElComparator<T> extends Comparator<T> {
public interface ElComparator<T> extends Comparator<T>, Serializable {
/**
* Compare given 2 beans.

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