这个方法的目的是检查一个给定的项目ID(projectId)是否在当前数据库中被使用(搜索全库)

发布于:2025-07-12 ⋅ 阅读:(15) ⋅ 点赞:(0)
  @Override
    public 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 的sql
        final 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_attrgc_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_flagnull'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避免数据库压力,并在发现引用后立即返回。


潜在风险

  1. SQL注入:直接拼接projectId(Long类型风险较低)和表名(来自系统表,需确保无恶意表名)。

  2. 性能问题:若包含project_id的表极多(如上百张),分批查询仍可能有性能压力。

  3. 扩展性:新增业务表时需注意是否应加入排除列表。


典型应用场景

  • 删除项目前的校验(如提示“项目已被使用,无法删除”)。

  • 修改项目关键信息前的安全校验。


网站公告

今日签到

点亮在社区的每一天
去签到