Add ability for DataObjects to specify custom db table name (#1348)

Automated migration for existing databases is supported.
This commit is contained in:
tastybento 2020-05-13 01:49:42 -07:00 committed by GitHub
parent de124ab182
commit feab01cde8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 227 additions and 84 deletions

View File

@ -97,9 +97,10 @@ public class Settings implements ConfigObject {
@ConfigEntry(path = "general.database.use-ssl", since = "1.12.0")
private boolean useSSL = false;
@ConfigComment("Database table prefix character. Adds a prefix to the database tables. Not used by flatfile databases.")
@ConfigComment("Database table prefix. Adds a prefix to the database tables. Not used by flatfile databases.")
@ConfigComment("Only the characters A-Z, a-z, 0-9 can be used. Invalid characters will become an underscore.")
@ConfigComment("Set this to a unique value if you are running multiple BentoBox instances that share a database.")
@ConfigComment("Be careful about length - databases usually have a limit of 63 characters for table lengths")
@ConfigEntry(path = "general.database.prefix-character", since = "1.13.0")
private String databasePrefix = "";
@ -238,16 +239,16 @@ public class Settings implements ConfigObject {
private boolean autoOwnershipTransferIgnoreRanks = false;
// Island deletion related settings
@ConfigComment("Toggles whether islands, when players are resetting them, should be kept in the world or deleted.")
@ConfigComment("* If set to 'true', whenever a player resets his island, his previous island will become unowned and won't be deleted from the world.")
@ConfigComment(" You can, however, still delete those unowned islands through purging.")
@ConfigComment(" On bigger servers, this can lead to an increasing world size.")
@ConfigComment(" Yet, this allows admins to retrieve a player's old island in case of an improper use of the reset command.")
@ConfigComment(" Admins can indeed re-add the player to his old island by registering him to it.")
@ConfigComment("* If set to 'false', whenever a player resets his island, his previous island will be deleted from the world.")
@ConfigComment(" This is the default behaviour.")
@ConfigEntry(path = "island.deletion.keep-previous-island-on-reset", since = "1.13.0")
private boolean keepPreviousIslandOnReset = false;
@ConfigComment("Toggles whether islands, when players are resetting them, should be kept in the world or deleted.")
@ConfigComment("* If set to 'true', whenever a player resets his island, his previous island will become unowned and won't be deleted from the world.")
@ConfigComment(" You can, however, still delete those unowned islands through purging.")
@ConfigComment(" On bigger servers, this can lead to an increasing world size.")
@ConfigComment(" Yet, this allows admins to retrieve a player's old island in case of an improper use of the reset command.")
@ConfigComment(" Admins can indeed re-add the player to his old island by registering him to it.")
@ConfigComment("* If set to 'false', whenever a player resets his island, his previous island will be deleted from the world.")
@ConfigComment(" This is the default behaviour.")
@ConfigEntry(path = "island.deletion.keep-previous-island-on-reset", since = "1.13.0")
private boolean keepPreviousIslandOnReset = false;
/* WEB */
@ConfigComment("Toggle whether BentoBox can connect to GitHub to get data about updates and addons.")
@ -639,7 +640,7 @@ public class Settings implements ConfigObject {
*/
public String getDatabasePrefix() {
if (databasePrefix == null) databasePrefix = "";
return databasePrefix.isEmpty() ? "" : databasePrefix.replaceAll("[^a-zA-Z0-9]", "_").substring(0,1);
return databasePrefix.isEmpty() ? "" : databasePrefix.replaceAll("[^a-zA-Z0-9]", "_");
}
/**
@ -649,23 +650,23 @@ public class Settings implements ConfigObject {
this.databasePrefix = databasePrefix;
}
/**
* Returns whether islands, when reset, should be kept or deleted.
* @return {@code true} if islands, when reset, should be kept; {@code false} otherwise.
* @since 1.13.0
*/
public boolean isKeepPreviousIslandOnReset() {
return keepPreviousIslandOnReset;
}
/**
* Returns whether islands, when reset, should be kept or deleted.
* @return {@code true} if islands, when reset, should be kept; {@code false} otherwise.
* @since 1.13.0
*/
public boolean isKeepPreviousIslandOnReset() {
return keepPreviousIslandOnReset;
}
/**
* Sets whether islands, when reset, should be kept or deleted.
* @param keepPreviousIslandOnReset {@code true} if islands, when reset, should be kept; {@code false} otherwise.
* @since 1.13.0
*/
public void setKeepPreviousIslandOnReset(boolean keepPreviousIslandOnReset) {
this.keepPreviousIslandOnReset = keepPreviousIslandOnReset;
}
/**
* Sets whether islands, when reset, should be kept or deleted.
* @param keepPreviousIslandOnReset {@code true} if islands, when reset, should be kept; {@code false} otherwise.
* @since 1.13.0
*/
public void setKeepPreviousIslandOnReset(boolean keepPreviousIslandOnReset) {
this.keepPreviousIslandOnReset = keepPreviousIslandOnReset;
}
/**
* Returns a MongoDB client connection URI to override default connection options.

View File

@ -10,18 +10,21 @@ import org.bukkit.Bukkit;
import com.google.gson.Gson;
import com.mongodb.MongoClientException;
import com.mongodb.MongoNamespace;
import com.mongodb.MongoTimeoutException;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.FindOneAndReplaceOptions;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.Indexes;
import com.mongodb.client.model.RenameCollectionOptions;
import com.mongodb.util.JSON;
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;
import world.bentobox.bentobox.database.objects.Table;
/**
*
@ -59,7 +62,15 @@ public class MongoDBDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
plugin.logError("Could not connect to the database. Are the credentials in the config.yml file correct?");
connected = false;
} else {
collection = database.getCollection(plugin.getSettings().getDatabasePrefix() + dataObject.getCanonicalName());
// Check for old collections
String oldName = plugin.getSettings().getDatabasePrefix() + type.getCanonicalName();
String newName = getName(plugin, dataObject);
if (!oldName.equals((newName)) && collectionExists(database, oldName) && !collectionExists(database, newName)){
collection = database.getCollection(oldName);
collection.renameCollection(new MongoNamespace(database.getName(), newName));
} else {
collection = database.getCollection(newName);
}
IndexOptions indexOptions = new IndexOptions().unique(true);
collection.createIndex(Indexes.text(UNIQUEID), indexOptions);
}
@ -80,6 +91,23 @@ public class MongoDBDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
}
}
private boolean collectionExists(MongoDatabase database, final String collectionName) {
for (final String name : database.listCollectionNames()) {
if (name.equalsIgnoreCase(collectionName)) {
return true;
}
}
return false;
}
private String getName(BentoBox plugin, Class<T> type) {
return plugin.getSettings().getDatabasePrefix() +
(type.getAnnotation(Table.class) == null ?
type.getCanonicalName()
: type.getAnnotation(Table.class)
.name());
}
@Override
public List<T> loadObjects() {
List<T> list = new ArrayList<>();
@ -147,7 +175,7 @@ public class MongoDBDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
try {
collection.findOneAndDelete(new Document(MONGO_ID, uniqueId));
} catch (Exception e) {
plugin.logError("Could not delete object " + plugin.getSettings().getDatabasePrefix() + dataObject.getCanonicalName() + " " + uniqueId + " " + e.getMessage());
plugin.logError("Could not delete object " + getName(plugin, dataObject) + " " + uniqueId + " " + e.getMessage());
}
}

View File

@ -53,6 +53,7 @@ import world.bentobox.bentobox.util.Util;
* @author tastybento
* @author Poslovitch
*/
@Table(name = "Islands")
public class Island implements DataObject {
// True if this island is deleted and pending deletion from the database

View File

@ -16,6 +16,7 @@ import world.bentobox.bentobox.BentoBox;
* @author tastybento
* @since 1.1
*/
@Table(name = "IslandDeletion")
public class IslandDeletion implements DataObject {
@Expose

View File

@ -9,20 +9,21 @@ import com.google.gson.annotations.Expose;
* @author tastybento
*
*/
@Table(name = "Names")
public class Names implements DataObject {
@Expose
private String uniqueId = ""; // name
@Expose
private UUID uuid;
public Names() {}
public Names(String name, UUID uuid) {
this.uniqueId = name;
this.uuid = uuid;
}
@Override
public String getUniqueId() {
return uniqueId;
@ -30,7 +31,7 @@ public class Names implements DataObject {
@Override
public void setUniqueId(String uniqueId) {
this.uniqueId = uniqueId;
this.uniqueId = uniqueId;
}
/**

View File

@ -24,6 +24,7 @@ import world.bentobox.bentobox.util.Util;
*
* @author tastybento
*/
@Table(name = "Players")
public class Players implements DataObject {
@Expose
private Map<Location, Integer> homeLocations = new HashMap<>();

View File

@ -0,0 +1,22 @@
package world.bentobox.bentobox.database.objects;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target(TYPE)
/**
* Annotation to explicitly name tables
* @author tastybento
* @since 1.14.0
*/
public @interface Table {
/**
* @return name of the table to be used in the database
*/
String name();
}

View File

@ -1,5 +1,8 @@
package world.bentobox.bentobox.database.sql;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.database.objects.Table;
/**
* Contains the SQL strings for the database.
* The default strings are for MySQL, so only the deltas need to be supplied.
@ -13,47 +16,68 @@ public class SQLConfiguration {
private String objectExistsSQL;
private String schemaSQL;
private String loadObjectsSQL;
private String renameTableSQL;
private final String tableName;
private boolean renameRequired;
private final String oldTableName;
/**
* @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 <T> SQLConfiguration(BentoBox plugin, Class<T> type) {
// Set the table name
oldTableName = plugin.getSettings().getDatabasePrefix() + type.getCanonicalName();
this.tableName = plugin.getSettings().getDatabasePrefix() +
(type.getAnnotation(Table.class) == null ?
type.getCanonicalName()
: type.getAnnotation(Table.class).name());
// Only rename if there is a specific Table annotation
renameRequired = !tableName.equals(oldTableName);
schema("CREATE TABLE IF NOT EXISTS `[tableName]` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )");
loadObjects("SELECT `json` FROM `[tableName]`");
loadObject("SELECT `json` FROM `[tableName]` WHERE uniqueId = ? LIMIT 1");
saveObject("INSERT INTO `[tableName]` (json) VALUES (?) ON DUPLICATE KEY UPDATE json = ?");
deleteObject("DELETE FROM `[tableName]` WHERE uniqueId = ?");
objectExists("SELECT IF ( EXISTS( SELECT * FROM `[tableName]` WHERE `uniqueId` = ?), 1, 0)");
renameTable("SELECT Count(*) INTO @exists " +
"FROM information_schema.tables " +
"WHERE table_schema = '" + plugin.getSettings().getDatabaseName() + "' " +
"AND table_type = 'BASE TABLE' " +
"AND table_name = '[oldTableName]'; " +
"SET @query = If(@exists=1,'RENAME TABLE `[oldTableName]` TO `[tableName]`','SELECT \\'nothing to rename\\' status'); " +
"PREPARE stmt FROM @query;" +
"EXECUTE stmt;");
}
public SQLConfiguration loadObject(String string) {
this.loadObjectSQL = string;
this.loadObjectSQL = string.replaceFirst("\\[tableName\\]", tableName);
return this;
}
public SQLConfiguration saveObject(String string) {
this.saveObjectSQL = string;
this.saveObjectSQL = string.replaceFirst("\\[tableName\\]", tableName);
return this;
}
public SQLConfiguration deleteObject(String string) {
this.deleteObjectSQL = string;
this.deleteObjectSQL = string.replaceFirst("\\[tableName\\]", tableName);
return this;
}
public SQLConfiguration objectExists(String string) {
this.objectExistsSQL = string;
this.objectExistsSQL = string.replaceFirst("\\[tableName\\]", tableName);
return this;
}
public SQLConfiguration schema(String string) {
this.schemaSQL = string;
this.schemaSQL = string.replaceFirst("\\[tableName\\]", tableName);
return this;
}
public SQLConfiguration loadObjects(String string) {
this.loadObjectsSQL = string;
this.loadObjectsSQL = string.replaceFirst("\\[tableName\\]", tableName);
return this;
}
public SQLConfiguration renameTable(String string) {
this.renameTableSQL = string.replaceAll("\\[tableName\\]", tableName).replaceAll("\\[oldTableName\\]", oldTableName);
return this;
}
@ -94,4 +118,29 @@ public class SQLConfiguration {
return loadObjectsSQL;
}
/**
* @return the renameTableSQL
*/
public String getRenameTableSQL() {
return renameTableSQL;
}
/**
* @return the tableName
*/
public String getTableName() {
return tableName;
}
/**
* @return the oldName
*/
public String getOldTableName() {
return oldTableName;
}
public boolean renameRequired() {
return renameRequired;
}
}

View File

@ -81,11 +81,19 @@ public class SQLDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
* Creates the table in the database if it doesn't exist already
*/
protected void createSchema() {
if (sqlConfig.renameRequired()) {
// Transition from the old table name
try (PreparedStatement pstmt = connection.prepareStatement(sqlConfig.getRenameTableSQL())) {
pstmt.execute();
} catch (SQLException e) {
plugin.logError("Could not rename " + sqlConfig.getOldTableName() + " for data object " + dataObject.getCanonicalName() + " " + e.getMessage());
}
}
// Prepare and execute the database statements
try (PreparedStatement pstmt = connection.prepareStatement(sqlConfig.getSchemaSQL())) {
pstmt.executeUpdate();
pstmt.execute();
} catch (SQLException e) {
plugin.logError("Problem trying to create schema for data object " + plugin.getSettings().getDatabasePrefix() + dataObject.getCanonicalName() + " " + e.getMessage());
plugin.logError("Problem trying to create schema for data object " + dataObject.getCanonicalName() + " " + e.getMessage());
}
}

View File

@ -23,8 +23,8 @@ public class MariaDBDatabaseHandler<T> extends SQLDatabaseHandler<T> {
* @param databaseConnector - authentication details for the database
*/
MariaDBDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
super(plugin, type, databaseConnector, new SQLConfiguration(plugin.getSettings().getDatabasePrefix() + type.getCanonicalName())
.schema("CREATE TABLE IF NOT EXISTS `" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() +
"` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (JSON_EXTRACT(json, \"$.uniqueId\")), UNIQUE INDEX i (uniqueId))"));
super(plugin, type, databaseConnector,
new SQLConfiguration(plugin, type)
.schema("CREATE TABLE IF NOT EXISTS `[tableName]` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (JSON_EXTRACT(json, \"$.uniqueId\")), UNIQUE INDEX i (uniqueId))"));
}
}

View File

@ -23,8 +23,7 @@ public class MySQLDatabaseHandler<T> extends SQLDatabaseHandler<T> {
* @param dbConnecter - authentication details for the database
*/
MySQLDatabaseHandler(BentoBox plugin, Class<T> type, DatabaseConnector dbConnecter) {
super(plugin, type, dbConnecter, new SQLConfiguration(plugin.getSettings().getDatabasePrefix() + type.getCanonicalName())
.schema("CREATE TABLE IF NOT EXISTS `" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() +
"` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) ) ENGINE = INNODB"));
super(plugin, type, dbConnecter, new SQLConfiguration(plugin, type)
.schema("CREATE TABLE IF NOT EXISTS `[tableName]` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) ) ENGINE = INNODB"));
}
}

View File

@ -30,21 +30,22 @@ public class PostgreSQLDatabaseHandler<T> extends SQLDatabaseHandler<T> {
* @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(plugin.getSettings().getDatabasePrefix() + type.getCanonicalName())
super(plugin, type, databaseConnector, new SQLConfiguration(plugin, type)
// Set uniqueid as the primary key (index). Postgresql convention is to use lower case field names
// Postgresql also uses double quotes (") instead of (`) around tables names with dots.
.schema("CREATE TABLE IF NOT EXISTS \"" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() + "\" (uniqueid VARCHAR PRIMARY KEY, json jsonb NOT NULL)")
.loadObject("SELECT * FROM \"" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() + "\" WHERE uniqueid = ? LIMIT 1")
.deleteObject("DELETE FROM \"" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() + "\" WHERE uniqueid = ?")
.schema("CREATE TABLE IF NOT EXISTS \"[tableName]\" (uniqueid VARCHAR PRIMARY KEY, json jsonb NOT NULL)")
.loadObject("SELECT * FROM \"[tableName]\" WHERE uniqueid = ? LIMIT 1")
.deleteObject("DELETE FROM \"[tableName]\" WHERE uniqueid = ?")
// uniqueId has to be added into the row explicitly so we need to override the saveObject method
// The json value is a string but has to be cast to json when done in Java
.saveObject("INSERT INTO \"" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() + "\" (uniqueid, json) VALUES (?, cast(? as json)) "
.saveObject("INSERT INTO \"[tableName]\" (uniqueid, json) VALUES (?, cast(? as json)) "
// This is the Postgresql version of UPSERT.
+ "ON CONFLICT (uniqueid) "
+ "DO UPDATE SET json = cast(? as json)")
.loadObjects("SELECT json FROM \"" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() + "\"")
.loadObjects("SELECT json FROM \"[tableName]\"")
// Postgres exists function returns true or false natively
.objectExists("SELECT EXISTS(SELECT * FROM \"" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() + "\" WHERE uniqueid = ?)")
.objectExists("SELECT EXISTS(SELECT * FROM \"[tableName]\" WHERE uniqueid = ?)")
.renameTable("ALTER TABLE IF EXISTS \"[oldTableName]\" RENAME TO \"[tableName]\"")
);
}

View File

@ -32,11 +32,42 @@ public class SQLiteDatabaseHandler<T> extends SQLDatabaseHandler<T> {
* @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(plugin.getSettings().getDatabasePrefix() + type.getCanonicalName())
.schema("CREATE TABLE IF NOT EXISTS `" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() + "` (json JSON, uniqueId VARCHAR(255) NOT NULL PRIMARY KEY)")
.saveObject("INSERT INTO `" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName()
+ "` (json, uniqueId) VALUES (?, ?) ON CONFLICT(uniqueId) DO UPDATE SET json = ?")
.objectExists("SELECT EXISTS (SELECT 1 FROM `" + plugin.getSettings().getDatabasePrefix() + type.getCanonicalName() + "` WHERE `uniqueId` = ?)"));
super(plugin, type, databaseConnector, new SQLConfiguration(plugin, type)
.schema("CREATE TABLE IF NOT EXISTS `[tableName]` (json JSON, uniqueId VARCHAR(255) NOT NULL PRIMARY KEY)")
.saveObject("INSERT INTO `[tableName]` (json, uniqueId) VALUES (?, ?) ON CONFLICT(uniqueId) DO UPDATE SET json = ?")
.objectExists("SELECT EXISTS (SELECT 1 FROM `[tableName]` WHERE `uniqueId` = ?)")
.renameTable("ALTER TABLE `[oldTableName]` RENAME TO `[tableName]`"));
}
@Override
/**
* Creates the table in the database if it doesn't exist already
*/
protected void createSchema() {
if (getSqlConfig().renameRequired()) {
// SQLite does not have a rename if exists command so we have to manually check if the old table exists
String sql = "SELECT EXISTS (SELECT 1 FROM sqlite_master WHERE type='table' AND name='" + getSqlConfig().getOldTableName() + "' COLLATE NOCASE)";
try (PreparedStatement pstmt = getConnection().prepareStatement(sql)) {
ResultSet resultSet = pstmt.executeQuery();
if (resultSet.next() && resultSet.getBoolean(1)) {
// Transition from the old table name
try (PreparedStatement pstmt2 = getConnection().prepareStatement(getSqlConfig().getRenameTableSQL())) {
pstmt2.execute();
} catch (SQLException e) {
plugin.logError("Could not rename " + getSqlConfig().getOldTableName() + " for data object " + dataObject.getCanonicalName() + " " + e.getMessage());
}
}
} catch (SQLException e) {
plugin.logError("Could not check if " + getSqlConfig().getOldTableName() + " exists for data object " + dataObject.getCanonicalName() + " " + e.getMessage());
}
}
// Prepare and execute the database statements
try (PreparedStatement pstmt = getConnection().prepareStatement(getSqlConfig().getSchemaSQL())) {
pstmt.execute();
} catch (SQLException e) {
plugin.logError("Problem trying to create schema for data object " + dataObject.getCanonicalName() + " " + e.getMessage());
}
}
@Override

View File

@ -147,7 +147,7 @@ public class MySQLDatabaseHandlerTest {
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`");
verify(ps).executeQuery("SELECT `json` FROM `Islands`");
assertTrue(objects.size() == 3);
assertEquals("xyz", objects.get(2).getUniqueId());
}
@ -166,7 +166,7 @@ public class MySQLDatabaseHandlerTest {
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 `aworld.bentobox.bentobox.database.objects.Island`");
verify(ps).executeQuery("SELECT `json` FROM `aIslands`");
assertTrue(objects.size() == 3);
assertEquals("xyz", objects.get(2).getUniqueId());
}
@ -183,7 +183,7 @@ public class MySQLDatabaseHandlerTest {
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`");
verify(ps).executeQuery("SELECT `json` FROM `Islands`");
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 $");
}
@ -200,7 +200,7 @@ public class MySQLDatabaseHandlerTest {
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`");
verify(ps).executeQuery("SELECT `json` FROM `Islands`");
assertTrue(objects.isEmpty());
verify(plugin).logError("Could not load objects SQL error");
@ -327,7 +327,7 @@ public class MySQLDatabaseHandlerTest {
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!"));
verify(plugin).logError(eq("Could not save object Islands fail!"));
}
@ -375,7 +375,7 @@ public class MySQLDatabaseHandlerTest {
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) ) ENGINE = INNODB");
verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `Islands` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) ) ENGINE = INNODB");
verify(ps).executeQuery();
verify(ps).setString(1, "\"hello\"");
}
@ -391,7 +391,7 @@ public class MySQLDatabaseHandlerTest {
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) ) ENGINE = INNODB");
verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `Islands` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) ) ENGINE = INNODB");
verify(ps).executeQuery();
verify(ps).setString(1, "\"hello\"");
}
@ -407,7 +407,7 @@ public class MySQLDatabaseHandlerTest {
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) ) ENGINE = INNODB");
verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `Islands` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) ) ENGINE = INNODB");
verify(ps).executeQuery();
verify(ps).setString(1, "\"hello\"");
}
@ -425,7 +425,7 @@ public class MySQLDatabaseHandlerTest {
when(resultSet.next()).thenReturn(true);
when(resultSet.getBoolean(eq(1))).thenReturn(true);
assertTrue(handler.objectExists("hello"));
verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `aworld.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) ) ENGINE = INNODB");
verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `aIslands` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) ) ENGINE = INNODB");
verify(ps).executeQuery();
verify(ps).setString(1, "\"hello\"");
}
@ -468,7 +468,7 @@ public class MySQLDatabaseHandlerTest {
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!"));
verify(plugin).logError(eq("Could not delete object Islands abc123 fail!"));
}
/**
@ -489,7 +489,7 @@ public class MySQLDatabaseHandlerTest {
@Test
public void testMySQLDatabaseHandlerCreateSchema() 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->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) ) ENGINE = INNODB");
verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `Islands` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) ) ENGINE = INNODB");
}
/**
@ -501,7 +501,7 @@ public class MySQLDatabaseHandlerTest {
public void testMySQLDatabaseHandlerCreateSchemaPrefix() throws SQLException {
when(settings.getDatabasePrefix()).thenReturn("a");
verify(dbConn).createConnection(any());
verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `aworld.bentobox.bentobox.database.objects.Island` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) ) ENGINE = INNODB");
verify(connection).prepareStatement("CREATE TABLE IF NOT EXISTS `aIslands` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) ) ENGINE = INNODB");
}
/**
* Test method for {@link world.bentobox.bentobox.database.sql.mysql.MySQLDatabaseHandler#MySQLDatabaseHandler(world.bentobox.bentobox.BentoBox, java.lang.Class, world.bentobox.bentobox.database.DatabaseConnector)}.
@ -509,7 +509,7 @@ public class MySQLDatabaseHandlerTest {
*/
@Test
public void testMySQLDatabaseHandlerSchemaFail() throws SQLException {
when(ps.executeUpdate()).thenThrow(new SQLException("oh no!"));
when(ps.execute()).thenThrow(new SQLException("oh no!"));
handler = new MySQLDatabaseHandler<>(plugin, Island.class, dbConn);
verify(plugin).logError("Problem trying to create schema for data object world.bentobox.bentobox.database.objects.Island oh no!");