Starting to implement async in db actions

This commit is contained in:
anthorob committed 2020-01-08 20:30:20 -05:00
1 parent 35290a83da
commit 5ae7e8f819
8 files changed
+176 -65

No files matched your search

@@ -18,19 +18,19 @@ public class ThreadManager {
public static void shutdownExecutor() {
try {
System.out.println("[Executor] Shutting down...");
MusicBot.LOGGER.info("[Executor] Shutting down...");
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
}
catch (InterruptedException e) {
System.err.println("[Executor] Running tasks interrupted");
MusicBot.LOGGER.warn("[Executor] Running tasks interrupted");
}
finally {
if (!executor.isTerminated()) {
System.err.println("[Executor] Forcing shutdown of running tasks..");
MusicBot.LOGGER.warn("[Executor] Forcing shutdown of running tasks..");
}
executor.shutdownNow();
System.out.println("[Executor] Shutdown finished!");
MusicBot.LOGGER.info("[Executor] Shutdown finished!");
}
}
}
@@ -48,7 +48,7 @@ public class BlacklistCommand extends AbstractCommand {
switch (parseArgs(args, 0)) {
case "get":
event.getTextChannel().sendMessage("Retreiving list, please wait...").queue(t -> {
event.getTextChannel().sendMessage("Retrieving list, please wait...").queue(t -> {
try {
@@ -0,0 +1,87 @@
package netneutrality.db;
/*
* Copyright (c) anthorob
*
* https://xtremcommunity.com/
* https://discord.gg/CdFcyQ
*/
import netneutrality.MusicBot;
import netneutrality.ThreadManager;
import netneutrality.db.models.MTableModel;
import java.sql.ResultSet;
import java.util.function.Consumer;
public class AsyncMySqlAction {
private String sqlString;
private Object[] params;
private MysqlDatabase.CommandType type;
private MysqlDatabase db;
public AsyncMySqlAction(MysqlDatabase db, String sql, Object... params) {
this.db = db;
this.sqlString = sql;
this.params = params;
type = MysqlDatabase.CommandType.stringToType(sqlString.toUpperCase().split(" ")[0]);
}
public void async(Consumer<MTableModel> success) {
switch (type) {
case INSERT:
ThreadManager.executor.submit(() -> {
db.insert(this.sqlString, this.params);
});
break;
case UPDATE:
ThreadManager.executor.submit(() -> {
db.update(this.sqlString, this.params);
});
break;
case DELETE:
ThreadManager.executor.submit(() -> {
db.delete(this.sqlString, this.params);
});
break;
case SELECT:
ThreadManager.executor.submit(() -> {
ResultSet result = db.prepareAndExecute(this.sqlString, this.params);
MTableModel table = MysqlDatabase.convertToTableModel(result);
success.accept(table);
});
break;
}
}
public void async() {
async(o -> {
});
}
public MTableModel blocking() {
switch (type) {
case INSERT:
db.insert(this.sqlString, this.params);
case UPDATE:
db.update(this.sqlString, this.params);
case DELETE:
db.delete(this.sqlString, this.params);
case SELECT:
ResultSet result = db.prepareAndExecute(this.sqlString, this.params);
return MysqlDatabase.convertToTableModel(result);
}
return null;
}
}
@@ -13,7 +13,7 @@ import org.reflections.Reflections;
/**
* When creating a controller, all database actions must be executed in another thread using an executor. One is provided in the {@link ThreadManager} class
* A simple thread can't be used in this case because unpredictable errors might happen and some actions might not be executed.
* A simple thread can't be used in this case because overlapping actions might happen and won't be executed.
*/
public class DBController {
@@ -23,7 +23,7 @@ public class DBController {
private final static HashMap<String, AbstractController> controllers = new HashMap<>();
private static MysqlDatabase database = new MysqlDatabase("discord");
private static MysqlDatabase database = new MysqlDatabase("netneutrality");
/**
* @param controller The name of the controller as defined in the {@link AbstractController#getControllerName()}'s method
@@ -9,8 +9,10 @@ import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Calendar;
import java.util.function.Consumer;
import netneutrality.MusicBot;
import netneutrality.ThreadManager;
import netneutrality.db.models.MRowModel;
import netneutrality.db.models.MTableModel;
import netneutrality.exceptions.UnimplementedParameterException;
@@ -18,10 +20,24 @@ import netneutrality.handler.LogHandler;
public class MysqlDatabase {
protected enum CommandType {
SELECT("SELECT"), INSERT("INSERT"), UPDATE("UPDATE"), DELETE("DELETE");
private String type;
CommandType(String type) {
this.type = type;
}
public static CommandType stringToType(String str) {
return CommandType.valueOf(str);
}
}
private Connection connection;
private String dbName;
MysqlDatabase(String dbName) {
protected MysqlDatabase(String dbName) {
this.dbName = dbName;
try {
MusicBot.LOGGER.info("Connecting to database...");
@@ -100,7 +116,6 @@ public class MysqlDatabase {
}
private int executeUpdate(String sql, Object[] params) {
MusicBot.LOGGER.debug("Query: " + sql);
try {
PreparedStatement query = connection.prepareStatement(sql);
@@ -119,7 +134,7 @@ public class MysqlDatabase {
*
* @param sql The SQL query to execute
* @param params The parameters to include in the query
* @return a ResultSet object, which can be converted to an model using {@link MysqlDatabase#convertToTableModel(ResultSet)}
* @return a ResultSet object, which can be converted to a model using {@link MysqlDatabase#convertToTableModel(ResultSet)}
*/
public ResultSet prepareAndExecute(String sql, Object... params) {
MusicBot.LOGGER.debug("Query: " + sql);
@@ -129,12 +144,25 @@ public class MysqlDatabase {
resolveParameters(query, params);
return query.executeQuery();
} catch (SQLException e) {
MusicBot.LOGGER.error(e.toString());
e.printStackTrace();
}
return null;
}
/**
* Execute a SELECT statement with the specified parameters.
* In the SQL query, where there is a parameter to be included, you need to replace it with ?
*
* @param sql The SQL query to execute
* @param params The parameters to include in the query
* @return a ResultSet object, which can be converted to a model using {@link MysqlDatabase#convertToTableModel(ResultSet)}
*/
public AsyncMySqlAction prepareAndExecuteAsync(String sql, Object... params) {
return new AsyncMySqlAction(this, sql, params);
}
/**
* Insert parameters (specified with a ?) in the SQL statement
* Taken from https://github.com/Kaaz/DiscordBot/blob/master/src/main/java/emily/db/MySQLAdapter.java#resolveParameters
@@ -182,7 +210,7 @@ public class MysqlDatabase {
* @throws SQLException
*/
public void close() throws SQLException {
System.out.println("Closing database...");
MusicBot.LOGGER.info("Closing database...");
connection.close();
}
@@ -3,8 +3,10 @@ package netneutrality.db.controllers;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import netneutrality.MusicBot;
import netneutrality.ThreadManager;
import netneutrality.db.AbstractController;
import netneutrality.db.DBController;
@@ -28,26 +30,25 @@ public class DBBlacklist extends AbstractController {
}
public void add(MBlacklist obj, Consumer<Boolean> success) {
ThreadManager.executor.submit(() -> {
if (!isAlreadyBlacklisted(obj.getWord(), obj.getServerId())) {
database.insert("INSERT INTO blacklist VALUES(?,?,?,?)", obj.getWord(), obj.getServerId(), obj.isEnabled(), obj.getCreatorId());
isAlreadyBlacklisted(obj.getWord(), obj.getServerId(), blacklisted -> {
if (blacklisted == 0) {
database.prepareAndExecuteAsync("INSERT INTO blacklist VALUES(?,?,?,?)", obj.getWord(), obj.getServerId(), obj.isEnabled(), obj.getCreatorId()).async();
success.accept(true);
} else {
success.accept(false);
}
});
}
public void delete(String word, String serverId, Consumer<Boolean> success) {
ThreadManager.executor.submit(() -> {
if (isAlreadyBlacklisted(word, serverId)) {
System.out.println("true");
database.delete("DELETE FROM blacklist WHERE word = ? AND serverId = ?", word, serverId);
isAlreadyBlacklisted(word, serverId, blacklisted -> {
if (blacklisted >= 1) {
database.prepareAndExecuteAsync("DELETE FROM blacklist WHERE word = ? AND serverId = ?", word, serverId).async();
MusicBot.LOGGER.info("blacklisted");
success.accept(true);
} else {
MusicBot.LOGGER.info("not blacklisted");
success.accept(false);
}
});
@@ -56,26 +57,32 @@ public class DBBlacklist extends AbstractController {
public void enable(String word, String serverId, Consumer<Boolean> success) {
ThreadManager.executor.submit(() -> {
if (isAlreadyBlacklisted(word, serverId)) {
database.update("UPDATE blacklist SET isEnabled = 1 WHERE word = ? AND serverId = ?", word, serverId);
isAlreadyBlacklisted(word, serverId, blacklisted -> {
if (blacklisted >= 1) {
database.prepareAndExecuteAsync("UPDATE blacklist SET isEnabled = 1 WHERE word = ? AND serverId = ?", word, serverId).async();
MusicBot.LOGGER.info("enabled");
success.accept(true);
} else {
MusicBot.LOGGER.info("error enabling");
success.accept(false);
}
});
}
public void disable(String word, String serverId, Consumer<Boolean> success) {
ThreadManager.executor.submit(() -> {
if (isAlreadyBlacklisted(word, serverId)) {
database.update("UPDATE blacklist SET isEnabled = 0 WHERE word = ? AND serverId = ?", word, serverId);
isAlreadyBlacklisted(word, serverId, blacklisted -> {
if (blacklisted >= 1) {
database.prepareAndExecuteAsync("UPDATE blacklist SET isEnabled = 0 WHERE word = ? AND serverId = ?", word, serverId).async();
MusicBot.LOGGER.info("disabled");
success.accept(true);
} else {
MusicBot.LOGGER.info("error disabled");
success.accept(false);
}
});
}
public void getBlacklistForWord(String word, String serverId, Consumer<MBlacklist> success) {
@@ -84,7 +91,7 @@ public class DBBlacklist extends AbstractController {
if (isAlreadyBlacklisted(word, serverId)) {
ResultSet rs = database.prepareAndExecute("SELECT * FROM blacklist WHERE word = ? AND serverId = ?", word, serverId);
MTableModel table = database.convertToTableModel(rs);
MTableModel table = MysqlDatabase.convertToTableModel(rs);
if (table.getNbRows() > 0) {
blacklisted = new MBlacklist((String) table.getRow(0).get(0), (Boolean) table.getRow(0).get(2), (String) table.getRow(0).get(3),
@@ -98,42 +105,44 @@ public class DBBlacklist extends AbstractController {
}
public void getBlacklistFromServer(String serverId, Consumer<ArrayList<MBlacklist>> success) {
ThreadManager.executor.submit(() -> {
ArrayList<MBlacklist> output = new ArrayList<>();
try {
ResultSet rs = database.prepareAndExecute("SELECT * FROM blacklist WHERE serverId = ? AND isEnabled = 1", serverId);
MTableModel model = MysqlDatabase.convertToTableModel(rs);
database.prepareAndExecuteAsync("SELECT * FROM blacklist WHERE serverId = ? AND isEnabled = 1", serverId).async(model -> {
ArrayList<MBlacklist> output = new ArrayList<>();
if (model.getNbRows() != 0) {
for (int row = 0; row < model.getNbRows(); row++) {
output.add(new MBlacklist((String) model.getRow(row).get(0), (Boolean) model.getRow(row).get(2), (String) model.getRow(row).get(3),
(String) model.getRow(row).get(1)));
}
} else {
throw new RuntimeException("Blacklist is empty");
}
} catch (NullPointerException | IndexOutOfBoundsException ignored) { }
success.accept(output);
});
}
public boolean isAlreadyBlacklisted(String word, String serverId) {
ResultSet rs = database.prepareAndExecute("SELECT COUNT(*) FROM blacklist WHERE word = ? AND serverId = ?", word, serverId);
@Deprecated
private boolean isAlreadyBlacklisted(String word, String serverId) {
AtomicBoolean result = new AtomicBoolean();
try {
if (rs.next()) {
return rs.getInt(1) != 0;
database.prepareAndExecuteAsync("SELECT COUNT(*) FROM blacklist WHERE word = ? AND serverId = ?", word, serverId).async(table -> {
if (!table.isEmpty()) {
result.set((int) table.getRowModel(0).getItem(0) != 0);
}
} catch (SQLException e) {
//TODO: error log
e.printStackTrace();
});
return result.get();
}
return false;
private void isAlreadyBlacklisted(String word, String serverId, Consumer<Integer> result) {
database.prepareAndExecuteAsync("SELECT COUNT(*) FROM blacklist WHERE word = ? AND serverId = ?", word, serverId).async(table -> {
if (!table.isEmpty()) {
int count = Integer.parseInt(table.getRowModel(0).getItem(0).toString());
result.accept(count);
}
});
}
@Override
@@ -28,18 +28,12 @@ public class DBMessage extends AbstractController {
}
public void log(String servername, String channelname, String username, String datesent, String messageId, String message) {
ThreadManager.executor.submit(() -> {
database.insert("INSERT INTO log_message VALUES(?,?,?,?,?,?,?)", servername, channelname, username, datesent, messageId, message, null);
});
database.prepareAndExecuteAsync("INSERT INTO log_message VALUES(?,?,?,?,?,?,?)", servername, channelname, username, datesent, messageId, message, null).async();
}
public void logEdit(String messageId, String newMessage) {
ThreadManager.executor.submit(() -> {
database.update("UPDATE log_message SET editedMessage = ? WHERE messageId = ?", newMessage, messageId);
});
database.prepareAndExecuteAsync("UPDATE log_message SET editedMessage = ? WHERE messageId = ?", newMessage, messageId).async();
}
@@ -1,10 +1,8 @@
package netneutrality.db.controllers;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import netneutrality.ThreadManager;
import netneutrality.db.AbstractController;
import netneutrality.db.MysqlDatabase;
import netneutrality.db.models.MTableModel;
@@ -13,13 +11,9 @@ public class DBSettings extends AbstractController {
public void getSettingsByKey(String key, Consumer<Object> success) {
ThreadManager.executor.submit(() -> {
database.prepareAndExecuteAsync("SELECT value FROM settings WHERE skey = ?", key).async(table -> {
Object value = null;
ResultSet rs;
rs = database.prepareAndExecute("SELECT value FROM settings WHERE skey = ?", key);
MTableModel table = MysqlDatabase.convertToTableModel(rs);
if (table.getNbRows() == 1) {
value = table.getRowModel(0).getItem(0);
}
@@ -28,11 +22,10 @@ public class DBSettings extends AbstractController {
});
}
@Deprecated
public String getSettingsByKey(String key) {
ResultSet rs;
rs = database.prepareAndExecute("SELECT value FROM settings WHERE skey = ?", key);
MTableModel table = MysqlDatabase.convertToTableModel(rs);
MTableModel table = database.prepareAndExecuteAsync("SELECT value FROM settings WHERE skey = ?", key).blocking();
if (table.getNbRows() == 1) {
return table.getRow(0).get(0).toString();