Skip to content

Commit 772a3cd

Browse files
committed
Issue ebean-orm#3647: Undo generated properties on general persistenceExceptions
With ebean-orm#2372 we introduced a rollback mechanism when an optimisticLock happend, so that you can correct the issue and retry. This PR also handles the situation for other persistence exceptions like DuplicateKey fix test
1 parent e474634 commit 772a3cd

File tree

5 files changed

+74
-2
lines changed

5 files changed

+74
-2
lines changed

ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ private void onUpdateGeneratedProperties() {
284284
}
285285
}
286286

287-
private void onFailedUpdateUndoGeneratedProperties() {
287+
public void onFailedUpdateUndoGeneratedProperties() {
288288
for (BeanProperty prop : beanDescriptor.propertiesGenUpdate()) {
289289
if (transaction == null || transaction.isOverwriteGeneratedProperties() || prop.isVersion()) {
290290
Object oldVal = intercept.origValue(prop.propertyIndex());

ebean-core/src/main/java/io/ebeaninternal/server/persist/BatchPostExecute.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,11 @@ public interface BatchPostExecute {
4545
* Add timing metrics for batch persist.
4646
*/
4747
void addTimingBatch(long startNanos, int batch);
48+
49+
/**
50+
* Undos generated properties.
51+
*/
52+
default void onFailedUpdateUndoGeneratedProperties() {
53+
54+
}
4855
}

ebean-core/src/main/java/io/ebeaninternal/server/persist/BatchedPstmt.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,12 @@ private void postExecute() {
155155

156156
private void executeAndCheckRowCounts() throws SQLException {
157157
try {
158-
results = pstmt.executeBatch();
158+
try {
159+
results = pstmt.executeBatch();
160+
} catch (SQLException ex) {
161+
list.forEach(BatchPostExecute::onFailedUpdateUndoGeneratedProperties);
162+
throw ex;
163+
}
159164
if (transaction.isLogSql()) {
160165
transaction.logSql(" -- executeBatch() size:{0} sql:{1}", results.length, sql);
161166
}

ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/DmlBeanPersister.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ private int execute(PersistRequestBean<?> request, PersistHandler handler) {
7474
if (request.transaction().isLogSummary()) {
7575
request.transaction().logSummary(msg);
7676
}
77+
request.onFailedUpdateUndoGeneratedProperties();
7778
throw dbPlatform.translate(msg, e);
7879
} finally {
7980
if (!batched) {

ebean-test/src/test/java/org/tests/insert/TestInsertDuplicateKey.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io.ebean.DB;
44
import io.ebean.DuplicateKeyException;
5+
import io.ebean.Transaction;
56
import io.ebean.annotation.Transactional;
67
import io.ebean.xtest.BaseTestCase;
78
import org.junit.jupiter.api.BeforeEach;
@@ -13,6 +14,7 @@
1314
import java.util.List;
1415

1516
import static org.assertj.core.api.Assertions.assertThat;
17+
import static org.junit.jupiter.api.Assertions.assertEquals;
1618
import static org.junit.jupiter.api.Assertions.assertThrows;
1719

1820
public class TestInsertDuplicateKey extends BaseTestCase {
@@ -100,4 +102,61 @@ private void insertTheBatch_duplicateKey_catchAndContinue() {
100102
doc0.setBody("insertTheBatch_duplicateKey_catchAndContinue-1");
101103
doc0.save();
102104
}
105+
106+
107+
@Test
108+
public void insert_duplicateKey_retry() {
109+
Document doc1 = new Document();
110+
doc1.setTitle("Key1ABC");
111+
doc1.setBody("one");
112+
doc1.save();
113+
114+
Document doc2 = new Document();
115+
doc2.setTitle("Key1ABC");
116+
doc2.setBody("clashes with doc1");
117+
Long version = doc2.getVersion();
118+
assertThrows(DuplicateKeyException.class, doc2::save);
119+
assertEquals(version, doc2.getVersion());
120+
121+
doc2.setTitle("Key1ABCD");
122+
123+
doc2.save();
124+
125+
doc1.setTitle("Key1ABCD");
126+
assertThrows(DuplicateKeyException.class, doc1::save);
127+
doc1.setTitle("Key1ABCDE");
128+
doc1.save();
129+
}
130+
131+
@Test
132+
public void insert_duplicateKey_retryWithBatch() {
133+
Document doc1 = new Document();
134+
doc1.setTitle("Key2ABC");
135+
doc1.setBody("one");
136+
doc1.save();
137+
138+
Document doc2 = new Document();
139+
doc2.setTitle("Key2ABC");
140+
doc2.setBody("clashes with doc1");
141+
Long version = doc2.getVersion();
142+
try (Transaction tx = DB.beginTransaction()) {
143+
tx.setBatchMode(true);
144+
doc2.save();
145+
assertThrows(DuplicateKeyException.class, tx::commit);
146+
}
147+
assertEquals(version, doc2.getVersion());
148+
149+
doc2.setTitle("Key2ABCD");
150+
151+
doc2.save();
152+
153+
doc1.setTitle("Key2ABCD");
154+
assertThrows(DuplicateKeyException.class, doc1::save);
155+
doc1.setTitle("Key2ABCDE");
156+
try (Transaction tx = DB.beginTransaction()) {
157+
tx.setBatchMode(true);
158+
doc1.save();
159+
tx.commit();
160+
}
161+
}
103162
}

0 commit comments

Comments
 (0)