/*
* Copyright (c) 2023. yo-saito. All Rights Reserved.
*/
package net.piedpiper.bremer.utils
import net.piedpiper.bremer.utils.sql.Column
import net.piedpiper.bremer.utils.sql.Table
import org.apache.ibatis.jdbc.SQL
import java.time.LocalDateTime
import kotlin.reflect.KClass
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.memberProperties
import kotlin.reflect.javaType
class SelectSQLBuilder<T : Any>(tableClass: KClass<T>) {
private val tableName: String =
tableClass.findAnnotation<Table>()?.name ?: throw IllegalArgumentException()
private val sql: SQL = SQL().FROM(tableName)
private var columns: String =
tableClass.memberProperties
.mapNotNull { it.findAnnotation<Column>()?.name }
.joinToString(separator = ",") { "${tableName}.${it} AS ${it}" }
fun <U : Any> leftOuterJoin(
tableClass: KClass<U>,
cond: String,
prefix: String? = null
): SelectSQLBuilder<T> {
val joinTableName = tableClass.findAnnotation<Table>()?.name
?: throw IllegalArgumentException()
columns += "," + tableClass.memberProperties.mapNotNull { it.findAnnotation<Column>()?.name }
.joinToString(separator = ",") { "${joinTableName}.${it} AS ${prefix ?: joinTableName}__${it}" }
sql.LEFT_OUTER_JOIN("${joinTableName} ON ${cond}")
return this
}
fun toSql(): SQL {
return sql.SELECT(columns)
}
}
object DaoUtils {
@OptIn(ExperimentalStdlibApi::class)
@JvmStatic
fun <T : Any> insertOne(entity: T): String {
val properties = entity::class.memberProperties
.filter {
it.findAnnotation<Column>()?.let { it.insertable } ?: false
}
.filter { it.getter.call(entity) != null }
val sql =
SQL().INSERT_INTO(
entity::class.findAnnotation<Table>()?.name
?: throw java.lang.IllegalArgumentException()
);
val columns = properties.map { it.findAnnotation<Column>()?.name }
val values = properties.map {
if (it.returnType.javaType == LocalDateTime::class.java)
"#{entity.${it?.name}, typeHandler=net.piedpiper.bremer.dao.handler.LocalDateTimeTypeHandler}"
else
"#{entity.${it?.name}}"
}
repeat(columns.size) {
sql.VALUES(columns[it], values[it])
}
return sql.toString()
}
@OptIn(ExperimentalStdlibApi::class)
@JvmStatic
fun <T : Any> updateOne(entity: T): String {
val properties = entity::class.memberProperties
.filter {
val column = it.findAnnotation<Column>()
column != null && column.insertable
}
.filter { it.getter.call(entity) != null }
val sql =
SQL().UPDATE(
entity::class.findAnnotation<Table>()?.name
?: throw java.lang.IllegalArgumentException()
);
properties.forEach {
val v = if (it.returnType.javaType == LocalDateTime::class.java)
"#{entity.${it?.name}, typeHandler=net.piedpiper.bremer.dao.handler.LocalDateTimeTypeHandler}"
else
"#{entity.${it?.name}}"
sql.SET("${it.findAnnotation<Column>()?.name} = ${v}")
}
return sql.WHERE("id = #{entity.id}").toString()
}
@JvmStatic
fun <T : Any> deleteQuery(
clazz: KClass<T>,
): SQL = SQL().DELETE_FROM(
clazz.findAnnotation<Table>()?.name
?: throw java.lang.IllegalArgumentException()
)
@JvmStatic
private fun <T : Any> deleteFrom(
clazz: KClass<T>,
): SQL = SQL().DELETE_FROM(
clazz.findAnnotation<Table>()?.name
?: throw java.lang.IllegalArgumentException()
)
}
fun SQL.WHERE_IN(
targetColumn: String,
parameterName: String,
num: Int,
): SQL {
if (num == 0) {
throw IllegalArgumentException()
}
generateSequence(0) { v -> v + 1 }.take(num).map {
"#{${parameterName}[${it}]}"
}.joinToString(",", "(", ")").let {
WHERE("${targetColumn} IN ${it}")
}
return this
}