pgjdbc / pgadba

Implementation of Java 10 sql2 spec for PostgreSQL
BSD 2-Clause "Simplified" License
70 stars 10 forks source link

pgAdba

An implementation of ADBA, a proposed asynchronous SQL specification, for PostgreSQL.

Asynchronous querying with Future

The primary difference from JDBC is that with ADBA, query execution is managed using futures. When the database returns a result it completes the future, either normally or exceptionally if the query had an error.

Compared to plain JDBC, ADBA, this decouples the network communication to the database from the business logic that produces queries and consumes the results.

This reduces the number of threads needed in the application, since there isn't any need for threads to wait for the database to produce results. This in turn reduces memory footprint and the amount of context switching needed.

Structural improvements over JDBC

How does it work

The library's network stack is based on the asynchronous part of the NIO framework.

The programmer interface is a significant rework and upgrade of the old JDBC standard.

The core concepts are DataSources, Sessions and Operations.

Dependency inclusion

Maven

<dependency>
  <groupId>org.postgresql</groupId>
  <artifactId>pgadba</artifactId>
  <version>0.1.0-ALPHA</version>
</dependency>

Gradle

compile 'org.postgresql:pgadba:0.1.0-ALPHA'

Examples

Creating a DataSource

    DataSourceFactory.newFactory("org.postgresql.adba.PgDataSourceFactory")
        .builder()
        .url("jdbc:postgresql://" + postgres.getContainerIpAddress() + ":" + postgres.getMappedPort(5432)
            + "/" + postgres.getDatabaseName())
        .username(postgres.getUsername())
        .password(postgres.getPassword())
        .build();

This is a straightforward building pattern.

Performing a query

    List<Integer> result = new ArrayList<>();
    try (Session session = ds.getSession()) {
      Submission<List<Integer>> sub = session.<List<Integer>>rowOperation("select $1 as id")
          .set("$1", 1, AdbaType.INTEGER)
          .collect(() -> result,
              (list, row) -> list.add(row.at("id").get(Integer.class)))
          .submit();
      get10(sub.getCompletionStage());
    }

Lets go over this line by line.

  1. Just a list to hold the result of the operation.
  2. We start by getting a session in a try-with-resources construct. getSession() is a convenience function that creates a ConnectionOperation and submits it to the operation stack
  3. Here we create a row operation, queries that returns rows generally want to use row operations, while queries that return the number of rows affected should use countOperations
  4. Setting of parameters, no parsing of the query is done in the library, so replacing the $1 with the parameter is done by the server in the same way that prepared statements work in regular jdbc.
  5. The collect call takes a standard java.util.stream.Collector, and have a overloaded default implementation that uses Collector.of().
  6. submit() signals that we are done building the Operation and want to send it off to be executed by the server.
  7. get10 is a helper function that waits for a future to complete, with a timeout of 10s.

Full application example

Spring Boot Example

How can I get involved

This is very much a work in progress. Bail in if interested!

All bug reports, pull requests and suggestions are very welcome.