Mybatis之如何利用自定义类型处理器实现数据的加解密

252 阅读3分钟

目录

  • 概述
  • 类型处理器类结构及作用
  • Mybatis常用的默认类型处理器
  • 类型处理器实现原理
  • 利用自定义类型处理器实现数据加解密

概述

Mybatis作为持久层框架在存储和查询数据时,只需在Mapper.xml文件中配置好对应字段的JdbcType和JavaType,Mybatis通过内置的类型转换器就会自定转换成对应的类型。Mybatis内置默认的类型转换器是通过org.apache.ibatis.type.TypeHandlerRegistry类进行注册的。当然,我们如果需要对某些字段做特殊处理,比如加解密、状态转换、类型转换等,可以自定义类型转换器来实现。

类型处理器类结构及作用

  • TypeHandler:定义类型处理器接口
  • TypeReferance:定义了一个类型引用的抽象类,用于引用一个泛型类型
  • BaseTypeHandler:类型处理器的公共类,所有类型处理器的公共模块,几乎所有类型处理器都是通过直接继承BaseTypeHandler来实现

image.png

Mybatis常用的默认类型处理器

MyBatis提供了一组默认的类型处理器(TypeHandler),用于在Java类型和JDBC 类型之间进行转换。默认类型处理器是自动注册的,无需额外配置。以下是 MyBatis 默认支持的类型处理器列表:

TypeHandlerJava类型Jdbc类型
BooleanTypeHandlerBooleanBIT、TINYINT、INTEGER
ByteTypeHnadlerByteNUMERIC、TINYINT、SMALLINT
ShortTypeHandlerShortSMALLINT、TINYINT INTEGER
IntegerTypeHandlerIntegerSMALLINT、INTEGER、BIGINT
LongTypeHandlerLongSMALLINT、INTEGER、BIGINT
FloatTypeHanlerFloatDOUBLE、REAL、FLOAT
DoubleTypeHandlerDoubleREAL、DOUBLE、FLOAT
BigDecimalTypeHandlerBigDecimalNUMERIC、DECIMAL
StringTypeHandlerStringCHAR、VARCHAR、LONGVARCHAR
ClobTypeHandlerStringCLOB、LONGVARCHAR
NStringTypeHandlerStringNVARCHAR、NCHAR
NClobTypeHandlerStringNCLOB
ByteArrayTypeHandlerbyte[]BLOB、LONGVARBINARY、VARBINARY
BlobTypeHandlerbyte[]BLOB、LONGVARBINARY
DateTypeHandlerDateTIMESTAMP
DateOnlyTypeHandlerDateDATE
TimeOnlyTypeHandlerDateTIME
SqlTimestampTypeHandlerTimestampTIMESTAMP
SqlDateTypeHandlerDateDATE

类型处理器实现原理

Mybatis在预处理语句(PreparedStatement)中设置一个参数时,或者从结果集(ResultSet)中取出一个值时,都会用到TypeHandler。它的作用是将java类型(JavaType)转换成jdbc类型(JdbcType),或者将jdbc类型(JdbcType)转换成java类型(JavaType)。 自定义类型转换器方式是实现TypeHandler接口或继承BaseTypeHandler类,TypeHandler接口提供了四个方法是实现类必须实现的,如下: image.png

当update/insert时,在DefaultParameterHandler#setParameters方法中设置Java类型与Jdbc类型的转换 image.png

利用自定义类型处理器实现数据加解密

  • 自定义Base64TypeHandler实现org.apache.ibatis.type.TypeHandler接口或继承org.apache.ibatis.type.BaseTypeHandler,使用Base64模拟加解密(可根据实际项目选择加解密方案,比如AES、加密机等)
package com.learning.mybatis.typehandler;

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;
import java.util.Base64;

/**
 * <p>
 * 自定义类型处理器,实现org.apache.ibatis.type.TypeHandler接口
 * </p>
 */
public class Base64TypeHandler implements TypeHandler<String> {

    @Override
    public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        try {
            // 设置参数的时候对参数进行加密
            String encode = Base64.getEncoder().encodeToString(parameter.getBytes());
            ps.setString(i, encode);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public String getResult(ResultSet rs, String columnName) throws SQLException {
        // 获取结果集对加密数据进行解密
        String result = rs.getString(columnName);
        if (result != null && !result.equals("")) {
            try {
                return new String(Base64.getDecoder().decode(result));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    @Override
    public String getResult(ResultSet rs, int columnIndex) throws SQLException {
        // 获取结果集对加密数据进行解密
        String result = rs.getString(columnIndex);
        if (result != null && !result.equals("")) {
            try {
                return new String(Base64.getDecoder().decode(result));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    @Override
    public String getResult(CallableStatement cs, int columnIndex) throws SQLException {
        // 获取结果集对加密数据进行解密
        String result = cs.getString(columnIndex);
        if (result != null && !result.equals("")) {
            try {
                return new String(Base64.getDecoder().decode(result));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}
  • 配置注册自定义处理器(mybatis-config.xml)
<!-- 配置自定义处理器-->
<typeHandlers>
    <typeHandler handler="com.learning.mybatis.typehandler.Base64TypeHandler"/>
</typeHandlers>
  • 使用自定义处理器(mapper.xml文件)
<?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.learning.mybatis.basicusage.mapper.UserMapper">

    <resultMap id="resultMap" type="com.learning.mybatis.basicusage.entity.TEntity">
        <id column="id" property="id"/>
        <!-- 指定字段使用自定义类型处理器,对select生效-->
        <result column="c" property="c" typeHandler="com.learning.mybatis.typehandler.Base64ypeHandler"/>
    </resultMap>

    <sql id="columnList">
        id, c
    </sql>

    <!-- 如果是insert/update等进行生效,如下方法-->
    <update id="update" parameterType="com.learning.mybatis.basicusage.entity.TEntity">
        update T set c = #{entity.c, typeHandler=com.learning.mybatis.typehandler.Base64ypeHandler} where id = #{entity.id}
    </update>

    <select id="selectById1" resultMap="resultMap" useCache="false">
        select <include refid="columnList"></include> from T where id = ${id}
    </select>
</mapper>
  • 测试类
package com.learning.mybatis.basicusage;

import com.learning.mybatis.basicusage.entity.TEntity;
import com.learning.mybatis.basicusage.mapper.UserMapper;
import com.learning.mybatis.plugin.page.PageInfo;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MybatisXmlTest {

    public static void main(String[] args) throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession();
        
        UserMapper userMapper = session.getMapper(UserMapper.class);

        // 更新
        TEntity tEntity = new TEntity();
        tEntity.setId(1);
        tEntity.setC("3232");
        userMapper.update(tEntity);

        // 查询
        List<TEntity> tBeans = userMapper.selectById1(1, new PageInfo());

        System.out.println("查询结果:" + tBeans);

        session.commit();

        session.close();
    }
}

写在最后

Mybatis源码解析:mp.weixin.qq.com/s/asGekskJy…

通过自定义类型处理器实现数据加解密来理解Mybatis的类型处理器的实现机制。欢迎关注“程序员笨鸥”一起学习吧!