MyBatis
JDBC流程
(12条消息) 操作数据库的三种选择_mybatis gpa_A minor的博客-CSDN博客
Java DataBase Connectivity — java 数据库连接
1、加载驱动Driver,通过类加载机制 执行static代码 进行 xx
2、获取连接 – (连接池)
3、创建sql
4、statement参数设置
5、statement.execute()
6、处理返回集信息
Mybatis执行流程
(10条消息) 【MyBatis】执行原理(一):创建会话工厂(SqlSessionFactory) –配置解析源码分析_A minor的博客-CSDN博客
配置类解析
在这一步,我们主要完成了config配置文件、Mapper文件、Mapper接口上的注解的解析。
我们得到了一个最重要的对象Configuration,这里面存放了全部的配置信息,它在属性里面还有各种各样的容器。
最后,返回了一个DefaultSqlSessionFactory,里面持有了Configuration的实例。
(10条消息) 【MyBatis】执行原理(二):创建会话(SqlSession) 源码分析_sqlsession addbatch_A minor的博客-CSDN博客
创建会话
SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
ReuseExecutor:执行 update 或 select,以 sql 作为 key查找 Statement 对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象,而是放置于 Map 内,供下一次使用。简言之,就是重复使用Statement对象。
BatchExecutor:执行 update(没有select,JDBC 批处理不支持 select),将所有 sql 都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement 对象,每个Statement 对象都是 addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。
创建代理对象
(12条消息) 【MyBatis】执行原理(三):获取代理对象(MapperProxy) 源码分析_A minor的博客-CSDN博客
获得Mapper对象的过程,实质上是获取了一个 MapperProxy 的代理对象。MapperProxy 中有 sqlSession、mapperInterface、methodCache。
MapperProxy -> InvocationHandler (JDK动态代理 – 通过接口)
执行Executor
(12条消息) 【MyBatis】执行原理(四):Executor 执行SQL源码分析_sqlsessionfactory execute sql_A minor的博客-CSDN博客
使用
第一种
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public void testMapper() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources. getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = newSqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); try { Blog blog = session.selectOne("blog.findUserById",1); System.out.println(blog); } finally { session.close(); } }
|
第二种
Mapper代理模式。通过接口来代理一个 mapper.xml 的执行,这个接口全路径必须跟 Mapper.xml 里面的 namespace 对应起来,方法也要跟 StatementID一一对应。
1 2 3 4
| public interface BlogMapper { Blog selectBlogById(int id); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public void testMapper() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources. getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = newSqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); try { BlogMapper mapper = session.getMapper(BlogMapper.class); Blog blog = mapper.selectBlogById(1); System.out.println(blog); } finally { session.close(); } }
|
#{}:占位符号,好处防止sql注入 ${}:sql拼接符号
(1条消息) MyBatis源码解析(二)——执行流程_mybati执行代码_爱看雲的雲的博客-CSDN博客
架构
1 2 3 4 5 6 7 8 9 10 11 12
| MyBatis层级结构如上图所示,先简单介绍一下各个组件:
SqlSession:它是 MyBatis 的核心API,主要用来执行命令,获取映射,管理事务。接收开发人员提供的StatementId和参数,并返回操作结果。 Executor:执行器,是 MyBatis 调度的核心,负责SQL语句的生成以及查询缓存的维护。 StatementHandler:用于封装JDBC中Statement的操作。即声明SQL、设置参数、执行SQL等。通常一次SQL执行完之后就要关闭它,最多它的生命周期也不会超过一次事务的范围。 ParameterHandler:负责对用户传递过来的参数转换成JDBC Statement所需要的参数。 ResultSetHandler:负责将 JDBC 返回的ResultSet结果集对象转换成List类型的集合。 TypeHandler:用于 Java类型和 JDBC类型之间的转换。 BoundSql:表示动态生成的 SQL语句以及相应的参数信息。 MappedStatement:动态SQL 的封装,存储了一个 SQL所有的信息。 SqlSource:表示从 XML文件或注解方法读取的映射语句的内容,它创建将从用户接收的输入参数传递给数据库的SQL。 Configuration:MyBatis 所有的配置信息都维持在 Configuration 对象中。
|
1 2 3 4 5 6 7 8
| 先通过 Resource 读取 MyBatis 配置文件(mybatis-config.xml),将其转换成输入流。 将输入流传入 SqlSession工厂构建器(SqlSessionFactoryBuilder),调用 build方法传入输入流生成 XML配置文件构造器(XMLConfigBuilder)。 XMLConfigBuilder 会解析 mybatis-config.xml 文件中的所有标签,其中最重要的就是解析标签,加载的所有配置都会保存在 Configuration 对象中。 遍历 mapper 标签,根据资源标识类型进行不同的操作,对于资源标识为 package 和 class 的,使用configuration 的 addMappers 和 addMapper 方法解析;资源标识为 resource 和 url 的,新建一个 mapper映射文件构造器(XMLMapperBuilder)去解析对应的 mapper 映射文件。 先看 Configuration 对象的 addMappers,调用 mapper 注册器(MapperRegistry)的 addMappers 方法。mapper 注册器解析流程为:资源标识为 package,获取配置包下的所有 mapper 映射文件,再获取文件中指向的 mapper 接口 Class 对象集合,遍历解析 Class 对象。资源标识为 class,则直接解析 Class 对象。调用 mapper 注解构造器(MapperAnnotationBuilder)的 parse 方法解析 Class 对象。 mapper注解构造器主要分为两个步骤,一是调用 XMLMapperBuilder 解析 mapper接口对应的 mapper映射文件,这里和解析资源标识为 resource 和 url 的 mapper 标签,殊途同归了;二是解析 mapper 接口方法上的SQL语句,生成对应的 MappedStatement 对象。 mapper映射文件构造器先解析 mapper 映射文件中的 resultMap、sql 等标签,然后调用 mapper 映射文件声明构造器(XMLStatementBuilder)解析CRUD标签(select、insert、update、delete),遍历这些标签,然后封装为SqlSource,最后调用 MappedStatement 创建助手(MapperBuilderAssistant)封装成MappedStatement 对象,一个 CRUD 标签生成一个 MappedStatement 对象。 最后将 MappedStatement 添加到 Configuration 对象的 mappedStatements 属性中。
|
Mybatis关键类
对象 |
相关对象 |
作用 |
Configuration |
MapperRegistry TypeAliasRegistry TypeHandlerRegistry |
包含了 MyBatis 的所有的配置信息 |
SqlSession |
SqlSessionFactory DefaultSqlSession |
对操作数据库的增删改査的 API 进行了封装,提供给应用层使用 |
Executor |
BaseExecutor SimpleExecutor BatchExecutor ReuseExecutor |
MyBatis 执行器 , 是 MyBatis 调度的核心, 负责SQL语句的生成和查询缓存的维护 |
StatementHandler |
BaseStatementHandler SimpleStatementHandler PreparedStatementHandler CallableStatementHandler |
封装了 JDBC Statement 操作,负责对 JDBC Statement 的操作, 如设置参数、将Statement结果转换为List集合 |
ParameterHandler |
DefaultParameterHandler |
把用户传递的参数转换成 JDBC Statement 所需要的参数 |
MapperProxy |
MapperProxyFactory |
代理对象,用于代理 Mapper 接口方法 |
MappedStatement |
SqlSource BoundSql |
MappedStatement 维护了一条 〈select 丨 update丨 delete丨 insert〉节点的封装,包括了 SQL 信息、入参信息、出参信息 |
Executor
BaseExecutor:
ReuseExecutor: 缓存sql,直接再次使用
SimpleExecutor : 普通的Executor 准备Handler,
– 获取连接,准备statement,调用Hanlder,
– 调用Handler的query()
StatementHandler
Statement
PrepareStatementWrapper
ClientPraparedStatement
插件plugin
增强Executor
在初始 Executor的时候,会调用Configuration的newExecutor() 方法,执行插件执行链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? this.defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Object executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); }
if (this.cacheEnabled) { executor = new CachingExecutor((Executor)executor); } Executor executor = (Executor)this.interceptorChain.pluginAll(executor); return executor; }
|
增强Handler
在初始化Handler的时候,会进入Configuration中,调用newStatementHandler() ,其中会调用 插件执行链进行增强操作
1 2 3 4 5 6 7 8 9 10
| protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, (ResultHandler)null, boundSql); Statement stmt = this.prepareStatement(handler, ms.getStatementLog()); Cursor<E> cursor = handler.queryCursor(stmt); stmt.closeOnCompletion(); return cursor; }
|
1 2 3 4 5 6 7 8
| public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); StatementHandler statementHandler = (StatementHandler)this.interceptorChain.pluginAll(statementHandler); return statementHandler; }
|
Spring整合MyBatis
mybatis关键对象,SqlSessionFactory(创建SqlSession), SqlSession, MapperProxy(接口代理), Configuration(配置类封装)
步骤:
1、将工厂注入到容器 —- 通过实现 FactoryBean实现自定义 工厂创建逻辑 (需要加载Configuration 等)
2、SqlSession的默认实现DefalutSqlSession 生命周期为 (请求 / 连接),线程不安全,需要通过 SqlSessionTemplate,每一次请求创建一个DefalutSqlSession ,然后调用对应方法
3、Mapper接口注入为BeanDefinition,通过MapperScanner实现 扫描类路径,逻辑类似于 AnnotatedApplicationContext的 ClassPathBeanDefinitionScanner,用于类路径扫描并加载为BeanDefinition
4、Mapper是接口,需要生成对应的代理对象,Spring 中实现FactoryBean,为每个Mapper的BeanDefinition创建能够调用方法的代理对象MaperProxy
对象 |
生命周期 |
SqlSessionTemplate |
Spring 中 SqlSession 的替代品,是线程安全的,通过代理的方式调用 DefaultSqlSession 的方法 |
SqlSessionInterceptor(内部类) |
代理对象,用来代理 DefaultSqlSession,在 SqlSessionTemplate 中使用 |
SqlSessionDaoSupport |
用于获取 SqlSessionTemplate,只要继承它即可 MapperFactoryBean 注册到 IOC 容器中替换接口类,继承了 SqlSessionDaoSupport 用来获取 SqlSessionTemplate,因为注入接口的时候,就会调用它的 getObject()方法 |
SqlSessionHolder |
控制 SqlSession 和事务 |
接口 |
方法 |
作用 |
FactoryBean |
getObject() |
返回由 FactoryBean 创建的 Bean 实例 |
InitializingBean |
afterPropertiesSet() |
bean 属性初始化完成后添加操作 |
BeanDefinitionRegistryPostProcessor |
postProcessBeanDefinitionRegistry() |
注入 BeanDefination 时添加操作 |