文章目录
- MyBatis的核心流程介绍
- SqlSessionFactory的理解
- MyBatis中的Executor的源码理解
- Spring中是如何解决MySQL的SqlSession的线程安全问题
- MyBatis面向Mapper编程工作原理
- Mybatis动态sql执行原理
- Mybatis的一级、二级缓存实现原理
- Mybatis的插件运行原理
- 以及如何编写一个插件
- mybatis插件的应用有哪些
计算机的基本工作就是存储和计算,而MyBatis是存储领域的利器。MyBatis的基本工作原理就是:先封装SQL,接着调用JDBC操作数据库,最后把数据库返回的表结果封装成Java类。
MyBatis的核心流程介绍
mybatis应用程序通过SqlSessionFactoryBuilder从mybatis-config.xml配置文件中构建出SqlSessionFactory,然后,SqlSessionFactory的实例直接开启一个SqlSession,再通过SqlSession实例获得Mapper对象并运行Mapper映射的SQL语句,完成对数据库的CRUD和事务提交,之后关闭SqlSession。如下图所示:
MyBatis的工作原理如下图所示:
sqlsessionfactoryBuilder类加载核心配置文件(sqlmapConfig.xml)parse方法会产生会产生一个configration对象, configration对象通过构造方法的方式交给sqlsessionfactory对象。sqlsessionfactory产生sqlsession对象,并且将configuration对象通过sqlsession 构造方法的方式传给sqlsession对象。Sqlsession通configration对象拿到excutor执行器,调用select方法时,底层该方法会从configuration对象中去找sql语句(mappedstatement),执行并返回结果集
SqlSessionFactory的理解
SqlSessionFactory是MyBatis中的一个关键接口,用于创建SqlSession对象,是MyBatis的核心组件之一。
SqlSessionFactory是通过SqlSessionFactoryBuilder创建的,SqlSessionFactoryBuilder会读取MyBatis的配置文件(mybatis-config.xml),并根据配置文件中的信息构建SqlSessionFactory对象。
SqlSessionFactory的主要作用是提供了创建SqlSession对象的方法,SqlSession对象是MyBatis中执行数据库操作的主要接口。SqlSession可以通过SqlSessionFactory的openSession方法创建,并且可以设置是否自动提交事务。SqlSession的生命周期应该在一个较小的范围内控制,避免长时间持有,以免占用数据库连接资源。
SqlSessionFactory可以被多个线程共享,应该保证SqlSessionFactory的单例,避免资源浪费。
除此之外,SqlSessionFactory还可以配置MyBatis的一些全局属性,如数据库连接池、缓存等,这些全局属性可以在整个应用程序中共享,从而提高应用程序的性能和可维护性。
总之,SqlSessionFactory是MyBatis中非常重要的一个接口,负责创建SqlSession对象和管理MyBatis全局属性的配置,使用SqlSessionFactory可以简化数据库操作的编写和管理,提高应用程序的性能和可维护性。
SqlSession的理解
MyBatis是一个优秀的持久层框架,而SqlSession则是MyBatis框架中最为核心的组件之一。SqlSession可以看做是对数据库操作的一次会话,每个会话中可以执行多次数据库操作。下面是对SqlSession的一些理解:
- SqlSession的生命周期:SqlSession的生命周期是从它的创建到关闭。SqlSession的创建可以通过SqlSessionFactory来创建,一般情况下,我们在需要访问数据库的时候,就会创建一个SqlSession对象。当SqlSession对象不再使用时,应该将其关闭。
- SqlSession的作用:SqlSession封装了对数据库的操作,包括数据的插入、更新、删除和查询等操作。通过SqlSession可以执行Mapper中定义的方法,并将执行结果返回给应用程序。SqlSession还提供了事务管理的支持。
- SqlSession的管理:在MyBatis中,SqlSession的管理是由SqlSessionFactory来管理的。SqlSessionFactory可以通过配置文件或者Java代码来创建,每个应用程序通常只需要一个SqlSessionFactory实例,用于创建SqlSession对象。在应用程序中,SqlSession的管理一般由Spring框架或者自己手动管理。
- SqlSession的线程安全性:SqlSession不是线程安全的,每个SqlSession实例都应该被单独使用,不能被多个线程共享。在多线程环境下,如果多个线程共用一个SqlSession对象,则可能会出现数据混乱的情况,因此需要保证每个线程都有自己的SqlSession实例。
总之,SqlSession是MyBatis框架中最为核心的组件之一,它封装了对数据库的操作,提供了事务管理的支持,并由SqlSessionFactory进行管理。使用SqlSession时需要注意其生命周期、线程安全性等问题。
MyBatis中的Executor的源码理解
MyBatis框架中的Executor是一个执行器,负责执行SQL语句,与数据库进行交互,并将执行结果返回给调用方。Executor是MyBatis中最为核心的组件之一,它的实现涉及到多种设计模式和技术,包括装饰器模式、代理模式、线程池等。
Executor的实现类:MyBatis中默认提供了三个Executor的实现类,分别是SimpleExecutor、ReuseExecutor和BatchExecutor。SimpleExecutor是最简单的Executor实现,每次执行SQL语句都会创建一个新的Statement对象;ReuseExecutor会尝试重用Statement对象,避免多次创建Statement对象,提高执行效率;BatchExecutor则是批量执行SQL语句的Executor实现。
Executor的作用:Executor的主要作用是执行SQL语句,并将执行结果返回给调用方。在执行SQL语句之前,Executor会首先创建Statement对象,然后通过JDBC与数据库进行交互,将执行结果返回给MyBatis框架。Executor还负责缓存Statement对象,避免多次创建Statement对象,提高执行效率。
Executor的执行流程:Executor的执行流程可以概括为以下几个步骤:
- 根据传入的MappedStatement对象创建StatementHandler对象。
- 判断是否开启了二级缓存,如果开启了,则先从二级缓存中获取执行结果。
- 判断是否需要刷新缓存,如果需要,则清空缓存。
- 执行SQL语句,并将执行结果保存到缓存中。
- 如果开启了二级缓存,则将执行结果保存到二级缓存中。
Executor的线程安全性:Executor是线程安全的,多个线程可以共用同一个Executor实例。在多线程环境下,Executor会使用线程池来管理多个线程的执行,避免线程竞争和线程创建销毁的开销。
总之,Executor是MyBatis框架中最为核心的组件之一,它的实现涉及到多种设计模式和技术。Executor负责执行SQL语句,并将执行结果返回给调用方。使用Executor时需要注意其实现类、执行流程、线程安全性等问题。
Spring中是如何解决MySQL的SqlSession的线程安全问题
Spring提供了两种解决方案来解决SqlSession的线程安全问题:
1.使用SqlSessionTemplate
SqlSessionTemplate是Spring提供的一个线程安全的SqlSession实现。它封装了SqlSession的操作,并确保每个线程都有自己的SqlSession实例。因此,在多线程环境下,每个线程都可以独立地使用自己的SqlSession实例,而不会相互干扰。可以在配置文件中定义SqlSessionTemplate bean,然后在需要使用SqlSession时注入该bean。
2.使用@Scope注解
另一个解决方案是在配置文件中使用@Scope注解,将SqlSession的作用域设置为prototype。这将确保每次从容器中获取SqlSession时都会返回一个新的实例,因此每个线程都可以使用自己的SqlSession实例。可以在配置文件中声明SqlSession bean,并使用@Scope注解将其作用域设置为prototype。在需要使用SqlSession时,可以注入该bean。
这两种解决方案都可以有效地解决SqlSession的线程安全问题。选择哪种方案取决于具体的需求和实现细节。
MyBatis中的Configuration的源码的理解
Configuration类的源码主要涉及以下几个方面:
1.加载配置文件
在MyBatis的配置文件中,可以配置数据源、映射文件、插件、类型别名等信息。Configuration通过XMLConfigBuilder类来加载配置文件,并将解析后的配置信息保存到Configuration对象中。
2.创建SqlSessionFactory
Configuration类也负责创建SqlSessionFactory。它会通过build方法创建SqlSessionFactory对象,并将该对象缓存起来,以便后续使用。在创建SqlSessionFactory对象时,会将Configuration对象作为参数传入,以便SqlSessionFactory可以获取MyBatis的配置信息和映射信息。
3.管理映射信息
MyBatis中的映射文件通常包含SQL语句和实体类之间的映射关系。Configuration会读取映射文件,将其中的SQL语句解析成MappedStatement对象,并将其保存到mappedStatements集合中。mappedStatements集合中保存了所有映射文件中定义的SQL语句,以及它们对应的MappedStatement对象。
4.管理缓存
Configuration还负责管理MyBatis的缓存。它会读取配置文件中的缓存配置信息,并创建对应的缓存对象,缓存对象被保存在caches集合中。在执行SQL语句时,如果该语句对应的MappedStatement对象中配置了缓存,则会从caches集合中获取缓存对象,并使用缓存对象来提高查询效率。
总的来说,Configuration类是MyBatis框架的核心组成部分之一,它负责加载配置文件、管理映射信息和缓存等功能,是整个框架的配置和管理中心。通过深入理解Configuration的源码,我们可以更好地理解MyBatis框架的
MyBatis面向Mapper编程工作原理
Mapper接口是没有实现类的,当调用接口方法时,采用了JDK的动态代理,先从Configuration配置类MapperRegistry对象中获取mapper接口和对应的代理对象工厂信息(MapperProxyFactory),然后利用代理对象工厂MapperProxyFactory创建实际代理类(MapperProxy),最后在MapperProxy类中通过MapperMethod类对象内保存的中对应方法的信息,以及对应的sql语句的信息进行分析,最终确定对应的增强方法进行调用。
为什么MyBatis Mapper接口中的方法不支持重载
在MyBatis源码中有这么几行代码,我们可以看到在解析XML文件创建mapper接口对应方法的时候,采用了接口全限名+方法名的方式作为StrictMap(MappedStatement数据存放的Map集合)的key值,而源码对于StrictMap的put方法进行了判断,如果存入的数据key已重复则抛出异常,所以Mapper接口中的方法不支持重载
id = applyCurrentNamespace(id, false);
public String applyCurrentNamespace(String base, boolean isReference) {
...
//返回值为mapper的全限名(xml中namespace的值)+方法名(xml中Statement id的值)
return currentNamespace + "." + base;
}
Mybatis动态sql执行原理
(1)初始化阶段:通过XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder解析XML文件中的信息存储到Configuration类中;
(2)代理阶段:先从Configuration配置类MapperRegistry对象中获取mapper接口和对应的代理对象工厂信息,再利用代理对象工厂MapperProxyFactory创建实际代理类,最后在MapperProxy类中通过MapperMethod类对象内保存的中对应方法的信息,以及对应的sql语句的信息进行分析,最终确定对应的增强方法进行调用。
(3)数据读写阶段:通过四种Executor调用四种Handler进行查询和封装数据;
Mybatis都有哪些Executor执行器?它们之间的区别是什么
BaseExecutor:基础抽象类,实现了executor接口的大部分方法,主要提供了缓存管理和事务管理的能力,使用了模板模式,doUpdate,doQuery,doQueryCursor 等方法的具体实现交给不同的子类进行实现
CachingExecutor:直接实现Executor接口,使用装饰器模式提供二级缓存能力。先从二级缓存查,缓存没有命中再从数据库查,最后将结果添加到缓存中。如果在xml文件中配置了cache节点,则会创建CachingExecutor。
BatchExecutor:BaseExecutor具体子类实现,在doUpdate方法中,提供批量执行多条SQL语句的能力;
SimpleExecutor:BaseExecutor具体子类实现且为默认配置,在doQuery方法中使用PrepareStatement对象访问数据库, 每次访问都要创建新的 PrepareStatement对象;
ReuseExecutor:BaseExecutor具体子类实现,与SimpleExecutor不同的是,在doQuery方法中,使用预编译PrepareStatement对象访问数据库,访问时,会重用缓存中的statement对象,而不是每次都创建新的PrepareStatement。
Mybatis的一级、二级缓存实现原理
(1)一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,Mybatis默认打开一级缓存,一级缓存存放在BaseExecutor的localCache变量中:
(2)二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace)级别。Mybatis默认不打开二级缓存,可以在config文件中xml开启全局的二级缓存,但并不会为所有的Mapper设置二级缓存,每个mapper.xml文件中使用标签来开启当前mapper的二级缓存,二级缓存存放在MappedStatement类cache变量中:
(3)对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被清除并重新更新,如果开启了二级缓存,则只根据配置判断是否刷新。
Mybatis的插件运行原理
Mybatis 仅可以编写针对 ParameterHandler、ResultSetHandler、
StatementHandler、Executor 这 4 种接口的插件,Mybatis 使用 JDK 的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandler 的 invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
编写插件:实现 Mybatis 的 Interceptor 接口并复写 intercept()方法,然 后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了 在配置文件中配置你编写的插件
MyBatis 的插件(Interceptor)是一种可以拦截并修改 MyBatis 执行过程的机制,它可以在 SQL 语句执行的各个阶段进行干预和增强,比如在 SQL 语句执行前后添加日志、统计信息等功能。插件的运行原理主要涉及以下几个关键点:
- Interceptor 接口:
○ MyBatis 的插件需要实现 Interceptor 接口,并覆盖其中的 intercept 方法,在这个方法中编写拦截逻辑。 - 拦截器链:
○ 在 MyBatis 的配置文件(mybatis-config.xml)中,可以通过 标签配置插件,并指定插件类的路径。MyBatis 在初始化时会构建一个插件链,将所有配置的插件按顺序组成一个链表结构,当执行 SQL 语句时,会依次调用每个插件的 intercept 方法。 - 拦截点:
每个插件可以通过实现 intercept 方法来定义拦截点,即在 SQL 语句执行的哪个阶段进行拦截和处理。常见的拦截点包括:生成代理对象、创建 Statement 对象、执行查询操作等。 - Invocation 对象:
○ 在 intercept 方法中,每个插件都会接收一个 Invocation 对象作为参数,该对象包含了目标对象、方法以及方法参数等信息,插件可以根据这些信息进行自定义的处理。 - 动态代理:
○ MyBatis 中的插件是通过动态代理来实现的,插件会对目标对象进行代理,当调用目标对象的方法时,会先经过插件的拦截逻辑,再转发给目标对象执行。
总体来说,MyBatis 插件的运行原理是通过动态代理和拦截器链实现的,插件可以在 SQL 执行的各个阶段进行拦截和处理,从而实现对 MyBatis 执行过程的定制化扩展。开发者可以利用插件机制来实现日志记录、性能监控、权限控制等功能,从而满足项目特定需求。
应用场景
- 一些字段的自动填充
- SQL语句监控、打印、数据权限等
- 数据加解密操作、数据脱敏操作
- 分页插件
- 参数、结果集的类型转换
以及如何编写一个插件
Mybatis 是一个流行的 Java ORM 框架,允许将 SQL 语句映射到 Java 对象。编写一个 Mybatis 插件可以扩展框架的功能,提供更好的灵活性和可维护性。下面是一个简单的 Mybatis 插件示例:
- 创建一个 Java 项目并添加 Mybatis 和 Mybatis-Spring 的依赖。
- 创建一个类,实现 org.apache.ibatis.plugin.Interceptor 接口,并重写以下方法:
public Object intercept(Invocation invocation) throws Throwable;
public Object plugin(Object target);
public void setProperties(Properties properties);
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.Configuration;
import java.sql.Connection;
import java.util.Properties;
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class MyPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 在 StatementHandler 准备期间执行的操作
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
String originalSql = statementHandler.getBoundSql().getSql();
System.out.println("原始 SQL 语句: " + originalSql);
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
// 为目标对象创建代理
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 接收来自 Mybatis 配置文件的属性设置
String foo = properties.getProperty("foo");
System.out.println("foo 属性: " + foo);
}
}
- 在 intercept 方法中,您可以添加任何您想要执行的逻辑。例如,可以在此处修改 SQL 语句或添加自定义逻辑。在本例中,我们只是简单地打印原始 SQL 语句。
- 在 plugin 方法中,使用 Plugin.wrap 方法为目标对象创建代理。这将使 Mybatis 在执行目标对象的方法之前和之后调用插件中的方法。
- 在 setProperties 方法中,可以接收来自 Mybatis 配置文件的属性设置。这些属性可用于根据需要配置插件行为。
- 将插件添加到 Mybatis 配置文件(例如 mybatis-config.xml)中:
编写插件的步骤:
(1)实现Interceptor接口方法
(2)确定拦截的签名
(3)在配置文件中配置插件
在创建三个重要的Handler(StatementHandler、ParameterHandler、ResultSetHandler)时通过插件数组包装了三大Handler:
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
获取到所有的Interceptor(拦截器)(插件需要实现的接口),调用:
interceptor.plugin(target);
返回target包装后的对象,最后回调回自定义插件的intercept方法执行插件内的代码逻辑。可拦截的接口和方法一览:
Executor(update、query 、 flushStatment 、 commit 、 rollback 、 getTransaction 、 close 、 isClose)
StatementHandler(prepare 、 paramterize 、 batch 、 update 、 query)
ParameterHandler( getParameterObject 、 setParameters )
ResultSetHandler( handleResultSets 、 handleCursorResultSets 、 handleOutputParameters )
mybatis插件的应用有哪些
MyBatis 是一个开源的持久层框架,它简化了数据库操作,并且提供了很多方便的功能。MyBatis 插件是一种扩展机制,允许开发人员在 MyBatis 的核心功能上添加自定义的功能或者行为。下面是一些常见的 MyBatis 插件的应用场景:
- 分页插件:提供了在 SQL 查询中添加分页功能的支持,常见的分页插件有 MyBatis-PageHelper 和 MyBatis-Spring 的分页支持。
- 缓存插件:MyBatis 提供了一级缓存和二级缓存,但有时候需要更加灵活地控制缓存的行为,比如清除缓存、自定义缓存键等。缓存插件可以扩展 MyBatis 的缓存功能,如 MyBatis-Redis、MyBatis-Ehcache 等。
- 审计插件:用于记录数据库操作的日志,包括操作类型、执行时间、参数等信息,便于后期排查问题和审计。常见的审计插件有 MyBatis-SQLLogger、MyBatis-Log4j2 等。
- 乐观锁插件:实现乐观锁机制,用于在并发情况下避免数据的冲突。乐观锁插件可以在更新操作中自动判断数据版本,避免更新过期数据。常见的乐观锁插件有 MyBatis-OptimisticLocker 等。
- 数据加密插件:用于对数据库中的敏感数据进行加密处理,保护数据安全。数据加密插件可以在 MyBatis 的查询和写入操作中自动加解密数据。常见的数据加密插件有 MyBatis-Encrypt 等。
- SQL 注入插件:用于检测和防止 SQL 注入攻击,可以在 SQL 执行前对参数进行检查和过滤,确保 SQL 的安全执行。常见的 SQL 注入插件有 MyBatis-SQLInjection 等。
- 自定义类型处理器插件:用于处理数据库字段与 Java 类型之间的映射关系,可以自定义类型处理器来处理特定类型的数据转换。常见的自定义类型处理器插件有 MyBatis-EnumTypeHandler、MyBatis-JsonTypeHandler 等。
这些插件可以根据项目的需求进行选择和集成,提高开发效率和代码质量。