| Summary: | delete query fails with optimistic lock exception if JDBC batching is enabled on Oracle | ||
|---|---|---|---|
| Product: | z_Archived | Reporter: | Adrian Goerler <adrian.goerler> |
| Component: | Eclipselink | Assignee: | Nobody - feel free to take it <nobody> |
| Status: | RESOLVED FIXED | QA Contact: | |
| Severity: | normal | ||
| Priority: | P3 | CC: | andrei.ilitchev, eclipselink.foundation-inbox, jamesssss, kevin.yuan, ramkrishnal, tom.ware |
| Version: | unspecified | ||
| Target Milestone: | --- | ||
| Hardware: | PC | ||
| OS: | Windows 7 | ||
| Whiteboard: | oracle | ||
| Attachments: | |||
|
Description
Adrian Goerler
Created attachment 190269 [details]
proposed patch
Tested with Oracle10Platform:
- WDF: OK
- JPA SRG: OK
- Core SRG: OK
I talked to James about this bug and it appears to be bigger than it seems. Related bugs: Bug 266151 - Delete queries return invalid values for executeUpdate when batching is enabled; Bug 328714 - support batch writing with optimistic locking. Background: In DatabaseAccessor.basicExecuteCall when batch writing is enabled the decision is made whether to add the call to the batch or to execute the previously batched calls before executing the call: if (isInBatchWritingMode(session)) { // if there is nothing returned and we are not using optimistic locking then batch //if it is a StoredProcedure with in/out or out parameters then do not batch //logic may be weird but we must not batch if we are not using JDBC batchwriting and we have parameters // we may want to refactor this some day if (dbCall.isNothingReturned() && (!dbCall.hasOptimisticLock() || getPlatform().canBatchWriteWithOptimisticLocking(dbCall) ) && (!dbCall.shouldBuildOutputRow()) && (getPlatform().usesJDBCBatchWriting() || (!dbCall.hasParameters())) && (!dbCall.isLOBLocatorNeeded())) { // this will handle executing batched statements, or switching mechanisms if required getActiveBatchWritingMechanism().appendCall(session, dbCall); //bug 4241441: passing 1 back to avoid optimistic lock exceptions since there // is no way to know if it succeeded on the DB at this point. return Integer.valueOf(1); } else { getActiveBatchWritingMechanism().executeBatchedStatements(session); } } Problem 1. DeleteAllQuery doesn't support optimistic locking, therefore DeleteAllQuery should not set hasOptimisticLock flag to true (see CallQueryMechanism.prepareDeleteAll method - both of these calls should be removed). However, fixing Problem 1 arises the following Problem 2. There is no way to get a number of deleted fields from DeleteAllQuery if batch writing is used - it will always be batched (and therefore 1 always will be returned, see DatabaseAccessor.basicExecuteCall above). In fact, the same happens to native queries, too. Suggested solution is to define a new boolean flag indicating that the query call(s) could be batched (batchable ?) on ModifyQuery with default value true. A call that is not batchable will trigger batch execution in DatabaseAccessor.basicExecuteCall. The corresonding QueryHint should be defined for JPA. Also in JPA case the user usually expects the query to be executed immediately, therefore EJBQueryImpl.executeUpdate should set QueryHint.BATCHABLE to false (unless of course it has been already set to true by the user). So in JPA case by default UpdateAll, DeleteAll and native queries will be executed right away (even when batching is enabled0 and return the number of updated / deleted rows. Problem 3. DatabasePlatform.executeBatch method. Oracle10Platform uses getUpdateCount to retun the number of updated / deleted rows after batch writing. Why would not that work for not PreparedStatement, too? Does it work for any other data bases? If so, should be moved to the base method in DatabsePlatform. If instead of getUpdateCount method we still have to examine int[] updateResult = statement.executeBatch() then (in the base method, in both prepared and not prepared statement cases and based on the comment in Statement.executeBatch) we should do something like what currently done on MySQLPlatform (for some reason only in case of prepared statement): either mode forgiving version (used in MySQLPlatform): int[] updateResult = statement.executeBatch(); int updateCount = 0; for (int count : updateResult) { if (count == Statement.SUCCESS_NO_INFO) { count = 1; } updateCount += count; } return updateCount; or more strict: int[] updateResult = statement.executeBatch(); int updateCount = 0; for (int count : updateResult) { if (count >= 0) { updateCount += count; } } return updateCount; Adrian, please set the target for this bug when you know what it is. Sorry... didn't notice it was already targetted. I won't be able to deliver a patch that addresses the concerns raised by Andrei. Therefore, I am unassigning me from this bug. Created attachment 196776 [details]
Patch skipping the tests tests with issues on Oracle10Platform and higher
until a patch for this issue is found, the tests
TestDeleteQuery.testDeleteAllDepartments as well as
TestBiDirectionalManyToMany.testCascadeMerge
are skipped.
Tested on Oracle 11 with Oracle10Platform
*** Bug 338727 has been marked as a duplicate of this bug. *** Comment on attachment 196776 [details]
Patch skipping the tests tests with issues on Oracle10Platform and higher
Tested on Oracle 11.
Reviewed by Andrei
Checked in at 9474
Leaving this bug open until the issue is solved on Oracle so that the skip can be removed. The checked-in code has a problem. It adds a dependency to Oracle10Platform. Currently the wdf tests don't depend on our Oracle-specific projects, we get a compile failure. Can we skip them for OraclePlatform instead? (In reply to comment #10) > The checked-in code has a problem. > It adds a dependency to Oracle10Platform. Currently the wdf tests don't depend > on our Oracle-specific projects, we get a compile failure. Can we skip them > for OraclePlatform instead? Hm, it compiled fine on my side. I think the WDF test build against the ecliselink.jar, which contains the OraclePlatform. Actually, the test pass with the generic OraclePlatform as the generic OraclePlatform does not override the canBatchWriteWithOptimisticLocking method. So I am proposing to skip the test on the platform by name and not by class. Created attachment 196962 [details]
updated patch skipping the test on Oracle10 and Oracle11 (by platform class name)
Comment on attachment 196962 [details]
updated patch skipping the test on Oracle10 and Oracle11 (by platform class name)
Tested with plain OracelPlatform
checked in at 8979
*** Bug 380506 has been marked as a duplicate of this bug. *** Fixed, See, bug#328714 fixed in 2.5 The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink |