|
|
|
@ -21,128 +21,167 @@ package de.steamwar.bungeecore.sql;
|
|
|
|
|
|
|
|
|
|
import de.steamwar.bungeecore.BungeeCore;
|
|
|
|
|
import net.md_5.bungee.api.ProxyServer;
|
|
|
|
|
import net.md_5.bungee.config.Configuration;
|
|
|
|
|
import net.md_5.bungee.config.ConfigurationProvider;
|
|
|
|
|
import net.md_5.bungee.config.YamlConfiguration;
|
|
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.sql.*;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
import java.util.logging.Level;
|
|
|
|
|
import java.util.logging.Logger;
|
|
|
|
|
|
|
|
|
|
public class Statement implements AutoCloseable {
|
|
|
|
|
|
|
|
|
|
private static final Logger logger = BungeeCore.get().getLogger();
|
|
|
|
|
|
|
|
|
|
private static final List<Statement> statements = new ArrayList<>();
|
|
|
|
|
private static final Deque<Connection> connections = new ArrayDeque<>();
|
|
|
|
|
|
|
|
|
|
private static Connection con;
|
|
|
|
|
private static String url;
|
|
|
|
|
private static String user;
|
|
|
|
|
private static String password;
|
|
|
|
|
private static final String URL;
|
|
|
|
|
private static final String USER;
|
|
|
|
|
private static final String PASSWORD;
|
|
|
|
|
|
|
|
|
|
public static void connect(String url, String user, String password) {
|
|
|
|
|
Statement.url = url;
|
|
|
|
|
Statement.user = user;
|
|
|
|
|
Statement.password = password;
|
|
|
|
|
static {
|
|
|
|
|
File file = new File(BungeeCore.get().getDataFolder(), "MySQL.yml");
|
|
|
|
|
Configuration config;
|
|
|
|
|
try {
|
|
|
|
|
con = DriverManager.getConnection(url + "?autoReconnect=true&useServerPrepStmts=true", user, password);
|
|
|
|
|
} catch (SQLException e) {
|
|
|
|
|
config = ConfigurationProvider.getProvider(YamlConfiguration.class).load(file);
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
ProxyServer.getInstance().stop();
|
|
|
|
|
throw new SecurityException("Could not start SQL-Connection", e);
|
|
|
|
|
throw new SecurityException("", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void reset() {
|
|
|
|
|
closeAll();
|
|
|
|
|
connect(url, user, password);
|
|
|
|
|
try {
|
|
|
|
|
for (Statement statement : statements) {
|
|
|
|
|
statement.init();
|
|
|
|
|
}
|
|
|
|
|
} catch (SQLException e) {
|
|
|
|
|
throw new SecurityException("Could not reprepare SQL statements", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void close() {
|
|
|
|
|
try {
|
|
|
|
|
st.close();
|
|
|
|
|
} catch (SQLException e) {
|
|
|
|
|
BungeeCore.get().getLogger().log(Level.INFO, "Could not close statement", e);
|
|
|
|
|
}
|
|
|
|
|
statements.remove(this);
|
|
|
|
|
URL = "jdbc:mysql://" + config.getString("HOST") + ":" + config.getString("PORT") + "/" + config.getString("DATABASE");
|
|
|
|
|
USER = config.getString("USER");
|
|
|
|
|
PASSWORD = config.getString("PASSWORD");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void closeAll() {
|
|
|
|
|
synchronized (statements) {
|
|
|
|
|
for (Statement statement : statements) {
|
|
|
|
|
synchronized (connections) {
|
|
|
|
|
for(Connection connection : connections) {
|
|
|
|
|
try {
|
|
|
|
|
statement.st.close();
|
|
|
|
|
synchronized (statements) {
|
|
|
|
|
statements.forEach(statement -> statement.close(connection));
|
|
|
|
|
}
|
|
|
|
|
connection.close();
|
|
|
|
|
} catch (SQLException e) {
|
|
|
|
|
BungeeCore.get().getLogger().log(Level.INFO, "Could not close statement", e);
|
|
|
|
|
logger.log(Level.INFO, "Could not close SQL connection", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
con.close();
|
|
|
|
|
} catch (SQLException e) {
|
|
|
|
|
BungeeCore.get().getLogger().log(Level.INFO, "Could not close SQL connection", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static boolean connectionStable() {
|
|
|
|
|
try {
|
|
|
|
|
return !con.isClosed();
|
|
|
|
|
} catch (SQLException e) {
|
|
|
|
|
return false;
|
|
|
|
|
connections.clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private final String sql;
|
|
|
|
|
private PreparedStatement st;
|
|
|
|
|
private final Map<Connection, PreparedStatement> cachedStatements = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
public Statement(String sql) {
|
|
|
|
|
this.sql = sql;
|
|
|
|
|
statements.add(this);
|
|
|
|
|
try {
|
|
|
|
|
init();
|
|
|
|
|
} catch (SQLException e) {
|
|
|
|
|
throw new SecurityException("Could not init SQL statement", e);
|
|
|
|
|
synchronized (statements) {
|
|
|
|
|
statements.add(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void init() throws SQLException {
|
|
|
|
|
st = con.prepareStatement(sql);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public <T> T select(ResultSetUser<T> user, Object... objects) {
|
|
|
|
|
synchronized (statements) {
|
|
|
|
|
return prepare(() -> {
|
|
|
|
|
ResultSet rs = st.executeQuery();
|
|
|
|
|
T result = user.use(rs);
|
|
|
|
|
rs.close();
|
|
|
|
|
return result;
|
|
|
|
|
}, objects);
|
|
|
|
|
}
|
|
|
|
|
return withConnection(st -> {
|
|
|
|
|
ResultSet rs = st.executeQuery();
|
|
|
|
|
T result = user.use(rs);
|
|
|
|
|
rs.close();
|
|
|
|
|
return result;
|
|
|
|
|
}, objects);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void update(Object... objects) {
|
|
|
|
|
synchronized (statements) {
|
|
|
|
|
prepare(st::executeUpdate, objects);
|
|
|
|
|
}
|
|
|
|
|
withConnection(PreparedStatement::executeUpdate, objects);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private <T> T prepare(SQLRunnable<T> runnable, Object... objects) {
|
|
|
|
|
private <T> T withConnection(SQLRunnable<T> runnable, Object... objects) {
|
|
|
|
|
Connection connection = aquireConnection();
|
|
|
|
|
|
|
|
|
|
T result;
|
|
|
|
|
try {
|
|
|
|
|
setObjects(objects);
|
|
|
|
|
return runnable.run();
|
|
|
|
|
result = tryWithConnection(connection, runnable, objects);
|
|
|
|
|
} catch (SQLException e) {
|
|
|
|
|
reset();
|
|
|
|
|
throw new SecurityException("Could not execute SQL statement", e);
|
|
|
|
|
closeConnection(connection);
|
|
|
|
|
connection = aquireConnection();
|
|
|
|
|
try {
|
|
|
|
|
result = tryWithConnection(connection, runnable, objects);
|
|
|
|
|
} catch (SQLException ex) {
|
|
|
|
|
closeConnection(connection);
|
|
|
|
|
throw new SecurityException("Could not execute statement", ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
synchronized (connections) {
|
|
|
|
|
connections.push(connection);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setObjects(Object... objects) throws SQLException {
|
|
|
|
|
private <T> T tryWithConnection(Connection connection, SQLRunnable<T> runnable, Object... objects) throws SQLException {
|
|
|
|
|
PreparedStatement st = cachedStatements.get(connection);
|
|
|
|
|
if(st == null) {
|
|
|
|
|
st = connection.prepareStatement(sql);
|
|
|
|
|
cachedStatements.put(connection, st);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < objects.length; i++) {
|
|
|
|
|
st.setObject(i + 1, objects[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return runnable.run(st);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void close() {
|
|
|
|
|
cachedStatements.values().forEach(st -> closeStatement(st, false));
|
|
|
|
|
cachedStatements.clear();
|
|
|
|
|
synchronized (statements) {
|
|
|
|
|
statements.remove(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void close(Connection connection) {
|
|
|
|
|
PreparedStatement st = cachedStatements.remove(connection);
|
|
|
|
|
if(st != null)
|
|
|
|
|
closeStatement(st, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static Connection aquireConnection() {
|
|
|
|
|
synchronized (connections) {
|
|
|
|
|
if(!connections.isEmpty())
|
|
|
|
|
return connections.pop();
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
return DriverManager.getConnection(URL + "?autoReconnect=true&useServerPrepStmts=true", USER, PASSWORD);
|
|
|
|
|
} catch (SQLException e) {
|
|
|
|
|
throw new SecurityException("Could not open connection", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void closeConnection(Connection connection) {
|
|
|
|
|
synchronized (statements) {
|
|
|
|
|
for (Statement statement : statements) {
|
|
|
|
|
statement.close(connection);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
connection.close();
|
|
|
|
|
} catch (SQLException e) {
|
|
|
|
|
logger.log(Level.INFO, "Could not close connection", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void closeStatement(PreparedStatement st, boolean silent) {
|
|
|
|
|
try {
|
|
|
|
|
st.close();
|
|
|
|
|
} catch (SQLException e) {
|
|
|
|
|
if(!silent)
|
|
|
|
|
logger.log(Level.INFO, "Could not close statement", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public interface ResultSetUser<T> {
|
|
|
|
@ -150,6 +189,6 @@ public class Statement implements AutoCloseable {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private interface SQLRunnable<T> {
|
|
|
|
|
T run() throws SQLException;
|
|
|
|
|
T run(PreparedStatement st) throws SQLException;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|