Don't create backing tables on startup

This commit is contained in:
cnr 2016-05-25 10:25:49 -05:00
parent 5331e2bec9
commit 1c66c10293
1 changed files with 28 additions and 40 deletions

View File

@ -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;
* <p>
* {@code new PlayerKeyValueRepository("tableName", PreparedStatement::setString, ResultSet::getString, "VARCHAR(255)")}
* <p>
* 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):
* <p><blockquote><pre>
* 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)
* )}
* </pre></blockquote></p>
*
* @param <V> The value type to use for this repository
*/
public class PlayerKeyValueRepository<V>
{
private static final ImmutableMap<Class<?>, ValueMapper<?>> PRIM_MAPPERS = ImmutableMap.<Class<?>, 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<V> _mapper;
@ -51,7 +61,11 @@ public class PlayerKeyValueRepository<V>
@SuppressWarnings("unchecked") // java's generics are garbage.
public PlayerKeyValueRepository(String tableName, Class<V> clazz) // we could infer the type parameter at runtime, but it's super ugly
{
this(tableName, (ValueMapper<V>) PRIM_MAPPERS.get(clazz));
ValueMapper<V> mapper = (ValueMapper<V>) 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<V>
* @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<V> serializer, Deserializer<V> deserializer, String columnDef)
{
this(tableName, new ValueMapper<V>(serializer, deserializer, columnDef));
}
private PlayerKeyValueRepository(String tableName, ValueMapper<V> mapper)
public PlayerKeyValueRepository(String tableName, Serializer<V> serializer, Deserializer<V> 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<V>(serializer, deserializer);
}
/**
@ -279,13 +269,11 @@ public class PlayerKeyValueRepository<V>
{
private final Serializer<V> _serializer;
private final Deserializer<V> _deserializer;
private final String _columnDef;
private ValueMapper(Serializer<V> serializer, Deserializer<V> deserializer, String columnDef)
private ValueMapper(Serializer<V> serializer, Deserializer<V> deserializer)
{
_serializer = serializer;
_deserializer = deserializer;
_columnDef = columnDef;
}
}