mirror of
https://github.com/songoda/SongodaCore.git
synced 2025-01-20 22:41:40 +01:00
Fix database converter, fix uuid data support. Adds test for database
This commit is contained in:
parent
5d0b0409da
commit
84c7d1da4b
@ -272,21 +272,12 @@ public abstract class SongodaPlugin extends JavaPlugin {
|
||||
* @param migrations List of migrations to run.
|
||||
*/
|
||||
protected void initDatabase(List<DataMigration> migrations) {
|
||||
boolean legacy = this.config.contains("MySQL");
|
||||
boolean isSQLite = !this.config.getBoolean("MySQL.Enabled", false);
|
||||
if (legacy && isSQLite) {
|
||||
this.config.set("MySQL", null);
|
||||
File databaseFile = new File(getDataFolder(), getName().toLowerCase() + ".db");
|
||||
boolean legacy = databaseFile.exists();
|
||||
|
||||
if (legacy) {
|
||||
getLogger().warning("SQLite detected, converting to H2...");
|
||||
this.dataManager = new DataManager(this, migrations, DatabaseType.SQLITE);
|
||||
} else if (legacy) {
|
||||
//Copy creditental from old config to new config
|
||||
this.databaseConfig.set("MySQL.Hostname", this.config.getString("MySQL.Hostname", "localhost"));
|
||||
this.databaseConfig.set("MySQL.Port", this.config.getInt("MySQL.Port", 3306));
|
||||
this.databaseConfig.set("MySQL.Database", this.config.getString("MySQL.Database", "database"));
|
||||
this.databaseConfig.set("MySQL.Username", this.config.getString("MySQL.Username", "username"));
|
||||
this.databaseConfig.set("MySQL.Password", this.config.getString("MySQL.Password", "password"));
|
||||
this.databaseConfig.set("MySQL.Pool Size", this.config.getInt("MySQL.Pool Size", 5));
|
||||
this.databaseConfig.set("MySQL.Use SSL", this.config.getBoolean("MySQL.Use SSL", false));
|
||||
this.dataManager = new DataManager(this, migrations);
|
||||
} else {
|
||||
this.dataManager = new DataManager(this, migrations);
|
||||
}
|
||||
@ -294,10 +285,14 @@ public abstract class SongodaPlugin extends JavaPlugin {
|
||||
//Check if the type is SQLite
|
||||
if (dataManager.getDatabaseConnector().getType() == DatabaseType.SQLITE) {
|
||||
//Let's convert it to H2
|
||||
DataManager newDataManager = DataMigration.convert(this, DatabaseType.H2);
|
||||
if (newDataManager != null && newDataManager.getDatabaseConnector().isInitialized()) {
|
||||
//Set the new data manager
|
||||
setDataManager(newDataManager);
|
||||
try {
|
||||
DataManager newDataManager = DataMigration.convert(this, DatabaseType.H2);
|
||||
if (newDataManager != null && newDataManager.getDatabaseConnector().isInitialized()) {
|
||||
//Set the new data manager
|
||||
setDataManager(newDataManager);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -312,7 +307,8 @@ public abstract class SongodaPlugin extends JavaPlugin {
|
||||
if (this.dataManager == dataManager) return;
|
||||
//Make sure to shut down the old data manager.
|
||||
if (this.dataManager != null) {
|
||||
dataManager.shutdown();
|
||||
this.dataManager.shutdown();
|
||||
this.dataManager = null;
|
||||
}
|
||||
this.dataManager = dataManager;
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import com.craftaro.core.SongodaPlugin;
|
||||
import com.craftaro.core.configuration.Config;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jooq.Condition;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.Query;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.impl.DSL;
|
||||
@ -19,11 +21,13 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
@ -47,6 +51,22 @@ public class DataManager {
|
||||
@Deprecated
|
||||
private static final Map<String, LinkedList<Runnable>> queues = new HashMap<>();
|
||||
|
||||
|
||||
|
||||
DataManager() {
|
||||
this.databaseConfig = null;
|
||||
this.plugin = null;
|
||||
this.migrations = Collections.emptyList();
|
||||
this.databaseConnector = new H2Connector();
|
||||
}
|
||||
|
||||
DataManager(DatabaseType type) {
|
||||
this.databaseConfig = null;
|
||||
this.plugin = null;
|
||||
this.migrations = Collections.emptyList();
|
||||
this.databaseConnector = new SQLiteConnector();
|
||||
}
|
||||
|
||||
public DataManager(SongodaPlugin plugin, List<DataMigration> migrations) {
|
||||
this.plugin = plugin;
|
||||
this.migrations = migrations;
|
||||
@ -77,15 +97,7 @@ public class DataManager {
|
||||
break;
|
||||
}
|
||||
case "SQLITE": {
|
||||
//Lets check if we have the sqlite file in the plugin folder
|
||||
File databaseFile = new File(plugin.getDataFolder(), plugin.getName().toLowerCase()+".db");
|
||||
if (!databaseFile.exists()) {
|
||||
//Lets start SQLite and it will be converted to H2
|
||||
this.databaseConnector = new SQLiteConnector(plugin);
|
||||
} else {
|
||||
//No need for conversion, lets use H2 instead
|
||||
this.databaseConnector = new H2Connector(plugin);
|
||||
}
|
||||
this.databaseConnector = new SQLiteConnector(plugin);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@ -116,6 +128,9 @@ public class DataManager {
|
||||
* @return the prefix to be used by all table names
|
||||
*/
|
||||
public String getTablePrefix() {
|
||||
if (this.plugin == null) {
|
||||
return "";
|
||||
}
|
||||
return this.plugin.getDescription().getName().toLowerCase() + '_';
|
||||
}
|
||||
|
||||
@ -226,7 +241,7 @@ public class DataManager {
|
||||
databaseConnector.connectDSL(context -> {
|
||||
context.insertInto(DSL.table(getTablePrefix() + data.getTableName()))
|
||||
.set(data.serialize())
|
||||
.onConflict(DSL.field("id")).doUpdate()
|
||||
.onConflict(data.getId() != -1 ? DSL.field("id") : DSL.field("uuid")).doUpdate()
|
||||
.set(data.serialize())
|
||||
.where(data.getId() != -1 ? DSL.field("id").eq(data.getId()) : DSL.field("uuid").eq(data.getUniqueId().toString()))
|
||||
.execute();
|
||||
@ -241,7 +256,7 @@ public class DataManager {
|
||||
databaseConnector.connectDSL(context -> {
|
||||
context.insertInto(DSL.table(getTablePrefix() + data.getTableName()))
|
||||
.set(data.serialize())
|
||||
.onConflict(DSL.field("id")).doUpdate()
|
||||
.onConflict(data.getId() != -1 ? DSL.field("id") : DSL.field("uuid")).doUpdate()
|
||||
.set(data.serialize())
|
||||
.where(data.getId() != -1 ? DSL.field("id").eq(data.getId()) : DSL.field("uuid").eq(data.getUniqueId().toString()))
|
||||
.execute();
|
||||
@ -258,7 +273,7 @@ public class DataManager {
|
||||
for (Data data : dataBatch) {
|
||||
queries.add(context.insertInto(DSL.table(getTablePrefix() + data.getTableName()))
|
||||
.set(data.serialize())
|
||||
.onConflict(DSL.field("id")).doUpdate()
|
||||
.onConflict(data.getId() != -1 ? DSL.field("id") : DSL.field("uuid")).doUpdate()
|
||||
.set(data.serialize())
|
||||
.where(data.getId() != -1 ? DSL.field("id").eq(data.getId()) : DSL.field("uuid").eq(data.getUniqueId().toString())));
|
||||
}
|
||||
@ -277,7 +292,7 @@ public class DataManager {
|
||||
for (Data data : dataBatch) {
|
||||
queries.add(context.insertInto(DSL.table(getTablePrefix() + data.getTableName()))
|
||||
.set(data.serialize())
|
||||
.onConflict(DSL.field("id")).doUpdate()
|
||||
.onConflict(data.getId() != -1 ? DSL.field("id") : DSL.field("uuid")).doUpdate()
|
||||
.set(data.serialize())
|
||||
.where(data.getId() != -1 ? DSL.field("id").eq(data.getId()) : DSL.field("uuid").eq(data.getUniqueId().toString())));
|
||||
}
|
||||
@ -308,19 +323,25 @@ public class DataManager {
|
||||
public <T extends Data> T load(int id, Class<?> clazz, String table) {
|
||||
try {
|
||||
AtomicReference<Data> data = new AtomicReference<>((Data) clazz.getConstructor().newInstance());
|
||||
AtomicBoolean found = new AtomicBoolean(false);
|
||||
databaseConnector.connectDSL(context -> {
|
||||
try {
|
||||
data.set((Data) clazz.getDeclaredConstructor().newInstance());
|
||||
data.get().deserialize(Objects.requireNonNull(context.select()
|
||||
.from(DSL.table(getTablePrefix() + table))
|
||||
.where(DSL.field("id").eq(id))
|
||||
.fetchOne())
|
||||
.intoMap());
|
||||
found.set(true);
|
||||
} catch (NullPointerException ignored) {
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
});
|
||||
return (T) data.get();
|
||||
if (found.get()) {
|
||||
return (T) data.get();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
@ -329,6 +350,8 @@ public class DataManager {
|
||||
/**
|
||||
* Loads the data from the database
|
||||
* @param uuid The uuid of the data
|
||||
* @param clazz The class of the data
|
||||
* @param table The table of the data without prefix
|
||||
* @return The loaded data
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@ -346,7 +369,42 @@ public class DataManager {
|
||||
.intoMap());
|
||||
found.set(true);
|
||||
} catch (NullPointerException ignored) {
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
});
|
||||
if (found.get()) {
|
||||
return (T) data.get();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the data from the database
|
||||
* @param uuid The uuid of the data
|
||||
* @param clazz The class of the data
|
||||
* @param table The table of the data without prefix
|
||||
* @param uuidColumn The column of the uuid
|
||||
* @return The loaded data
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Data> T load(UUID uuid, Class<?> clazz, String table, String uuidColumn) {
|
||||
try {
|
||||
AtomicReference<Data> data = new AtomicReference<>((Data) clazz.getConstructor().newInstance());
|
||||
AtomicBoolean found = new AtomicBoolean(false);
|
||||
databaseConnector.connectDSL(context -> {
|
||||
try {
|
||||
data.get().deserialize(Objects.requireNonNull(context.select()
|
||||
.from(DSL.table(table))
|
||||
.where(DSL.field(uuidColumn).eq(uuid.toString()))
|
||||
.fetchOne())
|
||||
.intoMap());
|
||||
found.set(true);
|
||||
} catch (NullPointerException ignored) {
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
@ -5,9 +5,13 @@ import com.craftaro.core.SongodaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@ -33,7 +37,7 @@ public abstract class DataMigration {
|
||||
* @param toType The new database type
|
||||
* @return The new data manager instance
|
||||
*/
|
||||
public static DataManager convert(SongodaPlugin plugin, DatabaseType toType) {
|
||||
public static DataManager convert(SongodaPlugin plugin, DatabaseType toType) throws Exception {
|
||||
DataManager from = plugin.getDataManager();
|
||||
if (from.getDatabaseConnector().getType() == toType) {
|
||||
plugin.getLogger().severe("Cannot convert to the same database type!");
|
||||
@ -45,65 +49,117 @@ public abstract class DataMigration {
|
||||
return null;
|
||||
}
|
||||
|
||||
plugin.getLogger().info("Converting data from " + from.getDatabaseConnector().getType().name() + " to " + toType.name() + "...");
|
||||
DatabaseConnector fromConnector = from.getDatabaseConnector();
|
||||
DatabaseConnector toConnector = to.getDatabaseConnector();
|
||||
|
||||
Connection fromConnection;
|
||||
Connection toConnection = null;
|
||||
Connection fromConnection = fromConnector.getConnection();
|
||||
Connection toConnection = toConnector.getConnection();
|
||||
|
||||
try {
|
||||
fromConnection = fromConnector.getConnection();
|
||||
toConnection = toConnector.getConnection();
|
||||
toConnection.setAutoCommit(false);
|
||||
|
||||
// Retrieve the list of tables from the old database
|
||||
List<String> tableNames = new ArrayList<>();
|
||||
try (ResultSet rs = fromConnection.getMetaData().getTables(null, null, null, new String[] {"TABLE"})) {
|
||||
while (rs.next()) {
|
||||
String tableName = rs.getString("TABLE_NAME");
|
||||
tableNames.add(tableName);
|
||||
}
|
||||
}
|
||||
// Export schema
|
||||
DatabaseMetaData meta = fromConnection.getMetaData();
|
||||
ResultSet tables = meta.getTables(null, null, null, new String[]{"TABLE"});
|
||||
|
||||
// Transfer the data from the old database to the new database
|
||||
for (String tableName : tableNames) {
|
||||
try (
|
||||
PreparedStatement fromStmt = fromConnection.prepareStatement("SELECT * FROM " + tableName);
|
||||
ResultSet rs = fromStmt.executeQuery();
|
||||
PreparedStatement toStmt = toConnection.prepareStatement("INSERT INTO " + tableName + " VALUES (" + String.join(",", Collections.nCopies(rs.getMetaData().getColumnCount(), "?")) + ")")
|
||||
) {
|
||||
while (rs.next()) {
|
||||
for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
|
||||
toStmt.setObject(i, rs.getObject(i));
|
||||
}
|
||||
toStmt.executeUpdate();
|
||||
while (tables.next()) {
|
||||
String tableName = tables.getString("TABLE_NAME");
|
||||
Statement stmt = fromConnection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT * FROM " + tableName);
|
||||
|
||||
ResultSetMetaData metaRs = rs.getMetaData();
|
||||
int columnCount = metaRs.getColumnCount();
|
||||
|
||||
StringBuilder createTableQuery = new StringBuilder();
|
||||
createTableQuery.append("CREATE TABLE IF NOT EXISTS ").append(tableName).append(" (");
|
||||
|
||||
for (int i = 1; i <= columnCount; i++) {
|
||||
String columnName = metaRs.getColumnName(i);
|
||||
String columnType = metaRs.getColumnTypeName(i);
|
||||
int columnSize = metaRs.getColumnDisplaySize(i);
|
||||
|
||||
createTableQuery.append(columnName).append(" ").append(columnType).append("(").append(columnSize).append(")");
|
||||
|
||||
if (i < columnCount) {
|
||||
createTableQuery.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
createTableQuery.append(")");
|
||||
|
||||
toConnection.createStatement().execute(createTableQuery.toString());
|
||||
|
||||
while (rs.next()) {
|
||||
StringBuilder insertQuery = new StringBuilder();
|
||||
insertQuery.append("INSERT INTO ").append(tableName).append(" VALUES (");
|
||||
|
||||
for (int i = 1; i <= columnCount; i++) {
|
||||
Object value = rs.getObject(i);
|
||||
|
||||
if (value == null) {
|
||||
insertQuery.append("NULL");
|
||||
} else if (value instanceof String || value instanceof Timestamp) {
|
||||
insertQuery.append("'").append(value).append("'");
|
||||
} else {
|
||||
insertQuery.append(value);
|
||||
}
|
||||
|
||||
if (i < columnCount) {
|
||||
insertQuery.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
insertQuery.append(")");
|
||||
toConnection.createStatement().execute(insertQuery.toString());
|
||||
}
|
||||
}
|
||||
|
||||
toConnection.commit();
|
||||
plugin.getLogger().info("Successfully migrated data from " + from.getDatabaseConnector().getType() + " to " + to.getDatabaseConnector().getType());
|
||||
} catch (Exception e) {
|
||||
if (toConnection != null)
|
||||
if (toConnection != null) {
|
||||
try {
|
||||
toConnection.rollback();
|
||||
} catch (SQLException e1) {
|
||||
e1.printStackTrace();
|
||||
SongodaCore.getInstance().getLogger().severe("Failed to rollback data for the new database");
|
||||
plugin.getLogger().severe("Failed to rollback data for the new database");
|
||||
}
|
||||
}
|
||||
e.printStackTrace();
|
||||
SongodaCore.getInstance().getLogger().severe("Failed to migrate data from " + from.getDatabaseConnector().getType() + " to " + to.getDatabaseConnector().getType());
|
||||
plugin.getLogger().severe("Failed to migrate data from " + from.getDatabaseConnector().getType() + " to " + to.getDatabaseConnector().getType());
|
||||
return null;
|
||||
} finally {
|
||||
SongodaCore.getInstance().getLogger().info("Successfully migrated data from " + from.getDatabaseConnector().getType() + " to " + to.getDatabaseConnector().getType());
|
||||
}
|
||||
fromConnector.closeConnection();
|
||||
//Get rid of the old database file
|
||||
//Get rid of the old SQLite database file if it exists and create a backup
|
||||
File databaseFile = new File(plugin.getDataFolder(), plugin.getName().toLowerCase()+".db");
|
||||
if (databaseFile.exists()) {
|
||||
|
||||
//rename it to .old
|
||||
databaseFile.renameTo(new File(plugin.getDataFolder(), plugin.getName().toLowerCase()+".old"));
|
||||
databaseFile.renameTo(new File(plugin.getDataFolder(), plugin.getName().toLowerCase() + ".db.old"));
|
||||
plugin.getLogger().info("Old database file renamed to " + plugin.getName().toLowerCase() + ".db.old");
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
private String getTableColumns(Connection connection, String tableName) {
|
||||
StringBuilder columns = new StringBuilder();
|
||||
try {
|
||||
DatabaseMetaData meta = connection.getMetaData();
|
||||
ResultSet rs = meta.getColumns(null, null, tableName, null);
|
||||
|
||||
while (rs.next()) {
|
||||
String columnName = rs.getString("COLUMN_NAME");
|
||||
String columnType = rs.getString("TYPE_NAME");
|
||||
|
||||
columns.append(columnName).append(" ").append(columnType).append(", ");
|
||||
}
|
||||
|
||||
rs.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
columns.setLength(columns.length() - 2);
|
||||
return columns.toString();
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,29 @@ public class H2Connector implements DatabaseConnector {
|
||||
private HikariDataSource hikari;
|
||||
private boolean initializedSuccessfully;
|
||||
|
||||
H2Connector() {
|
||||
this.plugin = null;
|
||||
|
||||
int poolSize = 2;
|
||||
String password = "password";
|
||||
String username = "username";
|
||||
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setDriverClassName("org.h2.Driver");
|
||||
config.setJdbcUrl("jdbc:h2:./db_test/CraftaroCoreTest;AUTO_RECONNECT=TRUE;MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE");
|
||||
config.setPassword(username);
|
||||
config.setUsername(password);
|
||||
config.setMaximumPoolSize(poolSize);
|
||||
|
||||
try {
|
||||
this.hikari = new HikariDataSource(config);
|
||||
this.initializedSuccessfully = true;
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
this.initializedSuccessfully = false;
|
||||
}
|
||||
}
|
||||
|
||||
public H2Connector(SongodaPlugin plugin) {
|
||||
this(plugin, plugin.getDatabaseConfig());
|
||||
}
|
||||
@ -31,7 +54,7 @@ public class H2Connector implements DatabaseConnector {
|
||||
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setDriverClassName("com.craftaro.core.third_party.org.h2.Driver");
|
||||
config.setJdbcUrl("jdbc:h2:./h2_" + plugin.getDataFolder().getPath().replaceAll("\\\\", "/") + "/" + plugin.getDescription().getName().toLowerCase()+ ";AUTO_RECONNECT=TRUE;MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE");
|
||||
config.setJdbcUrl("jdbc:h2:./" + plugin.getDataFolder().getPath().replaceAll("\\\\", "/") + "/" + plugin.getDescription().getName().toLowerCase()+ ";AUTO_RECONNECT=TRUE;MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE");
|
||||
config.setPassword(username);
|
||||
config.setUsername(password);
|
||||
config.setMaximumPoolSize(poolSize);
|
||||
@ -70,7 +93,9 @@ public class H2Connector implements DatabaseConnector {
|
||||
try (Connection connection = getConnection()) {
|
||||
return callback.accept(connection);
|
||||
} catch (Exception ex) {
|
||||
SongodaCore.getInstance().getLogger().severe("An error occurred executing a MySQL query: " + ex.getMessage());
|
||||
if (this.plugin != null) {
|
||||
SongodaCore.getInstance().getLogger().severe("An error occurred executing a MySQL query: " + ex.getMessage());
|
||||
}
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return OptionalResult.empty();
|
||||
@ -81,7 +106,9 @@ public class H2Connector implements DatabaseConnector {
|
||||
try (Connection connection = getConnection()){
|
||||
callback.accept(DSL.using(connection, SQLDialect.MYSQL));
|
||||
} catch (Exception ex) {
|
||||
this.plugin.getLogger().severe("An error occurred executing a MySQL query: " + ex.getMessage());
|
||||
if (this.plugin != null) {
|
||||
this.plugin.getLogger().severe("An error occurred executing a MySQL query: " + ex.getMessage());
|
||||
}
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
@ -91,7 +118,9 @@ public class H2Connector implements DatabaseConnector {
|
||||
try (Connection connection = getConnection()) {
|
||||
return callback.accept(DSL.using(connection, SQLDialect.MYSQL));
|
||||
} catch (Exception ex) {
|
||||
SongodaCore.getInstance().getLogger().severe("An error occurred executing a MySQL query: " + ex.getMessage());
|
||||
if (this.plugin != null) {
|
||||
SongodaCore.getInstance().getLogger().severe("An error occurred executing a MySQL query: " + ex.getMessage());
|
||||
}
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return OptionalResult.empty();
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.craftaro.core.database;
|
||||
|
||||
import com.craftaro.core.SongodaCore;
|
||||
import eu.decentsoftware.holograms.api.utils.scheduler.S;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jooq.SQLDialect;
|
||||
import org.jooq.impl.DSL;
|
||||
@ -16,6 +17,11 @@ public class SQLiteConnector implements DatabaseConnector {
|
||||
private final String connectionString;
|
||||
private Connection connection;
|
||||
|
||||
SQLiteConnector() {
|
||||
this.plugin = null;
|
||||
this.connectionString = "jdbc:sqlite:" + "."+File.separator+"db_test"+File.separator+"CraftaroCoreTestSQLite.db";
|
||||
}
|
||||
|
||||
public SQLiteConnector(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.connectionString = "jdbc:sqlite:" + plugin.getDataFolder() + File.separator + plugin.getDescription().getName().toLowerCase() + ".db";
|
||||
|
409
Core/src/test/java/com/craftaro/core/database/DatabaseTest.java
Normal file
409
Core/src/test/java/com/craftaro/core/database/DatabaseTest.java
Normal file
@ -0,0 +1,409 @@
|
||||
package com.craftaro.core.database;
|
||||
|
||||
import org.jooq.impl.DSL;
|
||||
import org.jooq.impl.SQLDataType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
public class DatabaseTest {
|
||||
|
||||
//Create database tests for DataManager
|
||||
|
||||
static {
|
||||
// Disable tips and logo for Jooq
|
||||
System.setProperty("org.jooq.no-tips", "true");
|
||||
System.setProperty("org.jooq.no-logo", "true");
|
||||
}
|
||||
|
||||
private static boolean deleteDirectory(File directoryToBeDeleted) {
|
||||
File[] allContents = directoryToBeDeleted.listFiles();
|
||||
if (allContents != null) {
|
||||
for (File file : allContents) {
|
||||
deleteDirectory(file);
|
||||
}
|
||||
}
|
||||
return directoryToBeDeleted.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testId() {
|
||||
File dbDir = new File("./db_test");
|
||||
File logsDir = new File("./logs");
|
||||
DataManager dataManager = new DataManager();
|
||||
if (!dataManager.getDatabaseConnector().isInitialized()) {
|
||||
throw new RuntimeException("Database 'Data' test failed - DatabaseConnector not initialized");
|
||||
}
|
||||
//Create tables
|
||||
dataManager.getDatabaseConnector().connectDSL(context -> {
|
||||
context.createTableIfNotExists("data_test")
|
||||
.column("id", SQLDataType.INTEGER.identity(true))
|
||||
.column("name", SQLDataType.VARCHAR(16))
|
||||
.column("points", SQLDataType.INTEGER)
|
||||
.column("other_points", SQLDataType.INTEGER)
|
||||
.constraint(DSL.constraint().primaryKey(DSL.field("id")))
|
||||
.execute();
|
||||
});
|
||||
|
||||
int id = new Random().nextInt(1000);
|
||||
DataTestId dataTest = new DataTestId(id, "Test", 10, 20);
|
||||
dataManager.saveSync(dataTest);
|
||||
|
||||
DataTestId dataTest2 = dataManager.load(id, DataTestId.class, "data_test");
|
||||
if (!dataTest.equals(dataTest2)) {
|
||||
|
||||
|
||||
System.err.println("Database 'Data - ID' test failed");
|
||||
} else {
|
||||
System.out.println("Database 'Data - ID' test passed");
|
||||
}
|
||||
|
||||
dataManager.shutdownNow();
|
||||
|
||||
if (dbDir.exists()) {
|
||||
deleteDirectory(dbDir);
|
||||
}
|
||||
if (logsDir.exists()) {
|
||||
deleteDirectory(logsDir);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUUID() {
|
||||
File dbDir = new File("./db_test");
|
||||
File logsDir = new File("./logs");
|
||||
|
||||
DataManager dataManager = new DataManager();
|
||||
if (!dataManager.getDatabaseConnector().isInitialized()) {
|
||||
throw new RuntimeException("Database 'Data' test failed - DatabaseConnector not initialized");
|
||||
}
|
||||
//Create tables
|
||||
dataManager.getDatabaseConnector().connectDSL(context -> {
|
||||
context.createTableIfNotExists("data_uuid_test")
|
||||
.column("uuid", SQLDataType.VARCHAR(36))
|
||||
.column("name", SQLDataType.VARCHAR(16))
|
||||
.column("points", SQLDataType.INTEGER)
|
||||
.column("other_points", SQLDataType.INTEGER)
|
||||
.constraint(DSL.constraint().primaryKey(DSL.field("uuid")))
|
||||
.execute();
|
||||
});
|
||||
|
||||
|
||||
UUID uuid = UUID.randomUUID();
|
||||
DataTestUUID dataTest = new DataTestUUID(uuid, "Test", 10, 20);
|
||||
dataManager.saveSync(dataTest);
|
||||
|
||||
DataTestUUID dataTest2 = dataManager.load(uuid, DataTestUUID.class, "data_uuid_test");
|
||||
|
||||
if (!dataTest.equals(dataTest2)) {
|
||||
System.err.println(dataTest);
|
||||
System.err.println(dataTest2);
|
||||
System.err.println("Database 'Data - UUID' test failed");
|
||||
} else {
|
||||
System.out.println("Database 'Data - UUID' test passed");
|
||||
}
|
||||
|
||||
|
||||
|
||||
dataManager.shutdownNow();
|
||||
|
||||
if (dbDir.exists()) {
|
||||
deleteDirectory(dbDir);
|
||||
}
|
||||
if (logsDir.exists()) {
|
||||
deleteDirectory(logsDir);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public static void testConvert() {
|
||||
File dbDir = new File("./db_test");
|
||||
File logsDir = new File("./logs");
|
||||
|
||||
DataManager sqlite = new DataManager(DatabaseType.SQLITE);
|
||||
DataManager h2 = new DataManager();
|
||||
|
||||
if (!sqlite.getDatabaseConnector().isInitialized()) {
|
||||
throw new RuntimeException("Database 'Data - Convert' test failed - DatabaseConnector not initialized");
|
||||
}
|
||||
|
||||
if (!h2.getDatabaseConnector().isInitialized()) {
|
||||
throw new RuntimeException("Database 'Data - Convert' test failed - DatabaseConnector not initialized");
|
||||
}
|
||||
|
||||
//Create tables for SQLite
|
||||
try (Connection sqliteConnection = sqlite.getDatabaseConnector().getConnection(); Connection h2Connection = h2.getDatabaseConnector().getConnection()) {
|
||||
sqliteConnection.createStatement().execute("CREATE TABLE IF NOT EXISTS `data_convert_test` (`id` INTEGER PRIMARY KEY NOT NULL, `name` VARCHAR(16), `points` INTEGER, `other_points` INTEGER)");
|
||||
//create 2 other tables for some example data with different column names and types
|
||||
sqliteConnection.createStatement().execute("CREATE TABLE IF NOT EXISTS `data_convert_test2` (`id` INTEGER PRIMARY KEY NOT NULL, `name` VARCHAR(16), `points` INTEGER, `other_points` INTEGER)");
|
||||
sqliteConnection.createStatement().execute("CREATE TABLE IF NOT EXISTS `data_convert_test3` (`id` INTEGER PRIMARY KEY NOT NULL, `name` VARCHAR(16), `points` INTEGER, `other_points` INTEGER)");
|
||||
|
||||
//Fill with some example data using loops
|
||||
for (int i = 0; i < 10; i++) {
|
||||
sqliteConnection.createStatement().execute("INSERT INTO `data_convert_test` (`name`, `points`, `other_points`) VALUES ('Test" + i + "', " + i + ", " + i + ")");
|
||||
sqliteConnection.createStatement().execute("INSERT INTO `data_convert_test2` (`name`, `points`, `other_points`) VALUES ('Test" + i + "', " + i + ", " + i + ")");
|
||||
sqliteConnection.createStatement().execute("INSERT INTO `data_convert_test3` (`name`, `points`, `other_points`) VALUES ('Test" + i + "', " + i + ", " + i + ")");
|
||||
}
|
||||
|
||||
//Migrate tables and data to H2
|
||||
try {
|
||||
// Export schema
|
||||
DatabaseMetaData meta = sqliteConnection.getMetaData();
|
||||
ResultSet tables = meta.getTables(null, null, null, new String[]{"TABLE"});
|
||||
|
||||
while (tables.next()) {
|
||||
String tableName = tables.getString("TABLE_NAME");
|
||||
Statement stmt = sqliteConnection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT * FROM " + tableName);
|
||||
|
||||
ResultSetMetaData metaRs = rs.getMetaData();
|
||||
int columnCount = metaRs.getColumnCount();
|
||||
|
||||
StringBuilder createTableQuery = new StringBuilder();
|
||||
createTableQuery.append("CREATE TABLE ").append(tableName).append(" (");
|
||||
|
||||
for (int i = 1; i <= columnCount; i++) {
|
||||
String columnName = metaRs.getColumnName(i);
|
||||
String columnType = metaRs.getColumnTypeName(i);
|
||||
int columnSize = metaRs.getColumnDisplaySize(i);
|
||||
|
||||
createTableQuery.append(columnName).append(" ").append(columnType).append("(").append(columnSize).append(")");
|
||||
|
||||
if (i < columnCount) {
|
||||
createTableQuery.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
createTableQuery.append(")");
|
||||
|
||||
h2Connection.createStatement().execute(createTableQuery.toString());
|
||||
|
||||
while (rs.next()) {
|
||||
StringBuilder insertQuery = new StringBuilder();
|
||||
insertQuery.append("INSERT INTO ").append(tableName).append(" VALUES (");
|
||||
|
||||
for (int i = 1; i <= columnCount; i++) {
|
||||
Object value = rs.getObject(i);
|
||||
|
||||
if (value == null) {
|
||||
insertQuery.append("NULL");
|
||||
} else if (value instanceof String || value instanceof Timestamp) {
|
||||
insertQuery.append("'").append(value).append("'");
|
||||
} else {
|
||||
insertQuery.append(value);
|
||||
}
|
||||
|
||||
if (i < columnCount) {
|
||||
insertQuery.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
insertQuery.append(")");
|
||||
h2Connection.createStatement().execute(insertQuery.toString());
|
||||
}
|
||||
}
|
||||
|
||||
//Query data from both databases and compare
|
||||
Statement stmt = sqliteConnection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT * FROM `data_convert_test`");
|
||||
|
||||
//H2
|
||||
Statement stmt2 = h2Connection.createStatement();
|
||||
ResultSet rs2 = stmt2.executeQuery("SELECT * FROM `data_convert_test`");
|
||||
|
||||
//Compare data
|
||||
while (rs.next() && rs2.next()) {
|
||||
int id = rs.getInt("id");
|
||||
String name = rs.getString("name");
|
||||
int points = rs.getInt("points");
|
||||
int otherPoints = rs.getInt("other_points");
|
||||
|
||||
int id2 = rs2.getInt("id");
|
||||
String name2 = rs2.getString("name");
|
||||
int points2 = rs2.getInt("points");
|
||||
int otherPoints2 = rs2.getInt("other_points");
|
||||
|
||||
if (id != id2 || !name.equals(name2) || points != points2 || otherPoints != otherPoints2) {
|
||||
System.err.println("Database 'Data - Convert' test failed - Data mismatch");
|
||||
System.err.println("SQLite: " + id + " " + name + " " + points + " " + otherPoints);
|
||||
System.err.println("H2: " + id2 + " " + name2 + " " + points2 + " " + otherPoints2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
System.out.println("Database 'Data - Convert' test passed");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
|
||||
sqlite.shutdownNow();
|
||||
h2.shutdownNow();
|
||||
|
||||
if (dbDir.exists()) {
|
||||
deleteDirectory(dbDir);
|
||||
}
|
||||
if (logsDir.exists()) {
|
||||
deleteDirectory(logsDir);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getTableColumns(Connection sqliteConnection, String tableName) {
|
||||
StringBuilder columns = new StringBuilder();
|
||||
try {
|
||||
DatabaseMetaData meta = sqliteConnection.getMetaData();
|
||||
ResultSet rs = meta.getColumns(null, null, tableName, null);
|
||||
|
||||
while (rs.next()) {
|
||||
String columnName = rs.getString("COLUMN_NAME");
|
||||
String columnType = rs.getString("TYPE_NAME");
|
||||
|
||||
columns.append(columnName).append(" ").append(columnType).append(", ");
|
||||
}
|
||||
|
||||
rs.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
columns.setLength(columns.length() - 2);
|
||||
return columns.toString();
|
||||
}
|
||||
|
||||
private static class DataTestId implements Data {
|
||||
|
||||
private int id;
|
||||
private String name;
|
||||
private int points;
|
||||
private int otherPoints;
|
||||
|
||||
public DataTestId() {
|
||||
}
|
||||
|
||||
public DataTestId(int id, String name, int points, int otherPoints) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.points = points;
|
||||
this.otherPoints = otherPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("id", this.id);
|
||||
map.put("name", this.name);
|
||||
map.put("points", this.points);
|
||||
map.put("other_points", this.otherPoints);
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Data deserialize(Map<String, Object> map) {
|
||||
this.id = (int) map.get("id");
|
||||
this.name = (String) map.get("name");
|
||||
this.points = (int) map.get("points");
|
||||
this.otherPoints = (int) map.get("other_points");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTableName() {
|
||||
return "data_test";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof DataTestId)) return false;
|
||||
|
||||
DataTestId other = (DataTestId) obj;
|
||||
return id == other.id && name.equals(other.name) && points == other.points && otherPoints == other.otherPoints;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DataTestUUID implements Data {
|
||||
|
||||
private UUID uuid;
|
||||
private String name;
|
||||
private int points;
|
||||
private int otherPoints;
|
||||
|
||||
public DataTestUUID() {
|
||||
}
|
||||
|
||||
public DataTestUUID(UUID uuid, String name, int points, int otherPoints) {
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
this.points = points;
|
||||
this.otherPoints = otherPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUniqueId() {
|
||||
return this.uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("uuid", this.uuid.toString());
|
||||
map.put("name", this.name);
|
||||
map.put("points", this.points);
|
||||
map.put("other_points", this.otherPoints);
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Data deserialize(Map<String, Object> map) {
|
||||
this.uuid = UUID.fromString((String) map.get("uuid"));
|
||||
this.name = (String) map.get("name");
|
||||
this.points = (int) map.get("points");
|
||||
this.otherPoints = (int) map.get("other_points");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTableName() {
|
||||
return "data_uuid_test";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof DataTestUUID)) return false;
|
||||
|
||||
DataTestUUID other = (DataTestUUID) obj;
|
||||
return uuid.equals(other.uuid) && name.equals(other.name) && points == other.points && otherPoints == other.otherPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DataTestUUID{" +
|
||||
"uuid=" + uuid +
|
||||
", name='" + name + '\'' +
|
||||
", points=" + points +
|
||||
", otherPoints=" + otherPoints +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user