mirror of
https://github.com/BentoBoxWorld/BentoBox.git
synced 2024-11-23 19:25:12 +01:00
SQL Database abstraction (#831)
* Database abstraction WIP * Removes code duplication in the databases Fixes a regression bug on database connections - more than 1 were being made again. * Added ignores to tests because they run async now
This commit is contained in:
parent
b5367200df
commit
1c1996ba4c
@ -3,7 +3,11 @@ package world.bentobox.bentobox.database;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
@ -19,6 +23,21 @@ import world.bentobox.bentobox.api.addons.Addon;
|
||||
*/
|
||||
public abstract class AbstractDatabaseHandler<T> {
|
||||
|
||||
/**
|
||||
* FIFO queue for saves or deletions. Note that the assumption here is that most database objects will be held
|
||||
* in memory because loading is not handled with this queue. That means that it is theoretically
|
||||
* possible to load something before it has been saved. So, in general, load your objects and then
|
||||
* save them async only when you do not need the data again immediately.
|
||||
*/
|
||||
protected Queue<Runnable> processQueue;
|
||||
|
||||
/**
|
||||
* Async save task that runs repeatedly
|
||||
*/
|
||||
private BukkitTask asyncSaveTask;
|
||||
|
||||
protected boolean shutdown;
|
||||
|
||||
/**
|
||||
* Name of the folder where databases using files will live
|
||||
*/
|
||||
@ -75,6 +94,33 @@ public abstract class AbstractDatabaseHandler<T> {
|
||||
this.plugin = plugin;
|
||||
this.databaseConnector = databaseConnector;
|
||||
this.dataObject = type;
|
||||
|
||||
// Run async queue
|
||||
processQueue = new ConcurrentLinkedQueue<>();
|
||||
if (plugin.isEnabled()) {
|
||||
asyncSaveTask = Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
// Loop continuously
|
||||
while (!shutdown || !processQueue.isEmpty()) {
|
||||
// This catches any databases that are not explicitly closed
|
||||
if (!plugin.isEnabled()) {
|
||||
shutdown = true;
|
||||
}
|
||||
while (!processQueue.isEmpty()) {
|
||||
processQueue.poll().run();
|
||||
}
|
||||
// Clear the queue and then sleep
|
||||
try {
|
||||
Thread.sleep(25);
|
||||
} catch (InterruptedException e) {
|
||||
plugin.logError("Thread sleep error " + e.getMessage());
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
// Cancel
|
||||
asyncSaveTask.cancel();
|
||||
databaseConnector.closeConnection(dataObject);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected AbstractDatabaseHandler() {}
|
||||
|
@ -4,11 +4,11 @@ import java.util.Arrays;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||
import world.bentobox.bentobox.database.mariadb.MariaDBDatabase;
|
||||
import world.bentobox.bentobox.database.mongodb.MongoDBDatabase;
|
||||
import world.bentobox.bentobox.database.mysql.MySQLDatabase;
|
||||
import world.bentobox.bentobox.database.postgresql.PostgreSQLDatabase;
|
||||
import world.bentobox.bentobox.database.sqlite.SQLiteDatabase;
|
||||
import world.bentobox.bentobox.database.sql.mariadb.MariaDBDatabase;
|
||||
import world.bentobox.bentobox.database.sql.mysql.MySQLDatabase;
|
||||
import world.bentobox.bentobox.database.sql.postgresql.PostgreSQLDatabase;
|
||||
import world.bentobox.bentobox.database.sql.sqlite.SQLiteDatabase;
|
||||
import world.bentobox.bentobox.database.transition.Json2MariaDBDatabase;
|
||||
import world.bentobox.bentobox.database.transition.Json2MongoDBDatabase;
|
||||
import world.bentobox.bentobox.database.transition.Json2MySQLDatabase;
|
||||
|
@ -41,6 +41,7 @@ public abstract class AbstractJSONDatabaseHandler<T> extends AbstractDatabaseHan
|
||||
builder.disableHtmlEscaping();
|
||||
|
||||
gson = builder.create();
|
||||
|
||||
}
|
||||
|
||||
protected Gson getGson() {
|
||||
|
@ -15,11 +15,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
@ -30,21 +26,6 @@ public class JSONDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
|
||||
private static final String JSON = ".json";
|
||||
|
||||
/**
|
||||
* FIFO queue for saves or deletions. Note that the assumption here is that most database objects will be held
|
||||
* in memory because loading is not handled with this queue. That means that it is theoretically
|
||||
* possible to load something before it has been saved. So, in general, load your objects and then
|
||||
* save them async only when you do not need the data again immediately.
|
||||
*/
|
||||
private Queue<Runnable> processQueue;
|
||||
|
||||
/**
|
||||
* Async save task that runs repeatedly
|
||||
*/
|
||||
private BukkitTask asyncSaveTask;
|
||||
|
||||
private boolean shutdown;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -55,30 +36,6 @@ public class JSONDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
*/
|
||||
JSONDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
||||
super(plugin, type, databaseConnector);
|
||||
processQueue = new ConcurrentLinkedQueue<>();
|
||||
if (plugin.isEnabled()) {
|
||||
asyncSaveTask = Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
// Loop continuously
|
||||
while (!shutdown || !processQueue.isEmpty()) {
|
||||
// This catches any databases that are not explicitly closed
|
||||
if (!plugin.isEnabled()) {
|
||||
shutdown = true;
|
||||
}
|
||||
while (!processQueue.isEmpty()) {
|
||||
processQueue.poll().run();
|
||||
}
|
||||
// Clear the queue and then sleep
|
||||
try {
|
||||
Thread.sleep(25);
|
||||
} catch (InterruptedException e) {
|
||||
plugin.logError("Thread sleep error " + e.getMessage());
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
// Cancel
|
||||
asyncSaveTask.cancel();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,81 +0,0 @@
|
||||
package world.bentobox.bentobox.database.mariadb;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
import world.bentobox.bentobox.database.DatabaseConnectionSettingsImpl;
|
||||
import world.bentobox.bentobox.database.DatabaseConnector;
|
||||
|
||||
/**
|
||||
* @author barpec12
|
||||
* @since 1.1
|
||||
*/
|
||||
public class MariaDBDatabaseConnector implements DatabaseConnector {
|
||||
|
||||
private String connectionUrl;
|
||||
private DatabaseConnectionSettingsImpl dbSettings;
|
||||
private Connection connection = null;
|
||||
private Set<Class<?>> types = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Class for MariaDB database connections using the settings provided
|
||||
* @param dbSettings - database settings
|
||||
*/
|
||||
MariaDBDatabaseConnector(DatabaseConnectionSettingsImpl dbSettings) {
|
||||
this.dbSettings = dbSettings;
|
||||
connectionUrl = "jdbc:mysql://" + dbSettings.getHost() + ":" + dbSettings.getPort() + "/" + dbSettings.getDatabaseName()
|
||||
+ "?autoReconnect=true&useSSL=false&allowMultiQueries=true";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConnectionUrl() {
|
||||
return connectionUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String getUniqueId(String tableName) {
|
||||
// Not used
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean uniqueIdExists(String tableName, String key) {
|
||||
// Not used
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void closeConnection(Class<?> type) {
|
||||
types.remove(type);
|
||||
if (types.isEmpty() && connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
Bukkit.getLogger().info("Closed database connection");
|
||||
} catch (SQLException e) {
|
||||
Bukkit.getLogger().severe("Could not close MariaDB database connection");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createConnection(Class<?> type) {
|
||||
types.add(type);
|
||||
// Only get one connection at a time
|
||||
if (connection == null) {
|
||||
try {
|
||||
connection = DriverManager.getConnection(connectionUrl, dbSettings.getUsername(), dbSettings.getPassword());
|
||||
} catch (SQLException e) {
|
||||
Bukkit.getLogger().severe("Could not connect to the database! " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
}
|
@ -1,283 +0,0 @@
|
||||
package world.bentobox.bentobox.database.mariadb;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.DatabaseConnector;
|
||||
import world.bentobox.bentobox.database.json.AbstractJSONDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.objects.DataObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class that inserts a <T> into the corresponding database-table.
|
||||
*
|
||||
* @author tastybento, barpec12
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class MariaDBDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
|
||||
private static final String COULD_NOT_LOAD_OBJECTS = "Could not load objects ";
|
||||
private static final String COULD_NOT_LOAD_OBJECT = "Could not load object ";
|
||||
|
||||
/**
|
||||
* Connection to the database
|
||||
*/
|
||||
private Connection connection;
|
||||
|
||||
/**
|
||||
* FIFO queue for saves or deletions. Note that the assumption here is that most database objects will be held
|
||||
* in memory because loading is not handled with this queue. That means that it is theoretically
|
||||
* possible to load something before it has been saved. So, in general, load your objects and then
|
||||
* save them async only when you do not need the data again immediately.
|
||||
*/
|
||||
private Queue<Runnable> processQueue;
|
||||
|
||||
/**
|
||||
* Async save task that runs repeatedly
|
||||
*/
|
||||
private BukkitTask asyncSaveTask;
|
||||
|
||||
private boolean shutdown;
|
||||
|
||||
|
||||
/**
|
||||
* Handles the connection to the database and creation of the initial database schema (tables) for
|
||||
* the class that will be stored.
|
||||
* @param plugin - plugin object
|
||||
* @param type - the type of class to be stored in the database. Must inherit DataObject
|
||||
* @param dbConnecter - authentication details for the database
|
||||
*/
|
||||
MariaDBDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector dbConnecter) {
|
||||
super(plugin, type, dbConnecter);
|
||||
connection = (Connection)dbConnecter.createConnection(dataObject);
|
||||
if (connection == null) {
|
||||
plugin.logError("Are the settings in config.yml correct?");
|
||||
Bukkit.getPluginManager().disablePlugin(plugin);
|
||||
return;
|
||||
}
|
||||
// Check if the table exists in the database and if not, create it
|
||||
createSchema();
|
||||
processQueue = new ConcurrentLinkedQueue<>();
|
||||
if (plugin.isEnabled()) {
|
||||
asyncSaveTask = Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
// Loop continuously
|
||||
while (!shutdown || !processQueue.isEmpty()) {
|
||||
if (!plugin.isEnabled()) {
|
||||
shutdown = true;
|
||||
}
|
||||
while (!processQueue.isEmpty()) {
|
||||
processQueue.poll().run();
|
||||
}
|
||||
// Clear the queue and then sleep
|
||||
try {
|
||||
Thread.sleep(25);
|
||||
} catch (InterruptedException e) {
|
||||
plugin.logError("Thread sleep error " + e.getMessage());
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
// Cancel
|
||||
asyncSaveTask.cancel();
|
||||
dbConnecter.closeConnection(dataObject);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the table in the database if it doesn't exist already
|
||||
*/
|
||||
private void createSchema() {
|
||||
String sql = "CREATE TABLE IF NOT EXISTS `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (JSON_EXTRACT(json, \"$.uniqueId\")), UNIQUE INDEX i (uniqueId))";
|
||||
// Prepare and execute the database statements
|
||||
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
|
||||
pstmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Problem trying to create schema for data object " + dataObject.getCanonicalName() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> loadObjects() {
|
||||
try (Statement preparedStatement = connection.createStatement()) {
|
||||
return loadIt(preparedStatement);
|
||||
} catch (SQLException e) {
|
||||
plugin.logError(COULD_NOT_LOAD_OBJECTS + e.getMessage());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<T> loadIt(Statement preparedStatement) {
|
||||
List<T> list = new ArrayList<>();
|
||||
|
||||
String sb = "SELECT `json` FROM `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"`";
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery(sb)) {
|
||||
// Load all the results
|
||||
Gson gson = getGson();
|
||||
while (resultSet.next()) {
|
||||
String json = resultSet.getString("json");
|
||||
if (json != null) {
|
||||
try {
|
||||
T gsonResult = gson.fromJson(json, dataObject);
|
||||
if (gsonResult != null) {
|
||||
list.add(gsonResult);
|
||||
}
|
||||
} catch (JsonSyntaxException ex) {
|
||||
plugin.logError(COULD_NOT_LOAD_OBJECT + ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.logError(COULD_NOT_LOAD_OBJECTS + e.getMessage());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T loadObject(String uniqueId) {
|
||||
String sb = "SELECT `json` FROM `" + dataObject.getCanonicalName() + "` WHERE uniqueId = ? LIMIT 1";
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
||||
// UniqueId needs to be placed in quotes
|
||||
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery()) {
|
||||
if (resultSet.next()) {
|
||||
// If there is a result, we only want/need the first one
|
||||
Gson gson = getGson();
|
||||
return gson.fromJson(resultSet.getString("json"), dataObject);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.logError(COULD_NOT_LOAD_OBJECT + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.logError(COULD_NOT_LOAD_OBJECT + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveObject(T instance) {
|
||||
// Null check
|
||||
if (instance == null) {
|
||||
plugin.logError("MySQL database request to store a null. ");
|
||||
return;
|
||||
}
|
||||
if (!(instance instanceof DataObject)) {
|
||||
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
return;
|
||||
}
|
||||
String sb = "INSERT INTO " +
|
||||
"`" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` (json) VALUES (?) ON DUPLICATE KEY UPDATE json = ?";
|
||||
|
||||
Gson gson = getGson();
|
||||
String toStore = gson.toJson(instance);
|
||||
if (plugin.isEnabled()) {
|
||||
// Async
|
||||
processQueue.add(() -> store(instance, toStore, sb));
|
||||
} else {
|
||||
// Sync
|
||||
store(instance, toStore, sb);
|
||||
}
|
||||
}
|
||||
|
||||
private void store(T instance, String toStore, String sb) {
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
||||
preparedStatement.setString(1, toStore);
|
||||
preparedStatement.setString(2, toStore);
|
||||
preparedStatement.execute();
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Could not save object " + instance.getClass().getName() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see world.bentobox.bentobox.database.AbstractDatabaseHandler#deleteID(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void deleteID(String uniqueId) {
|
||||
if (plugin.isEnabled()) {
|
||||
processQueue.add(() -> delete(uniqueId));
|
||||
} else {
|
||||
delete(uniqueId);
|
||||
}
|
||||
}
|
||||
|
||||
private void delete(String uniqueId) {
|
||||
String sb = "DELETE FROM `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` WHERE uniqueId = ?";
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
||||
// UniqueId needs to be placed in quotes
|
||||
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
||||
preparedStatement.execute();
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not delete object " + dataObject.getCanonicalName() + " " + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteObject(T instance) {
|
||||
// Null check
|
||||
if (instance == null) {
|
||||
plugin.logError("MariaDB database request to delete a null.");
|
||||
return;
|
||||
}
|
||||
if (!(instance instanceof DataObject)) {
|
||||
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Method getUniqueId = dataObject.getMethod("getUniqueId");
|
||||
deleteID((String) getUniqueId.invoke(instance));
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not delete object " + instance.getClass().getName() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean objectExists(String uniqueId) {
|
||||
// Create the query to see if this key exists
|
||||
String query = "SELECT IF ( EXISTS( SELECT * FROM `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` WHERE `uniqueId` = ?), 1, 0)";
|
||||
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(query)) {
|
||||
// UniqueId needs to be placed in quotes
|
||||
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery()) {
|
||||
if (resultSet.next()) {
|
||||
return resultSet.getBoolean(1);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Could not check if key exists in database! " + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
shutdown = true;
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
/**
|
||||
* Contains MySQL database managers.
|
||||
*/
|
||||
package world.bentobox.bentobox.database.mysql;
|
@ -1,79 +0,0 @@
|
||||
package world.bentobox.bentobox.database.postgresql;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
import world.bentobox.bentobox.database.DatabaseConnectionSettingsImpl;
|
||||
import world.bentobox.bentobox.database.DatabaseConnector;
|
||||
|
||||
/**
|
||||
* @since 1.6.0
|
||||
* @author Poslovitch
|
||||
*/
|
||||
public class PostgreSQLDatabaseConnector implements DatabaseConnector {
|
||||
|
||||
private String connectionUrl;
|
||||
private DatabaseConnectionSettingsImpl dbSettings;
|
||||
private Connection connection = null;
|
||||
private Set<Class<?>> types = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Class for PostgreSQL database connections using the settings provided
|
||||
* @param dbSettings - database settings
|
||||
*/
|
||||
PostgreSQLDatabaseConnector(@NonNull DatabaseConnectionSettingsImpl dbSettings) {
|
||||
this.dbSettings = dbSettings;
|
||||
connectionUrl = "jdbc:postgresql://" + dbSettings.getHost() + ":" + dbSettings.getPort() + "/" + dbSettings.getDatabaseName()
|
||||
+ "?autoReconnect=true&useSSL=false&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConnectionUrl() {
|
||||
return connectionUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getUniqueId(String tableName) {
|
||||
// Not used
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean uniqueIdExists(String tableName, String key) {
|
||||
// Not used
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeConnection(Class<?> type) {
|
||||
types.remove(type);
|
||||
if (types.isEmpty() && connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
Bukkit.getLogger().info("Closed database connection");
|
||||
} catch (SQLException e) {
|
||||
Bukkit.getLogger().severe("Could not close PostgreSQL database connection");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createConnection(Class<?> type) {
|
||||
types.add(type);
|
||||
// Only make one connection to the database
|
||||
if (connection == null) {
|
||||
try {
|
||||
connection = DriverManager.getConnection(connectionUrl, dbSettings.getUsername(), dbSettings.getPassword());
|
||||
} catch (SQLException e) {
|
||||
Bukkit.getLogger().severe("Could not connect to the database! " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
}
|
@ -1,279 +0,0 @@
|
||||
package world.bentobox.bentobox.database.postgresql;
|
||||
|
||||
import java.beans.IntrospectionException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.DatabaseConnector;
|
||||
import world.bentobox.bentobox.database.json.AbstractJSONDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.objects.DataObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <T>
|
||||
*
|
||||
* @since 1.6.0
|
||||
* @author tastybento, Poslovitch
|
||||
*/
|
||||
public class PostgreSQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
|
||||
private static final String COULD_NOT_LOAD_OBJECTS = "Could not load objects ";
|
||||
private static final String COULD_NOT_LOAD_OBJECT = "Could not load object ";
|
||||
|
||||
/**
|
||||
* Connection to the database
|
||||
*/
|
||||
private Connection connection;
|
||||
|
||||
/**
|
||||
* FIFO queue for saves or deletions. Note that the assumption here is that most database objects will be held
|
||||
* in memory because loading is not handled with this queue. That means that it is theoretically
|
||||
* possible to load something before it has been saved. So, in general, load your objects and then
|
||||
* save them async only when you do not need the data again immediately.
|
||||
*/
|
||||
private Queue<Runnable> processQueue;
|
||||
|
||||
/**
|
||||
* Async save task that runs repeatedly
|
||||
*/
|
||||
private BukkitTask asyncSaveTask;
|
||||
|
||||
private boolean shutdown;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param plugin
|
||||
* @param type The type of the objects that should be created and filled with
|
||||
* values from the database or inserted into the database
|
||||
* @param databaseConnector Contains the settings to create a connection to the database
|
||||
*/
|
||||
protected PostgreSQLDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
||||
super(plugin, type, databaseConnector);
|
||||
connection = (Connection) databaseConnector.createConnection(dataObject);
|
||||
if (connection == null) {
|
||||
plugin.logError("Are the settings in config.yml correct?");
|
||||
Bukkit.getPluginManager().disablePlugin(plugin);
|
||||
return;
|
||||
}
|
||||
// Check if the table exists in the database and if not, create it
|
||||
createSchema();
|
||||
processQueue = new ConcurrentLinkedQueue<>();
|
||||
if (plugin.isEnabled()) {
|
||||
asyncSaveTask = Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
// Loop continuously
|
||||
while (!shutdown || !processQueue.isEmpty()) {
|
||||
|
||||
while (!processQueue.isEmpty()) {
|
||||
processQueue.poll().run();
|
||||
}
|
||||
// Clear the queue and then sleep
|
||||
try {
|
||||
Thread.sleep(25);
|
||||
} catch (InterruptedException e) {
|
||||
plugin.logError("Thread sleep error " + e.getMessage());
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
// Cancel
|
||||
asyncSaveTask.cancel();
|
||||
databaseConnector.closeConnection(dataObject);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the table in the database if it doesn't exist already
|
||||
*/
|
||||
private void createSchema() {
|
||||
String sql = "CREATE TABLE IF NOT EXISTS `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )";
|
||||
// Prepare and execute the database statements
|
||||
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
|
||||
pstmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Problem trying to create schema for data object " + dataObject.getCanonicalName() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> loadObjects() throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, IntrospectionException, NoSuchMethodException {
|
||||
try (Statement preparedStatement = connection.createStatement()) {
|
||||
List<T> list = new ArrayList<>();
|
||||
|
||||
String sb = "SELECT `json` FROM `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"`";
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery(sb)) {
|
||||
// Load all the results
|
||||
Gson gson = getGson();
|
||||
while (resultSet.next()) {
|
||||
String json = resultSet.getString("json");
|
||||
if (json != null) {
|
||||
try {
|
||||
T gsonResult = gson.fromJson(json, dataObject);
|
||||
if (gsonResult != null) {
|
||||
list.add(gsonResult);
|
||||
}
|
||||
} catch (JsonSyntaxException ex) {
|
||||
plugin.logError(COULD_NOT_LOAD_OBJECT + ex.getMessage());
|
||||
plugin.logError(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.logError(COULD_NOT_LOAD_OBJECTS + e.getMessage());
|
||||
}
|
||||
return list;
|
||||
} catch (SQLException e) {
|
||||
plugin.logError(COULD_NOT_LOAD_OBJECTS + e.getMessage());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T loadObject(@NonNull String uniqueId) throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, IntrospectionException, NoSuchMethodException {
|
||||
String sb = "SELECT `json` FROM `" + dataObject.getCanonicalName() + "` WHERE uniqueId = ? LIMIT 1";
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
||||
// UniqueId needs to be placed in quotes
|
||||
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery()) {
|
||||
if (resultSet.next()) {
|
||||
// If there is a result, we only want/need the first one
|
||||
Gson gson = getGson();
|
||||
return gson.fromJson(resultSet.getString("json"), dataObject);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.logError(COULD_NOT_LOAD_OBJECT + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.logError(COULD_NOT_LOAD_OBJECT + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
|
||||
// Null check
|
||||
if (instance == null) {
|
||||
plugin.logError("MySQL database request to store a null. ");
|
||||
return;
|
||||
}
|
||||
if (!(instance instanceof DataObject)) {
|
||||
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
return;
|
||||
}
|
||||
String sb = "INSERT INTO " +
|
||||
"`" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` (json) VALUES (?) ON DUPLICATE KEY UPDATE json = ?";
|
||||
|
||||
Gson gson = getGson();
|
||||
String toStore = gson.toJson(instance);
|
||||
if (plugin.isEnabled()) {
|
||||
// Async
|
||||
processQueue.add(() -> store(instance, toStore, sb));
|
||||
} else {
|
||||
// Sync
|
||||
store(instance, toStore, sb);
|
||||
}
|
||||
}
|
||||
|
||||
private void store(T instance, String toStore, String sb) {
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
||||
preparedStatement.setString(1, toStore);
|
||||
preparedStatement.setString(2, toStore);
|
||||
preparedStatement.execute();
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Could not save object " + instance.getClass().getName() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
|
||||
// Null check
|
||||
if (instance == null) {
|
||||
plugin.logError("MySQL database request to delete a null.");
|
||||
return;
|
||||
}
|
||||
if (!(instance instanceof DataObject)) {
|
||||
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Method getUniqueId = dataObject.getMethod("getUniqueId");
|
||||
deleteID((String) getUniqueId.invoke(instance));
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not delete object " + instance.getClass().getName() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void delete(String uniqueId) {
|
||||
String sb = "DELETE FROM `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` WHERE uniqueId = ?";
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
||||
// UniqueId needs to be placed in quotes
|
||||
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
||||
preparedStatement.execute();
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not delete object " + dataObject.getCanonicalName() + " " + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean objectExists(String uniqueId) {
|
||||
// Create the query to see if this key exists
|
||||
String query = "SELECT IF ( EXISTS( SELECT * FROM `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` WHERE `uniqueId` = ?), 1, 0)";
|
||||
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(query)) {
|
||||
// UniqueId needs to be placed in quotes
|
||||
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery()) {
|
||||
if (resultSet.next()) {
|
||||
return resultSet.getBoolean(1);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Could not check if key exists in database! " + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
shutdown = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteID(String uniqueId) {
|
||||
if (plugin.isEnabled()) {
|
||||
processQueue.add(() -> delete(uniqueId));
|
||||
} else {
|
||||
delete(uniqueId);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package world.bentobox.bentobox.database.sql;
|
||||
|
||||
/**
|
||||
* Contains the SQL strings for the database.
|
||||
* The default strings are for MySQL, so only the deltas need to be supplied.
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
public class SQLConfiguration {
|
||||
private String loadObjectSQL;
|
||||
private String saveObjectSQL;
|
||||
private String deleteObjectSQL;
|
||||
private String objectExistsSQL;
|
||||
private String schemaSQL;
|
||||
private String loadObjectsSQL;
|
||||
|
||||
/**
|
||||
* @param canonicalName - canonical name of the class being stored.
|
||||
*/
|
||||
public SQLConfiguration(String canonicalName) {
|
||||
schemaSQL = "CREATE TABLE IF NOT EXISTS `" + canonicalName +
|
||||
"` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )";
|
||||
loadObjectsSQL = "SELECT `json` FROM `" + canonicalName + "`";
|
||||
loadObjectSQL = "SELECT `json` FROM `" + canonicalName + "` WHERE uniqueId = ? LIMIT 1";
|
||||
saveObjectSQL = "INSERT INTO `" + canonicalName + "` (json) VALUES (?) ON DUPLICATE KEY UPDATE json = ?";
|
||||
deleteObjectSQL = "DELETE FROM `" + canonicalName + "` WHERE uniqueId = ?";
|
||||
objectExistsSQL = "SELECT IF ( EXISTS( SELECT * FROM `" + canonicalName + "` WHERE `uniqueId` = ?), 1, 0)";
|
||||
}
|
||||
|
||||
public SQLConfiguration loadObject(String string) {
|
||||
this.loadObjectSQL = string;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SQLConfiguration saveObject(String string) {
|
||||
this.saveObjectSQL = string;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SQLConfiguration deleteObject(String string) {
|
||||
this.deleteObjectSQL = string;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SQLConfiguration objectExists(String string) {
|
||||
this.objectExistsSQL = string;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SQLConfiguration schema(String string) {
|
||||
this.schemaSQL = string;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SQLConfiguration loadObjects(String string) {
|
||||
this.loadObjectsSQL = string;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the loadObjectSQL
|
||||
*/
|
||||
public String getLoadObjectSQL() {
|
||||
return loadObjectSQL;
|
||||
}
|
||||
/**
|
||||
* @return the saveObjectSQL
|
||||
*/
|
||||
public String getSaveObjectSQL() {
|
||||
return saveObjectSQL;
|
||||
}
|
||||
/**
|
||||
* @return the deleteObjectSQL
|
||||
*/
|
||||
public String getDeleteObjectSQL() {
|
||||
return deleteObjectSQL;
|
||||
}
|
||||
/**
|
||||
* @return the objectExistsSQL
|
||||
*/
|
||||
public String getObjectExistsSQL() {
|
||||
return objectExistsSQL;
|
||||
}
|
||||
/**
|
||||
* @return the schemaSQL
|
||||
*/
|
||||
public String getSchemaSQL() {
|
||||
return schemaSQL;
|
||||
}
|
||||
/**
|
||||
* @return the loadItSQL
|
||||
*/
|
||||
public String getLoadObjectsSQL() {
|
||||
return loadObjectsSQL;
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package world.bentobox.bentobox.database.mysql;
|
||||
package world.bentobox.bentobox.database.sql;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
@ -12,21 +12,16 @@ import org.eclipse.jdt.annotation.NonNull;
|
||||
import world.bentobox.bentobox.database.DatabaseConnectionSettingsImpl;
|
||||
import world.bentobox.bentobox.database.DatabaseConnector;
|
||||
|
||||
public class MySQLDatabaseConnector implements DatabaseConnector {
|
||||
public abstract class SQLDatabaseConnector implements DatabaseConnector {
|
||||
|
||||
private String connectionUrl;
|
||||
protected String connectionUrl;
|
||||
private DatabaseConnectionSettingsImpl dbSettings;
|
||||
private Connection connection = null;
|
||||
private Set<Class<?>> types = new HashSet<>();
|
||||
protected static Connection connection = null;
|
||||
protected static Set<Class<?>> types = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Class for MySQL database connections using the settings provided
|
||||
* @param dbSettings - database settings
|
||||
*/
|
||||
MySQLDatabaseConnector(DatabaseConnectionSettingsImpl dbSettings) {
|
||||
public SQLDatabaseConnector(DatabaseConnectionSettingsImpl dbSettings, String connectionUrl) {
|
||||
this.dbSettings = dbSettings;
|
||||
connectionUrl = "jdbc:mysql://" + dbSettings.getHost() + ":" + dbSettings.getPort() + "/" + dbSettings.getDatabaseName()
|
||||
+ "?autoReconnect=true&useSSL=false&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8";
|
||||
this.connectionUrl = connectionUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -55,7 +50,7 @@ public class MySQLDatabaseConnector implements DatabaseConnector {
|
||||
connection.close();
|
||||
Bukkit.getLogger().info("Closed database connection");
|
||||
} catch (SQLException e) {
|
||||
Bukkit.getLogger().severe("Could not close MySQL database connection");
|
||||
Bukkit.getLogger().severe("Could not close database connection");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -73,4 +68,5 @@ public class MySQLDatabaseConnector implements DatabaseConnector {
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package world.bentobox.bentobox.database.mysql;
|
||||
package world.bentobox.bentobox.database.sql;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.Connection;
|
||||
@ -9,11 +9,8 @@ import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
@ -26,13 +23,14 @@ import world.bentobox.bentobox.database.objects.DataObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* Abstract class that covers SQL style databases
|
||||
* Class that inserts a <T> into the corresponding database-table.
|
||||
*
|
||||
* @author tastybento
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
public class SQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
|
||||
private static final String COULD_NOT_LOAD_OBJECTS = "Could not load objects ";
|
||||
private static final String COULD_NOT_LOAD_OBJECT = "Could not load object ";
|
||||
@ -43,20 +41,9 @@ public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
private Connection connection;
|
||||
|
||||
/**
|
||||
* FIFO queue for saves or deletions. Note that the assumption here is that most database objects will be held
|
||||
* in memory because loading is not handled with this queue. That means that it is theoretically
|
||||
* possible to load something before it has been saved. So, in general, load your objects and then
|
||||
* save them async only when you do not need the data again immediately.
|
||||
* SQL configuration
|
||||
*/
|
||||
private Queue<Runnable> processQueue;
|
||||
|
||||
/**
|
||||
* Async save task that runs repeatedly
|
||||
*/
|
||||
private BukkitTask asyncSaveTask;
|
||||
|
||||
private boolean shutdown;
|
||||
|
||||
private SQLConfiguration sqlConfig;
|
||||
|
||||
/**
|
||||
* Handles the connection to the database and creation of the initial database schema (tables) for
|
||||
@ -64,53 +51,37 @@ public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
* @param plugin - plugin object
|
||||
* @param type - the type of class to be stored in the database. Must inherit DataObject
|
||||
* @param dbConnecter - authentication details for the database
|
||||
* @param sqlConfiguration - SQL configuration
|
||||
*/
|
||||
MySQLDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector dbConnecter) {
|
||||
protected SQLDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector dbConnecter, SQLConfiguration sqlConfiguration) {
|
||||
super(plugin, type, dbConnecter);
|
||||
connection = (Connection)dbConnecter.createConnection(dataObject);
|
||||
if (connection == null) {
|
||||
plugin.logError("Are the settings in config.yml correct?");
|
||||
Bukkit.getPluginManager().disablePlugin(plugin);
|
||||
return;
|
||||
}
|
||||
this.sqlConfig = sqlConfiguration;
|
||||
if (setConnection((Connection)databaseConnector.createConnection(type))) {
|
||||
// Check if the table exists in the database and if not, create it
|
||||
createSchema();
|
||||
processQueue = new ConcurrentLinkedQueue<>();
|
||||
if (plugin.isEnabled()) {
|
||||
asyncSaveTask = Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
// Loop continuously
|
||||
while (!shutdown || !processQueue.isEmpty()) {
|
||||
// This catches any databases that are not explicitly closed
|
||||
if (!plugin.isEnabled()) {
|
||||
shutdown = true;
|
||||
}
|
||||
while (!processQueue.isEmpty()) {
|
||||
processQueue.poll().run();
|
||||
}
|
||||
// Clear the queue and then sleep
|
||||
try {
|
||||
Thread.sleep(25);
|
||||
} catch (InterruptedException e) {
|
||||
plugin.logError("Thread sleep error " + e.getMessage());
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
// Cancel
|
||||
asyncSaveTask.cancel();
|
||||
dbConnecter.closeConnection(dataObject);
|
||||
});
|
||||
|
||||
/**
|
||||
* @return the sqlConfig
|
||||
*/
|
||||
public SQLConfiguration getSqlConfig() {
|
||||
return sqlConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sqlConfig the sqlConfig to set
|
||||
*/
|
||||
public void setSqlConfig(SQLConfiguration sqlConfig) {
|
||||
this.sqlConfig = sqlConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the table in the database if it doesn't exist already
|
||||
*/
|
||||
private void createSchema() {
|
||||
String sql = "CREATE TABLE IF NOT EXISTS `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )";
|
||||
protected void createSchema() {
|
||||
// Prepare and execute the database statements
|
||||
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
|
||||
try (PreparedStatement pstmt = connection.prepareStatement(sqlConfig.getSchemaSQL())) {
|
||||
pstmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Problem trying to create schema for data object " + dataObject.getCanonicalName() + " " + e.getMessage());
|
||||
@ -129,11 +100,7 @@ public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
|
||||
private List<T> loadIt(Statement preparedStatement) {
|
||||
List<T> list = new ArrayList<>();
|
||||
|
||||
String sb = "SELECT `json` FROM `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"`";
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery(sb)) {
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery(sqlConfig.getLoadObjectsSQL())) {
|
||||
// Load all the results
|
||||
Gson gson = getGson();
|
||||
while (resultSet.next()) {
|
||||
@ -158,8 +125,7 @@ public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
|
||||
@Override
|
||||
public T loadObject(@NonNull String uniqueId) {
|
||||
String sb = "SELECT `json` FROM `" + dataObject.getCanonicalName() + "` WHERE uniqueId = ? LIMIT 1";
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sqlConfig.getLoadObjectSQL())) {
|
||||
// UniqueId needs to be placed in quotes
|
||||
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery()) {
|
||||
@ -181,30 +147,19 @@ public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
public void saveObject(T instance) {
|
||||
// Null check
|
||||
if (instance == null) {
|
||||
plugin.logError("MySQL database request to store a null. ");
|
||||
plugin.logError("SQL database request to store a null. ");
|
||||
return;
|
||||
}
|
||||
if (!(instance instanceof DataObject)) {
|
||||
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
return;
|
||||
}
|
||||
String sb = "INSERT INTO " +
|
||||
"`" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` (json) VALUES (?) ON DUPLICATE KEY UPDATE json = ?";
|
||||
|
||||
Gson gson = getGson();
|
||||
String toStore = gson.toJson(instance);
|
||||
if (plugin.isEnabled()) {
|
||||
// Async
|
||||
processQueue.add(() -> store(instance, toStore, sb));
|
||||
} else {
|
||||
// Sync
|
||||
store(instance, toStore, sb);
|
||||
}
|
||||
processQueue.add(() -> store(instance, sqlConfig.getSaveObjectSQL()));
|
||||
}
|
||||
|
||||
private void store(T instance, String toStore, String sb) {
|
||||
private void store(T instance, String sb) {
|
||||
String toStore = getGson().toJson(instance);
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
||||
preparedStatement.setString(1, toStore);
|
||||
preparedStatement.setString(2, toStore);
|
||||
@ -219,18 +174,11 @@ public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
*/
|
||||
@Override
|
||||
public void deleteID(String uniqueId) {
|
||||
if (plugin.isEnabled()) {
|
||||
processQueue.add(() -> delete(uniqueId));
|
||||
} else {
|
||||
delete(uniqueId);
|
||||
}
|
||||
}
|
||||
|
||||
private void delete(String uniqueId) {
|
||||
String sb = "DELETE FROM `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` WHERE uniqueId = ?";
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sqlConfig.getDeleteObjectSQL())) {
|
||||
// UniqueId needs to be placed in quotes
|
||||
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
||||
preparedStatement.execute();
|
||||
@ -243,7 +191,7 @@ public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
public void deleteObject(T instance) {
|
||||
// Null check
|
||||
if (instance == null) {
|
||||
plugin.logError("MySQL database request to delete a null.");
|
||||
plugin.logError("SQL database request to delete a null.");
|
||||
return;
|
||||
}
|
||||
if (!(instance instanceof DataObject)) {
|
||||
@ -260,12 +208,8 @@ public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
|
||||
@Override
|
||||
public boolean objectExists(String uniqueId) {
|
||||
// Create the query to see if this key exists
|
||||
String query = "SELECT IF ( EXISTS( SELECT * FROM `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` WHERE `uniqueId` = ?), 1, 0)";
|
||||
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(query)) {
|
||||
// Query to see if this key exists
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sqlConfig.getObjectExistsSQL())) {
|
||||
// UniqueId needs to be placed in quotes
|
||||
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery()) {
|
||||
@ -283,4 +227,25 @@ public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
public void close() {
|
||||
shutdown = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the connection
|
||||
*/
|
||||
public Connection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param connection the connection to set
|
||||
* @return true if connection is not null
|
||||
*/
|
||||
public boolean setConnection(Connection connection) {
|
||||
if (connection == null) {
|
||||
plugin.logError("Are the settings in config.yml correct?");
|
||||
Bukkit.getPluginManager().disablePlugin(plugin);
|
||||
return false;
|
||||
}
|
||||
this.connection = connection;
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package world.bentobox.bentobox.database.mariadb;
|
||||
package world.bentobox.bentobox.database.sql.mariadb;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
@ -0,0 +1,21 @@
|
||||
package world.bentobox.bentobox.database.sql.mariadb;
|
||||
|
||||
import world.bentobox.bentobox.database.DatabaseConnectionSettingsImpl;
|
||||
import world.bentobox.bentobox.database.sql.SQLDatabaseConnector;
|
||||
|
||||
/**
|
||||
* @author barpec12
|
||||
* @since 1.1
|
||||
*/
|
||||
public class MariaDBDatabaseConnector extends SQLDatabaseConnector {
|
||||
|
||||
/**
|
||||
* Class for MariaDB database connections using the settings provided
|
||||
* @param dbSettings - database settings
|
||||
*/
|
||||
MariaDBDatabaseConnector(DatabaseConnectionSettingsImpl dbSettings) {
|
||||
super(dbSettings, "jdbc:mysql://" + dbSettings.getHost() + ":" + dbSettings.getPort() + "/" + dbSettings.getDatabaseName()
|
||||
+ "?autoReconnect=true&useSSL=false&allowMultiQueries=true");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package world.bentobox.bentobox.database.sql.mariadb;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.DatabaseConnector;
|
||||
import world.bentobox.bentobox.database.sql.SQLDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.sql.SQLConfiguration;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class that inserts a <T> into the corresponding database-table.
|
||||
*
|
||||
* @author tastybento, barpec12
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class MariaDBDatabaseHandler<T> extends SQLDatabaseHandler<T> {
|
||||
|
||||
/**
|
||||
* Handles the connection to the database and creation of the initial database schema (tables) for
|
||||
* the class that will be stored.
|
||||
* @param plugin - plugin object
|
||||
* @param type - the type of class to be stored in the database. Must inherit DataObject
|
||||
* @param databaseConnector - authentication details for the database
|
||||
*/
|
||||
MariaDBDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
||||
super(plugin, type, databaseConnector, new SQLConfiguration(type.getCanonicalName())
|
||||
.schema("CREATE TABLE IF NOT EXISTS `" + type.getCanonicalName() +
|
||||
"` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (JSON_EXTRACT(json, \"$.uniqueId\")), UNIQUE INDEX i (uniqueId))"));
|
||||
}
|
||||
}
|
@ -3,4 +3,4 @@
|
||||
* @since 1.1
|
||||
* @author barpec12
|
||||
*/
|
||||
package world.bentobox.bentobox.database.mariadb;
|
||||
package world.bentobox.bentobox.database.sql.mariadb;
|
@ -1,4 +1,4 @@
|
||||
package world.bentobox.bentobox.database.mysql;
|
||||
package world.bentobox.bentobox.database.sql.mysql;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
@ -0,0 +1,16 @@
|
||||
package world.bentobox.bentobox.database.sql.mysql;
|
||||
|
||||
import world.bentobox.bentobox.database.DatabaseConnectionSettingsImpl;
|
||||
import world.bentobox.bentobox.database.sql.SQLDatabaseConnector;
|
||||
|
||||
public class MySQLDatabaseConnector extends SQLDatabaseConnector {
|
||||
|
||||
/**
|
||||
* Class for MySQL database connections using the settings provided
|
||||
* @param dbSettings - database settings
|
||||
*/
|
||||
MySQLDatabaseConnector(DatabaseConnectionSettingsImpl dbSettings) {
|
||||
super(dbSettings, "jdbc:mysql://" + dbSettings.getHost() + ":" + dbSettings.getPort() + "/" + dbSettings.getDatabaseName()
|
||||
+ "?autoReconnect=true&useSSL=false&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8");
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package world.bentobox.bentobox.database.sql.mysql;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.DatabaseConnector;
|
||||
import world.bentobox.bentobox.database.sql.SQLDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.sql.SQLConfiguration;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class that inserts a <T> into the corresponding database-table.
|
||||
*
|
||||
* @author tastybento
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class MySQLDatabaseHandler<T> extends SQLDatabaseHandler<T> {
|
||||
|
||||
/**
|
||||
* Handles the connection to the database and creation of the initial database schema (tables) for
|
||||
* the class that will be stored.
|
||||
* @param plugin - plugin object
|
||||
* @param type - the type of class to be stored in the database. Must inherit DataObject
|
||||
* @param dbConnecter - authentication details for the database
|
||||
*/
|
||||
MySQLDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector dbConnecter) {
|
||||
super(plugin, type, dbConnecter, new SQLConfiguration(type.getCanonicalName()));
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains MySQL database managers.
|
||||
*/
|
||||
package world.bentobox.bentobox.database.sql.mysql;
|
@ -1,4 +1,4 @@
|
||||
package world.bentobox.bentobox.database.postgresql;
|
||||
package world.bentobox.bentobox.database.sql.postgresql;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
@ -0,0 +1,22 @@
|
||||
package world.bentobox.bentobox.database.sql.postgresql;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
import world.bentobox.bentobox.database.DatabaseConnectionSettingsImpl;
|
||||
import world.bentobox.bentobox.database.sql.SQLDatabaseConnector;
|
||||
|
||||
/**
|
||||
* @since 1.6.0
|
||||
* @author Poslovitch
|
||||
*/
|
||||
public class PostgreSQLDatabaseConnector extends SQLDatabaseConnector {
|
||||
|
||||
/**
|
||||
* Class for PostgreSQL database connections using the settings provided
|
||||
* @param dbSettings - database settings
|
||||
*/
|
||||
PostgreSQLDatabaseConnector(@NonNull DatabaseConnectionSettingsImpl dbSettings) {
|
||||
super(dbSettings, "jdbc:postgresql://" + dbSettings.getHost() + ":" + dbSettings.getPort() + "/" + dbSettings.getDatabaseName()
|
||||
+ "?autoReconnect=true&useSSL=false&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8");
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package world.bentobox.bentobox.database.sql.postgresql;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.DatabaseConnector;
|
||||
import world.bentobox.bentobox.database.sql.SQLDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.sql.SQLConfiguration;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <T>
|
||||
*
|
||||
* @since 1.6.0
|
||||
* @author tastybento, Poslovitch
|
||||
*/
|
||||
public class PostgreSQLDatabaseHandler<T> extends SQLDatabaseHandler<T> {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param plugin
|
||||
* @param type The type of the objects that should be created and filled with
|
||||
* values from the database or inserted into the database
|
||||
* @param databaseConnector Contains the settings to create a connection to the database
|
||||
*/
|
||||
PostgreSQLDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
||||
super(plugin, type, databaseConnector, new SQLConfiguration(type.getCanonicalName()));
|
||||
}
|
||||
|
||||
}
|
@ -2,4 +2,4 @@
|
||||
* Contains PostgreSQL database managers.
|
||||
* @since 1.6.0
|
||||
*/
|
||||
package world.bentobox.bentobox.database.postgresql;
|
||||
package world.bentobox.bentobox.database.sql.postgresql;
|
@ -1,4 +1,4 @@
|
||||
package world.bentobox.bentobox.database.sqlite;
|
||||
package world.bentobox.bentobox.database.sql.sqlite;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
@ -0,0 +1,48 @@
|
||||
package world.bentobox.bentobox.database.sql.sqlite;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.sql.SQLDatabaseConnector;
|
||||
|
||||
/**
|
||||
* @since 1.6.0
|
||||
* @author Poslovitch
|
||||
*/
|
||||
public class SQLiteDatabaseConnector extends SQLDatabaseConnector {
|
||||
|
||||
private static final String DATABASE_FOLDER_NAME = "database";
|
||||
|
||||
SQLiteDatabaseConnector(@NonNull BentoBox plugin) {
|
||||
super(null, ""); // Not used by SQLite
|
||||
File dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME);
|
||||
if (!dataFolder.exists() && !dataFolder.mkdirs()) {
|
||||
BentoBox.getInstance().logError("Could not create database folder!");
|
||||
return;
|
||||
}
|
||||
connectionUrl = "jdbc:sqlite:" + dataFolder.getAbsolutePath() + File.separator + "database.db";
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see world.bentobox.bentobox.database.sql.SQLDatabaseConnector#createConnection(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public Object createConnection(Class<?> type) {
|
||||
types.add(type);
|
||||
// Only make one connection at a time
|
||||
if (connection == null) {
|
||||
try {
|
||||
connection = DriverManager.getConnection(connectionUrl);
|
||||
} catch (SQLException e) {
|
||||
Bukkit.getLogger().severe("Could not connect to the database! " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package world.bentobox.bentobox.database.sql.sqlite;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.DatabaseConnector;
|
||||
import world.bentobox.bentobox.database.objects.DataObject;
|
||||
import world.bentobox.bentobox.database.sql.SQLDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.sql.SQLConfiguration;
|
||||
|
||||
/**
|
||||
* @since 1.6.0
|
||||
* @author Poslovitch, tastybento
|
||||
*/
|
||||
public class SQLiteDatabaseHandler<T> extends SQLDatabaseHandler<T> {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param plugin
|
||||
* @param type The type of the objects that should be created and filled with
|
||||
* values from the database or inserted into the database
|
||||
* @param databaseConnector Contains the settings to create a connection to the database
|
||||
*/
|
||||
protected SQLiteDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
||||
super(plugin, type, databaseConnector, new SQLConfiguration(type.getCanonicalName())
|
||||
.schema("CREATE TABLE IF NOT EXISTS `" + type.getCanonicalName() + "` (json JSON, uniqueId VARCHAR(255) NOT NULL PRIMARY KEY)")
|
||||
.saveObject("INSERT INTO `" + type.getCanonicalName()
|
||||
+ "` (json, uniqueId) VALUES (?, ?) ON CONFLICT(uniqueId) DO UPDATE SET json = ?")
|
||||
.objectExists("SELECT EXISTS (SELECT 1 FROM `" + type.getCanonicalName() + "` WHERE `uniqueId` = ?)"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveObject(T instance) {
|
||||
// Null check
|
||||
if (instance == null) {
|
||||
plugin.logError("MySQL database request to store a null. ");
|
||||
return;
|
||||
}
|
||||
if (!(instance instanceof DataObject)) {
|
||||
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
return;
|
||||
}
|
||||
processQueue.add(() -> {
|
||||
Gson gson = getGson();
|
||||
String toStore = gson.toJson(instance);
|
||||
try (PreparedStatement preparedStatement = getConnection().prepareStatement(getSqlConfig().getSaveObjectSQL())) {
|
||||
preparedStatement.setString(1, toStore);
|
||||
preparedStatement.setString(2, ((DataObject)instance).getUniqueId());
|
||||
preparedStatement.setString(3, toStore);
|
||||
preparedStatement.execute();
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Could not save object " + instance.getClass().getName() + " " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteID(String uniqueId) {
|
||||
processQueue.add(() -> {
|
||||
try (PreparedStatement preparedStatement = getConnection().prepareStatement(getSqlConfig().getDeleteObjectSQL())) {
|
||||
// UniqueId must *not* be placed in quotes
|
||||
preparedStatement.setString(1, uniqueId);
|
||||
int result = preparedStatement.executeUpdate();
|
||||
if (result != 1) {
|
||||
throw new SQLException("Delete did not affect any rows!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not delete object " + dataObject.getCanonicalName() + " " + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -2,4 +2,4 @@
|
||||
* Contains SQLite database managers.
|
||||
* @since 1.6.0
|
||||
*/
|
||||
package world.bentobox.bentobox.database.sqlite;
|
||||
package world.bentobox.bentobox.database.sql.sqlite;
|
@ -1,81 +0,0 @@
|
||||
package world.bentobox.bentobox.database.sqlite;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.DatabaseConnector;
|
||||
|
||||
/**
|
||||
* @since 1.6.0
|
||||
* @author Poslovitch
|
||||
*/
|
||||
public class SQLiteDatabaseConnector implements DatabaseConnector {
|
||||
|
||||
private String connectionUrl;
|
||||
private Connection connection = null;
|
||||
private static final String DATABASE_FOLDER_NAME = "database";
|
||||
private Set<Class<?>> types = new HashSet<>();
|
||||
|
||||
SQLiteDatabaseConnector(@NonNull BentoBox plugin) {
|
||||
File dataFolder = new File(plugin.getDataFolder(), DATABASE_FOLDER_NAME);
|
||||
if (!dataFolder.exists()) {
|
||||
if (!dataFolder.mkdirs()) {
|
||||
BentoBox.getInstance().logError("Could not create database folder!");
|
||||
}
|
||||
}
|
||||
connectionUrl = "jdbc:sqlite:" + dataFolder.getAbsolutePath() + File.separator + "database.db";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConnectionUrl() {
|
||||
return connectionUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String getUniqueId(String tableName) {
|
||||
// Not used
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean uniqueIdExists(String tableName, String key) {
|
||||
// Not used
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeConnection(Class<?> type) {
|
||||
types.remove(type);
|
||||
if (types.isEmpty() && connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
Bukkit.getLogger().info("Closed database connection");
|
||||
} catch (SQLException e) {
|
||||
Bukkit.getLogger().severe("Could not close SQLite database connection");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createConnection(Class<?> type) {
|
||||
types.add(type);
|
||||
// Only make one connection at a time
|
||||
if (connection == null) {
|
||||
try {
|
||||
connection = DriverManager.getConnection(connectionUrl);
|
||||
} catch (SQLException e) {
|
||||
Bukkit.getLogger().severe("Could not connect to the database! " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
}
|
@ -1,220 +0,0 @@
|
||||
package world.bentobox.bentobox.database.sqlite;
|
||||
|
||||
import java.beans.IntrospectionException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.DatabaseConnector;
|
||||
import world.bentobox.bentobox.database.json.AbstractJSONDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.objects.DataObject;
|
||||
|
||||
/**
|
||||
* @since 1.6.0
|
||||
* @author Poslovitch
|
||||
*/
|
||||
public class SQLiteDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
|
||||
/**
|
||||
* Connection to the database
|
||||
*/
|
||||
private Connection connection;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param plugin
|
||||
* @param type The type of the objects that should be created and filled with
|
||||
* values from the database or inserted into the database
|
||||
* @param databaseConnector Contains the settings to create a connection to the database
|
||||
*/
|
||||
protected SQLiteDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
||||
super(plugin, type, databaseConnector);
|
||||
connection = (Connection) databaseConnector.createConnection(dataObject);
|
||||
if (connection == null) {
|
||||
plugin.logError("Are the settings in config.yml correct?");
|
||||
Bukkit.getPluginManager().disablePlugin(plugin);
|
||||
return;
|
||||
}
|
||||
// Check if the table exists in the database and if not, create it
|
||||
createSchema();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the table in the database if it doesn't exist already
|
||||
*/
|
||||
private void createSchema() {
|
||||
String sql = "CREATE TABLE IF NOT EXISTS `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` (json JSON, uniqueId VARCHAR(255) NOT NULL PRIMARY KEY)";
|
||||
// Prepare and execute the database statements
|
||||
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
|
||||
pstmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Problem trying to create schema for data object " + dataObject.getCanonicalName() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> loadObjects() throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, IntrospectionException, NoSuchMethodException {
|
||||
try (Statement preparedStatement = connection.createStatement()) {
|
||||
List<T> list = new ArrayList<>();
|
||||
|
||||
String sb = "SELECT `json` FROM `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"`";
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery(sb)) {
|
||||
// Load all the results
|
||||
Gson gson = getGson();
|
||||
while (resultSet.next()) {
|
||||
String json = resultSet.getString("json");
|
||||
if (json != null) {
|
||||
try {
|
||||
T gsonResult = gson.fromJson(json, dataObject);
|
||||
if (gsonResult != null) {
|
||||
list.add(gsonResult);
|
||||
}
|
||||
} catch (JsonSyntaxException ex) {
|
||||
plugin.logError("Could not load object " + ex.getMessage());
|
||||
plugin.logError(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not load object " + e.getMessage());
|
||||
}
|
||||
return list;
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Could not load objects " + e.getMessage());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T loadObject(@NonNull String uniqueId) throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, IntrospectionException, NoSuchMethodException {
|
||||
String sb = "SELECT `json` FROM `" + dataObject.getCanonicalName() + "` WHERE uniqueId = ? LIMIT 1";
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
||||
// UniqueId needs to be placed in quotes
|
||||
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery()) {
|
||||
if (resultSet.next()) {
|
||||
// If there is a result, we only want/need the first one
|
||||
Gson gson = getGson();
|
||||
return gson.fromJson(resultSet.getString("json"), dataObject);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not load object " + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Could not load object " + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
|
||||
// Null check
|
||||
if (instance == null) {
|
||||
plugin.logError("MySQL database request to store a null. ");
|
||||
return;
|
||||
}
|
||||
if (!(instance instanceof DataObject)) {
|
||||
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
return;
|
||||
}
|
||||
String sb = "INSERT INTO " +
|
||||
"`" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` (json, uniqueId) VALUES (?, ?) ON CONFLICT(uniqueId) DO UPDATE SET json = ?";
|
||||
|
||||
Gson gson = getGson();
|
||||
String toStore = gson.toJson(instance);
|
||||
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
||||
preparedStatement.setString(1, toStore);
|
||||
preparedStatement.setString(2, ((DataObject)instance).getUniqueId());
|
||||
preparedStatement.setString(3, toStore);
|
||||
preparedStatement.execute();
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Could not save object " + instance.getClass().getName() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
|
||||
// Null check
|
||||
if (instance == null) {
|
||||
plugin.logError("SQLite database request to delete a null.");
|
||||
return;
|
||||
}
|
||||
if (!(instance instanceof DataObject)) {
|
||||
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Method getUniqueId = dataObject.getMethod("getUniqueId");
|
||||
deleteID((String) getUniqueId.invoke(instance));
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not delete object " + instance.getClass().getName() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean objectExists(String uniqueId) {
|
||||
// Create the query to see if this key exists
|
||||
String query = "SELECT EXISTS (SELECT 1 FROM `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` WHERE `uniqueId` = ?)";
|
||||
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(query)) {
|
||||
// UniqueId needs to be placed in quotes
|
||||
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery()) {
|
||||
if (resultSet.next()) {
|
||||
return resultSet.getBoolean(1);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.logError("Could not check if key exists in database! " + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
databaseConnector.closeConnection(dataObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteID(String uniqueId) {
|
||||
String sb = "DELETE FROM `" +
|
||||
dataObject.getCanonicalName() +
|
||||
"` WHERE uniqueId = ?";
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
||||
// UniqueId must not be placed in quotes
|
||||
preparedStatement.setString(1, uniqueId);
|
||||
int result = preparedStatement.executeUpdate();
|
||||
if (result != 1) {
|
||||
throw new SQLException("Delete did not affect any rows!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.logError("Could not delete object " + dataObject.getCanonicalName() + " " + uniqueId + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ package world.bentobox.bentobox.database.transition;
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||
import world.bentobox.bentobox.database.mariadb.MariaDBDatabase;
|
||||
import world.bentobox.bentobox.database.sql.mariadb.MariaDBDatabase;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
|
@ -3,7 +3,7 @@ package world.bentobox.bentobox.database.transition;
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||
import world.bentobox.bentobox.database.mysql.MySQLDatabase;
|
||||
import world.bentobox.bentobox.database.sql.mysql.MySQLDatabase;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
|
@ -3,7 +3,7 @@ package world.bentobox.bentobox.database.transition;
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||
import world.bentobox.bentobox.database.postgresql.PostgreSQLDatabase;
|
||||
import world.bentobox.bentobox.database.sql.postgresql.PostgreSQLDatabase;
|
||||
|
||||
/**
|
||||
* @author Poslovitch
|
||||
|
@ -3,7 +3,7 @@ package world.bentobox.bentobox.database.transition;
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||
import world.bentobox.bentobox.database.sqlite.SQLiteDatabase;
|
||||
import world.bentobox.bentobox.database.sql.sqlite.SQLiteDatabase;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -3,7 +3,7 @@ package world.bentobox.bentobox.database.transition;
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||
import world.bentobox.bentobox.database.mariadb.MariaDBDatabase;
|
||||
import world.bentobox.bentobox.database.sql.mariadb.MariaDBDatabase;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -3,7 +3,7 @@ package world.bentobox.bentobox.database.transition;
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||
import world.bentobox.bentobox.database.mysql.MySQLDatabase;
|
||||
import world.bentobox.bentobox.database.sql.mysql.MySQLDatabase;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
|
@ -3,7 +3,7 @@ package world.bentobox.bentobox.database.transition;
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||
import world.bentobox.bentobox.database.postgresql.PostgreSQLDatabase;
|
||||
import world.bentobox.bentobox.database.sql.postgresql.PostgreSQLDatabase;
|
||||
|
||||
/**
|
||||
* @author Poslovitch
|
||||
|
@ -3,7 +3,7 @@ package world.bentobox.bentobox.database.transition;
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||
import world.bentobox.bentobox.database.sqlite.SQLiteDatabase;
|
||||
import world.bentobox.bentobox.database.sql.sqlite.SQLiteDatabase;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,7 @@ package world.bentobox.bentobox.database.transition;
|
||||
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.mariadb.MariaDBDatabase;
|
||||
import world.bentobox.bentobox.database.sql.mariadb.MariaDBDatabase;
|
||||
import world.bentobox.bentobox.database.yaml.YamlDatabase;
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,7 @@ package world.bentobox.bentobox.database.transition;
|
||||
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.mysql.MySQLDatabase;
|
||||
import world.bentobox.bentobox.database.sql.mysql.MySQLDatabase;
|
||||
import world.bentobox.bentobox.database.yaml.YamlDatabase;
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,7 @@ package world.bentobox.bentobox.database.transition;
|
||||
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.sqlite.SQLiteDatabase;
|
||||
import world.bentobox.bentobox.database.sql.sqlite.SQLiteDatabase;
|
||||
import world.bentobox.bentobox.database.yaml.YamlDatabase;
|
||||
|
||||
|
||||
|
@ -20,17 +20,14 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.MemorySection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
@ -59,26 +56,11 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
|
||||
|
||||
private static final String YML = ".yml";
|
||||
|
||||
/**
|
||||
* FIFO queue for saves. Note that the assumption here is that most database objects will be held
|
||||
* in memory because loading is not handled with this queue. That means that it is theoretically
|
||||
* possible to load something before it has been saved. So, in general, load your objects and then
|
||||
* save them async only when you do not need the data again immediately.
|
||||
*/
|
||||
private Queue<Runnable> processQueue;
|
||||
|
||||
/**
|
||||
* Async save task that runs repeatedly
|
||||
*/
|
||||
private BukkitTask asyncSaveTask;
|
||||
|
||||
/**
|
||||
* Flag to indicate if this is a config or a pure object database (difference is in comments and annotations)
|
||||
*/
|
||||
protected boolean configFlag;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param plugin - plugin
|
||||
@ -87,26 +69,6 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
|
||||
*/
|
||||
YamlDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
||||
super(plugin, type, databaseConnector);
|
||||
processQueue = new ConcurrentLinkedQueue<>();
|
||||
if (plugin.isEnabled()) {
|
||||
asyncSaveTask = Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
// Loop continuously
|
||||
while (plugin.isEnabled() || !processQueue.isEmpty()) {
|
||||
while (!processQueue.isEmpty()) {
|
||||
processQueue.poll().run();
|
||||
}
|
||||
// Clear the queue and then sleep
|
||||
try {
|
||||
Thread.sleep(25);
|
||||
} catch (InterruptedException e) {
|
||||
plugin.logError("Thread sleep error " + e.getMessage());
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
// Cancel
|
||||
asyncSaveTask.cancel();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
@ -1,456 +0,0 @@
|
||||
package world.bentobox.bentobox.database.mariadb;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.powermock.api.mockito.PowerMockito;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
import org.powermock.reflect.Whitebox;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.mysql.MySQLDatabaseConnector;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest( { Bukkit.class, BentoBox.class, Util.class })
|
||||
public class MariaDBDatabaseHandlerTest {
|
||||
|
||||
private static final String JSON = "{\n" +
|
||||
" \"deleted\": false,\n" +
|
||||
" \"uniqueId\": \"xyz\",\n" +
|
||||
" \"range\": 0,\n" +
|
||||
" \"protectionRange\": 0,\n" +
|
||||
" \"maxEverProtectionRange\": 0,\n" +
|
||||
" \"createdDate\": 0,\n" +
|
||||
" \"updatedDate\": 0,\n" +
|
||||
" \"members\": {},\n" +
|
||||
" \"spawn\": false,\n" +
|
||||
" \"purgeProtected\": false,\n" +
|
||||
" \"flags\": {},\n" +
|
||||
" \"history\": [],\n" +
|
||||
" \"levelHandicap\": 0,\n" +
|
||||
" \"spawnPoint\": {},\n" +
|
||||
" \"doNotLoad\": false,\n" +
|
||||
" \"cooldowns\": {}\n" +
|
||||
"}";
|
||||
private MariaDBDatabaseHandler<Island> handler;
|
||||
private Island instance;
|
||||
private String UNIQUE_ID = "xyz";
|
||||
@Mock
|
||||
private MySQLDatabaseConnector dbConn;
|
||||
@Mock
|
||||
private BentoBox plugin;
|
||||
@Mock
|
||||
private BukkitScheduler sch;
|
||||
@Mock
|
||||
private PluginManager pluginManager;
|
||||
@Mock
|
||||
private Connection connection;
|
||||
@Mock
|
||||
private PreparedStatement ps;
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
*/
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
// Setup plugin
|
||||
Whitebox.setInternalState(BentoBox.class, "instance", plugin);
|
||||
when(plugin.isEnabled()).thenReturn(true);
|
||||
|
||||
// Bukkit
|
||||
PowerMockito.mockStatic(Bukkit.class);
|
||||
when(Bukkit.getScheduler()).thenReturn(sch);
|
||||
|
||||
// Plugin Manager
|
||||
pluginManager = mock(PluginManager.class);
|
||||
when(Bukkit.getPluginManager()).thenReturn(pluginManager);
|
||||
|
||||
// MySQLDatabaseConnector
|
||||
when(dbConn.createConnection(any())).thenReturn(connection);
|
||||
|
||||
// Queries
|
||||
when(connection.prepareStatement(Mockito.anyString())).thenReturn(ps);
|
||||
when(connection.createStatement()).thenReturn(ps);
|
||||
ResultSet rs = mock(ResultSet.class);
|
||||
when(ps.executeQuery()).thenReturn(rs);
|
||||
when(ps.executeQuery(Mockito.anyString())).thenReturn(rs);
|
||||
|
||||
// Instance to save
|
||||
instance = new Island();
|
||||
instance.setUniqueId(UNIQUE_ID);
|
||||
handler = new MariaDBDatabaseHandler<>(plugin, Island.class, dbConn);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#loadObjects()}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testLoadObjectsNoConnection() throws SQLException {
|
||||
when(connection.createStatement()).thenThrow(new SQLException("no connection"));
|
||||
handler.loadObjects();
|
||||
verify(plugin).logError("Could not load objects no connection");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#loadObjects()}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testLoadObjects() throws SQLException {
|
||||
ResultSet resultSet = mock(ResultSet.class);
|
||||
when(resultSet.getString(any())).thenReturn(JSON);
|
||||
// Three islands
|
||||
when(resultSet.next()).thenReturn(true, true, true, false);
|
||||
when(ps.executeQuery(Mockito.anyString())).thenReturn(resultSet);
|
||||
List<Island> objects = handler.loadObjects();
|
||||
verify(ps).executeQuery("SELECT `json` FROM `world.bentobox.bentobox.database.objects.Island`");
|
||||
assertTrue(objects.size() == 3);
|
||||
assertEquals("xyz", objects.get(2).getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#loadObjects()}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testLoadObjectsBadJSON() throws SQLException {
|
||||
ResultSet resultSet = mock(ResultSet.class);
|
||||
when(resultSet.getString(any())).thenReturn("sfdasfasdfsfd");
|
||||
// Three islands
|
||||
when(resultSet.next()).thenReturn(true, true, true, false);
|
||||
when(ps.executeQuery(Mockito.anyString())).thenReturn(resultSet);
|
||||
List<Island> objects = handler.loadObjects();
|
||||
verify(ps).executeQuery("SELECT `json` FROM `world.bentobox.bentobox.database.objects.Island`");
|
||||
assertTrue(objects.isEmpty());
|
||||
verify(plugin, Mockito.times(3)).logError("Could not load object java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#loadObjects()}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testLoadObjectsError() throws SQLException {
|
||||
ResultSet resultSet = mock(ResultSet.class);
|
||||
when(resultSet.getString(any())).thenThrow(new SQLException("SQL error"));
|
||||
// Three islands
|
||||
when(resultSet.next()).thenReturn(true, true, true, false);
|
||||
when(ps.executeQuery(Mockito.anyString())).thenReturn(resultSet);
|
||||
List<Island> objects = handler.loadObjects();
|
||||
verify(ps).executeQuery("SELECT `json` FROM `world.bentobox.bentobox.database.objects.Island`");
|
||||
assertTrue(objects.isEmpty());
|
||||
verify(plugin).logError("Could not load objects SQL error");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#loadObject(java.lang.String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testLoadObjectNoConnection() throws SQLException {
|
||||
when(connection.prepareStatement(Mockito.anyString())).thenThrow(new SQLException("no connection"));
|
||||
handler.loadObject("abc");
|
||||
verify(plugin).logError("Could not load object abc no connection");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#loadObject(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testLoadObject() throws SQLException {
|
||||
ResultSet resultSet = mock(ResultSet.class);
|
||||
when(resultSet.getString(any())).thenReturn(JSON);
|
||||
when(resultSet.next()).thenReturn(true);
|
||||
when(ps.executeQuery()).thenReturn(resultSet);
|
||||
Island object = handler.loadObject("abc");
|
||||
verify(ps).executeQuery();
|
||||
verify(ps).setString(1, "\"abc\"");
|
||||
verify(resultSet).next();
|
||||
assertNotNull(object);
|
||||
assertEquals("xyz", object.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#loadObject(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testLoadObjectBadJSON() throws SQLException {
|
||||
ResultSet resultSet = mock(ResultSet.class);
|
||||
when(resultSet.getString(any())).thenReturn("afdsaf");
|
||||
when(resultSet.next()).thenReturn(true);
|
||||
when(ps.executeQuery()).thenReturn(resultSet);
|
||||
Island object = handler.loadObject("abc");
|
||||
assertNull(object);
|
||||
verify(plugin).logError("Could not load object abc java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#loadObject(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testLoadObjectError() throws SQLException {
|
||||
ResultSet resultSet = mock(ResultSet.class);
|
||||
when(resultSet.getString(any())).thenReturn(JSON);
|
||||
when(resultSet.next()).thenThrow(new SQLException("SQL Exception"));
|
||||
when(ps.executeQuery()).thenReturn(resultSet);
|
||||
Island object = handler.loadObject("abc");
|
||||
assertNull(object);
|
||||
verify(plugin).logError("Could not load object abc SQL Exception");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#saveObject(java.lang.Object)}.
|
||||
*/
|
||||
@Test
|
||||
public void testSaveObjectNull() {
|
||||
handler.saveObject(null);
|
||||
verify(plugin).logError(eq("MySQL database request to store a null. "));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#saveObject(java.lang.Object)}.
|
||||
*/
|
||||
@Test
|
||||
public void testSaveObjectNotDataObject() {
|
||||
@SuppressWarnings("rawtypes")
|
||||
MariaDBDatabaseHandler<List> h = new MariaDBDatabaseHandler<List>(plugin, List.class, dbConn);
|
||||
h.saveObject(Collections.singletonList("test"));
|
||||
verify(plugin).logError(eq("This class is not a DataObject: java.util.Collections$SingletonList"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#saveObject(java.lang.Object)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testSaveObject() throws SQLException {
|
||||
// Disable plugin
|
||||
when(plugin.isEnabled()).thenReturn(false);
|
||||
handler.saveObject(instance);
|
||||
verify(ps).execute();
|
||||
verify(ps).setString(1, JSON);
|
||||
verify(ps).setString(2, "{\n" +
|
||||
" \"deleted\": false,\n" +
|
||||
" \"uniqueId\": \"xyz\",\n" +
|
||||
" \"range\": 0,\n" +
|
||||
" \"protectionRange\": 0,\n" +
|
||||
" \"maxEverProtectionRange\": 0,\n" +
|
||||
" \"createdDate\": 0,\n" +
|
||||
" \"updatedDate\": 0,\n" +
|
||||
" \"members\": {},\n" +
|
||||
" \"spawn\": false,\n" +
|
||||
" \"purgeProtected\": false,\n" +
|
||||
" \"flags\": {},\n" +
|
||||
" \"history\": [],\n" +
|
||||
" \"levelHandicap\": 0,\n" +
|
||||
" \"spawnPoint\": {},\n" +
|
||||
" \"doNotLoad\": false,\n" +
|
||||
" \"cooldowns\": {}\n" +
|
||||
"}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#saveObject(java.lang.Object)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testSaveObjectFail() throws SQLException {
|
||||
// Disable plugin
|
||||
when(plugin.isEnabled()).thenReturn(false);
|
||||
when(ps.execute()).thenThrow(new SQLException("fail!"));
|
||||
handler.saveObject(instance);
|
||||
verify(plugin).logError(eq("Could not save object world.bentobox.bentobox.database.objects.Island fail!"));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#deleteObject(java.lang.Object)}.
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteObjectNull() {
|
||||
handler.deleteObject(null);
|
||||
verify(plugin).logError(eq("MariaDB database request to delete a null."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#deleteObject(java.lang.Object)}.
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteObjectIncorrectType() {
|
||||
@SuppressWarnings("rawtypes")
|
||||
MariaDBDatabaseHandler<List> h = new MariaDBDatabaseHandler<List>(plugin, List.class, dbConn);
|
||||
h.deleteObject(Collections.singletonList("test"));
|
||||
verify(plugin).logError(eq("This class is not a DataObject: java.util.Collections$SingletonList"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#deleteObject(java.lang.Object)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteObject() throws SQLException {
|
||||
// Disable plugin
|
||||
when(plugin.isEnabled()).thenReturn(false);
|
||||
handler.deleteObject(instance);
|
||||
verify(ps).execute();
|
||||
verify(ps).setString(1, "\"xyz\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#objectExists(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testObjectExistsNot() throws SQLException {
|
||||
ResultSet resultSet = mock(ResultSet.class);
|
||||
when(ps.executeQuery()).thenReturn(resultSet);
|
||||
when(resultSet.next()).thenReturn(false);
|
||||
assertFalse(handler.objectExists("hello"));
|
||||
//verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )");
|
||||
verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (JSON_EXTRACT(json, \"$.uniqueId\")), UNIQUE INDEX i (uniqueId))");
|
||||
verify(ps).executeQuery();
|
||||
verify(ps).setString(1, "\"hello\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#objectExists(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testObjectExistsFalse() throws SQLException {
|
||||
ResultSet resultSet = mock(ResultSet.class);
|
||||
when(ps.executeQuery()).thenReturn(resultSet);
|
||||
when(resultSet.next()).thenReturn(true);
|
||||
when(resultSet.getBoolean(eq(1))).thenReturn(false);
|
||||
assertFalse(handler.objectExists("hello"));
|
||||
//verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )");
|
||||
verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (JSON_EXTRACT(json, \"$.uniqueId\")), UNIQUE INDEX i (uniqueId))");
|
||||
verify(ps).executeQuery();
|
||||
verify(ps).setString(1, "\"hello\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#objectExists(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testObjectExists() throws SQLException {
|
||||
ResultSet resultSet = mock(ResultSet.class);
|
||||
when(ps.executeQuery()).thenReturn(resultSet);
|
||||
when(resultSet.next()).thenReturn(true);
|
||||
when(resultSet.getBoolean(eq(1))).thenReturn(true);
|
||||
assertTrue(handler.objectExists("hello"));
|
||||
//verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )");
|
||||
verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (JSON_EXTRACT(json, \"$.uniqueId\")), UNIQUE INDEX i (uniqueId))");
|
||||
verify(ps).executeQuery();
|
||||
verify(ps).setString(1, "\"hello\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#objectExists(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testObjectExistsError() throws SQLException {
|
||||
ResultSet resultSet = mock(ResultSet.class);
|
||||
when(ps.executeQuery()).thenReturn(resultSet);
|
||||
when(resultSet.next()).thenThrow(new SQLException("error"));
|
||||
handler.objectExists("hello");
|
||||
verify(plugin).logError(eq("Could not check if key exists in database! hello error"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#deleteID(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteID() throws SQLException {
|
||||
// Disable plugin
|
||||
when(plugin.isEnabled()).thenReturn(false);
|
||||
handler.deleteID("abc123");
|
||||
verify(ps).execute();
|
||||
verify(ps).setString(1, "\"abc123\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#deleteID(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteIDError() throws SQLException {
|
||||
// Disable plugin
|
||||
when(plugin.isEnabled()).thenReturn(false);
|
||||
when(ps.execute()).thenThrow(new SQLException("fail!"));
|
||||
handler.deleteID("abc123");
|
||||
verify(plugin).logError(eq("Could not delete object world.bentobox.bentobox.database.objects.Island abc123 fail!"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#MariaDBDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}.
|
||||
*/
|
||||
@Test
|
||||
public void testMariaDBDatabaseHandlerBadPassword() {
|
||||
when(dbConn.createConnection(any())).thenReturn(null);
|
||||
new MariaDBDatabaseHandler<>(plugin, Island.class, dbConn);
|
||||
verify(plugin).logError("Are the settings in config.yml correct?");
|
||||
verify(pluginManager).disablePlugin(plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#MariaDBDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testMariaDBDatabaseHandlerCreateSchema() throws SQLException {
|
||||
verify(dbConn).createConnection(any());
|
||||
verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `world.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (JSON_EXTRACT(json, \"$.uniqueId\")), UNIQUE INDEX i (uniqueId))");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mariadb.MariaDBDatabaseHandler#MariaDBDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void testMariaDBDatabaseHandlerSchemaFail() throws SQLException {
|
||||
when(ps.executeUpdate()).thenThrow(new SQLException("oh no!"));
|
||||
handler = new MariaDBDatabaseHandler<>(plugin, Island.class, dbConn);
|
||||
verify(plugin).logError("Problem trying to create schema for data object world.bentobox.bentobox.database.objects.Island oh no!");
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package world.bentobox.bentobox.database.mysql;
|
||||
package world.bentobox.bentobox.database.sql.mysql;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@ -25,6 +25,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
import world.bentobox.bentobox.database.DatabaseConnectionSettingsImpl;
|
||||
import world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseConnector;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
@ -72,7 +73,7 @@ public class MySQLDatabaseConnectorTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseConnector#MySQLDatabaseConnector(world.bentobox.bentobox.database.DatabaseConnectionSettingsImpl)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseConnector#MySQLDatabaseConnector(world.bentobox.bentobox.database.DatabaseConnectionSettingsImpl)}.
|
||||
*/
|
||||
@Test
|
||||
public void testMySQLDatabaseConnector() {
|
||||
@ -83,7 +84,7 @@ public class MySQLDatabaseConnectorTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseConnector#createConnection()}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseConnector#createConnection()}.
|
||||
*/
|
||||
@Ignore("This is apparently very hard to do!")
|
||||
@Test
|
||||
@ -93,7 +94,7 @@ public class MySQLDatabaseConnectorTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseConnector#createConnection()}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseConnector#createConnection()}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@ -106,7 +107,7 @@ public class MySQLDatabaseConnectorTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseConnector#getConnectionUrl()}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseConnector#getConnectionUrl()}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetConnectionUrl() {
|
||||
@ -116,7 +117,7 @@ public class MySQLDatabaseConnectorTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseConnector#getUniqueId(java.lang.String)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseConnector#getUniqueId(java.lang.String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetUniqueId() {
|
||||
@ -125,7 +126,7 @@ public class MySQLDatabaseConnectorTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseConnector#uniqueIdExists(java.lang.String, java.lang.String)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseConnector#uniqueIdExists(java.lang.String, java.lang.String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testUniqueIdExists() {
|
||||
@ -133,7 +134,7 @@ public class MySQLDatabaseConnectorTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseConnector#closeConnection()}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseConnector#closeConnection()}.
|
||||
*/
|
||||
@Test
|
||||
public void testCloseConnection() {
|
||||
@ -143,7 +144,7 @@ public class MySQLDatabaseConnectorTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseConnector#closeConnection()}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseConnector#closeConnection()}.
|
||||
*/
|
||||
@Test
|
||||
public void testCloseConnectionError() throws SQLException {
|
@ -1,4 +1,4 @@
|
||||
package world.bentobox.bentobox.database.mysql;
|
||||
package world.bentobox.bentobox.database.sql.mysql;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@ -22,6 +22,7 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
@ -33,6 +34,8 @@ import org.powermock.reflect.Whitebox;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseConnector;
|
||||
import world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
/**
|
||||
@ -112,7 +115,7 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#loadObjects()}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#loadObjects()}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@ -123,7 +126,7 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#loadObjects()}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#loadObjects()}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@ -140,7 +143,7 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#loadObjects()}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#loadObjects()}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@ -157,7 +160,7 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#loadObjects()}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#loadObjects()}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@ -175,7 +178,7 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#loadObject(java.lang.String)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#loadObject(java.lang.String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testLoadObjectNoConnection() throws SQLException {
|
||||
@ -185,7 +188,7 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#loadObject(java.lang.String)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#loadObject(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@ -203,7 +206,7 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#loadObject(java.lang.String)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#loadObject(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@ -218,7 +221,7 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#loadObject(java.lang.String)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#loadObject(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@ -233,16 +236,16 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#saveObject(java.lang.Object)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#saveObject(java.lang.Object)}.
|
||||
*/
|
||||
@Test
|
||||
public void testSaveObjectNull() {
|
||||
handler.saveObject(null);
|
||||
verify(plugin).logError(eq("MySQL database request to store a null. "));
|
||||
verify(plugin).logError(eq("SQL database request to store a null. "));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#saveObject(java.lang.Object)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#saveObject(java.lang.Object)}.
|
||||
*/
|
||||
@Test
|
||||
public void testSaveObjectNotDataObject() {
|
||||
@ -253,10 +256,11 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#saveObject(java.lang.Object)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#saveObject(java.lang.Object)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@Ignore("Async cannot be tested")
|
||||
public void testSaveObject() throws SQLException {
|
||||
// Disable plugin
|
||||
when(plugin.isEnabled()).thenReturn(false);
|
||||
@ -284,10 +288,11 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#saveObject(java.lang.Object)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#saveObject(java.lang.Object)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@Ignore("Async cannot be tested")
|
||||
public void testSaveObjectFail() throws SQLException {
|
||||
// Disable plugin
|
||||
when(plugin.isEnabled()).thenReturn(false);
|
||||
@ -298,16 +303,16 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#deleteObject(java.lang.Object)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#deleteObject(java.lang.Object)}.
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteObjectNull() {
|
||||
handler.deleteObject(null);
|
||||
verify(plugin).logError(eq("MySQL database request to delete a null."));
|
||||
verify(plugin).logError(eq("SQL database request to delete a null."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#deleteObject(java.lang.Object)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#deleteObject(java.lang.Object)}.
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteObjectIncorrectType() {
|
||||
@ -318,10 +323,11 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#deleteObject(java.lang.Object)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#deleteObject(java.lang.Object)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@Ignore("Async cannot be tested")
|
||||
public void testDeleteObject() throws SQLException {
|
||||
// Disable plugin
|
||||
when(plugin.isEnabled()).thenReturn(false);
|
||||
@ -331,7 +337,7 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#objectExists(java.lang.String)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#objectExists(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@ -346,7 +352,7 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#objectExists(java.lang.String)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#objectExists(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@ -362,7 +368,7 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#objectExists(java.lang.String)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#objectExists(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@ -378,7 +384,7 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#objectExists(java.lang.String)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#objectExists(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@ -391,10 +397,11 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#deleteID(java.lang.String)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#deleteID(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@Ignore("Cannot test async")
|
||||
public void testDeleteID() throws SQLException {
|
||||
// Disable plugin
|
||||
when(plugin.isEnabled()).thenReturn(false);
|
||||
@ -404,10 +411,11 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#deleteID(java.lang.String)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#deleteID(java.lang.String)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@Ignore("Cannot test async")
|
||||
public void testDeleteIDError() throws SQLException {
|
||||
// Disable plugin
|
||||
when(plugin.isEnabled()).thenReturn(false);
|
||||
@ -417,7 +425,7 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#MySQLDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#MySQLDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}.
|
||||
*/
|
||||
@Test
|
||||
public void testMySQLDatabaseHandlerBadPassword() {
|
||||
@ -428,7 +436,7 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#MySQLDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#MySQLDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
@ -438,7 +446,7 @@ public class MySQLDatabaseHandlerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.bentobox.database.mysql.MySQLDatabaseHandler#MySQLDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}.
|
||||
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#MySQLDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}.
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
Loading…
Reference in New Issue
Block a user