Compare commits

...
Author SHA1 Message Date
rbygrave 3c6d17c9a5 [maven-release-plugin] prepare release ebean-parent-12.11.1 2021-08-16 09:23:11 +12:00
rbygrave 885ba0fdd2 Set autoReleaseAfterClose true 2021-08-16 09:18:31 +12:00
rbygrave f3b0e34b67 #2315 - Refactor internals - make more classes final in transaction packages 2021-08-16 09:16:31 +12:00
rbygrave 4109fab0d8 #2313 - Refactor internals - make more classes final in query packages 2021-08-15 15:22:02 +12:00
rbygrave 0688061f5c Speed up TestUuidGenerator 2021-08-15 15:17:19 +12:00
rbygrave 82c12b1565 #2312 - Refactor internals - make more classes final in persist packages 2021-08-15 14:50:43 +12:00
rbygrave ebc0fabbdb #2311 - Fix BindPadding for empty list 2021-08-15 14:15:03 +12:00
rbygrave 587b2e3075 #2310 Tidy to only cast once 2021-08-15 14:09:44 +12:00
Rob BygraveandGitHub ec82dd3da9 Merge pull request #2310 from FOCONIS/nutation-on-cache
FIX: mutation info is not restored on cache hit
2021-08-15 13:50:19 +12:00
rbygrave 3e190b34f5 Merge branch 'master' of github.com:ebean-orm/ebean 2021-08-15 13:48:43 +12:00
rbygrave e059ad9172 Merge branch 'FOCONIS-mutationdetection-source-special-json-types' 2021-08-15 13:48:27 +12:00
rbygrave 1297324fa9 Fix where using JDK 10 Reader.transferTo() method plus add final to methods and classes 2021-08-15 13:47:46 +12:00
rbygrave e2b7434d98 Merge branch 'mutationdetection-source-special-json-types' of https://github.com/FOCONIS/ebean into FOCONIS-mutationdetection-source-special-json-types 2021-08-15 12:06:12 +12:00
rbygrave ada8df5755 Improve startup logging messages 2021-08-15 12:05:30 +12:00
Roland Praml 843b90cbba FIX: mutation info is not restored on cache hit 2021-08-13 11:52:31 +02:00
Roland Praml 6b263919d5 handle also JsonNode 2021-08-13 11:19:24 +02:00
Roland Praml 965c1e803f FIX: mutationDetection = SOURCE works also for special json types 2021-08-13 10:47:14 +02:00
Rob BygraveandGitHub a13bc481b8 Merge pull request #2306 from FOCONIS/pr/enh/softreference_in_cache
Use SoftReferences in Cache
2021-08-13 09:35:38 +12:00
rbygrave fc7379aabc Tidy internals - server.persist.dml package for whitespace and typos 2021-08-13 09:18:31 +12:00
rbygrave a17683c549 #2307 - Tidy ShutdownManager DatabaseFactory only 2021-08-12 16:32:12 +12:00
rbygrave eeacad0fb9 #2307 - ENH: Add DatabaseFactory.disableShutdownHook() ... [ alias for ShutdownManager.deregisterShutdownHook() ] 2021-08-12 14:36:14 +12:00
rbygrave c51140b59a Add DefaultServer.toString() and tidy DbOffline remove unnecessary log message 2021-08-12 13:01:07 +12:00
Jonas Pöhler f1817a0dbb Merge remote-tracking branch 'ebean/master' into pr/enh/softreference_in_cache 2021-08-11 15:26:42 +02:00
Jonas Pöhler 5b05510bb9 Merge remote-tracking branch 'ebean/master' into pr/enh/softreference_in_cache 2021-08-11 14:08:55 +02:00
rbygrave b676476cd2 Add SpiQuery.Type.defaultSelect() for ATTRIBUTE and sub-query type queries 2021-08-11 20:51:46 +12:00
rbygrave 709f8ed05f Merge branch 'master' of github.com:ebean-orm/ebean 2021-08-11 20:21:26 +12:00
rbygrave 31282416d8 Merge branch 'FOCONIS-subquery-with-fetch-path' 2021-08-11 20:16:33 +12:00
Rob BygraveandGitHub 385c84ae6f Merge pull request #2302 from FOCONIS/feature/switch-db2-driver
Switch DB2 driver to the publicly available JCC driver
2021-08-11 19:43:09 +12:00
Roland Praml 2d24020202 Added / enhanced test case 2021-08-11 09:32:21 +02:00
Roland Praml 1a2aba3f56 Distinguish between exists and in subquery 2021-08-11 08:54:40 +02:00
Thomas Fellner f50f0192d1 Switch DB2 driver to the publicly available JCC driver 2021-08-11 08:47:49 +02:00
Roland Praml 12b8b4b955 Merge remote-tracking branch 'upstream/master' into subquery-with-fetch-path 2021-08-11 07:42:11 +02:00
rbygrave c267366578 Refactor tidy internals - aList -> expr in for loops etc. No functional change. 2021-08-11 15:32:54 +12:00
Rob BygraveandGitHub 18e49cc1ce Merge pull request #2300 from FOCONIS/exists-with-dynamic
Enables select + exists for dynamic formula properties
2021-08-11 14:01:46 +12:00
Rob BygraveandGitHub 1ce4b1a994 Merge pull request #2299 from FOCONIS/push-pops-in-wrong-order
PushJson/PopJson did not work correctly, if null beans were involved
2021-08-11 13:48:00 +12:00
Rob BygraveandGitHub f3c87e488a Merge pull request #2301 from FOCONIS/bugfix/update-ddl-generator-version
Updating ebean-ddl-generator version in ebean-core
2021-08-11 13:46:19 +12:00
Rob BygraveandGitHub f24c698cd5 Merge pull request #2298 from ebean-orm/FOCONIS-md5-for-querybindhash
Foconis md5 for querybindhash
2021-08-11 13:29:44 +12:00
rbygrave 144a3b54bb Refactor rename method queryBindHash() to queryBindKey() 2021-08-11 13:25:04 +12:00
rbygrave ec7c7048db Refactor rename BindHash to BindValuesKey 2021-08-11 13:19:44 +12:00
rbygrave fd2e542c0b Change BindHash from interface to implementation, Delete unused server persist BindValues 2021-08-11 12:43:58 +12:00
rbygrave 11e8e3696f BindParams does not need encryptionKey in equals/hashCode 2021-08-11 12:24:36 +12:00
Thomas Fellner d08f7af1e0 update ebean-ddl-generator version in ebean-core 2021-08-10 18:31:02 +02:00
Roland Praml 60dfaab22c Enables select + exists for dynamic formula properties 2021-08-10 15:29:10 +02:00
Roland Praml 75fa7cb7fd PushJson/PopJson did not work correctly, if null beans were involved 2021-08-10 13:24:13 +02:00
rbygrave e47b1737a8 Add HashCodeBindHashTest 2021-08-10 22:28:31 +12:00
rbygrave e1ad210bb0 Tidy JunctionExpression 2021-08-10 22:18:33 +12:00
rbygrave ac3edbedf4 Remove MdBindHash, tidy DefaultOrmQuery 2021-08-10 22:07:48 +12:00
rbygrave 2dc48cf08d ArrayContainsExpression equals() to use enhanced for loop 2021-08-10 22:00:46 +12:00
rbygrave ede1ad0a4c Fix BindParams equals() and hashCode() 2021-08-10 21:58:41 +12:00
rbygrave 3e8f76cd6d Update HashCodeBindHash to use values for equals() 2021-08-10 21:49:31 +12:00
rbygrave cd6df2be39 Merge branch 'md5-for-querybindhash' of https://github.com/FOCONIS/ebean into FOCONIS-md5-for-querybindhash 2021-08-10 21:18:43 +12:00
Rob BygraveandGitHub 7091e972fc Merge pull request #2297 from FOCONIS/fix-mutable-next-changelog
FIX: Changelog-oldValue did not work for Json mutable properties
2021-08-10 21:10:15 +12:00
Roland Praml c8ae3c5ef6 FIX: Changelog-oldValue did not work for Json mutable properties 2021-08-10 09:30:25 +02:00
Rob BygraveandGitHub 54e8c4df8a Merge pull request #2296 from FOCONIS/fix-test-for-java-8
Fix the test to run with java 8
2021-08-10 17:04:14 +12:00
Roland Praml e18c190905 Fix the test to run with java 8 2021-08-09 09:46:49 +02:00
Roland Praml 181dc73d39 Use MD5 for QueryBindHash 2021-08-09 09:19:53 +02:00
rbygrave 7ed2de9178 Update github issue template 2021-08-09 16:57:43 +12:00
rbygrave 8c101fd0a4 #2268 - Feature request/question: Query execution time for debugging 2021-08-09 16:48:59 +12:00
rbygrave 43213ead05 #2291 - ENH: Explicit option for fast-failing or not fast-failing (skipDataSourceCheck config option) 2021-08-09 15:10:59 +12:00
Rob BygraveandGitHub 0ef5c65c94 Merge pull request #2294 from ebean-orm/feature/metric-hash-2
#2293 - Metric hash back to MD5 of sql + name + loc (minus location file and line source) ~= Revert of #2288
2021-08-09 14:49:17 +12:00
rbygrave c610f95b2d #2293 - Metric hash back to MD5 of sql + name + loc (minus location file and line source) ~= Revert of #2288 2021-08-09 14:05:10 +12:00
rbygrave c4484c758e [maven-release-plugin] prepare for next development iteration 2021-08-06 14:56:15 +12:00
rbygrave ee615836ba [maven-release-plugin] prepare release ebean-parent-12.11.0 2021-08-06 14:56:09 +12:00
rbygrave fea7de42d5 #2290 - Add MetaTimedMetric locationHash() which is a crc32 checksum of package + type + method (excludes line number) 2021-08-06 14:48:42 +12:00
Rob BygraveandGitHub 4787303160 Merge pull request #2289 from ebean-orm/feature/meta-api
Refactor io.ebean.meta API, method rename with deprecation
2021-08-06 13:17:08 +12:00
rbygrave 801d419f51 Refactor io.ebean.meta API, method rename with deprecation 2021-08-06 13:14:29 +12:00
Rob BygraveandGitHub 3579557200 Merge pull request #2288 from ebean-orm/feature/sql-hash
Change query metric hash from MD5 of sql + name + loc to Checksum of sql (+ rename to sqlHash)
2021-08-06 12:52:42 +12:00
rbygrave 202d785710 Rename hash to sqlHash to better reflect what it is (CRC32 checksum of the sql) 2021-08-06 12:06:43 +12:00
rbygrave 7ca72a39b1 Change query metric hash from MD5 of sql + name + loc to Checksum of sql 2021-08-06 11:52:38 +12:00
rbygrave f34e0f5f2b #2287 - ebean-migration: For multiple platforms automatically run the correct migrations 2021-08-05 23:03:02 +12:00
rbygrave d98a31fa6b Bump to 12.11.0-SNAPSHOT 2021-08-05 16:53:51 +12:00
rbygrave 9a82dbb3e6 Bump parent to java8-oss 3.2 with GPG maven plugin active only with "release" profile
The GPG maven plugin is now only active with the "release" profile
use -P release when we want to release with GPG signing
2021-08-05 13:17:32 +12:00
rbygrave a76442641b Tidy test only - rename ServerConfigTest to DatabaseConfigTest and tidy 2021-08-05 13:14:00 +12:00
rbygrave 2043fdf7e1 #2274 Add DatabaseConfig jsonMutationDetection property setting the global default mode 2021-08-05 13:12:18 +12:00
rbygrave a06f7bea41 Tidy javadoc for MutableValueInfo 2021-08-05 12:55:42 +12:00
rbygrave db34afe867 #2284 - ENH: ddl-generator - add optional generation of idx_<platform>.migrations file 2021-08-03 00:03:02 +12:00
rbygrave 589ee7a5ea #2284 - ENH: ddl-generator - add optional generation of idx_<platform>.migrations file 2021-08-02 23:59:49 +12:00
rbygrave 4ba26ee7b7 #2283 - ddl-generator - Refactor tidy DefaultDbMigration etc 2021-08-02 20:49:30 +12:00
rbygrave 71223895ac #2283 - ddl-generator - Refactor tidy whitespace etc 2021-08-02 20:38:05 +12:00
rbygrave 928b90122f #2283 - ddl-generator - Add DbMigration.addPlatform(Platform) with default sub-directory based on Platform 2021-08-02 20:15:51 +12:00
rbygrave d601a88d82 #2278 - Move null to empty json collections check to BeanPropertyJsonBasic 2021-07-30 23:53:28 +12:00
rbygrave 443e826f61 #2278 - Embedded bean override with DbJson property 2021-07-30 23:22:06 +12:00
rbygrave 2691221168 #2278 - Do not override empty json collections, when database value is null 2021-07-30 22:53:51 +12:00
rbygrave e62bb3dc6b Merge branch 'pr/bugfix/null_to_empty_json_collection' of https://github.com/FOCONIS/ebean into FOCONIS-pr/bugfix/null_to_empty_json_collection 2021-07-30 22:51:40 +12:00
Rob BygraveandGitHub 604099429e Merge pull request #2282 from ebean-orm/feature/json-mutable-collections
Treat json Jackson collections the same wrt mutation detection
2021-07-30 21:15:04 +12:00
Rob BygraveandGitHub 11dc7f64ba Merge pull request #2274 from ebean-orm/feature/json-dirty-detection-md5
Add support for JSON bean mutation detection via HASH and SOURCE of JSON string content
2021-07-30 21:11:42 +12:00
rbygrave ceea705a76 Treat json Jackson collections the same wrt mutation detection 2021-07-30 20:20:03 +12:00
rbygrave d9f7531e81 Tidy tests only - TestDbJson_Jackson3 TestJacksonPlainBean 2021-07-30 17:26:46 +12:00
rbygrave 7bd7640c63 Update test TestDbJson_Jackson3 showing HASH mode is used even on ModifyAwareType 2021-07-30 17:18:28 +12:00
rbygrave ca1636ff9e Fix when json/jackson bean inserted as null and not changed
Expectation is that it is not included in update (still null, no change)
2021-07-30 17:16:05 +12:00
rbygrave ea741d0169 Update TestDbJson_Jackson3 with comments 2021-07-30 15:41:52 +12:00
rbygrave c43e2e6956 Update TestDbJson_Jackson3 2021-07-30 15:34:54 +12:00
rbygrave 03004452ac ebean.mf set back to synthetic: true 2021-07-30 14:53:32 +12:00
rbygrave a470877017 Update kotlin-querybean-generator pom to make kotlin in IntelliJ happy 2021-07-30 14:52:49 +12:00
rbygrave 777d322222 #2279 - Remove _ebean_getMarker() from EntityBean interface - not actually required 2021-07-30 14:51:38 +12:00
Jonas Pöhler 1f0ac1f1c1 ADD: Testcases for List and Map as well 2021-07-29 16:50:08 +02:00
Jonas Pöhler (JPo) 1fad6e910c FIX: do not override empty (json) collections with null
Original: https://github.com/ebean-orm/ebean/commit/5551c35ec2496a9d62ab0afcaecbed739f17aa7c
Signed-off-by: Jonas Pöhler (JPo) <jonas.poehler@foconis.de>
2021-07-29 15:53:44 +02:00
rbygrave 822fb7325a Due to mutableNext handling, with checkMutableProperties() even when known dirty go into beanProperty.checkMutable()
As per rPraml's PR and comment

Due to handling of mutableNext we need  checkMutableProperties() to call into what is now beanProperty.checkMutable() even when we already know it's dirty.
2021-07-29 23:37:29 +12:00
rbygrave 20e0eb1021 Re-enable TestDbJson_Jackson3 via adding mutationDetection = HASH to beanList property
@DbJsonB(mutationDetection = HASH)
  List<PlainBean> beanList;
2021-07-29 23:17:11 +12:00
rbygrave 20becf951d Use MutationDetection replacing dirtyDetection and keepSource
Also adds NoMutationDetection to support NONE
2021-07-29 23:11:42 +12:00
rbygrave 5aa8557168 Update tests only - TestJacksonPlainBean with dirtyDetection = false property 2021-07-29 16:29:10 +12:00
rbygrave c66d248e96 Add MutableValueNext to replace mutableContent such that hash compute is only done once
Adds MutableValueInfo.nextDirty() to replace the isEqualToJson() method. The next is computed once and stored. BindablePropertyJsonUpdate makes the .mutableNext(propertyIndex) call to move the next MutableValueInfo and return the json content.
2021-07-29 15:42:16 +12:00
rbygrave 0d8e7c852b Change Checksum to use CRC32 2021-07-29 15:32:06 +12:00
rbygrave 991c6d6b5c No effective change - tidy FactoryProperty 2021-07-29 13:30:15 +12:00
rbygrave 2bc5d3d325 Refactor rename MutableHash to MutableValueInfo (and subsequent rename on methods etc) 2021-07-29 13:20:30 +12:00
rbygrave 5ad8e7ead4 Use new @DbJson dirtyDetection and keepSource attributes
- Bump ebean-annotation with new @DbJson dirtyDetection and keepSource attributes
- Get those to BeanPropertyJsonMapper to chose MutableHash implementation
- Modify MD5MutableHash to include check for isDirty()
- EBasicJsonList needs keepSource=true to pass that test with oldValue
2021-07-29 12:47:10 +12:00
rbygrave 8832bbc57f No effective change - format EntityBeanIntercept only
Plus adjust timing on SqlQueryCancelTest
2021-07-29 12:43:37 +12:00
rbygrave 86483ff947 Use scalarType.format(value) removing scalarType.jsonMapper(value)
Remove scalarType.jsonMapper(value) as we can just use format(value) instead
2021-07-29 11:52:15 +12:00
rbygrave 92d7a7562f Tidy only - no functional change to MutableHash and BeanPropertyJsonMapper 2021-07-29 11:49:01 +12:00
rbygrave 52fe3cf3c8 Change from MD5 to Checksum (Adler32) for json content dirty detection 2021-07-29 11:40:05 +12:00
rbygrave 94fb6414fa Refactor move createMutableHash() method from ScalarType to BeanProperty / BeanPropertyJsonMapper 2021-07-29 11:28:25 +12:00
rbygrave d0270dbc6a Temporarily disable TestDbJson_Jackson3 updateIncludesJsonColumn_when_list_loadedAndNotDirtyAware()
BeanState state = DB.getBeanState(found);
    assertThat(state.getChangedProps()).containsExactlyInAnyOrder("beanList");
    // this test fails, because we have a OmList instead of a GenericObject
    // TODO: Can/Should we enhance the @DbJson/@DbJsonB annotations with a property "dirtyDetection"
