• Mybatis自定义类型映射处理器


    前言

    mybatis作为一款相当优秀的一款orm框架,不仅本身就提供了很多类型映射,还支持自定义类型映射(javaType与jdbcType之间的关系),通过TypeHandler我们可以自己在数据库类型与实体类型之间任意转换。
    比如说,我现在需要讲前端传来的手机号进行脱敏入库处理,而用户查的时候,看到的得是正常的!这就涉及加密解密处理了,当然我们可以在service里处理,这里我们演示下如何通过自定义类型映射,进行处理。

    运行环境

    java8, springboot2.5.0,mysql8,maven3.3.9

    创建表

    CREATE TABLE `t_user` (
      `id` int(10) NOT NULL AUTO_INCREMENT,
      `name` varchar(255) DEFAULT NULL,
      `phone` varchar(255) DEFAULT NULL,
      `remark` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    导入依赖

    		<dependency>
    			<groupId>org.springframework.bootgroupId>
    			<artifactId>spring-boot-starter-webartifactId>
    		dependency>
    
    		<dependency>
    			<groupId>org.mybatis.spring.bootgroupId>
    			<artifactId>mybatis-spring-boot-starterartifactId>
    			<version>2.1.4version>
    		dependency>
    
    		<dependency>
    			<groupId>cn.hutoolgroupId>
    			<artifactId>hutool-allartifactId>
    			<version>5.6.5version>
    		dependency>
    
    		<dependency>
    			<groupId>mysqlgroupId>
    			<artifactId>mysql-connector-javaartifactId>
    			<scope>runtimescope>
    		dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    创建实体

    import java.util.List;
    
    public class User {
        private Integer id;
    
        private String name;
    
        private MyEncrypt phone;
    
        private List<String> remark;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public MyEncrypt getPhone() {
            return phone;
        }
    
        public void setPhone(MyEncrypt phone) {
            this.phone = phone;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public List<String> getRemark() {
            return remark;
        }
    
        public void setRemark(List<String> remark) {
            this.remark = remark;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    public class MyEncrypt {
    
        private String realPhone;
    
        public MyEncrypt(String realPhone) {
            this.realPhone = realPhone;
        }
    
        public String getRealPhone() {
            return realPhone;
        }
    
        public void setRealPhone(String realPhone) {
            this.realPhone = realPhone;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    数据库手机号设置的varchar类型,而我定义的是一个MyEncrypt实体类,所以我需要将他与jdbcType关联起来,并且完成加密解密的过程!

    建立TypeHandler

    import cn.hutool.crypto.SecureUtil;
    import cn.hutool.crypto.symmetric.AES;
    import com.jcl.mybatistypehandler.model.MyEncrypt;
    import org.apache.ibatis.type.BaseTypeHandler;
    import org.apache.ibatis.type.JdbcType;
    import org.apache.ibatis.type.MappedJdbcTypes;
    import org.apache.ibatis.type.MappedTypes;
    
    import java.nio.charset.StandardCharsets;
    import java.sql.CallableStatement;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    @MappedJdbcTypes(JdbcType.VARCHAR)
    @MappedTypes(MyEncrypt.class)
    public class MyEncryptTypeHandler extends BaseTypeHandler<MyEncrypt> {
    
        private static final byte[] KEYS = "12345678abcdefgh".getBytes(StandardCharsets.UTF_8);
    
        /**
         * 设置参数,默认从前端传来是源手机号,加密处理后,又重新塞回
         * preparedStatement: 预执行句柄
         */
        @Override
        public void setNonNullParameter(PreparedStatement preparedStatement, int i, MyEncrypt myEncrypt, JdbcType jdbcType) throws SQLException {
            if (myEncrypt == null || myEncrypt.getRealPhone() == null) {
                preparedStatement.setString(i, null);
                return;
            }
            String phone = myEncrypt.getRealPhone();
            AES aes = SecureUtil.aes(KEYS);
            String encrypt = aes.encryptHex(phone);
            preparedStatement.setString(i, encrypt);
        }
    
        @Override
        public MyEncrypt getNullableResult(ResultSet resultSet, String s) throws SQLException {
            return decrypt(resultSet.getString(s));
        }
    
        @Override
        public MyEncrypt getNullableResult(ResultSet resultSet, int i) throws SQLException {
            return decrypt(resultSet.getString(i));
        }
    
        @Override
        public MyEncrypt getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
            return decrypt(callableStatement.getString(i));
        }
    
        /**
         * 解密
         *
         * @param value 手机号
         * @return 返回解密对象
         */
        public MyEncrypt decrypt(String value) {
            if (null == value) {
                return null;
            }
            return new MyEncrypt(SecureUtil.aes(KEYS).decryptStr(value));
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    以上就是全文核心,看的就是这里!

    @MappedJdbcTypes和@MappedTypes干什么用的

    我认为表面上是对jdbcType和javaType做了个绑定,更像是做了个筛选。比如我上面

    @MappedJdbcTypes(JdbcType.VARCHAR)
    @MappedTypes(MyEncrypt.class)
    
    • 1
    • 2

    就是将MyEncryptvarchar建立了联系,你的参数里有MyEncrypt这个类型且是要转成varchar的,他才会起作用!

    BaseTypeHandler要实现的方法是干什么的

    他要实现一个set方法和三个get方法。
    setNonNullParameter是塞值,我理解成把javaType转成jdbcType的过程(通俗点是把让你把传的对象转成你能放进数据库类型)
    getNullableResult被重载了两次,但是在我眼里他就是和上面反过来,是把jdbcType转成javaType的过程,也就是把数据库里的数据转成你约定的对象。

    写好的TypeHandler怎么使用

    个人建议全局配在配置文件里,写在xml里,不仅麻烦,还报各种错误,麻烦死了。而且一般写这个,大概率是为了全局使用的!

    # 包名
    mybatis.type-handlers-package=com.jcl.mybatistypehandler.handler
    
    • 1
    • 2

    写在xml里的话,那可就麻烦了

    高度建议全局配

    
    DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.jcl.mybatistypehandler.dao.UserDao">
    
        <resultMap id="BaseResultMapper" type="com.jcl.mybatistypehandler.model.User">
            <id column="id" property="id"/>
            <result column="name" property="name"/>
            <result column="phone" property="phone" typeHandler="com.jcl.mybatistypehandler.handler.MyEncryptTypeHandler"/>
            <result column="remark" property="remark" typeHandler="com.jcl.mybatistypehandler.handler.ListTypeHandler"/>
        resultMap>
    
        <insert id="add">
            insert into t_user(name, phone, remark)
            values (#{name}, #{phone,typeHandler=com.jcl.mybatistypehandler.handler.MyEncryptTypeHandler},
                    #{remark,typeHandler=com.jcl.mybatistypehandler.handler.ListTypeHandler})
        insert>
    
        <select id="findByPhone" resultMap="BaseResultMapper">
            select *
            from t_user
            where phone = #{phone,typeHandler=com.jcl.mybatistypehandler.handler.MyEncryptTypeHandler}
        select>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    创建dao

    import com.jcl.mybatistypehandler.model.MyEncrypt;
    import com.jcl.mybatistypehandler.model.User;
    import org.apache.ibatis.annotations.Param;
    
    public interface UserDao {
    
        int add(@Param("name") String name,@Param("phone") MyEncrypt phone, @Param("remark") String remark);
    
        User findByPhone(@Param("phone") MyEncrypt phone);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    xml上面已给!

    ListTypeHandler是我设定的一个字符串转List的一个转换器!
    可以不写,所以xml里面也就别配了!

    package com.jcl.mybatistypehandler.handler;
    
    import org.apache.ibatis.type.BaseTypeHandler;
    import org.apache.ibatis.type.JdbcType;
    import org.apache.ibatis.type.MappedJdbcTypes;
    import org.apache.ibatis.type.MappedTypes;
    import org.springframework.util.StringUtils;
    
    import java.sql.CallableStatement;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.Arrays;
    import java.util.List;
    
    @MappedJdbcTypes(JdbcType.VARCHAR)
    @MappedTypes(List.class)
    public class ListTypeHandler extends BaseTypeHandler<List<String>> {
        
        @Override
        public void setNonNullParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException {
            if (null == parameter || parameter.isEmpty()) {
                ps.setString(i, null);
                return;
            }
            ps.setString(i, String.join(",", parameter));
        }
        
        @Override
        public List<String> getNullableResult(ResultSet rs, String columnName) throws SQLException {
            final String value = rs.getString(columnName);
            if (StringUtils.hasText(value)) {
                return Arrays.asList(value.split(","));
            }
            return null;
        }
        
        @Override
        public List<String> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
            final String value = rs.getString(columnIndex);
            if (StringUtils.hasText(value)) {
                return Arrays.asList(value.split(","));
            }
            return null;
        }
        
        @Override
        public List<String> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
            final String value = cs.getString(columnIndex);
            if (StringUtils.hasText(value)) {
                return Arrays.asList(value.split(","));
            }
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    控制层

    import com.jcl.mybatistypehandler.dao.UserDao;
    import com.jcl.mybatistypehandler.model.MyEncrypt;
    import com.jcl.mybatistypehandler.model.User;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class UserController {
    
        @Autowired
        private UserDao userDao;
    
        @GetMapping("add")
        public String addUser(@RequestParam("name") String name,
                              @RequestParam("phone") String phone,
                              @RequestParam("remark") String remark) {
            MyEncrypt myEncrypt = new MyEncrypt(phone);
            int result = userDao.add(name, myEncrypt, remark);
            return "添加结果: " + result;
        }
    
        @GetMapping("findByPhone")
        public User findCustomer(@RequestParam("phone") String phone) {
            return userDao.findByPhone(new MyEncrypt(phone));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
  • 相关阅读:
    简析低功耗蓝牙芯片PHY6222/PHY6252 蓝牙锁的应用
    关于力扣链表的一个细节 头结点head
    创作神器:9款国产ai写作服务工具大盘点
    SQLAlchemy的使用
    PHP:对象接口
    代码随想录算法训练营第五十天| 309.最佳买卖股票时机含冷冻期 714.买卖股票的最佳时机含手续费
    【JSP】Page指令和九大内置对象
    【Linux】iptables之防火墙概述及规则匹配+实例(1)
    zookeeper源码(08)请求处理及数据读写流程
    MFC Windows 程序设计[225]之滑动状态条联动(附源码)
  • 原文地址:https://blog.csdn.net/Curtisjia/article/details/126912073