How to make a JDBC driver

Introduction

I wondered if I could realize a serverless RDB, but I feel that it is quite so if I use CloudRun. However, CloudRun can't speak JDBC, so I thought I should make my own JDBC driver and use HTTP on the back side, so I investigated how to make it.

Surprisingly, it seems that the JDBC driver is easy to make, so I tried to make the minimum skeleton implementation for the time being. Since there is no RDB on the back side, basically I will make the following that just returns an echo.

--Main class for testing

Execution process for testing

First, let's create a main class that tests and executes both rabbits and horns. This is a JDBC driver created by MyDriver and MyConnection. It's a simple implementation that just confirms that the return value is MyConnection, but now you can see that your own JDBC driver is being called properly.

github: https://github.com/koduki/jdbc-skeleton/blob/master/src/main/java/Main.java

var url = "jdbc:myjdbc://localhost:80/testdb";

Class.forName("cn.orz.pascal.jdbc.MyDriver");
try (var con = DriverManager.getConnection(url); var st = con.createStatement()) {
    st.execute("INSERT DUMMY SQL");
    try (var rs = st.executeQuery("SELECT DUMMY SQL")) {
        while (rs.next()) {
            System.out.println("rs[1]=" + rs.getString(1));
        }
    }
}

The execution result is as follows.

jdbc uri: jdbc:myjdbc://localhost:80/testdb
execute sql: INSERT DUMMY SQL
execute sql: SELECT DUMMY SQL
rs[1]=a
rs[1]=e
rs[1]=h
MyResultSet close
MyStatement close
MyConnection close

Driver

Next is the JDBC driver. The point is the static initializer, which completes the driver registration when the class is loaded with Class.forName.

You can also get the JDBC URL here. In the actual implementation, it seems that it is common to parse the URL at this timing and assemble the connection destination information.

