一、技术简介
自定义mybatis插件可以帮助我们省去某些频繁的操作,如数据库表的有更新时间和修改时间的,我们可以通过插件来处理,而不需要再controller层或serevice层手动判断和设置两个时间。当然自定义插件的应用远不止于此,这里不过多赘述。
二、介绍技术的难点和关键知识点
首先是定义插件时的参数介绍,不同的参数决定了我们的拦截器拦截的位置。
Mybatis 提供 Interceptor 接口,配合 @Intercepts 注解可以拦截如下 4 个对象的方法调用:
1 Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) 2 ParameterHandler (getParameterObject, setParameters) 3 ResultSetHandler (handleResultSets, handleOutputParameters) 4 StatementHandler (prepare, parameterize, batch, update, query)
其关系如下图:

解释:
-
Executor 是 Mybatis 的内部执行器,它负责调用 StatementHandler 操作数据库,并把结果集通过 ResultSetHandler 进行自动映射,另外,它还处理了二级缓存的操作。
-
StatementHandler 是 Mybatis 直接和数据库执行 sql 脚本的对象,另外,它也实现了 Mybatis 的一级缓存。
-
ParameterHandler 是 Mybatis 实现 sql 入参设置的对象。
-
ResultSetHandler 是 Mybatis 把 ResultSet 集合映射成 POJO 的接口对象。
本篇不陈述 Mybatis 的内部原理,感兴趣的读取请自行查阅相关资料。
三、搭建一个springboot的demo,能够实现基本的数据库操作。
项目架构:

