diff --git a/Plugins/Mineplex.Core/src/mineplex/core/database/PlayerKeyValueRepository.java b/Plugins/Mineplex.Core/src/mineplex/core/database/PlayerKeyValueRepository.java index 74efc5086..9325679ae 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/database/PlayerKeyValueRepository.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/database/PlayerKeyValueRepository.java @@ -1,5 +1,6 @@ package mineplex.core.database; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import mineplex.serverdata.database.DBPool; @@ -20,22 +21,31 @@ import java.util.concurrent.CompletableFuture; *

* {@code new PlayerKeyValueRepository("tableName", PreparedStatement::setString, ResultSet::getString, "VARCHAR(255)")} *

- * NOTE: EACH CONSTRUCTOR IS BLOCKING, and initializes a backing table - * if one does not yet exist + * Compatible backing table schemas can be written as follows (replace $VARS as appropriate): + *

+ *     CREATE TABLE IF NOT EXISTS $TABLE_NAME (
+ *         accountId INT NOT NULL,
+ *         kvKey VARCHAR(255) NOT NULL,
+ *         kvValue $VALUE_COLUMN_TYPE
+ *         PRIMARY KEY (accountId,kvKey),
+ *         INDEX acc_ind (accountId),
+ *         FOREIGN KEY (accountId) REFERENCES accounts(id)
+ *     )}
+ * 

* * @param The value type to use for this repository */ public class PlayerKeyValueRepository { private static final ImmutableMap, ValueMapper> PRIM_MAPPERS = ImmutableMap., ValueMapper>builder() - .put(String.class, new ValueMapper<>(PreparedStatement::setString, ResultSet::getString, "VARCHAR(255)")) - .put(Boolean.class, new ValueMapper<>(PreparedStatement::setBoolean, ResultSet::getBoolean, "BOOL")) - .put(Byte.class, new ValueMapper<>(PreparedStatement::setByte, ResultSet::getByte, "TINYINT")) - .put(Short.class, new ValueMapper<>(PreparedStatement::setShort, ResultSet::getShort, "SMALLINT")) - .put(Integer.class, new ValueMapper<>(PreparedStatement::setInt, ResultSet::getInt, "INTEGER")) - .put(Long.class, new ValueMapper<>(PreparedStatement::setLong, ResultSet::getLong, "BIGINT")) - .put(Float.class, new ValueMapper<>(PreparedStatement::setFloat, ResultSet::getFloat, "REAL")) - .put(Double.class, new ValueMapper<>(PreparedStatement::setDouble, ResultSet::getDouble, "DOUBLE")) + .put(String.class, new ValueMapper<>(PreparedStatement::setString, ResultSet::getString)) + .put(Boolean.class, new ValueMapper<>(PreparedStatement::setBoolean, ResultSet::getBoolean)) + .put(Byte.class, new ValueMapper<>(PreparedStatement::setByte, ResultSet::getByte)) + .put(Short.class, new ValueMapper<>(PreparedStatement::setShort, ResultSet::getShort)) + .put(Integer.class, new ValueMapper<>(PreparedStatement::setInt, ResultSet::getInt)) + .put(Long.class, new ValueMapper<>(PreparedStatement::setLong, ResultSet::getLong)) + .put(Float.class, new ValueMapper<>(PreparedStatement::setFloat, ResultSet::getFloat)) + .put(Double.class, new ValueMapper<>(PreparedStatement::setDouble, ResultSet::getDouble)) .build(); private final String _tableName; private final ValueMapper _mapper; @@ -51,7 +61,11 @@ public class PlayerKeyValueRepository @SuppressWarnings("unchecked") // java's generics are garbage. public PlayerKeyValueRepository(String tableName, Class clazz) // we could infer the type parameter at runtime, but it's super ugly { - this(tableName, (ValueMapper) PRIM_MAPPERS.get(clazz)); + ValueMapper mapper = (ValueMapper) PRIM_MAPPERS.get(clazz); + Preconditions.checkNotNull(mapper, "Unsupported value type: " + clazz.getName() + ". (use the other constructor)"); + + this._tableName = tableName; + this._mapper = mapper; } /** @@ -63,35 +77,11 @@ public class PlayerKeyValueRepository * @param serializer the serializing function used to insert values * @param deserializer the deserializing function used to retrieve * values - * @param columnDef the value type's SQL datatype declaration, e.g., {@code "VARCHAR(255)"} for Strings. */ - public PlayerKeyValueRepository(String tableName, Serializer serializer, Deserializer deserializer, String columnDef) - { - this(tableName, new ValueMapper(serializer, deserializer, columnDef)); - } - - private PlayerKeyValueRepository(String tableName, ValueMapper mapper) + public PlayerKeyValueRepository(String tableName, Serializer serializer, Deserializer deserializer) { this._tableName = tableName; - this._mapper = mapper; - - // Create a table to back this repository - try (Connection conn = DBPool.getAccount().getConnection()) - { - Statement stmt = conn.createStatement(); - stmt.executeUpdate("CREATE TABLE IF NOT EXISTS " + _tableName + "(" - + "accountId INT NOT NULL," - + "kvKey VARCHAR(255) NOT NULL," - + "kvValue " + _mapper._columnDef + "," - + "PRIMARY KEY (accountId,kvKey)," - + "INDEX acc_ind (accountId)," - + "FOREIGN KEY (accountId) REFERENCES accounts(id) ON DELETE NO ACTION ON UPDATE NO ACTION" - + ")"); - } - catch (SQLException e) - { - e.printStackTrace(); - } + this._mapper = new ValueMapper(serializer, deserializer); } /** @@ -279,13 +269,11 @@ public class PlayerKeyValueRepository { private final Serializer _serializer; private final Deserializer _deserializer; - private final String _columnDef; - private ValueMapper(Serializer serializer, Deserializer deserializer, String columnDef) + private ValueMapper(Serializer serializer, Deserializer deserializer) { _serializer = serializer; _deserializer = deserializer; - _columnDef = columnDef; } }