127 lines
4.0 KiB
Java
127 lines
4.0 KiB
Java
package org.appledash.saneeconomy.utils.database;
|
|
|
|
import org.appledash.sanelib.database.DatabaseCredentials;
|
|
import org.appledash.sanelib.database.SaneDatabase;
|
|
|
|
import java.sql.Connection;
|
|
import java.sql.PreparedStatement;
|
|
import java.sql.SQLException;
|
|
import java.util.function.Consumer;
|
|
import java.util.logging.Logger;
|
|
|
|
/**
|
|
* Created by appledash on 9/19/16.
|
|
* Blackjack is best pony.
|
|
*/
|
|
public class MySQLConnection {
|
|
private static final Logger LOGGER = Logger.getLogger("MySQLConnection");
|
|
public static final int FIVE_SECONDS = 5000;
|
|
private final DatabaseCredentials dbCredentials;
|
|
private final SaneDatabase saneDatabase;
|
|
private boolean canLockTables = true;
|
|
|
|
public MySQLConnection(DatabaseCredentials dbCredentials) {
|
|
this.dbCredentials = dbCredentials;
|
|
this.saneDatabase = new SaneDatabase(dbCredentials);
|
|
|
|
try (Connection conn = this.saneDatabase.getConnection()){
|
|
|
|
} catch (SQLException e) {
|
|
this.canLockTables = false;
|
|
}
|
|
}
|
|
|
|
public Connection openConnection() {
|
|
try {
|
|
return this.saneDatabase.getConnection();
|
|
} catch (SQLException e) {
|
|
throw new RuntimeException("Database unavailable.", e);
|
|
}
|
|
}
|
|
|
|
public PreparedStatement prepareStatement(Connection conn, String sql) throws SQLException {
|
|
PreparedStatement preparedStatement = conn.prepareStatement(sql);
|
|
|
|
preparedStatement.setQueryTimeout(this.dbCredentials.getQueryTimeout()); // 5 second timeout
|
|
|
|
return preparedStatement;
|
|
}
|
|
|
|
public boolean testConnection() {
|
|
try (Connection ignored = this.openConnection()) {
|
|
return true;
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public void lockTable(Connection conn, String tableName) throws SQLException {
|
|
if (!this.canLockTables) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
conn.prepareStatement("LOCK TABLE " + this.getTable(tableName) + " WRITE").execute();
|
|
this.canLockTables = true;
|
|
} catch (SQLException e) {
|
|
this.canLockTables = false;
|
|
}
|
|
}
|
|
|
|
public void unlockTables(Connection conn) throws SQLException {
|
|
if (!this.canLockTables) {
|
|
return;
|
|
}
|
|
|
|
conn.prepareStatement("UNLOCK TABLES").execute();
|
|
}
|
|
|
|
public void executeAsyncOperation(String tag, Consumer<Connection> callback) {
|
|
this.saneDatabase.runDatabaseOperationAsync(tag, () -> this.doExecuteAsyncOperation(1, callback));
|
|
}
|
|
|
|
// This is a bit weird because it has to account for recursion...
|
|
private void doExecuteAsyncOperation(int levels, Consumer<Connection> callback) {
|
|
try (Connection conn = this.openConnection()) {
|
|
callback.accept(conn);
|
|
} catch (Exception e) {
|
|
if (levels > this.dbCredentials.getMaxRetries()) {
|
|
throw new RuntimeException("This shouldn't happen (database error)", e);
|
|
}
|
|
|
|
LOGGER.severe("An internal SQL error has occured, trying up to " + (5 - levels) + " more times...");
|
|
e.printStackTrace();
|
|
levels++;
|
|
this.doExecuteAsyncOperation(levels, callback);
|
|
}
|
|
}
|
|
|
|
public DatabaseCredentials getCredentials() {
|
|
return this.dbCredentials;
|
|
}
|
|
|
|
public String getTable(String tableName) {
|
|
return this.dbCredentials.getTablePrefix() + tableName;
|
|
}
|
|
|
|
public void waitUntilFlushed() {
|
|
long startTime = System.currentTimeMillis();
|
|
while (!this.saneDatabase.areAllTransactionsDone()) {
|
|
if ((System.currentTimeMillis() - startTime) > FIVE_SECONDS) {
|
|
LOGGER.warning("Took too long to flush all transactions - something has probably hung :(");
|
|
break;
|
|
}
|
|
|
|
try {
|
|
Thread.sleep(50);
|
|
} catch (InterruptedException ignored) {
|
|
}
|
|
}
|
|
}
|
|
|
|
public SaneDatabase getConnection() {
|
|
return this.saneDatabase;
|
|
}
|
|
}
|