目录
- 前言
- 运行环境
- 创建表
- 导入依赖
- 创建实体
- 建立TypeHandler
- @MappedJdbcTypes和@MappedTypes干什么用的
- BaseTypeHandler要实现的方法是干什么的
- 写好的TypeHandler怎么使用
- 创建dao
- 控制层
前言
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;
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.6.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
创建实体
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;
}
}
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;
}
}
数据库手机号设置的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));
}
}
以上就是全文核心,看的就是这里!
@MappedJdbcTypes和@MappedTypes干什么用的
我认为表面上是对jdbcType和javaType做了个绑定,更像是做了个筛选。比如我上面
@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(MyEncrypt.class)
就是将MyEncrypt
和varchar
建立了联系,你的参数里有MyEncrypt这个类型且是要转成varchar的,他才会起作用!
BaseTypeHandler要实现的方法是干什么的
他要实现一个set方法和三个get方法。setNonNullParameter
是塞值,我理解成把javaType转成jdbcType的过程(通俗点是把让你把传的对象转成你能放进数据库类型)getNullableResult
被重载了两次,但是在我眼里他就是和上面反过来,是把jdbcType转成javaType的过程,也就是把数据库里的数据转成你约定的对象。
写好的TypeHandler怎么使用
个人建议全局配在配置文件里,写在xml里,不仅麻烦,还报各种错误,麻烦死了。而且一般写这个,大概率是为了全局使用的!
# 包名
mybatis.type-handlers-package=com.jcl.mybatistypehandler.handler
写在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.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>
创建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);
}
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;
}
}
控制层
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));
}
}