controller层:
/** * @version: 1.0 * @author: tesla * @className: UserController * @packageName: com.tesla.mybatis.controller * @description: * @data: 2020/7/6 11:30 */ @RestController @RequestMapping("/sysuser") public class UserController { @Resource UserService userService; @RequestMapping("/insert") public String insert(){ SysUser sysUser = new SysUser(); sysUser.setLoginName("weng"); sysUser.setUserName("1230"); userService.insert(sysUser); return "success"; } }
service层:
/** * @version: 1.0 * @author: tesla * @className: UserServices * @packageName: com.tesla.mybatis.service * @description: * @data: 2020/7/6 10:00 */ @Service public class UserServiceImpl implements UserService { @Resource SysUserMapper sysUserMapper; private final static Logger log = LoggerFactory.getLogger(UserServiceImpl.class); public int insert(SysUser record){ int result = 0;try{ result = sysUserMapper.insert(record); }catch (Exception e){ log.error(e+"插入用户出错"); throw e; } return result; } }
mapper层:
@Mapper public interface SysUserMapper { int deleteByPrimaryKey(Long userId); int insert(SysUser record); int insertSelective(SysUser record); SysUser selectByPrimaryKey(Long userId); int updateByPrimaryKeySelective(SysUser record); int updateByPrimaryKey(SysUser record);
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.tesla.mybatis.mapper.SysUserMapper">
<resultMap id="BaseResultMap" type="com.tesla.mybatis.entity.SysUser">
<id column="user_id" jdbcType="BIGINT" property="userId" />
<result column="dept_id" jdbcType="BIGINT" property="deptId" />
<result column="login_name" jdbcType="VARCHAR" property="loginName" />
<result column="user_name" jdbcType="VARCHAR" property="userName" />
<result column="user_type" jdbcType="VARCHAR" property="userType" />
<result column="email" jdbcType="VARCHAR" property="email" />
<result column="phonenumber" jdbcType="VARCHAR" property="phonenumber" />
<result column="sex" jdbcType="CHAR" property="sex" />
<result column="avatar" jdbcType="VARCHAR" property="avatar" />
<result column="password" jdbcType="VARCHAR" property="password" />
<result column="salt" jdbcType="VARCHAR" property="salt" />
<result column="status" jdbcType="CHAR" property="status" />
<result column="del_flag" jdbcType="CHAR" property="delFlag" />
<result column="login_ip" jdbcType="VARCHAR" property="loginIp" />
<result column="login_date" jdbcType="TIMESTAMP" property="loginDate" />
<result column="create_by" jdbcType="VARCHAR" property="createBy" />
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
<result column="update_by" jdbcType="VARCHAR" property="updateBy" />
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
<result column="remark" jdbcType="VARCHAR" property="remark" />
</resultMap>
<sql id="Base_Column_List">
user_id, dept_id, login_name, user_name, user_type, email, phonenumber, sex, avatar,
password, salt, status, del_flag, login_ip, login_date, create_by, create_time, update_by,
update_time, remark
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from sys_user
where user_id = #{userId,jdbcType=BIGINT}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
delete from sys_user
where user_id = #{userId,jdbcType=BIGINT}
</delete>
<insert id="insert" parameterType="com.tesla.mybatis.entity.SysUser">
insert into sys_user (user_id, dept_id, login_name,
user_name, user_type, email,
phonenumber, sex, avatar,
password, salt, status,
del_flag, login_ip, login_date,
create_by, create_time, update_by,
update_time, remark)
values (#{userId,jdbcType=BIGINT}, #{deptId,jdbcType=BIGINT}, #{loginName,jdbcType=VARCHAR},
#{userName,jdbcType=VARCHAR}, #{userType,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR},
#{phonenumber,jdbcType=VARCHAR}, #{sex,jdbcType=CHAR}, #{avatar,jdbcType=VARCHAR},
#{password,jdbcType=VARCHAR}, #{salt,jdbcType=VARCHAR}, #{status,jdbcType=CHAR},
#{delFlag,jdbcType=CHAR}, #{loginIp,jdbcType=VARCHAR}, #{loginDate,jdbcType=TIMESTAMP},
#{createBy,jdbcType=VARCHAR}, #{createTime,jdbcType=TIMESTAMP}, #{updateBy,jdbcType=VARCHAR},
#{updateTime,jdbcType=TIMESTAMP}, #{remark,jdbcType=VARCHAR})
</insert>
<insert id="insertSelective" parameterType="com.tesla.mybatis.entity.SysUser">
insert into sys_user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="userId != null">
user_id,
</if>
<if test="deptId != null">
dept_id,
</if>
<if test="loginName != null">
login_name,
</if>
<if test="userName != null">
user_name,
</if>
<if test="userType != null">
user_type,
</if>
<if test="email != null">
email,
</if>
<if test="phonenumber != null">
phonenumber,
</if>
<if test="sex != null">
sex,
</if>
<if test="avatar != null">
avatar,
</if>
<if test="password != null">
password,
</if>
<if test="salt != null">
salt,
</if>
<if test="status != null">
status,
</if>
<if test="delFlag != null">
del_flag,
</if>
<if test="loginIp != null">
login_ip,
</if>
<if test="loginDate != null">
login_date,
</if>
<if test="createBy != null">
create_by,
</if>
<if test="createTime != null">
create_time,
</if>
<if test="updateBy != null">
update_by,
</if>
<if test="updateTime != null">
update_time,
</if>
<if test="remark != null">
remark,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null">
#{userId,jdbcType=BIGINT},
</if>
<if test="deptId != null">
#{deptId,jdbcType=BIGINT},
</if>
<if test="loginName != null">
#{loginName,jdbcType=VARCHAR},
</if>
<if test="userName != null">
#{userName,jdbcType=VARCHAR},
</if>
<if test="userType != null">
#{userType,jdbcType=VARCHAR},
</if>
<if test="email != null">
#{email,jdbcType=VARCHAR},
</if>
<if test="phonenumber != null">
#{phonenumber,jdbcType=VARCHAR},
</if>
<if test="sex != null">
#{sex,jdbcType=CHAR},
</if>
<if test="avatar != null">
#{avatar,jdbcType=VARCHAR},
</if>
<if test="password != null">
#{password,jdbcType=VARCHAR},
</if>
<if test="salt != null">
#{salt,jdbcType=VARCHAR},
</if>
<if test="status != null">
#{status,jdbcType=CHAR},
</if>
<if test="delFlag != null">
#{delFlag,jdbcType=CHAR},
</if>
<if test="loginIp != null">
#{loginIp,jdbcType=VARCHAR},
</if>
<if test="loginDate != null">
#{loginDate,jdbcType=TIMESTAMP},
</if>
<if test="createBy != null">
#{createBy,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
#{createTime,jdbcType=TIMESTAMP},
</if>
<if test="updateBy != null">
#{updateBy,jdbcType=VARCHAR},
</if>
<if test="updateTime != null">
#{updateTime,jdbcType=TIMESTAMP},
</if>
<if test="remark != null">
#{remark,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.tesla.mybatis.entity.SysUser">
update sys_user
<set>
<if test="deptId != null">
dept_id = #{deptId,jdbcType=BIGINT},
</if>
<if test="loginName != null">
login_name = #{loginName,jdbcType=VARCHAR},
</if>
<if test="userName != null">
user_name = #{userName,jdbcType=VARCHAR},
</if>
<if test="userType != null">
user_type = #{userType,jdbcType=VARCHAR},
</if>
<if test="email != null">
email = #{email,jdbcType=VARCHAR},
</if>
<if test="phonenumber != null">
phonenumber = #{phonenumber,jdbcType=VARCHAR},
</if>
<if test="sex != null">
sex = #{sex,jdbcType=CHAR},
</if>
<if test="avatar != null">
avatar = #{avatar,jdbcType=VARCHAR},
</if>
<if test="password != null">
password = #{password,jdbcType=VARCHAR},
</if>
<if test="salt != null">
salt = #{salt,jdbcType=VARCHAR},
</if>
<if test="status != null">
status = #{status,jdbcType=CHAR},
</if>
<if test="delFlag != null">
del_flag = #{delFlag,jdbcType=CHAR},
</if>
<if test="loginIp != null">
login_ip = #{loginIp,jdbcType=VARCHAR},
</if>
<if test="loginDate != null">
login_date = #{loginDate,jdbcType=TIMESTAMP},
</if>
<if test="createBy != null">
create_by = #{createBy,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
create_time = #{createTime,jdbcType=TIMESTAMP},
</if>
<if test="updateBy != null">
update_by = #{updateBy,jdbcType=VARCHAR},
</if>
<if test="updateTime != null">
update_time = #{updateTime,jdbcType=TIMESTAMP},
</if>
<if test="remark != null">
remark = #{remark,jdbcType=VARCHAR},
</if>
</set>
where user_id = #{userId,jdbcType=BIGINT}
</update>
<update id="updateByPrimaryKey" parameterType="com.tesla.mybatis.entity.SysUser">
update sys_user
set dept_id = #{deptId,jdbcType=BIGINT},
login_name = #{loginName,jdbcType=VARCHAR},
user_name = #{userName,jdbcType=VARCHAR},
user_type = #{userType,jdbcType=VARCHAR},
email = #{email,jdbcType=VARCHAR},
phonenumber = #{phonenumber,jdbcType=VARCHAR},
sex = #{sex,jdbcType=CHAR},
avatar = #{avatar,jdbcType=VARCHAR},
password = #{password,jdbcType=VARCHAR},
salt = #{salt,jdbcType=VARCHAR},
status = #{status,jdbcType=CHAR},
del_flag = #{delFlag,jdbcType=CHAR},
login_ip = #{loginIp,jdbcType=VARCHAR},
login_date = #{loginDate,jdbcType=TIMESTAMP},
create_by = #{createBy,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=TIMESTAMP},
update_by = #{updateBy,jdbcType=VARCHAR},
update_time = #{updateTime,jdbcType=TIMESTAMP},
remark = #{remark,jdbcType=VARCHAR}
where user_id = #{userId,jdbcType=BIGINT}
</update>
</mapper>
实体类SysUser:
package com.tesla.mybatis.entity; import org.springframework.stereotype.Repository; import java.util.Date; @Repository public class SysUser { private Long userId; private Long deptId; private String loginName; private String userName; private String userType; private String email; private String phonenumber; private String sex; private String avatar; private String password; private String salt; private String status; private String delFlag; private String loginIp; private Date loginDate; private String createBy; private Date createTime; private String updateBy; private Date updateTime; private String remark; public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public Long getDeptId() { return deptId; } public void setDeptId(Long deptId) { this.deptId = deptId; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName == null ? null : loginName.trim(); } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName == null ? null : userName.trim(); } public String getUserType() { return userType; } public void setUserType(String userType) { this.userType = userType == null ? null : userType.trim(); } public String getEmail() { return email; } public void setEmail(String email) { this.email = email == null ? null : email.trim(); } public String getPhonenumber() { return phonenumber; } public void setPhonenumber(String phonenumber) { this.phonenumber = phonenumber == null ? null : phonenumber.trim(); } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex == null ? null : sex.trim(); } public String getAvatar() { return avatar; } public void setAvatar(String avatar) { this.avatar = avatar == null ? null : avatar.trim(); } public String getPassword() { return password; } public void setPassword(String password) { this.password = password == null ? null : password.trim(); } public String getSalt() { return salt; } public void setSalt(String salt) { this.salt = salt == null ? null : salt.trim(); } public String getStatus() { return status; } public void setStatus(String status) { this.status = status == null ? null : status.trim(); } public String getDelFlag() { return delFlag; } public void setDelFlag(String delFlag) { this.delFlag = delFlag == null ? null : delFlag.trim(); } public String getLoginIp() { return loginIp; } public void setLoginIp(String loginIp) { this.loginIp = loginIp == null ? null : loginIp.trim(); } public Date getLoginDate() { return loginDate; } public void setLoginDate(Date loginDate) { this.loginDate = loginDate; } public String getCreateBy() { return createBy; } public void setCreateBy(String createBy) { this.createBy = createBy == null ? null : createBy.trim(); } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public String getUpdateBy() { return updateBy; } public void setUpdateBy(String updateBy) { this.updateBy = updateBy == null ? null : updateBy.trim(); } public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark == null ? null : remark.trim(); } }
配置文件:
server: port: 8081 spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/huhutong?characterEncoding=utf8&useSSL=false password: w308557035 username: root application: name: mybatis: # 搜索指定包别名 typeAliasesPackage: com.tesla.**.entity # 配置mapper的扫描,找到所有的mapper.xml映射文件 mapperLocations: classpath*:/mappers/*Mapper.xml # 加载全局的配置文件 # configLocation: classpath:mybatis/mybatis-config.xml
这里为了大家方便,我贴出了demo几乎所有的代码,如果读者觉得太复杂,完全可以自己定义。到此为止spirngboot的demo就结束,我们可以来测试一下没有插件的情况下,执行插入的结果。浏览器输入http://localhost:8081/sysuser/insert,查看数据库变化

