X-Git-Url: https://code.wpia.club/?a=blobdiff_plain;f=src%2Forg%2Fcacert%2Fgigi%2Fdatabase%2FDatabaseConnection.java;h=c85b5348d6e253c2dd96d1219723b9913751db23;hb=bd738ff4450f7fabe08c4ca5bd3f7597d0b011ef;hp=67364d0a06fe95ee02760d5c270e8121a6c25e9b;hpb=8552a0eaf52ad7a05cb045ee9423cfc8a45c336a;p=gigi.git diff --git a/src/org/cacert/gigi/database/DatabaseConnection.java b/src/org/cacert/gigi/database/DatabaseConnection.java index 67364d0a..c85b5348 100644 --- a/src/org/cacert/gigi/database/DatabaseConnection.java +++ b/src/org/cacert/gigi/database/DatabaseConnection.java @@ -2,12 +2,16 @@ package org.cacert.gigi.database; import java.io.IOException; import java.io.InputStream; +import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverManager; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.HashMap; +import java.util.HashSet; +import java.util.Map.Entry; import java.util.Properties; import java.util.StringJoiner; import java.util.regex.Matcher; @@ -17,13 +21,93 @@ import org.cacert.gigi.database.SQLFileManager.ImportType; public class DatabaseConnection { - public static final int CURRENT_SCHEMA_VERSION = 5; + public static final int MAX_CACHED_INSTANCES = 3; + + private static class StatementDescriptor { + + String query; + + boolean scrollable; + + int instance; + + PreparedStatement target; + + public StatementDescriptor(String query, boolean scrollable) { + this.query = query; + this.scrollable = scrollable; + this.instance = 0; + } + + public synchronized void instanciate(Connection c) throws SQLException { + if (scrollable) { + target = c.prepareStatement(query, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + } else { + target = c.prepareStatement(query, query.startsWith("SELECT ") ? Statement.NO_GENERATED_KEYS : Statement.RETURN_GENERATED_KEYS); + } + + } + + public synchronized PreparedStatement getTarget() { + return target; + } + + public synchronized void increase() { + if (target != null) { + throw new IllegalStateException(); + } + instance++; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + instance; + result = prime * result + ((query == null) ? 0 : query.hashCode()); + result = prime * result + (scrollable ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + StatementDescriptor other = (StatementDescriptor) obj; + if (instance != other.instance) { + return false; + } + if (query == null) { + if (other.query != null) { + return false; + } + } else if ( !query.equals(other.query)) { + return false; + } + if (scrollable != other.scrollable) { + return false; + } + return true; + } + + } + + public static final int CURRENT_SCHEMA_VERSION = 9; public static final int CONNECTION_TIMEOUT = 24 * 60 * 60; private Connection c; - private HashMap statements = new HashMap(); + private HashMap statements = new HashMap(); + + HashSet underUse = new HashSet<>(); private static Properties credentials; @@ -48,36 +132,40 @@ public class DatabaseConnection { } } - public GigiPreparedStatement prepare(String query) { - ensureOpen(); - query = preprocessQuery(query); - GigiPreparedStatement statement = statements.get(query); - if (statement == null) { - try { - statement = new GigiPreparedStatement(c.prepareStatement(query, query.startsWith("SELECT ") ? Statement.NO_GENERATED_KEYS : Statement.RETURN_GENERATED_KEYS)); - } catch (SQLException e) { - throw new Error(e); - } - statements.put(query, statement); - } - return statement; + protected synchronized PreparedStatement prepareInternal(String query) throws SQLException { + return prepareInternal(query, false); } - public GigiPreparedStatement prepareScrollable(String query) { + protected synchronized PreparedStatement prepareInternal(String query, boolean scrollable) throws SQLException { + ensureOpen(); query = preprocessQuery(query); - GigiPreparedStatement statement = statements.get(query); - if (statement == null) { - try { - statement = new GigiPreparedStatement(c.prepareStatement(query, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)); - } catch (SQLException e) { - throw new Error(e); + StatementDescriptor searchHead = new StatementDescriptor(query, scrollable); + PreparedStatement statement = null; + while (statement == null) { + statement = statements.get(searchHead); + if (statement == null) { + searchHead.instanciate(c); + statement = searchHead.getTarget(); + if (searchHead.instance >= MAX_CACHED_INSTANCES) { + return statement; + } + underUse.add(statement); + statements.put(searchHead, statement); + } else if (underUse.contains(statement)) { + searchHead.increase(); + statement = null; + } else { + underUse.add(statement); } - statements.put(query, statement); } return statement; } + protected synchronized PreparedStatement prepareInternalScrollable(String query) throws SQLException { + return prepareInternal(query, true); + } + private long lastAction = System.currentTimeMillis(); private void ensureOpen() { @@ -95,16 +183,13 @@ public class DatabaseConnection { lastAction = System.currentTimeMillis(); } - private static ThreadLocal instances = new ThreadLocal() { + private static volatile DatabaseConnection instance; - @Override - protected DatabaseConnection initialValue() { - return new DatabaseConnection(); + public static synchronized DatabaseConnection getInstance() { + if (instance == null) { + instance = new DatabaseConnection(); } - }; - - public static DatabaseConnection getInstance() { - return instances.get(); + return instance; } public static boolean isInited() { @@ -116,10 +201,12 @@ public class DatabaseConnection { throw new Error("Re-initiaizing is forbidden."); } credentials = conf; - GigiResultSet rs = getInstance().prepare("SELECT version FROM \"schemeVersion\" ORDER BY version DESC LIMIT 1;").executeQuery(); int version = 0; - if (rs.next()) { - version = rs.getInt(1); + try (GigiPreparedStatement gigiPreparedStatement = new GigiPreparedStatement("SELECT version FROM \"schemeVersion\" ORDER BY version DESC LIMIT 1;")) { + GigiResultSet rs = gigiPreparedStatement.executeQuery(); + if (rs.next()) { + version = rs.getInt(1); + } } if (version == CURRENT_SCHEMA_VERSION) { return; // Good to go @@ -130,10 +217,6 @@ public class DatabaseConnection { upgrade(version); } - public void beginTransaction() throws SQLException { - c.setAutoCommit(false); - } - private static void upgrade(int version) { try { Statement s = getInstance().c.createStatement(); @@ -161,22 +244,6 @@ public class DatabaseConnection { } } - public void commitTransaction() throws SQLException { - c.commit(); - c.setAutoCommit(true); - } - - public void quitTransaction() { - try { - if ( !c.getAutoCommit()) { - c.rollback(); - c.setAutoCommit(true); - } - } catch (SQLException e) { - e.printStackTrace(); - } - } - public static final String preprocessQuery(String originalQuery) { originalQuery = originalQuery.replace('`', '"'); if (originalQuery.matches("^INSERT INTO [^ ]+ SET .*")) { @@ -211,4 +278,28 @@ public class DatabaseConnection { } return ident; } + + protected synchronized void returnStatement(PreparedStatement target) throws SQLException { + if ( !underUse.remove(target)) { + target.close(); + } + } + + public synchronized int getNumberOfLockedStatements() { + return underUse.size(); + } + + public synchronized void lockedStatements(PrintWriter writer) { + writer.println(underUse.size()); + for (PreparedStatement ps : underUse) { + for (Entry e : statements.entrySet()) { + if (e.getValue() == ps) { + writer.println("
"); + writer.println(e.getKey().instance + ":"); + + writer.println(e.getKey().query); + } + } + } + } }