Compare commits

...
Author SHA1 Message Date
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
rbygrave 705e4f4098 [maven-release-plugin] prepare release ebean-parent-12.10.0 2021-07-22 19:03:43 +12:00
rbygrave 1642833fdb Improve javadoc on TQRootBean and generated javadoc on query bean forFetchGroup() method 2021-07-22 18:56:02 +12:00
rbygrave 492bb98e0e #2269 - findSingleAttributeList does not filter soft-deleted record 2021-07-22 17:12:06 +12:00
rbygrave a8456f90db Bump ebean-agent version to 2.10.0 2021-07-22 16:46:57 +12:00
Rob BygraveandGitHub 103b75172d Merge pull request #2267 from FOCONIS/pr/bugfix/connection_closed_on_clob
Retrieve (C|B)LOB on SqlRow construction
2021-07-22 16:44:05 +12:00
rbygrave 0baa3a0eac #2270 - ebean-agent enhancement NPE with MappedSuperclass with no properties that uses named database 2021-07-22 16:38:28 +12:00
rbygrave f26e4b5381 Bump jedis to 3.6.3 (from 3.6.1) 2021-07-22 12:37:42 +12:00
Jonas Pöhler 3d4de4dee5 FIX TestRawSqlService and TestRawSqlBuilder 2021-07-16 15:17:04 +02:00
Jonas Pöhler c621435a28 Merge remote-tracking branch 'ebean/master' into pr/bugfix/connection_closed_on_clob 2021-07-16 13:32:24 +02:00
rbygrave 7408483a43 #2264 - Use of default timezone for OffsetDateTime offsets problematic for unit tests and presentation layer 2021-07-15 17:14:22 +12:00
rbygrave 4d15535381 No functional change - tidy DefaultDbSqlContext 2021-07-13 16:48:45 +12:00
Rob BygraveandGitHub 787c2f90f2 Merge pull request #2188 from FOCONIS/fix-order-by
FIX: orderBy does not work when used on formula property
2021-07-13 16:41:33 +12:00
Rob BygraveandGitHub e590a2149d Merge pull request #2257 from FOCONIS/queries-are-cancelable
Queries are cancelable
2021-07-13 16:11:36 +12:00
rbygrave 8c77740aa7 Bump version tp 12.10.0-SNAPSHOT (with 12.9.4-RC1 ddl generator) 2021-07-13 15:48:10 +12:00
Rob BygraveandGitHub 29582b57f7 Merge pull request #2256 from FOCONIS/many-where-support
Improved @Where support, especially for Many-to-many tables
2021-07-13 15:01:49 +12:00
rbygrave 2763f333d4 #2262 - Tidy tests for Transaction postCommit Callback 2021-07-13 14:48:40 +12:00
rbygrave 8840628d34 #2262 - Transaction postCommit Callback not invoked when the active transaction has no database writes 2021-07-13 14:45:48 +12:00
rbygrave 6fb28d7ded #2255 Specific test for setParameter cannot assigning the value when parameter in selecting section of query 2021-07-13 12:54:21 +12:00
rbygrave be810aa568 [maven-release-plugin] prepare for next development iteration 2021-07-13 12:16:12 +12:00
rbygrave 8a698bd71e [maven-release-plugin] prepare release ebean-parent-12.9.3 2021-07-13 12:16:01 +12:00
rbygrave baa0594501 Change ebean-test tests to use H2 rather than mariadb 2021-07-13 12:10:23 +12:00
rbygrave e82b8c80f0 #2265 - ArrayIndexOutOfBoundsException: Index -1 out of bounds with @IdClass and batch insert accessing id property 2021-07-13 11:30:31 +12:00
Rob BygraveandGitHub f8e54be9a6 Merge pull request #2260 from casmir/master
Fix additional autotune runtimeExceptions due to toString() calls instead of asString()
2021-07-01 13:05:17 +12:00
Cas de Graaf d3137be984 in addition to #2236
- autotune still throws RunTimeException due to existing
 ORMQueryDetail.toString() calls, replaced with .asString()
2021-06-30 16:23:20 +02:00
Roland Praml 824cfa27be Queries are cancelable 2021-06-23 17:11:21 +02:00
Roland Praml 66cb6d5e7d Imporved @Where support, especially for Many-to-many tables 2021-06-21 14:50:15 +02:00
rbygrave 8f432aa0a9 #2251 - Transparent persistence of to-many associations (with orphanRemoval) 2021-06-18 16:05:25 +12:00
rbygrave 4aa4137abe Merge branch 'transparent_persistence' of https://github.com/cit-consulting/ebean into cit-consulting-transparent_persistence 2021-06-18 13:03:36 +12:00
rbygrave f4bb4e1b6a #2254 - OraclePlatform needs columnAliasPrefix 'c' - Regression in version 12.2.1 due to #1967 2021-06-18 12:37:16 +12:00
rbygrave 7af4e166a4 #2253 - Incorrect JSON format for LocalDateTime. Includes Z suffix when it should not. 2021-06-17 13:26:34 +12:00
Vladimir Konkov 1fa488d870 Test for transparent persistence of to-many associations 2021-06-14 16:20:25 +03:00
rbygrave 37a3a51b6b Bump test dependency for Postgres JDBC driver 2021-06-14 11:09:37 +12:00
rbygrave 6a6e8f906f Bump jedis dependency to 3.6.1 (from 3.6.0) 2021-06-14 11:09:13 +12:00
rbygrave 55bc498f9d No functional change - refactor QueryIterator to use Iterator.remove()
default method

Iterator.remove() has a default implementation that throws UnsupportedOperationException which is what we want. Remove the close() method from QueryIterator and implementations so that we just use the Iterator.remove() default implementation.
2021-06-11 10:20:47 +12:00
rbygrave 4ee784c68f [maven-release-plugin] prepare for next development iteration 2021-06-11 00:41:06 +12:00
rbygrave fc757c087a [maven-release-plugin] prepare release ebean-parent-12.9.2 2021-06-11 00:40:55 +12:00
rbygrave 884dd939b7 #2249 - Bug with DbJson Jackson property that is null is updated as null (unnecessarily included in update)
When the value is null and not changed then is was null before and should not be treated as dirty. Hence only check for dirty when value != null in BeanDescriptor 3182
2021-06-11 00:19:20 +12:00
Rob BygraveandGitHub 6fd2f556db Merge pull request #2248 from ebean-orm/feature/DbJson-DirtyDetection-Refactor
Refactor DbJson Jackson handling adding DatabaseConfig.setJsonDirtyByDefault()
2021-06-11 00:10:37 +12:00
rbygrave 4e088a40d9 Refactor DbJson Jackson handling adding DatabaseConfig.setJsonDirtyByDefault()
Adds the ability to change the default "jsonDirtyByDefault" configuration setting used with DbJson Jackson properties. Currently these default to being assumed dirty and this allows us to change that to be assumed not dirty (which is very likely the better default).
2021-06-11 00:08:03 +12:00
rbygrave 8ba66af878 Modify test TestQueryDefaultBatchSize to show use of ProfileLocation 2021-06-10 22:11:45 +12:00
rbygrave 441f51ed79 #2247 - Refactor internals - Change DefaultTypeManager - tidy 2021-06-10 20:30:07 +12:00
rbygrave 9ee891dc66 #2247 - Refactor internals - Change DefaultTypeManager from class.newInstance() to class.getDeclaredConstructor().newInstance() 2021-06-10 20:09:34 +12:00
rbygrave 87ffcbf23e No effective change - tidy CQuery 2021-06-10 19:45:27 +12:00
rbygrave adfc54200f No effective change - extract JacksonTypeHelper for ScalarTypeJsonObjectMapper 2021-06-10 19:27:42 +12:00
rbygrave 3c36b43449 No effective change - tidy EntityBeanIntercept 2021-06-10 19:26:56 +12:00
rbygrave 9be1dcff6c Tidy internals ScalarTypeJsonObjectMapper, no functional change 2021-06-10 19:00:35 +12:00
rbygrave 37dff62fa9 #2246 - ENH: Add DtoQuery findStream() and findIterate() 2021-06-10 14:55:31 +12:00
rbygrave 4fd462b1c8 #2244 - ENH: Add SQL Server support for transparent encryption via EncryptByPassPhrase, DecryptByPassPhrase 2021-06-10 13:34:33 +12:00
rbygrave 33ac1923ff No effective change - refactor move Str into server.util package
Allows the removal of io.ebeaninternal.server.lib package as that was the only thing in there.
2021-06-09 23:40:48 +12:00
rbygrave 0b87ae5eac No effective change - tidy BindParams 2021-06-09 23:38:10 +12:00
rbygrave 678743cc10 Use upper case E for class name of generated _Ebean$ModuleInfo 2021-06-09 21:59:19 +12:00
rbygrave c8345ce093 No functional change - refactor internal toStream() to use closure rather than explicit Runnable type for closing the stream
Conceptually javac closure might be better than an extra explicit type here
2021-06-09 16:21:15 +12:00
rbygrave f3af43a9ce Add support for findEach() on SqlQuery with RowMapper and scalar result 2021-06-09 15:49:48 +12:00
rbygrave 1df2ccb1c6 Refactor internals DefaultRelationalQueryEngine remove unnecessary method
- Remove findEach() method that took RowReader<T> and Consumer<T>
- Use findEachRow() instead
2021-06-09 15:19:35 +12:00
rbygrave 3add4472ef Refactor internals DefaultRelationalQueryEngine.findListMapper()
- Remove findListMapper() method
- Introduce RowReader interface
- Combine findList internals for both SqlRow and RowMapper use
2021-06-09 14:58:08 +12:00
rbygrave 4613f63c66 Refactor internals DataReader remove resetColumnPosition()
- Move the reset of position to the DataReader.next() method
- Tidy up use of wasNull() in RsetDataReader
2021-06-09 12:57:56 +12:00
rbygrave 612295fb9d [maven-release-plugin] prepare for next development iteration 2021-06-04 17:59:21 +12:00
rbygrave 64405ade52 [maven-release-plugin] prepare release ebean-parent-12.9.1 2021-06-04 17:59:11 +12:00
rbygrave ce5f1b25f5 Bump ebean-maven-plugin to 12.9.1 2021-06-04 17:50:46 +12:00
rbygrave ae4f50e7b3 #2236 - Auto-tune does not work / throws RuntimeException (Regression from 12.7.1)
Note we are missing a test on ProfileManager but doing this for now.
2021-06-04 17:46:31 +12:00
rbygrave c9120be347 No effective change - tidy only H2HistoryTrigger and H2HistoryDdl 2021-06-04 17:37:37 +12:00
rbygrave b189e67b4c #2241 - Offline DDL migration generation for H2 requires H2 Driver 2021-06-04 17:35:19 +12:00
Rob BygraveandGitHub 5d5c275c79 Merge pull request #2240 from FOCONIS/bugfix/ddl-generator-version
Set ddl-generator version to a public-available version + fix classpath for Java8
2021-06-04 17:24:31 +12:00
rbygrave 52f31a7a8e Merge branch 'bkempe-testSelectOneToOneDisjunction' 2021-06-04 17:17:21 +12:00
rbygrave 6697aecd48 #2238 - Select with disjunction of expressions over OneToOne relationship missing join clause
Noting that there might be a better fix. The addJoin() at line 151 is suppressed as it is a duplicate for this case of a !oneToOneExported so a better fix might be to only add the soft delete clause when the join as actually added (not suppressed as a duplicate).
2021-06-04 17:16:50 +12:00
Jonas Pöhler 348c075a14 FIX: Build for java8 2021-06-01 17:56:47 +02:00
Jonas Pöhler bf2420702c FIX: set ddl-generator version to a repo-available version 2021-06-01 17:19:38 +02:00
Ben Kempe 16832ac67a add test testSelectOneToOneDisjunction 2021-05-24 23:47:55 +02:00
rbygrave 47bd2ff03d [maven-release-plugin] prepare for next development iteration 2021-05-23 23:08:47 +12:00
rbygrave 850f1c1469 [maven-release-plugin] prepare release ebean-parent-12.9.0 2021-05-23 23:08:38 +12:00
rbygrave 622a90c7d5 No effective change - tidy whitespace in TransactionManager 2021-05-23 23:00:34 +12:00
rbygrave 4c317f11ae Add additional test for PrimaryKeyJoinColumn 2021-05-23 22:21:05 +12:00
rbygrave 01bce5cfa6 #2226 - Automatically determining join columns and ignoring PrimaryKeyJoinColumn.name Id warning when I want to use Id as a foreign key
Improve the message and dropping to INFO level.
2021-05-23 22:20:40 +12:00
rbygrave 2158925d3c #2202 - kotlin-maven-plugin issue with JDK 16
InaccessibleObjectException: Unable to make protected void java.util.ResourceBundle.setParent(java.util.ResourceBundle) accessible: module java.base does not "opens java.util" to unnamed module @62732be7
2021-05-23 21:28:11 +12:00
Rob BygraveandGitHub 2aff6403a2 Merge pull request #2235 from ebean-orm/feature/2223
#2223 #2224 Support BeanMap modification via entrySet() and keySet()
2021-05-23 17:20:55 +12:00
rbygrave 3c4d600187 #2223 #2224 Support BeanMap modification via entrySet() and keySet() 2021-05-23 17:17:34 +12:00
Rob BygraveandGitHub f60ccb9b90 Merge pull request #2222 from ebean-orm/feature/support-filterMany-inline
Change such that filterMany can be included in the main query.
2021-05-21 17:20:17 +12:00
rbygrave 2a6108087c Bump version to 12.9.0-SNAPSHOT 2021-05-21 17:18:30 +12:00
rbygrave 0da86a82ae #2233 Bump to ebean-agent 12.9.0 2021-05-21 17:14:49 +12:00
rbygrave 643b0499c4 #2233 Add ebean-version.mf so ebean-agent can sniff the internal version
This allows the ebean-agent to support the older behaviour. Devs using the latest IntelliJ ebean plugin will get the old behaviour unless they bump ebean-core to 12.9 or later.
2021-05-21 16:09:15 +12:00
rbygrave 676bac390e #2233 Followup change that unsets load buffer from BeanCollection
- Clears the buffer after it has been used to load BeanCollection's
- Sets the BeanCollection to use the server rather than the Load Buffer just in case there are subsequent calls for loading (which there should not be)
2021-05-21 09:01:45 +12:00
rbygrave 79740accfa #2233 Fix with setter on ToMany now not invoking lazy loading
- ebean-agent changed to not invoke preGetter on ToMany (just like Id)
- EntityBeanIntercept preSetterMany() changed to ensure loading flag set
2021-05-21 08:48:42 +12:00
rbygrave 77c6f1cb9f #2233 Tidy failing test Ebean -> DB and rename test 2021-05-21 08:44:04 +12:00
rbygrave 538361d5fd #2233 Failing test 2021-05-21 08:33:31 +12:00
rbygrave b17593ba3c No effective change - tidy BeanList, BeanMap, BeanSet internals 2021-05-20 21:44:07 +12:00
rbygrave eb00690b30 #2231 - Change OrderBy to not be final to support mocking via Mockito 2021-05-20 14:09:22 +12:00
Rob BygraveandGitHub a772e65d32 Merge pull request #2225 from ebean-orm/dependabot/maven/ebean-core/commons-io-commons-io-2.7
Bump commons-io from 2.5 to 2.7 in /ebean-core
2021-05-20 13:56:00 +12:00
Brian Payne 66f7f69ec7 Fixed bug with soft-delete on one-to-one exported 2021-05-12 09:24:33 -07:00
Brian Payne ad52711fe1 Add test demonstrating soft-delete bug with optional relationship 2021-05-11 10:42:04 -07:00
dependabot[bot]andGitHub d6fdb4ee34 Bump commons-io from 2.5 to 2.7 in /ebean-core
Bumps commons-io from 2.5 to 2.7.

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-26 20:37:55 +00:00
Jonas Pöhler (JPo) 4977aa2ad1 Merge remote-tracking branch 'ebean/master' into pr/bugfix/connection_closed_on_clob 2021-04-26 14:54:32 +02:00
rbygrave 74c7268d2f Change such that filterMany can be included in the main query.
That is, currently a filterMany automatically marks that path as a fetchQuery path. This change turns that off and the filterMany path and predicates can then be included in the main query.

