/* * 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 joinTableMap : MutableMap<String, String> = mutableMapOf() 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, ): SelectSQLBuilder<T> { return leftOuterJoin(tableClass, cond, Unit::class); } fun <U : Any, V : Any> leftOuterJoin( tableClass: KClass<U>, cond: String, parentTableClass: KClass<V> ): SelectSQLBuilder<T> { val joinTableName = tableClass.findAnnotation<Table>()?.name ?: throw IllegalArgumentException() val col : String if (parentTableClass == Unit::class) { col = joinTableName } else { val joinParentTableName = parentTableClass.findAnnotation<Table>()?.name ?: throw IllegalArgumentException() if (joinTableMap.containsKey(joinParentTableName)) { col = "${joinTableMap[joinParentTableName]}__${joinTableName}" joinTableMap.put(joinTableName, col) } else { throw IllegalArgumentException() } } joinTableMap.put(joinTableName, col) columns += "," + tableClass.memberProperties.mapNotNull { it.findAnnotation<Column>()?.name } .joinToString(separator = ",") { "${joinTableName}.${it} AS ${col}__${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 }