7EykAb1R 发表于 2025-2-22 18:40:16

反范式设计,冗余用户姓名,修改用户姓名后,业务表同步更新 -- MySQL 存储过程

反范式设计,冗余用户姓名,通过存储过程进行业务表的同步更新。
所有的表,在创建的时候,都加了创建人、修改人的字段。。用户姓名发生变化时,要将所有的表都更新一遍。
创建存储过程

MySQL
CREATE PROCEDURE UpdateAllUserInfo(IN userId VARCHAR(255), IN newName VARCHAR(255))BEGIN    DECLARE var_table_name VARCHAR(255);    DECLARE done INT DEFAULT 0;            -- 定义游标,查找所有符合条件的表    DECLARE cur CURSOR FOR      -- 变量名要和字段名不同,否则后面取变量时,取不取值      SELECT TABLE_NAME as var_table_name          FROM INFORMATION_SCHEMA.COLUMNS      WHERE TABLE_SCHEMA = 'vipsoft'-- 注意库名      AND COLUMN_NAME IN ('create_user_name','create_userId')      GROUP BY TABLE_NAME      HAVING COUNT(DISTINCT COLUMN_NAME) = 2;    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;    -- 打开游标    OPEN cur;    -- 循环处理每张表    read_loop: LOOP      FETCH cur INTO var_table_name;      IF done THEN            LEAVE read_loop;      END IF;      -- 动态生成 SQL 语句      SET @createUser = CONCAT('UPDATE ', var_table_name, ' SET create_user_name = "', newName, '" WHERE create_userId = "', userId, '";');      -- 打印SQL      -- SELECT @createUser;      -- 执行动态 SQL      PREPARE c_stmt FROM @createUser;      EXECUTE c_stmt;      DEALLOCATE PREPARE c_stmt;       -- 动态生成 SQL 语句      SET @updateUser = CONCAT('UPDATE ', var_table_name, ' SET update_user_name = "', newName, '" WHERE update_userId = "', userId, '";');      -- SELECT @updateUser;      -- 执行动态 SQL      PREPARE u_stmt FROM @updateUser;      EXECUTE u_stmt;      DEALLOCATE PREPARE u_stmt;    END LOOP;    -- 关闭游标    CLOSE cur;    -- 更新固定表    SET @proInfo = CONCAT('UPDATE project_info SET project_manager_name = "', newName, '" WHERE project_manager_id = "', userId, '";');    -- 执行动态 SQL    PREPARE pro_stmt FROM @proInfo;    EXECUTE pro_stmt;    DEALLOCATE PREPARE pro_stmt;    -- 普通SQL更新    UPDATE project_task SET user_name = proName WHERE project_id = proId;   END代码调用

MyBatis-Plus 调用代码
/** * 数据层 */public interface SysUserMapper extends BaseMapper<SysUser> {    @Select("CALL UpdateAllUserInfo(#{userId}, #{newName})")    void callUpdateUserInfo(@Param("userId") String userId, @Param("newName") String newName);}@Autowiredprivate UserMapper userMapper;//service 层如果姓名有变化,才更新if (entity != null && !entity.getRealName().equals(param.getRealName())) {   userMapper.callUpdateUserInfo(userInfo.getId(), userInfo.getRealName());}在 MyBatis-Plus 中调用存储过程(如 CALL UpdateUserInfo('张三', 'zs');),可以通过以下步骤实现:
<hr>1. 使用 @Select 注解调用存储过程

如果你的存储过程没有返回值,可以直接使用 @Select 注解调用存储过程。
示例代码

import org.apache.ibatis.annotations.Select;import org.springframework.stereotype.Repository;@Repositorypublic interface UserMapper {    @Select("CALL UpdateUserInfo(#{newName}, #{userId})")    void callUpdateUserInfo(@Param("newName") String newName, @Param("userId") String userId);}调用方法

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class UserService {    @Autowired    private UserMapper userMapper;    public void updateUserInfo(String newName, String userId) {      userMapper.callUpdateUserInfo(newName, userId);    }}<hr>2. 使用 @Options 注解设置存储过程调用

