跨境互联网 跨境互联网
首页
  • AI 工具

    • 绘图提示词工具 (opens new window)
    • ChatGPT 指令 (opens new window)
  • ChatGPT

    • ChatGP T介绍
    • ChatGPT API 中文开发手册
    • ChatGPT 中文调教指南
    • ChatGPT 开源项目
  • Midjourney

    • Midjourney 文档
  • Stable Diffusion

    • Stable Diffusion 文档
  • 其他

    • AIGC 热门文章
    • 账号合租 (opens new window)
    • 有趣的网站
  • Vue

    • Vue3前置
  • JAVA基础

    • Stream
    • Git
    • Maven
    • 常用第三方类库
    • 性能调优工具
    • UML系统建模
    • 领域驱动设计
    • 敏捷开发
    • Java 测试
    • 代码规范及工具
    • Groovy 编程
  • 并发编程&多线程

    • 并发编程
    • 高性能队列 Disruptor
    • 多线程并发在电商系统下的应用
  • 其他

    • 面试题
  • 消息中间中间件

    • Kafka
    • RabbitMQ
    • RocketMQ
  • 任务调度

    • Quartz
    • XXL-Job
    • Elastic-Job
  • 源码解析

    • Mybatis 高级使用
    • Mybatis 源码剖析
    • Mybatis-Plus
    • Spring Data JPA
    • Spring 高级使用
    • Spring 源码剖析
    • SpringBoot 高级使用
    • SpringBoot 源码剖析
    • Jdk 解析
    • Tomcat 架构设计&源码剖析
    • Tomcat Web应用服务器
    • Zookeeper 高级
    • Netty
  • 微服务框架

    • 分布式原理
    • 分布式集群架构场景化解决方案
    • Dubbo 高级使用
    • Dubbo 核心源码剖析
    • Spring Cloud Gateway
    • Nacos 实战应用
    • Sentinel 实战应用
    • Seata 分布式事务
  • 数据结构和算法的深入应用
  • 存储

    • 图和Neo4j
    • MongoDB
    • TiDB
    • MySQL 优化
    • MySQL 平滑扩容实战
    • MySQL 海量数据存储与优化
    • Elasticsearch
  • 缓存

    • Redis
    • Aerospike
    • Guava Cache
    • Tair
  • 文件存储

    • 阿里云 OSS 云存储
    • FastDF 文件存储
  • 基础

    • Linux 使用
    • Nginx 使用与配置
    • OpenResty 使用
    • LVS+Keepalived 高可用部署
    • Jekins
  • 容器技术

    • Docker
    • K8S
    • K8S
  • 01.全链路(APM)
  • 02.电商终极搜索解决方案
  • 03.电商亿级数据库设计
  • 04.大屏实时计算
  • 05.分库分表的深入实战
  • 06.多维系统下单点登录
  • 07.多服务之间分布式事务
  • 08.业务幂等性技术架构体系
  • 09.高并发下的12306优化
  • 10.每秒100W请求的秒杀架构体系
  • 11.集中化日志管理平台的应用
  • 12.数据中台配置中心
  • 13.每天千万级订单的生成背后痛点及技术突破
  • 14.红包雨的架构设计及源码实现
  • 人工智能

    • Python 笔记
    • Python 工具库
    • 人工智能(AI) 笔记
    • 人工智能(AI) 项目笔记
  • 大数据

    • Flink流处理框架
  • 加密区

    • 机器学习(ML) (opens new window)
    • 深度学习(DL) (opens new window)
    • 自然语言处理(NLP) (opens new window)
AI 导航 (opens new window)

Revin

