feature #8: Pagination for request
This commit is contained in:
@@ -6,6 +6,7 @@ import com.github.jasync.sql.db.QueryResult
|
|||||||
import com.github.jasync.sql.db.pool.ConnectionPool
|
import com.github.jasync.sql.db.pool.ConnectionPool
|
||||||
import com.github.jasync.sql.db.postgresql.PostgreSQLConnection
|
import com.github.jasync.sql.db.postgresql.PostgreSQLConnection
|
||||||
import com.github.jasync.sql.db.postgresql.PostgreSQLConnectionBuilder
|
import com.github.jasync.sql.db.postgresql.PostgreSQLConnectionBuilder
|
||||||
|
import com.github.jasync.sql.db.util.length
|
||||||
import fr.postgresjson.entity.EntityI
|
import fr.postgresjson.entity.EntityI
|
||||||
import fr.postgresjson.serializer.Serializer
|
import fr.postgresjson.serializer.Serializer
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
@@ -50,13 +51,18 @@ class Connection(
|
|||||||
serializer.deserialize(json, typeReference)
|
serializer.deserialize(json, typeReference)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inline fun <reified R : EntityI<*>> selectOne(sql: String, values: List<Any?> = emptyList()): R? = select(sql, object: TypeReference<R>() {}, values)
|
|
||||||
|
inline fun <reified R: EntityI<*>> selectOne(sql: String, values: List<Any?> = emptyList()): R? =
|
||||||
|
select(sql, object: TypeReference<R>() {}, values)
|
||||||
|
|
||||||
override fun <R: EntityI<*>> select(sql: String, typeReference: TypeReference<R>, values: Map<String, Any?>): R? {
|
override fun <R: EntityI<*>> select(sql: String, typeReference: TypeReference<R>, values: Map<String, Any?>): R? {
|
||||||
val replacedQuery = replaceArgs(sql, values)
|
return replaceArgs(sql, values) {
|
||||||
return select(replacedQuery.sql, typeReference, replacedQuery.parameters)
|
select(this.sql, typeReference, this.parameters)
|
||||||
}
|
}
|
||||||
inline fun <reified R : EntityI<*>> selectOne(sql: String, values: Map<String, Any?>): R? = select(sql, object: TypeReference<R>() {}, values)
|
}
|
||||||
|
|
||||||
|
inline fun <reified R: EntityI<*>> selectOne(sql: String, values: Map<String, Any?>): R? =
|
||||||
|
select(sql, object: TypeReference<R>() {}, values)
|
||||||
|
|
||||||
override fun <R: List<EntityI<*>>> select(sql: String, typeReference: TypeReference<R>, values: List<Any?>): R {
|
override fun <R: List<EntityI<*>>> select(sql: String, typeReference: TypeReference<R>, values: List<Any?>): R {
|
||||||
val future = connect().sendPreparedStatement(sql, compileArgs(values))
|
val future = connect().sendPreparedStatement(sql, compileArgs(values))
|
||||||
@@ -67,21 +73,70 @@ class Connection(
|
|||||||
serializer.deserializeList(json, typeReference)
|
serializer.deserializeList(json, typeReference)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inline fun <reified R : List<EntityI<*>>> select(sql: String, values: List<Any?> = emptyList()): R = select(sql, object : TypeReference<R>() {}, values)
|
|
||||||
|
|
||||||
override fun <R : List<EntityI<*>>> select(sql: String, typeReference: TypeReference<R>, values: Map<String, Any?>): R {
|
inline fun <reified R: List<EntityI<*>>> select(sql: String, values: List<Any?> = emptyList()): R =
|
||||||
val replacedQuery = replaceArgs(sql, values)
|
select(sql, object: TypeReference<R>() {}, values)
|
||||||
return select(replacedQuery.sql, typeReference, replacedQuery.parameters)
|
|
||||||
|
fun <R: EntityI<*>> select(
|
||||||
|
sql: String,
|
||||||
|
page: Int,
|
||||||
|
limit: Int,
|
||||||
|
typeReference: TypeReference<List<R>>,
|
||||||
|
values: Map<String, Any?>
|
||||||
|
): Paginated<R> {
|
||||||
|
val offset = (page - 1) * limit
|
||||||
|
val newValues = values
|
||||||
|
.plus("offset" to offset)
|
||||||
|
.plus("limit" to limit)
|
||||||
|
|
||||||
|
val line = replaceArgs(sql, newValues) {
|
||||||
|
connect().sendPreparedStatement(this.sql, compileArgs(this.parameters)).get().rows[0]
|
||||||
}
|
}
|
||||||
inline fun <reified R : List<EntityI<*>>> select(sql: String, values: Map<String, Any?>): R = select(sql, object : TypeReference<R>() {}, values)
|
|
||||||
|
return line.run {
|
||||||
|
val json = getString(0)
|
||||||
|
val entities = if (json === null) {
|
||||||
|
listOf<EntityI<*>>() as List<R>
|
||||||
|
} else {
|
||||||
|
serializer.deserializeList(json, typeReference)
|
||||||
|
}
|
||||||
|
Paginated(
|
||||||
|
entities,
|
||||||
|
offset,
|
||||||
|
limit,
|
||||||
|
getInt("total") ?: error("The query not return total")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline fun <reified R: EntityI<*>> select(
|
||||||
|
sql: String,
|
||||||
|
page: Int,
|
||||||
|
limit: Int,
|
||||||
|
values: Map<String, Any?> = emptyMap()
|
||||||
|
): Paginated<R> =
|
||||||
|
select(sql, page, limit, object: TypeReference<List<R>>() {}, values)
|
||||||
|
|
||||||
|
override fun <R: List<EntityI<*>>> select(
|
||||||
|
sql: String,
|
||||||
|
typeReference: TypeReference<R>,
|
||||||
|
values: Map<String, Any?>
|
||||||
|
): R {
|
||||||
|
return replaceArgs(sql, values) {
|
||||||
|
select(this.sql, typeReference, this.parameters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified R: List<EntityI<*>>> select(sql: String, values: Map<String, Any?>): R =
|
||||||
|
select(sql, object: TypeReference<R>() {}, values)
|
||||||
|
|
||||||
override fun exec(sql: String, values: List<Any?>): CompletableFuture<QueryResult> {
|
override fun exec(sql: String, values: List<Any?>): CompletableFuture<QueryResult> {
|
||||||
return connect().sendPreparedStatement(sql, compileArgs(values))
|
return connect().sendPreparedStatement(sql, compileArgs(values))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun exec(sql: String, values: Map<String, Any?>): CompletableFuture<QueryResult> {
|
override fun exec(sql: String, values: Map<String, Any?>): CompletableFuture<QueryResult> {
|
||||||
val replacedQuery = replaceArgs(sql, values)
|
return replaceArgs(sql, values) {
|
||||||
return exec(replacedQuery.sql, replacedQuery.parameters)
|
exec(this.sql, this.parameters)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compileArgs(values: List<Any?>): List<Any?> {
|
private fun compileArgs(values: List<Any?>): List<Any?> {
|
||||||
@@ -96,7 +151,7 @@ class Connection(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun replaceArgs(sql: String, values: Map<String, Any?>): ParametersQuery {
|
private fun <T> replaceArgs(sql: String, values: Map<String, Any?>, block: ParametersQuery.() -> T): T {
|
||||||
val paramRegex = "(?<!:):([a-zA-Z0-9_-]+)".toRegex(RegexOption.IGNORE_CASE)
|
val paramRegex = "(?<!:):([a-zA-Z0-9_-]+)".toRegex(RegexOption.IGNORE_CASE)
|
||||||
val newArgs = paramRegex.findAll(sql).map { match ->
|
val newArgs = paramRegex.findAll(sql).map { match ->
|
||||||
val name = match.groups[1]!!.value
|
val name = match.groups[1]!!.value
|
||||||
@@ -109,10 +164,25 @@ class Connection(
|
|||||||
newSql = newSql.replace(regex, "?")
|
newSql = newSql.replace(regex, "?")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return block(ParametersQuery(newSql, newArgs))
|
||||||
return ParametersQuery(newSql, newArgs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ParametersQuery(val sql: String, val parameters: List<Any?>)
|
data class ParametersQuery(val sql: String, val parameters: List<Any?>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class Paginated<T: EntityI<*>>(
|
||||||
|
val result: List<T>,
|
||||||
|
val offset: Int,
|
||||||
|
val limit: Int,
|
||||||
|
val total: Int
|
||||||
|
) {
|
||||||
|
val currentPage: Int = (offset / limit) + 1
|
||||||
|
val count: Int = result.length
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (offset < 0) error("offset must be greather or equal than 0")
|
||||||
|
if (limit < 1) error("limit must be greather than 1")
|
||||||
|
if (total < 1) error("total must be greather or equal than 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package fr.postgresjson
|
|||||||
|
|
||||||
import com.github.jasync.sql.db.util.isCompleted
|
import com.github.jasync.sql.db.util.isCompleted
|
||||||
import fr.postgresjson.connexion.Connection
|
import fr.postgresjson.connexion.Connection
|
||||||
|
import fr.postgresjson.connexion.Paginated
|
||||||
import fr.postgresjson.entity.IdEntity
|
import fr.postgresjson.entity.IdEntity
|
||||||
import org.junit.Assert.*
|
import org.junit.Assert.*
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
@@ -129,4 +130,26 @@ class ConnectionTest(): TestAbstract() {
|
|||||||
assertEquals(result.seconde, "sec")
|
assertEquals(result.seconde, "sec")
|
||||||
assertEquals(result.third, 123)
|
assertEquals(result.third, 123)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `select paginated`() {
|
||||||
|
val result: Paginated<ObjTest> = connection.select(
|
||||||
|
"""
|
||||||
|
SELECT json_build_array(
|
||||||
|
json_build_object('id', 3, 'name', :name::text),
|
||||||
|
json_build_object('id', 4, 'name', :name::text || '-2')
|
||||||
|
), 10 as total
|
||||||
|
LIMIT :limit OFFSET :offset
|
||||||
|
""".trimIndent(),
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
mapOf("name" to "ff")
|
||||||
|
|
||||||
|
)
|
||||||
|
assertNotNull(result)
|
||||||
|
assertEquals(result.result[0].name, "ff")
|
||||||
|
assertEquals(result.result[1].name, "ff-2")
|
||||||
|
assertEquals(result.total, 10)
|
||||||
|
assertEquals(result.offset, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user