Throw OrmConcurrencyException when optimistic locking fails
If an entity is using @RowVersion to protect itself from concurrent
updates we may be able to detect concurrent updates when the UPDATE
SQL returns 0 rows affected. In such a case the caller may need to
handle the exception specially by retrying the operation.
Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/src/main/java/com/google/gwtorm/client/OrmConcurrencyException.java b/src/main/java/com/google/gwtorm/client/OrmConcurrencyException.java
new file mode 100644
index 0000000..e8da3d3
--- /dev/null
+++ b/src/main/java/com/google/gwtorm/client/OrmConcurrencyException.java
@@ -0,0 +1,30 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtorm.client;
+
+/** Indicates one or more entities were modified concurrently. */
+public class OrmConcurrencyException extends OrmException {
+ public OrmConcurrencyException() {
+ super("Concurrent modification detected");
+ }
+
+ public OrmConcurrencyException(final String message) {
+ super(message);
+ }
+
+ public OrmConcurrencyException(final String message, final Throwable why) {
+ super(message, why);
+ }
+}
diff --git a/src/main/java/com/google/gwtorm/jdbc/JdbcAccess.java b/src/main/java/com/google/gwtorm/jdbc/JdbcAccess.java
index 4cc35e5..bd08a98 100644
--- a/src/main/java/com/google/gwtorm/jdbc/JdbcAccess.java
+++ b/src/main/java/com/google/gwtorm/jdbc/JdbcAccess.java
@@ -16,6 +16,7 @@
import com.google.gwtorm.client.Access;
import com.google.gwtorm.client.Key;
+import com.google.gwtorm.client.OrmConcurrencyException;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.impl.AbstractAccess;
import com.google.gwtorm.client.impl.ListResultSet;
@@ -225,7 +226,7 @@
}
private static void execute(final PreparedStatement ps, final int cnt)
- throws SQLException {
+ throws SQLException, OrmConcurrencyException {
if (cnt == 0) {
return;
}
@@ -234,13 +235,9 @@
if (states == null) {
throw new SQLException("No rows affected; expected " + cnt + " rows");
}
- if (states.length != cnt) {
- throw new SQLException("Expected " + cnt + " rows affected, received "
- + states.length + " instead");
- }
for (int i = 0; i < cnt; i++) {
- if (states[i] != 1) {
- throw new SQLException("Entity " + (i + 1) + " not affected by update");
+ if (states.length <= i || states[i] != 1) {
+ throw new OrmConcurrencyException();
}
}
}
diff --git a/src/test/java/com/google/gwtorm/server/PhoneBookDbTestCase.java b/src/test/java/com/google/gwtorm/server/PhoneBookDbTestCase.java
index ba19836..f9afcfe 100644
--- a/src/test/java/com/google/gwtorm/server/PhoneBookDbTestCase.java
+++ b/src/test/java/com/google/gwtorm/server/PhoneBookDbTestCase.java
@@ -14,6 +14,7 @@
package com.google.gwtorm.server;
+import com.google.gwtorm.client.OrmConcurrencyException;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.Transaction;
import com.google.gwtorm.data.PersonAccess;
@@ -261,10 +262,8 @@
try {
schema.people().update(Collections.singleton(bob));
fail("Update of missing person succeeded");
- } catch (OrmException e) {
- assertEquals("Update failure: people", e.getMessage());
- assertTrue(e.getCause() instanceof SQLException);
- assertEquals("Entity 1 not affected by update", e.getCause().getMessage());
+ } catch (OrmConcurrencyException e) {
+ assertEquals("Concurrent modification detected", e.getMessage());
}
}