|
@@ -0,0 +1,453 @@
|
|
|
+/*
|
|
|
+ * ********************************************************************************************************************
|
|
|
+ *
|
|
|
+ * iFHS7.
|
|
|
+ * ;BBMBMBMc rZMBMBR BMB
|
|
|
+ * MBEr:;PBM, 7MBMMEOBB: BBB RBW
|
|
|
+ * XK: BO SB. :SZ MBM. c;; ir BBM :FFr :SSF: ;xBMB:r iuGXv. i:. iF2;
|
|
|
+ * DBBM0r. :D S7 ;XMBMB GMBMu. MBM: BMB MBMBBBMBMS WMBMBMBBK MBMBMBM BMBRBMBW .MBMBMBMBB
|
|
|
+ * :JMRMMD .. , 1MMRM1; ;MBMBBR: MBM ;MB: BMB: MBM. RMBr sBMH BM0 UMB, BMB. KMBv
|
|
|
+ * ;. XOW B1; :uM: 1RE, i .2BMBs rMB. MBO MBO JMB; MBB MBM BBS 7MBMBOBM: MBW :BMc
|
|
|
+ * OBRJ.SEE MRDOWOR, 3DE:7OBM . ;BMB RMR7BM BMB MBB. BMB ,BMR .BBZ MMB rMB, BMM rMB7
|
|
|
+ * :FBRO0D0 RKXSXPR. JOKOOMPi BMBSSWBMB; BMBB: MBMB0ZMBMS .BMBOXRBMB MBMDE RBM2;SMBM; MBB xBM2
|
|
|
+ * iZGE O0SHSPO. uGZ7. sBMBMBDL :BMO OZu:BMBK, rRBMB0; ,EBMB xBMBr:ER. RDU :OO;
|
|
|
+ * ,BZ, 1D0 RPSFHXR. xWZ .SMr . .BBB
|
|
|
+ * :0BMRDG RESSSKR. 2WOMBW; BMBMR
|
|
|
+ * i0BM: SWKHKGO MBDv
|
|
|
+ * .UB OOGDM. MK, Copyright (c) 2015-2019. 斯伯坦机器人
|
|
|
+ * , XMW ..
|
|
|
+ * r All rights reserved.
|
|
|
+ *
|
|
|
+ * ********************************************************************************************************************
|
|
|
+ */
|
|
|
+
|
|
|
+package com.sybotan.android.database
|
|
|
+
|
|
|
+import android.util.Log
|
|
|
+import com.sybotan.android.database.SDatabase.use
|
|
|
+import com.sybotan.base.exceptions.SFieldNotFoundException
|
|
|
+import com.sybotan.base.extensions.*
|
|
|
+import com.sybotan.database.SAbstractDao
|
|
|
+import com.sybotan.database.SFilter
|
|
|
+import com.sybotan.database.SQueryBuilder
|
|
|
+import com.sybotan.database.enums.SOps
|
|
|
+import com.sybotan.database.enums.SDirection
|
|
|
+import org.jetbrains.anko.db.*
|
|
|
+import java.io.Serializable
|
|
|
+import javax.persistence.Column
|
|
|
+
|
|
|
+/**
|
|
|
+ * 数据库访问对象
|
|
|
+ *
|
|
|
+ * @author 庞利祥(sybotan@126.com)
|
|
|
+ */
|
|
|
+open class SAndroidDao<ENTITY: Serializable>(entityClazz: Class<ENTITY>): SAbstractDao<ENTITY>(entityClazz) {
|
|
|
+ companion object {
|
|
|
+ val TAG = SAndroidDao::class.java.name
|
|
|
+ } // Companion object
|
|
|
+
|
|
|
+ /** 行解析器 */
|
|
|
+ private val parser = SRowParser<ENTITY>(entityClass)
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 在数据库中插入实体
|
|
|
+ *
|
|
|
+ * @param entity 插入数据库的实体
|
|
|
+ * @return 返回实体。如果主键为数据库生成时,该实体主键将被填充。
|
|
|
+ */
|
|
|
+ override fun insert(entity: ENTITY): Boolean {
|
|
|
+ use {
|
|
|
+ val listPair= buildInsertValues(entity)
|
|
|
+ insert("`${tableName()}`", *Array(listPair.size){i-> listPair[i]})
|
|
|
+ }
|
|
|
+ return true
|
|
|
+ } // Function insert()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据条件删除
|
|
|
+ *
|
|
|
+ * @param args 删除条件
|
|
|
+ */
|
|
|
+ override fun delete(args: List<SFilter>): Boolean {
|
|
|
+ use {
|
|
|
+ val whereArgs = ArrayList<Pair<String, Any>>()
|
|
|
+ val clause = buildWhereArgList(args, whereArgs)
|
|
|
+ delete("`${tableName()}`", clause, *Array(whereArgs.size){i-> whereArgs[i]})
|
|
|
+ }
|
|
|
+ return true
|
|
|
+ } // Function delete()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 删除所有
|
|
|
+ */
|
|
|
+ override fun deleteAll(): Boolean {
|
|
|
+ use {
|
|
|
+ delete(tableName())
|
|
|
+ }
|
|
|
+ return true
|
|
|
+ } // Function deleteAll()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据条件更新
|
|
|
+ *
|
|
|
+ * @param projection 将被更新的字段列表(以逗号分隔),为空表示更新所有非空字段
|
|
|
+ * @param args 删除条件
|
|
|
+ * @return 更新是否成功
|
|
|
+ */
|
|
|
+ override fun update(entity: ENTITY, projection: ArrayList<String>?, args: List<SFilter>): Boolean {
|
|
|
+ use {
|
|
|
+ val whereArgs = ArrayList<Pair<String, Any>>()
|
|
|
+ val clause = buildWhereArgList(args, whereArgs)
|
|
|
+ val valueList = buildUpdateValues(entity, projection)
|
|
|
+ update("`${tableName()}`", *Array(valueList.size){ i-> valueList[i]})
|
|
|
+ .whereArgs(clause, *Array(whereArgs.size){i-> whereArgs[i]}).exec()
|
|
|
+ }
|
|
|
+ return true
|
|
|
+ } // Function update()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据条件查询记录
|
|
|
+ *
|
|
|
+ * @param args 查询条件
|
|
|
+ * @return 查询到的记录
|
|
|
+ */
|
|
|
+ override fun select(args: List<SFilter>): SQueryBuilder<ENTITY> {
|
|
|
+ // 以下循环解决 “SingletonList cannot be cast to java.util.ArrayList” 错误。
|
|
|
+ val filterList = ArrayList<SFilter>()
|
|
|
+ args.forEach {
|
|
|
+ filterList.add(it)
|
|
|
+ }
|
|
|
+ return SQueryBuilder(this, filterList)
|
|
|
+ } // Function select()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询所有记录
|
|
|
+ *
|
|
|
+ * @return 查询到的记录
|
|
|
+ */
|
|
|
+ override fun selectAll(): SQueryBuilder<ENTITY> {
|
|
|
+ return SQueryBuilder(this, ArrayList())
|
|
|
+ } // Function selectAll()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行查询操作
|
|
|
+ *
|
|
|
+ * @param builder 查询查询构造器
|
|
|
+ * @return 查询到的记录
|
|
|
+ */
|
|
|
+ override fun execQuery(builder: SQueryBuilder<ENTITY>): ArrayList<ENTITY> {
|
|
|
+ var list = ArrayList<ENTITY>()
|
|
|
+ use {
|
|
|
+ val queryBuilder = select("`${builder.tableName ?: tableName()}`")
|
|
|
+ // 设置查询条件
|
|
|
+ if (builder.whereArgs.isNotEmpty()) { // 有查询条件
|
|
|
+ val whereArgs = ArrayList<Pair<String, Any>>()
|
|
|
+ val clause = buildWhereArgList(builder.whereArgs, whereArgs)
|
|
|
+ Log.d(TAG, "clause=$clause")
|
|
|
+ Log.d(TAG, "whereArgs=${whereArgs.toJson()}")
|
|
|
+ queryBuilder.whereArgs(clause, *Array(whereArgs.size) { i -> whereArgs[i] })
|
|
|
+ }
|
|
|
+
|
|
|
+ //queryBuilder.column(buildColumns(gennericClass()!!, builder.fieldList))
|
|
|
+
|
|
|
+ // 设置排序条件
|
|
|
+ for (order in builder.orderArgs) { // 依次取出排序条件
|
|
|
+ if (order.direction == SDirection.ASC) {
|
|
|
+ queryBuilder.orderBy("`${this@SAndroidDao.columnName(order.name)!!}`")
|
|
|
+ } else {
|
|
|
+ queryBuilder.orderBy("`${this@SAndroidDao.columnName(order.name)!!}`", SqlOrderDirection.DESC)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (builder.isDistinct) { // 如果去重
|
|
|
+ queryBuilder.distinct()
|
|
|
+ }
|
|
|
+
|
|
|
+ if (builder.count != null) { // 限定从指定记录开始取出的记录数与
|
|
|
+ if (builder.offset != null) {
|
|
|
+ queryBuilder.limit(builder.offset!!, builder.count!!)
|
|
|
+ } else {
|
|
|
+ queryBuilder.limit(builder.count!!)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 执行查询
|
|
|
+ queryBuilder.exec {
|
|
|
+ list = this.parseList(parser) as ArrayList<ENTITY>
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return list
|
|
|
+ } // Function exec()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行查询操作
|
|
|
+ *
|
|
|
+ * @param builder 查询查询构造器
|
|
|
+ * @return 查询到的记录
|
|
|
+ */
|
|
|
+ override fun execCount(builder: SQueryBuilder<ENTITY>): Long {
|
|
|
+ var result = 0L
|
|
|
+ var list = ArrayList<Serializable>()
|
|
|
+ use {
|
|
|
+ val queryBuilder = select("`${builder.tableName ?: tableName()}`")
|
|
|
+ // 设置查询条件
|
|
|
+ if (builder.whereArgs.isNotEmpty()) { // 有查询条件
|
|
|
+ val whereArgs = ArrayList<Pair<String, Any>>()
|
|
|
+ val clause = "count(1)"
|
|
|
+ Log.d(TAG, "clause=$clause")
|
|
|
+ Log.d(TAG, "whereArgs=${whereArgs.toJson()}")
|
|
|
+ queryBuilder.whereArgs(clause, *Array(whereArgs.size) { i -> whereArgs[i] })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 执行查询
|
|
|
+ queryBuilder.exec {
|
|
|
+ result = this.parseSingle(object:RowParser<Long>{
|
|
|
+ override fun parseRow(columns: Array<Any?>): Long {
|
|
|
+ return columns[0]!!.toLong() ?: 0
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result
|
|
|
+ } // Function exec()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获得列名
|
|
|
+ *
|
|
|
+ * @param name 属性名
|
|
|
+ * @return 对应的数据表字段名
|
|
|
+ */
|
|
|
+ private fun columnName(name: String): String? {
|
|
|
+ return try {
|
|
|
+ val clz = gennericClass()
|
|
|
+ val field = clz!!.getAllDeclaredField(name, true, true)
|
|
|
+ val anno = field?.getAnnotation(Column::class.java)
|
|
|
+ anno!!.name
|
|
|
+ } catch (e: Exception) {
|
|
|
+ null
|
|
|
+ }
|
|
|
+ } // Function columnName()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构造where子句参数
|
|
|
+ *
|
|
|
+ * @param filterList 过滤条件
|
|
|
+ * @param args 子句参数列表
|
|
|
+ * @return 返回where子句
|
|
|
+ */
|
|
|
+ private fun buildWhereArgList(filterList: List<SFilter>, args: ArrayList<Pair<String, Any>>): String {
|
|
|
+ val builder = StringBuilder()
|
|
|
+ for (filter in filterList) {
|
|
|
+ val where = buildWhereArg(filter, args)
|
|
|
+ if (builder.isNotEmpty()) {
|
|
|
+ builder.append(" AND ")
|
|
|
+ }
|
|
|
+ builder.append(where)
|
|
|
+ }
|
|
|
+ return builder.toString()
|
|
|
+ } // Function buildWhereArgList()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构造where子句参数
|
|
|
+ *
|
|
|
+ * @param filter 过滤条件
|
|
|
+ * @param args 子句参数列表
|
|
|
+ * @return 返回where子句
|
|
|
+ */
|
|
|
+ fun buildWhereArg(filter: SFilter, args: ArrayList<Pair<String, Any>>): String {
|
|
|
+ val builder = StringBuilder()
|
|
|
+ try {
|
|
|
+ val argName = "arg${args.size}"
|
|
|
+ when (filter.opt) {
|
|
|
+ SOps.EQ -> { // 等于
|
|
|
+ builder.append("`${columnName(filter.first!!.toString())}` = {$argName}")
|
|
|
+ args.add(Pair(argName, filter.second!!))
|
|
|
+ }
|
|
|
+ SOps.NE -> { // 不等于
|
|
|
+ builder.append("`${columnName(filter.first!!.toString())}` <> {$argName}")
|
|
|
+ args.add(Pair(argName, filter.second!!))
|
|
|
+ }
|
|
|
+ SOps.GT -> { // 大于
|
|
|
+ builder.append("`${columnName(filter.first!!.toString())}` > {$argName}")
|
|
|
+ args.add(Pair(argName, filter.second!!))
|
|
|
+ }
|
|
|
+ SOps.GTE -> { // 大于等于
|
|
|
+ builder.append("`${columnName(filter.first!!.toString())}` >= {$argName}")
|
|
|
+ args.add(Pair(argName, filter.second!!))
|
|
|
+ }
|
|
|
+ SOps.LT -> { // 小于
|
|
|
+ builder.append("`${columnName(filter.first!!.toString())}` < {$argName}")
|
|
|
+ args.add(Pair(argName, filter.second!!))
|
|
|
+ }
|
|
|
+ SOps.LTE -> { // 小于等行
|
|
|
+ builder.append("`${columnName(filter.first!!.toString())}` <= {$argName}")
|
|
|
+ args.add(Pair(argName, filter.second!!))
|
|
|
+ }
|
|
|
+ SOps.CONTAIN -> { // 包含
|
|
|
+ builder.append("`${columnName(filter.first!!.toString())}` LIKE \"%\" || {$argName} || \"%\"")
|
|
|
+ args.add(Pair(argName, filter.second!!))
|
|
|
+ }
|
|
|
+ SOps.STARTWITH -> { // 小于等行
|
|
|
+ builder.append("`${columnName(filter.first!!.toString())}` LIKE {$argName} || \"%\"")
|
|
|
+ args.add(Pair(argName, filter.second!!))
|
|
|
+ }
|
|
|
+ SOps.ENDWITH -> { // 小于等行
|
|
|
+ builder.append("`${columnName(filter.first!!.toString())}` LIKE \"%\" || {$argName}")
|
|
|
+ args.add(Pair(argName, filter.second!!))
|
|
|
+ }
|
|
|
+ SOps.ISNULL -> { // 为空
|
|
|
+ builder.append("`${columnName(filter.first!!.toString())}` ISNULL")
|
|
|
+ }
|
|
|
+ SOps.NOTNULL -> { // 不为空
|
|
|
+ builder.append("`${columnName(filter.first!!.toString())}` NOTNULL")
|
|
|
+ }
|
|
|
+ SOps.IN -> { // 在列表内
|
|
|
+ val inArg = buidInArgs(filter, args, argName)
|
|
|
+ builder.append("`${columnName(filter.first!!.toString())}` IN ($inArg)")
|
|
|
+ }
|
|
|
+ SOps.NOT -> { // 逻辑非
|
|
|
+ builder.append("NOT (${buildWhereArg(filter.first as SFilter, args)})")
|
|
|
+ }
|
|
|
+ SOps.AND -> { // 逻辑与
|
|
|
+ builder.append("(${buildWhereArg(filter.first as SFilter, args)}) AND (${buildWhereArg(filter.second as SFilter, args)})")
|
|
|
+ }
|
|
|
+ SOps.OR -> { // 逻辑或
|
|
|
+ builder.append("((${buildWhereArg(filter.first as SFilter, args)}) OR (${buildWhereArg(filter.second as SFilter, args)}))")
|
|
|
+ }
|
|
|
+ else -> {
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (e: Exception) {
|
|
|
+ e.printStackTrace()
|
|
|
+ }
|
|
|
+ return builder.toString()
|
|
|
+ } // Function buildWhereArgList()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成IN操作参数
|
|
|
+ */
|
|
|
+ private fun buidInArgs(filter: SFilter, args: ArrayList<Pair<String, Any>>, argName: String): String {
|
|
|
+ val builder = StringBuilder()
|
|
|
+ val argList = filter.second!! as List<Any>
|
|
|
+ for ((index, item) in argList.withIndex()) {
|
|
|
+ if (index > 0) {
|
|
|
+ builder.append(", {${argName}_$index}")
|
|
|
+ } else {
|
|
|
+ builder.append("{${argName}_$index}")
|
|
|
+ }
|
|
|
+ args.add(Pair("${argName}_$index", item))
|
|
|
+ }
|
|
|
+ return builder.toString()
|
|
|
+ } // Function buidInArgs()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建values值
|
|
|
+ *
|
|
|
+ * @param entity 实体对象
|
|
|
+ * @return values列表
|
|
|
+ */
|
|
|
+ protected fun buildInsertValues(entity: ENTITY): ArrayList<Pair<String, Any?>> {
|
|
|
+ val clz = entity::class.java
|
|
|
+ val list = ArrayList<Pair<String, Any?>>()
|
|
|
+ for (field in clz.declaredFields) {
|
|
|
+ if (field.isAnnotationPresent(Column::class.java)) {
|
|
|
+ val anno = field.getAnnotation(Column::class.java) ?: continue
|
|
|
+ // 如果插入状态或更新状态为否,则跳过该字段
|
|
|
+ if (!anno.insertable || !anno.updatable) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ field.isAccessible = true
|
|
|
+ val column = fieldToPair(entity, field)
|
|
|
+ if (column.second != null) {
|
|
|
+ list.add(column)
|
|
|
+ }
|
|
|
+ field.isAccessible = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return list
|
|
|
+ } // Function buildValues()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建values值
|
|
|
+ *
|
|
|
+ * @param entity 实体对象
|
|
|
+ * @param projection 字段映射(未转换)
|
|
|
+ * @return values列表
|
|
|
+ */
|
|
|
+ protected fun buildUpdateValues(entity: ENTITY, projection: ArrayList<String>?): ArrayList<Pair<String, Any?>> {
|
|
|
+ val clz = entity::class.java
|
|
|
+ val fieldList = ArrayList<String>()
|
|
|
+ try {
|
|
|
+ // 转换属性名
|
|
|
+ for (name in projection!!) {
|
|
|
+ fieldList.add(propertyName(entity.javaClass, name.trim()))
|
|
|
+ }
|
|
|
+ } catch (e: Exception) {
|
|
|
+ // DO NOTHING
|
|
|
+ }
|
|
|
+
|
|
|
+ val updateNull = fieldList.size > 0
|
|
|
+ val list = ArrayList<Pair<String, Any?>>()
|
|
|
+ for (field in clz.declaredFields) {
|
|
|
+ if (field.isAnnotationPresent(Column::class.java)) {
|
|
|
+ if (fieldList.size > 0 && !fieldList.contains(field.name)) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ val anno = field.getAnnotation(Column::class.java) ?: continue
|
|
|
+ // 如果更新状态为否,则跳过该字段
|
|
|
+ if (!anno.updatable) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ field.isAccessible = true
|
|
|
+ val column = fieldToPair(entity, field)
|
|
|
+ if (updateNull || column.second != null) {
|
|
|
+ list.add(column)
|
|
|
+ }
|
|
|
+ field.isAccessible = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return list
|
|
|
+ } // Function buildValues()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建columns值
|
|
|
+ *
|
|
|
+ * @param clazz 实体类类型
|
|
|
+ * @param fieldList 属性映射列表
|
|
|
+ * @return values列表
|
|
|
+ */
|
|
|
+ protected fun buildColumns(clazz: Class<*>, fieldList: ArrayList<String>?): String {
|
|
|
+ val builder = StringBuilder()
|
|
|
+ for (field in clazz.getAllDeclaredFields()) {
|
|
|
+ if (fieldList != null && fieldList.size > 0 && !fieldList.contains(field.name)) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ val anna = field.getAnnotation(Column::class.java) ?: continue
|
|
|
+
|
|
|
+ if (builder.isNotEmpty()) {
|
|
|
+ builder.append(", ")
|
|
|
+ }
|
|
|
+ builder.append("`${anna.name}` AS `${field.name}`")
|
|
|
+ }
|
|
|
+ return builder.toString()
|
|
|
+ } // Function buildColumns()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获得实体类属性名
|
|
|
+ *
|
|
|
+ * @param clazz 实体类类型
|
|
|
+ * @param name 名称
|
|
|
+ * @return 实体类属性名
|
|
|
+ */
|
|
|
+ protected fun propertyName(clazz: Class<*>, name: String): String {
|
|
|
+ try {
|
|
|
+ val field = clazz.getAllDeclaredField(name, true, true)
|
|
|
+ return field!!.name
|
|
|
+ } catch (e: Exception) {
|
|
|
+ throw SFieldNotFoundException(name)
|
|
|
+ }
|
|
|
+ } // Function propertyName()
|
|
|
+} // Class SAndroidDao
|