这个方法的目的是检查一个给定的项目ID(projectId)是否在当前数据库中被使用(搜索全库)
@Overridepublic Boolean existsUsedProject(Long projectId) {// 查询当前使用的库名String dataBaseName = "";dataBaseName = jdbcTemplate.queryForObject("SELECT DATABASE()", String.class);// 查询指定字段下的表名String queryTableNameSql = String.format("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.COLUMNS\n" +" WHERE TABLE_SCHEMA = '%s' AND COLUMN_NAME = 'project_id'", dataBaseName);List<String> tableNameList = jdbcTemplate.queryForList(queryTableNameSql, String.class);// 根据表名生成sql,并过滤不参与决策的表List<String> queryExistsSqlList = new ArrayList<>();tableNameList.removeIf(item ->ObjectUtil.equal(item, "gc_project_attr")|| ObjectUtil.equal(item, "gc_project_contract_sheet")|| ObjectUtil.equal(item, "gc_project_user")|| ObjectUtil.equal(item, "gc_store"));for (String tableName : tableNameList) {String queryExistsSql = String.format("SELECT EXISTS(SELECT 1 FROM %s where project_id = '%s' and (delete_flag is null or delete_flag = '0') LIMIT 1)",tableName,String.valueOf(projectId));queryExistsSqlList.add(queryExistsSql);}// 拼接 union all 的sqlfinal int BATCH_SIZE = 5;for (int i = 0; i < queryExistsSqlList.size(); i += BATCH_SIZE) {// 计算当前批次的结束位置int end = Math.min(i + BATCH_SIZE, queryExistsSqlList.size());// 获取当前批次的SQL列表List<String> batch = queryExistsSqlList.subList(i, end);// 构建UNION ALL查询语句String batchSql = String.join(" \n UNION ALL \n", batch);List<String> tempExecSqlResList = jdbcTemplate.queryForList(batchSql, String.class);if (tempExecSqlResList.contains("1")) {return Boolean.TRUE;}}return Boolean.FALSE;}
这个方法的主要功能是检查指定项目ID(projectId
)是否在数据库中被使用。它通过多步骤验证项目是否被关联到其他业务数据中,确保项目在删除或修改前未被引用。以下是详细逻辑:
核心步骤解析
1. 获取当前数据库名
String dataBaseName = jdbcTemplate.queryForObject("SELECT DATABASE()", String.class);
动态获取当前连接的数据库名称,用于后续系统表查询。
2. 查询包含project_id
字段的所有表
String queryTableNameSql = String.format("SELECT TABLE_NAME ... AND COLUMN_NAME = 'project_id'", dataBaseName); List<String> tableNameList = jdbcTemplate.queryForList(...);
从INFORMATION_SCHEMA.COLUMNS
系统表中检索当前数据库内所有包含project_id
字段的表名。
3. 过滤排除表
tableNameList.removeIf(item -> ObjectUtil.equal(item, "gc_project_attr") || ... // 排除4张特定表 );
移除不参与检查的表(如gc_project_attr
, gc_project_contract_sheet
等),这些表可能存储非核心关联数据。
4. 动态生成检查SQL
for (String tableName : tableNameList) {String sql = String.format("SELECT EXISTS(SELECT 1 FROM %s WHERE project_id='%s' AND (delete_flag IS NULL OR delete_flag='0')", ...);queryExistsSqlList.add(sql); }
为每个表生成检查SQL:判断指定projectId
是否存在且未被软删除(delete_flag
为null
或'0'
)。
5. 分批次执行SQL(关键优化)
final int BATCH_SIZE = 5; for (int i=0; i<queryExistsSqlList.size(); i+=BATCH_SIZE) {String batchSql = String.join(" \n UNION ALL \n", batch); // 每批最多5条SQL用UNION ALL合并List<String> results = jdbcTemplate.queryForList(batchSql, String.class);if (results.contains("1")) return true; // 发现引用立即返回 }
将大量SQL分批执行(每批5条),通过UNION ALL
合并查询。若任意表存在关联数据(返回"1"
),直接返回true
。
6. 最终返回
return Boolean.FALSE; // 所有检查均未找到引用
方法的核心目的
防止数据误删:在删除或修改项目前,确保它未被其他业务数据关联。
动态适配表结构:通过系统表自动发现所有包含
project_id
字段的表,无需硬编码表名。性能优化:分批次执行SQL避免数据库压力,并在发现引用后立即返回。
潜在风险
SQL注入:直接拼接
projectId
(Long类型风险较低)和表名(来自系统表,需确保无恶意表名)。性能问题:若包含
project_id
的表极多(如上百张),分批查询仍可能有性能压力。扩展性:新增业务表时需注意是否应加入排除列表。
典型应用场景
删除项目前的校验(如提示“项目已被使用,无法删除”)。
修改项目关键信息前的安全校验。