首页
  • AI 工具

    • 绘图提示词工具 (opens new window)
    • ChatGPT 指令 (opens new window)
  • ChatGPT

    • ChatGP T介绍
    • ChatGPT API 中文开发手册
    • ChatGPT 中文调教指南
    • ChatGPT 开源项目
  • Midjourney

    • Midjourney 文档
  • Stable Diffusion

    • Stable Diffusion 文档
  • 其他

    • AIGC 热门文章
    • 账号合租 (opens new window)
    • 有趣的网站
  • Vue

    • Vue3前置
  • JAVA基础

    • Stream
    • Git
    • Maven
    • 常用第三方类库
    • 性能调优工具
    • UML系统建模
    • 领域驱动设计
    • 敏捷开发
    • Java 测试
    • 代码规范及工具
    • Groovy 编程
  • 并发编程&多线程

    • 并发编程
    • 高性能队列 Disruptor
    • 多线程并发在电商系统下的应用
  • 其他

    • 面试题
  • 消息中间中间件

    • Kafka
    • RabbitMQ
    • RocketMQ
  • 任务调度

    • Quartz
    • XXL-Job
    • Elastic-Job
  • 源码解析

    • Mybatis 高级使用
    • Mybatis 源码剖析
    • Mybatis-Plus
    • Spring Data JPA
    • Spring 高级使用
    • Spring 源码剖析
    • SpringBoot 高级使用
    • SpringBoot 源码剖析
    • Jdk 解析
    • Tomcat 架构设计&源码剖析
    • Tomcat Web应用服务器
    • Zookeeper 高级
    • Netty
  • 微服务框架

    • 分布式原理
    • 分布式集群架构场景化解决方案
    • Dubbo 高级使用
    • Dubbo 核心源码剖析
    • Spring Cloud Gateway
    • Nacos 实战应用
    • Sentinel 实战应用
    • Seata 分布式事务
  • 数据结构和算法的深入应用
  • 存储

    • 图和Neo4j
    • MongoDB
    • TiDB
    • MySQL 优化
    • MySQL 平滑扩容实战
    • MySQL 海量数据存储与优化
    • Elasticsearch
  • 缓存

    • Redis
    • Aerospike
    • Guava Cache
    • Tair
  • 文件存储

    • 阿里云 OSS 云存储
    • FastDF 文件存储
  • 基础

    • Linux 使用
    • Nginx 使用与配置
    • OpenResty 使用
    • LVS+Keepalived 高可用部署
    • Jekins
  • 容器技术

    • Docker
    • K8S
    • K8S
  • 01.全链路(APM)
  • 02.电商终极搜索解决方案
  • 03.电商亿级数据库设计
  • 04.大屏实时计算
  • 05.分库分表的深入实战
  • 06.多维系统下单点登录
  • 07.多服务之间分布式事务
  • 08.业务幂等性技术架构体系
  • 09.高并发下的12306优化
  • 10.每秒100W请求的秒杀架构体系
  • 11.集中化日志管理平台的应用
  • 12.数据中台配置中心
  • 13.每天千万级订单的生成背后痛点及技术突破
  • 14.红包雨的架构设计及源码实现
  • 人工智能

    • Python 笔记
    • Python 工具库
    • 人工智能(AI) 笔记
    • 人工智能(AI) 项目笔记
  • 大数据

    • Flink流处理框架
  • 加密区

    • 机器学习(ML) (opens new window)
    • 深度学习(DL) (opens new window)
    • 自然语言处理(NLP) (opens new window)
AI 导航 (opens new window)
  • Spring Data JPA
  • MyBatis

    • Mybatis高级使用

      • 自定义持久层框架
      • Mybatis相关概念
      • Mybatis基本应用
      • Mybatis配置文件深入
      • Mybatis复杂映射开发
      • Mybatis注解开发
      • Mybatis缓存
      • Mybatis插件
        • 拦截
        • 8.4.1 插件接口
        • 8.4.2自定义插件
        • 1导入通用PageHelper坐标
        • 2 在mybatis核心配置文件中配置PageHelper插件
        • 3 测试分页代码实现
        • 什么是通用Mapper
        • 如何使用
      • Mybatis架构原理
      • Mybatis源码剖析
      • 设计模式
    • Mybatis源码剖析

    • Mybatis-Plus
    • 资料
  • Spring

  • SpringBoot

  • Jdk

  • Tomcat

  • Netty

  • 若依

  • Traefik

  • Openresty

  • 开源框架
  • MyBatis
  • Mybatis高级使用
Revin
2023-07-23
目录

Mybatis插件

# 8.1 插件简介

一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展。这样的好处是显而易见的,一是增加了框架的灵活性。二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作。以MyBatis为例,我们可基于MyBati s插件机制实现分页、分表,监控等功能。由于插件和业务 无关,业务也无法感知插件的存在。因此可以无感植入插件,在无形中增强功能

