PolyJDBC
PolyJDBC is a polyglot, lightweight wrapper around standard JDBC drivers with schema inspection/creation capabilities.
Why?
When developing SmartParam JDBC repository i realized that i was writing polyglot JDBC wrapper instead of focusing on core JDBC repository responsibilities. PolyJDBC came to life by extracting low-level wrapping code to separate project.
Before starting work on SmartParam JDBC repo i was looking for a good JDBC wrapper that would do more than suppress SQLExceptions. Specifically i was looking for transaction-aware wrapper that would also make it easy to perform DDL operations.
Target
PolyJDBC primary target are libraries that need light and cross-platform JDBC persistence. Hibernate is great, but it leaves little place for end-user customization, which is important when offering a library. It is also quite heavy. PolyJDBC size is only 75kB, no dependencies except for slf4j logging API.
Features
- polyglot (or better poly-dialect? multiple DB dialect support, including id generation strategies)
- transaction-oriented
- resources (Statements, ResultSets) are managed inside transaction scope
- intiutive transaction commit/rollback support
- option to leave transaction management to external framework (i.e. Spring)
- DDL operation DSL (CREATE TABLE/INDEX/SEQUENCE with constraints, DROP *)
- SQL query DSL (INSERT, SELECT, UPDATE, DELETE)
- lightweight
- schema inspection (table/sequence exists?)
- schema alteration
Supported database engines
- H2
- PostgreSQL
- MySQL
- Oracle
- Microsoft SQL Server
- DB2 and DB2400 (beta)
Thanks to testng magic PolyJDBC runs integration tests on each database. This way i can be sure that all features all supported across all engines.
How to get it?
repositories {
    mavenCentral()
}
dependencies {
    compile group: 'org.polyjdbc', name: 'polyjdbc', version: '0.5.0'
} 
Enough, show me the code
Instantiation
By default, PolyJDBC takes care of transaction management on it's own:
Dialect dialect = DialectRegistry.H2.getDialect();
PolyJDBC polyjdbc = PolyJDBCBuilder.polyJDBC(dialect).connectingToDataSource(dataSource).build(); 
But it is possible to leave connection management to any external framework:
Dialect dialect = DialectRegistry.H2.getDialect();
PolyJDBC polyjdbc = PolyJDBCBuilder.polyJDBC(dialect).usingManagedConnections(() -> frameworkManager::getConnection).build(); 
Querying
To ask simple questions, use simple tool. SimpleQueryRunner performs each query in new transaction:
SelectQuery query = polyJdbc.query().selectAll().from("test").where("name = :name")
        .withArgument("name", "test");
        
Test test = polyJdbc.simpleQueryRunner().queryUnique(query, new TestMapper()); 
You might want to span your transaction across multiple statements, if so use TransactionRunner:
TransactionRunner transactionRunner = polyjdbc.transactionRunner();
Test test = transactionRunner.run(new TransactionWrapper<Test>() {
    @Override
    public Test perform(QueryRunner queryRunner) {
        SelectQuery query = polyJdbc.query().selectAll().from("test").where("name = :name")
            .withArgument("name", "test");
        return queryRunner.queryUnique(query, new TestMapper());
    }
});
transactionRunner.run(new VoidTransactionWrapper() {
    @Override
    public void performVoid(QueryRunner queryRunner) {
        DeleteQuery query = polyJdbc.query().delete().from("test").where("year < :year")
            .withArgument("year", 2012);
        queryRunner.delete(query);
    }
}); 
Or if you need to get your hands dirty, see QueryRunner, but remember to free the resources:
QueryRunner queryRunner = null;
try {
    queryRunner = polyjdbc.queryRunner();
    SelectQuery query = polyJdbc.query().selectAll().from("test").where("year = :year")
        .withArgument("year", 2013).limit(10);
    List<Test> tests = queryRunner.selectList(query, new TestMapper());
    queryRunner.commit()
}
catch(Exception exception) {
    polyjdbc.rollback(queryRunner);
    throw exception;
}
finally {
    polyjdbc.close(queryRunner);
} 
Schema management
PolyJDBC comes with tools for schema creating and deletion. More options for schema inspection are planned, although there is no concrete release date.
To check if relation exists:
SchemaInspector schemaInspector = null;
try {
    schemaInspector = polyjdbc.schemaInspector();
    boolean relationExists = schemaInspector.relationExists("testRelation");
} finally {
    polyjdbc.close(schemaManager);
} 
To create new schema (group of relations):
SchemaManager schemaManager = null;
try {
    schemaManager = polyjdbc.schemaManager();
    Schema schema = new Schema(configuration.getDialect());
    schema.addRelation("test_one")
        .withAttribute().longAttr("id").withAdditionalModifiers("AUTO_INCREMENT").notNull().and()
        .withAttribute().string("name").withMaxLength(200).notNull().unique().and()
        .withAttribute().integer("age").notNull().and()
        .primaryKey("pk_test_one").using("id").and()
        .build();
    schema.addSequence("seq_test_one").build();
    schema.addRelation("test_two")
        .withAttribute().longAttr("id").withAdditionalModifiers("AUTO_INCREMENT").notNull().and()
        .withAttribute().longAttr("fk_test_one").notNull().and()
        .foreignKey("fk_test_one_id").references("test_one", "id").on("fk_test_one").and()
        .build();
    schema.addSequence("seq_test_two").build();
    schemaManager.create(schema);
} finally {
    polyjdbc.close(schemaManager);
} 
You don't need to define Schema object. Single Relation, Sequence or Index can be created using SchemaManager as well.
License
PolyJDBC is published under Apache License 2.0.
Changelog
- 0.6.3 (05.10.2016) 
  - added the possibility to create relations in given schema (pr by @ianagius)
 
- 0.5.0 
  - added possibility to plug in external framework for transaction management
- [API change] PolyJDBC is build using PolyJDBCBuilder
 
- 0.4.0 (24.08.2014) 
  - fixed bug with closing transaction on exception in query runners
- [API change] QueryRunner.close() does not commit, use QueryRunner.commit() explicitly
- [API change] some queries from QueryFactory.* now need Dialect (better Oracle support in future), use dialect-aware PolyJDBC.query()
 
- previous versions 
  - support for PostgreSQL, MySQL, H2 and partial support for Oracle
 
 JarCasting
 JarCasting
