Transactions using execute around method pattern and lambdas

After reading Functional Programming in Java by Venkat Subramaniam, I was really impressed by the execute around method pattern and how great it works together with lambdas.

The pattern is popular when handling resources, but also fits other situations where you have common parts. By using this pattern you remove the burden of having common code in several classes. That way they can focus on their core functionality.

To give an example of this pattern, I've created a transaction handler.

We have an interface, called Transaction, with an execute method.

import java.sql.Connection;  
import java.sql.SQLException;

public interface Transaction {  
    public void execute(Connection connection) throws SQLException;
}

This interface represents what we want to run in one transaction. It's SAM type, which means we can use lambdas to implement it.

Then we have the star of the show, the TransactionHandler.

import java.sql.Connection;  
import java.sql.DriverManager;

public class TransactionHandler {

    public static void runInTransaction(Transaction transaction) throws Exception {

        Connection dbConnection = createDatabaseConnection();
        dbConnection.setAutoCommit(false);

        try {

            System.out.println("Starting transaction");
            transaction.execute(dbConnection);


            System.out.println("Committing transaction");
            dbConnection.commit();

        } catch (Exception e) {

            System.out.println(e.getMessage());
            System.out.println("Rolling back...");
            dbConnection.rollback();
        } finally {
            dbConnection.close();
        }
    }

    private static Connection createDatabaseConnection() throws Exception {

        Class.forName("com.mysql.jdbc.Driver");
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/ticket_system", "user", "password");
    }
}

It contains a static method that has the responsibility of running our transactions and rollback if something goes wrong.

Now we have our TransactionHandler, so let's start using it!

I've created a simple ticket system to show how the TransactionHandler works together with lambdas.

Let's test a successful transaction, by doing a successful ticket purchase.

@Test
public void testSuccessfulPurchase() throws Exception {

    TransactionHandler.runInTransaction(connection -> {

        int ticketId = findAvailableTicket(connection);

        reserveTicket(ticketId, connection);
        markAsBought(ticketId, connection);
    });

    assertEquals(getNrOfTicketsIn(TicketState.AVAILABLE), 9);
    assertEquals(getNrOfTicketsIn(TicketState.RESERVED), 0);
    assertEquals(getNrOfTicketsIn(TicketState.BOUGHT), 1);
}

This test passed and printed:

Starting transaction  
Reserving ticket with id 1  
Marking ticket with id 1 as bought  
Committing transaction  

So far so good. Now, let's test the rollback.

@Test
public void testFailedPurchase() throws Exception {

    TransactionHandler.runInTransaction(connection -> {

        int ticketId = findAvailableTicket(connection);

        reserveTicket(ticketId, connection);
        throw new IllegalStateException("Not approved credit card");
    });

    assertEquals(getNrOfTicketsIn(TicketState.AVAILABLE), 10);
    assertEquals(getNrOfTicketsIn(TicketState.RESERVED), 0);
    assertEquals(getNrOfTicketsIn(TicketState.BOUGHT), 0);
}

The test reserves a ticket, then throws an exception that should trigger the rollback of the reservation.

Again, the test passed and printed:

Starting transaction  
Reserving ticket with id 1  
Not approved credit card  
Rolling back...  

Notice how concise and elegant the lambda expressions are compared to anonymous classes, where we would have to create the class and an instance of it. A bit too verbose, don't you think?

Notice how we use lambda expressions as a tool to test every aspect of the TransactionHandler.

You'll find the complete example on GitHub

Enjoyed the post?

If you don't want to miss future posts, make sure to subscribe