当前位置: 首页 > news >正文

手写Mybatis自动填充插件

目录

  • 一、Mybatis插件简介🥙
  • 二、工程创建及前期准备工作🥫
    • 实现代码
    • 配置文件
  • 三、插件核心代码实现🍗
  • 四、测试🥓

一、Mybatis插件简介🥙

Mybatis插件运行原理及自定义插件_简述mybatis的插件运行原理,以及如何编写一个插件-CSDN博客

MyBatis 是一款优秀的持久层框架,它简化了数据库操作过程,提供了强大的 SQL 映射功能。MyBatis 插件是用来扩展 MyBatis 框架功能的工具,可以通过插件来定制和增强 MyBatis 的功能。

MyBatis 插件可以用来实现一些自定义的功能,比如拦截 SQL 语句、修改 SQL 语句、添加新的功能等。通过插件,我们可以在 MyBatis 框架的各个阶段进行干预和扩展,从而实现更灵活、更强大的功能。

通常情况下,编写一个 MyBatis 插件需要实现 MyBatis 提供的接口,并在配置文件中注册插件。Mybatis只支持针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口

总的来说,MyBatis 插件是一种扩展机制,可以让我们更好地定制和增强 MyBatis 框架的功能,使得我们能够更好地适应各种不同的业务需求。

二、工程创建及前期准备工作🥫

以下都为前期准备工作,可直接略过,插件核心实现代码在第三节

PixPin_2024-03-11_14-26-17

PixPin_2024-03-11_14-27-44

创建test数据库

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (`id` int NOT NULL AUTO_INCREMENT,`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,`create_time` datetime NULL DEFAULT NULL,`update_time` datetime NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;

实现代码

本实验省略了Controller和Service

User.java

package com.example.mybatisplugin.entity;import com.example.mybatisplugin.anno.FiledFill;import java.io.Serial;
import java.time.LocalDateTime;
import java.util.Date;
import java.io.Serializable;/*** (User)实体类** @author makejava* @since 2024-03-11 14:41:35*/
public class User implements Serializable {@Serialprivate static final long serialVersionUID = 813676794892349198L;private Integer id;private String username;private String password;@FiledFill(fill = FiledFill.FillType.INSERT)private Date createTime;@FiledFill(fill = FiledFill.FillType.INSERT_UPDATE)private Date updateTime;//省略了getter和setter方法@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +", createTime=" + createTime +", updateTime=" + updateTime +'}';}
}

其中@FiledFill注解是自定义注解,文章后面会写到,可暂时不用添加

UserDao.java

@Mapper
public interface UserDao {/*** 新增数据** @param user 实例对象* @return 影响行数*/int insert(User user);/*** 修改数据** @param user 实例对象* @return 影响行数*/int update(User user);}

UserDao.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.example.mybatisplugin.dao.UserDao"><resultMap type="com.example.mybatisplugin.entity.User" id="UserMap"><result property="id" column="id" jdbcType="INTEGER"/><result property="username" column="username" jdbcType="VARCHAR"/><result property="password" column="password" jdbcType="VARCHAR"/><result property="createTime" column="create_time" jdbcType="TIMESTAMP"/><result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/></resultMap><!--新增所有列--><insert id="insert" keyProperty="id" useGeneratedKeys="true">insert into user(username,password,create_time,update_time)values (#{username},#{password},#{createTime},#{updateTime})</insert><!--通过主键修改数据--><update id="update">update user<set><if test="username != null and username != ''">username = #{username},</if><if test="password != null and password != ''">password = #{password},</if><if test="createTime != null">create_time = #{createTime},</if><if test="updateTime != null">update_time = #{updateTime},</if></set>where id = #{id}</update>
</mapper>

配置文件

application.yaml
数据库密码记得改成自己的

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: *****url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
server:port: 8080
mybatis:config-location: classpath:mybatis-config.xmlmapper-locations: classpath:mapper/*.xml

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC"-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><settings><!-- 设置驼峰标识 --><setting name="mapUnderscoreToCamelCase" value="true"/><!-- 打印SQL语句 --><setting name="logImpl" value="STDOUT_LOGGING"/></settings>
</configuration>

三、插件核心代码实现🍗

自定义注解FiledFill

package com.example.mybatisplugin.anno;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @Author YZK* @Date 2024/3/11*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FiledFill {enum FillType {INSERT, INSERT_UPDATE,}FillType fill();
}

插件核心代码

package com.example.mybatisplugin.plugin;import com.example.mybatisplugin.anno.FiledFill;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;/*** @Author YZK* @Date 2024/3/11* @Desc*/
@Intercepts({@Signature(type = Executor.class,method = "update",args = {MappedStatement.class, Object.class})})
public class MybatisAutoFill implements Interceptor {private static final Logger LOGGER = Logger.getLogger(MybatisAutoFill.class.getName());public Object intercept(Invocation invocation) throws Throwable {MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];//获取操作类型(INSERT和UPDATE)SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();//拿到SQL中传入的对象Object obj = invocation.getArgs()[1];//获取其字节对象Class<?> clazz = obj.getClass();//获取User类声明的所有字段Field[] fields = clazz.getDeclaredFields();//通过区分SQL的操作来进行不同的字段填充if (sqlCommandType == SqlCommandType.INSERT) {//是INSERT操作的话就同时填充createTime和updateTime字段fillInsertFields(obj, fields);} else if (sqlCommandType == SqlCommandType.UPDATE) {//是updateTime字段的话就只填充updateTime字段fillUpdateFields(obj, fields);}return invocation.proceed();}private void fillInsertFields(Object obj, Field[] fields) {Arrays.stream(fields)//过滤出所有带有@FiledFill注解的字段.filter(field -> field.isAnnotationPresent(FiledFill.class)).forEach(field -> {try {//对字段进行填充setFieldValue(obj, field);} catch (IllegalAccessException e) {LOGGER.log(Level.SEVERE, "字段填充错误", e);}});}private void fillUpdateFields(Object obj, Field[] fields) {Arrays.stream(fields)//过滤出所有带有@FiledFill注解的字段,以及注解值为INSERT_UPDATE的字段.filter(field -> field.isAnnotationPresent(FiledFill.class) &&field.getAnnotation(FiledFill.class).fill() == FiledFill.FillType.INSERT_UPDATE).forEach(field -> {try {//对字段进行填充setFieldValue(obj, field);} catch (IllegalAccessException e) {LOGGER.log(Level.SEVERE, "字段填充错误", e);}});}private void setFieldValue(Object obj, Field field) throws IllegalAccessException {//填充字段field.setAccessible(true);field.set(obj, new Date());}
}

mybatis-condig.xml配置文件中进行配置,将自定义的插件注册

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC"-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><settings><!-- 设置驼峰标识 --><setting name="mapUnderscoreToCamelCase" value="true"/><!-- 打印SQL语句 --><setting name="logImpl" value="STDOUT_LOGGING"/></settings><plugins><plugin interceptor="com.example.mybatisplugin.plugin.MybatisAutoFill"/></plugins>
</configuration>

四、测试🥓

插入操作

@Test
void contextLoads() {User user = new User();user.setUsername("笑的像个child");user.setPassword("123456");userDao.insert(user);
}

控制台打印的SQL

image-20240311225847571

image-20240311225857627

进行插入操作时create_time和update_time字段被同时填充

更新操作

    @Testvoid contextLoads() {User user = new User();user.setId(33);user.setUsername("笑的像个child");user.setPassword("12345678");userDao.update(user);}

控制台打印的SQL

image-20240311225948551

image-20240311230032331

进行更新时操作时update_time字段被填充

进行删除操作时,如果是硬删除,则记录被删除,软删除时同样是更新操作,字段也会被自动填充。

本插件还有许多需要完善的地方,只是自动填充的简单实现,如有需要,可以自己完善。

相关文章:

手写Mybatis自动填充插件

目录 一、Mybatis插件简介&#x1f959;二、工程创建及前期准备工作&#x1f96b;实现代码配置文件 三、插件核心代码实现&#x1f357;四、测试&#x1f953; 一、Mybatis插件简介&#x1f959; Mybatis插件运行原理及自定义插件_简述mybatis的插件运行原理,以及如何编写一个…...

upload文件上传漏洞复现

什么是文件上传漏洞&#xff1a; 文件上传漏洞是指由于程序员在对用户文件上传部分的控制不足或者处理缺陷&#xff0c;而导致的用户可以越过其本身权限向服务器上上传可执行的动态脚本文件。这里上传的文件可以是木马&#xff0c;病毒&#xff0c;恶意脚本或者WebShell等。“…...

Docker 安装部署 SqlServer 数据库

Docker 安装部署 SqlServer 数据库 背景&#xff1a; ​ 最近在开发数据中台数据集成模块&#xff0c;需要对接大量的数据做测试&#xff0c; 由于SqlServer 下载安装会耗费大量时间&#xff0c;所以采用 Docker 安装 Sqlserver 的方式部署数据库。 1、拉去 sqlserver 镜像 …...

cmath 中cos sin等常用函数的坑(弧度角度换算)

cmath中三角函数的输入是弧度,不是角度.忘了这件事,找bug找了好久! 弧度是旧称弪。在数学和物理中&#xff0c;弧度是角的度量单位。它是由国际单位制导出的单位&#xff0c;单位缩写是rad。弧度是指在一个圆中&#xff0c;弧长和半径之比&#xff0c;即|弧度|弧长半径。 角度…...

深度解析HTTP反向代理-okey proxy

反向代理這個概念可能並不常見&#xff0c;但其實它對於提升網路安全和訪問速度方面發揮著很大作用。 HTTP反向代理&#xff08;HTTP Reverse Proxy&#xff09;是一種特殊的代理伺服器&#xff0c;首先它能夠接收互聯網上的連接請求&#xff0c;然後將這些請求轉發給內部網路…...

SwinIR训练报错解决

swinir训练报错解决 记录swinir图像超分重建算法复现过程中的报错信息,并提供相应的解决方案 报错信息 UserWarning: torch.meshgrid: in an upcoming release, it will be required to pass the indexing argument. (Triggered internally at C:\actions-runner\_work\pyto…...

C++类和对象一

#include <iostream> using namespace std;//设计一个学生类 class CStudent {public: //公有成员void InputData(){cout << "请输入学号";cin >> sno;cout << "请输入姓名";cin >> sname;cout << "请输入分…...

Linux之线程互斥

目录 一、问题引入 二、线程互斥 1、相关概念 2、加锁保护 1、静态分配 2、动态分配 3、锁的原理 4、死锁 三、可重入与线程安全 1、概念 2、常见的线程不安全的情况 3、常见的线程安全的情况 4、常见不可重入的情况 5、常见可重入的情况 6、可重入与线程安全联系…...

C++ 拷贝构造函数和运算符重载

目录 一. 拷贝构造函数 1. 引入 2. 拷贝构造的概念 3. 浅拷贝 4. 深拷贝 二. C运算符重载 1. 概念 2. 注意事项 3.举例 一. 拷贝构造函数 1. 引入 我们在创建对象时&#xff0c;能不能创建一个与原先对象一模一样的新对象呢&#xff1f;为了解决这个问题&#x…...

二刷代码随想录算法训练营第二十三天 | 669. 修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树

目录 一、669. 修剪二叉搜索树 二、108. 将有序数组转换为二叉搜索树 三、538. 把二叉搜索树转换为累加树 一、669. 修剪二叉搜索树 题目链接&#xff1a;力扣 文章讲解&#xff1a;代码随想录 视频讲解&#xff1a; 你修剪的方式不对&#xff0c;我来给你纠正一下&#…...

信息抽取在旅游行业的应用:以景点信息抽取为例

开源项目推荐 今天先给大家推荐一个开源项目&#xff0c;多模态AI能力引擎平台: 免费的自然语言处理、情感分析、实体识别、图像识别与分类、OCR识别、语音识别接口&#xff0c;功能强大&#xff0c;欢迎体验。 https://gitee.com/stonedtx/free-nlp-api 场景描述 在旅游行业…...

Linux——基础指令

一、Linux目录结构 1、树形结构 Linux只有一个根目录 / &#xff0c;所有文件都在它下面 2、Linux路径的描述方式 在Linux系统中&#xff0c;路径之间的层级关系&#xff0c;使用&#xff1a; / 来表示 eg&#xff1a; /usr/local/hello.txt 注意&#xff1a; 开头/表示根…...

H5 带网站测速引导页源码

源码名称&#xff1a;带网站测速引导页源码 源码介绍&#xff1a;一款带网站测速功能的引导页源码 需求环境&#xff1a;H5 下载地址&#xff1a; https://www.changyouzuhao.cn/10717.html...

案例分析篇07:数据库设计相关28个考点(23~28)(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章推荐: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html 【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例分析篇-…...

Word中解决插入脚注导致的分页位置错误问题

先放一个截图&#xff1a; 上面的截图中&#xff0c;样式为标题3的段落“四、固执的念头”前插入了连续型分节符&#xff0c;并且该分节符的样式为正文&#xff0c;前后的正文段落中有脚注&#xff0c;结果在分页时&#xff0c;标题3段落“四、固执的念头”后的正文段落自动进入…...

2024/03/14(网络编程·day2)

一、思维导图 二、TCP通信 //服务器 #include<myhead.h>#define SER_PORT 8888 //服务器端口号 #define SER_IP "192.168.117.103" //服务器IP int main(int argc, const char *argv[]) {//1、创建一个套接字int sfd -1;sfd socket(AF_INET,SOCK_STREAM,…...

2024最新陪诊小程序/医院陪诊滴嗒陪诊小程序源码-陪护服务平台陪诊师陪

.系统介绍: 陪护小程序、微信陪诊、、ThinkPHP框架、ThinkPHP6框架、FastAdmin框架、微信小程序。 嘀嗒陪诊小程序功能相对简单,后台也简捷,如果只是做个陪诊服务的小程序也基本能满足了,整体测试了未发现BUG,小程序端也能正常为使用,用户授权接口是老的。 应用背景:人…...

基于单片机的温度控制系统设计

基于单片机的温度控制系统设计 摘要: 最近这些年&#xff0c;随着科学技术的不断发展和进步&#xff0c;单片机技术通过在各行各业中的应用也日臻完善。而温度测控系统也因单片机所特有的强大处理能力、功耗低以及体积小等优点向着小型化和智能化发展。本设计以STC89C52单片机…...

unity3d Animal Controller的Animal组件中Speeds,States和modes基础部分理解

Speeds 速度集是修改你可以做的原始动画,增加或减少运动,旋转,或动画速度。它们与 州 所以,当动物在运动状态下,在飞行或游泳时,你可以有不同的速度 如果你的性格动画是 (已到位), 你一定要调整速度 位置 和 旋转 每一种的价值观 速度装置 …否则,它们不会移动或旋转。 每个速…...

Tomcat详解

1Tomcat安装 下载 Tomcat&#xff1a;首先&#xff0c;您需要从 Tomcat 官方网站&#xff08;http://tomcat.apache.org&#xff09;下载适合您系统的最新版本的 Tomcat 软件包。通常情况下&#xff0c;您会选择一个稳定的版本进行下载。解压缩&#xff1a;下载完成后&#xf…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

MySQL用户和授权

开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务&#xff1a; test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)

一、OpenBCI_GUI 项目概述 &#xff08;一&#xff09;项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台&#xff0c;其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言&#xff0c;首次接触 OpenBCI 设备时&#xff0c;往…...

什么是VR全景技术

VR全景技术&#xff0c;全称为虚拟现实全景技术&#xff0c;是通过计算机图像模拟生成三维空间中的虚拟世界&#xff0c;使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验&#xff0c;结合图文、3D、音视频等多媒体元素…...