# 8.2 Mybatis插件介绍

Mybati s作为一个应用广泛的优秀的ORM开源框架,这个框架具有强大的灵活性,在四大组件

(Executor、StatementHandler、ParameterHandler、ResultSetHandler)处提供了简单易用的插 件扩展机制。Mybatis对持久层的操作就是借助于四大核心对象。MyBatis支持用插件对四大核心对象进 行拦截,对mybatis来说插件就是拦截器,用来增强核心对象的功能,增强功能本质上是借助于底层的 动态代理实现的,换句话说,MyBatis中的四大对象都是代理对象

Mybatis_Page54_001

MyBatis所允许拦截的方法如下:

  • 执行器Executor (update、query、commit、rollback等方法);
  • SQL语法构建器StatementHandler (prepare、parameterize、batch、updates query等方 法);
  • 参数处理器ParameterHandler (getParameterObject、setParameters方法);
  • 结果集处理器ResultSetHandler (handleResultSets、handleOutputParameters等方法);

# 8.3 Mybatis插件原理

在四大对象创建的时候

  • 1、每个创建出来的对象不是直接返回的,而是interceptorChain.pluginAll(parameterHandler);
  • 2、获取到所有的Interceptor (拦截器)(插件需要实现的接口);调用 interceptor.plugin(target);返回 target 包装后的对象
  • 3、插件机制,我们可以使用插件为目标对象创建一个代理对象;AOP (面向切面)我们的插件可 以为四大对象创建出代理对象,代理对象就可以拦截到四大对象的每一个执行;

# 拦截

插件具体是如何拦截并附加额外的功能的呢?以ParameterHandler来说

public ParameterHandler newParameterHandler(MappedStatement mappedStatement,Object object, BoundSql sql, InterceptorChain interceptorChain){
     ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement,object,sql);
    parameterHandler = (ParameterHandler)interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
}
public Object pluginAll(Object target) {
  for (Interceptor interceptor : interceptors) {
    target = interceptor.plugin(target);
  }
  return target;
}
1
2
3
4
5
6
7
8
9
10
11

interceptorChain保存了所有的拦截器(interceptors),是mybatis初始化的时候创建的。调用拦截器链中的拦截器依次的对目标进行拦截或增强。interceptor.plugin(target)中的target就可以理解为mybatis中的四大对象。返回的target是被重重代理后的对象

如果我们想要拦截Executor的query方法,那么可以这样定义插件:

@Intercepts({
@Signature( type = Executor.class, method = "query", args={MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class})})
public class ExeunplePlugin implements Interceptor {
  //省略逻辑
}
1
2
3
4
5

除此之外,我们还需将插件配置到sqlMapConfig.xml中。

<plugins>
  <plugin interceptor="com.lagou.plugin.ExamplePlugin"></plugin>
</plugins>
1
2
3

这样MyBatis在启动时可以加载插件,并保存插件实例到相关对象(InterceptorChain,拦截器链) 中。

待准备工作做完后,MyBatis处于就绪状态。我们在执行SQL时,需要先通过

DefaultSqlSessionFactory 创建 SqlSession。Executor 实例会在创建 SqlSession 的过程中被创建,Executor实例创建完毕后,MyBatis会通过JDK动态代理为实例生成代理类。这样,插件逻辑即可在Executor相关方法被调用前执行。

以上就是MyBatis插件机制的基本原理

# 8.4 自定义插件

# 8.4.1 插件接口

Mybatis 插件接口-Interceptor

  • Intercept方法,插件的核心方法
  • plugin方法,生成target的代理对象
  • setProperties方法,传递插件所需参数

# 8.4.2自定义插件

设计实现一个自定义插件

