Coverage Summary for Class: CascadeUpdateClause (com.kotlinorm.orm.cascade)

Class Class, % Method, % Branch, % Line, % Instruction, %
CascadeUpdateClause 100% (1/1) 28.6% (2/7) 15% (3/20) 22.2% (14/63) 25.4% (92/362)


 /**
  * Copyright 2022-2025 kronos-orm
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 package com.kotlinorm.orm.cascade
 
 import com.kotlinorm.beans.dsl.Field
 import com.kotlinorm.interfaces.KPojo
 import com.kotlinorm.beans.task.KronosActionTask
 import com.kotlinorm.beans.task.KronosActionTask.Companion.toKronosActionTask
 import com.kotlinorm.beans.task.KronosAtomicActionTask
 import com.kotlinorm.cache.kPojoAllFieldsCache
 import com.kotlinorm.enums.KOperationType
 import com.kotlinorm.orm.cascade.NodeOfKPojo.Companion.toTreeNode
 import com.kotlinorm.orm.select.select
 import com.kotlinorm.orm.update.update
 import com.kotlinorm.utils.KStack
 import com.kotlinorm.utils.pop
 import com.kotlinorm.utils.push
 import kotlin.reflect.KClass
 
 object CascadeUpdateClause {
 
     fun <T : KPojo> build(
         cascade: Boolean,
         cascadeAllowed: Set<Field>? = null,
         pojo: T,
         kClass: KClass<KPojo>,
         paramMap: Map<String, Any?>,
         toUpdateFields: LinkedHashSet<Field>,
         whereClauseSql: String?,
         rootTask: KronosAtomicActionTask
     ) =
         if (cascade) generateTask(
             cascadeAllowed, pojo, kClass, paramMap, toUpdateFields, whereClauseSql, rootTask
         ) else rootTask.toKronosActionTask()
 
     private fun <T : KPojo> generateTask(
         cascadeAllowed: Set<Field>? = null,
         pojo: T,
         kClass: KClass<KPojo>,
         paramMap: Map<String, Any?>,
         toUpdateFields: LinkedHashSet<Field>,
         whereClauseSql: String?,
         rootTask: KronosAtomicActionTask
     ): KronosActionTask {
         val toUpdateRecords: MutableList<KPojo> = mutableListOf()
         val validCascades = findValidRefs( // 获取有效的引用
             kClass,
             kPojoAllFieldsCache[kClass]!!,
             KOperationType.UPDATE,
             cascadeAllowed?.filter { it.tableName == pojo.kronosTableName()}?.map { it.name }?.toSet(), // 获取当前Pojo内允许级联的属性
             cascadeAllowed.isNullOrEmpty() // 是否允许所有属性级联
         ).filter { !it.mapperByThis }
 
         return rootTask.toKronosActionTask().apply {
             doBeforeExecute { wrapper ->
                 if (validCascades.isEmpty()) return@doBeforeExecute // 如果没有级联,直接返回
                 toUpdateRecords.addAll(
                     pojo.select()
                         .where { whereClauseSql.asSql() }
                         .patch(*paramMap.toList().toTypedArray())
                         .apply {
                             this.cascadeAllowed = cascadeAllowed
                             this.operationType = KOperationType.UPDATE
                         }
                         .queryList(wrapper)
                 )
                 if (toUpdateRecords.isEmpty()) return@doBeforeExecute
                 val forest = toUpdateRecords.map { record ->
                     record.toTreeNode(
                         NodeInfo(true),
                         operationType = KOperationType.UPDATE,
                         cascadeAllowed = cascadeAllowed,
                         updateParams = toUpdateFields.associateTo(mutableMapOf()) { it.name to it.name }
                     )
                 }
 
                 if (forest.any { it.children.isNotEmpty() }) {
                     this.atomicTasks.clear() // 清空原有的任务
                     val list = mutableListOf<NodeOfKPojo>()
                     forest.forEach { tree ->
                         val stack = KStack<NodeOfKPojo>() // 用于深度优先遍历
                         val all = KStack<NodeOfKPojo>() // 用于存储所有的节点
                         stack.push(tree) // 将根节点压入栈
                         var tmp: NodeOfKPojo
                         while (!stack.isEmpty()) { // 深度优先遍历
                             tmp = stack.pop()
                             all.push(tmp)
                             tmp.children.forEach {
                                 stack.push(it) // 将子节点压入栈
                             }
                         }
                         while (!all.isEmpty()) {
                             list.add(all.pop()) // 将所有节点压入list
                         }
                     }
                     atomicTasks.addAll(
                         list.mapNotNull {
                             getTask(it, paramMap)?.atomicTasks
                         }.flatten()
                     )
                 }
             }
         }
     }
 
     private fun getTask(
         node: NodeOfKPojo,
         paramMap: Map<String, Any?>
     ): KronosActionTask? {
         if (null == node.data) return null
         return node.kPojo.update().apply {
             node.updateParams.forEach { (_, value) ->
                 val updateField = allFields.first { it.name == value }
                 toUpdateFields += updateField
                 paramMapNew[updateField + "New"] = paramMap[value + "New"]
             }
         }.build()
     }
 
 }