Mybatis自定义TypeHandler完成字段加解密And枚举数据处理

发布时间 2023-10-16 13:51:22作者: unknown-n2

Mybatis自定义TypeHandler完成字段加解密And枚举数据处理

新增And查询对枚举数据处理

定义枚举
@Getter
public enum UserEnum {

    HOLD_A_POST("在职", 10),
    RESIGN("离职", 20);

    private String name;

    private Integer value;
    
    UserEnum(String name, Integer value) {
        this.name = name;
        this.value = value;
    }

    /**
     * <P>根据状态码返回UserEnum对象</P>
     */
    public static UserEnum getEnumByValue(Integer value) {

        return switch (value) {
            case 10 -> HOLD_A_POST;
            case 20 -> RESIGN;
            default -> null;
        };
    }
}
处理对象User
  • 处理对象中包含UserEnum并给与默认值
@Data
public class User implements Serializable {

    @Serial
    private static final long serialVersionUID = 123456789L;

    private Long id;

    private Integer age;

    private String username;

    private String email;

    private String did;

    private Dept dept;

    private UserEnum userEnum =UserEnum.RESIGN;

}
UserMapp.xml
  • 以下解释在加解密字段时会用上

  • ResultMap标签中的查询映射字段age添加typeHandler ,在查询时对字段解密

    • typeHandler: 自定义ypeHandler的项目路径
  • #{user.age,typeHandler="com.unknown.c.MyUserEnumTypeHandler}在新增时对age字段加密

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.unknown.c.base.UserMapper">

    <resultMap id="userResultMapper" type="com.unknown.c.base.User">
        <result column="id" property="id"/>
        <result column="age" property="age" typeHandler="com.unknown.c.MyUserEnumTypeHandler"/>
        <result column="username" property="username"/>
        <result column="email" property="email"/>
        <result column="did" property="did"/>
        <result column="user_enum" property="userEnum"/>
    </resultMap>

    <select id="findById" resultMap="userResultMapper">
        select *
        from user u
        where id = ${id}
    </select>

    <insert id="InsertUserAndEnum" useGeneratedKeys="true" keyProperty="id">
        insert into user value (null, #{user.age,typeHandler="com.unknown.c.MyUserEnumTypeHandler}, #{user.username}, #			  				{user.email}, #{user.did},#{user.userEnum"})
      </insert>
</mapper>    
自定义TypeHandler
  • 处理User对象中的枚举类型
package com.unknown.c;

import com.unknown.c.base.UserEnum;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @author unknown
 * @since 2023/10/15 15:40
 * TypeHandler<UserEnum>: TypeHandler处理的类型需要处理的类型
 */
public class MyUserEnumTypeHandler implements TypeHandler<UserEnum> {

    /**
     * <P>对枚举的处理</P>
     * @param ps PreparedStatement对象
     * @param i
     * @param userEnum 枚举参数
     * @param jdbcType jdbcType
     * @throws SQLException
     */
    @Override
    public void setParameter(PreparedStatement ps, int i, UserEnum userEnum, JdbcType jdbcType) throws SQLException {
        // 获取userEnum的value值
        ps.setString(i,userEnum.getValue().toString());
    }

    /**
     * <P>根据列名从返回结果中获取数据</P>
     * @param resultSet 返回结果
     * @param columnName 列名
     * @return UserEnum
     */
    @Override
    public UserEnum getResult(ResultSet resultSet, String columnName) throws SQLException {

        // 根据字段获取值
        int value = resultSet.getInt(columnName);
        return UserEnum.getEnumByValue(value);
    }

    /**
     * <P>根据索引从返回结果中获取数据</P>
     * @param resultSet 返回结果
     * @param columnIndex 索引
     * @return UserEnum
     */
    @Override
    public UserEnum getResult(ResultSet resultSet, int columnIndex) throws SQLException {
        int value = resultSet.getInt(columnIndex);
        System.out.println("从数据库获取的状态: " + value );
        return UserEnum.getEnumByValue(value);
    }

    /**
     * <P>从存储过程中获取数据</P>
     * @param cs 存储过程
     * @param columnIndex 索引
     * @return UserEnum
     */
    @Override
    public UserEnum getResult(CallableStatement cs, int columnIndex) {
        return null;
    }
}
注册自定义TypeHandler
  • properties配置文件

    • mybatis.type-handlers-package=com.unknown.c.MyUserEnumTypeHandler
      
  • yml配置文件

    • mybatis:
        type-handlers-package: com.unknown.c.MyUserEnumTypeHandler
      
  • mybatis-config.xml配置文件

    • <typeHandlers>
          <typeHandler handler="com.unknown.c.MyUserEnumTypeHandler" javaType="com.unknown.c.base.UserEnum"/>
      </typeHandlers>
      
测试结果
  • 新增

    @Test
    public void InsertUserAndEnumTest() throws IOException {
    
        // 读取Mybatis配置文件
        InputStream resourceAsStream = getResourceAsStream("mybatis-config.xml");
        // 构建SqlSessionFactorBean对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        // 获取SqlSession.设置自动提交数据
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        // 获取Mapper接口对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setAge(11);
        user.setUsername("111");
        user.setEmail("1111");
        user.setDid("1");
        /**
         * 如不自定义TypeHandler,数据库中存储的将是 HOLD_A_POST 化的字符串
         * 如果将User中的 UserEnum 枚举变为Long或者int类型,可以 user.setUserEnum(UserEnum.HOLD_A_POST.getValue()).但UserEnum必须有get方法
         */
        user.setUserEnum(UserEnum.HOLD_A_POST);
        mapper.InsertUserAndEnum(user);
        System.out.println("数据库中自增的ID:"+user.getId());
    
    }	
    
  • 结果验证

    • 如下图所示,红色框框中的是在没有对枚举字段进行处理,在新增中直接添加到是HOLD_A_POST化的字符串
    • 橘色框框中的是自定义TypeHandle后,在TypeHandle取出枚举的value进行存储

  • 查询

    @Test
    public void findByIdTest() throws IOException {
    
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User byId = mapper.findById(50);
        System.out.println("统计当前用户ID=2: "+byId);
    }
    
  • 结果验证

    • 在下图所示,ID=50的数据在数据库中是10,对应的枚举字段HOLD_A_POST,图片显示RESIGN是笔者出了个bug。现笔记已修复。不影响此段笔记的正常使用

总结
  • 笔者突发奇想,在小型项目(例如自己的博客网站)中,完全可以使用枚举代替数据字典。

字段加解密
自定义TypeHandler
  • 笔者在此并没有做什么加密操作,只是拿到字段做了一个字符串拼接。
package com.unknown.c;

import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * <P>自定义TypeHandler完成加解密</P>
 * @author unknown
 * @since 2023/10/16 11:05
 * TypeHandler<String>: 处理String类型的字段
 */
public class MyTypeHandlerEncryption implements TypeHandler<String> {
    @Override
    public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        String username="加密字段"+parameter;
        ps.setString(i,username);
    }

    @Override
    public String getResult(ResultSet rs, String columnName) throws SQLException {
        String string = rs.getString(columnName)+"解密字段";
        return string;
    }

    @Override
    public String getResult(ResultSet rs, int columnIndex) throws SQLException {
        return null;
    }

    @Override
    public String getResult(CallableStatement cs, int columnIndex) throws SQLException {
        return null;
    }
}
注册自定义TypeHandler
  • properties配置文件

    • mybatis.type-handlers-package=com.unknown.c.MyTypeHandlerEncryption
      
  • yml配置文件

    • mybatis:
        type-handlers-package: com.unknown.c.MyTypeHandlerEncryption
      
  • mybatis-config.xml配置文件

    • <typeHandlers>
          <typeHandler handler="com.unknown.c.MyTypeHandlerEncryption"/>
      </typeHandlers>
      