Intercepts ({//注意看这个大花括号,也就这说这里可以定义多个@Signature对多个地方拦截,都用这个拦截器
@Signature (type = StatementHandler .class , //这是指拦截哪个接口
        method = "prepare",//这个接口内的哪个方法名,不要拼错了
        args = { Connection.class, Integer .class}),//// 这是拦截的方法的入参,按顺序写到这,不要多也不要少,如果方法重载,可是要通过方法名和入参来确定唯一的
})
public class MyPlugin implements Interceptor {
  private final Logger logger = LoggerFactory.getLogger(this.getClass());
  // //这里是每次执行操作的时候,都会进行这个拦截器的方法内
  
  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    //增强逻辑
    System.out.println("对方法进行了增强....");
    return invocation.proceed(); //执行原方法
  }


  /**
  * //主要是为了把这个拦截器生成一个代理放到拦截器链中
  * ^Description包装目标对象 为目标对象创建代理对象
  * @Param target为要拦截的对象
  * @Return代理对象
  */
  @Override
  public Object plugin(Object target) {
    System.out.println("将要包装的目标对象:"+target);
    return Plugin.wrap(target,this);
  }
     
  /**获取配置文件的属性**/
  //插件初始化的时候调用,也只调用一次,插件配置的属性从这里设置进来
  @Override
   public void setProperties(Properties properties) {
      System.out.println("插件配置的初始化参数:"+properties );
    }
}
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

sqlMapConfig.xml

<plugins>
  <plugin interceptor="com.lagou.plugin.MySqlPagingPlugin">
    <!--配置参数-->
    <property name="name" value="Bob"/>
  </plugin>
</plugins>
1
2
3
4
5
6

mapper接口

public interface UserMapper {
  List<User> selectUser();
}
1
2
3

mapper.xml

<mapper namespace="com.lagou.mapper.UserMapper">
<select id="selectUser" resultType="com.lagou.pojo.User">
  SELECT
  id,username
  FROM
  user
</select>
</mapper>
1
2
3
4
5
6
7
8

测试类

