About Lukas Eder

Lukas is a Java and SQL enthusiast developer. He created the Data Geekery GmbH. He is the creator of jOOQ, a comprehensive SQL library for Java, and he is blogging mostly about these three topics: Java, SQL and jOOQ.

Java 8 Friday Goodies: Local Transaction Scope

At Data Geekery, we love Java. And as we’re really into jOOQ’s fluent API and query DSL, we’re absolutely thrilled about what Java 8 will bring to our ecosystem. We have blogged a couple of times about some nice Java 8 goodies, and now we feel it’s time to start a new blog series, the…

Java 8 Friday

Every Friday, we’re showing you a couple of nice new tutorial-style Java 8 features, which take advantage of lambda expressions, extension methods, and other great stuff. You’ll find the source code on GitHub.
 

Java 8 Goodie: Local Transaction Scope

The JavaScript folks often abuse anonymous functions to create local scope. Like any other language feature, this can be abused, but in some contexts, local scoping is really awesome. Java also allows for local scoping, although until Java 8, this has been equally cumbersome:

JavaScript

(function() {
    var local = function() { 
            scoping(); 
        },
        scoping = function() { 
            alert('If you really must');
        };

    local();
})();

Java

new Object() {
    void local() {
        scoping();
    }
    void scoping() {
        System.out.println(
            "Ouch, my fingers. Too much typing");
    }
}.local();

Both examples look really awkward, although the JavaScript folks call this a design pattern. No one would create such local scope in Java, even if the two pieces of code are roughly equivalent.

Awkwardness can be a design pattern in JavaScript.

Local scoping in Java 8

But with Java 8, everything changes, and so does local scoping. Let’s have a look at how we can creat a local semantic scope for transactions. For this, we’ll create two types. The Transactional interface:

@FunctionalInterface
interface Transactional {
    void run(DSLContext ctx);
}

For the example, we’re going to be using jOOQ to avoid checked exceptions and verbose statement creation. You can replace it by your SQL API of choice. So, jOOQ provides us with a locally scoped ctx object, which implicitly contains the transaction state. This transaction state is generated using a TransactionRunner:

class TransactionRunner {
    private final boolean silent;
    private final Connection connection;

    TransactionRunner(Connection connection) {
        this(connection, true);
    }

    TransactionRunner(Connection connection,
                      boolean silent) {
        this.connection = connection;
        this.silent = silent;
    }

    void run(Transactional tx) {
        // Initialise some jOOQ objects
        final DefaultConnectionProvider c =
            new DefaultConnectionProvider(connection);
        final Configuration configuration =
            new DefaultConfiguration()
                .set(c).set(SQLDialect.H2);

        try {
            // Run the transaction and pass a jOOQ
            // DSLContext object to it
            tx.run(DSL.using(configuration));

            // If we get here, then commit the
            // transaction
            c.commit();
        }
        catch (RuntimeException e) {

            // Any exception will cause a rollback
            c.rollback();
            System.err.println(e.getMessage());

            // Eat exceptions in silent mode.
            if (!silent)
                throw e;
        }
    }
}

The above is framework code, which we’ll write only once. From now on, we can use the above API trivially in our Java programs. For this, we’ll set up a TransactionRunner like such:

public static void main(String[] args) 
throws Exception {
    Class.forName("org.h2.Driver");
    try (Connection c = DriverManager.getConnection(
            "jdbc:h2:~/test-scope-goodies", 
            "sa", "")) {
        c.setAutoCommit(false);
        TransactionRunner silent = 
            new TransactionRunner(c);

        // Transactional code here ...
    }
}

And now, behold the wonders of Java 8!

// This is a transaction
silent.run(ctx -> {
    ctx.execute("drop table if exists person");
    ctx.execute("create table person(" + 
                "  id integer," +
                "  first_name varchar(50)," +
                "  last_name varchar(50)," +
                "  primary key(id)"+
                ")");
});

// And this is also one transaction
silent.run(ctx -> {
    ctx.execute("insert into person" +
                "  values(1, 'John', 'Smith');");
    ctx.execute("insert into person" +
                "  values(1, 'Steve', 'Adams');");
    // Ouch, fails -------^
    // Transaction rolls back
});

// And this is also one transaction
silent.run(ctx -> {
    ctx.execute("insert into person" + 
                "  values(2, 'Jane', 'Miller');");
    // Works, yay!
});

// And this is also one transaction
silent.run(ctx -> {
    ctx.execute("insert into person" +
                "  values(2, 'Anne', 'Roberts');");
    // Ouch, fails -------^
    // Transaction rolls back
});

What do we get from the above? Let’s check:

silent.run(ctx -> {
    System.out.println(
        ctx.fetch("select * from person"));
});

The above program will yield this output:

SQL [insert into person values(1, 'Steve', 'Adams');];
Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.PERSON(ID)"; SQL statement:
insert into person values(1, 'Steve', 'Adams'); [23505-174]
SQL [insert into person values(2, 'Anne', 'Roberts');];
Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.PERSON(ID)"; SQL statement:
insert into person values(2, 'Anne', 'Roberts'); [23505-174]
+----+----------+---------+
|  ID|FIRST_NAME|LAST_NAME|
+----+----------+---------+
|   2|Jane      |Miller   |
+----+----------+---------+

So, our commits and rollbacks worked as expected!

Nested transactions

We can also create nested calls to our TransactionRunner, e.g. when we’re inside methods calling other methods. For this, would have to adapt our TransactionRunner to count the nesting level, and remove the “silent” functionality. On the other hand, it would be very easy to implement savepoint functionality this way. Each time we nest another transaction, we’ll create a new savepoint.

Conclusion

As always in this series, we didn’t invent anything new. All of these things could be done with vanilla Java 7. But the client code of this TransactionRunner certainly wouldn’t look as lean as our lambdas.

Next week in this blog series, we’re going to look at how Java 8 will allow you to define local caching scope very easily, so stay tuned!
 

Related Whitepaper:

Bulletproof Java Code: A Practical Strategy for Developing Functional, Reliable, and Secure Java Code

Use Java? If you do, you know that Java software can be used to drive application logic of Web services or Web applications. Perhaps you use it for desktop applications? Or, embedded devices? Whatever your use of Java code, functional errors are the enemy!

To combat this enemy, your team might already perform functional testing. Even so, you're taking significant risks if you have not yet implemented a comprehensive team-wide quality management strategy. Such a strategy alleviates reliability, security, and performance problems to ensure that your code is free of functionality errors.Read this article to learn about this simple four-step strategy that is proven to make Java code more reliable, more secure, and easier to maintain.

Get it Now!  

Leave a Reply


1 × = six



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books