This is better for performance (to include the filterMany path in with the main query) when the query is a findEach and looking to return a large number of results.
2021-04-21 18:57:03 +12:00
rbygrave 42fc419759 #2221 - ebean-redis: bump jedis dependency to 3.6.0 2021-04-20 22:23:33 +12:00
rbygrave 4fb3b9cddc Tidy use of StringHelper replace() in BeanProperty, BaseTableDdl 2021-04-20 22:13:45 +12:00
Robin Bygrave c60077174e Tidy use of StringHelper replace() in BeanProperty, BaseTableDdl 2021-04-20 15:14:20 +12:00
rbygrave 5e893dd2ad [maven-release-plugin] prepare for next development iteration 2021-04-19 21:40:16 +12:00
rbygrave 9eb54da2dc [maven-release-plugin] prepare release ebean-parent-12.8.3 2021-04-19 21:40:07 +12:00
rbygrave bb6ff866c7 #2220 - DDL generation of nullable columns expected with nullable Embedded containing non null properties 2021-04-19 21:37:05 +12:00
Robin Bygrave 2d719c5b37 #2218 - @Encrypted columns on Oracle. The identifier alias starts with an underscore (ORA-00911)
Change to use zx__ for the encryption column alias prefix rather than _e_
2021-04-19 20:08:38 +12:00
Robin Bygrave 9d54b264ad #2219 - Compile error with ZonedDateTime and query beans - type argument java.time.ZonedDateTime is not within bounds of type-variable D 2021-04-19 17:04:57 +12:00
rbygrave 65b282a056 [maven-release-plugin] prepare for next development iteration 2021-04-09 17:49:16 +12:00
rbygrave fc174c8988 [maven-release-plugin] prepare release ebean-parent-12.8.2 2021-04-09 17:49:07 +12:00
rbygrave 06802896ef #2213 Bump agent for @Transactional(autoPersistUpdates = TxOption.ON) 2021-04-09 16:17:58 +12:00
Robin Bygrave 7f13ac9e5e #2213 - Support autoPersistUpdates via TxScope 2021-04-08 23:15:49 +12:00
Rob BygraveandGitHub 4a1374d22d Merge pull request #2213 from ebean-orm/feature/transparentPersistence
Initial experimental support for autoPersistUpdates / "transparent persistence"
2021-04-08 23:03:05 +12:00
rbygrave 4ee8d647b7 No effective change - tidy test TestCustomerFinder 2021-04-07 23:28:53 +12:00
rbygrave 59793f3c01 Improve javadoc for Transaction#setGetGeneratedKeys
More clearly document the limitation that we can't update beans that don't have id values
2021-04-07 15:50:04 +12:00
rbygrave f7f383d8f3 No effective change - tidy tests TestRawSqlPositionedParams, TestRawSqlUnparsedQuery 2021-04-07 13:38:34 +12:00
rbygrave 562a3959b6 Merge branch 'master' of github.com:ebean-orm/ebean 2021-04-06 21:14:21 +12:00
rbygrave bb1275f4f9 #2217 - Inserting an entity bean is skipped with a String @Id property and no other properties set 2021-04-06 21:13:59 +12:00
Rob BygraveandGitHub 7fde6b9331 Merge pull request #2216 from ebean-orm/feature/2177-take2
#2177 - Fix where schema not provided by JDBC driver
2021-04-06 20:13:24 +12:00
rbygrave 3e97a4005a Change TestCustomerFinder drop query plan threshold for tests only 2021-04-06 20:09:09 +12:00
Rob BygraveandGitHub c271760230 Merge pull request #2215 from PromanSEW/patch-1
Fix copy/paste typo in javadoc for endOr()
2021-04-06 20:00:02 +12:00
rbygrave f7c1845f0c #2177 - Fix where schema not provided by JDBC driver
For @Table with schema, using findNative with a JDBC driver that does not provide the schema this fix matches by just using the table name.
2021-04-06 19:58:02 +12:00
Roman ParshikovandGitHub 9b04f20ed3 Fix copy/paste typo 2021-04-06 13:32:57 +08:00
Rob BygraveandGitHub 62b6b6293b Merge pull request #2214 from cit-consulting/fix_with_lock
Explicit locking method withLock() is unaccessible on query beans
2021-04-06 08:27:38 +12:00
Vladimir Konkov 6d8a1c9398 Explicit locking method withLock() is unaccessible on query beans 2021-04-05 21:45:48 +03:00
Robin Bygrave 303039735f Refactor rename transparentPersistence to autoPersistUpdates 2021-04-01 23:04:22 +13:00
Robin Bygrave 6bcf5c6881 Add DatabaseConfig.autoPersistUpdates for experimental "transparent persistence" 2021-04-01 22:57:34 +13:00
Robin Bygrave 0527f45f5a For experimental "transparent persistence" - Inserts added to PC
When inserting bean with transparent persistence the beans are registered with the persistence context. This is done to support the cases where the beans are inserted, a flush() occurs, the inserted bean is mutated and now dirty - we want that to be detected and get an update
2021-04-01 16:59:03 +13:00
Robin Bygrave ba397bbb0c For experimental "transparent persistence" - Delete removes from PC early
When delete bean executed with transparent persistence, this marks the bean as removed from the persistence context early.

