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
的注解式,更加灵活多变,不使用不加注解就行,而实现Mybatis
的TypeHandler
,每次都需要再SQL
和ResultMap
映射上添加,有很大的误伤可能