如果存储过程有输出参数或需要设置其他选项,可以使用 @Options 注解。
示例代码

import org.apache.ibatis.annotations.Options;import org.apache.ibatis.annotations.Select;import org.apache.ibatis.mapping.StatementType;import org.springframework.stereotype.Repository;@Repositorypublic interface UserMapper {    @Select("CALL UpdateUserInfo(#{newName}, #{userId})")    @Options(statementType = StatementType.CALLABLE)    void callUpdateUserInfo(@Param("newName") String newName, @Param("userId") String userId);}<hr>3. 使用 XML 配置调用存储过程

如果你更喜欢使用 XML 配置,可以在 Mapper.xml 文件中定义存储过程调用。
示例代码

在 UserMapper.xml 中:
<select id="callUpdateUserInfo" statementType="CALLABLE">    CALL UpdateUserInfo(#{newName}, #{userId})</select>在 UserMapper 接口中:
import org.apache.ibatis.annotations.Param;import org.springframework.stereotype.Repository;@Repositorypublic interface UserMapper {    void callUpdateUserInfo(@Param("newName") String newName, @Param("userId") String userId);}<hr>4. 处理存储过程的输出参数

如果存储过程有输出参数,可以通过 Map 或自定义对象接收。
示例代码

import org.apache.ibatis.annotations.Options;import org.apache.ibatis.annotations.Select;import org.apache.ibatis.mapping.StatementType;import org.springframework.stereotype.Repository;import java.util.Map;@Repositorypublic interface UserMapper {    @Select("CALL UpdateUserInfo(#{newName}, #{userId}, #{result, mode=OUT, jdbcType=INTEGER})")    @Options(statementType = StatementType.CALLABLE)    void callUpdateUserInfo(      @Param("newName") String newName,      @Param("userId") String userId,      @Param("result") Map<String, Object> resultMap    );}调用方法

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.HashMap;import java.util.Map;@Servicepublic class UserService {    @Autowired    private UserMapper userMapper;    public void updateUserInfo(String newName, String userId) {      Map<String, Object> resultMap = new HashMap<>();      userMapper.callUpdateUserInfo(newName, userId, resultMap);      System.out.println("存储过程执行结果: " + resultMap.get("result"));    }}<hr>5. 注意事项


[*]数据库权限:

[*]确保应用程序连接数据库的用户有权限调用存储过程。

[*]存储过程参数:

[*]确保传入的参数类型和数量与存储过程定义一致。

[*]事务管理:

[*]如果存储过程中包含多个 SQL 操作,建议在调用存储过程时启用事务管理。

[*]错误处理:

[*]捕获并处理存储过程执行过程中可能抛出的异常。

<hr>6. 完整示例

以下是一个完整的示例,展示如何在 MyBatis-Plus 中调用存储过程:
存储过程定义

CREATE PROCEDURE UpdateUserInfo(IN new_name VARCHAR(255), IN user_id VARCHAR(255))BEGIN    -- 更新逻辑    UPDATE pe_test.user_table SET create_user_name = new_name WHERE create_user_id = user_id;    UPDATE pe_test.demo SET member_name = new_name WHERE member_id = user_id;END;MyBatis-Plus 调用代码

import org.apache.ibatis.annotations.Select;import org.springframework.stereotype.Repository;@Repositorypublic interface UserMapper {    @Select("CALL UpdateUserInfo(#{newName}, #{userId})")    void callUpdateUserInfo(@Param("newName") String newName, @Param("userId") String userId);}import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class UserService {    @Autowired    private UserMapper userMapper;    public void updateUserInfo(String newName, String userId) {      userMapper.callUpdateUserInfo(newName, userId);    }}import org.springframework.boot.CommandLineRunner;import org.springframework.stereotype.Component;@Componentpublic class AppRunner implements CommandLineRunner {    @Autowired    private UserService userService;    @Override    public void run(String... args) throws Exception {      userService.updateUserInfo("张三", "zs");      System.out.println("存储过程调用完成!");    }}
页: [1]
查看完整版本: 反范式设计,冗余用户姓名,修改用户姓名后,业务表同步更新 -- MySQL 存储过程