mirror of
https://github.com/BentoBoxWorld/BentoBox.git
synced 2024-11-27 13:15:28 +01:00
Database transition (#662)
- JSON is now the default database type - JSON database files are now pretty-printed - It is now possible to migrate from a database type to another through the use of a command and specific transition database types - It is recommended to move from YAML to JSON. = Commits breakdown = * Proposal to make JSON the default database and retire YAML. * Make JSON file format easier to read. * Fix tests. * Adds a hybrid Yaml2Json database type. This database always tries to use JSON if it is available. If a YAML file is found, it will be loaded and replaced with a JSON file. * Move to generic database transition code * Better comments * Adds transitional database options so admins can choose. Adds Yaml2MySQL option and changes config.yml to add instructions. * Enables full database migration between databases. Adds /bbox migrate command. Adds a number of transition databases. DB starts transition when the server boots up and will migrate organically. The admin can force an immediate update using the bbox migrate command. This operation requires an API breaking change: Addons that use the Config API must now implement ConfigObject in their config class instead of DataObject. This is to differentiate YAML config classes from YAML database classes. If a class is already implements WorldSettings (GameModeAddons), then no change is required because WorldSettings implements ConfigObject now. If an old addon is used that does not implement ConfigObject, BentoBox will not load. * Added null check to YAML deletion * Removed the 2YAML transition dbs because YAML is deprecated. YAML does not support some data structures so conversion could corrupt data. * Fixed some javadoc and added missing DatabaseType#JSON2MARIADB * Renamed package database/transitiondb to database/transition
This commit is contained in:
parent
0f4baad703
commit
180b88544a
@ -2,9 +2,9 @@ package world.bentobox.bentobox;
|
||||
|
||||
import world.bentobox.bentobox.api.configuration.ConfigComment;
|
||||
import world.bentobox.bentobox.api.configuration.ConfigEntry;
|
||||
import world.bentobox.bentobox.api.configuration.ConfigObject;
|
||||
import world.bentobox.bentobox.api.configuration.StoreAt;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType;
|
||||
import world.bentobox.bentobox.database.objects.DataObject;
|
||||
import world.bentobox.bentobox.managers.RanksManager;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -21,7 +21,7 @@ import java.util.Set;
|
||||
@ConfigComment("This config file is dynamic and is updated right after BentoBox loaded its settings from it.")
|
||||
@ConfigComment("You can edit it while the server is online and you can do '/bbox reload' to take the changes into account.")
|
||||
@ConfigComment("However, it is a better practice to edit this file while the server is offline.")
|
||||
public class Settings implements DataObject {
|
||||
public class Settings implements ConfigObject {
|
||||
|
||||
// ---------------------------------------------
|
||||
|
||||
@ -38,13 +38,18 @@ public class Settings implements DataObject {
|
||||
private boolean useEconomy = true;
|
||||
|
||||
// Database
|
||||
@ConfigComment("YAML, JSON, MYSQL, MARIADB (10.2.3+), MONGODB.")
|
||||
@ConfigComment("YAML and JSON are both file-based databases.")
|
||||
@ConfigComment("JSON, MYSQL, MARIADB (10.2.3+), MONGODB, and YAML(deprecated).")
|
||||
@ConfigComment("Transition database options are:")
|
||||
@ConfigComment(" YAML2JSON, YAML2MARIADB, YAML2MYSQL")
|
||||
@ConfigComment(" JSON2MARIADB, JSON2MYSQL, MYSQL2JSON")
|
||||
@ConfigComment("If you need others, please make a feature request.")
|
||||
@ConfigComment("Transition options enable migration from one database type to another. Use /bbox migrate.")
|
||||
@ConfigComment("YAML and JSON are file-based databases.")
|
||||
@ConfigComment("MYSQL might not work with all implementations: if available, use a dedicated database type (e.g. MARIADB).")
|
||||
@ConfigComment("If you use MONGODB, you must also run the BSBMongo plugin (not addon).")
|
||||
@ConfigComment("See https://github.com/tastybento/bsbMongo/releases/.")
|
||||
@ConfigEntry(path = "general.database.type", needsReset = true)
|
||||
private DatabaseType databaseType = DatabaseType.YAML;
|
||||
@ConfigEntry(path = "general.database.type")
|
||||
private DatabaseType databaseType = DatabaseType.JSON;
|
||||
|
||||
@ConfigEntry(path = "general.database.host")
|
||||
private String databaseHost = "localhost";
|
||||
@ -190,10 +195,6 @@ public class Settings implements DataObject {
|
||||
@ConfigEntry(path = "web.updater.check-updates.addons", since = "1.3.0", hidden = true)
|
||||
private boolean checkAddonsUpdates = true;
|
||||
|
||||
//---------------------------------------------------------------------------------------/
|
||||
@ConfigComment("These settings should not be edited")
|
||||
private String uniqueId = "config";
|
||||
|
||||
//---------------------------------------------------------------------------------------/
|
||||
// Getters and setters
|
||||
|
||||
@ -435,22 +436,6 @@ public class Settings implements DataObject {
|
||||
this.autoOwnershipTransferIgnoreRanks = autoOwnershipTransferIgnoreRanks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the uniqueId
|
||||
*/
|
||||
@Override
|
||||
public String getUniqueId() {
|
||||
return uniqueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uniqueId the uniqueId to set
|
||||
*/
|
||||
@Override
|
||||
public void setUniqueId(String uniqueId) {
|
||||
this.uniqueId = uniqueId;
|
||||
}
|
||||
|
||||
public boolean isLogCleanSuperFlatChunks() {
|
||||
return logCleanSuperFlatChunks;
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
package world.bentobox.bentobox.api.configuration;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.objects.DataObject;
|
||||
|
||||
/**
|
||||
* Config object for YAML objects
|
||||
* @author tastybento
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public interface ConfigObject extends DataObject {
|
||||
|
||||
@Override
|
||||
default BentoBox getPlugin() {
|
||||
return BentoBox.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the uniqueId
|
||||
*/
|
||||
@Override
|
||||
default String getUniqueId() {
|
||||
return "config";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uniqueId - unique ID the uniqueId to set
|
||||
*/
|
||||
@Override
|
||||
default void setUniqueId(String uniqueId) {}
|
||||
}
|
@ -16,7 +16,7 @@ import world.bentobox.bentobox.api.flags.Flag;
|
||||
* Depending on your implementation, you may need to add setters.
|
||||
* @author tastybento
|
||||
*/
|
||||
public interface WorldSettings {
|
||||
public interface WorldSettings extends ConfigObject {
|
||||
|
||||
/**
|
||||
* Get the default game mode for this game world, e.g. SURVIVAL
|
||||
|
@ -23,6 +23,10 @@ public class BentoBoxCommand extends CompositeCommand {
|
||||
new BentoBoxCatalogCommand(this);
|
||||
new BentoBoxReloadCommand(this);
|
||||
new BentoBoxLocaleCommand(this);
|
||||
// Database names with a 2 in them are migration databases
|
||||
if (getPlugin().getSettings().getDatabaseType().name().contains("2")) {
|
||||
new BentoBoxMigrateCommand(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,55 @@
|
||||
package world.bentobox.bentobox.commands;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.Database;
|
||||
import world.bentobox.bentobox.database.objects.Names;
|
||||
import world.bentobox.bentobox.database.objects.Players;
|
||||
|
||||
/**
|
||||
* Forces migration from one database to another
|
||||
*
|
||||
* @author tastybento
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class BentoBoxMigrateCommand extends ConfirmableCommand {
|
||||
|
||||
/**
|
||||
* Reloads settings, addons and localization command
|
||||
* @param parent command parent
|
||||
*/
|
||||
public BentoBoxMigrateCommand(CompositeCommand parent) {
|
||||
super(parent, "migrate");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
setPermission("admin.migrate");
|
||||
setDescription("commands.bentobox.migrate.description");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
this.askConfirmation(user, () -> {
|
||||
// Migrate BentoBox data
|
||||
user.sendMessage("commands.bentobox.migrate.players");
|
||||
new Database<>(getPlugin(), Players.class).loadObjects();
|
||||
user.sendMessage("commands.bentobox.migrate.migrated");
|
||||
user.sendMessage("commands.bentobox.migrate.names");
|
||||
new Database<>(getPlugin(), Names.class).loadObjects();
|
||||
user.sendMessage("commands.bentobox.migrate.migrated");
|
||||
// Migrate addons data
|
||||
user.sendMessage("commands.bentobox.migrate.addons");
|
||||
getPlugin().getAddonsManager().getDataObjects().forEach(t -> {
|
||||
user.sendMessage("commands.bentobox.migrate.class", TextVariables.DESCRIPTION, t.getCanonicalName());
|
||||
new Database<>(getPlugin(), t).loadObjects();
|
||||
user.sendMessage("commands.bentobox.migrate.migrated");
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
@ -77,6 +77,8 @@ public abstract class AbstractDatabaseHandler<T> {
|
||||
this.dataObject = type;
|
||||
}
|
||||
|
||||
protected AbstractDatabaseHandler() {}
|
||||
|
||||
/**
|
||||
* Loads all the records in this table and returns a list of them
|
||||
* @return list of <T>
|
||||
|
@ -5,10 +5,18 @@ import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||
import world.bentobox.bentobox.database.mariadb.MariaDBDatabase;
|
||||
import world.bentobox.bentobox.database.mongodb.MongoDBDatabase;
|
||||
import world.bentobox.bentobox.database.mysql.MySQLDatabase;
|
||||
import world.bentobox.bentobox.database.transition.Json2MariaDBDatabase;
|
||||
import world.bentobox.bentobox.database.transition.Json2MySQLDatabase;
|
||||
import world.bentobox.bentobox.database.transition.MySQL2JsonDatabase;
|
||||
import world.bentobox.bentobox.database.transition.Yaml2JsonDatabase;
|
||||
import world.bentobox.bentobox.database.transition.Yaml2MariaDBDatabase;
|
||||
import world.bentobox.bentobox.database.transition.Yaml2MySQLDatabase;
|
||||
import world.bentobox.bentobox.database.yaml.YamlDatabase;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author Poslovitch
|
||||
* @author Poslovitch, tastybento
|
||||
*/
|
||||
public interface DatabaseSetup {
|
||||
|
||||
@ -20,23 +28,60 @@ public interface DatabaseSetup {
|
||||
*/
|
||||
static DatabaseSetup getDatabase() {
|
||||
BentoBox plugin = BentoBox.getInstance();
|
||||
for(DatabaseType type : DatabaseType.values()){
|
||||
if(type == plugin.getSettings().getDatabaseType()) {
|
||||
return type.database;
|
||||
}
|
||||
}
|
||||
return DatabaseType.YAML.database;
|
||||
return Arrays.stream(DatabaseType.values())
|
||||
.filter(plugin.getSettings().getDatabaseType()::equals)
|
||||
.findFirst()
|
||||
.map(t -> t.database)
|
||||
.orElse(DatabaseType.JSON.database);
|
||||
}
|
||||
|
||||
/**
|
||||
* Database types
|
||||
*
|
||||
*/
|
||||
enum DatabaseType {
|
||||
YAML(new YamlDatabase()),
|
||||
/**
|
||||
* Transition database, from YAML to JSON
|
||||
* @since 1.5.0
|
||||
*/
|
||||
YAML2JSON(new Yaml2JsonDatabase()),
|
||||
/**
|
||||
* Transition database, from YAML to MySQL
|
||||
* @since 1.5.0
|
||||
*/
|
||||
YAML2MYSQL(new Yaml2MySQLDatabase()),
|
||||
/**
|
||||
* Transition database, from YAML to MySQL (MariaDB)
|
||||
* @since 1.5.0
|
||||
*/
|
||||
YAML2MARIADB(new Yaml2MariaDBDatabase()),
|
||||
|
||||
JSON(new JSONDatabase()),
|
||||
/**
|
||||
* Transition database, from JSON to MySQL
|
||||
* @since 1.5.0
|
||||
*/
|
||||
JSON2MYSQL(new Json2MySQLDatabase()),
|
||||
/**
|
||||
* Transition database, from JSON to MySQL (MariaDB)
|
||||
* @since 1.5.0
|
||||
*/
|
||||
JSON2MARIADB(new Json2MariaDBDatabase()),
|
||||
|
||||
MYSQL(new MySQLDatabase()),
|
||||
/**
|
||||
* Transition database, from MySQL to JSON
|
||||
* @since 1.5.0
|
||||
*/
|
||||
MYSQL2JSON(new MySQL2JsonDatabase()),
|
||||
/**
|
||||
* @since 1.1
|
||||
*/
|
||||
MARIADB(new MariaDBDatabase()),
|
||||
|
||||
MONGODB(new MongoDBDatabase());
|
||||
|
||||
DatabaseSetup database;
|
||||
|
||||
DatabaseType(DatabaseSetup database){
|
||||
|
@ -45,7 +45,7 @@ public abstract class AbstractJSONDatabaseHandler<T> extends AbstractDatabaseHan
|
||||
|
||||
// excludeFieldsWithoutExposeAnnotation - this means that every field to be stored should use @Expose
|
||||
// enableComplexMapKeySerialization - forces GSON to use TypeAdapters even for Map keys
|
||||
GsonBuilder builder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().enableComplexMapKeySerialization();
|
||||
GsonBuilder builder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().enableComplexMapKeySerialization().setPrettyPrinting();
|
||||
// Register adapters
|
||||
builder.registerTypeAdapter(Location.class, new LocationAdapter()) ;
|
||||
builder.registerTypeAdapter(World.class, new WorldAdapter());
|
||||
|
@ -139,7 +139,7 @@ public class JSONDatabaseHandler<T> extends AbstractJSONDatabaseHandler<T> {
|
||||
// Obtain the file and delete it
|
||||
File file = new File(tableFolder, uniqueId);
|
||||
try {
|
||||
Files.delete(file.toPath());
|
||||
Files.deleteIfExists(file.toPath());
|
||||
} catch (IOException e) {
|
||||
plugin.logError("Could not delete json database object! " + file.getName() + " - " + e.getMessage());
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
package world.bentobox.bentobox.database.transition;
|
||||
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||
import world.bentobox.bentobox.database.mariadb.MariaDBDatabase;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class Json2MariaDBDatabase implements DatabaseSetup {
|
||||
|
||||
@Override
|
||||
public <T> AbstractDatabaseHandler<T> getHandler(Class<T> type) {
|
||||
return new TransitionDatabaseHandler<>(type, new JSONDatabase().getHandler(type), new MariaDBDatabase().getHandler(type));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package world.bentobox.bentobox.database.transition;
|
||||
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||
import world.bentobox.bentobox.database.mysql.MySQLDatabase;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class Json2MySQLDatabase implements DatabaseSetup {
|
||||
|
||||
@Override
|
||||
public <T> AbstractDatabaseHandler<T> getHandler(Class<T> type) {
|
||||
return new TransitionDatabaseHandler<>(type, new JSONDatabase().getHandler(type), new MySQLDatabase().getHandler(type));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package world.bentobox.bentobox.database.transition;
|
||||
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||
import world.bentobox.bentobox.database.mysql.MySQLDatabase;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class MySQL2JsonDatabase implements DatabaseSetup {
|
||||
|
||||
@Override
|
||||
public <T> AbstractDatabaseHandler<T> getHandler(Class<T> type) {
|
||||
return new TransitionDatabaseHandler<>(type, new MySQLDatabase().getHandler(type), new JSONDatabase().getHandler(type));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
package world.bentobox.bentobox.database.transition;
|
||||
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
|
||||
import java.beans.IntrospectionException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class that transitions from one database type to another
|
||||
*
|
||||
* @author tastybento
|
||||
*
|
||||
* @param <T> Class <T> that is to be handled
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class TransitionDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
|
||||
|
||||
private AbstractDatabaseHandler<T> fromHandler;
|
||||
private AbstractDatabaseHandler<T> toHandler;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param type - class to store in the database
|
||||
* @param fromHandler - the database being moved away from
|
||||
* @param toHandler - the database being moved to
|
||||
*/
|
||||
TransitionDatabaseHandler(Class<T> type, AbstractDatabaseHandler<T> fromHandler, AbstractDatabaseHandler<T> toHandler) {
|
||||
this.fromHandler = fromHandler;
|
||||
this.toHandler = toHandler;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see world.bentobox.bentobox.database.AbstractDatabaseHandler#loadObjects()
|
||||
*/
|
||||
@Override
|
||||
public List<T> loadObjects() throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, IntrospectionException, NoSuchMethodException {
|
||||
// Load all objects from both databases
|
||||
List<T> listFrom = fromHandler.loadObjects();
|
||||
List<T> listTo = toHandler.loadObjects();
|
||||
// If source database has objects, then delete and save them in the destination database
|
||||
for (T object : listFrom) {
|
||||
toHandler.saveObject(object);
|
||||
fromHandler.deleteObject(object);
|
||||
}
|
||||
// Merge results
|
||||
listTo.addAll(listFrom);
|
||||
return listTo;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see world.bentobox.bentobox.database.AbstractDatabaseHandler#loadObject(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public T loadObject(String uniqueId) throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, IntrospectionException, NoSuchMethodException {
|
||||
// Try destination database
|
||||
@Nullable
|
||||
T object = toHandler.loadObject(uniqueId);
|
||||
if (object == null) {
|
||||
// Try source database
|
||||
object = fromHandler.loadObject(uniqueId);
|
||||
if (object != null) {
|
||||
// Save the object in the new database and delete it from the old one
|
||||
toHandler.saveObject(object);
|
||||
fromHandler.deleteObject(object);
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see world.bentobox.bentobox.database.AbstractDatabaseHandler#objectExists(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public boolean objectExists(String uniqueId) {
|
||||
// True if this object is in either database
|
||||
return toHandler.objectExists(uniqueId) || fromHandler.objectExists(uniqueId);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see world.bentobox.bentobox.database.AbstractDatabaseHandler#saveObject(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public void saveObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
|
||||
// Save only in the destination database
|
||||
toHandler.saveObject(instance);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see world.bentobox.bentobox.database.AbstractDatabaseHandler#deleteID(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void deleteID(String uniqueId) {
|
||||
// Delete in both databases if the object exists
|
||||
toHandler.deleteID(uniqueId);
|
||||
fromHandler.deleteID(uniqueId);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see world.bentobox.bentobox.database.AbstractDatabaseHandler#deleteObject(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public void deleteObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
|
||||
// Delete in both databases if the object exists
|
||||
toHandler.deleteObject(instance);
|
||||
fromHandler.deleteObject(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// Not used
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package world.bentobox.bentobox.database.transition;
|
||||
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.json.JSONDatabase;
|
||||
import world.bentobox.bentobox.database.yaml.YamlDatabase;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class Yaml2JsonDatabase implements DatabaseSetup {
|
||||
|
||||
@Override
|
||||
public <T> AbstractDatabaseHandler<T> getHandler(Class<T> type) {
|
||||
return new TransitionDatabaseHandler<>(type, new YamlDatabase().getHandler(type), new JSONDatabase().getHandler(type));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package world.bentobox.bentobox.database.transition;
|
||||
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.mariadb.MariaDBDatabase;
|
||||
import world.bentobox.bentobox.database.yaml.YamlDatabase;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class Yaml2MariaDBDatabase implements DatabaseSetup {
|
||||
|
||||
@Override
|
||||
public <T> AbstractDatabaseHandler<T> getHandler(Class<T> type) {
|
||||
return new TransitionDatabaseHandler<>(type, new YamlDatabase().getHandler(type), new MariaDBDatabase().getHandler(type));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package world.bentobox.bentobox.database.transition;
|
||||
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.mysql.MySQLDatabase;
|
||||
import world.bentobox.bentobox.database.yaml.YamlDatabase;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class Yaml2MySQLDatabase implements DatabaseSetup {
|
||||
|
||||
@Override
|
||||
public <T> AbstractDatabaseHandler<T> getHandler(Class<T> type) {
|
||||
return new TransitionDatabaseHandler<>(type, new YamlDatabase().getHandler(type), new MySQLDatabase().getHandler(type));
|
||||
}
|
||||
|
||||
}
|
@ -4,6 +4,7 @@ import java.beans.IntrospectionException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.configuration.ConfigObject;
|
||||
import world.bentobox.bentobox.database.DatabaseConnector;
|
||||
|
||||
/**
|
||||
@ -18,9 +19,16 @@ public class ConfigHandler<T> extends YamlDatabaseHandler<T> {
|
||||
|
||||
public ConfigHandler(BentoBox plugin, Class<T> type, DatabaseConnector databaseConnector) {
|
||||
super(plugin, type, databaseConnector);
|
||||
if (!ConfigObject.class.isAssignableFrom(type)) {
|
||||
throw new java.lang.ClassFormatError("Config classes must implement ConfigObject");
|
||||
}
|
||||
}
|
||||
|
||||
public void saveSettings(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
|
||||
// ConfigObject check
|
||||
if (!(instance instanceof ConfigObject)) {
|
||||
throw new java.lang.ClassFormatError("Config classes must implement ConfigObject");
|
||||
}
|
||||
configFlag = true;
|
||||
saveObject(instance);
|
||||
}
|
||||
|
@ -652,6 +652,9 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
|
||||
}
|
||||
|
||||
private void delete(String uniqueId) {
|
||||
if (uniqueId == null) {
|
||||
return;
|
||||
}
|
||||
// The filename of the YAML file is the value of uniqueId field plus .yml. Sometimes the .yml is already appended.
|
||||
if (!uniqueId.endsWith(YML)) {
|
||||
uniqueId = uniqueId + YML;
|
||||
@ -663,7 +666,7 @@ public class YamlDatabaseHandler<T> extends AbstractDatabaseHandler<T> {
|
||||
// Obtain the file and delete it
|
||||
File file = new File(tableFolder, uniqueId);
|
||||
try {
|
||||
Files.delete(file.toPath());
|
||||
Files.deleteIfExists(file.toPath());
|
||||
} catch (IOException e) {
|
||||
plugin.logError("Could not delete yml database object! " + file.getName() + " - " + e.getMessage());
|
||||
}
|
||||
|
@ -11,7 +11,9 @@ import world.bentobox.bentobox.api.addons.Addon;
|
||||
import world.bentobox.bentobox.api.addons.AddonClassLoader;
|
||||
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||
import world.bentobox.bentobox.api.addons.exceptions.InvalidAddonFormatException;
|
||||
import world.bentobox.bentobox.api.configuration.ConfigObject;
|
||||
import world.bentobox.bentobox.api.events.addon.AddonEvent;
|
||||
import world.bentobox.bentobox.database.objects.DataObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
@ -383,4 +385,19 @@ public class AddonsManager {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of addon classes that are of type {@link DataObject}
|
||||
* but not {@link ConfigObject}. Configs are not transitioned to database.
|
||||
* Used in database transition.
|
||||
* @return list of DataObjects
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public List<Class<?>> getDataObjects() {
|
||||
return classes.values().stream()
|
||||
.filter(DataObject.class::isAssignableFrom)
|
||||
// Do not include config files
|
||||
.filter(c -> !ConfigObject.class.isAssignableFrom(c))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
@ -11,13 +11,17 @@ general:
|
||||
# If there is no economy plugin present anyway, money will be automatically disabled.
|
||||
use-economy: true
|
||||
database:
|
||||
# YAML, JSON, MYSQL, MARIADB (10.2.3+), MONGODB.
|
||||
# YAML and JSON are both file-based databases.
|
||||
# JSON, MYSQL, MARIADB (10.2.3+), MONGODB, and YAML(deprecated).
|
||||
# Transition database options are:
|
||||
# YAML2JSON, YAML2MARIADB, YAML2MYSQL
|
||||
# JSON2YAML, JSON2MARIADB, JSON2MYSQL
|
||||
# MYSQL2JSON, MYSQL2YAML
|
||||
# If you need others, make a feature request.
|
||||
# Transition options enable migration from one database type to another. Use /bbox migrate.
|
||||
# MYSQL might not work with all implementations: if available, use a dedicated database type (e.g. MARIADB).
|
||||
# If you use MONGODB, you must also run the BSBMongo plugin (not addon).
|
||||
# See https://github.com/tastybento/bsbMongo/releases/.
|
||||
# /!\ BentoBox currently does not support changing this value mid-game. If you do need to change it, do a full reset of your databases and worlds.
|
||||
type: YAML
|
||||
type: JSON
|
||||
host: localhost
|
||||
# Port 3306 is MySQL's default. Port 27017 is MongoDB's default.
|
||||
port: 3306
|
||||
|
@ -274,6 +274,14 @@ commands:
|
||||
see-console: |-
|
||||
&aCheck the console to see the feedback.
|
||||
&aThis command is so spammy that the feedback cannot be read from chat...
|
||||
migrate:
|
||||
description: "migrates data from one database to another"
|
||||
players: "&6Migrating players"
|
||||
names: "&6Migrating names"
|
||||
addons: "&6Migrating addons"
|
||||
class: "&6Migrating [description]"
|
||||
migrated: "&AMigrated"
|
||||
|
||||
confirmation:
|
||||
confirm: "&cType command again within &b[seconds]s&c to confirm."
|
||||
previous-request-cancelled: "&6Previous confirmation request cancelled."
|
||||
|
@ -59,6 +59,7 @@ import world.bentobox.bentobox.Settings;
|
||||
import world.bentobox.bentobox.api.configuration.WorldSettings;
|
||||
import world.bentobox.bentobox.api.events.island.IslandEvent.IslandDeleteEvent;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.lists.Flags;
|
||||
import world.bentobox.bentobox.managers.island.IslandCache;
|
||||
@ -104,6 +105,7 @@ public class IslandsManagerTest {
|
||||
// Settings
|
||||
Settings s = mock(Settings.class);
|
||||
when(plugin.getSettings()).thenReturn(s);
|
||||
when(s.getDatabaseType()).thenReturn(DatabaseType.JSON);
|
||||
|
||||
// Player
|
||||
player = mock(Player.class);
|
||||
@ -363,13 +365,6 @@ public class IslandsManagerTest {
|
||||
*/
|
||||
@Test
|
||||
public void testBigScan() {
|
||||
Settings settings = mock(Settings.class);
|
||||
|
||||
when(plugin.getSettings()).thenReturn(settings);
|
||||
|
||||
IslandWorldManager iwm = mock(IslandWorldManager.class);
|
||||
when(plugin.getIWM()).thenReturn(iwm);
|
||||
|
||||
IslandsManager manager = new IslandsManager(plugin);
|
||||
|
||||
Location location = mock(Location.class);
|
||||
|
@ -43,6 +43,7 @@ import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.Settings;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.Database;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType;
|
||||
import world.bentobox.bentobox.database.objects.Players;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
@ -90,6 +91,7 @@ public class PlayersManagerTest {
|
||||
// Settings
|
||||
Settings s = mock(Settings.class);
|
||||
when(plugin.getSettings()).thenReturn(s);
|
||||
when(s.getDatabaseType()).thenReturn(DatabaseType.JSON);
|
||||
|
||||
// Set up spawn
|
||||
Location netherSpawn = mock(Location.class);
|
||||
|
Loading…
Reference in New Issue
Block a user