Files
ebean/ebean-test/src/test/java/org/tests/insert/TestInsertDuplicateKey.java
T
Rob Bygrave 7df168f232 ENH: Add Transaction.rollbackAndContinue()
Typically useful for handling DuplicateKeyException where we expect
DuplicateKeyException to be thrown and catch it with the intention of
continuing processing using the same transaction.

Note that some databases like Oracle do not require this explicit
rollback() and would work without the rollbackAndContinue(). Postgres
in particular requires the rollback() call on the underlying connection
such that we can continue using that transaction/java.sql.Connection.

Note that in the existing test we can see that rollbackAndContinue()
is pretty close to being syntactic sugar. I think adding rollbackAndContinue()
is justified and complements the existing commitAndContinue().
2022-09-01 17:08:11 +12:00

104 lines
3.0 KiB
Java

package org.tests.insert;
import io.ebean.DB;
import io.ebean.DuplicateKeyException;
import io.ebean.annotation.Transactional;
import io.ebean.xtest.BaseTestCase;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tests.model.draftable.Document;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class TestInsertDuplicateKey extends BaseTestCase {
private static final Logger log = LoggerFactory.getLogger(TestInsertDuplicateKey.class);
@BeforeEach
public void clearDb() {
server().find(Document.class).asDraft().where().contains("title", "UniqueKey").delete();
}
@Test
public void insert_duplicateKey() {
Document doc1 = new Document();
doc1.setTitle("ThisIsAUniqueKey");
doc1.setBody("one");
doc1.save();
assertThrows(DuplicateKeyException.class, () -> {
Document doc2 = new Document();
doc2.setTitle("ThisIsAUniqueKey");
doc2.setBody("clashes with doc1");
doc2.save();
});
}
@Test
public void insertBatch_duplicateKey() {
assertThrows(DuplicateKeyException.class, this::insertBatch_duplicateKey_action);
}
@Transactional(batchSize = 100)
void insertBatch_duplicateKey_action() {
Document doc1 = new Document();
doc1.setTitle("ThisIsASecondUniqueKey");
doc1.setBody("one");
doc1.save();
Document doc2 = new Document();
doc2.setTitle("ThisIsASecondUniqueKey");
doc2.setBody("clash when batch flushed");
doc2.save();
}
@Test
public void insertBatch_duplicateKey_catchAndContinue() {
insertTheBatch_duplicateKey_catchAndContinue();
List<Document> found = DB.getDefault()
.find(Document.class)
.asDraft()
.where().startsWith("body", "insertTheBatch_duplicateKey_catchAndContinue")
.findList();
assertThat(found).hasSize(1);
assertThat(found.get(0).getTitle()).isEqualTo("ThisIsAThirdUniqueKey");
assertThat(found.get(0).getBody()).isEqualTo("insertTheBatch_duplicateKey_catchAndContinue-1");
}
@Transactional//(batchSize = 100)
private void insertTheBatch_duplicateKey_catchAndContinue() {
Document doc1 = new Document();
doc1.setTitle("ThisIsAThirdUniqueKey");
doc1.setBody("insertTheBatch_duplicateKey_catchAndContinue-1");
doc1.save();
try {
Document doc2 = new Document();
doc2.setTitle("ThisIsAThirdUniqueKey");
doc2.setBody("insertTheBatch_duplicateKey_catchAndContinue-2");
doc2.save();
// flush at this point, fails
DB.getDefault().currentTransaction().flush();
} catch (DuplicateKeyException e) {
log.info("duplicate failed but just continue" + e.getMessage());
// rollback and continue using the transaction
DB.getDefault().currentTransaction().rollbackAndContinue();
}
Document doc0 = new Document();
doc0.setTitle("ThisIsAThirdUniqueKey");
doc0.setBody("insertTheBatch_duplicateKey_catchAndContinue-1");
doc0.save();
}
}