在公司中,因为需要多数据库部分字段做加。 想到一种思路,可以在数据字段插入数据库之前,转化成密文。这样就可以做到应用层无感知处理数据加密问题了。 当然如果需要做到正在安全的
<!--结果集 -->
<result column="personPhone" property="personPhone" jdbcType="VARCHAR"
typeHandler="com.hjb.learning.mybatis.type.MyTypeHandler" />
<!--更新、插入类 -->
#{personPhone,com.hjb.learning.mybatis.type.MyTypeHandler},
personPhone = #{personPhone,com.hjb.learning.mybatis.type.MyTypeHandler},
话,加密的token需要严格管控或者配置在服务器上只有部分小伙伴可以看到。这个是另外一个话题了。今天就聊一聊如何利用mybatis TypeHandler 实现加密解密处理。
1. 什么是typeHandler?
根据官方介绍https://mybatis.org/mybatis-3/zh/configuration.html#typeHandlers
官方的话, 可能不太好理解。说的通透一点, typeHandler 其实就是转换数据库对象类型和java对象类型的一个转换器。
2. 已经了解了什么是TypeHandler了, 那么怎么使用呢?
其实官方也提供了对应的案例。我这边不适用官方的案例。
1). 继承实现BaseTypeHandler,如下:
public class MyTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
throws SQLException {
// 设置 String 类型的参数的时候调用,Java类型到JDBC类型
// 注意只有在字段上添加typeHandler属性才会生效
// insertBlog name字段
System.out.println("---------------setNonNullParameter1:" + parameter);
ps.setString(i, parameter);
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
// 根据列名获取 String 类型的参数的时候调用,JDBC类型到java类型
// 注意只有在字段上添加typeHandler属性才会生效
System.out.println("---------------getNullableResult1:" + columnName);
return rs.getString(columnName);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
// 根据下标获取 String 类型的参数的时候调用
System.out.println("---------------getNullableResult2:" + columnIndex);
return rs.getString(columnIndex);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
System.out.println("---------------getNullableResult3:");
return cs.getString(columnIndex);
}
}
2). mybatis-config.xml 填写typeHandler 相关的配置。 比如上述类,需要注意是类全路径
<typeHandlers>
<typeHandler handler="com.hjb.learning.mybatis.type.MyTypeHandler"></typeHandler>
</typeHandlers>
3)在真正使用的时候,在官方定义明确说明了,一个是在参数设置阶段,另外一个是在返回值解析阶段;分别对应往数据库写和从数据库读2个操作。 针对这2个场景分别说明:
<!--结果集 -->
<result column="personPhone" property="personPhone" jdbcType="VARCHAR"
typeHandler="com.hjb.learning.mybatis.type.MyTypeHandler" />
<!--更新、插入类 -->
#{personPhone,com.hjb.learning.mybatis.type.MyTypeHandler},
personPhone = #{personPhone,com.hjb.learning.mybatis.type.MyTypeHandler},
经过上面3个步骤,基本的的typeHandler 使用应该不会有太大问题了。
当然主逻辑是写MyTypeHandler类里面。
3. 既然已经知道了如何使用了,那么该功能是如何实现的内?
抓住2个点:1. 设置参数的时候 ; 2 返回值解析的时候
就拿第一点设置参数的时候来来说明一下,这个怎么处理的。首选找到入口, mybatis 在什么时候设置参数?mybatis所有的信息都是从SQL配置文件搞起,typeHandler 也是其中一段文本, 那么必然需要解析这个属性。
主题思路如下:
1. SQLMapper文件里面解析出typeHandler 属性的对象,并保存到ParameterMapping
2. 在DefaultParameterHandler类的setParameters方法中,根据之前设置到的ParameterMapping,获取对应的typeHandler,并调用typehandler的setParameter方法。
就从解析SQL文本开始,可以找到解析类:SqlSourceBuilder。启用每一个#{}表达式都会进行解析,并且传递对应的参数类型
比如:#{vvv,jdbcType=Varchar}, #{vvv}, 一种是String,另外一种默认是Object(这个最终还是会适配对应的类型)
从代码里面,参数解析代码动作组要由内部类ParameterMappingTokenHandler实现。最终会调用到:
buildParameterMapping 方法比较关键, 这个方法实现了,java参数到SQL JDBC参数的转换过程。
这个方法有2出逻辑跟typeHandler相关。
1.
2. javaType 就是上一块代码得到的parameterType,下面这个代码的意思是: 如果存在typeHandler属性,那就解析,并存入builder 构造者中。
3. 解析TypeHandler .可以看到最核心的逻辑是, typeHandler 都是typeHandlerRegistry这类产生的。
4. 那么我们需要看下, 这个typeHandlerRegistry 到底是什么?
通过搜索发现,这个TypeHandlerRegistry,配置在Configuration里面,也就是配置文件级别全局唯一的,并且是直接new产生的。
可以发现这个类,内部定义了很多默认的java 和JDBC的映射
并且有一溜的register方法。
其实Mybatis 在解析mybatis-config.xml的时候会将其中的typehandlers属性,解析出来,并且保存在这个TypeHandlerRegistry的
allTypeHandlersMap对象里面。当然同时也可以通过注解的方式注册进来。
再来看之前的 typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);到底是怎么写的。
还有 handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType); 可以发现其实他只是使用了,反射实例化对象
通过以上分析,大约知道了, typehandler对象是怎么解析了,然后保存在MapperStatment里面的。
第二步: 设置参数
DefaultParameterHandler类的实现如下:
以上是对设置参数环节的一些理解。整个过程,其实还算比较简单的。
核心的思想就是JAVA对象类型和JDBC对象类型转换过程中,留了一些钩子方法,给广大开发处理
对应返回值的解析过程,这边就不说了, 大体过程雷同,有兴趣的小伙伴自己研究一下吧