#991 ENH: Add forUpdateNoWait() and forUpdateSkipLocked() options ... #528 ENH: Improve exception handling

This commit is contained in:
Rob Bygrave
2017-03-13 20:50:22 +13:00
parent a3dbd4538f
commit c8d01828b3
49 changed files with 985 additions and 336 deletions
@@ -1,46 +0,0 @@
package org.tests.basic;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.Query;
import org.tests.model.basic.Order;
import org.tests.model.basic.ResetBasicData;
import org.junit.Assert;
import org.junit.Test;
public class TestQuery extends BaseTestCase {
@Test
public void testCountOrderBy() {
ResetBasicData.reset();
Query<Order> query = Ebean.find(Order.class).setAutoTune(false).order().asc("orderDate")
.order().desc("id");
// .orderBy("orderDate");
int rc = query.findList().size();
Assert.assertTrue(rc > 0);
// String generatedSql = query.getGeneratedSql();
// Assert.assertFalse(generatedSql.contains("order by"));
}
public void testForUpdate() {
ResetBasicData.reset();
Query<Order> query = Ebean.find(Order.class).setAutoTune(false).setForUpdate(false)
.setMaxRows(1).order().asc("orderDate").order().desc("id");
int rc = query.findList().size();
Assert.assertTrue(rc > 0);
Assert.assertTrue(!query.getGeneratedSql().toLowerCase().contains("for update"));
query = Ebean.find(Order.class).setAutoTune(false).setForUpdate(true).setMaxRows(1).order()
.asc("orderDate").order().desc("id");
rc = query.findList().size();
Assert.assertTrue(rc > 0);
Assert.assertTrue(query.getGeneratedSql().toLowerCase().contains("for update"));
}
}
@@ -0,0 +1,79 @@
package org.tests.basic;
import io.ebean.AcquireLockException;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.EbeanServer;
import io.ebean.Query;
import io.ebean.Transaction;
import org.junit.Test;
import org.tests.model.basic.Customer;
import org.tests.model.basic.ResetBasicData;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertTrue;
public class TestQueryForUpdate extends BaseTestCase {
@Test
public void testForUpdate() {
if (isH2() || isPostgres()) {
ResetBasicData.reset();
Query<Customer> query = Ebean.find(Customer.class)
.forUpdate()
.setMaxRows(1)
.order().desc("id");
query.findList();
assertThat(sqlOf(query)).contains("for update");
}
}
@Test
public void testForUpdate_noWait() {
if (isPostgres() || isOracle()) {
ResetBasicData.reset();
EbeanServer server = Ebean.getDefaultServer();
Ebean.beginTransaction();
try {
Query<Customer> query = Ebean.find(Customer.class)
.forUpdateNoWait()
.setMaxRows(1)
.order().desc("id");
List<Customer> list = query.findList();
Customer first = list.get(0);
assertThat(sqlOf(query)).contains("for update nowait");
// create a 2nd transaction to test that the
// row is locked and we can't acquire it
Transaction txn2 = server.createTransaction();
try {
logger.info("... attempt another acquire using 2nd transaction");
Query<Customer> query2 =
server.find(Customer.class)
.where().idEq(first.getId())
.forUpdateNoWait();
server.findUnique(query2, txn2);
assertTrue(false); // never get here
} catch (AcquireLockException e) {
logger.info("... got AcquireLockException " + e);
} finally {
txn2.end();
}
} finally {
Ebean.endTransaction();
}
}
}
}
@@ -0,0 +1,28 @@
package org.tests.insert;
import io.ebean.BaseTestCase;
import io.ebean.DataIntegrityException;
import io.ebean.Ebean;
import org.junit.Test;
import org.tests.model.basic.Customer;
import org.tests.model.basic.Order;
import org.tests.model.basic.ResetBasicData;
public class TestInsertDataIntegrityException extends BaseTestCase {
@Test(expected = DataIntegrityException.class)
public void insert_invalidForeignKey() {
ResetBasicData.reset();
// an invalid foreign key value
Customer invalidCustomer = Ebean.getReference(Customer.class, 900000);
Order order = new Order();
order.setStatus(Order.Status.NEW);
order.setCustomer(invalidCustomer);
Ebean.save(order);
}
}
@@ -0,0 +1,103 @@
package org.tests.insert;
import io.ebean.BaseTestCase;
import io.ebean.DuplicateKeyException;
import io.ebean.Ebean;
import io.ebean.annotation.Transactional;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tests.model.draftable.Document;
import java.sql.SQLException;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class TestInsertDuplicateKey extends BaseTestCase {
private static final Logger log = LoggerFactory.getLogger(TestInsertDuplicateKey.class);
@Test(expected = DuplicateKeyException.class)
public void insert_duplicateKey() {
Document doc1 = new Document();
doc1.setTitle("ThisIsAUniqueKey");
doc1.setBody("one");
doc1.save();
Document doc2 = new Document();
doc2.setTitle("ThisIsAUniqueKey");
doc2.setBody("clashes with doc1");
doc2.save();
}
@Transactional(batchSize = 100)
@Test(expected = DuplicateKeyException.class)
public void insertBatch_duplicateKey() {
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 = Ebean.getDefaultServer()
.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
Ebean.getDefaultServer().currentTransaction().flushBatch();
} catch (DuplicateKeyException e) {
log.info("duplicate failed but just continue" + e.getMessage());
try {
// typically we would use transaction.commitAndContinue()
// ... this is a rollback and continue type scenario
// ... more sensible to use a second transaction that do this
Ebean.getDefaultServer().currentTransaction().getConnection().rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
Document doc0 = new Document();
doc0.setTitle("ThisIsAThirdUniqueKey");
doc0.setBody("insertTheBatch_duplicateKey_catchAndContinue-1");
doc0.save();
}
}