FacileJDBC
Tags: Java, Database, Jdbc, Functional, Java11

Examples

The following example show how to use FacileJDBC for different use cases.

Executing queries

SQL queries are defined via the Query class. Since the Query class is immutable, it is safe to use it in a multi-threaded environment or define it as static class member. The Query class is the main entry point of the FacileJDBC library. For executing a query, all what it needs is a JDBC Connection .

The execute method returns a boolean value as specified in the PreparedStatement.execute() method. The Javadoc documents the used methods of the PreparedStatement for every execute method.

Selecting objects

Usually, your selected rows will be stored in DTO objects. For the simple examples the following Person DTO will be used.

The following query will select all persons which matches a given name pattern.

Executing the select query is shown in the following code snippet. It also shows how query parameter are set and how the result rows are parsed.

The Query.on method is used for filling the query parameters. The variables uses the usual syntax for SQL bind variables. With the Param.value factory method it is possible to fill the defined variables. The Query.as method executes the query and returns the parsed result rows. For converting the query result to a DTO, a RowParser is needed. With the RowParser.list() method you will tell the query that you expect 0 to n result rows. If only one result is expected, you need to use the RowParser.single() or RowParser.singleOp() method.

The following code snippet shows the RowParser implementation for our Person DTO.

Since the RowParser is a functional interface it can be written as shown. The first function parameter represents the actual selected row and second parameter the JDBC Connection used for executing the query. In most cases the conn will not be used, but it is quite helpful for fetching dependent DTOs in a sub-query.

Inserting objects

For inserting one new Person into the DB an insert query have to be defined.

When all bind variable has been set, it can be inserted by calling the execute method.

Setting the bind variables this way is quite tedious, if you already have a filled Person DTO. Inserting the Person DTO directly you need to define the variable-field mapping. This is done via the Dctor (de-constructor) interface. The Dctor can be seen as the inverse function of the RowParser .

Once a de-constructor is defined for your DTO, you can easily insert single Person objects.

If you are interested in the automatically generated primary key of the insertion, you have to use the executeInsert method. This method returns an Optional in the case no primary key could be generated.

or

if you are need to control the parsing of the generated primary key.

Batch insertion

If you have a collection of Person s, you can insert it in one batch.

For simple insertions, you can also do some kind of ad-hoc batch insertions.

Selecting/inserting object graphs

The previous examples shows the basic usage of the library. It is possible to use this for all needed select and insert queries, as you will do it with plain JDBC. If you need to select or insert small object graphs, this becomes fast tedious as well. Let’s extend our initial example an convert the link of the Person into an object

and with a Link class, which will look like the following.

It is now possible to create one RowParser<Person> and one Dctor<Person> which automatically takes care about the linked Link object. The new parser will look like the following code snippet.

With the shown de-constructor.

The needed helper methods are responsible for selecting/inserting the Link object.

It is still necessary to implement the sub-inserts and sub-selects, but this can be reused in other queries, where inserting and selecting of Link s is needed. Note that this is not an automatic OR-mapping mechanism. The user is still in charge for the concrete implementation.

Note Although the described feature is quite expressive and may solve some selection/insertion task in an elegant way, does not mean you have to use it. Just treat it as additional possibility.

Transaction handling

FacileJDBC also contains two interfaces for simple transaction handling. The Transaction interface defines methods for executing one or more queries in a transactional context.

If you are not interested in the return value of the SQL execution, you can use the accept method instead. In the case of an error, the connection is rolled back. If everything works fine, the connection is committed.

The second interface is the Transactional interface, which represents the transactional capability, typically exposed by a database. In this sense, it can be seen as a minimal database interface, just by exposing a Connection factory method, Transactional::connection . Since Transactional is a functional interface, it can easily created by defining the Connection factory method.

The example above shows how to create a Transactional instance for a HSQLDB in-memory database, perfectly usable for testing purposes. Then it can be used for performing some SQL inserts.