2021-07-29 11:21:38 +12:00
rbygrave 11a047bf8e Merge branch 'FOCONIS-feature/json-dirty-detection-md5' into feature/json-dirty-detection-md5 2021-07-29 10:27:28 +12:00
Roland Praml eb70c7e196 some javadoc and refactored getOrigValue 2021-07-28 17:36:53 +02:00
Roland Praml b497b8635b some refactor and tidying 2021-07-28 17:22:29 +02:00
Roland Praml 5e595ec5f8 Proof of concept to support also bean change detection / changelog on mutable properties 2021-07-28 14:54:56 +02:00
rbygrave 49b86d07d3 Modify SqlBeanLoad to use readSet()
Note that we don't need to use readSetIntercept() now as there is no property change listener support
2021-07-28 22:48:29 +12:00
rbygrave 11eed4cfe9 Add BeanPropertyJsonMapper for JSON dirty detection 2021-07-28 21:43:57 +12:00
rbygrave 4aef0ad459 #2275 - Change Query<?> to Query<T> on ExtendedServer exists() method 2021-07-28 17:23:37 +12:00
Rob BygraveandGitHub 21cd74d0ca Merge pull request #2273 from ebean-orm/feature/2272
#2272 Fix for collection with orphanRemoval (such that equals/hashCode isn't use)
2021-07-28 17:14:09 +12:00
rbygrave 6c262f1df5 JSON bean dirty detection via MD5 of JSON string content
- MD5 of json content stored on EntityBeanIntercept for dirty detection
- Only convert to JSON once (at dirty detection time). Store this json content on EntityBeanIntercept to later push to ScalarTypeJsonObjectMapper for bind

Should consider alternative to extend BeanProperty rather than have these if blocks.
2021-07-23 16:50:24 +12:00
rbygrave f32ace4e5a Refactor EntityBeanIntercept replacing owner._ebean_getPropertyNames().length with flags.length 2021-07-23 13:54:29 +12:00
rbygrave e54b7052e0 #2272 Fix for collection with orphanRemoval (such that equals/hashCode isn't use)
- Changes ModifyHolder to use IdentityHashMap such that equals/hashcode isn't invoked
- Change SaveManyBeans such that M2M intersection deletes execute before inserts

The SaveManyBeans change is required to pass TestM2MModifyTest. With that test we get a delete/insert pair that we somewhat expect because we clear() the collection. Previously equals/hashCode meant we didn't get that delete/insert pair.

