From c5d4bfb07a1216850edca07580bcaa9ba39e2004 Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Mon, 3 Jun 2019 17:29:05 +0200 Subject: [PATCH] Add Connection and refactoring EntityCollection --- build.gradle | 1 + .../fr/postgresjson/connexion/Connection.kt | 40 ++++++++++ .../kotlin/fr/postgresjson/entity/Entity.kt | 8 +- .../postgresjson/entity/EntityCollection.kt | 35 +++++++-- .../fr/postgresjson/repository/Repository.kt | 26 ++----- .../fr/postgresjson/serializer/Serializer.kt | 74 +++++++++++++++++-- .../postgresjson/serializer/ConnectionTest.kt | 30 ++++++++ 7 files changed, 177 insertions(+), 37 deletions(-) create mode 100644 src/main/kotlin/fr/postgresjson/connexion/Connection.kt create mode 100644 src/test/kotlin/fr/postgresjson/serializer/ConnectionTest.kt diff --git a/build.gradle b/build.gradle index 04ba5db..b8a2391 100644 --- a/build.gradle +++ b/build.gradle @@ -17,6 +17,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.31" 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 "org.junit.jupiter:junit-jupiter:5.4.2" diff --git a/src/main/kotlin/fr/postgresjson/connexion/Connection.kt b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt new file mode 100644 index 0000000..8c36b34 --- /dev/null +++ b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt @@ -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 + val serializer = Serializer() + + fun connect(): ConnectionPool { + if (!::connection.isInitialized || !connection.isConnected()) { + connection = PostgreSQLConnectionBuilder.createConnectionPool( + "jdbc:postgresql://$host:$port/$database?user=$username&password=$password" + ) + } + return connection + } + + inline fun > execute(sql: String, values: List = 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(json) + + return obj + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/fr/postgresjson/entity/Entity.kt b/src/main/kotlin/fr/postgresjson/entity/Entity.kt index f81664f..3dbbd42 100644 --- a/src/main/kotlin/fr/postgresjson/entity/Entity.kt +++ b/src/main/kotlin/fr/postgresjson/entity/Entity.kt @@ -3,11 +3,11 @@ package fr.postgresjson.entity import java.util.* interface EntityI { - var id: T + var id: T? } -abstract class Entity(override var id: T) : EntityI -abstract class UuidEntity(override var id: UUID = UUID.randomUUID()) : Entity(id) -abstract class IdEntity(override var id: Int) : Entity(id) +abstract class Entity(override var id: T? = null) : EntityI +abstract class UuidEntity(override var id: UUID? = UUID.randomUUID()) : Entity(id) +abstract class IdEntity(override var id: Int? = null) : Entity(id) interface EntityVersioning { var version: T diff --git a/src/main/kotlin/fr/postgresjson/entity/EntityCollection.kt b/src/main/kotlin/fr/postgresjson/entity/EntityCollection.kt index 7a5e726..2a2efe1 100644 --- a/src/main/kotlin/fr/postgresjson/entity/EntityCollection.kt +++ b/src/main/kotlin/fr/postgresjson/entity/EntityCollection.kt @@ -1,13 +1,36 @@ package fr.postgresjson.entity -class EntityCollection> { - var collection: MutableMap = mutableMapOf() +import kotlin.reflect.KClass - fun get(id: T): E? { - return collection[id] +class EntityCollection { + val collections: MutableMap, EntityCollection>> = mutableMapOf() + + inline fun > get(id: I): R? { + val collection = collections[R::class] + val entity = collection?.get(id!!) + return entity as R? } - fun set(entity: E) { - collection.set(entity.id, entity) + inline fun > set(entity: R) { + if (collections[R::class] == null) { + collections[R::class] = EntityCollection() + } + + collections[R::class]!!.set(entity as EntityI) + } + + class EntityCollection> { + private var collection: MutableMap = mutableMapOf() + + fun get(id: T): E? { + return collection[id] + } + + fun set(entity: E) { + val id = entity.id + if (id !== null) { + collection[id] = entity + } + } } } diff --git a/src/main/kotlin/fr/postgresjson/repository/Repository.kt b/src/main/kotlin/fr/postgresjson/repository/Repository.kt index c14c646..e876ce7 100644 --- a/src/main/kotlin/fr/postgresjson/repository/Repository.kt +++ b/src/main/kotlin/fr/postgresjson/repository/Repository.kt @@ -1,37 +1,23 @@ 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.EntityI import fr.postgresjson.serializer.Serializer -import kotlin.reflect.KClass interface RepositoryI> abstract class Repository> : RepositoryI { - private val collections: MutableMap, EntityCollection>> = mutableMapOf() + abstract var connection: ConnectionPool - private inline fun > get(id: I): R? { - val collection = collections[R::class] - val entity = collection?.get(id!!) - return entity as R? - } - - private inline fun > set(entity: R) { - if (collections[R::class] == null) { - collections[R::class] = EntityCollection() - } - - collections[R::class]!!.set(entity as EntityI) - } - - fun findById(id: T): EntityI? { - return when (val e = get(id)) { + fun findById(id: T): EntityI? { + return when (val e = EntityCollection().get(id)) { null -> { // TODO create Request - Serializer().deserialize>("""{"plop", "plip"}""") + Serializer().deserialize>("""{"plop", "plip"}""") } else -> e - } } } \ No newline at end of file diff --git a/src/main/kotlin/fr/postgresjson/serializer/Serializer.kt b/src/main/kotlin/fr/postgresjson/serializer/Serializer.kt index 54f45ef..9829a99 100644 --- a/src/main/kotlin/fr/postgresjson/serializer/Serializer.kt +++ b/src/main/kotlin/fr/postgresjson/serializer/Serializer.kt @@ -1,26 +1,86 @@ 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.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.readValue import fr.postgresjson.entity.EntityCollection 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()) { + + 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 serialize(source: EntityI): String { return mapper.writeValueAsString(source) } - inline fun > deserialize(json: String): E { - val unserialized = mapper.readValue(json) - return EntityCollection().get(unserialized.id) ?: unserialized + inline fun > deserialize(json: String): E { + val unserialized = this.mapper.readValue(json) + return collection.get(unserialized.id) ?: unserialized } - fun > deserialize(json: String, target: E): E { + inline fun > deserialize(json: String, target: E): E { val unserialized = mapper.readerForUpdating(target).readValue(json) - return EntityCollection().get(unserialized.id) ?: unserialized + return collection.get(unserialized.id) ?: unserialized } } -fun EntityI.serialize() = Serializer().serialize(this) -fun > E.deserialize(json: String) = Serializer().deserialize(json, this) \ No newline at end of file +fun EntityI.serialize() = Serializer().serialize(this) +inline fun > E.deserialize(json: String) = Serializer().deserialize(json, this) + + +class EntityUuidDeserializer @JvmOverloads constructor(vc: Class<*>? = null) : StdDeserializer(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(jp) + val id = node.get("id").asText() + val entity = collection.get(UUID.fromString(id)) + + return (entity ?: ctxt.readValue(jp, UuidEntity::class.javaObjectType)) as T + } +} + + +class EntityIdDeserializer @JvmOverloads constructor(vc: Class<*>? = null) : StdDeserializer(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(jp) + val id = node.get("id").asInt() + val entity = collection.get(id) + + val obj = (entity ?: ctxt.readValue(jp, UuidEntity::class.javaObjectType)) as EntityI + collection.set(obj) + + return obj as T + } +} \ No newline at end of file diff --git a/src/test/kotlin/fr/postgresjson/serializer/ConnectionTest.kt b/src/test/kotlin/fr/postgresjson/serializer/ConnectionTest.kt new file mode 100644 index 0000000..80ac535 --- /dev/null +++ b/src/test/kotlin/fr/postgresjson/serializer/ConnectionTest.kt @@ -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("select to_json(a) from test a limit 1") + assertTrue(obj is ObjTest) + } + +// @Test +// fun getExistingObject() { +// val obj: ObjTest? = connection.execute("select to_json(a) from test a limit 1") +// assertTrue(obj is ObjTest) +// } +} + +class ObjTest(var name: String): IdEntity() \ No newline at end of file