Статьи

Java 8: лямбда-выражения против автоматического закрытия

Если вы использовали более ранние версии Neo4j через Java API с Java 6, вы, вероятно, имеете код, подобный следующему, чтобы гарантировать, что операции записи происходят внутри транзакции:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public class StylesOfTx
{
    public static void main( String[] args ) throws IOException
    {
        String path = "/tmp/tx-style-test";
        FileUtils.deleteRecursively(new File(path));
 
        GraphDatabaseService db = new GraphDatabaseFactory().newEmbeddedDatabase( path );
 
        Transaction tx = db.beginTx();
        try
        {
            db.createNode();
            tx.success();
        }
        finally
        {
            tx.close();
        }
    }
}

В Neo4j 2.0 Transaction начал расширять AutoCloseable, что означало, что вы можете использовать ‘try with resources’, и метод ‘close’ будет автоматически вызываться после завершения блока:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
public class StylesOfTx
{
    public static void main( String[] args ) throws IOException
    {
        String path = "/tmp/tx-style-test";
        FileUtils.deleteRecursively(new File(path));
 
        GraphDatabaseService db = new GraphDatabaseFactory().newEmbeddedDatabase( path );
 
        try ( Transaction tx = db.beginTx() )
        {
            Node node = db.createNode();
            tx.success();
        }
    }
}

Это работает довольно хорошо, хотя все еще возможно иметь транзакции в приложении, когда люди не используют этот синтаксис — старый стиль все еще допустим.

В книге Venkat Subramaniam’s Java 8 он предлагает альтернативный подход, в котором мы используем лямбда-подход:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class StylesOfTx
{
    public static void main( String[] args ) throws IOException
    {
        String path = "/tmp/tx-style-test";
        FileUtils.deleteRecursively(new File(path));
 
        GraphDatabaseService db = new GraphDatabaseFactory().newEmbeddedDatabase( path );
 
        Db.withinTransaction(db, neo4jDb -> {
            Node node = neo4jDb.createNode();
        });
    }
 
    static class Db {
        public static void withinTransaction(GraphDatabaseService db, Consumer<GraphDatabaseService> fn) {
            try ( Transaction tx = db.beginTx() )
            {
                fn.accept(db);
                tx.success();
            }
        }
    }
}

Функция ‘insideTransaction’ фактически будет работать с GraphDatabaseService или подобным, а не с этим классом Db, но для этого примера было проще поместить его туда.

Недостатком этого стиля является то, что вы не имеете явного контроля над транзакцией для обработки случая сбоя — предполагается, что если tx.success () не вызывается, то транзакция не удалась, и она откатывается. Я не уверен, какой% случаев действительно нуждается в таком мелкозернистом контроле.

Брайан Херт называет это « дырой в среднем паттерне », и я думаю, что мы увидим больше кода этого рода после того, как Java 8 будет выпущена и станет более широко используемой.