diff --git a/db/pom.xml b/db/pom.xml
new file mode 100644
index 0000000..a9526e3
--- /dev/null
+++ b/db/pom.xml
@@ -0,0 +1,26 @@
+
+ 4.0.0
+
+
+ parent
+ org.zhdev.varioutil
+ 1.0-SNAPSHOT
+
+
+ db
+
+
+
+ com.h2database
+ h2
+ 2.1.214
+ compile
+
+
+ org.xerial
+ sqlite-jdbc
+ 3.42.0.0
+ compile
+
+
+
\ No newline at end of file
diff --git a/db/src/main/java/org/zhdev/sql/AbstractSqlConnection.java b/db/src/main/java/org/zhdev/sql/AbstractSqlConnection.java
new file mode 100644
index 0000000..9b6adb3
--- /dev/null
+++ b/db/src/main/java/org/zhdev/sql/AbstractSqlConnection.java
@@ -0,0 +1,36 @@
+package org.zhdev.sql;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+abstract class AbstractSqlConnection implements SqlConnection {
+ private final Connection connection;
+
+ AbstractSqlConnection(Connection connection) {
+ this.connection = connection;
+ }
+
+ @Override
+ public PreparedStatement prepareStatement(String query) throws SQLException {
+ return connection.prepareStatement(query);
+ }
+
+ @Override
+ public boolean isClosed() {
+ try {
+ return connection.isClosed();
+ } catch (SQLException e) {
+ throw new SqlException(e);
+ }
+ }
+
+ @Override
+ public void close() {
+ try {
+ connection.close();
+ } catch (SQLException e) {
+ throw new SqlException(e);
+ }
+ }
+}
diff --git a/db/src/main/java/org/zhdev/sql/H2SqlConnection.java b/db/src/main/java/org/zhdev/sql/H2SqlConnection.java
new file mode 100644
index 0000000..6ed3c5a
--- /dev/null
+++ b/db/src/main/java/org/zhdev/sql/H2SqlConnection.java
@@ -0,0 +1,9 @@
+package org.zhdev.sql;
+
+import org.zhdev.util.SqlUtils;
+
+public final class H2SqlConnection extends AbstractSqlConnection {
+ public H2SqlConnection(String path, String username, String password) {
+ super(SqlUtils.createH2Connection(path, username, password));
+ }
+}
diff --git a/db/src/main/java/org/zhdev/sql/MySqlConnection.java b/db/src/main/java/org/zhdev/sql/MySqlConnection.java
new file mode 100644
index 0000000..f157ef1
--- /dev/null
+++ b/db/src/main/java/org/zhdev/sql/MySqlConnection.java
@@ -0,0 +1,9 @@
+package org.zhdev.sql;
+
+import org.zhdev.util.SqlUtils;
+
+public final class MySqlConnection extends AbstractSqlConnection {
+ public MySqlConnection(String address, String dbname, String username, String password, boolean ssl) {
+ super(SqlUtils.createMySqlConnection(address, dbname, username, password, ssl));
+ }
+}
diff --git a/db/src/main/java/org/zhdev/sql/SqlAdapter.java b/db/src/main/java/org/zhdev/sql/SqlAdapter.java
new file mode 100644
index 0000000..5489836
--- /dev/null
+++ b/db/src/main/java/org/zhdev/sql/SqlAdapter.java
@@ -0,0 +1,51 @@
+package org.zhdev.sql;
+
+import org.zhdev.util.CheckedFunction;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Objects;
+
+public final class SqlAdapter implements AutoCloseable {
+ private SqlConnection connection = SqlConnection.NOT_ESTABLISHED;
+
+ public SqlConnection getConnection() {
+ return connection;
+ }
+
+ public void setConnection(SqlConnection connection) {
+ Objects.requireNonNull(connection, "connection");
+ this.connection = connection;
+ }
+
+ public T prepareStatement(CheckedFunction function, String query, Object... args) throws SqlException {
+ try (PreparedStatement statement = connection.prepareStatement(query)) {
+ for (int i = 0, j = 1; i < args.length; i++, j++) {
+ statement.setObject(j, args[i]);
+ }
+ return function.apply(statement);
+ } catch (SQLException e) {
+ throw new SqlException(e);
+ }
+ }
+
+ public T executeQuery(CheckedFunction function, String query, Object... args) throws SqlException {
+ return prepareStatement(statement -> {
+ try (ResultSet set = statement.executeQuery()) {
+ return function.apply(set);
+ }
+ }, query, args);
+ }
+
+ public boolean isClosed() {
+ return connection.isClosed();
+ }
+
+ public void close() {
+ if (connection.isClosed()) {
+ connection.close();
+ }
+ connection = SqlConnection.CLOSED;
+ }
+}
diff --git a/db/src/main/java/org/zhdev/sql/SqlConnection.java b/db/src/main/java/org/zhdev/sql/SqlConnection.java
new file mode 100644
index 0000000..53ff976
--- /dev/null
+++ b/db/src/main/java/org/zhdev/sql/SqlConnection.java
@@ -0,0 +1,46 @@
+package org.zhdev.sql;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+public interface SqlConnection extends AutoCloseable {
+ SqlConnection NOT_ESTABLISHED = new SqlConnection() {
+ @Override
+ public PreparedStatement prepareStatement(String query) {
+ throw new SqlException("Connection not established");
+ }
+
+ @Override
+ public boolean isClosed() {
+ return true;
+ }
+
+ @Override
+ public void close() {
+ throw new SqlException("Connection not established");
+ }
+ };
+ SqlConnection CLOSED = new SqlConnection() {
+ @Override
+ public PreparedStatement prepareStatement(String query) {
+ throw new SqlException("Connection closed");
+ }
+
+ @Override
+ public boolean isClosed() {
+ return true;
+ }
+
+ @Override
+ public void close() {
+ throw new SqlException("Connection closed");
+ }
+ };
+
+ PreparedStatement prepareStatement(String query) throws SQLException;
+
+ boolean isClosed();
+
+ @Override
+ void close();
+}
diff --git a/db/src/main/java/org/zhdev/sql/SqlException.java b/db/src/main/java/org/zhdev/sql/SqlException.java
new file mode 100644
index 0000000..3745808
--- /dev/null
+++ b/db/src/main/java/org/zhdev/sql/SqlException.java
@@ -0,0 +1,18 @@
+package org.zhdev.sql;
+
+public class SqlException extends RuntimeException {
+ public SqlException() {
+ }
+
+ public SqlException(Throwable cause) {
+ super(cause);
+ }
+
+ public SqlException(String s) {
+ super(s);
+ }
+
+ public SqlException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/db/src/main/java/org/zhdev/sql/SqliteConnection.java b/db/src/main/java/org/zhdev/sql/SqliteConnection.java
new file mode 100644
index 0000000..4d3c111
--- /dev/null
+++ b/db/src/main/java/org/zhdev/sql/SqliteConnection.java
@@ -0,0 +1,9 @@
+package org.zhdev.sql;
+
+import org.zhdev.util.SqlUtils;
+
+public class SqliteConnection extends AbstractSqlConnection {
+ public SqliteConnection(String path) {
+ super(SqlUtils.createSqliteConnection(path));
+ }
+}
diff --git a/db/src/main/java/org/zhdev/util/CheckedFunction.java b/db/src/main/java/org/zhdev/util/CheckedFunction.java
new file mode 100755
index 0000000..b448da6
--- /dev/null
+++ b/db/src/main/java/org/zhdev/util/CheckedFunction.java
@@ -0,0 +1,6 @@
+package org.zhdev.util;
+
+@FunctionalInterface
+public interface CheckedFunction {
+ R apply(T t) throws E;
+}
diff --git a/db/src/main/java/org/zhdev/util/SqlUtils.java b/db/src/main/java/org/zhdev/util/SqlUtils.java
new file mode 100644
index 0000000..9ee2bae
--- /dev/null
+++ b/db/src/main/java/org/zhdev/util/SqlUtils.java
@@ -0,0 +1,86 @@
+package org.zhdev.util;
+
+import org.zhdev.sql.SqlException;
+
+import java.io.File;
+import java.io.IOException;
+import java.sql.*;
+
+public class SqlUtils {
+ public static Connection createMySqlConnection(String address, String dbname, String username, String password, boolean ssl) throws SqlException {
+ try {
+ try {
+ Class.forName("com.mysql.cj.jdbc.Driver");
+ } catch (ClassNotFoundException e) {
+ Class.forName("com.mysql.jdbc.Driver");
+ }
+ if (!address.contains(":")) {
+ address = address + ":3306";
+ }
+ return DriverManager.getConnection("jdbc:mysql://" + address + '/' + dbname + "?useSSL=" + ssl, username, password);
+ } catch (SQLException e) {
+ throw new SqlException(e);
+ } catch (ClassNotFoundException e) {
+ throw new SqlException("No suitable driver");
+ }
+ }
+
+ public static Connection createH2Connection(String path, String username, String password) throws SqlException {
+ try {
+ Class.forName("org.h2.Driver");
+ Connection connection;
+ if (username != null) {
+ connection = DriverManager.getConnection("jdbc:h2:./" + path + ";mode=MySQL;AUTO_SERVER=TRUE", username, password);
+ } else {
+ connection = DriverManager.getConnection("jdbc:h2:./" + path + ";mode=MySQL;AUTO_SERVER=TRUE", "sa", "");
+ }
+ return connection;
+ } catch (ClassNotFoundException e) {
+ throw new SqlException("No suitable driver");
+ } catch (SQLException e) {
+ throw new SqlException(e);
+ }
+ }
+
+ public static Connection createSqliteConnection(String path) throws SqlException {
+ File file = new File(path);
+ if (!file.exists()) {
+ File parent = file.getParentFile();
+ if (parent != null) {
+ parent.mkdirs();
+ }
+ try {
+ file.createNewFile();
+ } catch (IOException e) {
+ throw new SqlException(e);
+ }
+ }
+ try {
+ Class.forName("org.sqlite.JDBC");
+ return DriverManager.getConnection("jdbc:sqlite:" + file);
+ } catch (ClassNotFoundException e) {
+ throw new SqlException("No suitable driver");
+ } catch (SQLException e) {
+ throw new SqlException(e);
+ }
+ }
+
+ public static T prepareStatement(Connection connection, CheckedFunction function, String query, Object... args) throws SqlException {
+ try (PreparedStatement statement = connection.prepareStatement(query)) {
+ for (int i = 0, j = 1; i < args.length; i++, j++) {
+ statement.setObject(j, args[i]);
+ }
+ return function.apply(statement);
+ } catch (SQLException e) {
+ throw new SqlException(e);
+ }
+ }
+
+ public static T executeQuery(Connection connection, CheckedFunction function, String query, Object... args) throws SqlException {
+ return prepareStatement(connection, statement -> {
+ try (ResultSet set = statement.executeQuery()) {
+ return function.apply(set);
+ }
+ }, query, args);
+ }
+}