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.beans.IntrospectionException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.List;
|
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.NonNull;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
@ -19,6 +23,21 @@ import world.bentobox.bentobox.api.addons.Addon;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractDatabaseHandler<T> {
|
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
|
* Name of the folder where databases using files will live
|
||||||
*/
|
*/
|
||||||
@ -75,6 +94,33 @@ public abstract class AbstractDatabaseHandler<T> {
|
|||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.databaseConnector = databaseConnector;
|
this.databaseConnector = databaseConnector;
|
||||||
this.dataObject = type;
|
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() {}
|
protected AbstractDatabaseHandler() {}
|
||||||
|
@ -4,11 +4,11 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
import world.bentobox.bentobox.BentoBox;
|
import world.bentobox.bentobox.BentoBox;
|
||||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
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.mongodb.MongoDBDatabase;
|
||||||
import world.bentobox.bentobox.database.mysql.MySQLDatabase;
|
import world.bentobox.bentobox.database.sql.mariadb.MariaDBDatabase;
|
||||||
import world.bentobox.bentobox.database.postgresql.PostgreSQLDatabase;
|
import world.bentobox.bentobox.database.sql.mysql.MySQLDatabase;
|
||||||
import world.bentobox.bentobox.database.sqlite.SQLiteDatabase;
|
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.Json2MariaDBDatabase;
|
||||||
import world.bentobox.bentobox.database.transition.Json2MongoDBDatabase;
|
import world.bentobox.bentobox.database.transition.Json2MongoDBDatabase;
|
||||||
import world.bentobox.bentobox.database.transition.Json2MySQLDatabase;
|
import world.bentobox.bentobox.database.transition.Json2MySQLDatabase;
|
||||||
|
@ -41,6 +41,7 @@ public abstract class AbstractJSONDatabaseHandler<T> extends AbstractDatabaseHan
|
|||||||
builder.disableHtmlEscaping();
|
builder.disableHtmlEscaping();
|
||||||
|
|
||||||
gson = builder.create();
|
gson = builder.create();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Gson getGson() {
|
protected Gson getGson() {
|
||||||
|
@ -15,11 +15,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
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 org.eclipse.jdt.annotation.NonNull;
|
||||||
|
|
||||||
import world.bentobox.bentobox.BentoBox;
|
import world.bentobox.bentobox.BentoBox;
|
||||||
@ -30,21 +26,6 @@ public class JSONDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
|||||||
|
|
||||||
private static final String JSON = ".json";
|
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
|
* Constructor
|
||||||
*
|
*
|
||||||
@ -55,30 +36,6 @@ public class JSONDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
|||||||
*/
|
*/
|
||||||
JSONDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
JSONDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
||||||
super(plugin, type, 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
|
@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.Connection;
|
||||||
import java.sql.DriverManager;
|
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.DatabaseConnectionSettingsImpl;
|
||||||
import world.bentobox.bentobox.database.DatabaseConnector;
|
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 DatabaseConnectionSettingsImpl dbSettings;
|
||||||
private Connection connection = null;
|
protected static Connection connection = null;
|
||||||
private Set<Class<?>> types = new HashSet<>();
|
protected static Set<Class<?>> types = new HashSet<>();
|
||||||
|
|
||||||
/**
|
public SQLDatabaseConnector(DatabaseConnectionSettingsImpl dbSettings, String connectionUrl) {
|
||||||
* Class for MySQL database connections using the settings provided
|
|
||||||
* @param dbSettings - database settings
|
|
||||||
*/
|
|
||||||
MySQLDatabaseConnector(DatabaseConnectionSettingsImpl dbSettings) {
|
|
||||||
this.dbSettings = dbSettings;
|
this.dbSettings = dbSettings;
|
||||||
connectionUrl = "jdbc:mysql://" + dbSettings.getHost() + ":" + dbSettings.getPort() + "/" + dbSettings.getDatabaseName()
|
this.connectionUrl = connectionUrl;
|
||||||
+ "?autoReconnect=true&useSSL=false&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -55,7 +50,7 @@ public class MySQLDatabaseConnector implements DatabaseConnector {
|
|||||||
connection.close();
|
connection.close();
|
||||||
Bukkit.getLogger().info("Closed database connection");
|
Bukkit.getLogger().info("Closed database connection");
|
||||||
} catch (SQLException e) {
|
} 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;
|
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.lang.reflect.Method;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
@ -9,11 +9,8 @@ import java.sql.Statement;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
|
||||||
import org.eclipse.jdt.annotation.NonNull;
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
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.
|
* Class that inserts a <T> into the corresponding database-table.
|
||||||
*
|
*
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
*
|
*
|
||||||
* @param <T>
|
* @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_OBJECTS = "Could not load objects ";
|
||||||
private static final String COULD_NOT_LOAD_OBJECT = "Could not load object ";
|
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;
|
private Connection connection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FIFO queue for saves or deletions. Note that the assumption here is that most database objects will be held
|
* SQL configuration
|
||||||
* 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;
|
private SQLConfiguration sqlConfig;
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
* 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 plugin - plugin object
|
||||||
* @param type - the type of class to be stored in the database. Must inherit DataObject
|
* @param type - the type of class to be stored in the database. Must inherit DataObject
|
||||||
* @param dbConnecter - authentication details for the database
|
* @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);
|
super(plugin, type, dbConnecter);
|
||||||
connection = (Connection)dbConnecter.createConnection(dataObject);
|
this.sqlConfig = sqlConfiguration;
|
||||||
if (connection == null) {
|
if (setConnection((Connection)databaseConnector.createConnection(type))) {
|
||||||
plugin.logError("Are the settings in config.yml correct?");
|
// Check if the table exists in the database and if not, create it
|
||||||
Bukkit.getPluginManager().disablePlugin(plugin);
|
createSchema();
|
||||||
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()) {
|
|
||||||
// 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
|
* Creates the table in the database if it doesn't exist already
|
||||||
*/
|
*/
|
||||||
private void createSchema() {
|
protected 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
|
// Prepare and execute the database statements
|
||||||
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
|
try (PreparedStatement pstmt = connection.prepareStatement(sqlConfig.getSchemaSQL())) {
|
||||||
pstmt.executeUpdate();
|
pstmt.executeUpdate();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
plugin.logError("Problem trying to create schema for data object " + dataObject.getCanonicalName() + " " + e.getMessage());
|
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) {
|
private List<T> loadIt(Statement preparedStatement) {
|
||||||
List<T> list = new ArrayList<>();
|
List<T> list = new ArrayList<>();
|
||||||
|
try (ResultSet resultSet = preparedStatement.executeQuery(sqlConfig.getLoadObjectsSQL())) {
|
||||||
String sb = "SELECT `json` FROM `" +
|
|
||||||
dataObject.getCanonicalName() +
|
|
||||||
"`";
|
|
||||||
try (ResultSet resultSet = preparedStatement.executeQuery(sb)) {
|
|
||||||
// Load all the results
|
// Load all the results
|
||||||
Gson gson = getGson();
|
Gson gson = getGson();
|
||||||
while (resultSet.next()) {
|
while (resultSet.next()) {
|
||||||
@ -158,8 +125,7 @@ public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T loadObject(@NonNull String uniqueId) {
|
public T loadObject(@NonNull String uniqueId) {
|
||||||
String sb = "SELECT `json` FROM `" + dataObject.getCanonicalName() + "` WHERE uniqueId = ? LIMIT 1";
|
try (PreparedStatement preparedStatement = connection.prepareStatement(sqlConfig.getLoadObjectSQL())) {
|
||||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
|
||||||
// UniqueId needs to be placed in quotes
|
// UniqueId needs to be placed in quotes
|
||||||
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
||||||
try (ResultSet resultSet = preparedStatement.executeQuery()) {
|
try (ResultSet resultSet = preparedStatement.executeQuery()) {
|
||||||
@ -181,30 +147,19 @@ public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
|||||||
public void saveObject(T instance) {
|
public void saveObject(T instance) {
|
||||||
// Null check
|
// Null check
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
plugin.logError("MySQL database request to store a null. ");
|
plugin.logError("SQL database request to store a null. ");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(instance instanceof DataObject)) {
|
if (!(instance instanceof DataObject)) {
|
||||||
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
plugin.logError("This class is not a DataObject: " + instance.getClass().getName());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String sb = "INSERT INTO " +
|
// Async
|
||||||
"`" +
|
processQueue.add(() -> store(instance, sqlConfig.getSaveObjectSQL()));
|
||||||
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) {
|
private void store(T instance, String sb) {
|
||||||
|
String toStore = getGson().toJson(instance);
|
||||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
||||||
preparedStatement.setString(1, toStore);
|
preparedStatement.setString(1, toStore);
|
||||||
preparedStatement.setString(2, toStore);
|
preparedStatement.setString(2, toStore);
|
||||||
@ -219,18 +174,11 @@ public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void deleteID(String uniqueId) {
|
public void deleteID(String uniqueId) {
|
||||||
if (plugin.isEnabled()) {
|
processQueue.add(() -> delete(uniqueId));
|
||||||
processQueue.add(() -> delete(uniqueId));
|
|
||||||
} else {
|
|
||||||
delete(uniqueId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void delete(String uniqueId) {
|
private void delete(String uniqueId) {
|
||||||
String sb = "DELETE FROM `" +
|
try (PreparedStatement preparedStatement = connection.prepareStatement(sqlConfig.getDeleteObjectSQL())) {
|
||||||
dataObject.getCanonicalName() +
|
|
||||||
"` WHERE uniqueId = ?";
|
|
||||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sb)) {
|
|
||||||
// UniqueId needs to be placed in quotes
|
// UniqueId needs to be placed in quotes
|
||||||
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
||||||
preparedStatement.execute();
|
preparedStatement.execute();
|
||||||
@ -243,7 +191,7 @@ public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
|||||||
public void deleteObject(T instance) {
|
public void deleteObject(T instance) {
|
||||||
// Null check
|
// Null check
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
plugin.logError("MySQL database request to delete a null.");
|
plugin.logError("SQL database request to delete a null.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(instance instanceof DataObject)) {
|
if (!(instance instanceof DataObject)) {
|
||||||
@ -260,12 +208,8 @@ public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean objectExists(String uniqueId) {
|
public boolean objectExists(String uniqueId) {
|
||||||
// Create the query to see if this key exists
|
// Query to see if this key exists
|
||||||
String query = "SELECT IF ( EXISTS( SELECT * FROM `" +
|
try (PreparedStatement preparedStatement = connection.prepareStatement(sqlConfig.getObjectExistsSQL())) {
|
||||||
dataObject.getCanonicalName() +
|
|
||||||
"` WHERE `uniqueId` = ?), 1, 0)";
|
|
||||||
|
|
||||||
try (PreparedStatement preparedStatement = connection.prepareStatement(query)) {
|
|
||||||
// UniqueId needs to be placed in quotes
|
// UniqueId needs to be placed in quotes
|
||||||
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
preparedStatement.setString(1, "\"" + uniqueId + "\"");
|
||||||
try (ResultSet resultSet = preparedStatement.executeQuery()) {
|
try (ResultSet resultSet = preparedStatement.executeQuery()) {
|
||||||
@ -283,4 +227,25 @@ public class MySQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
|||||||
public void close() {
|
public void close() {
|
||||||
shutdown = true;
|
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.BentoBox;
|
||||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
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
|
* @since 1.1
|
||||||
* @author barpec12
|
* @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.BentoBox;
|
||||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
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.BentoBox;
|
||||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
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.
|
* Contains PostgreSQL database managers.
|
||||||
* @since 1.6.0
|
* @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.BentoBox;
|
||||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
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.
|
* Contains SQLite database managers.
|
||||||
* @since 1.6.0
|
* @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.AbstractDatabaseHandler;
|
||||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||||
import world.bentobox.bentobox.database.mariadb.MariaDBDatabase;
|
import world.bentobox.bentobox.database.sql.mariadb.MariaDBDatabase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
|
@ -3,7 +3,7 @@ package world.bentobox.bentobox.database.transition;
|
|||||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||||
import world.bentobox.bentobox.database.mysql.MySQLDatabase;
|
import world.bentobox.bentobox.database.sql.mysql.MySQLDatabase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
|
@ -3,7 +3,7 @@ package world.bentobox.bentobox.database.transition;
|
|||||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||||
import world.bentobox.bentobox.database.postgresql.PostgreSQLDatabase;
|
import world.bentobox.bentobox.database.sql.postgresql.PostgreSQLDatabase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Poslovitch
|
* @author Poslovitch
|
||||||
|
@ -3,7 +3,7 @@ package world.bentobox.bentobox.database.transition;
|
|||||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
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.AbstractDatabaseHandler;
|
||||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
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.AbstractDatabaseHandler;
|
||||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||||
import world.bentobox.bentobox.database.mysql.MySQLDatabase;
|
import world.bentobox.bentobox.database.sql.mysql.MySQLDatabase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tastybento
|
* @author tastybento
|
||||||
|
@ -3,7 +3,7 @@ package world.bentobox.bentobox.database.transition;
|
|||||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||||
import world.bentobox.bentobox.database.postgresql.PostgreSQLDatabase;
|
import world.bentobox.bentobox.database.sql.postgresql.PostgreSQLDatabase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Poslovitch
|
* @author Poslovitch
|
||||||
|
@ -3,7 +3,7 @@ package world.bentobox.bentobox.database.transition;
|
|||||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
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.AbstractDatabaseHandler;
|
||||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
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;
|
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.AbstractDatabaseHandler;
|
||||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
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;
|
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.AbstractDatabaseHandler;
|
||||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
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;
|
import world.bentobox.bentobox.database.yaml.YamlDatabase;
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,17 +20,14 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.configuration.MemorySection;
|
import org.bukkit.configuration.MemorySection;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
|
||||||
import org.eclipse.jdt.annotation.NonNull;
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
@ -59,26 +56,11 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
|
|||||||
|
|
||||||
private static final String YML = ".yml";
|
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)
|
* Flag to indicate if this is a config or a pure object database (difference is in comments and annotations)
|
||||||
*/
|
*/
|
||||||
protected boolean configFlag;
|
protected boolean configFlag;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param plugin - plugin
|
* @param plugin - plugin
|
||||||
@ -87,26 +69,6 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
|
|||||||
*/
|
*/
|
||||||
YamlDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
YamlDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
||||||
super(plugin, type, 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)
|
/* (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.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
@ -25,6 +25,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest;
|
|||||||
import org.powermock.modules.junit4.PowerMockRunner;
|
import org.powermock.modules.junit4.PowerMockRunner;
|
||||||
|
|
||||||
import world.bentobox.bentobox.database.DatabaseConnectionSettingsImpl;
|
import world.bentobox.bentobox.database.DatabaseConnectionSettingsImpl;
|
||||||
|
import world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseConnector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tastybento
|
* @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
|
@Test
|
||||||
public void testMySQLDatabaseConnector() {
|
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!")
|
@Ignore("This is apparently very hard to do!")
|
||||||
@Test
|
@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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@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
|
@Test
|
||||||
public void testGetConnectionUrl() {
|
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
|
@Test
|
||||||
public void testGetUniqueId() {
|
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
|
@Test
|
||||||
public void testUniqueIdExists() {
|
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
|
@Test
|
||||||
public void testCloseConnection() {
|
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
|
@Test
|
||||||
public void testCloseConnectionError() throws SQLException {
|
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.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
@ -22,6 +22,7 @@ import org.bukkit.Bukkit;
|
|||||||
import org.bukkit.plugin.PluginManager;
|
import org.bukkit.plugin.PluginManager;
|
||||||
import org.bukkit.scheduler.BukkitScheduler;
|
import org.bukkit.scheduler.BukkitScheduler;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
@ -33,6 +34,8 @@ import org.powermock.reflect.Whitebox;
|
|||||||
|
|
||||||
import world.bentobox.bentobox.BentoBox;
|
import world.bentobox.bentobox.BentoBox;
|
||||||
import world.bentobox.bentobox.database.objects.Island;
|
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;
|
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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@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
|
@Test
|
||||||
public void testLoadObjectNoConnection() throws SQLException {
|
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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@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
|
@Test
|
||||||
public void testSaveObjectNull() {
|
public void testSaveObjectNull() {
|
||||||
handler.saveObject(null);
|
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
|
@Test
|
||||||
public void testSaveObjectNotDataObject() {
|
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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
|
@Ignore("Async cannot be tested")
|
||||||
public void testSaveObject() throws SQLException {
|
public void testSaveObject() throws SQLException {
|
||||||
// Disable plugin
|
// Disable plugin
|
||||||
when(plugin.isEnabled()).thenReturn(false);
|
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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
|
@Ignore("Async cannot be tested")
|
||||||
public void testSaveObjectFail() throws SQLException {
|
public void testSaveObjectFail() throws SQLException {
|
||||||
// Disable plugin
|
// Disable plugin
|
||||||
when(plugin.isEnabled()).thenReturn(false);
|
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
|
@Test
|
||||||
public void testDeleteObjectNull() {
|
public void testDeleteObjectNull() {
|
||||||
handler.deleteObject(null);
|
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
|
@Test
|
||||||
public void testDeleteObjectIncorrectType() {
|
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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
|
@Ignore("Async cannot be tested")
|
||||||
public void testDeleteObject() throws SQLException {
|
public void testDeleteObject() throws SQLException {
|
||||||
// Disable plugin
|
// Disable plugin
|
||||||
when(plugin.isEnabled()).thenReturn(false);
|
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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
|
@Ignore("Cannot test async")
|
||||||
public void testDeleteID() throws SQLException {
|
public void testDeleteID() throws SQLException {
|
||||||
// Disable plugin
|
// Disable plugin
|
||||||
when(plugin.isEnabled()).thenReturn(false);
|
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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
|
@Ignore("Cannot test async")
|
||||||
public void testDeleteIDError() throws SQLException {
|
public void testDeleteIDError() throws SQLException {
|
||||||
// Disable plugin
|
// Disable plugin
|
||||||
when(plugin.isEnabled()).thenReturn(false);
|
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
|
@Test
|
||||||
public void testMySQLDatabaseHandlerBadPassword() {
|
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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@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
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
Loading…
Reference in New Issue
Block a user