UserMapper.xml
  • typeHandler="com.unknown.c.MyUserEnumTypeHandler": 查询时解密
  • typeHandler=com.unknown.c.MyTypeHandlerEncryption: 新增是加密
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.unknown.c.base.UserMapper">

    <resultMap id="findByIdEncryptionMap" type="com.unknown.c.base.User">
        <result column="id" property="id"/>
        <result column="age" property="age"/>
        <result column="username" property="username" typeHandler="com.unknown.c.MyTypeHandlerEncryption"/>
        <result column="email" property="email"/>
        <result column="did" property="did"/>
        <result column="user_enum" property="userEnum"/>
    </resultMap>

    <select id="findByIdEncryption" resultMap="findByIdEncryptionMap">
        select  id,age,username,email,did,user_enum
        from user u
        where id = ${id}
    </select>

    <insert id="insertEncryption" useGeneratedKeys="true" keyProperty="id">
     insert into user value (null,
                            #{user.age},
                            #{user.username,typeHandler=com.unknown.c.MyTypeHandlerEncryption},
                            #{user.email},
                            #{user.did},
                            #{user.userEnum})
    </insert>
</mapper>
新增代码
/**
 * <P>新增加密测试</P>
 * 参阅: {@link MyTypeHandlerEncryption}
 */
@Test
public void insertEncryption() throws IOException {
    // 读取Mybatis配置文件
    InputStream resourceAsStream = getResourceAsStream("mybatis-config.xml");
    // 构建SqlSessionFactorBean对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    // 获取SqlSession.设置自动提交数据
    SqlSession sqlSession = sqlSessionFactory.openSession(true);
    // 获取Mapper接口对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
    user.setAge(11);
    user.setUsername("111");
    user.setEmail("1111");
    user.setDid("1");
    user.setUserEnum(UserEnum.HOLD_A_POST);
    mapper.insertEncryption(user);
    System.out.println("数据库中自增的ID:"+user.getId());
}
  • 添加结果

    • 在控制台中可以看到TypeHandler成功的执行

查询代码
/**
 * <P>查询解密测试</P>
 * @throws IOException
 */
@Test
public void findByIdEncryption() throws IOException {

    InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession(true);
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User byId = mapper.findByIdEncryption(51);
    System.out.println("统计当前用户ID=50: "+byId);
}
  • 查询结果

    • 成功的改变了原本的数据,但数据库中的字段值并没有变

总结
  • 相对于这种加解密方法,笔者更喜欢Jackson的注解式,更加灵活多变,不使用不加注解就行,而实现MybatisTypeHandler,每次都需要再SQLResultMap映射上添加,有很大的误伤可能