一、技术简介
自定义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的值。