Search in sources :

Example 1 with Transaction

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;
}
Also used : TransactionSynchronizationAdapter(org.springframework.transaction.support.TransactionSynchronizationAdapter) Handle(org.jdbi.v3.core.Handle)

Example 2 with Transaction

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));
}
Also used : ConfigRegistry(org.jdbi.v3.core.config.ConfigRegistry) InvocationHandler(java.lang.reflect.InvocationHandler) ExtensionMethod(org.jdbi.v3.core.extension.ExtensionMethod) Method(java.lang.reflect.Method) InvocationHandler(java.lang.reflect.InvocationHandler)

Example 3 with Transaction

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);
}
Also used : InOrder(org.mockito.InOrder) SQLException(java.sql.SQLException) Connection(java.sql.Connection) PreparedStatement(java.sql.PreparedStatement) SQLException(java.sql.SQLException) Handle(org.jdbi.v3.core.Handle) Test(org.junit.Test)

Example 4 with Transaction

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[]
}
Also used : Arrays(java.util.Arrays) Transaction(org.jdbi.v3.sqlobject.transaction.Transaction) SqlUpdate(org.jdbi.v3.sqlobject.statement.SqlUpdate) Assertions.assertThat(org.assertj.core.api.Assertions.assertThat) SqlObject(org.jdbi.v3.sqlobject.SqlObject) Callable(java.util.concurrent.Callable) TransactionException(org.jdbi.v3.core.transaction.TransactionException) Future(java.util.concurrent.Future) Handle(org.jdbi.v3.core.Handle) BiConsumer(java.util.function.BiConsumer) ClassRule(org.junit.ClassRule) JdbiRule(org.jdbi.v3.testing.JdbiRule) ExpectedException(org.junit.rules.ExpectedException) ExecutorService(java.util.concurrent.ExecutorService) SqlQuery(org.jdbi.v3.sqlobject.statement.SqlQuery) Before(org.junit.Before) Jdbi(org.jdbi.v3.core.Jdbi) TransactionIsolationLevel(org.jdbi.v3.core.transaction.TransactionIsolationLevel) Test(org.junit.Test) SerializableTransactionRunner(org.jdbi.v3.core.transaction.SerializableTransactionRunner) Executors(java.util.concurrent.Executors) CountDownLatch(java.util.concurrent.CountDownLatch) Rule(org.junit.Rule) Optional(java.util.Optional) User(jdbi.doc.ResultsTest.User) ConstructorMapper(org.jdbi.v3.core.mapper.reflect.ConstructorMapper) PostgresDbRule(org.jdbi.v3.postgres.PostgresDbRule) ExecutorService(java.util.concurrent.ExecutorService) SerializableTransactionRunner(org.jdbi.v3.core.transaction.SerializableTransactionRunner) CountDownLatch(java.util.concurrent.CountDownLatch) Handle(org.jdbi.v3.core.Handle) Test(org.junit.Test)

Example 5 with Transaction

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);
}
Also used : Something(org.jdbi.v3.core.Something) Handle(org.jdbi.v3.core.Handle) Test(org.junit.Test)

Aggregations

Handle (org.jdbi.v3.core.Handle)5 Test (org.junit.Test)3 Method (java.lang.reflect.Method)2 TransactionException (org.jdbi.v3.core.transaction.TransactionException)2 TransactionIsolationLevel (org.jdbi.v3.core.transaction.TransactionIsolationLevel)2 Transaction (org.jdbi.v3.sqlobject.transaction.Transaction)2 InvocationHandler (java.lang.reflect.InvocationHandler)1 Connection (java.sql.Connection)1 PreparedStatement (java.sql.PreparedStatement)1 SQLException (java.sql.SQLException)1 Arrays (java.util.Arrays)1 Optional (java.util.Optional)1 Callable (java.util.concurrent.Callable)1 CountDownLatch (java.util.concurrent.CountDownLatch)1 ExecutorService (java.util.concurrent.ExecutorService)1 Executors (java.util.concurrent.Executors)1 Future (java.util.concurrent.Future)1 BiConsumer (java.util.function.BiConsumer)1 User (jdbi.doc.ResultsTest.User)1 Assertions.assertThat (org.assertj.core.api.Assertions.assertThat)1