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); + } +}