Add Connection and refactoring EntityCollection

This commit is contained in:
2019-06-03 17:29:05 +02:00
parent 65fb032ad9
commit c5d4bfb07a
7 changed files with 177 additions and 37 deletions

View File

@@ -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"

View 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
}
}
}

View File

@@ -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

View File

@@ -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
}
}
} }
} }

View File

@@ -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
} }
} }
} }

View File

@@ -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
}
}

View 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()