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
+58 -96
View File
@@ -17,121 +17,83 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * 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 de.steamwar.sql.internal.useDb
import lombok.AllArgsConstructor; import org.jetbrains.exposed.v1.core.SortOrder
import lombok.Getter; 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; object NodeDataTable: CompositeIdTable("NodeData") {
import java.io.*; val nodeId = reference("NodeId", SchematicNodeTable)
import java.sql.PreparedStatement; val createdAt = timestamp("CreatedAt").entityId()
import java.sql.Timestamp; val nodeFormat = enumeration("NodeFormat", NodeData.SchematicFormat::class)
import java.time.Instant; val schemData = blob("SchemData")
import java.util.List;
import java.util.Optional;
import java.util.zip.GZIPInputStream;
@AllArgsConstructor override val primaryKey = PrimaryKey(nodeId, createdAt)
@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);
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); companion object: CompositeEntityClass<NodeData>(NodeDataTable) {
private static final Statement selSchemData = new Statement("SELECT SchemData FROM NodeData WHERE NodeId = ? AND CreatedAt = ?"); @JvmStatic
private static final Statement delete = table.delete(Table.PRIMARY); 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 "); @JvmStatic
private static final Statement getRevisions = new Statement("SELECT COUNT(DISTINCT CreatedAt) as CNT FROM NodeData WHERE NodeId = ?"); fun get(node: SchematicNode) = useDb {
private static final SelectStatement<NodeData> getLatest = new SelectStatement<>(table, "SELECT NodeId, CreatedAt, NodeFormat FROM NodeData WHERE NodeId = ? ORDER BY CreatedAt DESC LIMIT 1"); find { (NodeDataTable.nodeId eq node.nodeId) }.orderBy(NodeDataTable.createdAt to SortOrder.ASC).toList()
}
public static NodeData getLatest(SchematicNode node) { @JvmStatic
if (node.isDir()) throw new IllegalArgumentException("Node is dir"); fun get(node: SchematicNode, revision: Int) = useDb {
return Optional.ofNullable(getLatest.select(node)).orElseGet(() -> new NodeData(node.getId(), Timestamp.from(Instant.now()), SchematicFormat.MCEDIT)); find { NodeDataTable.nodeId eq node.nodeId }.orderBy(NodeDataTable.createdAt to SortOrder.ASC).toList().get(revision - 1)
} }
public static List<NodeData> get(SchematicNode node) { @JvmStatic
return get.listSelect(node); fun getRevisions(node: SchematicNode) = useDb {
} count(NodeDataTable.nodeId eq node.nodeId).toInt()
}
public static NodeData get(SchematicNode node, int revision) { @JvmStatic
return get.listSelect(node).get(revision - 1); fun saveFromStream(node: SchematicNode, blob: InputStream, format: SchematicFormat) {
} NodeDataTable.insertIgnore {
it[NodeDataTable.nodeId] = EntityID(node.getId(), SchematicNodeTable)
public static int getRevisions(SchematicNode node) { it[NodeDataTable.nodeFormat] = format
return getRevisions.select(rs -> { it[NodeDataTable.schemData] = ExposedBlob(blob)
if (rs.next()) {
return rs.getInt("CNT");
} else {
return 0;
} }
}, 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 fun schemData(decompress: Boolean) = useDb {
public void saveFromStream(InputStream blob, SchematicFormat newFormat) { schemData.inputStream.let { if(decompress) GZIPInputStream(it) else it }
saveFromStream(SchematicNode.getSchematicNode(nodeId), blob, newFormat);
} }
public void delete() { fun schemData() = schemData(true)
delete.update(nodeId, createdAt);
}
@AllArgsConstructor override fun delete() = useDb { super.delete() }
@Getter
public enum SchematicFormat { enum class SchematicFormat(val fileEnding: String) {
MCEDIT(".schematic"), MCEDIT(".schematic"),
SPONGE_V2(".schem"), SPONGE_V2(".schem"),
SPONGE_V3(".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) } val members: Set<NodeMember> by lazy { NodeMember.getNodeMembers(nodeId) }
private lateinit var breadcrumbs: String 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 getId() = nodeId
fun isDir() = nodeType == null fun isDir() = nodeType == null
@@ -32,10 +32,14 @@ import java.util.UUID
object UserConfigTable: CompositeIdTable("UserConfig") { object UserConfigTable: CompositeIdTable("UserConfig") {
val userId = reference("User", SteamwarUserTable) val userId = reference("User", SteamwarUserTable)
val config = varchar("Config", 32) val config = varchar("Config", 32).entityId()
val value = text("Value", eagerLoading = true) val value = text("Value", eagerLoading = true)
override val primaryKey = PrimaryKey(userId, config) override val primaryKey = PrimaryKey(userId, config)
init {
addIdColumn(userId)
}
} }
class UserConfig(id: EntityID<CompositeID>): CompositeEntity(id) { 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.ColumnType
import org.jetbrains.exposed.v1.core.Expression import org.jetbrains.exposed.v1.core.Expression
import org.jetbrains.exposed.v1.core.ResultRow 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.StatementType
import org.jetbrains.exposed.v1.core.statements.api.RowApi import org.jetbrains.exposed.v1.core.statements.api.RowApi
import org.jetbrains.exposed.v1.dao.IntEntity import org.jetbrains.exposed.v1.dao.IntEntity
@@ -64,7 +65,10 @@ object KotlinDatabase {
fun <T: Any?> useDb(statement: JdbcTransaction.() -> T): T { fun <T: Any?> useDb(statement: JdbcTransaction.() -> T): T {
KotlinDatabase.ensureConnected() 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( fun <T : IntEntity> IntEntityClass<T>.fromSql(
@@ -36,6 +36,7 @@ import kotlinx.serialization.Serializable
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.DataInputStream import java.io.DataInputStream
import java.io.InputStream
import java.security.MessageDigest import java.security.MessageDigest
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant