Add Connection and refactoring EntityCollection
This commit is contained in:
@@ -17,6 +17,7 @@ dependencies {
|
|||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.31"
|
implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.31"
|
||||||
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.9"
|
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.9"
|
||||||
|
implementation "com.github.jasync-sql:jasync-postgresql:0.9.53"
|
||||||
|
|
||||||
testImplementation "io.mockk:mockk:1.9"
|
testImplementation "io.mockk:mockk:1.9"
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter:5.4.2"
|
testImplementation "org.junit.jupiter:junit-jupiter:5.4.2"
|
||||||
|
|||||||
40
src/main/kotlin/fr/postgresjson/connexion/Connection.kt
Normal file
40
src/main/kotlin/fr/postgresjson/connexion/Connection.kt
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package fr.postgresjson.connexion
|
||||||
|
|
||||||
|
import com.github.jasync.sql.db.pool.ConnectionPool
|
||||||
|
import com.github.jasync.sql.db.postgresql.PostgreSQLConnection
|
||||||
|
import com.github.jasync.sql.db.postgresql.PostgreSQLConnectionBuilder
|
||||||
|
import fr.postgresjson.entity.EntityI
|
||||||
|
import fr.postgresjson.serializer.Serializer
|
||||||
|
|
||||||
|
|
||||||
|
class Connection(
|
||||||
|
private val host: String = "localhost",
|
||||||
|
private val port: Int = 5432,
|
||||||
|
private val database: String = "dc-project",
|
||||||
|
private val username: String = "dc-project",
|
||||||
|
private val password: String = "dc-project"
|
||||||
|
) {
|
||||||
|
private lateinit var connection: ConnectionPool<PostgreSQLConnection>
|
||||||
|
val serializer = Serializer()
|
||||||
|
|
||||||
|
fun connect(): ConnectionPool<PostgreSQLConnection> {
|
||||||
|
if (!::connection.isInitialized || !connection.isConnected()) {
|
||||||
|
connection = PostgreSQLConnectionBuilder.createConnectionPool(
|
||||||
|
"jdbc:postgresql://$host:$port/$database?user=$username&password=$password"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return connection
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T, reified R :EntityI<T?>> execute(sql: String, values: List<Any?> = emptyList()): R? {
|
||||||
|
val future = connect().sendPreparedStatement(sql, values)
|
||||||
|
val json = future.get().rows[0].getString(0)
|
||||||
|
if (json === null) {
|
||||||
|
return null
|
||||||
|
} else {
|
||||||
|
val obj = serializer.deserialize<T, R>(json)
|
||||||
|
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,11 +3,11 @@ package fr.postgresjson.entity
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
interface EntityI<T> {
|
interface EntityI<T> {
|
||||||
var id: T
|
var id: T?
|
||||||
}
|
}
|
||||||
abstract class Entity<T>(override var id: T) : EntityI<T>
|
abstract class Entity<T>(override var id: T? = null) : EntityI<T?>
|
||||||
abstract class UuidEntity(override var id: UUID = UUID.randomUUID()) : Entity<UUID>(id)
|
abstract class UuidEntity(override var id: UUID? = UUID.randomUUID()) : Entity<UUID?>(id)
|
||||||
abstract class IdEntity(override var id: Int) : Entity<Int>(id)
|
abstract class IdEntity(override var id: Int? = null) : Entity<Int?>(id)
|
||||||
|
|
||||||
interface EntityVersioning<T> {
|
interface EntityVersioning<T> {
|
||||||
var version: T
|
var version: T
|
||||||
|
|||||||
@@ -1,13 +1,36 @@
|
|||||||
package fr.postgresjson.entity
|
package fr.postgresjson.entity
|
||||||
|
|
||||||
class EntityCollection<T, E : EntityI<T>> {
|
import kotlin.reflect.KClass
|
||||||
var collection: MutableMap<T, E> = mutableMapOf()
|
|
||||||
|
|
||||||
fun get(id: T): E? {
|
class EntityCollection {
|
||||||
return collection[id]
|
val collections: MutableMap<KClass<*>, EntityCollection<Any, EntityI<Any?>>> = mutableMapOf()
|
||||||
|
|
||||||
|
inline fun <I, reified R : EntityI<I?>> get(id: I): R? {
|
||||||
|
val collection = collections[R::class]
|
||||||
|
val entity = collection?.get(id!!)
|
||||||
|
return entity as R?
|
||||||
}
|
}
|
||||||
|
|
||||||
fun set(entity: E) {
|
inline fun <I, reified R : EntityI<I?>> set(entity: R) {
|
||||||
collection.set(entity.id, entity)
|
if (collections[R::class] == null) {
|
||||||
|
collections[R::class] = EntityCollection()
|
||||||
|
}
|
||||||
|
|
||||||
|
collections[R::class]!!.set(entity as EntityI<Any?>)
|
||||||
|
}
|
||||||
|
|
||||||
|
class EntityCollection<T, E : EntityI<T?>> {
|
||||||
|
private var collection: MutableMap<T, E> = mutableMapOf()
|
||||||
|
|
||||||
|
fun get(id: T): E? {
|
||||||
|
return collection[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun set(entity: E) {
|
||||||
|
val id = entity.id
|
||||||
|
if (id !== null) {
|
||||||
|
collection[id] = entity
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,23 @@
|
|||||||
package fr.postgresjson.repository
|
package fr.postgresjson.repository
|
||||||
|
|
||||||
|
import com.github.jasync.sql.db.pool.ConnectionPool
|
||||||
|
import com.github.jasync.sql.db.postgresql.PostgreSQLConnection
|
||||||
import fr.postgresjson.entity.EntityCollection
|
import fr.postgresjson.entity.EntityCollection
|
||||||
import fr.postgresjson.entity.EntityI
|
import fr.postgresjson.entity.EntityI
|
||||||
import fr.postgresjson.serializer.Serializer
|
import fr.postgresjson.serializer.Serializer
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
interface RepositoryI<T, E : EntityI<T>>
|
interface RepositoryI<T, E : EntityI<T>>
|
||||||
|
|
||||||
abstract class Repository<T, E : EntityI<T>> : RepositoryI<T, E> {
|
abstract class Repository<T, E : EntityI<T>> : RepositoryI<T, E> {
|
||||||
private val collections: MutableMap<KClass<*>, EntityCollection<Any, EntityI<Any>>> = mutableMapOf()
|
abstract var connection: ConnectionPool<PostgreSQLConnection>
|
||||||
|
|
||||||
private inline fun <I, reified R : EntityI<I>> get(id: I): R? {
|
fun <T> findById(id: T): EntityI<T?>? {
|
||||||
val collection = collections[R::class]
|
return when (val e = EntityCollection().get(id)) {
|
||||||
val entity = collection?.get(id!!)
|
|
||||||
return entity as R?
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <I, reified R : EntityI<I>> set(entity: R) {
|
|
||||||
if (collections[R::class] == null) {
|
|
||||||
collections[R::class] = EntityCollection()
|
|
||||||
}
|
|
||||||
|
|
||||||
collections[R::class]!!.set(entity as EntityI<Any>)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> findById(id: T): EntityI<T>? {
|
|
||||||
return when (val e = get(id)) {
|
|
||||||
null -> {
|
null -> {
|
||||||
// TODO create Request
|
// TODO create Request
|
||||||
Serializer().deserialize<T, EntityI<T>>("""{"plop", "plip"}""")
|
Serializer().deserialize<T, EntityI<T?>>("""{"plop", "plip"}""")
|
||||||
}
|
}
|
||||||
else -> e
|
else -> e
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,26 +1,86 @@
|
|||||||
package fr.postgresjson.serializer
|
package fr.postgresjson.serializer
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParser
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import fr.postgresjson.entity.EntityCollection
|
import fr.postgresjson.entity.EntityCollection
|
||||||
import fr.postgresjson.entity.EntityI
|
import fr.postgresjson.entity.EntityI
|
||||||
|
import fr.postgresjson.entity.IdEntity
|
||||||
|
import fr.postgresjson.entity.UuidEntity
|
||||||
|
import java.io.IOException
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
class Serializer(val mapper: ObjectMapper = jacksonObjectMapper()) {
|
class Serializer(val mapper: ObjectMapper = jacksonObjectMapper()) {
|
||||||
|
|
||||||
|
var collection: EntityCollection = EntityCollection()
|
||||||
|
|
||||||
|
init {
|
||||||
|
val module = SimpleModule()
|
||||||
|
module.addDeserializer(UuidEntity::class.java, EntityUuidDeserializer(collection))
|
||||||
|
module.addDeserializer(IdEntity::class.java, EntityIdDeserializer(collection))
|
||||||
|
mapper.registerModule(module)
|
||||||
|
}
|
||||||
|
|
||||||
fun <T> serialize(source: EntityI<T>): String {
|
fun <T> serialize(source: EntityI<T>): String {
|
||||||
return mapper.writeValueAsString(source)
|
return mapper.writeValueAsString(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <T, reified E : EntityI<T>> deserialize(json: String): E {
|
inline fun <T, reified E : EntityI<T?>> deserialize(json: String): E {
|
||||||
val unserialized = mapper.readValue<E>(json)
|
val unserialized = this.mapper.readValue<E>(json)
|
||||||
return EntityCollection<T, E>().get(unserialized.id) ?: unserialized
|
return collection.get(unserialized.id) ?: unserialized
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T, E : EntityI<T>> deserialize(json: String, target: E): E {
|
inline fun <T, reified E : EntityI<T?>> deserialize(json: String, target: E): E {
|
||||||
val unserialized = mapper.readerForUpdating(target).readValue<E>(json)
|
val unserialized = mapper.readerForUpdating(target).readValue<E>(json)
|
||||||
return EntityCollection<T, E>().get(unserialized.id) ?: unserialized
|
return collection.get(unserialized.id) ?: unserialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> EntityI<T>.serialize() = Serializer().serialize(this)
|
fun <T> EntityI<T?>.serialize() = Serializer().serialize(this)
|
||||||
fun <T, E : EntityI<T>> E.deserialize(json: String) = Serializer().deserialize(json, this)
|
inline fun <T, reified E : EntityI<T?>> E.deserialize(json: String) = Serializer().deserialize(json, this)
|
||||||
|
|
||||||
|
|
||||||
|
class EntityUuidDeserializer <T: UuidEntity> @JvmOverloads constructor(vc: Class<*>? = null) : StdDeserializer<T>(vc) {
|
||||||
|
var collection: EntityCollection = EntityCollection()
|
||||||
|
|
||||||
|
constructor(collection: EntityCollection) : this() {
|
||||||
|
this.collection = collection
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, JsonProcessingException::class)
|
||||||
|
override fun deserialize(jp: JsonParser, ctxt: DeserializationContext): T {
|
||||||
|
val node = jp.codec.readTree<JsonNode>(jp)
|
||||||
|
val id = node.get("id").asText()
|
||||||
|
val entity = collection.get<UUID, UuidEntity>(UUID.fromString(id))
|
||||||
|
|
||||||
|
return (entity ?: ctxt.readValue(jp, UuidEntity::class.javaObjectType)) as T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class EntityIdDeserializer <T: IdEntity> @JvmOverloads constructor(vc: Class<*>? = null) : StdDeserializer<T>(vc) {
|
||||||
|
var collection: EntityCollection = EntityCollection()
|
||||||
|
|
||||||
|
constructor(collection: EntityCollection) : this() {
|
||||||
|
this.collection = collection
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, JsonProcessingException::class)
|
||||||
|
override fun deserialize(jp: JsonParser, ctxt: DeserializationContext): T {
|
||||||
|
val node = jp.codec.readTree<JsonNode>(jp)
|
||||||
|
val id = node.get("id").asInt()
|
||||||
|
val entity = collection.get<Int?, IdEntity>(id)
|
||||||
|
|
||||||
|
val obj = (entity ?: ctxt.readValue(jp, UuidEntity::class.javaObjectType)) as EntityI<Int?>
|
||||||
|
collection.set(obj)
|
||||||
|
|
||||||
|
return obj as T
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/test/kotlin/fr/postgresjson/serializer/ConnectionTest.kt
Normal file
30
src/test/kotlin/fr/postgresjson/serializer/ConnectionTest.kt
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package fr.postgresjson.serializer
|
||||||
|
|
||||||
|
import fr.postgresjson.connexion.Connection
|
||||||
|
import fr.postgresjson.entity.IdEntity
|
||||||
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
class ConnectionTest() {
|
||||||
|
private lateinit var connection: Connection
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun before() {
|
||||||
|
connection = Connection()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getObject() {
|
||||||
|
val obj: ObjTest? = connection.execute<Int?, ObjTest>("select to_json(a) from test a limit 1")
|
||||||
|
assertTrue(obj is ObjTest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
// fun getExistingObject() {
|
||||||
|
// val obj: ObjTest? = connection.execute<Int?, ObjTest>("select to_json(a) from test a limit 1")
|
||||||
|
// assertTrue(obj is ObjTest)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjTest(var name: String): IdEntity()
|
||||||
Reference in New Issue
Block a user