ORMLite to JDBI

This commit is contained in:
2026-05-28 16:19:20 -04:00
parent 8b2ca5e072
commit 607595e3c2
25 changed files with 290 additions and 434 deletions
+1 -2
View File
@@ -28,8 +28,7 @@ dependencies {
library("org.postgresql:postgresql:42.7.11")
library("org.xerial:sqlite-jdbc:3.53.1.0")
library("com.zaxxer:HikariCP:7.0.2")
library("com.j256.ormlite:ormlite-core:6.1")
library("com.j256.ormlite:ormlite-jdbc:6.1")
library("org.jdbi:jdbi3-core:3.53.0")
library("org.jetbrains:annotations:26.1.0")
compileOnly("io.papermc.paper:paper-api:${paperApiVersion}.build.+")
compileOnly("com.github.MilkBowl:VaultAPI:1.7.1") {
+9 -9
View File
@@ -19,8 +19,8 @@ import dev.plex.player.PlexPlayer;
import dev.plex.punishment.PunishmentManager;
import dev.plex.services.ServiceManager;
import dev.plex.storage.RedisConnection;
import dev.plex.storage.SQLConnection;
import dev.plex.storage.StorageType;
import dev.plex.storage.database.Database;
import dev.plex.storage.player.SQLPlayerData;
import dev.plex.storage.player.PlayerModuleDataRepository;
import dev.plex.storage.player.SQLPlayerModuleData;
@@ -63,7 +63,7 @@ public class Plex extends JavaPlugin
public Config toggles;
public File modulesFolder;
private StorageType storageType = StorageType.SQLITE;
private SQLConnection sqlConnection;
private Database database;
private RedisConnection redisConnection;
private PlayerCache playerCache;
@@ -146,7 +146,7 @@ public class Plex extends JavaPlugin
// Don't add default entries to these files
indefBans.load(false);
sqlConnection = new SQLConnection(this);
database = new Database(this);
redisConnection = new RedisConnection(this);
playerCache = new PlayerCache();
@@ -220,10 +220,10 @@ public class Plex extends JavaPlugin
PlexLog.log("Redis is disabled in the configuration file, not connecting.");
}
punishmentRepository = new SQLPunishment(sqlConnection.getConnectionSource(), api.scheduler().asyncExecutor());
playerRepository = new SQLPlayerData(sqlConnection.getConnectionSource(), punishmentRepository);
playerModuleDataRepository = new SQLPlayerModuleData(sqlConnection, storageType);
noteRepository = new SQLNotes(sqlConnection.getConnectionSource(), api.scheduler().asyncExecutor());
punishmentRepository = new SQLPunishment(database.getJdbi(), api.scheduler().asyncExecutor());
playerRepository = new SQLPlayerData(database.getJdbi(), punishmentRepository, storageType);
playerModuleDataRepository = new SQLPlayerModuleData(database.getJdbi(), storageType);
noteRepository = new SQLNotes(database.getJdbi(), api.scheduler().asyncExecutor());
playerService = new PlayerService(playerCache, playerRepository);
playerNameResolver = new PlayerNameResolver(playerService);
@@ -275,9 +275,9 @@ public class Plex extends JavaPlugin
moduleManager.disableModules();
if (sqlConnection != null)
if (database != null)
{
sqlConnection.close();
database.close();
}
}
@@ -18,7 +18,7 @@ final class DefaultStorageApi implements StorageApi
@Override
public <T> T withConnection(SqlFunction<T> function) throws SQLException
{
try (Connection connection = plugin.getSqlConnection().getCon())
try (Connection connection = plugin.getDatabase().getConnection())
{
return function.apply(connection);
}
@@ -1,22 +0,0 @@
package dev.plex.storage;
import dev.plex.Plex;
import dev.plex.storage.database.Database;
/**
* Database bootstrap and connection holder.
*
* <p>The historical name is kept so existing module-facing accessors do not break.</p>
*/
public class SQLConnection extends Database
{
public SQLConnection(Plex plugin)
{
super(plugin);
}
public java.sql.Connection getCon() throws java.sql.SQLException
{
return getConnection();
}
}
@@ -134,6 +134,33 @@ public enum StorageType
};
}
public String playerUpsertSql()
{
return switch (this)
{
case SQLITE, POSTGRES -> """
INSERT INTO players (uuid, last_known_name, login_msg, prefix, staffChat, commandspy)
VALUES (:uuid, :name, :login, :prefix, :staffChat, :commandSpy)
ON CONFLICT(uuid) DO UPDATE SET
last_known_name = excluded.last_known_name,
login_msg = excluded.login_msg,
prefix = excluded.prefix,
staffChat = excluded.staffChat,
commandspy = excluded.commandspy
""";
case MARIADB -> """
INSERT INTO `players` (`uuid`, `last_known_name`, `login_msg`, `prefix`, `staffChat`, `commandspy`)
VALUES (:uuid, :name, :login, :prefix, :staffChat, :commandSpy)
ON DUPLICATE KEY UPDATE
`last_known_name` = VALUES(`last_known_name`),
`login_msg` = VALUES(`login_msg`),
`prefix` = VALUES(`prefix`),
`staffChat` = VALUES(`staffChat`),
`commandspy` = VALUES(`commandspy`)
""";
};
}
public String getDisplayName()
{
return displayName;
@@ -1,13 +1,11 @@
package dev.plex.storage.database;
import com.j256.ormlite.jdbc.DataSourceConnectionSource;
import com.j256.ormlite.support.ConnectionSource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import dev.plex.Plex;
import dev.plex.storage.StorageType;
import dev.plex.util.PlexLog;
import lombok.Getter;
import org.jdbi.v3.core.Jdbi;
import java.util.List;
@@ -16,7 +14,7 @@ public class Database
{
protected final Plex plugin;
private final HikariDataSource dataSource;
private final ConnectionSource connectionSource;
private final Jdbi jdbi;
private final StorageType storageType;
private final MigrationRunner migrationRunner;
@@ -39,9 +37,9 @@ public class Database
this.dataSource = new HikariDataSource(config);
try
{
this.connectionSource = new DataSourceConnectionSource(dataSource, config.getJdbcUrl());
this.migrationRunner = new MigrationRunner(storageType);
this.migrationRunner.runCore(dataSource, getClass().getClassLoader(), List.of("001_initial_schema"));
this.jdbi = Jdbi.create(dataSource);
}
catch (Exception e)
{
@@ -57,14 +55,6 @@ public class Database
public void close()
{
try
{
connectionSource.close();
}
catch (Exception e)
{
PlexLog.warn("Failed to close ORMLite connection source: " + e.getMessage());
}
dataSource.close();
}
}
@@ -1,31 +1,17 @@
package dev.plex.storage.database.entity;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@DatabaseTable(tableName = "notes")
public class NoteEntity
{
@DatabaseField(generatedId = true, columnName = "row_id")
private long rowId;
@DatabaseField(columnName = "id", index = true)
private int id;
@DatabaseField(columnName = "uuid", canBeNull = false, index = true, width = 46)
private String uuid;
@DatabaseField(columnName = "written_by_uuid", width = 46)
private String writtenByUuid;
@DatabaseField(columnName = "note", width = 2000)
private String note;
@DatabaseField(columnName = "timestamp")
private long timestamp;
public NoteEntity()
@@ -1,34 +1,17 @@
package dev.plex.storage.database.entity;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@DatabaseTable(tableName = "players")
public class PlayerEntity
{
@DatabaseField(id = true, columnName = "uuid", width = 46)
private String uuid;
@DatabaseField(columnName = "last_known_name", width = 18, index = true)
private String lastKnownName;
@DatabaseField(columnName = "login_msg", width = 2000)
private String loginMessage;
@DatabaseField(columnName = "prefix", width = 2000)
private String prefix;
@DatabaseField(columnName = "staffChat")
private boolean staffChat;
@DatabaseField(columnName = "ips", width = 2000)
private String ips;
@DatabaseField(columnName = "commandspy")
private boolean commandSpy;
public PlayerEntity()
@@ -1,22 +1,14 @@
package dev.plex.storage.database.entity;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@DatabaseTable(tableName = "player_ips")
public class PlayerIpEntity
{
@DatabaseField(generatedId = true, columnName = "id")
private long id;
@DatabaseField(columnName = "player_uuid", canBeNull = false, index = true, width = 46)
private String playerUuid;
@DatabaseField(columnName = "ip", canBeNull = false, index = true, width = 64)
private String ip;
public PlayerIpEntity()
@@ -1,49 +1,23 @@
package dev.plex.storage.database.entity;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@DatabaseTable(tableName = "punishments")
public class PunishmentEntity
{
@DatabaseField(generatedId = true, columnName = "id")
private long id;
@DatabaseField(columnName = "punished_uuid", canBeNull = false, index = true, width = 46)
private String punishedUuid;
@DatabaseField(columnName = "punisher_uuid", width = 46)
private String punisherUuid;
@DatabaseField(columnName = "source", width = 20)
private String source;
@DatabaseField(columnName = "punisher_reference", width = 200)
private String punisherReference;
@DatabaseField(columnName = "ip", width = 2000, index = true)
private String ip;
@DatabaseField(columnName = "type", width = 30)
private String type;
@DatabaseField(columnName = "reason", width = 2000)
private String reason;
@DatabaseField(columnName = "customTime")
private boolean customTime;
@DatabaseField(columnName = "active", index = true)
private boolean active;
@DatabaseField(columnName = "issueDate")
private long issueDate;
@DatabaseField(columnName = "endDate")
private long endDate;
public PunishmentEntity()
@@ -29,8 +29,8 @@ public class ServerModuleMigrations implements ModuleMigrations
@Override
public void run(String resourceRoot, List<String> versions) throws SQLException
{
plugin.getSqlConnection().getMigrationRunner().runModule(
plugin.getSqlConnection().getDataSource(),
plugin.getDatabase().getMigrationRunner().runModule(
plugin.getDatabase().getDataSource(),
module,
storage.scope(),
resourceRoot,
@@ -1,42 +0,0 @@
package dev.plex.storage.module;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.table.DatabaseTableConfig;
import dev.plex.api.storage.ModuleOrm;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ServerModuleOrm implements ModuleOrm
{
private final ConnectionSource connectionSource;
private final ServerModuleStorage storage;
private final Map<String, Dao<?, ?>> daos = new ConcurrentHashMap<>();
public ServerModuleOrm(ConnectionSource connectionSource, ServerModuleStorage storage)
{
this.connectionSource = connectionSource;
this.storage = storage;
}
@Override
@SuppressWarnings("unchecked")
public <T, ID> Dao<T, ID> dao(Class<T> entityClass, String localTableName) throws SQLException
{
String key = entityClass.getName() + ":" + localTableName;
Dao<?, ?> existing = daos.get(key);
if (existing != null)
{
return (Dao<T, ID>) existing;
}
DatabaseTableConfig<T> tableConfig = DatabaseTableConfig.fromClass(connectionSource.getDatabaseType(), entityClass);
tableConfig.setTableName(storage.table(localTableName));
Dao<T, ID> dao = DaoManager.createDao(connectionSource, tableConfig);
daos.put(key, dao);
return dao;
}
}
@@ -1,14 +1,10 @@
package dev.plex.storage.module;
import com.j256.ormlite.misc.TransactionManager;
import dev.plex.Plex;
import dev.plex.api.storage.ModuleMigrations;
import dev.plex.api.storage.ModuleOrm;
import dev.plex.api.storage.ModuleStorage;
import dev.plex.api.storage.SqlCallable;
import dev.plex.module.PlexModule;
import java.sql.SQLException;
import org.jdbi.v3.core.Jdbi;
public class ServerModuleStorage implements ModuleStorage
{
@@ -16,7 +12,6 @@ public class ServerModuleStorage implements ModuleStorage
private final PlexModule module;
private final String prefix;
private final ModuleMigrations migrations;
private final ModuleOrm orm;
public ServerModuleStorage(Plex plugin, PlexModule module)
{
@@ -24,7 +19,6 @@ public class ServerModuleStorage implements ModuleStorage
this.module = module;
this.prefix = ModuleNames.prefix(module);
this.migrations = new ServerModuleMigrations(plugin, module, this);
this.orm = new ServerModuleOrm(plugin.getSqlConnection().getConnectionSource(), this);
}
@Override
@@ -56,14 +50,8 @@ public class ServerModuleStorage implements ModuleStorage
}
@Override
public ModuleOrm orm()
public Jdbi jdbi()
{
return orm;
}
@Override
public <T> T transaction(SqlCallable<T> callable) throws SQLException
{
return TransactionManager.callInTransaction(plugin.getSqlConnection().getConnectionSource(), callable::call);
return plugin.getDatabase().getJdbi();
}
}
@@ -1,54 +1,44 @@
package dev.plex.storage.player;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.stmt.DeleteBuilder;
import dev.plex.player.PlexPlayer;
import dev.plex.storage.StorageType;
import dev.plex.storage.database.entity.PlayerEntity;
import dev.plex.storage.database.entity.PlayerIpEntity;
import dev.plex.storage.repository.PlayerRepository;
import dev.plex.storage.repository.PunishmentRepository;
import dev.plex.util.PlexLog;
import org.jdbi.v3.core.Handle;
import org.jdbi.v3.core.Jdbi;
import org.jdbi.v3.core.JdbiException;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
/**
* Player persistence backed by ORMLite.
* Player persistence backed by JDBI.
*/
public class SQLPlayerData implements PlayerRepository
{
private static final Gson GSON = new Gson();
private final Dao<PlayerEntity, String> players;
private final Dao<PlayerIpEntity, Long> playerIps;
private final Jdbi jdbi;
private final PunishmentRepository punishmentRepository;
private final StorageType storageType;
public SQLPlayerData(ConnectionSource connectionSource, PunishmentRepository punishmentRepository)
public SQLPlayerData(Jdbi jdbi, PunishmentRepository punishmentRepository, StorageType storageType)
{
this.jdbi = jdbi;
this.punishmentRepository = punishmentRepository;
try
{
this.players = DaoManager.createDao(connectionSource, PlayerEntity.class);
this.playerIps = DaoManager.createDao(connectionSource, PlayerIpEntity.class);
}
catch (SQLException e)
{
throw new IllegalStateException("Failed to create player DAOs", e);
}
this.storageType = storageType;
}
public boolean exists(UUID uuid)
{
try
{
return players.idExists(uuid.toString());
return jdbi.withHandle(h -> h.createQuery("SELECT 1 FROM players WHERE uuid = :u")
.bind("u", uuid.toString()).mapTo(Integer.class).findFirst().isPresent());
}
catch (SQLException e)
catch (JdbiException e)
{
e.printStackTrace();
PlexLog.warn("Failed to check player existence for {0}: {1}", uuid, e.getMessage());
return false;
}
}
@@ -57,11 +47,12 @@ public class SQLPlayerData implements PlayerRepository
{
try
{
return players.queryBuilder().where().eq("last_known_name", username).queryForFirst() != null;
return jdbi.withHandle(h -> h.createQuery("SELECT 1 FROM players WHERE last_known_name = :n")
.bind("n", username).mapTo(Integer.class).findFirst().isPresent());
}
catch (SQLException e)
catch (JdbiException e)
{
e.printStackTrace();
PlexLog.warn("Failed to check player existence for {0}: {1}", username, e.getMessage());
return false;
}
}
@@ -70,11 +61,16 @@ public class SQLPlayerData implements PlayerRepository
{
try
{
return toPlayer(players.queryForId(uuid.toString()), loadExtraData);
return jdbi.withHandle(h ->
{
PlayerEntity e = h.createQuery("SELECT * FROM players WHERE uuid = :u")
.bind("u", uuid.toString()).map((rs, ctx) -> mapRow(rs)).findFirst().orElse(null);
return toPlayer(h, e, loadExtraData);
});
}
catch (SQLException e)
catch (JdbiException e)
{
e.printStackTrace();
PlexLog.warn("Failed to load player by UUID {0}: {1}", uuid, e.getMessage());
return null;
}
}
@@ -83,12 +79,12 @@ public class SQLPlayerData implements PlayerRepository
{
try
{
PlayerEntity entity = players.queryForId(uuid.toString());
return entity == null ? null : entity.getLastKnownName();
return jdbi.withHandle(h -> h.createQuery("SELECT last_known_name FROM players WHERE uuid = :u")
.bind("u", uuid.toString()).mapTo(String.class).findFirst().orElse(null));
}
catch (SQLException e)
catch (JdbiException e)
{
e.printStackTrace();
PlexLog.warn("Failed to load player name by UUID {0}: {1}", uuid, e.getMessage());
return null;
}
}
@@ -102,11 +98,16 @@ public class SQLPlayerData implements PlayerRepository
{
try
{
return toPlayer(players.queryBuilder().limit(1L).where().eq("last_known_name", username).queryForFirst(), loadExtraData);
return jdbi.withHandle(h ->
{
PlayerEntity e = h.createQuery("SELECT * FROM players WHERE last_known_name = :n LIMIT 1")
.bind("n", username).map((rs, ctx) -> mapRow(rs)).findFirst().orElse(null);
return toPlayer(h, e, loadExtraData);
});
}
catch (SQLException e)
catch (JdbiException e)
{
e.printStackTrace();
PlexLog.warn("Failed to load player by name {0}: {1}", username, e.getMessage());
return null;
}
}
@@ -120,39 +121,46 @@ public class SQLPlayerData implements PlayerRepository
{
try
{
PlayerIpEntity playerIp = playerIps.queryBuilder().limit(1L).where().eq("ip", ip).queryForFirst();
if (playerIp != null)
return jdbi.withHandle(h ->
{
return toPlayer(players.queryForId(playerIp.getPlayerUuid()), true);
}
for (PlayerEntity entity : players.queryForAll())
{
List<String> ips = parseIps(entity.getIps());
if (ips.contains(ip))
String uuid = h.createQuery("SELECT player_uuid FROM player_ips WHERE ip = :ip LIMIT 1")
.bind("ip", ip).mapTo(String.class).findFirst().orElse(null);
if (uuid == null)
{
syncIps(entity.getUuid(), ips);
return toPlayer(entity, true);
return null;
}
}
PlayerEntity e = h.createQuery("SELECT * FROM players WHERE uuid = :u")
.bind("u", uuid).map((rs, ctx) -> mapRow(rs)).findFirst().orElse(null);
return toPlayer(h, e, true);
});
}
catch (SQLException e)
catch (JdbiException e)
{
e.printStackTrace();
PlexLog.warn("Failed to load player by IP {0}: {1}", ip, e.getMessage());
return null;
}
return null;
}
public void update(PlexPlayer player)
{
try
{
players.createOrUpdate(toEntity(player));
syncIps(player.getUuid().toString(), player.getIps());
jdbi.useTransaction(h ->
{
h.createUpdate(storageType.playerUpsertSql())
.bind("uuid", player.getUuid().toString())
.bind("name", player.getName())
.bind("login", player.getLoginMessage())
.bind("prefix", player.getPrefix())
.bind("staffChat", player.isStaffChat())
.bind("commandSpy", player.isCommandSpy())
.execute();
syncIps(h, player.getUuid().toString(), player.getIps());
});
}
catch (SQLException e)
catch (JdbiException e)
{
e.printStackTrace();
PlexLog.warn("Failed to update player {0}: {1}", player.getUuid(), e.getMessage());
}
}
@@ -161,7 +169,35 @@ public class SQLPlayerData implements PlayerRepository
update(player);
}
private PlexPlayer toPlayer(PlayerEntity entity, boolean loadExtraData)
private static PlayerEntity mapRow(java.sql.ResultSet rs) throws java.sql.SQLException
{
PlayerEntity e = new PlayerEntity();
e.setUuid(rs.getString("uuid"));
e.setLastKnownName(rs.getString("last_known_name"));
e.setLoginMessage(rs.getString("login_msg"));
e.setPrefix(rs.getString("prefix"));
e.setStaffChat(rs.getBoolean("staffChat"));
e.setCommandSpy(rs.getBoolean("commandspy"));
return e;
}
private List<String> loadIps(Handle h, String uuid)
{
return h.createQuery("SELECT ip FROM player_ips WHERE player_uuid = :u")
.bind("u", uuid).mapTo(String.class).list();
}
private void syncIps(Handle h, String playerUuid, List<String> ips)
{
h.createUpdate("DELETE FROM player_ips WHERE player_uuid = :u").bind("u", playerUuid).execute();
for (String ip : ips.stream().distinct().toList())
{
h.createUpdate("INSERT INTO player_ips (player_uuid, ip) VALUES (:u, :ip)")
.bind("u", playerUuid).bind("ip", ip).execute();
}
}
private PlexPlayer toPlayer(Handle h, PlayerEntity entity, boolean loadExtraData)
{
if (entity == null)
{
@@ -173,7 +209,7 @@ public class SQLPlayerData implements PlayerRepository
plexPlayer.setLoginMessage(entity.getLoginMessage());
plexPlayer.setPrefix(entity.getPrefix());
plexPlayer.setStaffChat(entity.isStaffChat());
plexPlayer.setIps(parseIps(entity.getIps()));
plexPlayer.setIps(loadIps(h, entity.getUuid()));
plexPlayer.setCommandSpy(entity.isCommandSpy());
if (loadExtraData)
{
@@ -182,41 +218,4 @@ public class SQLPlayerData implements PlayerRepository
}
return plexPlayer;
}
private PlayerEntity toEntity(PlexPlayer player)
{
PlayerEntity entity = new PlayerEntity();
entity.setUuid(player.getUuid().toString());
entity.setLastKnownName(player.getName());
entity.setLoginMessage(player.getLoginMessage());
entity.setPrefix(player.getPrefix());
entity.setStaffChat(player.isStaffChat());
entity.setIps(GSON.toJson(player.getIps()));
entity.setCommandSpy(player.isCommandSpy());
return entity;
}
private List<String> parseIps(String ips)
{
if (ips == null || ips.isBlank())
{
return List.of();
}
List<String> parsed = GSON.fromJson(ips, new TypeToken<List<String>>()
{
}.getType());
return parsed == null ? List.of() : parsed;
}
private void syncIps(String playerUuid, List<String> ips) throws SQLException
{
DeleteBuilder<PlayerIpEntity, Long> delete = playerIps.deleteBuilder();
delete.where().eq("player_uuid", playerUuid);
delete.delete();
for (String ip : ips.stream().distinct().toList())
{
playerIps.create(new PlayerIpEntity(playerUuid, ip));
}
}
}
@@ -3,48 +3,41 @@ package dev.plex.storage.player;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import dev.plex.storage.SQLConnection;
import dev.plex.storage.StorageType;
import dev.plex.util.PlexLog;
import org.jdbi.v3.core.Jdbi;
import org.jdbi.v3.core.JdbiException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Optional;
import java.util.UUID;
public class SQLPlayerModuleData implements PlayerModuleDataRepository
{
private final SQLConnection sqlConnection;
private final Jdbi jdbi;
private final StorageType storageType;
public SQLPlayerModuleData(SQLConnection sqlConnection, StorageType storageType)
public SQLPlayerModuleData(Jdbi jdbi, StorageType storageType)
{
this.sqlConnection = sqlConnection;
this.jdbi = jdbi;
this.storageType = storageType;
}
@Override
public Optional<JsonElement> get(UUID playerUuid, String module, String key)
{
String sql = "SELECT value_json FROM player_module_data WHERE player_uuid = ? AND module = ? AND data_key = ?";
try (Connection connection = sqlConnection.getConnection(); PreparedStatement statement = connection.prepareStatement(sql))
try
{
statement.setString(1, playerUuid.toString());
statement.setString(2, module);
statement.setString(3, key);
try (ResultSet resultSet = statement.executeQuery())
{
if (!resultSet.next())
{
return Optional.empty();
}
return Optional.of(JsonParser.parseString(resultSet.getString("value_json")));
}
return jdbi.withHandle(h -> h.createQuery(
"SELECT value_json FROM player_module_data WHERE player_uuid = :p AND module = :m AND data_key = :k")
.bind("p", playerUuid.toString())
.bind("m", module)
.bind("k", key)
.mapTo(String.class).findFirst())
.map(JsonParser::parseString);
}
catch (SQLException | JsonSyntaxException e)
catch (JdbiException | JsonSyntaxException e)
{
e.printStackTrace();
PlexLog.warn("Failed to load player module data {0}/{1}/{2}: {3}", playerUuid, module, key, e.getMessage());
return Optional.empty();
}
}
@@ -52,35 +45,37 @@ public class SQLPlayerModuleData implements PlayerModuleDataRepository
@Override
public void set(UUID playerUuid, String module, String key, JsonElement value)
{
try (Connection connection = sqlConnection.getConnection(); PreparedStatement statement = connection.prepareStatement(storageType.playerModuleDataUpsertSql()))
try
{
statement.setString(1, playerUuid.toString());
statement.setString(2, module);
statement.setString(3, key);
statement.setString(4, value.toString());
statement.setLong(5, System.currentTimeMillis());
statement.executeUpdate();
jdbi.useHandle(h -> h.createUpdate(storageType.playerModuleDataUpsertSql())
.bind(0, playerUuid.toString())
.bind(1, module)
.bind(2, key)
.bind(3, value.toString())
.bind(4, System.currentTimeMillis())
.execute());
}
catch (SQLException e)
catch (JdbiException e)
{
e.printStackTrace();
PlexLog.warn("Failed to save player module data {0}/{1}/{2}: {3}", playerUuid, module, key, e.getMessage());
}
}
@Override
public void remove(UUID playerUuid, String module, String key)
{
String sql = "DELETE FROM player_module_data WHERE player_uuid = ? AND module = ? AND data_key = ?";
try (Connection connection = sqlConnection.getConnection(); PreparedStatement statement = connection.prepareStatement(sql))
try
{
statement.setString(1, playerUuid.toString());
statement.setString(2, module);
statement.setString(3, key);
statement.executeUpdate();
jdbi.useHandle(h -> h.createUpdate(
"DELETE FROM player_module_data WHERE player_uuid = :p AND module = :m AND data_key = :k")
.bind("p", playerUuid.toString())
.bind("m", module)
.bind("k", key)
.execute());
}
catch (SQLException e)
catch (JdbiException e)
{
e.printStackTrace();
PlexLog.warn("Failed to remove player module data {0}/{1}/{2}: {3}", playerUuid, module, key, e.getMessage());
}
}
}
@@ -1,16 +1,14 @@
package dev.plex.storage.punishment;
import com.google.common.collect.Lists;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.stmt.DeleteBuilder;
import dev.plex.punishment.extra.Note;
import dev.plex.storage.database.entity.NoteEntity;
import dev.plex.storage.repository.NoteRepository;
import dev.plex.util.PlexLog;
import dev.plex.util.TimeUtils;
import org.jdbi.v3.core.Jdbi;
import org.jdbi.v3.core.JdbiException;
import java.sql.SQLException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
@@ -23,20 +21,13 @@ import java.util.concurrent.Executor;
public class SQLNotes implements NoteRepository
{
private final Dao<NoteEntity, Long> notes;
private final Jdbi jdbi;
private final Executor executor;
public SQLNotes(ConnectionSource connectionSource, Executor executor)
public SQLNotes(Jdbi jdbi, Executor executor)
{
try
{
this.notes = DaoManager.createDao(connectionSource, NoteEntity.class);
this.executor = executor;
}
catch (SQLException e)
{
throw new IllegalStateException("Failed to create note DAO", e);
}
this.jdbi = jdbi;
this.executor = executor;
}
public CompletableFuture<List<Note>> getNotes(UUID uuid)
@@ -45,15 +36,16 @@ public class SQLNotes implements NoteRepository
{
try
{
return notes.queryForEq("uuid", uuid.toString()).stream()
return jdbi.withHandle(h -> h.createQuery("SELECT * FROM notes WHERE uuid = :u")
.bind("u", uuid.toString()).map((rs, ctx) -> mapRow(rs)).list()).stream()
.sorted(Comparator.comparingInt(NoteEntity::getId))
.map(this::toNote)
.flatMap(Optional::stream)
.toList();
}
catch (SQLException e)
catch (JdbiException e)
{
e.printStackTrace();
PlexLog.warn("Failed to load notes for {0}: {1}", uuid, e.getMessage());
return Lists.newArrayList();
}
}, executor);
@@ -65,13 +57,14 @@ public class SQLNotes implements NoteRepository
{
try
{
DeleteBuilder<NoteEntity, Long> delete = notes.deleteBuilder();
delete.where().eq("uuid", uuid.toString()).and().eq("id", id);
delete.delete();
jdbi.useHandle(h -> h.createUpdate("DELETE FROM notes WHERE uuid = :u AND id = :id")
.bind("u", uuid.toString())
.bind("id", id)
.execute());
}
catch (SQLException e)
catch (JdbiException e)
{
e.printStackTrace();
PlexLog.warn("Failed to delete note {0} for {1}: {2}", id, uuid, e.getMessage());
}
}, executor);
}
@@ -82,22 +75,40 @@ public class SQLNotes implements NoteRepository
{
try
{
int nextId = notes.queryForEq("uuid", note.getUuid().toString()).stream()
.map(NoteEntity::getId)
.max(Integer::compareTo)
.orElse(0) + 1;
int nextId = jdbi.withHandle(h -> h.createQuery("SELECT COALESCE(MAX(id), 0) FROM notes WHERE uuid = :u")
.bind("u", note.getUuid().toString()).mapTo(Integer.class).one()) + 1;
NoteEntity entity = toEntity(note);
entity.setId(nextId);
notes.create(entity);
jdbi.useHandle(h -> h.createUpdate(
"INSERT INTO notes (id, uuid, written_by_uuid, note, timestamp) " +
"VALUES (:id, :uuid, :writtenBy, :note, :ts)")
.bind("id", entity.getId())
.bind("uuid", entity.getUuid())
.bind("writtenBy", entity.getWrittenByUuid())
.bind("note", entity.getNote())
.bind("ts", entity.getTimestamp())
.execute());
note.setId(nextId);
}
catch (SQLException e)
catch (JdbiException e)
{
e.printStackTrace();
PlexLog.warn("Failed to add note for {0}: {1}", note.getUuid(), e.getMessage());
}
}, executor);
}
private static NoteEntity mapRow(java.sql.ResultSet rs) throws java.sql.SQLException
{
NoteEntity e = new NoteEntity();
e.setRowId(rs.getLong("row_id"));
e.setId(rs.getInt("id"));
e.setUuid(rs.getString("uuid"));
e.setWrittenByUuid(rs.getString("written_by_uuid"));
e.setNote(rs.getString("note"));
e.setTimestamp(rs.getLong("timestamp"));
return e;
}
private Optional<Note> toNote(NoteEntity entity)
{
try
@@ -1,10 +1,6 @@
package dev.plex.storage.punishment;
import com.google.common.collect.Lists;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.stmt.UpdateBuilder;
import dev.plex.api.punishment.PunishmentSource;
import dev.plex.punishment.Punishment;
import dev.plex.punishment.PunishmentType;
@@ -12,8 +8,9 @@ import dev.plex.storage.database.entity.PunishmentEntity;
import dev.plex.storage.repository.PunishmentRepository;
import dev.plex.util.PlexLog;
import dev.plex.util.TimeUtils;
import org.jdbi.v3.core.Jdbi;
import org.jdbi.v3.core.JdbiException;
import java.sql.SQLException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
@@ -24,20 +21,13 @@ import java.util.concurrent.Executor;
public class SQLPunishment implements PunishmentRepository
{
private final Dao<PunishmentEntity, Long> punishments;
private final Jdbi jdbi;
private final Executor executor;
public SQLPunishment(ConnectionSource connectionSource, Executor executor)
public SQLPunishment(Jdbi jdbi, Executor executor)
{
try
{
this.punishments = DaoManager.createDao(connectionSource, PunishmentEntity.class);
this.executor = executor;
}
catch (SQLException e)
{
throw new IllegalStateException("Failed to create punishment DAO", e);
}
this.jdbi = jdbi;
this.executor = executor;
}
public CompletableFuture<List<Punishment>> getPunishments()
@@ -46,11 +36,12 @@ public class SQLPunishment implements PunishmentRepository
{
try
{
return punishments.queryForAll().stream().map(this::toPunishment).toList();
return jdbi.withHandle(h -> h.createQuery("SELECT * FROM punishments")
.map((rs, ctx) -> mapRow(rs)).list()).stream().map(this::toPunishment).toList();
}
catch (SQLException e)
catch (JdbiException e)
{
e.printStackTrace();
PlexLog.warn("Failed to load punishments: {0}", e.getMessage());
return Lists.newArrayList();
}
}, executor);
@@ -60,11 +51,13 @@ public class SQLPunishment implements PunishmentRepository
{
try
{
return punishments.queryForEq("punished_uuid", uuid.toString()).stream().map(this::toPunishment).toList();
return jdbi.withHandle(h -> h.createQuery("SELECT * FROM punishments WHERE punished_uuid = :u")
.bind("u", uuid.toString()).map((rs, ctx) -> mapRow(rs)).list())
.stream().map(this::toPunishment).toList();
}
catch (SQLException e)
catch (JdbiException e)
{
e.printStackTrace();
PlexLog.warn("Failed to load punishments for {0}: {1}", uuid, e.getMessage());
return Lists.newArrayList();
}
}
@@ -73,11 +66,13 @@ public class SQLPunishment implements PunishmentRepository
{
try
{
return punishments.queryForEq("ip", ip).stream().map(this::toPunishment).toList();
return jdbi.withHandle(h -> h.createQuery("SELECT * FROM punishments WHERE ip = :ip")
.bind("ip", ip).map((rs, ctx) -> mapRow(rs)).list())
.stream().map(this::toPunishment).toList();
}
catch (SQLException e)
catch (JdbiException e)
{
e.printStackTrace();
PlexLog.warn("Failed to load punishments for IP {0}: {1}", ip, e.getMessage());
return Lists.newArrayList();
}
}
@@ -89,11 +84,26 @@ public class SQLPunishment implements PunishmentRepository
try
{
PlexLog.debug("Persisting punishment for " + punishment.getPunished());
punishments.create(toEntity(punishment));
PunishmentEntity e = toEntity(punishment);
jdbi.useHandle(h -> h.createUpdate(
"INSERT INTO punishments (punished_uuid, punisher_uuid, source, punisher_reference, ip, type, reason, customTime, active, issueDate, endDate) " +
"VALUES (:punishedUuid, :punisherUuid, :source, :punisherReference, :ip, :type, :reason, :customTime, :active, :issueDate, :endDate)")
.bind("punishedUuid", e.getPunishedUuid())
.bind("punisherUuid", e.getPunisherUuid())
.bind("source", e.getSource())
.bind("punisherReference", e.getPunisherReference())
.bind("ip", e.getIp())
.bind("type", e.getType())
.bind("reason", e.getReason())
.bind("customTime", e.isCustomTime())
.bind("active", e.isActive())
.bind("issueDate", e.getIssueDate())
.bind("endDate", e.getEndDate())
.execute());
}
catch (SQLException e)
catch (JdbiException e)
{
e.printStackTrace();
PlexLog.warn("Failed to persist punishment for {0}: {1}", punishment.getPunished(), e.getMessage());
}
}, executor);
}
@@ -118,17 +128,37 @@ public class SQLPunishment implements PunishmentRepository
{
try
{
UpdateBuilder<PunishmentEntity, Long> update = punishments.updateBuilder();
update.updateColumnValue("active", active);
update.where().eq("punished_uuid", punished.toString()).and().eq("type", type.name());
update.update();
jdbi.useHandle(h -> h.createUpdate(
"UPDATE punishments SET active = :active WHERE punished_uuid = :u AND type = :t")
.bind("active", active)
.bind("u", punished.toString())
.bind("t", type.name())
.execute());
}
catch (SQLException e)
catch (JdbiException e)
{
e.printStackTrace();
PlexLog.warn("Failed to update punishment state for {0}: {1}", punished, e.getMessage());
}
}
private static PunishmentEntity mapRow(java.sql.ResultSet rs) throws java.sql.SQLException
{
PunishmentEntity e = new PunishmentEntity();
e.setId(rs.getLong("id"));
e.setPunishedUuid(rs.getString("punished_uuid"));
e.setPunisherUuid(rs.getString("punisher_uuid"));
e.setSource(rs.getString("source"));
e.setPunisherReference(rs.getString("punisher_reference"));
e.setIp(rs.getString("ip"));
e.setType(rs.getString("type"));
e.setReason(rs.getString("reason"));
e.setCustomTime(rs.getBoolean("customTime"));
e.setActive(rs.getBoolean("active"));
e.setIssueDate(rs.getLong("issueDate"));
e.setEndDate(rs.getLong("endDate"));
return e;
}
private Punishment toPunishment(PunishmentEntity entity)
{
UUID punisher = entity.getPunisherUuid() == null || entity.getPunisherUuid().isBlank() ? null : UUID.fromString(entity.getPunisherUuid());
@@ -88,9 +88,9 @@ public class PlexUtils
public static void testConnections(Plex plugin)
{
if (plugin.getSqlConnection().getDataSource() != null)
if (plugin.getDatabase().getDataSource() != null)
{
try (Connection ignored = plugin.getSqlConnection().getCon())
try (Connection ignored = plugin.getDatabase().getConnection())
{
PlexLog.log("Successfully enabled " + plugin.getStorageType().getDisplayName() + "!");
}
@@ -4,7 +4,6 @@ CREATE TABLE IF NOT EXISTS `players` (
`login_msg` VARCHAR(2000),
`prefix` VARCHAR(2000),
`staffChat` BOOLEAN,
`ips` VARCHAR(2000),
`commandspy` BOOLEAN,
PRIMARY KEY (`uuid`),
INDEX `idx_players_last_known_name` (`last_known_name`)
@@ -4,7 +4,6 @@ CREATE TABLE IF NOT EXISTS players (
login_msg VARCHAR(2000),
prefix VARCHAR(2000),
staffChat BOOLEAN,
ips VARCHAR(2000),
commandspy BOOLEAN
);
@@ -4,7 +4,6 @@ CREATE TABLE IF NOT EXISTS players (
login_msg VARCHAR(2000),
prefix VARCHAR(2000),
staffChat BOOLEAN,
ips VARCHAR(2000),
commandspy BOOLEAN
);