Database in work. Added connection pool, etc
This commit is contained in:
+12
-3
@@ -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')
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!")
|
||||
}
|
||||
+41
-4
@@ -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)
|
||||
}
|
||||
@@ -17,4 +17,6 @@
|
||||
<appender-ref ref="server_log" />
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
<logger name="com.zaxxer.hikari" level="WARN"/>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user