public class PluginTest {
  @Test
  public void test() throws IOException {
    InputStream resourceAsStream =
    Resources.getResourceAsStream("sqlMapConfig.xml");
    SqlSessionFactory sqlSessionFactory = new
    SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    List<User> byPaging = userMapper.selectUser();
    for (User user : byPaging) {
      System.out.println(user);
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 8.5 源码分析

执行插件逻辑

Plugin实现了 InvocationHandler接口,因此它的invoke方法会拦截所有的方法调用。invoke方法会 对所拦截的方法进行检测,以决定是否执行插件逻辑。该方法的逻辑如下:

   // -Plugin
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       try {
           /*
            *获取被拦截方法列表,比如:
            *
signatureMap.get(Executor.class), 可能返回 [query, update,
commit]
            */
           Set<Method> methods = signatureMap.get(method.getDeclaringClass());
           //检测方法列表是否包含被拦截的方法
           if (methods != null && methods.contains(method)) {
               //执行插件逻辑
               return interceptor.intercept(new Invocation(target, method, args));
               //执行被拦截的方法
               return method.invoke(target, args);
          } catch(Exception e){
          }
     
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

invoke方法的代码比较少,逻辑不难理解。首先,invoke方法会检测被拦截方法是否配置在插件的

@Signature注解中,若是,则执行插件逻辑,否则执行被拦截方法。插件逻辑封装在intercept中,该

方法的参数类型为Invocationo Invocation主要用于存储目标类,方法以及方法参数列表。下面简单看一下该类的定义

public class Invocation {
private final Object target;
private final Method method;
private final Object[] args;
public Invocation(Object targetf Method method, Object[] args) {
this.target = target;
this.method = method;
//省略部分代码
public Object proceed() throws InvocationTargetException, IllegalAccessException
{ //调用被拦截的方法
>> —
1
2
3
4
5
6
7
8
9
10
11

关于插件的执行逻辑就分析结束

# 8.6 pageHelper分页插件

MyBati s可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据

开发步骤:

1 导入通用PageHelper的坐标

2 在mybatis核心配置文件中配置PageHelper插件

3 测试分页数据获取

# 1导入通用PageHelper坐标

 <dependency>
       <groupId>com.github.pagehelper</groupId>
       <artifactId>pagehelper</artifactId>
       <version>3.7.5</version>
   </dependency>
   <dependency>
       <groupId>com.github.jsqlparser</groupId>
       <artifactId>jsqlparser</artifactId>
       <version>0.9.1</version>
    </dependency>
1
2
3
4
5
6
7
8
9
10

# 2 在mybatis核心配置文件中配置PageHelper插件

<!--注意:分页助手的插件 配置在通用馆mapper之前*-->*
<plugin interceptor="com.github.pagehelper.PageHelper">
<!—指定方言 —>
<property name="dialect" value="mysql"/>
</plugin>
1
2
3
4
5

# 3 测试分页代码实现

 @Test
   public void testPageHelper() {
       //设置分页参数
       PageHelper.startPage(1, 2);
       List<User> select = userMapper2.select(null);
       for (User user : select) {
           System.out.println(user);
      }
    }
}
1
2
3
4
5
6
7
8
9
10

获得分页相关的其他参数

//其他分页的数据
PageInfo<User> pageInfo = new PageInfo<User>(select);
System.out.println("总条数:"+pageInfo.getTotal());
System.out.println("总页数:"+pageInfo. getPages ());
System.out.println("当前页:"+pageInfo. getPageNum());
System.out.println("每页显万长度:"+pageInfo.getPageSize());
System.out.println("是否第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否最后一页:"+pageInfo.isIsLastPage());
1
2
3
4
5
6
7
8

# 8.7 通用 mapper

# 什么是通用Mapper

通用Mapper就是为了解决单表增删改查,基于Mybatis的插件机制。开发人员不需要编写SQL,不需要在DAO中增加方法,只要写好实体类,就能支持相应的增删改查方法

# 如何使用

  1. 首先在maven项目,在pom.xml中引入mapper的依赖
<dependency>
  <groupId>tk.mybatis</groupId>
  <artifactId>mapper</artifactId>
  <version>3.1.2</version>
</dependency>
1
2
3
4
5
  1. Mybatis配置文件中完成配置
<plugins>
  <!--分页插件:如果有分页插件,要排在通用mapper之前-->
  <plugin interceptor="com.github.pagehelper.PageHelper">
    <property name="dialect" value="mysql"/>
  </plugin>
  
  <plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor">
  <!-- 通用Mapper接口,多个通用接口用逗号隔开 -->
    <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
  </plugin>
</plugins>
1
2
3
4
5
6
7
8
9
10
11
  1. 实体类设置主键
@Table(name = "t_user")
public class User {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;
  private String username;
}
1
2
3
4
5
6
7
  1. 定义通用mapper
import com.lagou.domain.User;
import tk.mybatis.mapper.common.Mapper;
  public interface UserMapper extends Mapper<User> {
}
1
2
3
4
  1. 测试
public class UserTest {
   @Test
   public void test1() throws IOException {


       Inputstream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
       SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
       SqlSession sqlSession = build.openSession();
       UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
       User user = new User();
       user.setId(4);


       //(1)mapper基础接口
       //select 接口
       User user1 = userMapper.selectOne(user); //根据实体中的属性进行查询,只能有 —个返回值
       List<User> users = userMapper.select(null); //查询全部结果
       userMapper.selectByPrimaryKey(1); //根据主键字段进行查询,方法参数必须包含完 整的主键属性,查询条件使用等号
       userMapper.selectCount(user); //根据实体中的属性查询总数,查询条件使用等号
       // insert 接口
       int insert = userMapper.insert(user); //保存一个实体,null值也会保存,不会使用数据库默认值
       int i = userMapper.insertSelective(user); //保存实体,null的属性不会保存, 会使用数据库默认值
       // update 接口
       int i1 = userMapper.updateByPrimaryKey(user);//根据主键更新实体全部字段,null值会被更新
       // delete 接口
       int delete = userMapper.delete(user); //根据实体属性作为条件进行删除,查询条件使用等号
       userMapper.deleteByPrimaryKey(1); //根据主键字段进行删除,方法参数必须包含完 整的主键属性


       //(2)example方法
       Example example = new Example(User.class);
       example.createCriteria().andEqualTo("id", 1);
       example.createCriteria().andLike("val", "1");
       //自定义查询
       List<User> users1 = userMapper.selectByExample(example);
  }
}
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
上次更新: 2025/04/03, 11:07:08
Mybatis缓存
Mybatis架构原理

← Mybatis缓存 Mybatis架构原理→

最近更新
01
tailwindcss
03-26
02
PaddleSpeech
02-18
03
whisper
02-18
更多文章>
Theme by Vdoing | Copyright © 2019-2025 跨境互联网 | 豫ICP备14016603号-5 | 豫公网安备41090002410995号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式