The test changes from containsExactly() to containsOnly() as we now can't guarantee the ordering with IdentityHashMap.
2021-07-23 12:18:06 +12:00
rbygrave b40d448955 #2272 No effective change, tidy test TestM2MModifyTest only
Noting that this test fails with the fix for 2272 as M2M intersection deletes are currently executed after inserts. We need a fix to SaveManyBeans to change that so that deletes to intersection table execute after inserts.
2021-07-23 12:01:36 +12:00
rbygrave 33bccd3bcb [maven-release-plugin] prepare for next development iteration 2021-07-22 19:03:54 +12:00
Jonas Pöhler 60ef87b269 Merge remote-tracking branch 'ebean/master' into pr/enh/softreference_in_cache 2021-07-16 15:29:33 +02:00
Roland Praml 7e265f7bde FIX: SubQuery with fetch-path does not work correctly 2021-07-13 09:09:30 +02:00
Jonas Pöhler (JPo) bd7499a505 Use SoftReference in Cache, so it can be gc'ed if the jvm runs out of memory
Signed-off-by: Jonas Pöhler (JPo) <jonas.poehler@foconis.de>
2021-03-29 13:23:56 +02:00
408 changed files with 4499 additions and 3343 deletions
-6
View File
@@ -1,9 +1,3 @@
GITHUB ISSUES ARE STRICTLY CONTROLLED FOR THIS PROJECT.
Refer to http://ebean-orm.github.io/support for the policies controlling the use of github issues.
Please post issues to the Ebean group https://groups.google.com/forum/#!forum/ebean first.
## Expected behavior
## Actual behavior
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.10.0</version>
<version>12.11.1</version>
</parent>
<name>ebean api</name>
@@ -50,7 +50,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>7.0</version>
<version>7.2</version>
</dependency>
<dependency>
@@ -16,18 +16,15 @@ import java.util.concurrent.locks.ReentrantLock;
* <p>
* This uses either DatabaseConfig or properties in the application.properties file to
* configure and create a Database instance.
* </p>
* <p>
* The Database instance can either be registered with the DB singleton or
* not. The DB singleton effectively holds a map of Database by a name.
* If the Database is registered with the DB singleton you can retrieve it
* later via {@link DB#byName(String)}.
* </p>
* <p>
* One Database can be nominated as the 'default/primary' Database. Many
* methods on the DB singleton such as {@link DB#find(Class)} are just a
* convenient way of using the 'default/primary' Database.
* </p>
*/
public class DatabaseFactory {
@@ -48,7 +45,7 @@ public class DatabaseFactory {
public static void initialiseContainer(ContainerConfig containerConfig) {
lock.lock();
try {
getContainer(containerConfig);
container(containerConfig);
} finally {
lock.unlock();
}
@@ -60,7 +57,7 @@ public class DatabaseFactory {
public static Database create(String name) {
lock.lock();
try {
return getContainer(null).createServer(name);
return container(null).createServer(name);
} finally {
lock.unlock();
}
@@ -68,6 +65,16 @@ public class DatabaseFactory {
/**
* Create using the DatabaseConfig object to configure the database.
*
* <pre>{@code
*
* DatabaseConfig config = new DatabaseConfig();
* config.setName("db");
* config.loadProperties();
*
* Database database = DatabaseFactory.create(config);
*
* }</pre>
*/
public static Database create(DatabaseConfig config) {
lock.lock();
@@ -115,7 +122,6 @@ public class DatabaseFactory {
* Shutdown gracefully all Database instances cleaning up any resources as required.
* <p>
* This is typically invoked via JVM shutdown hook and not explicitly called.
* </p>
*/
public static void shutdown() {
lock.lock();
@@ -127,15 +133,15 @@ public class DatabaseFactory {
}
private static Database createInternal(DatabaseConfig config) {
return getContainer(config.getContainerConfig()).createServer(config);
return container(config.getContainerConfig()).createServer(config);
}
/**
* Get the EbeanContainer initialising it if necessary.
* Return the SpiContainer initialising it if necessary.
*
* @param containerConfig the configuration controlling clustering communication
*/
private static SpiContainer getContainer(ContainerConfig containerConfig) {
private static SpiContainer container(ContainerConfig containerConfig) {
// thread safe in that all calling methods hold lock
if (container != null) {
return container;
@@ -67,7 +67,7 @@ public interface ExtendedServer {
*
* @return True if the query finds a matching row in the database
*/
<T> boolean exists(Query<?> ormQuery, Transaction transaction);
<T> boolean exists(Query<T> ormQuery, Transaction transaction);
/**
* Return the number of 'top level' or 'root' entities this query should return.
@@ -27,17 +27,6 @@ public interface EntityBean extends Serializable {
throw new NotEnhancedException();
}
/**
* Return the enhancement marker value.
* <p>
* This is the class name of the enhanced class and used to check that all
* entity classes are enhanced (specifically not just a super class).
* </p>
*/
default String _ebean_getMarker() {
throw new NotEnhancedException();
}
/**
* Create and return a new entity bean instance.
*/
@@ -51,6 +51,11 @@ public final class EntityBeanIntercept implements Serializable {
*/
private static final byte FLAG_ORIG_VALUE_SET = 8;
/**
* Flags indicating if the mutable hash is set.
*/
private static final byte FLAG_MUTABLE_HASH_SET = 16;
private transient final ReentrantLock lock = new ReentrantLock();
private transient NodeUsageCollector nodeUsageCollector;
private transient PersistenceContext persistenceContext;
@@ -91,6 +96,17 @@ public final class EntityBeanIntercept implements Serializable {
private Object ownerId;
private int sortOrder;
/**
* Holds information of json loaded jackson beans (e.g. the original json or checksum).
*/
private MutableValueInfo[] mutableInfo;
/**
* Holds json content determined at point of dirty check.
* Stored here on dirty check such that we only convert to json once.
*/
private MutableValueNext[] mutableNext;
/**
* Create a intercept with a given entity.
*/
@@ -228,6 +244,17 @@ public final class EntityBeanIntercept implements Serializable {
* if any embedded beans are either new or dirty (and hence need saving).
*/
public boolean isDirty() {
if (dirty) {
return true;
}
if (mutableInfo != null) {
for (int i = 0; i < mutableInfo.length; i++) {
if (mutableInfo[i] != null && !mutableInfo[i].isEqualToObject(owner._ebean_getField(i))) {
dirty = true;
break;
}
}
}
return dirty;
}
@@ -368,8 +395,18 @@ public final class EntityBeanIntercept implements Serializable {
this.owner._ebean_setEmbeddedLoaded();
this.lazyLoadProperty = -1;
this.origValues = null;
// after save, transfer the mutable next values back to mutable info
if (mutableNext != null) {
for (int i = 0; i < mutableNext.length; i++) {
MutableValueNext next = mutableNext[i];
if (next != null) {
mutableInfo(i, next.info());
}
}
}
this.mutableNext = null;
for (int i = 0; i < flags.length; i++) {
flags[i] &= ~(FLAG_CHANGED_PROP + FLAG_ORIG_VALUE_SET);
flags[i] &= ~(FLAG_CHANGED_PROP | FLAG_ORIG_VALUE_SET);
}
this.dirty = false;
}
@@ -442,6 +479,10 @@ public final class EntityBeanIntercept implements Serializable {
* Return the original value that was changed via an update.
*/
public Object getOrigValue(int propertyIndex) {
if ((flags[propertyIndex] & (FLAG_ORIG_VALUE_SET | FLAG_MUTABLE_HASH_SET)) == FLAG_MUTABLE_HASH_SET) {
// mutable hash set, but not ORIG_VALUE
setOriginalValue(propertyIndex, mutableInfo[propertyIndex].get());
}
if (origValues == null) {
return null;
}
@@ -476,7 +517,7 @@ public final class EntityBeanIntercept implements Serializable {
* Return the number of properties.
*/
public int getPropertyLength() {
return owner._ebean_getPropertyNames().length;
return flags.length;
}
/**
@@ -564,7 +605,7 @@ public final class EntityBeanIntercept implements Serializable {
private void setOriginalValue(int propertyIndex, Object value) {
if (origValues == null) {
origValues = new Object[owner._ebean_getPropertyNames().length];
origValues = new Object[flags.length];
}
if ((flags[propertyIndex] & FLAG_ORIG_VALUE_SET) == 0) {
flags[propertyIndex] |= FLAG_ORIG_VALUE_SET;
@@ -577,7 +618,7 @@ public final class EntityBeanIntercept implements Serializable {
*/
private void setOriginalValueForce(int propertyIndex, Object value) {
if (origValues == null) {
origValues = new Object[owner._ebean_getPropertyNames().length];
origValues = new Object[flags.length];
}
origValues[propertyIndex] = value;
}
@@ -638,7 +679,7 @@ public final class EntityBeanIntercept implements Serializable {
public void addDirtyPropertyNames(Set<String> props, String prefix) {
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
if (isChangedProp(i)) {
// the property has been changed on this bean
props.add((prefix == null ? getProperty(i) : prefix + getProperty(i)));
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
@@ -656,7 +697,7 @@ public final class EntityBeanIntercept implements Serializable {
String[] names = owner._ebean_getPropertyNames();
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
if (isChangedProp(i)) {
if (propertyNames.contains(names[i])) {
return true;
}
@@ -684,7 +725,7 @@ public final class EntityBeanIntercept implements Serializable {
public void addDirtyPropertyValues(Map<String, ValuePair> dirtyValues, String prefix) {
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
if (isChangedProp(i)) {
// the property has been changed on this bean
String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i));
Object newVal = owner._ebean_getField(i);
@@ -706,7 +747,7 @@ public final class EntityBeanIntercept implements Serializable {
public void addDirtyPropertyValues(BeanDiffVisitor visitor) {
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
if (isChangedProp(i)) {
// the property has been changed on this bean
Object newVal = owner._ebean_getField(i);
Object oldVal = getOrigValue(i);
@@ -741,7 +782,7 @@ public final class EntityBeanIntercept implements Serializable {
}
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
if ((flags[i] & FLAG_CHANGED_PROP) != 0) { // we do not check against mutablecontent here.
sb.append(i).append(',');
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
// an embedded property has been changed - recurse
@@ -1112,7 +1153,7 @@ public final class EntityBeanIntercept implements Serializable {
*/
public void setLoadError(int propertyIndex, Exception t) {
if (loadErrors == null) {
loadErrors = new Exception[owner._ebean_getPropertyNames().length];
loadErrors = new Exception[flags.length];
}
loadErrors[propertyIndex] = t;
flags[propertyIndex] |= FLAG_LOADED_PROP;
@@ -1138,4 +1179,60 @@ public final class EntityBeanIntercept implements Serializable {
}
return ret;
}
private boolean isChangedProp(int i) {
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
return true;
} else if (mutableInfo == null || mutableInfo[i] == null || mutableInfo[i].isEqualToObject(owner._ebean_getField(i))) {
return false;
} else {
// mark for change
flags[i] |= FLAG_CHANGED_PROP;
dirty = true; // this makes the bean automatically dirty!
return true;
}
}
/**
* Return the MutableValueInfo for the given property or null.
*/
public MutableValueInfo mutableInfo(int propertyIndex) {
return mutableInfo == null ? null : mutableInfo[propertyIndex];
}
/**
* Set the MutableValueInfo for the given property.
*/
public void mutableInfo(int propertyIndex, MutableValueInfo info) {
if (mutableInfo == null) {
mutableInfo = new MutableValueInfo[flags.length];
}
flags[propertyIndex] |= FLAG_MUTABLE_HASH_SET;
mutableInfo[propertyIndex] = info;
}
/**
* Dirty detection set the next mutable property content and info .
* <p>
* Set here as the mutable property dirty detection is based on json content comparison.
* We only want to perform the json serialisation once so storing it here as part of
* dirty detection so that we can get it back to bind in insert or update etc.
*/
public void mutableNext(int propertyIndex, MutableValueNext next) {
if (mutableNext == null) {
mutableNext = new MutableValueNext[flags.length];
}
mutableNext[propertyIndex] = next;
}
/**
* Update the 'next' mutable info returning the content that was obtained via dirty detection.
*/
public String mutableNext(int propertyIndex) {
if (mutableNext == null) {
return null;
}
return mutableNext[propertyIndex].content();
}
}
@@ -0,0 +1,42 @@
package io.ebean.bean;
/**
* Holds information on mutable values (like plain beans stored as json).
* <p>
* Used internally in EntityBeanIntercept for dirty detection on mutable values.
* Typically, mutation detection is based on a hash/checksum of json content or the
* original json content itself.
* <p>
* Refer to the mapping options {@code @DbJson(mutationDetection)}.
*/
public interface MutableValueInfo {
/**
* Compares the given json returning null if deemed unchanged or returning
* the MutableValueNext to use if deemed dirty/changed.
* <p>
* Returning MutableValueNext allows an implementation based on hash/checksum
* to only perform that computation once.
*
* @return Null if deemed unchanged or the MutableValueNext if deemed changed.
*/
MutableValueNext nextDirty(String json);
/**
* Compares the given object to an internal value.
* <p>
* This is used to support changelog/beanState. The implementation can serialize the
* object into json form and compare it against the original json.
*/
boolean isEqualToObject(Object obj);
/**
* Creates a new instance from the internal json string.
* <p>
* This is used to provide an original/old value for change logging / persist listeners.
* This is only available for properties that have {@code @DbJson(keepSource=true)}.
*/
default Object get() {
return null;
}
}
@@ -0,0 +1,17 @@
package io.ebean.bean;
/**
* Represents a next value to use for mutable content properties (DbJson with jackson beans).
*/
public interface MutableValueNext {
/**
* Return the next content to use. Provided such that we serialise to json once.
*/
String content();
/**
* Return the next MutableValueInfo to use after an update.
*/
MutableValueInfo info();
}
@@ -3,9 +3,7 @@ package io.ebean.common;
import io.ebean.bean.EntityBean;
import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.*;
/**
* Holds sets of additions and deletions from a 'owner' List Set or Map.
@@ -23,19 +21,19 @@ class ModifyHolder<E> implements Serializable {
/**
* Deletions list for manyToMany persistence.
*/
private Set<E> modifyDeletions = new LinkedHashSet<>();
private Map<E,Object> modifyDeletions = new IdentityHashMap<>();
/**
* Additions list for manyToMany persistence.
*/
private Set<E> modifyAdditions = new LinkedHashSet<>();
private Map<E,Object> modifyAdditions = new IdentityHashMap<>();
private boolean touched;
void reset() {
touched = false;
modifyDeletions = new LinkedHashSet<>();
modifyAdditions = new LinkedHashSet<>();
modifyDeletions = new IdentityHashMap<>();
modifyAdditions = new IdentityHashMap<>();
}
/**
@@ -50,51 +48,46 @@ class ModifyHolder<E> implements Serializable {
}
private boolean undoDeletion(E bean) {
return (bean != null) && modifyDeletions.remove(bean);
return (bean != null) && modifyDeletions.remove(bean) != null;
}
void modifyAddition(E bean) {
if (bean != null) {
touched = true;
if (bean instanceof EntityBean) {
((EntityBean) bean)._ebean_getIntercept().setDeletedFromCollection(false);
}
// If it is to delete then just remove the deletion
if (!undoDeletion(bean)) {
// Insert
modifyAdditions.add(bean);
modifyAdditions.put(bean, bean);
}
}
}
private boolean undoAddition(Object bean) {
return (bean != null) && modifyAdditions.remove(bean);
return (bean != null) && modifyAdditions.remove(bean) != null;
}
@SuppressWarnings("unchecked")
void modifyRemoval(Object bean) {
if (bean != null) {
touched = true;
if (bean instanceof EntityBean) {
((EntityBean) bean)._ebean_getIntercept().setDeletedFromCollection(true);
}
// If it is to be added then just remove the addition
if (!undoAddition(bean)) {
modifyDeletions.add((E) bean);
modifyDeletions.put((E) bean, bean);
}
}
}
Set<E> getModifyAdditions() {
return modifyAdditions;
return modifyAdditions.keySet();
}
Set<E> getModifyRemovals() {
return modifyDeletions;
return modifyDeletions.keySet();
}
/**
@@ -7,9 +7,7 @@ import io.ebean.EbeanVersion;
import io.ebean.PersistenceContextScope;
import io.ebean.Query;
import io.ebean.Transaction;
import io.ebean.annotation.Encrypted;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.annotation.*;
import io.ebean.cache.ServerCachePlugin;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbEncrypt;
@@ -194,10 +192,9 @@ public class DatabaseConfig {
private JsonConfig.Include jsonInclude = JsonConfig.Include.ALL;
/**
* When true then by default DbJson beans are assumed to be dirty.
* I believe we want to change this default to false in the future.
* The default mode used for {@code @DbJson} with Jackson ObjectMapper.
*/
private boolean jsonDirtyByDefault = true;
private MutationDetection jsonMutationDetection = MutationDetection.HASH;
/**
* The database platform name. Used to imply a DatabasePlatform to use.
@@ -322,6 +319,8 @@ public class DatabaseConfig {
*/
private ExternalTransactionManager externalTransactionManager;
private boolean skipDataSourceCheck;
/**
* The data source (if programmatically provided).
*/
@@ -744,23 +743,21 @@ public class DatabaseConfig {
}
/**
* Return true if DbJson beans are assumed dirty by default.
* <p>
* That is, when true beans that do not implement ModifyAwareType are by
* default assumed to be dirty and included in updates.
* Return the default MutableDetection to use with {@code @DbJson} using Jackson.
*
* @see DbJson#mutationDetection()
*/
public boolean isJsonDirtyByDefault() {
return jsonDirtyByDefault;
public MutationDetection getJsonMutationDetection() {
return jsonMutationDetection;
}
/**
* Set to false if we want DbJson beans to not be assumed to be dirty.
* <p>
* That is, when true beans that do not implement ModifyAwareType are by
* default assumed to be dirty and included in updates.
* Set the default MutableDetection to use with {@code @DbJson} using Jackson.
*
* @see DbJson#mutationDetection()
*/
public void setJsonDirtyByDefault(boolean jsonDirtyByDefault) {
this.jsonDirtyByDefault = jsonDirtyByDefault;
public void setJsonMutationDetection(MutationDetection jsonMutationDetection) {
this.jsonMutationDetection = jsonMutationDetection;
}
/**
@@ -1656,6 +1653,20 @@ public class DatabaseConfig {
this.autoTuneConfig = autoTuneConfig;
}
/**
* Return true if the startup DataSource check should be skipped.
*/
public boolean skipDataSourceCheck() {
return skipDataSourceCheck;
}
/**
* Set to true to skip the startup DataSource check.
*/
public void setSkipDataSourceCheck(boolean skipDataSourceCheck) {
this.skipDataSourceCheck = skipDataSourceCheck;
}
/**
* Return the DataSource.
*/
@@ -2935,8 +2946,9 @@ public class DatabaseConfig {
jsonInclude = p.getEnum(JsonConfig.Include.class, "jsonInclude", jsonInclude);
jsonDateTime = p.getEnum(JsonConfig.DateTime.class, "jsonDateTime", jsonDateTime);
jsonDate = p.getEnum(JsonConfig.Date.class, "jsonDate", jsonDate);
jsonDirtyByDefault = p.getBoolean("jsonDirtyByDefault", jsonDirtyByDefault);
jsonMutationDetection = p.getEnum(MutationDetection.class, "jsonMutationDetection", jsonMutationDetection);
skipDataSourceCheck = p.getBoolean("skipDataSourceCheck", skipDataSourceCheck);
runMigration = p.getBoolean("migration.run", runMigration);
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
ddlRun = p.getBoolean("ddl.run", ddlRun);
@@ -14,10 +14,9 @@ import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
/**
* Manages the shutdown of the JVM Runtime.
* Manages the shutdown of Ebean.
* <p>
* Makes sure all the resources are shutdown properly and in order.
* </p>
*/
public final class ShutdownManager {
@@ -44,6 +43,9 @@ public final class ShutdownManager {
private ShutdownManager() {
}
/**
* Registers the container (potentially with cluster management).
*/
public static void registerContainer(SpiContainer ebeanContainer) {
container = ebeanContainer;
}
@@ -94,7 +96,7 @@ public final class ShutdownManager {
/**
* Register the shutdown hook with the Runtime.
*/
protected static void registerShutdownHook() {
private static void registerShutdownHook() {
lock.lock();
try {
String value = System.getProperty("ebean.registerShutdownHook");
@@ -123,13 +125,10 @@ public final class ShutdownManager {
// Already run shutdown...
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Shutting down");
}
stopping = true;
deregisterShutdownHook();
String shutdownRunner = System.getProperty("ebean.shutdown.runnable");
@@ -147,7 +146,6 @@ public final class ShutdownManager {
// shutdown cluster networking if active
container.shutdown();
}
// shutdown any registered servers that have not
// already been shutdown manually
for (Database server : databases) {
@@ -158,7 +156,6 @@ public final class ShutdownManager {
ex.printStackTrace();
}
}
if ("true".equalsIgnoreCase(System.getProperty("ebean.datasource.deregisterAllDrivers", "false"))) {
deregisterAllJdbcDrivers();
}
@@ -168,15 +165,15 @@ public final class ShutdownManager {
}
private static void deregisterAllJdbcDrivers() {
// This manually deregisters all JDBC drivers
// This manually de-registers all JDBC drivers
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
try {
logger.info("Deregistering jdbc driver: " + driver);
logger.info("De-registering jdbc driver: " + driver);
DriverManager.deregisterDriver(driver);
} catch (SQLException e) {
logger.error("Error deregistering driver " + driver, e);
logger.error("Error de-registering driver " + driver, e);
}
}
}
@@ -209,6 +206,9 @@ public final class ShutdownManager {
}
private static class ShutdownHook extends Thread {
private ShutdownHook() {
super("EbeanHook");
}
@Override
public void run() {
ShutdownManager.shutdown();
@@ -18,22 +18,22 @@ public abstract class AbstractMetricVisitor implements MetricVisitor {
}
@Override
public boolean isReset() {
public boolean reset() {
return reset;
}
@Override
public boolean isCollectTransactionMetrics() {
public boolean collectTransactionMetrics() {
return collectTransactionMetrics;
}
@Override
public boolean isCollectQueryMetrics() {
public boolean collectQueryMetrics() {
return collectQueryMetrics;
}
@Override
public boolean isCollectL2Metrics() {
public boolean collectL2Metrics() {
return collectL2Metrics;
}
@@ -27,17 +27,17 @@ public class BasicMetricVisitor extends AbstractMetricVisitor implements ServerM
}
@Override
public List<MetaTimedMetric> getTimedMetrics() {
public List<MetaTimedMetric> timedMetrics() {
return timed;
}
@Override
public List<MetaQueryMetric> getQueryMetrics() {
public List<MetaQueryMetric> queryMetrics() {
return query;
}
@Override
public List<MetaCountMetric> getCountMetrics() {
public List<MetaCountMetric> countMetrics() {
return count;
}
@@ -8,6 +8,13 @@ public interface MetaCountMetric extends MetaMetric {
/**
* Return the total count.
*/
long getCount();
long count();
/**
* Migrate to count()
*/
@Deprecated
default long getCount() {
return count();
}
}
@@ -8,6 +8,13 @@ public interface MetaMetric {
/**
* Return the metric name.
*/
String getName();
String name();
/**
* Migrate to name().
*/
@Deprecated
default String getName() {
return name();
}
}
@@ -8,21 +8,45 @@ public interface MetaQueryMetric extends MetaTimedMetric {
/**
* The type of entity or DTO bean.
*/
Class<?> getType();
Class<?> type();
/**
* Migrate to type().
*/
@Deprecated
default Class<?> getType() {
return type();
}
/**
* The label for the query (can be null).
*/
String getLabel();
String label();
/**
* Migrate to label().
*/
@Deprecated
default String getLabel() {
return label();
}
/**
* The actual SQL of the query.
*/
String getSql();
String sql();
/**
* Migrate to sql().
*/
@Deprecated
default String getSql() {
return sql();
}
/**
* Return the hash of the plan.
*/
String getHash();
String hash();
}
@@ -10,45 +10,45 @@ public interface MetaQueryPlan {
/**
* Return the bean type for the query.
*/
Class<?> getBeanType();
Class<?> beanType();
/**
* Return the label of the query.
*/
String getLabel();
String label();
/**
* Return the profile location for the query.
*/
ProfileLocation getProfileLocation();
ProfileLocation profileLocation();
/**
* Return the sql of the query.
*/
String getSql();
String sql();
/**
* Return the hash of the plan.
*/
String getHash();
String hash();
/**
* Return a description of the bind values.
*/
String getBind();
String bind();
/**
* Return the raw plan.
*/
String getPlan();
String plan();
/**
* Return the query execution time associated with the bind values capture.
*/
long getQueryTimeMicros();
long queryTimeMicros();
/**
* Return the total count of times bind capture has occurred.
*/
long getCaptureCount();
long captureCount();
}
@@ -9,27 +9,68 @@ public interface MetaTimedMetric extends MetaMetric {
/**
* Return the metric location if defined.
*/
String getLocation();
String location();
/**
* Migrate to location()
*/
@Deprecated
default String getLocation() {
return location();
}
/**
* Return the total count.
*/
long getCount();
long count();
/**
* Migrate to count()
*/
@Deprecated
default long getCount() {
return count();
}
/**
* Return the total execution time in micros.
*/
long getTotal();
long total();
/**
* Migrate to total()
*/
@Deprecated
default long getTotal() {
return total();
}
/**
* Return the max execution time in micros.
*/
long getMax();
long max();
/**
* Migrate to max()
*/
@Deprecated
default long getMax() {
return max();
}
/**
* Return the mean execution time in micros.
*/
long getMean();
long mean();
/**
* Migrate to mean()
*/
@Deprecated
default long getMean() {
return mean();
}
/**
* Return true if this is the first metrics collection for this query.
@@ -8,22 +8,22 @@ public interface MetricVisitor {
/**
* Return true if the metrics should be reset.
*/
boolean isReset();
boolean reset();
/**
* Return true if we should visit the transaction metrics.
*/
boolean isCollectTransactionMetrics();
boolean collectTransactionMetrics();
/**
* Return true if we should visit the ORM and SQL query metrics.
*/
boolean isCollectQueryMetrics();
boolean collectQueryMetrics();
/**
* Return true if we should visit the L2 cache metrics.
*/
boolean isCollectL2Metrics();
boolean collectL2Metrics();
/**
* Visit has started.
@@ -32,7 +32,7 @@ public class QueryPlanInit {
* Return the query execution time threshold which must be exceeded to initiate
* query plan collection.
*/
public long getThresholdMicros() {
public long thresholdMicros() {
return thresholdMicros;
}
@@ -40,7 +40,7 @@ public class QueryPlanInit {
* Set the query execution time threshold which must be exceeded to initiate
* query plan collection.
*/
public void setThresholdMicros(long thresholdMicros) {
public void thresholdMicros(long thresholdMicros) {
this.thresholdMicros = thresholdMicros;
}
@@ -54,14 +54,14 @@ public class QueryPlanInit {
/**
* Return the specific hashes that we want to collect query plans on.
*/
public Set<String> getHashes() {
public Set<String> hashes() {
return hashes;
}
/**
* Set the specific hashes that we want to collect query plans on.
*/
public void setHashes(Set<String> hashes) {
public void hashes(Set<String> hashes) {
this.hashes = hashes;
}
}
@@ -18,7 +18,7 @@ public class QueryPlanRequest {
* have been around for a while (e.g. 5 mins) and so reasonably represent
* bind values that match the slowest execution for this query plan.
*/
public long getSince() {
public long since() {
return since;
}
@@ -28,14 +28,14 @@ public class QueryPlanRequest {
*
* @param since The minimum age of the bind values capture.
*/
public void setSince(long since) {
public void since(long since) {
this.since = since;
}
/**
* Return the maximum number of plans to capture.
*/
public int getMaxCount() {
public int maxCount() {
return maxCount;
}
@@ -45,7 +45,7 @@ public class QueryPlanRequest {
* Use this to limit how much query plan capturing is done as query
* plan capture is actual database load.
*/
public void setMaxCount(int maxCount) {
public void maxCount(int maxCount) {
this.maxCount = maxCount;
}
@@ -54,7 +54,7 @@ public class QueryPlanRequest {
* <p>
* Query plan collection will stop once this time is exceeded.
*/
public long getMaxTimeMillis() {
public long maxTimeMillis() {
return maxTimeMillis;
}
@@ -65,7 +65,7 @@ public class QueryPlanRequest {
* this to ensure the query plan capture does not use excessive amount
* of time - put too much load on the database.
*/
public void setMaxTimeMillis(long maxTimeMillis) {
public void maxTimeMillis(long maxTimeMillis) {
this.maxTimeMillis = maxTimeMillis;
}
}
@@ -10,16 +10,39 @@ public interface ServerMetrics {
/**
* Return timed metrics for Transactions, labelled SqlQuery, labelled SqlUpdate.
*/
List<MetaTimedMetric> getTimedMetrics();
List<MetaTimedMetric> timedMetrics();
/**
* Migrate to timedMetrics().
*/
@Deprecated
default List<MetaTimedMetric> getTimedMetrics() {
return timedMetrics();
}
/**
* Return the query metrics.
*/
List<MetaQueryMetric> getQueryMetrics();
List<MetaQueryMetric> queryMetrics();
/**
* Migrate to queryMetrics().
*/
@Deprecated
default List<MetaQueryMetric> getQueryMetrics() {
return queryMetrics();
}
/**
* Return the Counter metrics.
*/
List<MetaCountMetric> getCountMetrics();
List<MetaCountMetric> countMetrics();
/**
* Migrate to countMetrics().
*/
@Deprecated
default List<MetaCountMetric> getCountMetrics() {
return countMetrics();
}
}
@@ -8,12 +8,12 @@ import java.util.Comparator;
public interface ServerMetricsAsJson {
/**
* Set to false to exclude profile location and sql.
* Set to false in order to exclude profile location and sql.
*/
ServerMetricsAsJson withExtraAttributes(boolean withLocation);
/**
* Set to false to exclude SQL hash.
* Set to false in order to exclude SQL hash.
*/
ServerMetricsAsJson withHash(boolean withHash);
@@ -32,7 +32,7 @@ public class SortMetric {
@Override
public int compare(MetaCountMetric o1, MetaCountMetric o2) {
return stringCompare(o1.getName(), o2.getName());
return stringCompare(o1.name(), o2.name());
}
}
@@ -43,8 +43,8 @@ public class SortMetric {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
int i = stringCompare(o1.getName(), o2.getName());
return i != 0 ? i : Long.compare(o1.getCount(), o2.getCount());
int i = stringCompare(o1.name(), o2.name());
return i != 0 ? i : Long.compare(o1.count(), o2.count());
}
}
@@ -55,7 +55,7 @@ public class SortMetric {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
return Long.compare(o2.getCount(), o1.getCount());
return Long.compare(o2.count(), o1.count());
}
}
@@ -66,7 +66,7 @@ public class SortMetric {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
return Long.compare(o2.getTotal(), o1.getTotal());
return Long.compare(o2.total(), o1.total());
}
}
@@ -77,7 +77,7 @@ public class SortMetric {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
return Long.compare(o2.getMean(), o1.getMean());
return Long.compare(o2.mean(), o1.mean());
}
}
@@ -88,7 +88,7 @@ public class SortMetric {
@Override
public int compare(MetaTimedMetric o1, MetaTimedMetric o2) {
return Long.compare(o2.getMax(), o1.getMax());
return Long.compare(o2.max(), o1.max());
}
}
}
+3 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.10.0</version>
<version>12.11.1</version>
</parent>
<!-- <parent>-->
<!-- <groupId>org.avaje</groupId>-->
@@ -14,7 +14,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-parent-12.10.0</tag>
<tag>ebean-parent-12.11.1</tag>
</scm>
<name>ebean autotune</name>
@@ -26,7 +26,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
<scope>provided</scope>
</dependency>
@@ -146,7 +146,8 @@ public class BaseQueryTuner {
case ID_LIST:
case UPDATE:
case DELETE:
case SUBQUERY:
case SQ_EXISTS:
case SQ_IN:
return false;
default:
// not using autoTune when explicitly loading the l2 bean cache
+19 -19
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.10.0</version>
<version>12.11.1</version>
</parent>
<name>ebean bom</name>
@@ -14,12 +14,12 @@
<properties>
<ebean-ddl-runner.version>1.0</ebean-ddl-runner.version>
<ebean-migration-auto.version>1.0</ebean-migration-auto.version>
<ebean-migration.version>12.4.0</ebean-migration.version>
<ebean-migration-auto.version>1.1</ebean-migration-auto.version>
<ebean-migration.version>12.11.0</ebean-migration.version>
<ebean-test-docker.version>4.1</ebean-test-docker.version>
<ebean-datasource.version>7.0</ebean-datasource.version>
<ebean-agent.version>12.10.0</ebean-agent.version>
<ebean-maven-plugin.version>12.10.0</ebean-maven-plugin.version>
<ebean-agent.version>12.11.0</ebean-agent.version>
<ebean-maven-plugin.version>12.11.0</ebean-maven-plugin.version>
</properties>
<dependencyManagement>
@@ -81,88 +81,88 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-xml</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-autotune</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-redis</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.10.0</version>
<version>12.11.1</version>
</parent>
<artifactId>ebean-core-type</artifactId>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
</dependency>
<dependency>
@@ -163,4 +163,15 @@ public interface DataBinder {
* Bind an array value.
*/
void setArray(String arrayType, Object[] elements) throws SQLException;
/**
* Push json from dirty detection to be available for binding.
*/
void pushJson(String json);
/**
* Pop json made during dirty detection for scalarType binding.
*/
String popJson();
}
@@ -48,4 +48,14 @@ public interface DataReader {
Object getObject() throws SQLException;
InputStream getBinaryStream() throws SQLException;
/**
* Push json from dirty detection to be available for binding.
*/
void pushJson(String json);
/**
* Pop json made during dirty detection for scalarType binding.
*/
String popJson();
}
@@ -2,6 +2,7 @@ package io.ebean.core.type;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import io.ebean.text.StringFormatter;
import io.ebean.text.StringParser;
@@ -34,6 +35,10 @@ import java.sql.SQLException;
*/
public interface ScalarType<T> extends StringParser, StringFormatter, ScalarDataReader<T> {
default boolean isJsonMapper() {
return false;
}
/**
* Return true if this is a binary type and can not support parse() and format() from/to string.
* This allows Ebean to optimise marshalling types to string.
+12 -21
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.10.0</version>
<version>12.11.1</version>
</parent>
<artifactId>ebean-core</artifactId>
@@ -15,27 +15,18 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-parent-12.10.0</tag>
<tag>ebean-parent-12.11.1</tag>
</scm>
<profiles>
<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>
<!-- https://mvnrepository.com/artifact/com.ibm.db2/jcc -->
<dependency>
<groupId>com.ibm.jdbc</groupId>
<artifactId>db2jcc4</artifactId>
<version>4.23.42</version>
<groupId>com.ibm.db2</groupId>
<artifactId>jcc</artifactId>
<version>11.5.5.0</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -52,13 +43,13 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>classpath-scanner</artifactId>
<version>4.2</version>
<version>6.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration-auto</artifactId>
<version>1.0</version>
<version>1.1</version>
</dependency>
<!-- keep testing in core using ebean-ddl-generator & ebean-migration -->
@@ -72,7 +63,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.9.4-RC1</version>
<version>12.11.0</version>
<scope>test</scope>
</dependency>
@@ -87,19 +78,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.10.0</version>
<version>12.11.1</version>
</dependency>
<dependency>
@@ -4,11 +4,7 @@ import io.ebeaninternal.server.persist.MultiValueWrapper;
import io.ebeaninternal.server.querydefn.NaturalKeyBindParam;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
/**
@@ -54,12 +50,11 @@ public class BindParams implements Serializable {
positionedParameters.clear();
}
public int queryBindHash() {
int hc = namedParameters.hashCode();
for (Param positionedParameter : positionedParameters) {
hc = hc * 92821 + positionedParameter.hashCode();
public void queryBindHash(BindValuesKey key) {
key.add(positionedParameters.size());
for (Param param : positionedParameters) {
param.queryBindHash(key);
}
return hc;
}
/**
@@ -424,7 +419,14 @@ public class BindParams implements Serializable {
@Override
public boolean equals(Object o) {
return o != null && (o == this || (o instanceof Param) && hashCode() == o.hashCode());
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Param param = (Param) o;
return isInParam == param.isInParam && isOutParam == param.isOutParam && type == param.type && Objects.equals(inValue, param.inValue);
}
void queryBindHash(BindValuesKey key) {
key.add(isInParam).add(isOutParam).add(type).add(inValue);
}
/**
@@ -0,0 +1,35 @@
package io.ebeaninternal.api;
import java.util.ArrayList;
import java.util.List;
/**
* BindValues used for L2 query cache key matching.
* <p>
* The equals/hashCode implementation must meet the requirement that the query bind values
* match for L2 query cache hit (given the query plan hash is already a match).
*/
public class BindValuesKey {
private final List<Object> values = new ArrayList<>();
/**
* Add a bind value.
*/
public BindValuesKey add(Object value) {
values.add(value);
return this;
}
@Override
public boolean equals(Object obj) {
return obj instanceof BindValuesKey && ((BindValuesKey) obj).values.equals(values);
}
@Override
public int hashCode() {
return values.hashCode();
}
}
@@ -1,8 +1,6 @@
package io.ebeaninternal.api;
import io.ebean.annotation.Platform;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Helper to indicate that an EbeanServer should come up offline
@@ -10,8 +8,6 @@ import org.slf4j.LoggerFactory;
*/
public class DbOffline {
private static final Logger logger = LoggerFactory.getLogger(DbOffline.class);
private static final String KEY = "ebean.dboffline";
private static boolean generateMigration;
@@ -73,7 +69,6 @@ public class DbOffline {
public static void reset() {
generateMigration = false;
System.clearProperty(KEY);
logger.debug("reset");
}
}
@@ -6,15 +6,14 @@ package io.ebeaninternal.api;
public class HashQuery {
private final CQueryPlanKey planHash;
private final int bindHash;
private final BindValuesKey bindValuesKey;
/**
* Create the HashQuery.
*/
public HashQuery(CQueryPlanKey planHash, int bindHash) {
public HashQuery(CQueryPlanKey planHash, BindValuesKey bindValuesKey) {
this.planHash = planHash;
this.bindHash = bindHash;
this.bindValuesKey = bindValuesKey;
}
@Override
@@ -25,7 +24,7 @@ public class HashQuery {
@Override
public int hashCode() {
int hc = 92821 * planHash.hashCode();
hc = 92821 * hc + bindHash;
hc = 92821 * hc + bindValuesKey.hashCode();
return hc;
}
@@ -37,8 +36,7 @@ public class HashQuery {
if (!(obj instanceof HashQuery)) {
return false;
}
HashQuery e = (HashQuery) obj;
return e.bindHash == bindHash && e.planHash.equals(planHash);
return e.bindValuesKey.equals(bindValuesKey) && e.planHash.equals(planHash);
}
}
@@ -92,9 +92,7 @@ public class LoadBeanRequest extends LoadRequest {
* Return the list of Id values for the beans in the lazy load buffer.
*/
public List<Object> getIdList() {
List<Object> idList = new ArrayList<>();
BeanDescriptor<?> desc = loadBuffer.getBeanDescriptor();
for (EntityBeanIntercept ebi : batch) {
idList.add(desc.getId(ebi.getOwner()));
@@ -106,10 +104,8 @@ public class LoadBeanRequest extends LoadRequest {
* Configure the query for lazy loading execution.
*/
public void configureQuery(SpiQuery<?> query, List<Object> idList) {
query.setMode(SpiQuery.Mode.LAZYLOAD_BEAN);
query.setPersistenceContext(loadBuffer.getPersistenceContext());
String mode = isLazy() ? "+lazy" : "+query";
query.setLoadDescription(mode, getDescription());
@@ -117,9 +113,7 @@ public class LoadBeanRequest extends LoadRequest {
// cascade the batch size (if set) for further lazy loading
query.setLazyLoadBatchSize(getBatchSize());
}
loadBuffer.configureQuery(query, lazyLoadProperty);
if (idList.size() == 1) {
query.where().idEq(idList.get(0));
} else {
@@ -131,19 +125,16 @@ public class LoadBeanRequest extends LoadRequest {
* Load the beans into the L2 cache if that is requested and check for load failures due to deletes.
*/
public void postLoad(List<?> list) {
Set<Object> loadedIds = new HashSet<>();
BeanDescriptor<?> desc = loadBuffer.getBeanDescriptor();
// collect Ids and maybe load bean cache
for (Object aList : list) {
EntityBean loadedBean = (EntityBean) aList;
for (Object bean : list) {
EntityBean loadedBean = (EntityBean) bean;
loadedIds.add(desc.getId(loadedBean));
}
if (isLoadCache()) {
desc.cacheBeanPutAll(list);
}
if (lazyLoadProperty != null) {
for (EntityBeanIntercept ebi : batch) {
// check if the underlying row in DB was deleted. Mark the bean as 'failed' if
@@ -8,6 +8,7 @@ import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.meta.MetricVisitor;
import io.ebeaninternal.api.SpiQuery.Type;
import io.ebeaninternal.server.core.SpiResultSet;
import io.ebeaninternal.server.core.timezone.DataTimeZone;
import io.ebeaninternal.server.deploy.BeanDescriptor;
@@ -149,7 +150,7 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect
/**
* Compile a query.
*/
<T> CQuery<T> compileQuery(Query<T> query, Transaction t);
<T> CQuery<T> compileQuery(Type type, Query<T> query, Transaction t);
/**
* Execute the findId's query but without copying the query.
@@ -54,9 +54,9 @@ public interface SpiExpression extends Expression {
void queryPlanHash(StringBuilder builder);
/**
* Return the hash value for the values that will be bound.
* Build the key for bind values of the query.
*/
int queryBindHash();
void queryBindKey(BindValuesKey key);
/**
* Return true if the expression is the same with respect to bind values.
@@ -84,7 +84,7 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
/**
* Find single attribute.
*/
ATTRIBUTE(FIND_ATTRIBUTE, "findAttribute"),
ATTRIBUTE(FIND_ATTRIBUTE, "findAttribute", false, false),
/**
* Find rowCount.
@@ -92,9 +92,14 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
COUNT(FIND_COUNT, "findCount"),
/**
* A subquery used as part of a where clause.
* A subquery used as part of an exists where clause.
*/
SUBQUERY(FIND_SUBQUERY, "subquery"),
SQ_EXISTS(FIND_SUBQUERY, "sqExists", false, false),
/**
* A subquery used as part of an in where clause.
*/
SQ_IN(FIND_SUBQUERY, "sqIn", false, false),
/**
* Delete query.
@@ -107,17 +112,21 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
UPDATE(FIND_UPDATE, "update", true);
private final boolean update;
private final boolean defaultSelect;
private final String profileEventId;
private final String label;
Type(String profileEventId, String label) {
this(profileEventId, label, false);
this(profileEventId, label, false, true);
}
Type(String profileEventId, String label, boolean update) {
this(profileEventId, label, update, true);
}
Type(String profileEventId, String label, boolean update, boolean defaultSelect) {
this.profileEventId = profileEventId;
this.label = label;
this.update = update;
this.defaultSelect = defaultSelect;
}
/**
@@ -127,6 +136,13 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
return update;
}
/**
* Return true if this allows default select clause.
*/
public boolean defaultSelect() {
return defaultSelect;
}
public String profileEventId() {
return profileEventId;
}
@@ -629,13 +645,11 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
CQueryPlanKey prepare(SpiOrmQueryRequest<T> request);
/**
* Calculate a hash based on the bind values used in the query.
* Build the key for the bind values used in the query (for l2 query cache).
* <p>
* Combined with queryPlanHash() to return getQueryHash (a unique hash for a
* query).
* </p>
* Combined with queryPlanHash() to return queryHash (a unique key for a query).
*/
int queryBindHash();
void queryBindKey(BindValuesKey key);
/**
* Identifies queries that are exactly the same including bind variables.
@@ -18,7 +18,7 @@ public interface SpiQueryPlan {
String getName();
/**
* The hash for the query plan.
* The hash of the sql.
*/
String getHash();
@@ -11,6 +11,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
@@ -37,7 +38,7 @@ public class DefaultServerCache implements ServerCache {
/**
* The underlying map (ConcurrentHashMap or similar)
*/
protected final Map<Object, CacheEntry> map;
protected final Map<Object, SoftReference<CacheEntry>> map;
protected final CountMetric hitCount;
protected final CountMetric missCount;
@@ -199,7 +200,8 @@ public class DefaultServerCache implements ServerCache {
* Get the cache entry - override for query cache to validate dependent tables.
*/
protected CacheEntry getCacheEntry(Object id) {
return map.get(key(id));
final SoftReference<CacheEntry> ref = map.get(key(id));
return ref != null ? ref.get() : null;
}
@Override
@@ -213,7 +215,7 @@ public class DefaultServerCache implements ServerCache {
@Override
public void put(Object id, Object value) {
Object key = key(id);
map.put(key, new CacheEntry(key, value));
map.put(key, new SoftReference<>(new CacheEntry(key, value)));
putCount.increment();
}
@@ -222,8 +224,8 @@ public class DefaultServerCache implements ServerCache {
*/
@Override
public void remove(Object id) {
CacheEntry entry = map.remove(key(id));
if (entry != null) {
SoftReference<CacheEntry> entry = map.remove(key(id));
if (entry != null && entry.get() != null) {
removeCount.increment();
}
}
@@ -266,6 +268,7 @@ public class DefaultServerCache implements ServerCache {
long startNanos = System.nanoTime();
long trimmedByIdle = 0;
long trimmedByGC = 0;
long trimmedByTTL = 0;
long trimmedByLRU = 0;
@@ -274,10 +277,14 @@ public class DefaultServerCache implements ServerCache {
long idleExpireNano = startNanos - TimeUnit.SECONDS.toNanos(maxIdleSecs);
long ttlExpireNano = startNanos - TimeUnit.SECONDS.toNanos(maxSecsToLive);
Iterator<CacheEntry> it = map.values().iterator();
Iterator<SoftReference<CacheEntry>> it = map.values().iterator();
while (it.hasNext()) {
CacheEntry cacheEntry = it.next();
if (maxIdleSecs > 0 && idleExpireNano > cacheEntry.getLastAccessTime()) {
SoftReference<CacheEntry> ref = it.next();
final CacheEntry cacheEntry = ref.get();
if (cacheEntry == null) {
it.remove();
trimmedByGC++;
} else if (maxIdleSecs > 0 && idleExpireNano > cacheEntry.getLastAccessTime()) {
it.remove();
trimmedByIdle++;
@@ -290,27 +297,27 @@ public class DefaultServerCache implements ServerCache {
}
}
if (trimForMaxSize > 0) {
trimmedByLRU = activeList.size() - maxSize;
if (trimmedByLRU > 0) {
// sort into last access time ascending
activeList.sort(BY_LAST_ACCESS);
int trimSize = getTrimSize();
for (int i = trimSize; i < activeList.size(); i++) {
// remove if still in the cache
map.remove(activeList.get(i).getKey());
if (trimForMaxSize > 0 && activeList.size() > maxSize) {
// sort into last access time ascending
activeList.sort(BY_LAST_ACCESS);
int trimSize = getTrimSize();
for (int i = trimSize; i < activeList.size(); i++) {
// remove if still in the cache
if (map.remove(activeList.get(i).getKey()) != null) {
trimmedByLRU++;
}
}
}
evictCount.add(trimmedByIdle);
evictCount.add(trimmedByGC);
evictCount.add(trimmedByTTL);
evictCount.add(trimmedByLRU);
if (logger.isTraceEnabled()) {
long exeMicros = TimeUnit.MICROSECONDS.convert(System.nanoTime() - startNanos, TimeUnit.NANOSECONDS);
logger.trace("Executed trim of cache {} in [{}]millis idle[{}] timeToLive[{}] accessTime[{}]"
, name, exeMicros, trimmedByIdle, trimmedByTTL, trimmedByLRU);
logger.trace("Executed trim of cache {} in [{}]millis idle[{}] timeToLive[{}] accessTime[{}] gc[{}]",
name, exeMicros, trimmedByIdle, trimmedByTTL, trimmedByLRU, trimmedByGC);
}
}
@@ -4,7 +4,9 @@ import io.ebean.cache.QueryCacheEntryValidate;
import io.ebean.cache.ServerCacheConfig;
import io.ebean.cache.ServerCacheOptions;
import io.ebean.config.CurrentTenantProvider;
import io.ebeaninternal.server.cache.DefaultServerCache.CacheEntry;
import java.lang.ref.SoftReference;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -17,13 +19,13 @@ public class DefaultServerCacheConfig {
private int maxSecsToLive;
private int trimFrequency;
private Map<Object, DefaultServerCache.CacheEntry> map;
private Map<Object, SoftReference<CacheEntry>> map;
public DefaultServerCacheConfig(ServerCacheConfig config) {
this(config, new ConcurrentHashMap<>());
}
public DefaultServerCacheConfig(ServerCacheConfig config, Map<Object, DefaultServerCache.CacheEntry> map) {
public DefaultServerCacheConfig(ServerCacheConfig config, Map<Object, SoftReference<CacheEntry>> map) {
this.config = config;
this.map = map;
@@ -50,7 +52,7 @@ public class DefaultServerCacheConfig {
return config.getShortName();
}
public Map<Object, DefaultServerCache.CacheEntry> getMap() {
public Map<Object, SoftReference<CacheEntry>> getMap() {
return map;
}
@@ -3,6 +3,8 @@ package io.ebeaninternal.server.cache;
import io.ebean.cache.QueryCacheEntry;
import io.ebean.cache.QueryCacheEntryValidate;
import java.lang.ref.SoftReference;
/**
* Server cache for query caching.
* <p>
@@ -27,7 +29,8 @@ public class DefaultServerQueryCache extends DefaultServerCache {
@Override
protected CacheEntry getCacheEntry(Object id) {
Object key = key(id);
CacheEntry entry = map.get(key);
final SoftReference<CacheEntry> ref = map.get(key);
CacheEntry entry = ref != null ? ref.get() : null;
if (entry == null) {
return null;
}
@@ -23,25 +23,16 @@ import javax.persistence.PersistenceException;
public abstract class AbstractSqlQueryRequest implements CancelableQuery {
protected final SpiSqlBinding query;
protected final SpiEbeanServer server;
protected SpiTransaction transaction;
private boolean createdTransaction;
protected String sql;
protected ResultSet resultSet;
protected String bindLog = "";
protected PreparedStatement pstmt;
protected long startNano;
private final ReentrantLock lock = new ReentrantLock();
/**
* Create the BeanFindRequest.
*/
@@ -161,7 +152,8 @@ public abstract class AbstractSqlQueryRequest implements CancelableQuery {
this.bindLog = binder.bind(bindParams, pstmt, conn);
}
if (isLogSql()) {
transaction.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ")"));
long micros = (System.nanoTime() - startNano) / 1000L;
transaction.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ") --micros(", micros + ")"));
}
} finally {
lock.unlock();
@@ -33,7 +33,7 @@ public final class BindPadding {
* Extra padding on binding id's in order to get better hit ratio on DB prepared statements / query plans.
*/
static int padding(int size) {
if (size == 1) {
if (size <= 1) {
return 0;
}
if (size <= 5) {
@@ -36,9 +36,8 @@ import java.sql.SQLException;
/**
* Create a DatabasePlatform from the configuration.
* <p>
* Will used platform name or use the meta data from the JDBC driver to
* Will used platform name or use the metadata from the JDBC driver to
* determine the platform automatically.
* </p>
*/
public class DatabasePlatformFactory {
@@ -54,14 +53,12 @@ public class DatabasePlatformFactory {
logger.info("offline platform [{}]", offlinePlatform);
return byDatabaseName(offlinePlatform);
}
if (config.getDatabasePlatformName() != null) {
// choose based on dbName
return byDatabaseName(config.getDatabasePlatformName());
}
if (config.getDataSourceConfig().isOffline()) {
throw new PersistenceException("You must specify a DatabasePlatformName when you are offline");
throw new PersistenceException("DatabasePlatformName must be specified with offline mode");
}
// guess using meta data from driver
return byDataSource(config.getDataSource());
@@ -142,10 +139,10 @@ public class DatabasePlatformFactory {
* Find the platform by the metaData.getDatabaseProductName().
*/
private DatabasePlatform byDatabaseMeta(DatabaseMetaData metaData, Connection connection) throws SQLException {
String dbProductName = metaData.getDatabaseProductName().toLowerCase();
final int majorVersion = metaData.getDatabaseMajorVersion();
final int minorVersion = metaData.getDatabaseMinorVersion();
logger.debug("platform for productName[{}] version[{}.{}]", dbProductName, majorVersion, minorVersion);
if (dbProductName.contains("oracle")) {
return oracleVersion(majorVersion);
@@ -206,7 +203,6 @@ public class DatabasePlatformFactory {
} catch (SQLException e) {
logger.warn("Error running detection query on Postgres", e);
}
if (majorVersion <= 9) {
return new Postgres9Platform();
}
@@ -34,7 +34,7 @@ import java.util.concurrent.locks.ReentrantLock;
*/
public class DefaultContainer implements SpiContainer {
private static final Logger logger = LoggerFactory.getLogger("io.ebean.internal.DefaultContainer");
private static final Logger logger = LoggerFactory.getLogger("io.ebean.DB");
private final ReentrantLock lock = new ReentrantLock();
private final ClusterManager clusterManager;
@@ -77,9 +77,10 @@ public class DefaultContainer implements SpiContainer {
public SpiEbeanServer createServer(DatabaseConfig config) {
lock.lock();
try {
long start = System.currentTimeMillis();
applyConfigServices(config);
setNamingConvention(config);
BootupClasses bootupClasses = getBootupClasses(config);
BootupClasses bootupClasses = bootupClasses(config);
boolean online = true;
if (config.isDocStoreOnly()) {
@@ -101,22 +102,18 @@ public class DefaultContainer implements SpiContainer {
// use a configured DbEncrypt rather than the platform default
config.getDatabasePlatform().setDbEncrypt(config.getDbEncrypt());
}
// inform the NamingConvention of the associated DatabasePlatform
config.getNamingConvention().setDatabasePlatform(config.getDatabasePlatform());
// executor and l2 caching service setup early (used during server construction)
SpiBackgroundExecutor executor = createBackgroundExecutor(config);
InternalConfiguration c = new InternalConfiguration(online, clusterManager, executor, config, bootupClasses);
DefaultServer server = new DefaultServer(c, c.cacheManager());
// generate and run DDL if required
// if there are any other tasks requiring action in their plugins, do them as well
// generate and run DDL if required plus other plugins
if (!DbOffline.isGenerateMigration()) {
startServer(online, server);
}
DbOffline.reset();
logger.info("started database[{}] platform[{}] in {}ms", config.getName(), config.getDatabasePlatform().getPlatform(), System.currentTimeMillis() - start);
return server;
} finally {
lock.unlock();
@@ -161,9 +158,8 @@ public class DefaultContainer implements SpiContainer {
* Get the entities, scalarTypes, Listeners etc combining the class registered
* ones with the already created instances.
*/
private BootupClasses getBootupClasses(DatabaseConfig config) {
BootupClasses bootup = getBootupClasses1(config);
private BootupClasses bootupClasses(DatabaseConfig config) {
BootupClasses bootup = bootupClasses1(config);
bootup.addIdGenerators(config.getIdGenerators());
bootup.addPersistControllers(config.getPersistControllers());
bootup.addPostLoaders(config.getPostLoaders());
@@ -173,7 +169,6 @@ public class DefaultContainer implements SpiContainer {
bootup.addQueryAdapters(config.getQueryAdapters());
bootup.addServerConfigStartup(config.getServerConfigStartupListeners());
bootup.addChangeLogInstances(config);
bootup.runServerConfigStartup(config);
return bootup;
}
@@ -181,14 +176,12 @@ public class DefaultContainer implements SpiContainer {
/**
* Get the class based entities, scalarTypes, Listeners etc.
*/
private BootupClasses getBootupClasses1(DatabaseConfig config) {
private BootupClasses bootupClasses1(DatabaseConfig config) {
List<Class<?>> entityClasses = config.getClasses();
if (config.isDisableClasspathSearch() || (entityClasses != null && !entityClasses.isEmpty())) {
// use classes we explicitly added via configuration
return new BootupClasses(entityClasses);
}
return BootupClassPathSearch.search(config);
}
@@ -205,7 +198,6 @@ public class DefaultContainer implements SpiContainer {
* Set the DatabasePlatform if it has not already been set.
*/
private void setDatabasePlatform(DatabaseConfig config) {
DatabasePlatform platform = config.getDatabasePlatform();
if (platform == null) {
if (config.getTenantMode().isDynamicDataSource()) {
@@ -215,7 +207,6 @@ public class DefaultContainer implements SpiContainer {
platform = new DatabasePlatformFactory().create(config);
config.setDatabasePlatform(platform);
}
logger.info("DatabasePlatform name:{} platform:{}", config.getName(), platform.getName());
platform.configure(config.getPlatformConfig());
}
@@ -255,6 +246,9 @@ public class DefaultContainer implements SpiContainer {
}
throw new RuntimeException("DataSource not set?");
}
if (config.skipDataSourceCheck()) {
return true;
}
try (Connection connection = config.getDataSource().getConnection()) {
if (connection.getAutoCommit()) {
logger.warn("DataSource [{}] has autoCommit defaulting to true!", config.getName());
@@ -18,8 +18,8 @@ class DefaultQueryPlanListener implements QueryPlanListener {
String dbName = capture.getDatabase().getName();
for (MetaQueryPlan plan : capture.getPlans()) {
log.info("queryPlan db:{} label:{} queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}",
dbName, plan.getLabel(), plan.getQueryTimeMicros(), plan.getProfileLocation(),
plan.getSql(), plan.getBind(), plan.getPlan());
dbName, plan.label(), plan.queryTimeMicros(), plan.profileLocation(),
plan.sql(), plan.bind(), plan.plan());
}
}
}
@@ -397,6 +397,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
if (dbSchema != null) {
migrationRunner.setDefaultDbSchema(dbSchema);
}
migrationRunner.setPlatform(config.getDatabasePlatform().getPlatform().base().name().toLowerCase());
migrationRunner.loadProperties(config.getProperties());
migrationRunner.run(config.getDataSource());
}
@@ -415,8 +416,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private void collectQueryPlans() {
QueryPlanRequest request = new QueryPlanRequest();
request.setMaxCount(config.getQueryPlanCaptureMaxCount());
request.setMaxTimeMillis(config.getQueryPlanCaptureMaxTimeMillis());
request.maxCount(config.getQueryPlanCaptureMaxCount());
request.maxTimeMillis(config.getQueryPlanCaptureMaxTimeMillis());
// obtains query explain plans ...
List<MetaQueryPlan> plans = metaInfoManager.queryPlanCollectNow(request);
@@ -489,6 +490,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Override
public String toString() {
return "Database{" + serverName + "}";
}
/**
* Return the server name.
*/
@@ -525,8 +531,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
* Compile a query. Only valid for ORM queries.
*/
@Override
public <T> CQuery<T> compileQuery(Query<T> query, Transaction t) {
SpiOrmQueryRequest<T> qr = createQueryRequest(Type.SUBQUERY, query, t);
public <T> CQuery<T> compileQuery(Type type, Query<T> query, Transaction t) {
SpiOrmQueryRequest<T> qr = createQueryRequest(type, query, t);
OrmQueryRequest<T> orm = (OrmQueryRequest<T>) qr;
return cqueryEngine.buildQuery(orm);
}
@@ -1283,14 +1289,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
@Override
public <T> boolean exists(Query<?> ormQuery, Transaction transaction) {
Query<?> ormQueryCopy = ormQuery.copy();
ormQueryCopy.setMaxRows(1);
public <T> boolean exists(Query<T> ormQuery, Transaction transaction) {
Query<T> ormQueryCopy = ormQuery.copy().setMaxRows(1);
SpiOrmQueryRequest<?> request = createQueryRequest(Type.ID_LIST, ormQueryCopy, transaction);
try {
request.initTransIfRequired();
List<Object> ids = request.findIds();
return !ids.isEmpty();
return !request.findIds().isEmpty();
} finally {
request.endTransIfRequired();
}
@@ -2056,17 +2060,15 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return transactionManager;
}
public void register(BeanPersistController c) {
List<BeanDescriptor<?>> list = beanDescriptorManager.getBeanDescriptorList();
for (BeanDescriptor<?> aList : list) {
aList.register(c);
public void register(BeanPersistController controller) {
for (BeanDescriptor<?> desc : beanDescriptorManager.getBeanDescriptorList()) {
desc.register(controller);
}
}
public void deregister(BeanPersistController c) {
List<BeanDescriptor<?>> list = beanDescriptorManager.getBeanDescriptorList();
for (BeanDescriptor<?> aList : list) {
aList.deregister(c);
for (BeanDescriptor<?> desc : beanDescriptorManager.getBeanDescriptorList()) {
desc.deregister(c);
}
}
@@ -2329,13 +2331,13 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public void visitMetrics(MetricVisitor visitor) {
visitor.visitStart();
if (visitor.isCollectTransactionMetrics()) {
if (visitor.collectTransactionMetrics()) {
transactionManager.visitMetrics(visitor);
}
if (visitor.isCollectL2Metrics()) {
if (visitor.collectL2Metrics()) {
serverCacheManager.visitMetrics(visitor);
}
if (visitor.isCollectQueryMetrics()) {
if (visitor.collectQueryMetrics()) {
beanDescriptorManager.visitMetrics(visitor);
dtoBeanManager.visitMetrics(visitor);
relationalQueryEngine.visitMetrics(visitor);
@@ -2352,7 +2354,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
List<MetaQueryPlan> queryPlanInit(QueryPlanInit initRequest) {
if (initRequest.isAll()) {
queryPlanManager.setDefaultThreshold(initRequest.getThresholdMicros());
queryPlanManager.setDefaultThreshold(initRequest.thresholdMicros());
}
return beanDescriptorManager.queryPlanInit(initRequest);
}
@@ -28,11 +28,8 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
private static final String ENC_PREFIX_UPPER = EncryptAlias.PREFIX.toUpperCase();
private final SpiDtoQuery<T> query;
private final DtoQueryEngine queryEngine;
private DtoQueryPlan plan;
private DataReader dataReader;
DtoQueryRequest(SpiEbeanServer server, DtoQueryEngine engine, SpiDtoQuery<T> query) {
@@ -78,11 +78,11 @@ class DumpMetrics {
out("-- Dumping metrics for " + server.getName() + " -- ");
ServerMetrics serverMetrics = server.getMetaInfoManager().collectMetrics();
for (MetaTimedMetric metric : serverMetrics.getTimedMetrics()) {
for (MetaTimedMetric metric : serverMetrics.timedMetrics()) {
log(metric);
}
List<MetaCountMetric> countMetrics = serverMetrics.getCountMetrics();
List<MetaCountMetric> countMetrics = serverMetrics.countMetrics();
if (!countMetrics.isEmpty()) {
out("\n-- Counters --");
countMetrics.sort(SortMetric.COUNT_NAME);
@@ -91,7 +91,7 @@ class DumpMetrics {
}
}
List<MetaQueryMetric> queryMetrics = serverMetrics.getQueryMetrics();
List<MetaQueryMetric> queryMetrics = serverMetrics.queryMetrics();
if (!queryMetrics.isEmpty()) {
out("\n-- Queries --");
queryMetrics.sort(sortBy);
@@ -104,8 +104,8 @@ class DumpMetrics {
private void logCount(MetaCountMetric metric) {
StringBuilder sb = new StringBuilder();
sb.append(padNameTimed(metric.getName())).append(" ");
sb.append(" count:").append(pad(metric.getCount()));
sb.append(padNameTimed(metric.name())).append(" ");
sb.append(" count:").append(pad(metric.count()));
out(sb.toString());
}
@@ -120,38 +120,38 @@ class DumpMetrics {
appendQueryName(metric, sb);
appendCounters(metric, sb);
if (dumpHash) {
sb.append("\n hash:").append(metric.getHash());
sb.append("\n hash:").append(metric.hash());
}
appendProfileAndSql(metric, sb);
out(sb.toString());
}
private void appendQueryName(MetaQueryMetric metric, StringBuilder sb) {
sb.append("query:").append(padName(metric.getName())).append(" ");
sb.append("query:").append(padName(metric.name())).append(" ");
}
private void appendProfileAndSql(MetaQueryMetric metric, StringBuilder sb) {
String location = metric.getLocation();
String location = metric.location();
if (dumpLoc && location != null) {
sb.append("\n loc:").append(location);
}
if (dumpSql) {
sb.append(" \n\n sql:").append(metric.getSql()).append("\n\n");
sb.append(" \n\n sql:").append(metric.sql()).append("\n\n");
}
}
private void log(MetaTimedMetric metric) {
StringBuilder sb = new StringBuilder();
sb.append(padNameTimed(metric.getName())).append(" ");
sb.append(padNameTimed(metric.name())).append(" ");
appendCounters(metric, sb);
out(sb.toString());
}
private void appendCounters(MetaTimedMetric timedMetric, StringBuilder sb) {
sb.append(" count:").append(pad(timedMetric.getCount()))
.append(" total:").append(pad(timedMetric.getTotal()))
.append(" mean:").append(pad(timedMetric.getMean()))
.append(" max:").append(pad(timedMetric.getMax()));
sb.append(" count:").append(pad(timedMetric.count()))
.append(" total:").append(pad(timedMetric.total()))
.append(" mean:").append(pad(timedMetric.mean()))
.append(" max:").append(pad(timedMetric.max()));
}
private String padName(String name) {
@@ -31,9 +31,9 @@ class DumpMetricsData {
private void collect(ServerMetrics serverMetrics) {
final List<MetaTimedMetric> timedMetrics = serverMetrics.getTimedMetrics();
final List<MetaCountMetric> countMetrics = serverMetrics.getCountMetrics();
final List<MetaQueryMetric> queryMetrics = serverMetrics.getQueryMetrics();
final List<MetaTimedMetric> timedMetrics = serverMetrics.timedMetrics();
final List<MetaCountMetric> countMetrics = serverMetrics.countMetrics();
final List<MetaQueryMetric> queryMetrics = serverMetrics.queryMetrics();
for (MetaTimedMetric metric : timedMetrics) {
add(metric);
@@ -47,7 +47,7 @@ class DumpMetricsData {
}
private MetricData create(MetaMetric metric) {
MetricData data = new MetricData(metric.getName());
MetricData data = new MetricData(metric.name());
list.add(data);
return data;
}
@@ -55,30 +55,30 @@ class DumpMetricsData {
private void add(MetaTimedMetric metric) {
final MetricData data = create(metric);
appendCounters(data, metric);
data.setLoc(metric.getLocation());
data.setLoc(metric.location());
}
private void addCount(MetaCountMetric metric) {
final MetricData data = create(metric);
data.setCount(metric.getCount());
data.setCount(metric.count());
}
private void addQuery(MetaQueryMetric metric) {
final MetricData data = create(metric);
appendCounters(data, metric);
appendLocationAndSql(data, metric);
data.setHash(metric.getHash());
data.setHash(metric.hash());
}
private void appendLocationAndSql(MetricData data, MetaQueryMetric metric) {
data.setLoc(metric.getLocation());
data.setSql(metric.getSql());
data.setLoc(metric.location());
data.setSql(metric.sql());
}
private void appendCounters(MetricData data, MetaTimedMetric timedMetric) {
data.setCount(timedMetric.getCount());
data.setTotal(timedMetric.getTotal());
data.setMean(timedMetric.getMean());
data.setMax(timedMetric.getMax());
data.setCount(timedMetric.count());
data.setTotal(timedMetric.total());
data.setMean(timedMetric.mean());
data.setMax(timedMetric.max());
}
}
@@ -84,11 +84,11 @@ class DumpMetricsJson implements ServerMetricsAsJson {
private void collect(ServerMetrics serverMetrics) {
try {
start();
for (MetaTimedMetric metric : serverMetrics.getTimedMetrics()) {
for (MetaTimedMetric metric : serverMetrics.timedMetrics()) {
logTimed(metric);
}
List<MetaCountMetric> countMetrics = serverMetrics.getCountMetrics();
List<MetaCountMetric> countMetrics = serverMetrics.countMetrics();
if (!countMetrics.isEmpty()) {
if (sortBy != null) {
countMetrics.sort(SortMetric.COUNT_NAME);
@@ -98,7 +98,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
}
}
List<MetaQueryMetric> queryMetrics = serverMetrics.getQueryMetrics();
List<MetaQueryMetric> queryMetrics = serverMetrics.queryMetrics();
if (!queryMetrics.isEmpty()) {
if (sortBy != null) {
queryMetrics.sort(sortBy);
@@ -170,7 +170,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
}
objStart();
key("name");
val(metric.getName());
val(metric.name());
}
private void metricEnd() throws IOException {
@@ -180,7 +180,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
private void logCount(MetaCountMetric metric) throws IOException {
metricStart(metric);
key("count");
val(metric.getCount());
val(metric.count());
metricEnd();
}
@@ -188,7 +188,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
metricStart(metric);
appendTiming(metric);
if (isIncludeDetail(metric)) {
appendExtra("loc", metric.getLocation());
append("loc", metric.location());
}
metricEnd();
}
@@ -197,11 +197,11 @@ class DumpMetricsJson implements ServerMetricsAsJson {
metricStart(metric);
appendTiming(metric);
if (withHash) {
appendExtra("hash", metric.getHash());
append("hash", metric.hash());
}
if (isIncludeDetail(metric)) {
appendExtra("loc", metric.getLocation());
appendExtra("sql", metric.getSql());
append("loc", metric.location());
append("sql", metric.sql());
}
metricEnd();
}
@@ -210,7 +210,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
return includeExtraAttributes == 2 || includeExtraAttributes == 1 && metric.initialCollection();
}
private void appendExtra(String key, String val) throws IOException {
private void append(String key, String val) throws IOException {
if (val != null) {
key(key);
val(val);
@@ -218,13 +218,14 @@ class DumpMetricsJson implements ServerMetricsAsJson {
}
private void appendTiming(MetaTimedMetric timedMetric) throws IOException {
key("count");
val(timedMetric.getCount());
key("total");
val(timedMetric.getTotal());
key("mean");
val(timedMetric.getMean());
key("max");
val(timedMetric.getMax());
append("count", timedMetric.count());
append("total", timedMetric.total());
append("mean", timedMetric.mean());
append("max", timedMetric.max());
}
private void append(String key, long value) throws IOException {
key(key);
val(value);
}
}
@@ -18,11 +18,8 @@ import java.util.function.Predicate;
public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
private final RelationalQueryEngine queryEngine;
private String[] propertyNames;
private int estimateCapacity;
private int rows;
/**
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.core.bootup;
import io.avaje.classpath.scanner.ClassFilter;
import io.ebean.annotation.DocStore;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.IdGenerator;
@@ -27,27 +26,23 @@ import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
/**
* Interesting classes for a EbeanServer such as Embeddable, Entity,
* ScalarTypes, Finders, Listeners and Controllers.
*/
public class BootupClasses implements ClassFilter {
public class BootupClasses implements Predicate<Class<?>> {
private static final Logger logger = LoggerFactory.getLogger(BootupClasses.class);
private final List<Class<?>> embeddableList = new ArrayList<>();
private final List<Class<?>> entityList = new ArrayList<>();
private final List<Class<? extends ScalarType<?>>> scalarTypeList = new ArrayList<>();
private final List<Class<? extends ScalarTypeConverter<?, ?>>> scalarConverterList = new ArrayList<>();
private final List<Class<? extends AttributeConverter<?, ?>>> attributeConverterList = new ArrayList<>();
// The following objects are instantiated on first request
@@ -55,19 +50,12 @@ public class BootupClasses implements ClassFilter {
// instance list, that holds the instance. Once a class is instantiated
// (or added) it will get removed from the candidate list
private final List<Class<? extends IdGenerator>> idGeneratorCandidates = new ArrayList<>();
private final List<Class<? extends BeanPersistController>> beanPersistControllerCandidates = new ArrayList<>();
private final List<Class<? extends BeanPostLoad>> beanPostLoadCandidates = new ArrayList<>();
private final List<Class<? extends BeanPostConstructListener>> beanPostConstructListenerCandidates = new ArrayList<>();
private final List<Class<? extends BeanFindController>> beanFindControllerCandidates = new ArrayList<>();
private final List<Class<? extends BeanPersistListener>> beanPersistListenerCandidates = new ArrayList<>();
private final List<Class<? extends BeanQueryAdapter>> beanQueryAdapterCandidates = new ArrayList<>();
private final List<Class<? extends ServerConfigStartup>> serverConfigStartupCandidates = new ArrayList<>();
private final List<IdGenerator> idGeneratorInstances = new ArrayList<>();
@@ -98,7 +86,7 @@ public class BootupClasses implements ClassFilter {
public BootupClasses(List<Class<?>> list) {
if (list != null) {
for (Class<?> cls : list) {
isMatch(cls);
test(cls);
}
}
}
@@ -188,13 +176,11 @@ public class BootupClasses implements ClassFilter {
}
public void addChangeLogInstances(DatabaseConfig config) {
readAuditPrepare = config.getReadAuditPrepare();
readAuditLogger = config.getReadAuditLogger();
changeLogPrepare = config.getChangeLogPrepare();
changeLogListener = config.getChangeLogListener();
changeLogRegister = config.getChangeLogRegister();
// if not already set create the implementations found
// via classpath scanning
if (readAuditPrepare == null && readAuditPrepareClass != null) {
@@ -341,18 +327,14 @@ public class BootupClasses implements ClassFilter {
}
@Override
public boolean isMatch(Class<?> cls) {
public boolean test(Class<?> cls) {
if (isEmbeddable(cls)) {
embeddableList.add(cls);
} else if (isEntity(cls)) {
entityList.add(cls);
} else {
return isInterestingInterface(cls);
}
return true;
}
@@ -364,7 +346,6 @@ public class BootupClasses implements ClassFilter {
*/
@SuppressWarnings("unchecked")
private boolean isInterestingInterface(Class<?> cls) {
if (Modifier.isAbstract(cls.getModifiers())) {
// do not include abstract classes as we can
// not instantiate them
@@ -1559,7 +1559,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
void queryPlanInit(QueryPlanInit request, List<MetaQueryPlan> list) {
for (CQueryPlan queryPlan : queryPlanCache.values()) {
if (request.includeHash(queryPlan.getHash())) {
queryPlan.queryPlanInit(request.getThresholdMicros());
queryPlan.queryPlanInit(request.thresholdMicros());
list.add(queryPlan.createMeta(null, null));
}
}
@@ -1572,7 +1572,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
iudMetrics.visit(visitor);
for (CQueryPlan queryPlan : queryPlanCache.values()) {
if (!queryPlan.isEmptyStats()) {
visitor.visitQuery(queryPlan.getSnapshot(visitor.isReset()));
visitor.visitQuery(queryPlan.getSnapshot(visitor.reset()));
}
}
}
@@ -2022,7 +2022,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
public boolean isTableManaged(String tableName) {
return owner.isTableManaged(tableName);
}
/**
* Return the order column property.
*/
@@ -2519,7 +2519,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
if (propName.indexOf('(') > -1) {
return findSqlTreeFormula(propName, path);
}
return _findBeanProperty(propName);
return findProperty(propName);
}
/**
@@ -3198,9 +3198,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
public void checkMutableProperties(EntityBeanIntercept ebi) {
for (BeanProperty beanProperty : propertiesMutable) {
int propertyIndex = beanProperty.getPropertyIndex();
if (!ebi.isDirtyProperty(propertyIndex) && ebi.isLoadedProperty(propertyIndex)) {
if (ebi.isLoadedProperty(propertyIndex)) {
Object value = beanProperty.getValue(ebi.getOwner());
if (value != null && beanProperty.isDirtyValue(value)) {
if (beanProperty.checkMutable(value, ebi.isDirtyProperty(propertyIndex), ebi)) {
// mutable scalar value which is considered dirty so mark
// it as such so that it is included in an update
ebi.markPropertyAsChanged(propertyIndex);
@@ -42,11 +42,7 @@ class BeanEmbeddedMetaFactory {
int dbScale = dbScale(column, sourceProperties[i]);
String colDefn = getDbColumnDefn(column, sourceProperties[i]);
BeanPropertyOverride overrides = new BeanPropertyOverride(dbColumn, dbNullable, dbLength, dbScale, colDefn);
if (sourceProperties[i] instanceof BeanPropertyAssocOne) {
embeddedProperties[i] = new BeanPropertyAssocOne((BeanPropertyAssocOne)sourceProperties[i], overrides);
} else {
embeddedProperties[i] = new BeanProperty(sourceProperties[i], overrides);
}
embeddedProperties[i] = sourceProperties[i].override(overrides);
}
return new BeanEmbeddedMeta(embeddedProperties);
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonToken;
import io.ebean.ValuePair;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.MutableValueInfo;
import io.ebean.bean.PersistenceContext;
import io.ebean.config.EncryptKey;
import io.ebean.config.dbplatform.DbEncryptFunction;
@@ -13,6 +14,7 @@ import io.ebean.core.type.DocPropertyType;
import io.ebean.core.type.ScalarType;
import io.ebean.plugin.Property;
import io.ebean.text.StringParser;
import io.ebean.text.TextException;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.api.SpiQuery;
@@ -31,11 +33,7 @@ import io.ebeaninternal.server.properties.BeanPropertySetter;
import io.ebeaninternal.server.query.STreeProperty;
import io.ebeaninternal.server.query.SqlBeanLoad;
import io.ebeaninternal.server.query.SqlJoinType;
import io.ebeaninternal.server.type.DataBind;
import io.ebeaninternal.server.type.LocalEncryptedType;
import io.ebeaninternal.server.type.ScalarTypeBoolean;
import io.ebeaninternal.server.type.ScalarTypeEnum;
import io.ebeaninternal.server.type.ScalarTypeLogicalType;
import io.ebeaninternal.server.type.*;
import io.ebeaninternal.util.ValueUtil;
import io.ebeanservice.docstore.api.mapping.DocMappingBuilder;
import io.ebeanservice.docstore.api.mapping.DocPropertyMapping;
@@ -52,6 +50,7 @@ import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -366,13 +365,14 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
return InternString.intern(s);
}
public BeanProperty override(BeanPropertyOverride override) {
return new BeanProperty(this, override);
}
/**
* Create a Matching BeanProperty with some attributes overridden.
* <p>
* Primarily for supporting Embedded beans with overridden dbColumn
* mappings.
* Create a Matching BeanProperty with some attributes overridden for Embedded beans.
*/
public BeanProperty(BeanProperty source, BeanPropertyOverride override) {
protected BeanProperty(BeanProperty source, BeanPropertyOverride override) {
this.descriptor = source.descriptor;
this.propertyIndex = source.propertyIndex;
this.name = source.getName();
@@ -642,15 +642,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
}
public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException {
try {
Object value = scalarType.read(ctx.getDataReader());
if (bean != null) {
setValue(bean, value);
}
return value;
} catch (Exception e) {
throw new PersistenceException("Error readSet on " + descriptor + "." + name, e);
}
return readSet(ctx.getDataReader(), bean);
}
@SuppressWarnings("unchecked")
@@ -828,6 +820,13 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
return scalarType.parse(value);
}
/**
* creates a mutableHash for the given JSON value.
*/
public MutableValueInfo createMutableInfo(String json) {
throw new UnsupportedOperationException();
}
/**
* Read the value for this property from L2 cache entry and set it to the bean.
* <p>
@@ -909,7 +908,8 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
/**
* Return the name of the property.
*/
@Override @Nonnull
@Override
@Nonnull
public String getName() {
return name;
}
@@ -1018,8 +1018,8 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
* Return true if the mutable value is considered dirty.
* This is only used for 'mutable' scalar types like hstore etc.
*/
boolean isDirtyValue(Object value) {
return scalarType.isDirty(value);
boolean checkMutable(Object value, boolean alreadyDirty, EntityBeanIntercept ebi) {
return alreadyDirty || value != null && scalarType.isDirty(value);
}
/**
@@ -1281,7 +1281,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
@Override
public Object localEncrypt(Object value) {
return ((LocalEncryptedType)scalarType).localEncrypt(value);
return ((LocalEncryptedType) scalarType).localEncrypt(value);
}
/**
@@ -1394,7 +1394,8 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
/**
* Return the property type.
*/
@Override @Nonnull
@Override
@Nonnull
public Class<?> getPropertyType() {
return propertyType;
}
@@ -94,10 +94,12 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
}
}
/**
* Copy constructor for ManyToOne inside Embeddable.
*/
public BeanPropertyAssocOne(BeanPropertyAssocOne source, BeanPropertyOverride override) {
@Override
public BeanPropertyAssocOne<?> override(BeanPropertyOverride override) {
return new BeanPropertyAssocOne<>(this, override);
}
protected BeanPropertyAssocOne(BeanPropertyAssocOne<T> source, BeanPropertyOverride override) {
super(source, override);
primaryKeyExport = source.primaryKeyExport;
primaryKeyJoin = source.primaryKeyJoin;
@@ -255,12 +257,8 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
}
private SqlUpdate deleteByParentIdList(List<Object> parentIds) {
StringBuilder sb = new StringBuilder(100);
sb.append(deleteByParentIdInSql);
sb.append(targetIdBinder.getIdInValueExpr(false, parentIds.size()));
DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString());
String sql = deleteByParentIdInSql + targetIdBinder.getIdInValueExpr(false, parentIds.size());
DefaultSqlUpdate delete = new DefaultSqlUpdate(sql);
bindParentIds(delete, parentIds);
return delete;
}
@@ -501,7 +499,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
return targetDescriptor.getIdProperty();
}
ScalarType getIdScalarType() {
ScalarType<?> getIdScalarType() {
return targetDescriptor.getIdProperty().scalarType;
}
@@ -0,0 +1,58 @@
package io.ebeaninternal.server.deploy;
import io.ebean.bean.EntityBean;
import io.ebean.core.type.DataReader;
import io.ebean.text.TextException;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import javax.persistence.PersistenceException;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Map;
/**
* A DbJson property that does not use Jackson ObjectMapper.
*/
public class BeanPropertyJsonBasic extends BeanProperty {
public BeanPropertyJsonBasic(BeanDescriptor<?> descriptor, DeployBeanProperty deploy) {
super(descriptor, deploy);
}
protected BeanPropertyJsonBasic(BeanProperty source, BeanPropertyOverride override) {
super(source, override);
}
@Override
public BeanProperty override(BeanPropertyOverride override) {
return new BeanPropertyJsonBasic(this, override);
}
protected Object checkForEmpty(EntityBean bean) {
final Object value = getValue(bean);
if (value instanceof Collection && ((Collection<?>) value).isEmpty()
|| value instanceof Map && ((Map<?, ?>) value).isEmpty()) {
return value;
}
return null;
}
@Override
public Object readSet(DataReader reader, EntityBean bean) throws SQLException {
try {
Object value = scalarType.read(reader);
if (value == null) {
value = checkForEmpty(bean);
}
if (bean != null) {
setValue(bean, value);
}
return value;
} catch (TextException e) {
throw e;
} catch (Exception e) {
throw new PersistenceException("Error readSet on " + descriptor + "." + name, e);
}
}
}
@@ -0,0 +1,222 @@
package io.ebeaninternal.server.deploy;
import io.ebean.annotation.MutationDetection;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.MutableValueInfo;
import io.ebean.bean.MutableValueNext;
import io.ebean.bean.PersistenceContext;
import io.ebean.core.type.DataReader;
import io.ebean.core.type.ScalarType;
import io.ebean.text.TextException;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import io.ebeaninternal.server.util.Checksum;
import javax.persistence.PersistenceException;
import java.sql.SQLException;
import java.util.Objects;
/**
* Handle json property with MutationDetection of SOURCE or HASH only.
*/
public final class BeanPropertyJsonMapper extends BeanPropertyJsonBasic {
private final boolean sourceDetection;
public BeanPropertyJsonMapper(BeanDescriptor<?> desc, DeployBeanProperty deployProp) {
super(desc, deployProp);
this.sourceDetection = deployProp.getMutationDetection() == MutationDetection.SOURCE;
}
private BeanPropertyJsonMapper(BeanPropertyJsonMapper source, BeanPropertyOverride override) {
super(source, override);
this.sourceDetection = source.sourceDetection;
}
@Override
public BeanProperty override(BeanPropertyOverride override) {
return new BeanPropertyJsonMapper(this, override);
}
@Override
public MutableValueInfo createMutableInfo(String json) {
if (sourceDetection) {
return new SourceMutableValue(scalarType, json);
} else {
return new ChecksumMutableValue(scalarType, json);
}
}
/**
* Next when no prior MutableValueInfo.
*/
private MutableValueNext next(String json) {
if (sourceDetection) {
return new SourceMutableValue(scalarType, json);
} else {
return new NextPair(json, new ChecksumMutableValue(scalarType, json));
}
}
/**
* Return true if the json property is considered dirty.
*/
@Override
boolean checkMutable(Object value, boolean alreadyDirty, EntityBeanIntercept ebi) {
// mutation detection based on json content or checksum of json content
// only perform serialisation to json once
final String json = scalarType.format(value);
final MutableValueInfo oldHash = ebi.mutableInfo(propertyIndex);
if (oldHash == null) {
if (value == null) {
return false; // no change, still null
}
ebi.mutableNext(propertyIndex, next(json));
return true;
}
// only perform compute of checksum/hash once (if checksum based)
final MutableValueNext next = oldHash.nextDirty(json);
if (next != null) {
ebi.mutableNext(propertyIndex, next);
return true;
}
return false;
}
@Override
public Object readSet(DataReader reader, EntityBean bean) throws SQLException {
try {
Object value = scalarType.read(reader);
if (value == null) {
value = checkForEmpty(bean);
}
if (bean != null) {
setValue(bean, value);
String json = reader.popJson();
if (json != null) {
final MutableValueInfo hash = createMutableInfo(json);
bean._ebean_getIntercept().mutableInfo(propertyIndex, hash);
}
}
return value;
} catch (TextException e) {
throw e;
} catch (Exception e) {
throw new PersistenceException("Error readSet on " + descriptor + "." + name, e);
}
}
@Override
public void setCacheDataValue(EntityBean bean, Object cacheData, PersistenceContext context) {
if (cacheData instanceof String) {
// parse back from string to support optimisation of java object serialisation
final String jsonContent = (String) cacheData;
final MutableValueInfo hash = createMutableInfo(jsonContent);
bean._ebean_getIntercept().mutableInfo(propertyIndex, hash);
cacheData = scalarType.parse(jsonContent);
}
setValue(bean, cacheData);
}
private static final class NextPair implements MutableValueNext {
private final String json;
private final MutableValueInfo next;
NextPair(String json, MutableValueInfo next) {
this.json = json;
this.next = next;
}
@Override
public String content() {
return json;
}
@Override
public MutableValueInfo info() {
return next;
}
}
/**
* Hold checksum of json source content to use for dirty detection.
* <p>
* Does not support rebuilding 'oldValue' as no original json content.
*/
private static final class ChecksumMutableValue implements MutableValueInfo {
private final ScalarType<?> parent;
private final long checksum;
ChecksumMutableValue(ScalarType<?> parent, String json) {
this.parent = parent;
this.checksum = Checksum.checksum(json);
}
/**
* Create with pre-computed checksum.
*/
ChecksumMutableValue(ScalarType<?> parent, long checksum) {
this.parent = parent;
this.checksum = checksum;
}
@Override
public MutableValueNext nextDirty(String json) {
final long nextChecksum = Checksum.checksum(json);
return nextChecksum == checksum ? null : new NextPair(json, new ChecksumMutableValue(parent, nextChecksum));
}
@Override
public boolean isEqualToObject(Object obj) {
return Checksum.checksum(parent.format(obj)) == checksum;
}
@Override
public Object get() {
return null; // cannot create object from json
}
}
/**
* Hold json source content. This supports rebuilding the 'oldValue'.
*/
private static final class SourceMutableValue implements MutableValueInfo, MutableValueNext {
private final String originalJson;
private final ScalarType<?> parent;
SourceMutableValue(ScalarType<?> parent, String json) {
this.parent = parent;
this.originalJson = json;
}
@Override
public MutableValueNext nextDirty(String json) {
return Objects.equals(originalJson, json) ? null : new SourceMutableValue(parent, json);
}
@Override
public boolean isEqualToObject(Object obj) {
return Objects.equals(originalJson, parent.format(obj));
}
@Override
public Object get() {
// rebuild the 'oldValue' for change log etc
return parent.parse(originalJson);
}
@Override
public String content() {
return originalJson;
}
@Override
public MutableValueInfo info() {
return this;
}
}
}
@@ -28,11 +28,6 @@ class ElementEntityBean implements EntityBean {
return properties[pos];
}
@Override
public String _ebean_getMarker() {
return null;
}
@Override
public Object _ebean_newInstance() {
return new ElementEntityBean(properties);
@@ -702,13 +702,11 @@ public class DeployBeanDescriptor<T> {
}
public void sortProperties() {
ArrayList<DeployBeanProperty> list = new ArrayList<>(propMap.values());
list.sort(PROP_ORDER);
propMap = new LinkedHashMap<>(list.size());
for (DeployBeanProperty aList : list) {
addBeanProperty(aList);
for (DeployBeanProperty property : list) {
addBeanProperty(property);
}
}
@@ -1,18 +1,6 @@
package io.ebeaninternal.server.deploy.meta;
import io.ebean.annotation.CreatedTimestamp;
import io.ebean.annotation.DocCode;
import io.ebean.annotation.DocProperty;
import io.ebean.annotation.DocSortable;
import io.ebean.annotation.Formula;
import io.ebean.annotation.Platform;
import io.ebean.annotation.SoftDelete;
import io.ebean.annotation.UpdatedTimestamp;
import io.ebean.annotation.WhenCreated;
import io.ebean.annotation.WhenModified;
import io.ebean.annotation.Where;
import io.ebean.annotation.WhoCreated;
import io.ebean.annotation.WhoModified;
import io.ebean.annotation.*;
import io.ebean.config.ScalarTypeConverter;
import io.ebean.config.dbplatform.DbDefaultValue;
import io.ebean.config.dbplatform.DbEncrypt;
@@ -39,7 +27,6 @@ import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -109,6 +96,7 @@ public class DeployBeanProperty {
private boolean jsonSerialize = true;
private boolean jsonDeserialize = true;
private MutationDetection mutationDetection;
private boolean dbEncrypted;
private DbEncryptFunction dbEncryptFunction;
@@ -327,6 +315,14 @@ public class DeployBeanProperty {
this.jsonDeserialize = jsonDeserialize;
}
public MutationDetection getMutationDetection() {
return mutationDetection;
}
public void setMutationDetection(MutationDetection dirtyDetection) {
this.mutationDetection = dirtyDetection;
}
/**
* Return the sortOrder for the properties.
*/
@@ -1201,4 +1197,11 @@ public class DeployBeanProperty {
return false;
}
boolean isJsonMapper() {
return scalarType != null && scalarType.isJsonMapper();
}
boolean isJsonType() {
return mutationDetection != null;
}
}
@@ -1,15 +1,7 @@
package io.ebeaninternal.server.deploy.meta;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanDescriptorMap;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.BeanPropertyIdClass;
import io.ebeaninternal.server.deploy.BeanPropertyOrderColumn;
import io.ebeaninternal.server.deploy.BeanPropertySimpleCollection;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.*;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import io.ebeaninternal.server.properties.BeanPropertySetter;
import io.ebeaninternal.server.type.ScalarTypeString;
@@ -30,47 +22,28 @@ public class DeployBeanPropertyLists {
private static final NoopSetter NOOP_SETTER = new NoopSetter();
private BeanProperty versionProperty;
private BeanProperty unmappedJson;
private BeanProperty draft;
private BeanProperty draftDirty;
private BeanProperty tenant;
private final BeanDescriptor<?> desc;
private final LinkedHashMap<String, BeanProperty> propertyMap;
private BeanProperty id;
private final List<BeanProperty> local = new ArrayList<>();
private final List<BeanProperty> mutable = new ArrayList<>();
private final List<BeanPropertyAssocMany<?>> manys = new ArrayList<>();
private final List<BeanProperty> nonManys = new ArrayList<>();
private final List<BeanProperty> aggs = new ArrayList<>();
private final List<BeanPropertyAssocOne<?>> ones = new ArrayList<>();
private final List<BeanPropertyAssocOne<?>> onesImported = new ArrayList<>();
private final List<BeanPropertyAssocOne<?>> embedded = new ArrayList<>();
private final List<BeanProperty> baseScalar = new ArrayList<>();
private final List<BeanProperty> transients = new ArrayList<>();
private final List<BeanProperty> nonTransients = new ArrayList<>();
private final BeanPropertyAssocOne<?> unidirectional;
private final BeanProperty orderColumn;
@SuppressWarnings({"unchecked"})
public DeployBeanPropertyLists(BeanDescriptorMap owner, BeanDescriptor<?> desc, DeployBeanDescriptor<?> deploy) {
this.desc = desc;
@@ -86,7 +59,7 @@ public class DeployBeanPropertyLists {
this.orderColumn = deployOrderColumn != null ? new BeanPropertyOrderColumn(desc, deployOrderColumn) : null;
DeployBeanPropertyAssocOne<?> deployUnidirectional = deploy.getUnidirectional();
this.unidirectional = deployUnidirectional == null ? null : new BeanPropertyAssocOne(owner, desc, deployUnidirectional);
this.unidirectional = deployUnidirectional == null ? null : new BeanPropertyAssocOne<>(owner, desc, deployUnidirectional);
this.propertyMap = new LinkedHashMap<>();
@@ -127,7 +100,7 @@ public class DeployBeanPropertyLists {
}
if (orderColumn != null) {
orderColumn.setDeployOrder(order++);
orderColumn.setDeployOrder(order);
allocateToList(orderColumn);
propertyMap.put(orderColumn.getName(), orderColumn);
}
@@ -154,7 +127,6 @@ public class DeployBeanPropertyLists {
}
private void setImportedPrimaryKeysFor(DeployBeanDescriptor<?> deploy, DeployBeanPropertyAssocOne<?> id) {
for (DeployBeanProperty prop : id.getTargetDeploy().properties()) {
DeployBeanProperty match = findImported(deploy, prop);
if (match != null) {
@@ -164,7 +136,6 @@ public class DeployBeanPropertyLists {
}
private DeployBeanProperty findImported(DeployBeanDescriptor<?> deploy, DeployBeanProperty embeddedScalar) {
// the logical name and db column we are looking for a match on
String name = embeddedScalar.getName();
String dbColumn = embeddedScalar.getDbColumn();
@@ -180,7 +151,6 @@ public class DeployBeanPropertyLists {
return assocOne;
}
}
return null;
}
@@ -368,7 +338,6 @@ public class DeployBeanPropertyLists {
}
public BeanProperty getSoftDeleteProperty() {
for (BeanProperty prop : nonManys) {
if (prop.isSoftDelete()) {
return prop;
@@ -385,7 +354,6 @@ public class DeployBeanPropertyLists {
* Return the properties set via generated values on insert.
*/
public BeanProperty[] getGeneratedInsert() {
List<BeanProperty> list = new ArrayList<>();
for (BeanProperty prop : nonTransients) {
GeneratedProperty gen = prop.getGeneratedProperty();
@@ -400,7 +368,6 @@ public class DeployBeanPropertyLists {
* Return the properties set via generated values on update.
*/
public BeanProperty[] getGeneratedUpdate() {
List<BeanProperty> list = new ArrayList<>();
for (BeanProperty prop : nonTransients) {
GeneratedProperty gen = prop.getGeneratedProperty();
@@ -438,8 +405,7 @@ public class DeployBeanPropertyLists {
}
}
}
return (BeanPropertyAssocOne[]) list.toArray(new BeanPropertyAssocOne[0]);
return list.toArray(new BeanPropertyAssocOne<?>[0]);
}
private BeanPropertyAssocMany<?>[] getMany2Many() {
@@ -449,8 +415,7 @@ public class DeployBeanPropertyLists {
list.add(prop);
}
}
return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[0]);
return list.toArray(new BeanPropertyAssocMany<?>[0]);
}
private BeanPropertyAssocMany<?>[] getMany(Mode mode) {
@@ -471,25 +436,26 @@ public class DeployBeanPropertyLists {
break;
}
}
return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[0]);
return list.toArray(new BeanPropertyAssocMany<?>[0]);
}
@SuppressWarnings({"unchecked", "rawtypes"})
private BeanProperty createBeanProperty(BeanDescriptorMap owner, DeployBeanProperty deployProp) {
if (deployProp instanceof DeployBeanPropertyAssocOne) {
return new BeanPropertyAssocOne(owner, desc, (DeployBeanPropertyAssocOne) deployProp);
}
if (deployProp instanceof DeployBeanPropertySimpleCollection<?>) {
return new BeanPropertySimpleCollection(desc, (DeployBeanPropertySimpleCollection) deployProp);
}
if (deployProp instanceof DeployBeanPropertyAssocMany) {
return new BeanPropertyAssocMany(desc, (DeployBeanPropertyAssocMany) deployProp);
}
if (deployProp.isJsonMapper()) {
return new BeanPropertyJsonMapper(desc, deployProp);
}
if (deployProp.isJsonType()) {
return new BeanPropertyJsonBasic(desc, deployProp);
}
return new BeanProperty(desc, deployProp);
}
@@ -1,10 +1,6 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.annotation.DbArray;
import io.ebean.annotation.DbJson;
import io.ebean.annotation.DbJsonB;
import io.ebean.annotation.DbJsonType;
import io.ebean.annotation.DbMap;
import io.ebean.annotation.*;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.EncryptDeploy;
import io.ebean.config.EncryptDeployManager;
@@ -209,26 +205,22 @@ public class DeployUtil {
}
}
/**
* This property is marked as a Lob object.
*/
void setDbJsonType(DeployBeanProperty prop, DbJson dbJsonType) {
int dbType = getDbJsonStorage(dbJsonType.storage());
setDbJsonType(prop, dbType, dbJsonType.length());
setDbJsonType(prop, dbType, dbJsonType.length(), dbJsonType.mutationDetection());
}
void setDbJsonBType(DeployBeanProperty prop, DbJsonB dbJsonB) {
setDbJsonType(prop, DbPlatformType.JSONB, dbJsonB.length());
setDbJsonType(prop, DbPlatformType.JSONB, dbJsonB.length(), dbJsonB.mutationDetection());
}
private void setDbJsonType(DeployBeanProperty prop, int dbType, int dbLength) {
private void setDbJsonType(DeployBeanProperty prop, int dbType, int dbLength, MutationDetection mutationDetection) {
prop.setDbType(dbType);
prop.setMutationDetection(mutationDetection);
ScalarType<?> scalarType = typeManager.getJsonScalarType(prop, dbType, dbLength);
if (scalarType == null) {
throw new RuntimeException("No ScalarType for JSON property [" + prop + "] [" + dbType + "]");
}
prop.setDbType(dbType);
prop.setScalarType(scalarType);
if (dbType == Types.VARCHAR || dbLength > 0) {
// determine the db column size
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -37,9 +38,9 @@ public abstract class AbstractTextExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
return 0;
}
public void queryBindKey(BindValuesKey key) {
// do nothing, only execute against document store
};
@Override
public boolean isSameByBind(SpiExpression other) {
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -122,14 +123,11 @@ class AllEqualsExpression extends NonPrepareExpression {
}
@Override
public int queryBindHash() {
int hc = 92821;
public void queryBindKey(BindValuesKey key) {
key.add(propMap.size());
for (Object value : propMap.values()) {
hc = hc * 92821 + (value == null ? 0 : value.hashCode());
key.add(value);
}
return hc;
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -49,12 +50,11 @@ public class ArrayContainsExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
int hc = values[0].hashCode();
for (int i = 1; i < values.length; i++) {
hc = hc * 92821 + values[i].hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(values.length);
for (Object value : values) {
key.add(value);
}
return hc;
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -33,8 +34,8 @@ public class ArrayIsEmptyExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
return empty ? 0 : 92821;
public void queryBindKey(BindValuesKey key) {
key.add(empty);
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -49,10 +50,8 @@ class BetweenExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
int hc = low().hashCode();
hc = hc * 92821 + high().hashCode();
return hc;
public void queryBindKey(BindValuesKey key) {
key.add(low()).add(high());
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -95,8 +96,8 @@ class BetweenPropertyExpression extends NonPrepareExpression {
}
@Override
public int queryBindHash() {
return val().hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(val());
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -39,8 +40,8 @@ class BitwiseExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
return Long.hashCode(flags);
public void queryBindKey(BindValuesKey key) {
key.add(flags).add(match);
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
@@ -69,8 +70,8 @@ class CaseInsensitiveEqualExpression extends AbstractValueExpression {
}
@Override
public int queryBindHash() {
return val().hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(val());
}
@Override
@@ -5,6 +5,7 @@ import io.ebean.LikeType;
import io.ebean.bean.EntityBean;
import io.ebean.event.BeanQueryRequest;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -136,10 +137,8 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins) {
list = buildExpressions(desc);
if (list != null) {
for (SpiExpression aList : list) {
aList.containsMany(desc, whereManyJoins);
}
for (SpiExpression expr : list) {
expr.containsMany(desc, whereManyJoins);
}
}
@@ -186,8 +185,8 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
@Override
public void validate(SpiExpressionValidation validation) {
for (SpiExpression aList : list) {
aList.validate(validation);
for (SpiExpression expr : list) {
expr.validate(validation);
}
}
@@ -228,25 +227,20 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
*/
@Override
public void queryPlanHash(StringBuilder builder) {
builder.append("Example[");
for (SpiExpression aList : list) {
aList.queryPlanHash(builder);
for (SpiExpression expr : list) {
expr.queryPlanHash(builder);
builder.append(",");
}
builder.append("]");
}
/**
* Return a hash for the actual bind values used.
*/
@Override
public int queryBindHash() {
int hc = DefaultExampleExpression.class.getName().hashCode();
for (SpiExpression aList : list) {
hc = hc * 92821 + aList.queryBindHash();
public void queryBindKey(BindValuesKey key) {
key.add(list.size());
for (SpiExpression expr : list) {
expr.queryBindKey(key);
}
return hc;
}
@Override
@@ -267,7 +261,6 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
* Build the List of expressions.
*/
private ArrayList<SpiExpression> buildExpressions(BeanDescriptor<?> beanDescriptor) {
ArrayList<SpiExpression> list = new ArrayList<>();
addExpressions(list, beanDescriptor, entity, null);
return list;
@@ -26,6 +26,7 @@ import io.ebean.search.MultiMatch;
import io.ebean.search.TextCommonTerms;
import io.ebean.search.TextQueryString;
import io.ebean.search.TextSimple;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -109,10 +110,8 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
* @return A single SpiExpression that has the nestedPath set
*/
SpiExpression wrap(List<SpiExpression> list, String nestedPath, Junction.Type type) {
DefaultExpressionList<T> wrapper = new DefaultExpressionList<>(query, expr, null, list, false);
wrapper.setAllDocNested(nestedPath);
if (type != null) {
return new JunctionExpression<>(type, wrapper);
} else {
@@ -121,15 +120,15 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
}
void simplifyEntries() {
for (SpiExpression element : list) {
element.simplify();
for (SpiExpression expr : list) {
expr.simplify();
}
}
@Override
public void prefixProperty(String path) {
for (SpiExpression exp : list) {
exp.prefixProperty(path);
for (SpiExpression expr : list) {
expr.prefixProperty(path);
}
}
@@ -174,7 +173,6 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
context.startNested(allDocNestedPath);
}
int size = list.size();
SpiExpression first = list.get(0);
boolean explicitBool = first instanceof SpiJunction<?>;
boolean implicitBool = !explicitBool && size > 1;
@@ -210,7 +208,6 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
@Override
public void writeDocQuery(DocQueryContext context, SpiExpression idEquals) throws IOException {
if (allDocNestedPath != null) {
context.startNested(allDocNestedPath);
}
@@ -227,8 +224,8 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
if (idEquals != null) {
idEquals.writeDocQuery(context);
}
for (SpiExpression aList : list) {
aList.writeDocQuery(context);
for (SpiExpression expr : list) {
expr.writeDocQuery(context);
}
context.endBool();
}
@@ -278,16 +275,15 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
*/
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins) {
for (SpiExpression aList : list) {
aList.containsMany(desc, whereManyJoins);
for (SpiExpression expr : list) {
expr.containsMany(desc, whereManyJoins);
}
}
@Override
public void validate(SpiExpressionValidation validation) {
for (SpiExpression aList : list) {
aList.validate(validation);
for (SpiExpression expr : list) {
expr.validate(validation);
}
}
@@ -630,7 +626,6 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
@Override
public void addSql(SpiExpressionRequest request) {
for (int i = 0, size = list.size(); i < size; i++) {
SpiExpression expression = list.get(i);
if (i > 0) {
@@ -642,15 +637,15 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
@Override
public void addBindValues(SpiExpressionRequest request) {
for (SpiExpression aList : list) {
aList.addBindValues(request);
for (SpiExpression expr : list) {
expr.addBindValues(request);
}
}
@Override
public void prepareExpression(BeanQueryRequest<?> request) {
for (SpiExpression aList : list) {
aList.prepareExpression(request);
for (SpiExpression expr : list) {
expr.prepareExpression(request);
}
}
@@ -667,23 +662,19 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
if (allDocNestedPath != null) {
builder.append("path:").append(allDocNestedPath).append(" ");
}
for (SpiExpression aList : list) {
aList.queryPlanHash(builder);
for (SpiExpression expr : list) {
expr.queryPlanHash(builder);
builder.append(",");
}
builder.append("]");
}
/**
* Calculate a hash based on the expressions.
*/
@Override
public int queryBindHash() {
int hash = DefaultExpressionList.class.getName().hashCode();
for (SpiExpression aList : list) {
hash = hash * 92821 + aList.queryBindHash();
public void queryBindKey(BindValuesKey key) {
key.add(list.size());
for (SpiExpression expr : list) {
expr.queryBindKey(key);
}
return hash;
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiEbeanServer;
@@ -8,6 +9,7 @@ import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.api.SpiExpressionValidation;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.SpiQuery.Type;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.query.CQuery;
@@ -81,7 +83,7 @@ class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpress
*/
protected CQuery<?> compileSubQuery(BeanQueryRequest<?> queryRequest) {
SpiEbeanServer ebeanServer = (SpiEbeanServer) queryRequest.getEbeanServer();
return ebeanServer.compileQuery(subQuery, queryRequest.getTransaction());
return ebeanServer.compileQuery(Type.SQ_EXISTS, subQuery, queryRequest.getTransaction());
}
@Override
@@ -91,8 +93,8 @@ class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpress
}
@Override
public int queryBindHash() {
return subQuery.queryBindHash();
public void queryBindKey(BindValuesKey key) {
subQuery.queryBindKey(key);
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -77,8 +78,8 @@ class IdExpression extends NonPrepareExpression implements SpiExpression {
}
@Override
public int queryBindHash() {
return value.hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(value);
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -133,8 +134,11 @@ public class IdInExpression extends NonPrepareExpression {
}
@Override
public int queryBindHash() {
return idCollection.hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(idCollection.size());
for (Object elem : idCollection) {
key.add(elem);
}
}
@Override
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
import io.ebean.bean.EntityBean;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -176,12 +177,11 @@ class InExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
int hc = 92821;
public void queryBindKey(BindValuesKey key) {
key.add(bindValues.size());
for (Object bindValue : bindValues) {
hc = 92821 * hc + bindValue.hashCode();
key.add(bindValue);
}
return hc;
}
@Override
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.expression;
import io.ebean.Pairs;
import io.ebean.Pairs.Entry;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -124,12 +125,11 @@ class InPairsExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
int hc = 92821;
public void queryBindKey(BindValuesKey key) {
key.add(entries.size());
for (Pairs.Entry entry : entries) {
hc = 92821 * hc + entry.hashCode();
key.add(entry.getA()).add(entry.getB());
}
return hc;
}
@Override
@@ -1,10 +1,12 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.SpiQuery.Type;
import io.ebeaninternal.server.query.CQuery;
import java.io.IOException;
@@ -68,12 +70,12 @@ class InQueryExpression extends AbstractExpression implements UnsupportedDocStor
private CQuery<?> compileSubQuery(BeanQueryRequest<?> queryRequest) {
SpiEbeanServer ebeanServer = (SpiEbeanServer) queryRequest.getEbeanServer();
return ebeanServer.compileQuery(subQuery, queryRequest.getTransaction());
return ebeanServer.compileQuery(Type.SQ_IN, subQuery, queryRequest.getTransaction());
}
@Override
public int queryBindHash() {
return subQuery.queryBindHash();
public void queryBindKey(BindValuesKey key) {
subQuery.queryBindKey(key);
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -47,10 +48,8 @@ class InRangeExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
int hc = low().hashCode();
hc = hc * 92821 + high().hashCode();
return hc;
public void queryBindKey(BindValuesKey key) {
key.add(low()).add(high());
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -103,8 +104,8 @@ class IsEmptyExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
return 1;
public void queryBindKey(BindValuesKey key) {
// no bind values
}
@Override
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -83,10 +84,8 @@ class JsonPathExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
int hc = (value == null) ? 0 : value.hashCode();
hc = (upperValue == null) ? hc : hc * 92821 + upperValue.hashCode();
return hc;
public void queryBindKey(BindValuesKey key) {
key.add(value).add(upperValue);
}
@Override
@@ -25,6 +25,7 @@ import io.ebean.search.MultiMatch;
import io.ebean.search.TextCommonTerms;
import io.ebean.search.TextQueryString;
import io.ebean.search.TextSimple;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -113,9 +114,8 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
context.startBool(type);
List<SpiExpression> list = exprList.internalList();
for (SpiExpression aList : list) {
aList.writeDocQuery(context);
for (SpiExpression expr : exprList.internalList()) {
expr.writeDocQuery(context);
}
context.endBool();
}
@@ -123,9 +123,8 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public void writeDocQueryJunction(DocQueryContext context) throws IOException {
context.startBoolGroupList(type);
List<SpiExpression> list = exprList.internalList();
for (SpiExpression aList : list) {
aList.writeDocQuery(context);
for (SpiExpression expr : exprList.internalList()) {
expr.writeDocQuery(context);
}
context.endBoolGroupList();
}
@@ -138,18 +137,15 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins manyWhereJoin) {
List<SpiExpression> list = exprList.internalList();
// get the current state for 'require outer joins'
boolean parentOuterJoins = manyWhereJoin.isRequireOuterJoins();
if (type == Type.OR) {
// turn on outer joins required for disjunction expressions
manyWhereJoin.setRequireOuterJoins(true);
}
for (SpiExpression aList : list) {
aList.containsMany(desc, manyWhereJoin);
for (SpiExpression expr : list) {
expr.containsMany(desc, manyWhereJoin);
}
if (type == Type.OR && !parentOuterJoins) {
// restore state to not forcing outer joins
@@ -176,18 +172,14 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public void addBindValues(SpiExpressionRequest request) {
List<SpiExpression> list = exprList.internalList();
for (SpiExpression aList : list) {
aList.addBindValues(request);
for (SpiExpression expr : exprList.internalList()) {
expr.addBindValues(request);
}
}
@Override
public void addSql(SpiExpressionRequest request) {
List<SpiExpression> list = exprList.internalList();
if (!list.isEmpty()) {
request.append(type.prefix());
request.append("(");
@@ -204,9 +196,8 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public void prepareExpression(BeanQueryRequest<?> request) {
List<SpiExpression> list = exprList.internalList();
for (SpiExpression aList : list) {
aList.prepareExpression(request);
for (SpiExpression expr : exprList.internalList()) {
expr.prepareExpression(request);
}
}
@@ -216,22 +207,18 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public void queryPlanHash(StringBuilder builder) {
builder.append(type).append("[");
List<SpiExpression> list = exprList.internalList();
for (SpiExpression aList : list) {
aList.queryPlanHash(builder);
for (SpiExpression expr : exprList.internalList()) {
expr.queryPlanHash(builder);
builder.append(",");
}
builder.append("]");
}
@Override
public int queryBindHash() {
int hc = JunctionExpression.class.getName().hashCode();
List<SpiExpression> list = exprList.internalList();
for (SpiExpression aList : list) {
hc = hc * 92821 + aList.queryBindHash();
public void queryBindKey(BindValuesKey key) {
for (SpiExpression expr : exprList.internalList()) {
expr.queryBindKey(key);
}
return hc;
}
@Override
@@ -275,7 +262,6 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
return exprList.textCommonTerms(search, options);
}
@Override
public ExpressionList<T> allEq(Map<String, Object> propertyMap) {
return exprList.allEq(propertyMap);
@@ -1025,7 +1011,6 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public String nestedPath(BeanDescriptor<?> desc) {
PrepareDocNested.prepare(exprList, desc, type);
String nestedPath = exprList.allDocNestedPath;
if (nestedPath != null) {
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.LikeType;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
@@ -70,8 +71,8 @@ class LikeExpression extends AbstractValueExpression {
}
@Override
public int queryBindHash() {
return strValue().hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(strValue());
}
@Override
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.expression;
import io.ebean.Expression;
import io.ebean.Junction;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -168,10 +169,8 @@ abstract class LogicExpression implements SpiExpression {
}
@Override
public int queryBindHash() {
int hc = expOne.queryBindHash();
hc = hc * 92821 + expTwo.queryBindHash();
return hc;
public void queryBindKey(BindValuesKey key) {
key.add(expOne).add(expTwo);
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.LikeType;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
@@ -54,8 +55,8 @@ class NativeILikeExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
return val.hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(val);
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -79,8 +80,8 @@ class NestedPathWrapperExpression implements SpiExpression {
}
@Override
public int queryBindHash() {
return delegate.queryBindHash();
public void queryBindKey(BindValuesKey key) {
delegate.queryBindKey(key);
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -74,9 +75,8 @@ class NoopExpression implements SpiExpression {
}
@Override
public int queryBindHash() {
public void queryBindKey(BindValuesKey key) {
// no bind values
return 0;
}
@Override
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.expression;
import io.ebean.Expression;
import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiExpression;
@@ -99,8 +100,8 @@ final class NotExpression implements SpiExpression {
}
@Override
public int queryBindHash() {
return exp.queryBindHash();
public void queryBindKey(BindValuesKey key) {
exp.queryBindKey(key);
}
@Override
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.expression;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -94,7 +95,7 @@ class NullExpression extends AbstractExpression {
}
@Override
public int queryBindHash() {
return (notNull ? 1 : 0);
public void queryBindKey(BindValuesKey key) {
key.add(notNull);
}
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.expression;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
@@ -73,12 +74,11 @@ class RawExpression extends NonPrepareExpression {
}
@Override
public int queryBindHash() {
int hc = sql.hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(values.length);
for (Object value : values) {
hc = hc * 92821 + value.hashCode();
key.add(value);
}
return hc;
}
@Override
@@ -5,6 +5,7 @@ import io.ebean.plugin.ExpressionPath;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.server.el.ElPropertyValue;
import io.ebeaninternal.api.BindValuesKey;
import io.ebeaninternal.api.NaturalKeyQueryData;
import java.io.IOException;
@@ -121,8 +122,8 @@ public class SimpleExpression extends AbstractValueExpression {
}
@Override
public int queryBindHash() {
return value().hashCode();
public void queryBindKey(BindValuesKey key) {
key.add(value());
}
@Override
@@ -12,7 +12,7 @@ import java.util.Comparator;
* keys from inserts. These values are required to persist the 'detail' beans.
* </p>
*/
class BatchDepthComparator implements Comparator<BatchedBeanHolder>, Serializable {
final class BatchDepthComparator implements Comparator<BatchedBeanHolder>, Serializable {
private static final long serialVersionUID = 264611821665757991L;
@@ -6,7 +6,7 @@ import java.util.Map;
/**
* Helper to determine batch execution order for BatchedBeanHolders.
*/
class BatchDepthOrder {
final class BatchDepthOrder {
private final Map<Integer, Counter> map = new HashMap<>();
@@ -22,7 +22,7 @@ class BatchDepthOrder {
map.clear();
}
private static class Counter {
private static final class Counter {
int count;
@@ -18,7 +18,7 @@ import java.util.ArrayList;
* executed. The lowest depth is executed first.
* </p>
*/
class BatchedBeanHolder {
final class BatchedBeanHolder {
/**
* The owning queue.
@@ -20,7 +20,7 @@ import java.util.List;
* This can hold CallableStatements as well.
* </p>
*/
public class BatchedPstmt implements SpiProfileTransactionEvent {
public final class BatchedPstmt implements SpiProfileTransactionEvent {
private static final Logger log = LoggerFactory.getLogger(BatchedPstmt.class);

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