In the sample code, it is implemented with ʻUnsupportedOperationException` except where it is described. You need to replace it with the actual code if necessary.

github: https://github.com/koduki/jdbc-skeleton/blob/master/src/main/java/cn/orz/pascal/jdbc/MyDriver.java

package cn.orz.pascal.jdbc;

public class MyDriver implements Driver {
    private static final String URI_PREFIX = "jdbc:myjdbc://";

    static {
        try {
            java.sql.DriverManager.registerDriver(new MyDriver());
        } catch (SQLException ex) {
            throw new RuntimeException("Can't register driver!");
        }
    }

    @Override
    public Connection connect(String url, Properties info) throws SQLException {
        if (!url.startsWith(URI_PREFIX)) {
            return null;
        }
        return new MyConnection(url, info);
    }

-Abbreviation-

Connection

Connection creates a Statement. In the actual implementation, the connection is maintained in the unit of Connection, so I think that you will go to the connection with the constructor etc.

github: https://github.com/koduki/jdbc-skeleton/blob/master/src/main/java/cn/orz/pascal/jdbc/MyConnection.java

package cn.orz.pascal.jdbc;

public class MyConnection implements Connection {
    public MyConnection(String uri, Properties info) throws SQLException {
        System.out.println("jdbc uri: " + uri);
    }

    @Override
    public void close() throws SQLException {
        System.out.println(this.getClass().getSimpleName() + " close");
    }

    @Override
    public Statement createStatement() throws SQLException {
        return new MyStatement();
    }

-Abbreviation-

Statement

Statement processes queries. The SQL parser should be called here. By the way, since I / F is just passing a character string, it is possible to pass a query other than SQL without any problem.

In the dummy, List of List is mapped to ResultSet and returned.

github: https://github.com/koduki/jdbc-skeleton/blob/master/src/main/java/cn/orz/pascal/jdbc/MyStatement.java

package cn.orz.pascal.jdbc;

public class MyStatement implements java.sql.Statement {

    @Override
    public boolean execute(String sql) throws SQLException {
        System.out.println("execute sql: " + sql);

        return true;
    }

    @Override
    public ResultSet executeQuery(String sql) throws SQLException {
        System.out.println("execute sql: " + sql);
        
        var result = new MyResultSet(List.of(
                List.of("a", "b", "c"),
                List.of("e", "f", "g"),
                List.of("h", "i", "j")
        ));
        return result;
    }

    @Override
    public void close() throws SQLException {
        System.out.println(this.getClass().getSimpleName() + " close");
    }

-Abbreviation-

ResultSet

ResultSet is the result of query execution. The dummy is implemented by wrapping the iterator.

In the actual code, the actual data will be handled here, but since the DB is generally large, it will be necessary to implement iterator-like implementation instead of List-like implementation.

github: https://github.com/koduki/jdbc-skeleton/blob/master/src/main/java/cn/orz/pascal/jdbc/MyResultSet.java

package cn.orz.pascal.jdbc;

public class MyResultSet implements java.sql.ResultSet {

    private final Iterator<List<String>> itr;
    private List<String> current;

    public MyResultSet(List<List<String>> source) {
        this.itr = source.iterator();
    }

    @Override
    public String getString(int index) throws SQLException {
        return current.get(index - 1);
    }

    @Override
    public boolean next() throws SQLException {
        var result = itr.hasNext();

        if (result) {
            current = itr.next();
        }
        return result;
    }

-Abbreviation-

Summary

Well, when I tried it, I was able to implement the JDBC driver more easily than I expected. I thought it would be more difficult, so I'm a little out of tune, but it means that the I / F is implemented neatly.

If you can hack the JDBC driver, you can hook SQL and output it to the log, proxy it and send it to another DB, or connect it to KVS or another DB implementation without difficulty, including ORM such as JPA.

Since there are many APIs, it seems that it will take some patience to implement it seriously, but if it is a type that wraps the actual JDBC, it seems to be relatively easy to make.

This seems to be useful, so it seems good to put it in the tool box.

Then Happy Hacking!

reference

-Create JDBC over Thrift Part 1 (How to create a JDBC driver) --Why you can access DB with Class.forName

Recommended Posts

How to make a JDBC driver
How to make a Java container
How to make a splash screen
How to make a Jenkins plugin
How to make a Maven project
How to make a Java array
How to make a Discord bot (Java)
How to make shaded-jar
How to make a lightweight JRE for distribution
How to make a follow function in Rails
How to leave a comment
[Rails] How to make seed
How to insert a video
How to create a method
How to create a JDBC URL (Oracle Database, Thin)
How to make JavaScript work on a specific page
How to make a cache without thinking too much
How to make a mod for Slay the Spire
How to add columns to a table
Try to make a simple callback
How to use Spring Data JDBC
How to sign a Minecraft MOD
Learning Ruby with AtCoder 13 How to make a two-dimensional array
[Java] How to create a folder
How to write a ternary operator
[Swift] How to send a notification
Try to make a peepable iterator
[Android] How to make Dialog Fragment
How to make a hinadan for a Spring Boot project using SPRING INITIALIZR
How to make a jar file with no dependencies in Maven
How to identify the path that is easy to make a mistake
How to make a groundbreaking diamond using Java for statement wwww
[Xcode] How to add a README.md file
How to execute a contract using web3j
How to sort a List using Comparator
[Basic] How to write a Dockerfile Self-learning â‘¡
How to insert a video in Rails
How to add a new hash / array
[Introduction to Java] How to write a Java program
How to create a Maven repository for 2020
How to print a Java Word document
[Swift5] How to create a splash screen
[rails] How to create a partial template
How to make asynchronous pagenations using Kaminari
How to publish a library in jCenter
[SpringBoot] How to write a controller test
How to deploy
JVM Performance Tuning: What is Tuning and How to Make a Good Plan
How to make an app with a plugin mechanism [C # and Java]
Rails: How to write a rake task nicely
How to create a database for H2 Database anywhere
[Rails] How to write when making a subquery
How to deploy a container on AWS Lambda
[Rails] How to create a graph using lazy_high_charts
[Android] How to convert a character string to resourceId
Make a margin to the left of the TextField
How to get a heapdump from a Docker container
How to display a web page in Java
How to make Spring Boot Docker Image smaller
How to delete a controller etc. using a command
[Ethereum] How to execute a contract using web3j-Part 2-