可以看到createTime为空。
四、自定义mybatis插件,同时使用@Conponent注解交给spring来管理
package com.tesla.mybatis.plugin; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.plugin.*; import java.lang.reflect.Field; import java.util.Date; import java.util.Properties; /** * @version: 1.0 * @author: tesla * @className: Myplugin * @packageName: com.tesla.mybatis.plugin * @description: 自定义插件拦截器,实现字段的属性注入 * @data: 2020/7/6 15:10 */ @Intercepts( @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}) )
@Component public class Myplugin implements Interceptor { //获取到拦截的对象,底层也是通过代理来实现的,实际上是拿到一个目标的代理对象 @Override public Object plugin(Object target) { return Plugin.wrap(target,this); } //获取配置文件中设置的阈值等参数 @Override public void setProperties(Properties properties) { } @Override public Object intercept(Invocation invocation) throws Throwable { MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0]; //获取操作类型,crud SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); //获取参数 Object parameter = invocation.getArgs()[1]; Field[]fields = parameter.getClass().getDeclaredFields(); for(Field field: fields){ //注入对应的属性值 if (field.getName().equals("createTime")&&SqlCommandType.INSERT.equals(sqlCommandType)){ field.setAccessible(true); field.set(parameter,new Date()); } if (field.getName().equals("updateTime")&&SqlCommandType.UPDATE.equals(sqlCommandType)){ field.setAccessible(true); field.set(parameter,new Date()); } } Object object = invocation.proceed(); return object; } }
这里我们插件内的逻辑是根据反射设置参数中含有createTime和updateTime属性的值。
重启idea,再次访问接口,http://localhost:8081/sysuser/insert
![]()
可以看到,这次有创建时间了。
五、总结和补充或拓展
在自动注入时,根据属性名称设置值有局限性且,优化方向是自定义注解,然后将注解打在需要注入的字段上,拦截器中根据属性上的注解来判断是否设置parameter的值。
