Database in work. Added connection pool, etc

This commit is contained in:
2026-05-12 21:43:18 +03:00
parent 99800e9d37
commit efa3cb63b9
38 changed files with 589 additions and 104 deletions
+12 -3
View File
@@ -5,7 +5,7 @@ plugins {
id 'org.jetbrains.dokka' version '2.2.0'
}
group = 'lab6.prog'
group = 'lab7.prog'
version = '1.0'
repositories {
@@ -13,7 +13,7 @@ repositories {
}
shadowJar {
archiveBaseName.set('Lab6-server')
archiveBaseName.set('Lab7-server')
archiveVersion.set('1.0')
manifest {
attributes 'Main-Class': 'MainKt'
@@ -27,7 +27,16 @@ dependencies {
implementation "io.ktor:ktor-network:3.4.2"
implementation "io.github.oshai:kotlin-logging-jvm:7.0.3"
implementation "org.slf4j:slf4j-api:2.0.13"
implementation "ch.qos.logback:logback-classic:1.5.6"
implementation "ch.qos.logback:logback-classic:1.5.20"
// oh my gawd
implementation "org.jetbrains.exposed:exposed-core:1.2.0"
implementation "org.jetbrains.exposed:exposed-jdbc:1.2.0"
implementation "org.jetbrains.exposed:exposed-dao:1.2.0"
implementation "org.jetbrains.exposed:exposed-java-time:1.2.0"
implementation "org.postgresql:postgresql:42.7.8"
implementation "com.zaxxer:HikariCP:4.0.3"
implementation project(':common')
}
+3 -3
View File
@@ -1,4 +1,4 @@
import collection.CollectionManager
import database.Repository
import commands.*
import file.FileManager
import mu.KotlinLogging
@@ -17,7 +17,7 @@ fun main(args: Array<String>) {
logger.info{ "[startup] Starting server on port $port with $args args" }
val fileManager = FileManager(filePath)
val manager = CollectionManager()
val manager = Repository()
val invoker = CommandInvoker()
// Загружаем коллекцию из файла
@@ -58,7 +58,7 @@ fun main(args: Array<String>) {
fun registerServerCommands(
invoker: CommandInvoker,
manager: CollectionManager,
manager: Repository,
fileManager: FileManager
) {
listOf(
@@ -0,0 +1,19 @@
package auth
class AuthentificationManager {
fun register(username: String, password: String) {
// go-to db
// return token or error
}
fun login(username: String, password: String) {
// go-to db
// return token or error
}
fun grantToken() {}
fun validateToken(token: String) {}
}
@@ -1,13 +1,13 @@
package commands
import collection.CollectionManager
import database.Repository
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import models.HumanBeing
import network.Response
@Serializable
class AddCommand(@Contextual private val manager: CollectionManager) : Command {
class AddCommand(@Contextual private val manager: Repository) : Command {
override val name = "add"
override val description = "добавить новый элемент в коллекцию"
override val args = listOf(CommandArgType.NONE)
@@ -1,10 +1,10 @@
package commands
import collection.CollectionManager
import database.Repository
import models.HumanBeing
import network.Response
class AddIfMaxCommand(private val manager: CollectionManager) : Command {
class AddIfMaxCommand(private val manager: Repository) : Command {
override val name = "add_if_max"
override val description = "добавить элемент, если он больше максимального"
override val type = CommandType.MODEL
+2 -2
View File
@@ -1,13 +1,13 @@
package commands
import collection.CollectionManager
import database.Repository
import models.HumanBeing
import kotlinx.serialization.Serializable
import kotlinx.serialization.Contextual
import network.Response
@Serializable
class AddIfMinCommand(@Contextual private val manager: CollectionManager) : Command {
class AddIfMinCommand(@Contextual private val manager: Repository) : Command {
override val name = "add_if_min"
override val description = "добавить элемент, если он меньше минимального"
override val type = CommandType.MODEL
@@ -1,13 +1,13 @@
package commands
import collection.CollectionManager
import database.Repository
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import models.HumanBeing
import network.Response
@Serializable
class ClearCommand(@Contextual private val manager: CollectionManager) : Command {
class ClearCommand(@Contextual private val manager: Repository) : Command {
override val name = "clear"
override val description = "очистить коллекцию"
override val args = listOf(CommandArgType.NONE)
@@ -1,13 +1,13 @@
package commands
import collection.CollectionManager
import database.Repository
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import models.HumanBeing
import network.Response
@Serializable
class InfoCommand(@Contextual private val manager: CollectionManager) : Command {
class InfoCommand(@Contextual private val manager: Repository) : Command {
override val name = "info"
override val description = "вывести информацию о коллекции"
override val type = CommandType.SIMPLE
@@ -1,6 +1,6 @@
package commands
import collection.CollectionManager
import database.Repository
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import models.HumanBeing
@@ -10,7 +10,7 @@ import network.Response
* Выводит любой элемент с минимальным значением поля [HumanBeing.name].
*/
@Serializable
class MinByNameCommand(@Contextual private val manager: CollectionManager) : Command {
class MinByNameCommand(@Contextual private val manager: Repository) : Command {
override val name = "min_by_name"
override val description = "вывести элемент с минимальным именем"
override val type = CommandType.SIMPLE
@@ -1,6 +1,6 @@
package commands
import collection.CollectionManager
import database.Repository
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import models.HumanBeing
@@ -10,7 +10,7 @@ import network.Response
* Выводит значения поля [models.HumanBeing.minutesOfWaiting] в порядке убывания.
*/
@Serializable
class PrintDescendingMinutesCommand(@Contextual private val manager: CollectionManager) : Command {
class PrintDescendingMinutesCommand(@Contextual private val manager: Repository) : Command {
override val name = "print_field_descending_minutes_of_waiting"
override val description = "вывести minutesOfWaiting в порядке убывания"
override val type = CommandType.SIMPLE
@@ -1,6 +1,6 @@
package commands
import collection.CollectionManager
import database.Repository
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import java.util.UUID
@@ -8,7 +8,7 @@ import models.HumanBeing
import network.Response
@Serializable
class RemoveByIdCommand(@Contextual private val manager: CollectionManager) : Command {
class RemoveByIdCommand(@Contextual private val manager: Repository) : Command {
override val name = "remove_by_id"
override val description = "удалить элемент по id: remove_by_id <uuid>"
override val type = CommandType.MODEL
@@ -1,6 +1,6 @@
package commands
import collection.CollectionManager
import database.Repository
import file.FileManager
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
@@ -9,7 +9,7 @@ import network.Response
@Serializable
class SaveCommand(
@Contextual private val manager: CollectionManager,
@Contextual private val manager: Repository,
@Contextual private val fileManager: FileManager
) : Command {
override val name = "save"
@@ -1,13 +1,13 @@
package commands
import collection.CollectionManager
import database.Repository
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import models.HumanBeing
import network.Response
@Serializable
class ShowCommand(@Contextual private val manager: CollectionManager) : Command {
class ShowCommand(@Contextual private val manager: Repository) : Command {
override val name = "show"
override val description = "вывести все элементы коллекции"
override val type = CommandType.SIMPLE
@@ -1,6 +1,6 @@
package commands
import collection.CollectionManager
import database.Repository
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import models.HumanBeing
@@ -10,7 +10,7 @@ import network.Response
* Выводит сумму значений поля [models.HumanBeing.minutesOfWaiting].
*/
@Serializable
class SumOfMinutesCommand(@Contextual private val manager: CollectionManager) : Command {
class SumOfMinutesCommand(@Contextual private val manager: Repository) : Command {
override val name = "sum_of_minutes_of_waiting"
override val description = "вывести сумму minutesOfWaiting всех элементов"
override val type = CommandType.SIMPLE
@@ -1,6 +1,6 @@
package commands
import collection.CollectionManager
import database.Repository
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import models.HumanBeing
@@ -8,7 +8,7 @@ import network.Response
import java.util.UUID
@Serializable
class UpdateCommand(@Contextual private val manager: CollectionManager) : Command {
class UpdateCommand(@Contextual private val manager: Repository) : Command {
override val name = "update"
override val description = "обновить элемент по id: update <uuid>"
override val type = CommandType.MODEL
@@ -0,0 +1,60 @@
package database
import com.zaxxer.hikari.*
import database.tables.CarTable
import database.tables.CoordinatesTable
import database.tables.HumanBeingTable
import models.Coordinates
import org.jetbrains.exposed.v1.jdbc.*
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
class DatabaseManager {
fun start() {
val config = HikariConfig().apply {
jdbcUrl = "jdbc:postgresql://aklivtsov.tech:5430/prog7_db"
driverClassName = "org.postgresql.Driver"
username = "mainUser"
password = "superDuperPassword"
maximumPoolSize = 10 // Set according to your needs
isAutoCommit = false // Exposed handles transactions manually
transactionIsolation = "TRANSACTION_REPEATABLE_READ"
validate()
}
val dataSource = HikariDataSource(config)
Database.connect(dataSource)
transaction {
exec("""
DO ${'$'}${'$'} BEGIN
CREATE TYPE weapontype AS ENUM ('hammer', 'shotgun', 'bat');
EXCEPTION
WHEN duplicate_object THEN null;
END ${'$'}${'$'};
""")
SchemaUtils.create(CoordinatesTable)
SchemaUtils.create(CarTable)
SchemaUtils.create(HumanBeingTable)
}
}
fun test(): List<String> {
val smth = mutableListOf<String>()
transaction {
val rows = HumanBeingTable.selectAll().toList()
rows.forEach { row ->
smth.add(row[HumanBeingTable.name])
}
}
return smth
}
}
fun main(args: Array<String>) {
val manager = DatabaseManager()
print("here we go!")
manager.start()
print(manager.test())
print("here we don't go!")
}
@@ -1,7 +1,13 @@
package collection
package database
import database.tables.CarTable
import database.tables.CoordinatesTable
import models.HumanBeing
import models.Car
import database.tables.HumanBeingTable
import mu.KotlinLogging
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import org.jetbrains.exposed.v1.jdbc.insert
import java.time.LocalDate
import java.util.TreeSet
import java.util.UUID
@@ -9,14 +15,45 @@ import java.util.UUID
private val logger = KotlinLogging.logger {}
class CollectionManager {
class Repository {
private val collection: TreeSet<HumanBeing> = TreeSet()
val initDate: LocalDate = LocalDate.now()
fun add(humanBeing: HumanBeing): Boolean {
logger.info { "[ADD] Element created ${humanBeing.id}" }
return collection.add(humanBeing)
var allGood = true
transaction {
try {
val cords = CoordinatesTable.insert {
it[x] = humanBeing.coordinates.x
it[y] = humanBeing.coordinates.y
} get CoordinatesTable.id
val theCar = CarTable.insert {
it[cool] = humanBeing.car.cool
} get CarTable.id
HumanBeingTable.insert {
it[name] = humanBeing.name
it[coordinates] = cords
it[creationDate] = humanBeing.creationDate
it[realHero] = humanBeing.realHero
it[hasToothpick] = humanBeing.hasToothpick
it[impactSpeed] = humanBeing.impactSpeed
it[soundtrackName] = humanBeing.soundtrackName
it[minutesOfWaiting] = humanBeing.minutesOfWaiting
it[car] = theCar
it[weaponType] = humanBeing.weaponType
}
logger.info { "[ADD] Element created ${humanBeing.id}" }
allGood = true
} catch (e: Exception) {
logger.error(e) { "[ADD] Error during inserting: $e" }
allGood = false
}
}
return allGood
}
fun update(id: UUID, updated: HumanBeing): Boolean {
@@ -0,0 +1,7 @@
package database.tables
import org.jetbrains.exposed.v1.core.dao.id.java.UUIDTable
object CarTable : UUIDTable("car") {
val cool = bool("cool").nullable()
}
@@ -0,0 +1,7 @@
package database.tables
import org.jetbrains.exposed.v1.core.dao.id.java.UUIDTable
object CoordinatesTable : UUIDTable("coordinates") {
val x = long("x").nullable()
val y = integer("y").nullable()
}
@@ -0,0 +1,32 @@
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TYPE WeaponType AS ENUM ('hammer', 'shotgun', 'bat', 'hands'); -- last one is default
CREATE DOMAIN auto_uuid as UUID DEFAULT gen_random_uuid();
CREATE TABLE IF NOT EXISTS Coordinates(
id auto_uuid PRIMARY KEY,
x BIGINT,
y INTEGER
);
CREATE TABLE IF NOT EXISTS Car(
id auto_uuid PRIMARY KEY,
cool BOOLEAN
);
CREATE TABLE IF NOT EXISTS HumanBeing(
id auto_uuid PRIMARY KEY,
name TEXT NOT NULL,
coordinates UUID references Coordinates(id),
last_name TEXT,
creation_date DATE,
real_hero BOOLEAN,
has_toothpick BOOLEAN,
impact_speed DOUBLE PRECISION,
soundtrack_name TEXT,
minutes_of_waiting FLOAT,
weapon_type WeaponType,
car UUID references Car(id)
);
@@ -0,0 +1,34 @@
package database.tables
import models.WeaponType
import org.jetbrains.exposed.v1.core.dao.id.java.UUIDTable
import org.jetbrains.exposed.v1.javatime.date
import org.postgresql.util.PGobject
object HumanBeingTable : UUIDTable("humanBeing") {
val name = text("name")
val coordinates = reference("coordinates", CoordinatesTable.id)
val creationDate = date("creation_date")
val realHero = bool("real_hero")
val hasToothpick = bool("has_toothpick")
val impactSpeed = double("impact_speed")
val soundtrackName = text("soundtrack_name")
val minutesOfWaiting = float("minutes_of_waiting")
val car = reference("car", CarTable.id)
val weaponType = customEnumeration(
name = "weapon_type",
sql = "weapontype",
fromDb = { value ->
WeaponType.valueOf(
(value as PGobject).value!!
)
},
toDb = { value ->
PGobject().apply {
type = "weapontype"
this.value = value.name
}
}
).nullable().default(null)
}
+2
View File
@@ -17,4 +17,6 @@
<appender-ref ref="server_log" />
<appender-ref ref="console" />
</root>
<logger name="com.zaxxer.hikari" level="WARN"/>
</configuration>