use of org.jdbi.v3.sqlobject.transaction.Transaction in project jdbi by jdbi.
the class JdbiUtil method getHandle.
/**
* Obtain a Handle instance, either the transactionally bound one if we are in a transaction,
* or a new one otherwise.
* @param jdbi the Jdbi instance from which to obtain the handle
*
* @return the Handle instance
*/
public static Handle getHandle(Jdbi jdbi) {
Handle bound = (Handle) TransactionSynchronizationManager.getResource(jdbi);
if (bound == null) {
bound = jdbi.open();
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.bindResource(jdbi, bound);
TransactionSynchronizationManager.registerSynchronization(new Adapter(jdbi, bound));
TRANSACTIONAL_HANDLES.add(bound);
}
}
return bound;
}
use of org.jdbi.v3.sqlobject.transaction.Transaction in project jdbi by jdbi.
the class SqlObjectFactory method attach.
/**
* Create a sql object of the specified type bound to this handle. Any state changes to the handle, or the sql
* object, such as transaction status, closing it, etc, will apply to both the object and the handle.
*
* @param extensionType the type of sql object to create
* @param handle the Handle instance to attach ths sql object to
* @return the new sql object bound to this handle
*/
@Override
public <E> E attach(Class<E> extensionType, HandleSupplier handle) {
Map<Method, Handler> handlers = methodHandlersFor(extensionType, handle.getConfig(Handlers.class), handle.getConfig(HandlerDecorators.class));
ConfigRegistry instanceConfig = handle.getConfig().createCopy();
for (Class<?> iface : extensionType.getInterfaces()) {
forEachConfigurer(iface, (configurer, annotation) -> configurer.configureForType(instanceConfig, annotation, extensionType));
}
forEachConfigurer(extensionType, (configurer, annotation) -> configurer.configureForType(instanceConfig, annotation, extensionType));
InvocationHandler invocationHandler = createInvocationHandler(extensionType, instanceConfig, handlers, handle);
return extensionType.cast(Proxy.newProxyInstance(extensionType.getClassLoader(), new Class[] { extensionType }, invocationHandler));
}
use of org.jdbi.v3.sqlobject.transaction.Transaction in project jdbi by jdbi.
the class TestTransactionsAutoCommit method restoreAutoCommitInitialStateOnUnexpectedError.
@Test
public void restoreAutoCommitInitialStateOnUnexpectedError() throws Exception {
final Connection connection = mock(Connection.class);
final PreparedStatement statement = mock(PreparedStatement.class);
InOrder inOrder = inOrder(connection, statement);
Handle h = Jdbi.create(() -> connection).open();
when(connection.getAutoCommit()).thenReturn(true);
when(connection.prepareStatement(anyString(), anyInt(), anyInt())).thenReturn(statement);
when(statement.execute()).thenReturn(true);
when(statement.getUpdateCount()).thenReturn(1);
// throw e.g some underlying database error
doThrow(new SQLException("infrastructure error")).when(connection).commit();
h.begin();
assertThatExceptionOfType(Exception.class).isThrownBy(() -> {
h.execute(SAMPLE_SQL, 1L, "Tom");
// throws exception on commit
h.commit();
});
// expected behaviour chain:
// 1. store initial auto-commit state
inOrder.verify(connection, atLeastOnce()).getAutoCommit();
// 2. turn off auto-commit
inOrder.verify(connection).setAutoCommit(false);
// 3. execute statement (without commit)
inOrder.verify(connection).prepareStatement("insert into something (id, name) values (?, ?)", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
inOrder.verify(statement).execute();
inOrder.verify(statement).getUpdateCount();
// 4. commit transaction
inOrder.verify(connection).commit();
// 5. set auto-commit back to initial state
inOrder.verify(connection).setAutoCommit(true);
}
use of org.jdbi.v3.sqlobject.transaction.Transaction in project jdbi by jdbi.
the class TransactionTest method serializableTransaction.
@Test
public void serializableTransaction() throws Exception {
// tag::serializable[]
// Automatically rerun transactions
db.setTransactionHandler(new SerializableTransactionRunner());
// Set up some values
BiConsumer<Handle, Integer> insert = (h, i) -> h.execute("INSERT INTO ints(value) VALUES(?)", i);
handle.execute("CREATE TABLE ints (value INTEGER)");
insert.accept(handle, 10);
insert.accept(handle, 20);
// Run the following twice in parallel, and synchronize
ExecutorService executor = Executors.newCachedThreadPool();
CountDownLatch latch = new CountDownLatch(2);
Callable<Integer> sumAndInsert = () -> db.inTransaction(TransactionIsolationLevel.SERIALIZABLE, h -> {
// Both read initial state of table
int sum = h.select("SELECT sum(value) FROM ints").mapTo(int.class).findOnly();
// First time through, make sure neither transaction writes until both have read
latch.countDown();
latch.await();
// Now do the write.
insert.accept(h, sum);
return sum;
});
// Both of these would calculate 10 + 20 = 30, but that violates serialization!
Future<Integer> result1 = executor.submit(sumAndInsert);
Future<Integer> result2 = executor.submit(sumAndInsert);
// One of the transactions gets 30, the other will abort and automatically rerun.
// On the second attempt it will compute 10 + 20 + 30 = 60, seeing the update from its sibling.
// This assertion fails under any isolation level below SERIALIZABLE!
assertThat(result1.get() + result2.get()).isEqualTo(30 + 60);
executor.shutdown();
// end::serializable[]
}
use of org.jdbi.v3.sqlobject.transaction.Transaction in project jdbi by jdbi.
the class TestTransactionAnnotation method testTxActuallyCommits.
@Test
public void testTxActuallyCommits() throws Exception {
Handle h2 = this.dbRule.openHandle();
Dao one = handle.attach(Dao.class);
Dao two = h2.attach(Dao.class);
// insert in @Transaction method
Something inserted = one.insertAndFetch(1, "Brian");
// fetch from another connection
Something fetched = two.findById(1);
assertThat(fetched).isEqualTo(inserted);
}
Aggregations