This avoids a "dirty" deleted bean from being seen as a "dirty" bean in the persistence context at flush() time.
2021-04-01 16:04:18 +13:00
Robin Bygrave a617d58902 Initial experimental support for "transparent persistence"
In short, when this is turned on at flush() get any dirty beans held by the PersistenceContext and persist them (always update for these beans)
2021-04-01 14:47:54 +13:00
Robin Bygrave 7fc4a229ae Merge branch 'master' of https://github.com/ebean-orm/ebean 2021-04-01 12:23:00 +13:00
Robin Bygrave f53d31aa2e No functional change - final field for BatchControl 2021-04-01 12:22:45 +13:00
Rob BygraveandGitHub b889b6a6b0 Merge pull request #2212 from tobias-/bitwise_querybean_operators
Add bitwise operators to query beans
2021-04-01 09:38:00 +13:00
Tobias f9b55a06b8 Add bitwise operators to query beans 2021-03-31 12:53:06 +02:00
rob bygrave 09d772a5e5 [maven-release-plugin] prepare for next development iteration 2021-03-31 00:06:13 +13:00
Jonas Pöhler (JPo) dd6a473008 ADD: Test and possible fix for (C)LOBs being handed out of connnection-context
Signed-off-by: Jonas Pöhler (JPo) <jonas.poehler@foconis.de>
2021-03-22 15:59:59 +01:00
Roland Praml ef1143bb70 FIX: orderBy does not work when used on formula property or in conjunction with "exists" query 2021-03-02 15:26:29 +01:00
376 changed files with 7715 additions and 2799 deletions
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.11.0</version>
</parent>
<name>ebean api</name>
@@ -50,7 +50,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>6.15</version>
<version>7.2</version>
</dependency>
<dependency>
@@ -0,0 +1,19 @@
package io.ebean;
/**
* Defines a cancelable query.
* <p>
* Typically holds a representation of the PreparedStatement to perform the
* actual cancel.
* </p>
*/
public interface CancelableQuery {
/**
* Cancel the query.
* <p>
* For JDBC this translates to calling cancel on the PreparedStatement.
* </p>
*/
void cancel();
}
+22 -1
View File
@@ -6,6 +6,7 @@ import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* Query for performing native SQL queries that return DTO Bean's.
@@ -37,7 +38,7 @@ import java.util.function.Predicate;
*
* }</pre>
*/
public interface DtoQuery<T> {
public interface DtoQuery<T> extends CancelableQuery {
/**
* Execute the query returning a list.
@@ -45,6 +46,26 @@ public interface DtoQuery<T> {
@Nonnull
List<T> findList();
/**
* Execute the query iterating a row at a time.
* <p>
* Note that the QueryIterator holds resources related to the underlying
* resultSet and potentially connection and MUST be closed. We should use
* QueryIterator in a <em>try with resource block</em>.
*/
@Nonnull
QueryIterator<T> findIterate();
/**
* Execute the query returning a Stream.
* <p>
* Note that the Stream holds resources related to the underlying
* resultSet and potentially connection and MUST be closed. We should use
* the Stream in a <em>try with resource block</em>.
*/
@Nonnull
Stream<T> findStream();
/**
* Execute the query iterating a row at a time.
* <p>
@@ -1671,7 +1671,7 @@ public interface ExpressionList<T> {
ExpressionList<T> endAnd();
/**
* End a AND junction - synonym for endJunction().
* End a OR junction - synonym for endJunction().
*/
ExpressionList<T> endOr();
@@ -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.
+4 -11
View File
@@ -16,7 +16,7 @@ import java.util.Objects;
* on the Query object.
* </p>
*/
public final class OrderBy<T> implements Serializable {
public class OrderBy<T> implements Serializable {
private static final long serialVersionUID = 9157089257745730539L;
@@ -69,7 +69,6 @@ public final class OrderBy<T> implements Serializable {
* Add a property with ascending order to this OrderBy.
*/
public Query<T> asc(String propertyName) {
list.add(new Property(propertyName, true));
return query;
}
@@ -98,7 +97,6 @@ public final class OrderBy<T> implements Serializable {
return query;
}
/**
* Return true if the property is known to be contained in the order by clause.
*/
@@ -207,7 +205,6 @@ public final class OrderBy<T> implements Serializable {
if (!(obj instanceof OrderBy<?>)) {
return false;
}
OrderBy<?> e = (OrderBy<?>) obj;
return e.list.equals(list);
}
@@ -249,7 +246,7 @@ public final class OrderBy<T> implements Serializable {
/**
* A property and its ascending descending order.
*/
public static final class Property implements Serializable {
public static class Property implements Serializable {
private static final long serialVersionUID = 1546009780322478077L;
@@ -415,13 +412,10 @@ public final class OrderBy<T> implements Serializable {
}
private void parse(String orderByClause) {
if (orderByClause == null) {
return;
}
String[] chunks = orderByClause.split(",");
for (String chunk : chunks) {
for (String chunk : orderByClause.split(",")) {
Property p = parseProperty(chunk);
if (p != null) {
list.add(p);
@@ -467,8 +461,7 @@ public final class OrderBy<T> implements Serializable {
if (s.startsWith("desc")) {
return false;
}
String m = "Expecting [" + s + "] to be asc or desc?";
throw new RuntimeException(m);
throw new RuntimeException("Expecting [" + s + "] to be asc or desc?");
}
private boolean isEmptyString(String s) {
@@ -45,6 +45,14 @@ public interface ProfileLocation {
*/
String label();
/**
* Return a hash of the location that intentionally excludes the line number.
* <p>
* The hash is expected to be stable regardless of line number in the source file
* so that is identifies the class and method location over a long time.
*/
long hash();
/**
* Return the full location.
*/
+1 -10
View File
@@ -177,7 +177,7 @@ import java.util.stream.Stream;
*
* @param <T> the type of Entity bean this query will fetch.
*/
public interface Query<T> {
public interface Query<T> extends CancelableQuery {
/**
* The lock type (strength) to use with query FOR UPDATE row locking.
@@ -291,15 +291,6 @@ public interface Query<T> {
*/
UpdateQuery<T> asUpdate();
/**
* Cancel the query execution if supported by the underlying database and
* driver.
* <p>
* This must be called from a different thread to the query executor.
* </p>
*/
void cancel();
/**
* Return a copy of the query.
* <p>
@@ -56,7 +56,7 @@ import java.util.Iterator;
*
* @param <T> the type of entity bean in the iteration
*/
public interface QueryIterator<T> extends Iterator<T>, java.io.Closeable {
public interface QueryIterator<T> extends Iterator<T>, AutoCloseable {
/**
* Returns <tt>true</tt> if the iteration has more elements.
@@ -70,12 +70,6 @@ public interface QueryIterator<T> extends Iterator<T>, java.io.Closeable {
@Override
T next();
/**
* Remove is not allowed.
*/
@Override
void remove();
/**
* Close the underlying resources held by this iterator.
*/
@@ -37,7 +37,7 @@ import java.util.function.Predicate;
*
* }</pre>
*/
public interface SqlQuery extends Serializable {
public interface SqlQuery extends Serializable, CancelableQuery {
/**
* Execute the query returning a list.
@@ -365,5 +365,10 @@ public interface SqlQuery extends Serializable {
* Return the list of values.
*/
List<T> findList();
/**
* Find streaming the result effectively consuming a row at a time.
*/
void findEach(Consumer<T> consumer);
}
}
@@ -59,6 +59,14 @@ public interface Transaction extends AutoCloseable {
*/
void register(TransactionCallback callback);
/**
* EXPERIMENTAL - turn on automatic persistence of dirty beans and batchMode true.
* <p>
* With this turned on beans that are dirty in the persistence context
* are automatically persisted on flush() and commit().
*/
void setAutoPersistUpdates(boolean autoPersistUpdates);
/**
* Set a label on the transaction.
* <p>
@@ -339,15 +347,12 @@ public interface Transaction extends AutoCloseable {
* The batch is automatically flushed when it hits the batch size and also when we
* execute queries or when we mix UpdateSql and CallableSql with save and delete of
* beans.
* </p>
* <p>
* We use {@link #flush()} to explicitly flush the batch and we can use
* {@link #setFlushOnQuery(boolean)} and {@link #setFlushOnMixed(boolean)}
* to control the automatic flushing behaviour.
* </p>
* <p>
* Example: batch processing of CallableSql executing every 10 rows
* </p>
*
* <pre>{@code
*
@@ -392,14 +397,11 @@ public interface Transaction extends AutoCloseable {
* <p>
* This only takes effect when batch mode on the transaction has not already meant that
* JDBC batch mode is being used.
* </p>
* <p>
* This is useful when the single save() or delete() cascades. For example, inserting a 'master' cascades
* and inserts a collection of 'detail' beans. The detail beans can be inserted using JDBC batch.
* </p>
* <p>
* This is effectively already turned on for all platforms apart from older Sql Server.
* </p>
*
* @param batchMode the batch mode to use per save(), insert(), update() or delete()
* @see io.ebean.config.DatabaseConfig#setPersistBatchOnCascade(PersistBatch)
@@ -422,15 +424,19 @@ public interface Transaction extends AutoCloseable {
int getBatchSize();
/**
* Specify if you want batched inserts to use getGeneratedKeys.
* Specify if we want batched inserts to use getGeneratedKeys.
* <p>
* By default batched inserts will try to use getGeneratedKeys if it is
* supported by the underlying jdbc driver and database.
* </p>
* <p>
* You may want to turn getGeneratedKeys off when you are inserting a large
* number of objects and you don't care about getting back the ids.
* </p>
* We want to turn off getGeneratedKeys when we are inserting a large
* number of objects and we don't care about getting back the ids. In this
* way we avoid the extra cost of getting back the generated id values
* from the database.
* <p>
* Note that when we do turn off getGeneratedKeys then we have the limitation
* that after a bean has been inserted we are unable to then mutate the bean
* and update it in the same transaction as we have not obtained it's id value.
*/
void setGetGeneratedKeys(boolean getGeneratedKeys);
@@ -449,13 +455,11 @@ public interface Transaction extends AutoCloseable {
* <p>
* If you want to execute both WITHOUT having the batch automatically flush
* you need to call this with batchFlushOnMixed = false.
* </p>
* <p>
* Note that UpdateSql and CallableSql are ALWAYS executed first (before the
* beans are executed). This is because the UpdateSql and CallableSql have
* already been bound to their PreparedStatements. The beans on the other hand
* have a 2 step process (delayed binding).
* </p>
*/
void setFlushOnMixed(boolean batchFlushOnMixed);
@@ -473,7 +477,6 @@ public interface Transaction extends AutoCloseable {
* <p>
* Calling this method with batchFlushOnQuery = false means that you can
* execute a query and the batch will not be automatically flushed.
* </p>
*/
void setFlushOnQuery(boolean batchFlushOnQuery);
@@ -490,7 +493,6 @@ public interface Transaction extends AutoCloseable {
* should be flushed prior to executing a query.
* <p>
* The default is for this to be true.
* </p>
*/
boolean isFlushOnQuery();
@@ -507,7 +509,6 @@ public interface Transaction extends AutoCloseable {
* flush the batch if you like.
* <p>
* Flushing occurs automatically when:
* </p>
* <ul>
* <li>the batch size is reached</li>
* <li>A query is executed on the same transaction</li>
@@ -533,11 +534,9 @@ public interface Transaction extends AutoCloseable {
* commit() rollback() and end() methods on the Transaction should still be
* used. Calling these methods on the Connection would be a big no no unless
* you know what you are doing.
* </p>
* <p>
* Examples of when a developer may wish to use the connection directly are:
* Savepoints, advanced CLOB BLOB use and advanced stored procedure calls.
* </p>
*/
Connection getConnection();
@@ -545,17 +544,14 @@ public interface Transaction extends AutoCloseable {
* Add table modification information to the TransactionEvent.
* <p>
* Use this in conjunction with getConnection() and raw JDBC.
* </p>
* <p>
* This effectively informs Ebean of the data that has been changed by the
* transaction and this information is normally automatically handled by Ebean
* when you save entity beans or use UpdateSql etc.
* </p>
* <p>
* If you use raw JDBC then you can use this method to inform Ebean for the
* tables that have been modified. Ebean uses this information to keep its
* caches in synch and maintain text indexes.
* </p>
*/
void addModification(String tableName, boolean inserts, boolean updates, boolean deletes);
@@ -2,6 +2,7 @@ package io.ebean;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.TxIsolation;
import io.ebean.annotation.TxOption;
import io.ebean.annotation.TxType;
import java.util.ArrayList;
@@ -33,6 +34,8 @@ public final class TxScope {
private TxIsolation isolation;
private TxOption autoPersistUpdates;
private PersistBatch batch;
private PersistBatch batchOnCascade;
@@ -123,6 +126,13 @@ public final class TxScope {
+ "] serverName[" + serverName + "] rollbackFor[" + rollbackFor + "] noRollbackFor[" + noRollbackFor + "]";
}
/**
* Return the AutoPersistUpdates mode as a nullable Boolean.
*/
public Boolean getAutoPersistUpdates() {
return autoPersistUpdates == null ? null : autoPersistUpdates.asBoolean();
}
/**
* Return true if PersistBatch has been set.
*/
@@ -176,6 +186,14 @@ public final class TxScope {
return this;
}
/**
* Set the autoPersistUpdates mode.
*/
public TxScope setAutoPersistUpdates(TxOption autoPersistUpdates) {
this.autoPersistUpdates = autoPersistUpdates;
return this;
}
/**
* Return the transaction profile id.
*/
@@ -13,12 +13,10 @@ import java.util.Set;
* from the Map Set or List. The purpose of gathering the additions and removals
* is to support persisting ManyToMany objects. The additions and removals
* become inserts and deletes from the intersection table.
* </p>
* <p>
* Technically this is <em>NOT</em> an extension of
* <em>java.util.Collection</em>. The reason being that java.util.Map is not a
* Collection. I realise this makes this name confusing so I apologise for that.
* </p>
*/
public interface BeanCollection<E> extends Serializable {
@@ -68,7 +66,6 @@ public interface BeanCollection<E> extends Serializable {
* Return true if the collection is uninitialised or is empty without any held modifications.
* <p>
* Returning true means can safely skip cascade save for this bean collection.
* </p>
*/
boolean isSkipSave();
@@ -93,7 +90,6 @@ public interface BeanCollection<E> extends Serializable {
* <p>
* That is, if the collection was not loaded due to filterMany predicates etc
* then make sure the collection is set to empty.
* </p>
*/
boolean checkEmptyLazyLoad();
@@ -136,10 +132,7 @@ public interface BeanCollection<E> extends Serializable {
boolean isReadOnly();
/**
* Add the bean to the collection.
* <p>
* This is disallowed for BeanMap.
* </p>
* Add the bean to the collection. This is disallowed for BeanMap.
*/
void internalAdd(Object bean);
@@ -168,7 +161,6 @@ public interface BeanCollection<E> extends Serializable {
* Map.Entry.
* <p>
* For maps this returns the entrySet as we need the keys of the map.
* </p>
*/
Collection<?> getActualEntries();
@@ -185,6 +177,11 @@ public interface BeanCollection<E> extends Serializable {
*/
boolean isReference();
/**
* Return true if the collection is modify listening and has modifications.
*/
boolean hasModifications();
/**
* Set modify listening on or off. This is used to keep track of objects that
* have been added to or removed from the list set or map.
@@ -192,7 +189,6 @@ public interface BeanCollection<E> extends Serializable {
* This is required only for ManyToMany collections. The additions and
* deletions are used to insert or delete entries from the intersection table.
* Otherwise modifyListening is false.
* </p>
*/
void setModifyListening(ModifyListenMode modifyListenMode);
@@ -206,7 +202,6 @@ public interface BeanCollection<E> extends Serializable {
* <p>
* This will potentially end up as an insert into a intersection table for a
* ManyToMany.
* </p>
*/
void modifyAddition(E bean);
@@ -215,7 +210,6 @@ public interface BeanCollection<E> extends Serializable {
* <p>
* This will potentially end up as an delete from an intersection table for a
* ManyToMany.
* </p>
*/
void modifyRemoval(Object bean);
@@ -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.
*/
@@ -32,39 +32,52 @@ public final class EntityBeanIntercept implements Serializable {
private static final int STATE_REFERENCE = 1;
private static final int STATE_LOADED = 2;
/**
* Used when a bean is partially filled.
*/
private static final byte FLAG_LOADED_PROP = 1;
private static final byte FLAG_CHANGED_PROP = 2;
private static final byte FLAG_CHANGEDLOADED_PROP = 3;
/**
* Flags indicating if a property is a dirty embedded bean. Used to distinguish
* between an embedded bean being completely overwritten and one of its
* embedded properties being made dirty.
*/
private static final byte FLAG_EMBEDDED_DIRTY = 4;
/**
* Flags indicating if a property is a dirty embedded bean. Used to distinguish
* between an embedded bean being completely overwritten and one of its
* embedded properties being made dirty.
*/
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;
private transient BeanLoader beanLoader;
private transient PreGetterCallback preGetterCallback;
private String ebeanServerName;
private boolean deletedFromCollection;
/**
* The actual entity bean that 'owns' this intercept.
*/
private final EntityBean owner;
private EntityBean embeddedOwner;
private int embeddedOwnerIndex;
/**
* One of NEW, REF, UPD.
*/
private int state;
private boolean forceUpdate;
private boolean readOnly;
private boolean dirty;
/**
* Flag set to disable lazy loading - typically for SQL "report" type entity beans.
*/
@@ -74,41 +87,26 @@ public final class EntityBeanIntercept implements Serializable {
* Flag set when lazy loading failed due to the underlying bean being deleted in the DB.
*/
private boolean lazyLoadFailure;
/**
* Used when a bean is partially filled.
*/
private static final byte FLAG_LOADED_PROP = 1;
/**
* Set of changed properties.
*/
private static final byte FLAG_CHANGED_PROP = 2;
/**
* Flags indicating if a property is a dirty embedded bean. Used to distinguish
* between an embedded bean being completely overwritten and one of its
* embedded properties being made dirty.
*/
private static final byte FLAG_EMBEDDED_DIRTY = 4;
/**
* Flags indicating if a property is a dirty embedded bean. Used to distinguish
* between an embedded bean being completely overwritten and one of its
* embedded properties being made dirty.
*/
private static final byte FLAG_ORIG_VALUE_SET = 8;
private final byte[] flags;
private boolean fullyLoadedBean;
private boolean loadedFromCache;
private final byte[] flags;
private Object[] origValues;
private Exception[] loadErrors;
private int lazyLoadProperty = -1;
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.
*/
@@ -246,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;
}
@@ -386,8 +395,9 @@ public final class EntityBeanIntercept implements Serializable {
this.owner._ebean_setEmbeddedLoaded();
this.lazyLoadProperty = -1;
this.origValues = null;
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;
}
@@ -460,6 +470,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;
}
@@ -494,7 +508,7 @@ public final class EntityBeanIntercept implements Serializable {
* Return the number of properties.
*/
public int getPropertyLength() {
return owner._ebean_getPropertyNames().length;
return flags.length;
}
/**
@@ -569,6 +583,10 @@ public final class EntityBeanIntercept implements Serializable {
flags[propertyIndex] |= FLAG_CHANGED_PROP;
}
private void setChangeLoaded(int propertyIndex) {
flags[propertyIndex] |= FLAG_CHANGEDLOADED_PROP;
}
/**
* Set that an embedded bean has had one of its properties changed.
*/
@@ -578,7 +596,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;
@@ -591,7 +609,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;
}
@@ -652,7 +670,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) {
@@ -670,7 +688,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;
}
@@ -698,7 +716,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);
@@ -720,7 +738,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);
@@ -755,7 +773,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
@@ -937,7 +955,7 @@ public final class EntityBeanIntercept implements Serializable {
if (readOnly) {
throw new IllegalStateException("This bean is readOnly");
}
setChangedProperty(propertyIndex);
setChangeLoaded(propertyIndex);
}
}
@@ -977,7 +995,6 @@ public final class EntityBeanIntercept implements Serializable {
}
}
/**
* Check for primitive boolean.
*/
@@ -1127,7 +1144,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;
@@ -1153,4 +1170,62 @@ 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;
}
final MutableValueNext next = mutableNext[propertyIndex];
mutableInfo(propertyIndex, next.info());
return next.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();
}
@@ -5,7 +5,6 @@ package io.ebean.bean;
* <p>
* This is used to ensure only one instance for a given entity type and id is
* used to build object graphs from queries and lazy loading.
* </p>
*/
public interface PersistenceContext {
@@ -20,7 +19,6 @@ public interface PersistenceContext {
* <p>
* Returns an existing entity bean (if one is already there) and otherwise
* returns null.
* </p>
*/
Object putIfAbsent(Class<?> rootType, Object id, Object bean);
@@ -82,7 +80,6 @@ public interface PersistenceContext {
* <p>
* If a bean has been deleted then for the same persistence context is should
* not be able to be fetched from persistence context or L2 cache.
* </p>
*/
class WithOption {
@@ -135,6 +135,11 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
// Support for modify additions deletions etc - ManyToMany
// ---------------------------------------------------------
@Override
public boolean hasModifications() {
return modifyHolder != null && modifyHolder.hasModifications();
}
@Override
public ModifyListenMode getModifyListening() {
return modifyListenMode;
@@ -145,7 +150,6 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
*/
@Override
public void setModifyListening(ModifyListenMode mode) {
this.modifyListenMode = mode;
this.modifyListening = mode != null && ModifyListenMode.NONE != mode;
if (modifyListening) {
@@ -198,10 +198,9 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
}
if (list == null) {
sb.append("deferred ");
} else {
sb.append("size[").append(list.size()).append("] ");
sb.append("list").append(list).append("");
sb.append("list").append(list);
}
return sb.toString();
}
@@ -175,10 +175,6 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
/**
* Returns the map entrySet.
* <p>
* This is because the key values may need to be set against the details (so
* they don't need to be set twice).
* </p>
*/
@Override
public Collection<?> getActualEntries() {
@@ -194,7 +190,6 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
}
if (map == null) {
sb.append("deferred ");
} else {
sb.append("size[").append(map.size()).append("]");
sb.append(" map").append(map);
@@ -243,17 +238,12 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
}
@Override
@SuppressWarnings({"unchecked"})
public Set<Entry<K, E>> entrySet() {
init();
if (isReadOnly()) {
return Collections.unmodifiableSet(map.entrySet());
}
if (modifyListening) {
Set<Entry<K, E>> s = map.entrySet();
return new ModifySet(this, s);
}
return map.entrySet();
return modifyListening ? new ModifyEntrySet<>(this, map.entrySet()) : map.entrySet();
}
@Override
@@ -274,8 +264,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
if (isReadOnly()) {
return Collections.unmodifiableSet(map.keySet());
}
// we don't really care about modifications to the ketSet?
return map.keySet();
return modifyListening ? new ModifyKeySet<>(this, map.keySet()) : map.keySet();
}
@Override
@@ -346,11 +335,7 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
if (isReadOnly()) {
return Collections.unmodifiableCollection(map.values());
}
if (modifyListening) {
Collection<E> c = map.values();
return new ModifyCollection<>(this, c);
}
return map.values();
return modifyListening ? new ModifyCollection<>(this, map.values()) : map.values();
}
@Override
@@ -176,7 +176,6 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
}
if (set == null) {
sb.append("deferred ");
} else {
sb.append("size[").append(set.size()).append("]");
sb.append(" set").append(set);
@@ -25,7 +25,7 @@ class ModifyCollection<E> implements Collection<E> {
* The owner is notified of the additions and removals.
* </p>
*/
public ModifyCollection(BeanCollection<E> owner, Collection<E> c) {
ModifyCollection(BeanCollection<E> owner, Collection<E> c) {
this.owner = owner;
this.c = c;
}
@@ -0,0 +1,131 @@
package io.ebean.common;
import java.util.*;
/**
* Handles the Entry Set for BeanMap.
*/
class ModifyEntrySet<K, E> implements Set<Map.Entry<K, E>> {
private final BeanMap<K, E> owner;
private final Set<Map.Entry<K, E>> entrySet;
ModifyEntrySet(BeanMap<K, E> owner, Set<Map.Entry<K, E>> entrySet) {
this.owner = owner;
this.entrySet = entrySet;
}
@Override
public int size() {
return entrySet.size();
}
@Override
public boolean isEmpty() {
return entrySet.isEmpty();
}
@Override
public boolean contains(Object o) {
return entrySet.contains(o);
}
@Override
public Object[] toArray() {
return entrySet.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return entrySet.toArray(a);
}
@Override
public boolean containsAll(Collection<?> entries) {
return entrySet.containsAll(entries);
}
@Override
public void clear() {
owner.clear();
}
@Override
public boolean add(Map.Entry<K, E> entry) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(Collection<? extends Map.Entry<K, E>> c) {
throw new UnsupportedOperationException();
}
@SuppressWarnings("rawtypes")
@Override
public boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry entry = (Map.Entry) o;
final E val = owner.get(entry.getKey());
if (Objects.equals(val, entry.getValue())) {
owner.remove(entry.getKey());
return true;
}
}
return false;
}
@Override
public boolean retainAll(Collection<?> entries) {
boolean modified = false;
final Iterator<Map.Entry<K, E>> it = iterator();
while (it.hasNext()) {
if (!entries.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
@Override
public boolean removeAll(Collection<?> entries) {
boolean modified = false;
for (Object entry : entries) {
modified |= remove(entry);
}
return modified;
}
@Override
public Iterator<Map.Entry<K, E>> iterator() {
return new EntrySetIterator(new ArrayList<>(entrySet).iterator());
}
class EntrySetIterator implements Iterator<Map.Entry<K, E>> {
private final Iterator<Map.Entry<K, E>> iterator;
private Map.Entry<K, E> entry;
EntrySetIterator(Iterator<Map.Entry<K, E>> iterator) {
this.iterator = iterator;
}
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public Map.Entry<K, E> next() {
entry = iterator.next();
return entry;
}
@Override
public void remove() {
owner.remove(entry.getKey());
iterator.remove();
}
}
}
@@ -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();
}
/**
@@ -0,0 +1,126 @@
package io.ebean.common;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
/**
* Handle the Key Set for BeanMap.
*/
class ModifyKeySet<E> implements Set<E> {
private final Set<E> keySet;
private final BeanMap<E, ?> owner;
ModifyKeySet(BeanMap<E, ?> owner, Set<E> keySet) {
this.owner = owner;
this.keySet = keySet;
}
@Override
public int size() {
return keySet.size();
}
@Override
public boolean isEmpty() {
return keySet.isEmpty();
}
@Override
public boolean contains(Object o) {
return keySet.contains(o);
}
@Override
public Object[] toArray() {
return keySet.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return keySet.toArray(a);
}
@Override
public boolean add(E key) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(Collection<? extends E> keys) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o) {
return owner.remove(o) != null;
}
@Override
public boolean containsAll(Collection<?> keys) {
return keySet.containsAll(keys);
}
@Override
public void clear() {
owner.clear();
}
@Override
public Iterator<E> iterator() {
return new KeySetIterator<>(new ArrayList<>(keySet).iterator());
}
@Override
public boolean retainAll(Collection<?> keys) {
return keysMatch(keys, false);
}
@Override
public boolean removeAll(Collection<?> keys) {
return keysMatch(keys, true);
}
private boolean keysMatch(Collection<?> keys, boolean containsMatch) {
boolean changed = false;
final Iterator<E> iterator = iterator();
while (iterator.hasNext()) {
final E key = iterator.next();
if (keys.contains(key) == containsMatch) {
iterator.remove();
changed = true;
}
}
return changed;
}
class KeySetIterator<K> implements Iterator<K> {
private final Iterator<K> iterator;
private K key;
KeySetIterator(Iterator<K> iterator) {
this.iterator = iterator;
}
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public K next() {
key = iterator.next();
return key;
}
@Override
public void remove() {
owner.remove(key);
iterator.remove();
}
}
}
@@ -1,24 +0,0 @@
package io.ebean.common;
import io.ebean.bean.BeanCollection;
import java.util.Set;
/**
* Wraps a Set for the purposes of notifying removals and additions to the
* BeanCollection owner.
* <p>
* This is required for persisting ManyToMany objects. Additions and removals
* become inserts and deletes to the intersection table.
* </p>
*/
class ModifySet<E> extends ModifyCollection<E> implements Set<E> {
/**
* Create with an Owner that is notified of any additions or deletions.
*/
public ModifySet(BeanCollection<E> owner, Set<E> s) {
super(owner, s);
}
}
@@ -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;
@@ -193,6 +191,11 @@ public class DatabaseConfig {
*/
private JsonConfig.Include jsonInclude = JsonConfig.Include.ALL;
/**
* The default mode used for {@code @DbJson} with Jackson ObjectMapper.
*/
private MutationDetection jsonMutationDetection = MutationDetection.HASH;
/**
* The database platform name. Used to imply a DatabasePlatform to use.
*/
@@ -232,6 +235,12 @@ public class DatabaseConfig {
*/
private String historyTableSuffix = "_history";
/**
* When true explicit transactions beans that have been made dirty will be
* automatically persisted via update on flush.
*/
private boolean autoPersistUpdates;
/**
* Use for transaction scoped batch mode.
*/
@@ -731,6 +740,24 @@ public class DatabaseConfig {
this.jsonInclude = jsonInclude;
}
/**
* Return the default MutableDetection to use with {@code @DbJson} using Jackson.
*
* @see DbJson#mutationDetection()
*/
public MutationDetection getJsonMutationDetection() {
return jsonMutationDetection;
}
/**
* Set the default MutableDetection to use with {@code @DbJson} using Jackson.
*
* @see DbJson#mutationDetection()
*/
public void setJsonMutationDetection(MutationDetection jsonMutationDetection) {
this.jsonMutationDetection = jsonMutationDetection;
}
/**
* Return the name of the Database.
*/
@@ -905,6 +932,20 @@ public class DatabaseConfig {
this.tenantCatalogProvider = tenantCatalogProvider;
}
/**
* Return true if dirty beans are automatically persisted.
*/
public boolean isAutoPersistUpdates() {
return autoPersistUpdates;
}
/**
* Set to true if dirty beans are automatically persisted.
*/
public void setAutoPersistUpdates(boolean autoPersistUpdates) {
this.autoPersistUpdates = autoPersistUpdates;
}
/**
* Return the PersistBatch mode to use by default at the transaction level.
* <p>
@@ -2807,6 +2848,7 @@ public class DatabaseConfig {
loadDocStoreSettings(p);
defaultServer = p.getBoolean("defaultServer", defaultServer);
autoPersistUpdates = p.getBoolean("autoPersistUpdates", autoPersistUpdates);
loadModuleInfo = p.getBoolean("loadModuleInfo", loadModuleInfo);
maxCallStack = p.getInt("maxCallStack", maxCallStack);
dumpMetricsOnShutdown = p.getBoolean("dumpMetricsOnShutdown", dumpMetricsOnShutdown);
@@ -2888,6 +2930,7 @@ 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);
jsonMutationDetection = p.getEnum(MutationDetection.class, "jsonMutationDetection", jsonMutationDetection);
runMigration = p.getBoolean("migration.run", runMigration);
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
@@ -9,9 +9,6 @@ import java.sql.Types;
* functions for varchar, date and timestamp. If they are left null then that is
* treated as though that data type can not be encrypted in the DB and will
* instead use java client encryption.
* </p>
*
* @author rbygrave
*/
public abstract class AbstractDbEncrypt implements DbEncrypt {
@@ -47,13 +44,10 @@ public abstract class AbstractDbEncrypt implements DbEncrypt {
case Types.CHAR:
case Types.LONGVARCHAR:
return varcharEncryptFunction;
case Types.DATE:
return dateEncryptFunction;
case Types.TIMESTAMP:
return timestampEncryptFunction;
default:
return null;
}
@@ -5,8 +5,6 @@ import io.ebean.config.dbplatform.DbEncryptFunction;
/**
* H2 encryption support via encrypt decrypt function.
*
* @author rbygrave
*/
public class H2DbEncrypt extends AbstractDbEncrypt {
@@ -4,11 +4,7 @@ import org.h2.api.Trigger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.*;
import java.util.Arrays;
/**
@@ -43,7 +39,6 @@ public class H2HistoryTrigger implements Trigger {
@Override
public void init(Connection conn, String schemaName, String triggerName, String tableName, boolean before, int type) throws SQLException {
// get the columns for the table
ResultSet rs = conn.getMetaData().getColumns(null, schemaName, tableName, null);
@@ -79,7 +74,6 @@ public class H2HistoryTrigger implements Trigger {
@Override
public void fire(Connection connection, Object[] oldRow, Object[] newRow) throws SQLException {
if (oldRow != null) {
// a delete or update event
Timestamp now = new Timestamp(System.currentTimeMillis());
@@ -99,7 +93,6 @@ public class H2HistoryTrigger implements Trigger {
* Insert the data into the history table.
*/
private void insertIntoHistory(Connection connection, Object[] oldRow) throws SQLException {
try (PreparedStatement stmt = connection.prepareStatement(insertHistorySql)) {
for (int i = 0; i < oldRow.length; i++) {
stmt.setObject(i + 1, oldRow[i]);
@@ -110,11 +103,11 @@ public class H2HistoryTrigger implements Trigger {
@Override
public void close() throws SQLException {
// do nothing
}
@Override
public void remove() throws SQLException {
// do nothing
}
}
@@ -5,8 +5,6 @@ import io.ebean.config.dbplatform.DbEncryptFunction;
/**
* MySql aes_encrypt aes_decrypt based encryption support.
*
* @author rbygrave
*/
public class MySqlDbEncrypt extends AbstractDbEncrypt {
@@ -11,7 +11,6 @@ public class Oracle11Platform extends OraclePlatform {
public Oracle11Platform() {
super();
this.platform = Platform.ORACLE11;
this.columnAliasPrefix = "c";
this.sqlLimiter = new OracleRownumSqlLimiter();
this.basicSqlLimiter = new OracleRownumBasicLimiter();
dbIdentity.setIdType(IdType.SEQUENCE);
@@ -60,7 +60,6 @@ public class OracleDbEncrypt extends AbstractDbEncrypt {
* @param decryptFunction the decrypt stored procedure
*/
public OracleDbEncrypt(String encryptFunction, String decryptFunction) {
this.varcharEncryptFunction = new OraVarcharFunction(encryptFunction, decryptFunction);
this.dateEncryptFunction = new OraDateFunction(encryptFunction, decryptFunction);
}
@@ -22,6 +22,7 @@ public class OraclePlatform extends DatabasePlatform {
public OraclePlatform() {
super();
this.platform = Platform.ORACLE;
this.columnAliasPrefix = "c";
this.supportsDeleteTableAlias = true;
this.maxTableNameLength = 30;
this.maxConstraintNameLength = 30;
@@ -25,6 +25,7 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
this.platform = Platform.SQLSERVER;
// disable persistBatchOnCascade mode for
// SQL Server unless we are using sequences
this.dbEncrypt = new SqlServerDbEncrypt();
this.persistBatchOnCascade = PersistBatch.NONE;
this.idInExpandedForm = true;
this.selectCountWithAlias = true;
@@ -0,0 +1,46 @@
package io.ebean.config.dbplatform.sqlserver;
import io.ebean.config.dbplatform.AbstractDbEncrypt;
import io.ebean.config.dbplatform.DbEncryptFunction;
/**
* SQL Server EncryptByPassPhrase DecryptByPassPhrase based encryption support.
*/
public class SqlServerDbEncrypt extends AbstractDbEncrypt {
public SqlServerDbEncrypt() {
this.varcharEncryptFunction = new VarcharFunction();
this.dateEncryptFunction = new DateFunction();
}
@Override
public boolean isBindEncryptDataFirst() {
return false;
}
private static class VarcharFunction implements DbEncryptFunction {
@Override
public String getDecryptSql(String columnWithTableAlias) {
return "convert(nvarchar,DecryptByPassPhrase(?," + columnWithTableAlias + "))";
}
@Override
public String getEncryptBindSql() {
return "EncryptByPassPhrase(?,?)";
}
}
private static class DateFunction implements DbEncryptFunction {
@Override
public String getDecryptSql(String columnWithTableAlias) {
return "cast(convert(nvarchar,DecryptByPassPhrase(?," + columnWithTableAlias + ")) as date)";
}
@Override
public String getEncryptBindSql() {
return "EncryptByPassPhrase(?,format(?,'yyyy-MM-dd'))";
}
}
}
@@ -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,52 @@ 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();
/**
* Return the hash of the plan.
* Migrate to sql().
*/
String getHash();
@Deprecated
default String getSql() {
return sql();
}
/**
* Return the hash of the sql.
*/
long sqlHash();
/**
* Migrate to sqlHash().
*/
@Deprecated
default long getSqlHash() {
return sqlHash();
}
}
@@ -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();
long sqlHash();
/**
* 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();
}
@@ -6,30 +6,79 @@ package io.ebean.meta;
*/
public interface MetaTimedMetric extends MetaMetric {
/**
* Return the metric location hash if defined.
* <p>
* This hash excludes line number with the intention of being stable over time
* as code changes move the source line (but the method is the same).
*/
long locationHash();
/**
* 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.
@@ -6,7 +6,7 @@ package io.ebean.meta;
public class MetricData {
private String name;
private String hash;
private long sqlHash;
private String loc;
private String sql;
@@ -14,6 +14,7 @@ public class MetricData {
private Long mean;
private Long max;
private Long total;
private long locHash;
public MetricData(String name) {
this.name = name;
@@ -30,12 +31,20 @@ public class MetricData {
this.name = name;
}
public String getHash() {
return hash;
public long getSqlHash() {
return sqlHash;
}
public void setHash(String hash) {
this.hash = hash;
public void setSqlHash(long sqlHash) {
this.sqlHash = sqlHash;
}
public void setLocHash(long locHash) {
this.locHash = locHash;
}
public long getLocHash() {
return locHash;
}
public String getLoc() {
@@ -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.
@@ -10,7 +10,7 @@ public class QueryPlanInit {
private boolean all;
private Set<String> hashes = new HashSet<>();
private Set<Long> hashes = new HashSet<>();
private long thresholdMicros;
@@ -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,28 +40,28 @@ 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;
}
/**
* Return true if the query plan should be initiated based on it's hash.
*/
public boolean includeHash(String hash) {
return all || hashes.contains(hash);
public boolean includeHash(long sqlHash) {
return all || hashes.contains(sqlHash);
}
/**
* Return the specific hashes that we want to collect query plans on.
*/
public Set<String> getHashes() {
public Set<Long> sqlHashes() {
return hashes;
}
/**
* Set the specific hashes that we want to collect query plans on.
*/
public void setHashes(Set<String> hashes) {
public void sqlHashes(Set<Long> 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());
}
}
}
@@ -12,6 +12,11 @@ public interface TimedMetricStats extends MetaTimedMetric {
*/
void setLocation(String location);
/**
* Additionally set the location hash.
*/
void setLocationHash(long locationHash);
/**
* Override the name based on profile location.
*/
@@ -66,4 +66,17 @@ public class JdbcClose {
logger.warn("Error on connection rollback", e);
}
}
/**
* Cancels the statement
*/
public static void cancel(Statement stmt) {
try {
if (stmt != null) {
stmt.cancel();
}
} catch (SQLException e) {
logger.warn("Error on cancelling statement", e);
}
}
}
+3 -3
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.11.0</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.8.1</tag>
<tag>ebean-parent-12.11.0</tag>
</scm>
<name>ebean autotune</name>
@@ -26,7 +26,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.1</version>
<version>12.11.0</version>
<scope>provided</scope>
</dependency>
@@ -113,7 +113,7 @@ public class AutoTuneDiffCollection {
diffCount++;
Origin origin = createOrigin(entry, point, tuneDetail.toString());
Origin origin = createOrigin(entry, point, tuneDetail.asString());
ProfileDiff diff = document.getProfileDiff();
if (diff == null) {
diff = new ProfileDiff();
@@ -147,7 +147,7 @@ public class AutoTuneDiffCollection {
Origin origin = new Origin();
origin.setKey(point.getKey());
origin.setBeanType(point.getBeanType());
origin.setDetail(entry.getDetail().toString());
origin.setDetail(entry.getDetail().asString());
origin.setCallStack(point.getCallOrigin().getFullDescription());
origin.setOriginal(query);
@@ -46,7 +46,6 @@ public class ProfileManager implements ProfilingListener {
@Override
public boolean isProfileRequest(ObjectGraphNode origin, SpiQuery<?> query) {
ProfileOrigin profileOrigin = profileMap.get(origin.getOriginQueryPoint().getKey());
if (profileOrigin == null) {
profileMap.put(origin.getOriginQueryPoint().getKey(), createProfileOrigin(origin, query));
@@ -61,12 +60,11 @@ public class ProfileManager implements ProfilingListener {
* <p>
* For new profiling entries it is useful to compare the profiling against the current
* query detail that is specified in the code (as the query might already be manually optimised).
* </p>
*/
private ProfileOrigin createProfileOrigin(ObjectGraphNode origin, SpiQuery<?> query) {
ProfileOrigin profileOrigin = new ProfileOrigin(origin.getOriginQueryPoint(), queryTuningAddVersion, profilingBase, profilingRate);
// set the current query detail (fetch group) so that we can compare against profiling for new entries
profileOrigin.setOriginalQuery(query.getDetail().toString());
profileOrigin.setOriginalQuery(query.getDetail().asString());
return profileOrigin;
}
@@ -77,7 +75,6 @@ public class ProfileManager implements ProfilingListener {
*/
@Override
public void collectQueryInfo(ObjectGraphNode node, long beans, long micros) {
if (node != null) {
ObjectGraphOrigin origin = node.getOriginQueryPoint();
if (origin != null) {
@@ -92,11 +89,9 @@ public class ProfileManager implements ProfilingListener {
* <p>
* This is sent to use from a EntityBeanIntercept when the finalise method
* is called on the bean.
* </p>
*/
@Override
public void collectNodeUsage(NodeUsageCollector usageCollector) {
ProfileOrigin profileOrigin = getProfileOrigin(usageCollector.getNode().getOriginQueryPoint());
profileOrigin.collectUsageInfo(usageCollector);
}
@@ -114,16 +109,13 @@ public class ProfileManager implements ProfilingListener {
* Collect all the profiling information.
*/
public AutoTuneCollection profilingCollection(boolean reset) {
AutoTuneCollection req = new AutoTuneCollection();
for (ProfileOrigin origin : profileMap.values()) {
BeanDescriptor<?> desc = server.getBeanDescriptorById(origin.getOrigin().getBeanType());
if (desc != null) {
origin.profilingCollection(desc, req, reset);
}
}
return req;
}
@@ -65,7 +65,7 @@ public class TunedQueryInfo implements Serializable {
@Override
public String toString() {
return tunedDetail.toString();
return tunedDetail.asString();
}
}
@@ -27,7 +27,7 @@ public class ProfileOriginTest extends BaseTestCase {
OrmQueryDetail detail = po.buildDetail(desc);
assertThat(detail.asStringDebug().trim()).isEqualTo("fetch customer (name)");
assertThat(detail.asString().trim()).isEqualTo("fetch customer (name)");
}
@Test
@@ -46,7 +46,7 @@ public class ProfileOriginTest extends BaseTestCase {
OrmQueryDetail detail = po.buildDetail(desc);
assertThat(detail.asStringDebug()).isEqualTo("select (orderDate) fetch customer (name)");
assertThat(detail.asString()).isEqualTo("select (orderDate) fetch customer (name)");
}
@Test
@@ -64,7 +64,7 @@ public class ProfileOriginTest extends BaseTestCase {
OrmQueryDetail detail = po.buildDetail(desc);
assertThat(detail.asStringDebug().trim()).isEqualTo("select (orderDate,customer)");
assertThat(detail.asString().trim()).isEqualTo("select (orderDate,customer)");
}
@Test
@@ -87,7 +87,7 @@ public class ProfileOriginTest extends BaseTestCase {
OrmQueryDetail detail = po.buildDetail(desc);
assertThat(detail.asStringDebug()).isEqualTo("select (orderDate) fetch customer (billingAddress)");
assertThat(detail.asString()).isEqualTo("select (orderDate) fetch customer (billingAddress)");
}
@@ -116,7 +116,7 @@ public class ProfileOriginTest extends BaseTestCase {
po.collectUsageInfo(c);
OrmQueryDetail detail = po.buildDetail(desc);
assertThat(detail.asStringDebug()).isEqualTo("fetch customer (name,note) fetch customer.billingAddress (line1)");
assertThat(detail.asString()).isEqualTo("fetch customer (name,note) fetch customer.billingAddress (line1)");
}
private NodeUsageCollector node(String path) {
+19 -19
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.11.0</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.8.1</ebean-agent.version>
<ebean-maven-plugin.version>12.8.1</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.8.1</version>
<version>12.11.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.8.1</version>
<version>12.11.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.8.1</version>
<version>12.11.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.8.1</version>
<version>12.11.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.8.1</version>
<version>12.11.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.8.1</version>
<version>12.11.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-xml</artifactId>
<version>12.8.1</version>
<version>12.11.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-autotune</artifactId>
<version>12.8.1</version>
<version>12.11.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>12.8.1</version>
<version>12.11.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>12.8.1</version>
<version>12.11.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>12.8.1</version>
<version>12.11.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.8.1</version>
<version>12.11.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis</artifactId>
<version>12.8.1</version>
<version>12.11.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-redis</artifactId>
<version>12.8.1</version>
<version>12.11.0</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.11.0</version>
</parent>
<artifactId>ebean-core-type</artifactId>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.8.1</version>
<version>12.11.0</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();
}
@@ -11,8 +11,6 @@ public interface DataReader {
boolean next() throws SQLException;
void resetColumnPosition();
void incrementPos(int increment);
byte[] getBinaryBytes() throws SQLException;
@@ -50,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.
+20 -20
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.8.1</version>
<version>12.11.0</version>
</parent>
<artifactId>ebean-core</artifactId>
@@ -15,7 +15,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-parent-12.8.1</tag>
<tag>ebean-parent-12.11.0</tag>
</scm>
<profiles>
@@ -52,13 +52,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 +72,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.6.1</version>
<version>12.9.4-RC1</version>
<scope>test</scope>
</dependency>
@@ -87,19 +87,19 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.8.1</version>
<version>12.11.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.8.1</version>
<version>12.11.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.8.1</version>
<version>12.11.0</version>
</dependency>
<dependency>
@@ -119,6 +119,15 @@
<scope>provided</scope>
</dependency>
<!-- Provided scope so that the H2HistoryTrigger can live in Ebean core
and not require a separate module for it -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
@@ -173,7 +182,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.10</version>
<version>42.2.20</version>
<optional>true</optional>
</dependency>
@@ -214,15 +223,6 @@
<scope>test</scope>
</dependency>
<!-- Provided scope so that the H2HistoryTrigger can live in Ebean core
and not require a separate module for it -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
@@ -282,7 +282,7 @@
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
<version>2.7</version>
<scope>test</scope>
</dependency>
@@ -302,7 +302,7 @@
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>12.8.1</version>
<version>12.10.0</version>
<executions>
<execution>
<id>test</id>
@@ -155,7 +155,6 @@ public class BindParams implements Serializable {
* Set an In Out parameter using position.
*/
public void setParameter(int position, Object value, int outType) {
Param p = getParam(position);
p.setInValue(value);
p.setOutType(outType);
@@ -178,8 +177,8 @@ public class BindParams implements Serializable {
* Using position set the In value of a parameter. Note that for nulls you
* must use setNullParameter.
*/
@SuppressWarnings("rawtypes")
public void setParameter(int position, Object value) {
Param p = getParam(position);
if (value instanceof Collection) {
// use of postgres ANY with positioned parameter
@@ -214,7 +213,6 @@ public class BindParams implements Serializable {
* Set a named In Out parameter.
*/
public void setParameter(String name, Object value, int outType) {
Param p = getParam(name);
p.setInValue(value);
p.setOutType(outType);
@@ -232,7 +230,6 @@ public class BindParams implements Serializable {
* Set a named In parameter that is not null.
*/
public Param setParameter(String name, Object value) {
Param p = getParam(name);
p.setInValue(value);
return p;
@@ -299,7 +296,6 @@ public class BindParams implements Serializable {
* Return true if the bind hash and count has not changed.
*/
public boolean isSameBindHash() {
if (bindHash == null) {
bindHash = calcQueryPlanHash();
return false;
@@ -330,10 +326,6 @@ public class BindParams implements Serializable {
private final StringBuilder preparedSql;
public OrderedList() {
this(new ArrayList<>());
}
public OrderedList(List<Param> paramList) {
this.paramList = paramList;
this.preparedSql = new StringBuilder();
@@ -71,7 +71,6 @@ public class LoadManyRequest extends LoadRequest {
* This for use when lazy loading is invoked on methods such as clear() and removeAll() where it
* generally makes sense to only fetch the Id values as the other property information is not
* used.
* </p>
*/
private boolean isOnlyIds() {
return onlyIds;
@@ -91,18 +90,16 @@ public class LoadManyRequest extends LoadRequest {
return loadContext.getBatchSize();
}
private List<Object> getParentIdList() {
private List<Object> parentIdList(SpiEbeanServer server) {
List<Object> idList = new ArrayList<>();
BeanPropertyAssocMany<?> many = getMany();
for (BeanCollection<?> bc : batch) {
idList.add(many.getParentId(bc.getOwnerBean()));
bc.setLoader(server); // don't use the load buffer again
}
if (many.getTargetDescriptor().isPadInExpression()) {
BindPadding.padIds(idList);
}
return idList;
}
@@ -111,9 +108,7 @@ public class LoadManyRequest extends LoadRequest {
}
public SpiQuery<?> createQuery(SpiEbeanServer server) {
BeanPropertyAssocMany<?> many = getMany();
SpiQuery<?> query = many.newQuery(server);
String orderBy = many.getLazyFetchOrderBy();
if (orderBy != null) {
@@ -124,11 +119,11 @@ public class LoadManyRequest extends LoadRequest {
if (extraWhere != null) {
// replace special ${ta} placeholder with the base table alias
// which is always t0 and add the extra where clause
query.where().raw(extraWhere.replace("${ta}", "t0"));
query.where().raw(extraWhere.replace("${ta}", "t0").replace("${mta}", "int_"));
}
query.setLazyLoadForParents(many);
many.addWhereParentIdIn(query, getParentIdList(), loadContext.isUseDocStore());
many.addWhereParentIdIn(query, parentIdList(server), loadContext.isUseDocStore());
query.setPersistenceContext(loadContext.getPersistenceContext());
String mode = isLazy() ? "+lazy" : "+query";
@@ -146,7 +141,6 @@ public class LoadManyRequest extends LoadRequest {
// override to just select the Id values
query.select(many.getTargetIdProperty());
}
return query;
}
@@ -154,10 +148,8 @@ public class LoadManyRequest extends LoadRequest {
* After the query execution check for empty collections and load L2 cache if desired.
*/
public void postLoad() {
BeanDescriptor<?> desc = loadContext.getBeanDescriptor();
BeanPropertyAssocMany<?> many = getMany();
// check for BeanCollection's that where never processed
// in the +query or +lazy load due to no rows (predicates)
for (BeanCollection<?> bc : batch) {
@@ -172,6 +164,5 @@ public class LoadManyRequest extends LoadRequest {
desc.cacheManyPropPut(many, bc, parentId);
}
}
}
}
@@ -73,6 +73,10 @@ public class ScopeTrans {
restoreBatchGeneratedKeys = transaction.getBatchGetGeneratedKeys();
restoreBatchFlushOnQuery = transaction.isFlushOnQuery();
}
Boolean autoPersistUpdates = txScope.getAutoPersistUpdates();
if (autoPersistUpdates != null) {
transaction.setAutoPersistUpdates(autoPersistUpdates);
}
if (txScope.isBatchSet()) {
transaction.setBatchMode(txScope.isBatchMode());
}
@@ -0,0 +1,17 @@
package io.ebeaninternal.api;
import io.ebean.bean.EntityBean;
/**
* SPI interface for underlying BeanDescriptor.
*/
public interface SpiBeanType {
/**
* Return true if the bean contains a many property that has modifications.
* <p>
* That is a ManyToMany or a OneToMany with orphan removal with additions
* or removals from the collection.
*/
boolean isToManyDirty(EntityBean bean);
}
@@ -0,0 +1,13 @@
package io.ebeaninternal.api;
/**
* Manager of SpiBeanTypes.
*/
public interface SpiBeanTypeManager {
/**
* Return the bean type for the given entity class.
*/
SpiBeanType getBeanType(Class<?> entityType);
}
@@ -0,0 +1,26 @@
package io.ebeaninternal.api;
import javax.persistence.PersistenceException;
import io.ebean.CancelableQuery;
/**
* Cancellable query, that has a delegate.
*
* @author Roland Praml, FOCONIS AG
*
*/
public interface SpiCancelableQuery extends CancelableQuery {
/**
* Checks if the query was cancelled.
* @throws PersistenceException if query was cancelled.
*/
void checkCancelled();
/**
* Set the underlying cancelable query (with the PreparedStatement).
*/
void setCancelableQuery(CancelableQuery cancelableQuery);
}
@@ -1,14 +1,6 @@
package io.ebeaninternal.api;
import io.ebean.DtoQuery;
import io.ebean.EbeanServer;
import io.ebean.ExtendedServer;
import io.ebean.PersistenceContextScope;
import io.ebean.Query;
import io.ebean.RowConsumer;
import io.ebean.RowMapper;
import io.ebean.Transaction;
import io.ebean.TxScope;
import io.ebean.*;
import io.ebean.bean.BeanCollectionLoader;
import io.ebean.bean.CallOrigin;
import io.ebean.config.DatabaseConfig;
@@ -25,6 +17,7 @@ import io.ebeaninternal.server.transaction.RemoteTransactionEvent;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* Service Provider extension to EbeanServer.
@@ -234,6 +227,11 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect
*/
<T> List<T> findSingleAttributeList(SpiSqlQuery query, Class<T> cls);
/**
* SqlQuery find single attribute streaming the result to a consumer.
*/
<T> void findSingleAttributeEach(SpiSqlQuery query, Class<T> cls, Consumer<T> consumer);
/**
* SqlQuery find one with mapper.
*/
@@ -249,6 +247,16 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanCollect
*/
void findEachRow(SpiSqlQuery query, RowConsumer consumer);
/**
* DTO findIterate query.
*/
<T> QueryIterator<T> findDtoIterate(SpiDtoQuery<T> query);
/**
* DTO findStream query.
*/
<T> Stream<T> findDtoStream(SpiDtoQuery<T> query);
/**
* DTO findList query.
*/
@@ -105,4 +105,9 @@ public interface SpiExpression extends Expression {
* Check for match to a natural key query returning false if it doesn't match.
*/
boolean naturalKey(NaturalKeyQueryData<?> data);
/**
* Apply property prefix when filterMany expressions included into main query.
*/
void prefixProperty(String path);
}
@@ -43,4 +43,9 @@ public interface SpiExpressionList<T> extends ExpressionList<T>, SpiExpression {
default void applyRowLimits(SpiQuery<?> query) {
// do nothing by default
}
/**
* Apply property prefix when filterMany expressions included in main query.
*/
void prefixProperty(String path);
}
@@ -0,0 +1,17 @@
package io.ebeaninternal.api;
import io.ebean.bean.PersistenceContext;
import java.util.List;
/**
* SPI extension to PersistenceContext.
*/
public interface SpiPersistenceContext extends PersistenceContext {
/**
* Return the list of dirty beans held by this persistence context.
*/
List<Object> dirtyBeans(SpiBeanTypeManager manager);
}
@@ -17,7 +17,6 @@ import io.ebeaninternal.server.core.SpiOrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.query.CancelableQuery;
import io.ebeaninternal.server.querydefn.NaturalKeyBindParam;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
import io.ebeaninternal.server.querydefn.OrmQueryProperties;
@@ -31,7 +30,7 @@ import java.util.Set;
/**
* Object Relational query - Internal extension to Query object.
*/
public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCodes {
public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCodes, SpiCancelableQuery {
enum Mode {
NORMAL(false), LAZYLOAD_MANY(false), LAZYLOAD_BEAN(true), REFRESH_BEAN(true);
@@ -847,16 +846,6 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
*/
ReadEvent getFutureFetchAudit();
/**
* Set the underlying cancelable query (with the PreparedStatement).
*/
void setCancelableQuery(CancelableQuery cancelableQuery);
/**
* Return true if this query has been cancelled.
*/
boolean isCancelled();
/**
* Return the base table to use if user defined on the query.
*/
@@ -18,9 +18,9 @@ public interface SpiQueryPlan {
String getName();
/**
* The hash for the query plan.
* The hash of the sql.
*/
String getHash();
long getSqlHash();
/**
* The SQL for the query plan.
@@ -3,7 +3,7 @@ package io.ebeaninternal.api;
/**
* SQL query binding (for SqlQuery and DtoQuery).
*/
public interface SpiSqlBinding {
public interface SpiSqlBinding extends SpiCancelableQuery {
/**
* Return the named or positioned parameters.
@@ -3,7 +3,6 @@ package io.ebeaninternal.api;
import io.ebean.ProfileLocation;
import io.ebean.Transaction;
import io.ebean.annotation.DocStoreMode;
import io.ebean.bean.PersistenceContext;
import io.ebean.event.changelog.BeanChange;
import io.ebean.event.changelog.ChangeSet;
import io.ebeaninternal.server.core.PersistDeferredRelationship;
@@ -147,6 +146,11 @@ public interface SpiTransaction extends Transaction {
*/
int depth();
/**
* Return true if dirty beans are automatically persisted.
*/
boolean isAutoPersistUpdates();
/**
* Return true if this transaction was created explicitly via
* <code>Ebean.beginTransaction()</code>.
@@ -191,7 +195,7 @@ public interface SpiTransaction extends Transaction {
* later. This is along the lines of 'extended persistence context'
* behaviour.
*/
PersistenceContext getPersistenceContext();
SpiPersistenceContext getPersistenceContext();
/**
* Set the persistence context to this transaction.
@@ -203,7 +207,7 @@ public interface SpiTransaction extends Transaction {
* and setPersistenceContext() enable a developer to reuse a single
* PersistenceContext with multiple transactions.
*/
void setPersistenceContext(PersistenceContext context);
void setPersistenceContext(SpiPersistenceContext context);
/**
* Return the underlying Connection for internal use.
@@ -3,7 +3,6 @@ package io.ebeaninternal.api;
import io.ebean.ProfileLocation;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.bean.PersistenceContext;
import io.ebean.event.changelog.BeanChange;
import io.ebean.event.changelog.ChangeSet;
import io.ebeaninternal.server.core.PersistDeferredRelationship;
@@ -43,6 +42,16 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
return transaction.getLabel();
}
@Override
public void setAutoPersistUpdates(boolean autoPersistUpdates) {
transaction.setAutoPersistUpdates(autoPersistUpdates);
}
@Override
public boolean isAutoPersistUpdates() {
return transaction.isAutoPersistUpdates();
}
@Override
public void commitAndContinue() {
transaction.commitAndContinue();
@@ -369,12 +378,12 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
}
@Override
public PersistenceContext getPersistenceContext() {
public SpiPersistenceContext getPersistenceContext() {
return transaction.getPersistenceContext();
}
@Override
public void setPersistenceContext(PersistenceContext context) {
public void setPersistenceContext(SpiPersistenceContext context) {
transaction.setPersistenceContext(context);
}
@@ -1,14 +1,10 @@
package io.ebeaninternal.server.core;
import io.ebean.EbeanServer;
import io.ebean.CancelableQuery;
import io.ebean.Transaction;
import io.ebean.util.JdbcClose;
import io.ebeaninternal.api.BindParams;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.SpiSqlBinding;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.server.lib.Str;
import io.ebeaninternal.api.*;
import io.ebeaninternal.server.util.Str;
import io.ebeaninternal.server.persist.Binder;
import io.ebeaninternal.server.persist.TrimLogSql;
import io.ebeaninternal.server.util.BindParamsParser;
@@ -17,11 +13,14 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.locks.ReentrantLock;
import javax.persistence.PersistenceException;
/**
* Wraps the objects involved in executing a SQL / Relational Query.
*/
public abstract class AbstractSqlQueryRequest {
public abstract class AbstractSqlQueryRequest implements CancelableQuery {
protected final SpiSqlBinding query;
@@ -41,6 +40,8 @@ public abstract class AbstractSqlQueryRequest {
protected long startNano;
private final ReentrantLock lock = new ReentrantLock();
/**
* Create the BeanFindRequest.
*/
@@ -48,6 +49,7 @@ public abstract class AbstractSqlQueryRequest {
this.server = server;
this.query = query;
this.transaction = (SpiTransaction) t;
this.query.setCancelableQuery(this);
}
/**
@@ -83,11 +85,6 @@ public abstract class AbstractSqlQueryRequest {
return transaction.isLogSql();
}
/**
* Set the resultSet and associated query plan if known.
*/
abstract void setResultSet(ResultSet resultSet, Object queryPlanKey) throws SQLException;
/**
* Return the bindLog for this request.
*/
@@ -95,12 +92,15 @@ public abstract class AbstractSqlQueryRequest {
return bindLog;
}
/**
* Set the resultSet and associated query plan if known.
*/
abstract void setResultSet(ResultSet resultSet, Object queryPlanKey) throws SQLException;
/**
* Return true if we can navigate to the next row.
*/
public boolean next() throws SQLException {
return resultSet.next();
}
public abstract boolean next() throws SQLException;
protected abstract void requestComplete();
@@ -113,7 +113,6 @@ public abstract class AbstractSqlQueryRequest {
JdbcClose.close(pstmt);
}
/**
* Prepare the SQL taking into account named bind parameters.
*/
@@ -145,24 +144,30 @@ public abstract class AbstractSqlQueryRequest {
}
protected void executeAsSql(Binder binder) throws SQLException {
prepareSql();
Connection conn = transaction.getInternalConnection();
pstmt = conn.prepareStatement(sql);
if (query.getTimeout() > 0) {
pstmt.setQueryTimeout(query.getTimeout());
lock.lock();
try {
query.checkCancelled();
prepareSql();
Connection conn = transaction.getInternalConnection();
pstmt = conn.prepareStatement(sql);
if (query.getTimeout() > 0) {
pstmt.setQueryTimeout(query.getTimeout());
}
if (query.getBufferFetchSizeHint() > 0) {
pstmt.setFetchSize(query.getBufferFetchSizeHint());
}
BindParams bindParams = query.getBindParams();
if (!bindParams.isEmpty()) {
this.bindLog = binder.bind(bindParams, pstmt, conn);
}
if (isLogSql()) {
transaction.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ")"));
}
} finally {
lock.unlock();
}
if (query.getBufferFetchSizeHint() > 0) {
pstmt.setFetchSize(query.getBufferFetchSizeHint());
}
BindParams bindParams = query.getBindParams();
if (!bindParams.isEmpty()) {
this.bindLog = binder.bind(bindParams, pstmt, conn);
}
if (isLogSql()) {
transaction.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ")"));
}
setResultSet(pstmt.executeQuery(), null);
query.checkCancelled();
}
/**
@@ -172,4 +177,13 @@ public abstract class AbstractSqlQueryRequest {
return sql;
}
@Override
public void cancel() {
lock.lock();
try {
JdbcClose.cancel(pstmt);
} finally {
lock.unlock();
}
}
}
@@ -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());
}
}
}
@@ -236,7 +236,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
this.clockService = config.getClockService();
DocStoreIntegration docStoreComponents = config.createDocStoreIntegration(this);
this.transactionManager = config.createTransactionManager(docStoreComponents.updateProcessor());
this.transactionManager = config.createTransactionManager(this, docStoreComponents.updateProcessor());
this.documentStore = docStoreComponents.documentStore();
this.queryPlanManager = config.initQueryPlanManager(transactionManager);
this.metaInfoManager = new DefaultMetaInfoManager(this);
@@ -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);
@@ -1283,14 +1284,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();
}
@@ -1382,7 +1381,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Nonnull
@Override
public <T> FutureList<T> findFutureList(Query<T> query, Transaction t) {
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
SpiQuery<T> spiQuery = (SpiQuery<T>) query.copy();
spiQuery.setFutureFetch(true);
// FutureList query always run in it's own persistence content
spiQuery.setPersistenceContext(new DefaultPersistenceContext());
@@ -1433,32 +1432,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Nonnull
@Override
public <T> Stream<T> findStream(Query<T> query, Transaction transaction) {
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, transaction);
try {
request.initTransIfRequired();
return toStream(request.findIterate());
} catch (RuntimeException ex) {
request.endTransIfRequired();
throw ex;
}
return toStream(findIterate(query, transaction));
}
private <T> Stream<T> toStream(QueryIterator<T> queryIterator) {
return stream(spliteratorUnknownSize(queryIterator, Spliterator.ORDERED), false)
.onClose(new QueryIteratorClose(queryIterator));
}
private static class QueryIteratorClose implements Runnable {
private final QueryIterator<?> iterator;
private QueryIteratorClose(QueryIterator<?> iterator) {
this.iterator = iterator;
}
@Override
public void run() {
iterator.close();
}
return stream(spliteratorUnknownSize(queryIterator, Spliterator.ORDERED), false).onClose(queryIterator::close);
}
@Override
@@ -1606,6 +1584,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return executeSqlQuery((req) -> req.findOneMapper(mapper), query);
}
@Override
public <T> void findSingleAttributeEach(SpiSqlQuery query, Class<T> cls, Consumer<T> consumer) {
executeSqlQuery((req) -> req.findSingleAttributeEach(cls, consumer), query);
}
@Override
public <T> List<T> findSingleAttributeList(SpiSqlQuery query, Class<T> cls) {
return executeSqlQuery((req) -> req.findSingleAttributeList(cls), query);
@@ -1649,6 +1632,23 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
}
@Override
public <T> QueryIterator<T> findDtoIterate(SpiDtoQuery<T> query) {
DtoQueryRequest<T> request = new DtoQueryRequest<>(this, dtoQueryEngine, query);
try {
request.initTransIfRequired();
return request.findIterate();
} catch (RuntimeException ex) {
request.endTransIfRequired();
throw ex;
}
}
@Override
public <T> Stream<T> findDtoStream(SpiDtoQuery<T> query) {
return toStream(findDtoIterate(query));
}
@Override
public <T> List<T> findDtoList(SpiDtoQuery<T> query) {
DtoQueryRequest<T> request = new DtoQueryRequest<>(this, dtoQueryEngine, query);
@@ -2328,13 +2328,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);
@@ -2351,7 +2351,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);
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.core;
import io.ebean.QueryIterator;
import io.ebean.core.type.DataReader;
import io.ebeaninternal.api.SpiDtoQuery;
import io.ebeaninternal.api.SpiEbeanServer;
@@ -23,6 +24,9 @@ import java.util.function.Predicate;
*/
public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
private static final String ENC_PREFIX = EncryptAlias.PREFIX;
private static final String ENC_PREFIX_UPPER = EncryptAlias.PREFIX.toUpperCase();
private final SpiDtoQuery<T> query;
private final DtoQueryEngine queryEngine;
@@ -49,6 +53,7 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
ormQuery.setType(type);
ormQuery.setManualId();
query.setCancelableQuery(ormQuery);
// execute the underlying ORM query returning the ResultSet
SpiResultSet result = server.findResultSet(ormQuery, transaction);
this.pstmt = result.getStatement();
@@ -87,6 +92,11 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
}
}
public QueryIterator<T> findIterate() {
flushJdbcBatchOnQuery();
return queryEngine.findIterate(this);
}
public void findEach(Consumer<T> consumer) {
flushJdbcBatchOnQuery();
queryEngine.findEach(this, consumer);
@@ -107,9 +117,13 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
return queryEngine.findList(this);
}
public boolean next() throws SQLException {
query.checkCancelled();
return dataReader.next();
}
@SuppressWarnings("unchecked")
public T readNextBean() throws SQLException {
dataReader.resetColumnPosition();
return (T) plan.readRow(dataReader);
}
@@ -133,9 +147,9 @@ public final class DtoQueryRequest<T> extends AbstractSqlQueryRequest {
}
static String parseColumn(String columnLabel) {
if (columnLabel.startsWith("_e_") || columnLabel.startsWith("_E_")) {
if (columnLabel.startsWith(ENC_PREFIX) || columnLabel.startsWith(ENC_PREFIX_UPPER)) {
// encrypted column alias in the form _e_<tableAlias>_<column>
final int pos = columnLabel.indexOf("_", 3);
final int pos = columnLabel.indexOf("_", 4);
if (pos > -1) {
return columnLabel.substring(pos + 1);
}
@@ -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,39 @@ class DumpMetrics {
appendQueryName(metric, sb);
appendCounters(metric, sb);
if (dumpHash) {
sb.append("\n hash:").append(metric.getHash());
sb.append("\n sqlHash:").append(metric.sqlHash());
}
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);
sb.append("\n locHash:").append(metric.locationHash());
}
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,32 @@ class DumpMetricsData {
private void add(MetaTimedMetric metric) {
final MetricData data = create(metric);
appendCounters(data, metric);
data.setLoc(metric.getLocation());
data.setLoc(metric.location());
data.setLocHash(metric.locationHash());
}
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.setSqlHash(metric.sqlHash());
}
private void appendLocationAndSql(MetricData data, MetaQueryMetric metric) {
data.setLoc(metric.getLocation());
data.setSql(metric.getSql());
data.setLocHash(metric.locationHash());
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,8 @@ class DumpMetricsJson implements ServerMetricsAsJson {
metricStart(metric);
appendTiming(metric);
if (isIncludeDetail(metric)) {
appendExtra("loc", metric.getLocation());
keyVal("locHash", metric.locationHash());
appendExtra("loc", metric.location());
}
metricEnd();
}
@@ -197,11 +198,12 @@ class DumpMetricsJson implements ServerMetricsAsJson {
metricStart(metric);
appendTiming(metric);
if (withHash) {
appendExtra("hash", metric.getHash());
keyVal("sqlHash", metric.sqlHash());
keyVal("locHash", metric.locationHash());
}
if (isIncludeDetail(metric)) {
appendExtra("loc", metric.getLocation());
appendExtra("sql", metric.getSql());
appendExtra("loc", metric.location());
appendExtra("sql", metric.sql());
}
metricEnd();
}
@@ -218,13 +220,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());
keyVal("count", timedMetric.count());
keyVal("total", timedMetric.total());
keyVal("mean", timedMetric.mean());
keyVal("max", timedMetric.max());
}
private void keyVal(String key, long value) throws IOException {
key(key);
val(value);
}
}
@@ -0,0 +1,8 @@
package io.ebeaninternal.server.core;
/**
* Used to create column alias for encrypted columns.
*/
public interface EncryptAlias {
String PREFIX = "zx__";
}
@@ -423,13 +423,13 @@ public class InternalConfiguration {
/**
* Create the TransactionManager taking into account autoCommit mode.
*/
TransactionManager createTransactionManager(DocStoreUpdateProcessor indexUpdateProcessor) {
TransactionManager createTransactionManager(SpiServer server, DocStoreUpdateProcessor indexUpdateProcessor) {
TransactionScopeManager scopeManager = createTransactionScopeManager();
boolean notifyL2CacheInForeground = cacheManager.isLocalL2Caching() || config.isNotifyL2CacheInForeground();
TransactionManagerOptions options =
new TransactionManagerOptions(notifyL2CacheInForeground, config, scopeManager, clusterManager, backgroundExecutor,
new TransactionManagerOptions(server, notifyL2CacheInForeground, config, scopeManager, clusterManager, backgroundExecutor,
indexUpdateProcessor, beanDescriptorManager, dataSource(), profileHandler(), logManager,
tableModState, cacheNotify, clockService);
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.core;
import io.ebean.CacheMode;
import io.ebean.CancelableQuery;
import io.ebean.OrderBy;
import io.ebean.PersistenceContextScope;
import io.ebean.QueryIterator;
@@ -35,7 +36,6 @@ import io.ebeaninternal.server.deploy.DeployPropertyParserMap;
import io.ebeaninternal.server.el.ElPropertyValue;
import io.ebeaninternal.server.loadcontext.DLoadContext;
import io.ebeaninternal.server.query.CQueryPlan;
import io.ebeaninternal.server.query.CancelableQuery;
import io.ebeaninternal.server.transaction.DefaultPersistenceContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -219,10 +219,10 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
*/
@Override
public void prepareQuery() {
secondaryQueries = query.convertJoins();
beanDescriptor.prepareQuery(query);
adapterPreQuery();
this.secondaryQueries = query.convertJoins();
this.queryPlanKey = query.prepare(this);
queryPlanKey = query.prepare(this);
}
public boolean isNativeSql() {
@@ -379,9 +379,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
public void setBatched() {
batched = true;
if (type == Type.INSERT || type == Type.UPDATE) {
// used to trigger automatic jdbc batch flush
intercept.registerGetterCallback(this);
getterCallback = true;
if (beanDescriptor.hasSingleIdProperty()) {
// used to trigger automatic jdbc batch flush
intercept.registerGetterCallback(this);
getterCallback = true;
}
}
}
@@ -887,6 +889,14 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
}
}
/**
* Remove deleted beans from the persistence context early.
*/
public void removeFromPersistenceContext() {
idValue = beanDescriptor.getId(entityBean);
beanDescriptor.contextDeleted(transaction.getPersistenceContext(), idValue);
}
/**
* Aggressive L1 and L2 cache cleanup for deletes.
*/
@@ -1030,6 +1040,17 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
if (!publish) {
beanDescriptor.setDraft(entityBean);
}
if (transaction.isAutoPersistUpdates() && idValue != null) {
// with getGeneratedKeys off we will not have a idValue
beanDescriptor.contextPut(transaction.getPersistenceContext(), idValue, entityBean);
}
}
/**
* Return if persist can be skipped on the reference only bean.
*/
public boolean isSkipReference() {
return intercept.isReference() || (Flags.isRecurse(flags) && beanDescriptor.referenceIdPropertyOnly(intercept));
}
public boolean isReference() {
@@ -3,7 +3,7 @@ package io.ebeaninternal.server.core;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiSqlUpdate;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.server.lib.Str;
import io.ebeaninternal.server.util.Str;
import io.ebeaninternal.server.persist.BatchControl;
import io.ebeaninternal.server.persist.PersistExecute;
import io.ebeaninternal.server.persist.TrimLogSql;
@@ -20,17 +20,22 @@ public interface RelationalQueryEngine {
/**
* Find a list of beans using relational query.
*/
List<SqlRow> findList(RelationalQueryRequest request);
/**
* Find each query using relational query.
*/
void findEach(RelationalQueryRequest request, Consumer<SqlRow> consumer);
<T> List<T> findList(RelationalQueryRequest request, RowReader<T> reader);
/**
* Find each while query using relational query.
*/
void findEach(RelationalQueryRequest request, Predicate<SqlRow> consumer);
<T> void findEach(RelationalQueryRequest request, RowReader<T> reader, Predicate<T> consumer);
/**
* Find each via raw consumer.
*/
void findEach(RelationalQueryRequest request, RowConsumer mapper);
/**
* Find one via mapper.
*/
<T> T findOne(RelationalQueryRequest request, RowMapper<T> mapper);
/**
* Find single attribute.
@@ -43,19 +48,9 @@ public interface RelationalQueryEngine {
<T> List<T> findSingleAttributeList(RelationalQueryRequest request, Class<T> cls);
/**
* Find one via mapper.
* Find single attribute streaming the result to a consumer.
*/
<T> T findOneMapper(RelationalQueryRequest request, RowMapper<T> mapper);
/**
* Find list via mapper.
*/
<T> List<T> findListMapper(RelationalQueryRequest request, RowMapper<T> mapper);
/**
* Find each via raw consumer.
*/
void findEachRow(RelationalQueryRequest request, RowConsumer mapper);
<T> void findSingleAttributeEach(RelationalQueryRequest request, Class<T> cls, Consumer<T> consumer);
/**
* Collect SQL query execution statistics.
@@ -1,10 +1,6 @@
package io.ebeaninternal.server.core;
import io.ebean.RowConsumer;
import io.ebean.RowMapper;
import io.ebean.SqlQuery;
import io.ebean.SqlRow;
import io.ebean.Transaction;
import io.ebean.*;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiSqlBinding;
@@ -57,18 +53,24 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
boolean findEachRow(RowConsumer mapper) {
flushJdbcBatchOnQuery();
queryEngine.findEachRow(this, mapper);
queryEngine.findEach(this, mapper);
return true;
}
<T> List<T> findListMapper(RowMapper<T> mapper) {
flushJdbcBatchOnQuery();
return queryEngine.findListMapper(this, mapper);
return queryEngine.findList(this, () -> mapper.map(resultSet, rows++));
}
<T> T findOneMapper(RowMapper<T> mapper) {
flushJdbcBatchOnQuery();
return queryEngine.findOneMapper(this, mapper);
return queryEngine.findOne(this, mapper);
}
public <T> boolean findSingleAttributeEach(Class<T> cls, Consumer<T> consumer) {
flushJdbcBatchOnQuery();
queryEngine.findSingleAttributeEach(this, cls, consumer);
return true;
}
public <T> List<T> findSingleAttributeList(Class<T> cls) {
@@ -83,17 +85,17 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
public void findEach(Consumer<SqlRow> consumer) {
flushJdbcBatchOnQuery();
queryEngine.findEach(this, consumer);
queryEngine.findEach(this, (resultSet, rowNum) -> consumer.accept(createNewRow()));
}
public void findEachWhile(Predicate<SqlRow> consumer) {
flushJdbcBatchOnQuery();
queryEngine.findEach(this, consumer);
queryEngine.findEach(this, this::createNewRow, consumer);
}
public List<SqlRow> findList() {
flushJdbcBatchOnQuery();
return queryEngine.findList(this);
return queryEngine.findList(this, this::createNewRow);
}
/**
@@ -135,16 +137,15 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
return resultSet;
}
public void incrementRows() {
rows++;
}
public <T> List<T> mapList(RowMapper<T> mapper) throws SQLException {
List<T> list = new ArrayList<>();
while (next()) {
list.add(mapper.map(resultSet, rows++));
@Override
public boolean next() throws SQLException {
query.checkCancelled();
if (!resultSet.next()) {
return false;
} else {
rows++;
return true;
}
return list;
}
public <T> T mapOne(RowMapper<T> mapper) throws SQLException {
@@ -0,0 +1,14 @@
package io.ebeaninternal.server.core;
import java.sql.SQLException;
/**
* Read a row building a result for that row.
*/
public interface RowReader<T> {
/**
* Build and return a result for a row.
*/
T read() throws SQLException;
}
@@ -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
@@ -38,15 +38,7 @@ import io.ebean.plugin.BeanType;
import io.ebean.plugin.ExpressionPath;
import io.ebean.plugin.Property;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.BeanCacheResult;
import io.ebeaninternal.api.CQueryPlanKey;
import io.ebeaninternal.api.ConcurrencyMode;
import io.ebeaninternal.api.LoadBeanContext;
import io.ebeaninternal.api.LoadContext;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.api.SpiUpdatePlan;
import io.ebeaninternal.api.*;
import io.ebeaninternal.api.TransactionEventTable.TableIUD;
import io.ebeaninternal.api.json.SpiJsonReader;
import io.ebeaninternal.api.json.SpiJsonWriter;
@@ -119,7 +111,7 @@ import static io.ebeaninternal.server.persist.DmlUtil.isNullOrZero;
/**
* Describes Beans including their deployment information.
*/
public class BeanDescriptor<T> implements BeanType<T>, STreeType {
public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
private static final Logger logger = LoggerFactory.getLogger(BeanDescriptor.class);
@@ -1566,8 +1558,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
void queryPlanInit(QueryPlanInit request, List<MetaQueryPlan> list) {
for (CQueryPlan queryPlan : queryPlanCache.values()) {
if (request.includeHash(queryPlan.getHash())) {
queryPlan.queryPlanInit(request.getThresholdMicros());
if (request.includeHash(queryPlan.getSqlHash())) {
queryPlan.queryPlanInit(request.thresholdMicros());
list.add(queryPlan.createMeta(null, null));
}
}
@@ -1580,7 +1572,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
iudMetrics.visit(visitor);
for (CQueryPlan queryPlan : queryPlanCache.values()) {
if (!queryPlan.isEmptyStats()) {
visitor.visitQuery(queryPlan.getSnapshot(visitor.isReset()));
visitor.visitQuery(queryPlan.getSnapshot(visitor.reset()));
}
}
}
@@ -1755,6 +1747,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return idProperty != null;
}
/**
* Return false for IdClass case with multiple @Id properties.
*/
public boolean hasSingleIdProperty() {
return idPropertyIndex != -1;
}
/**
* Return true if this type has a simple Id and the platform supports mutli-value binding.
*/
@@ -1880,7 +1879,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
if (refBean == null) {
refBean = createReference(readOnly, false, id, pc);
}
return (EntityBean)refBean;
return (EntityBean) refBean;
}
/**
@@ -2017,6 +2016,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return owner.getBeanDescriptor(otherType);
}
/**
* Returns true, if the table is managed (i.e. an existing m2m relation).
*/
public boolean isTableManaged(String tableName) {
return owner.isTableManaged(tableName);
}
/**
* Return the order column property.
*/
@@ -2473,7 +2479,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Return the property path given the db table and column.
*/
public String findBeanPath(String schemaName, String tableName, String columnName) {
if (matchBaseTable(schemaName, tableName)) {
if (matchBaseTable(tableName)) {
return columnPath.get(columnName);
}
BeanPropertyAssoc<?> assocProperty = tablePath.get(tableName);
@@ -2489,10 +2495,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return null;
}
private boolean matchBaseTable(String schemaName, String tableName) {
boolean matchBaseTable(String tableName) {
return tableName.isEmpty()
|| baseTable.equalsIgnoreCase(tableName)
|| baseTable.equalsIgnoreCase(schemaName + "." + tableName);
|| baseTable.endsWith("." + tableName);
}
/**
@@ -2937,6 +2943,20 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return false;
}
@Override
public boolean isToManyDirty(EntityBean bean) {
final EntityBeanIntercept ebi = bean._ebean_getIntercept();
for (BeanPropertyAssocMany<?> many : propertiesManySave) {
if (ebi.isLoadedProperty(many.getPropertyIndex())) {
final BeanCollection<?> value = (BeanCollection<?>) many.getValue(bean);
if (value != null && value.hasModifications()) {
return true;
}
}
}
return false;
}
/**
* Return true if the bean is draftable and considered a 'live' instance.
*/
@@ -3134,12 +3154,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return ebi.isReference() || referenceIdPropertyOnly(ebi);
}
boolean referenceIdPropertyOnly(EntityBeanIntercept ebi) {
public boolean referenceIdPropertyOnly(EntityBeanIntercept ebi) {
return idOnlyReference && ebi.hasIdOnly(idPropertyIndex);
}
public boolean isIdLoaded(EntityBeanIntercept ebi) {
return ebi.isLoadedProperty(idPropertyIndex);
// assume id loaded for IdClass case with idPropertyIndex == -1
return idPropertyIndex == -1 ? true : ebi.isLoadedProperty(idPropertyIndex);
}
boolean hasIdValue(EntityBean bean) {
@@ -3177,9 +3198,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
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);
@@ -26,9 +26,7 @@ import io.ebean.meta.MetricVisitor;
import io.ebean.meta.QueryPlanInit;
import io.ebean.plugin.BeanType;
import io.ebean.util.AnnotationUtil;
import io.ebeaninternal.api.ConcurrencyMode;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.TransactionEventTable;
import io.ebeaninternal.api.*;
import io.ebeaninternal.server.cache.CacheChangeSet;
import io.ebeaninternal.server.cache.SpiCacheManager;
import io.ebeaninternal.server.core.InternString;
@@ -88,7 +86,7 @@ import java.util.concurrent.TimeUnit;
/**
* Creates BeanDescriptors.
*/
public class BeanDescriptorManager implements BeanDescriptorMap {
public class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTypeManager {
private static final Logger logger = LoggerFactory.getLogger(BeanDescriptorManager.class);
@@ -263,6 +261,11 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
return descQueueMap.get(queueId);
}
@Override
public SpiBeanType getBeanType(Class<?> entityType) {
return getBeanDescriptor(entityType);
}
@Override
@SuppressWarnings("unchecked")
public <T> BeanDescriptor<T> getBeanDescriptor(Class<T> entityType) {
@@ -442,6 +445,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
return tableToDescMap.get(tableName.toLowerCase());
}
@Override
public boolean isTableManaged(String tableName) {
return tableToDescMap.get(tableName.toLowerCase()) != null
|| tableToViewDescMap.get(tableName.toLowerCase()) != null;
}
/**
* Invalidate entity beans based on views via their dependent tables.
*/
@@ -76,4 +76,10 @@ public interface BeanDescriptorMap {
* Return true if Jackson core is present on the classpath.
*/
boolean isJacksonCorePresent();
/**
* Returns true, if the given table (or view) is managed by ebean
* (= an entity exists)
*/
boolean isTableManaged(String tableName);
}
@@ -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,11 +14,13 @@ 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;
import io.ebeaninternal.api.json.SpiJsonReader;
import io.ebeaninternal.api.json.SpiJsonWriter;
import io.ebeaninternal.server.core.EncryptAlias;
import io.ebeaninternal.server.core.InternString;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedWhenCreated;
@@ -30,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;
@@ -51,12 +50,11 @@ 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;
import static io.ebean.util.StringHelper.replace;
/**
* Description of a property of a bean. Includes its deployment information such
* as database column mapping information.
@@ -65,6 +63,8 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
private static final Logger logger = LoggerFactory.getLogger(BeanProperty.class);
private static final String ENC_PREFIX = " " + EncryptAlias.PREFIX;
/**
* Flag to mark this is the id property.
*/
@@ -353,25 +353,26 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
}
private String tableAliasIntern(BeanDescriptor<?> descriptor, String s, boolean dbEncrypted, String dbColumn) {
if (descriptor != null) {
s = replace(s, "${ta}.", "${}");
s = replace(s, "${ta}", "${}");
if (s != null && descriptor != null) {
s = s.replace("${ta}.", "${}");
s = s.replace("${ta}", "${}");
if (dbEncrypted) {
s = dbEncryptFunction.getDecryptSql(s);
String namedParam = ":encryptkey_" + descriptor.getBaseTable() + "___" + dbColumn;
s = replace(s, "?", namedParam);
s = s.replace("?", namedParam);
}
}
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();
@@ -531,7 +532,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
* Return the SQL for the column including decryption function and column alias.
*/
private String getDecryptSqlWithColumnAlias(String tableAlias) {
return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()) + " _e_" + tableAlias + "_" + this.getDbColumn();
return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()) + ENC_PREFIX + tableAlias + "_" + this.getDbColumn();
}
@Override
@@ -641,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")
@@ -827,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>
@@ -908,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;
}
@@ -1017,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);
}
/**
@@ -1280,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);
}
/**
@@ -1393,7 +1394,8 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
/**
* Return the property type.
*/
@Override @Nonnull
@Override
@Nonnull
public Class<?> getPropertyType() {
return propertyType;
}

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