Refactor NodeData

Signed-off-by: Chaoscaot <max@maxsp.de>
This commit is contained in:
2025-10-28 22:25:31 +01:00
parent 14e82f36a5
commit 6ea73f4890
5 changed files with 70 additions and 99 deletions
+57 -95
View File
@@ -17,121 +17,83 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
package de.steamwar.sql
import de.steamwar.sql.internal.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.SortOrder
import org.jetbrains.exposed.v1.core.dao.id.CompositeID
import org.jetbrains.exposed.v1.core.dao.id.CompositeIdTable
import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.statements.api.ExposedBlob
import org.jetbrains.exposed.v1.dao.CompositeEntity
import org.jetbrains.exposed.v1.dao.CompositeEntityClass
import org.jetbrains.exposed.v1.javatime.timestamp
import org.jetbrains.exposed.v1.jdbc.insertIgnore
import java.io.InputStream
import java.util.zip.GZIPInputStream
import javax.swing.plaf.nimbus.State;
import java.io.*;
import java.sql.PreparedStatement;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.zip.GZIPInputStream;
object NodeDataTable: CompositeIdTable("NodeData") {
val nodeId = reference("NodeId", SchematicNodeTable)
val createdAt = timestamp("CreatedAt").entityId()
val nodeFormat = enumeration("NodeFormat", NodeData.SchematicFormat::class)
val schemData = blob("SchemData")
@AllArgsConstructor
@Getter
public class NodeData {
static {
new SqlTypeMapper<>(PipedInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("PipedInputStream is write only datatype"); }, PreparedStatement::setBinaryStream);
new SqlTypeMapper<>(ByteArrayInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("ByteArrayInputStream is write only datatype"); }, PreparedStatement::setBinaryStream);
new SqlTypeMapper<>(BufferedInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("BufferedInputStream is write only datatype"); }, PreparedStatement::setBinaryStream);
override val primaryKey = PrimaryKey(nodeId, createdAt)
SqlTypeMapper.ordinalEnumMapper(SchematicFormat.class);
init {
addIdColumn(nodeId)
}
}
private static final Table<NodeData> table = new Table<>(NodeData.class);
class NodeData(id: EntityID<CompositeID>): CompositeEntity(id) {
val nodeId by NodeDataTable.nodeId
val createdAt by NodeDataTable.createdAt
val nodeFormat by NodeDataTable.nodeFormat
val schemData by NodeDataTable.schemData
private static final Statement updateDatabase = new Statement("INSERT INTO NodeData(NodeId, NodeFormat, SchemData) VALUES (?, ?, ?)", true);
private static final Statement selSchemData = new Statement("SELECT SchemData FROM NodeData WHERE NodeId = ? AND CreatedAt = ?");
private static final Statement delete = table.delete(Table.PRIMARY);
companion object: CompositeEntityClass<NodeData>(NodeDataTable) {
@JvmStatic
fun getLatest(node: SchematicNode) = useDb {
find { (NodeDataTable.nodeId eq node.nodeId) }.orderBy(NodeDataTable.createdAt to SortOrder.DESC).firstOrNull()
}
private static final SelectStatement<NodeData> get = new SelectStatement<>(table, "SELECT NodeId, CreatedAt, NodeFormat FROM NodeData WHERE NodeId = ? ORDER BY CreatedAt ");
private static final Statement getRevisions = new Statement("SELECT COUNT(DISTINCT CreatedAt) as CNT FROM NodeData WHERE NodeId = ?");
private static final SelectStatement<NodeData> getLatest = new SelectStatement<>(table, "SELECT NodeId, CreatedAt, NodeFormat FROM NodeData WHERE NodeId = ? ORDER BY CreatedAt DESC LIMIT 1");
@JvmStatic
fun get(node: SchematicNode) = useDb {
find { (NodeDataTable.nodeId eq node.nodeId) }.orderBy(NodeDataTable.createdAt to SortOrder.ASC).toList()
}
public static NodeData getLatest(SchematicNode node) {
if (node.isDir()) throw new IllegalArgumentException("Node is dir");
return Optional.ofNullable(getLatest.select(node)).orElseGet(() -> new NodeData(node.getId(), Timestamp.from(Instant.now()), SchematicFormat.MCEDIT));
}
@JvmStatic
fun get(node: SchematicNode, revision: Int) = useDb {
find { NodeDataTable.nodeId eq node.nodeId }.orderBy(NodeDataTable.createdAt to SortOrder.ASC).toList().get(revision - 1)
}
public static List<NodeData> get(SchematicNode node) {
return get.listSelect(node);
}
@JvmStatic
fun getRevisions(node: SchematicNode) = useDb {
count(NodeDataTable.nodeId eq node.nodeId).toInt()
}
public static NodeData get(SchematicNode node, int revision) {
return get.listSelect(node).get(revision - 1);
}
public static int getRevisions(SchematicNode node) {
return getRevisions.select(rs -> {
if (rs.next()) {
return rs.getInt("CNT");
} else {
return 0;
@JvmStatic
fun saveFromStream(node: SchematicNode, blob: InputStream, format: SchematicFormat) {
NodeDataTable.insertIgnore {
it[NodeDataTable.nodeId] = EntityID(node.getId(), SchematicNodeTable)
it[NodeDataTable.nodeFormat] = format
it[NodeDataTable.schemData] = ExposedBlob(blob)
}
}, node);
}
public static void saveFromStream(SchematicNode node, InputStream blob, SchematicFormat format) {
updateDatabase.update(node.getId(), format, blob);
}
@Field(keys = {Table.PRIMARY})
private final int nodeId;
@Field(keys = {Table.PRIMARY})
private Timestamp createdAt;
@Field
public SchematicFormat nodeFormat;
public InputStream schemData() throws IOException {
return schemData(true);
}
public InputStream schemData(boolean decompress) throws IOException {
try {
return selSchemData.select(rs -> {
rs.next();
InputStream schemData = rs.getBinaryStream("SchemData");
try {
if(rs.wasNull() || schemData.available() == 0) {
throw new SecurityException("SchemData is null");
}
if (decompress) {
return new GZIPInputStream(schemData);
} else {
return schemData;
}
} catch (IOException e) {
throw new SecurityException("SchemData is wrong", e);
}
}, nodeId, createdAt);
} catch (Exception e) {
throw new IOException(e);
}
}
@Deprecated
public void saveFromStream(InputStream blob, SchematicFormat newFormat) {
saveFromStream(SchematicNode.getSchematicNode(nodeId), blob, newFormat);
fun schemData(decompress: Boolean) = useDb {
schemData.inputStream.let { if(decompress) GZIPInputStream(it) else it }
}
public void delete() {
delete.update(nodeId, createdAt);
}
fun schemData() = schemData(true)
@AllArgsConstructor
@Getter
public enum SchematicFormat {
override fun delete() = useDb { super.delete() }
enum class SchematicFormat(val fileEnding: String) {
MCEDIT(".schematic"),
SPONGE_V2(".schem"),
SPONGE_V3(".schem");
public final String fileEnding;
}
}
@@ -335,7 +335,7 @@ class SchematicNode(id: EntityID<Int>) : IntEntity(id) {
val members: Set<NodeMember> by lazy { NodeMember.getNodeMembers(nodeId) }
private lateinit var breadcrumbs: String
fun getFileEnding(): String = checkDir { NodeData.getLatest(this).nodeFormat.fileEnding }
fun getFileEnding(): String = checkDir { NodeData.getLatest(this)!!.nodeFormat.fileEnding }
fun getId() = nodeId
fun isDir() = nodeType == null
@@ -32,10 +32,14 @@ import java.util.UUID
object UserConfigTable: CompositeIdTable("UserConfig") {
val userId = reference("User", SteamwarUserTable)
val config = varchar("Config", 32)
val config = varchar("Config", 32).entityId()
val value = text("Value", eagerLoading = true)
override val primaryKey = PrimaryKey(userId, config)
init {
addIdColumn(userId)
}
}
class UserConfig(id: EntityID<CompositeID>): CompositeEntity(id) {
@@ -23,6 +23,7 @@ import org.intellij.lang.annotations.Language
import org.jetbrains.exposed.v1.core.ColumnType
import org.jetbrains.exposed.v1.core.Expression
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.StdOutSqlLogger
import org.jetbrains.exposed.v1.core.statements.StatementType
import org.jetbrains.exposed.v1.core.statements.api.RowApi
import org.jetbrains.exposed.v1.dao.IntEntity
@@ -64,7 +65,10 @@ object KotlinDatabase {
fun <T: Any?> useDb(statement: JdbcTransaction.() -> T): T {
KotlinDatabase.ensureConnected()
return TransactionManager.currentOrNull()?.statement() ?: transaction(KotlinDatabase.db, statement)
return TransactionManager.currentOrNull()?.statement() ?: transaction(KotlinDatabase.db) {
addLogger(StdOutSqlLogger)
statement()
}
}
fun <T : IntEntity> IntEntityClass<T>.fromSql(
@@ -36,6 +36,7 @@ import kotlinx.serialization.Serializable
import java.io.BufferedInputStream
import java.io.ByteArrayInputStream
import java.io.DataInputStream
import java.io.InputStream
import java.security.MessageDigest
import java.time.Duration
import java.time.Instant