mybatis工作原理及流程,Mybatis中@Mapper与@MapperScan配置及注入原理解析
mybatis工作原理及流程,Mybatis中@Mapper与@MapperScan配置及注入原理解析详细介绍
本文目录一览: Mybatis 执行流程浅析(附深度文章推荐 & 面试题集锦)
假如我们要自己设计一个半自动的仿 Mybatis 框架,有哪些环节是必不可少的呢?思考再三,必然有以下环节:
如果仅考虑这三点的话,其实实现一个简单的 ORM 框架就很容易了,再附加一些反射和正则表达式等等就可以搞定了.
那如果去参考 Mybatis,我们来看看它的几个环节是如何设计的:
其实大致思路一样,需要一个数据结构去存储全部的变量,通过接口代理的方式调用 Sqlsession 里面内置的方法,`` 不同的是真正的执行者又加了一层,是 Executor ``,再通过原始 JDBC 返回数据给调用者,当然, 真正的 Mybatis 包含了众多的设计模式以及数据源,缓存,动态 SQL,数据库事务,延迟加载处理 等等
为了验证 mybatis 的执行流程,采用了两种方式去调用接口,如下所示:
>
这里有个小点需要强调下,真正的执行者是`` Executor ``,我们每次在使用以下代码:
通过查看源码也可以看到,SqlSession 接口的默认实现类是`` DefaultSqlSession ``
而方法真正的执行,如 selectList 方法:
>
#{} 是预编译处理,${}是字符串替换。Mybatis 在处理 #{}时,会将 sql 中的 #{}替换为?号,调用 PreparedStatement 的
set 方法来赋值;
Mybatis 在处理时,就是把时,就是把{}替换成变量的值。
使用 #{}可以有效的防止 SQL 注入,提高系统安全性
PS:mybatis 执行的本质还是 SQL,因此回归本质可以简单理解为一个对于 PreparedStatement ,一个对应 Statement
Dao 接口即 Mapper 接口,接口的全限名,就是映射文件中的 namespace 的值;接口的方法名,就是映射文件中 Mapper 的 Statement 的 id 值;接口方法内的参数,就是传递给 sql 的参数
`` 实现原理: ` Mapper接口的工作原理是JDK动态代理,mybatis会对每一个mapper代理生成一个mapperProxy对象,代理对象会拦截接口方法,转而自动对应到sqlsession上,最终由 ` Executor ``执行
`` 参数不同,方法不可重载 ``,为什么?
上文说到 mybatis 有一个环节是解析 XML 文件或者解析接口,它会去构建一个叫做 MapperStatement 对象去存储 mapper 的相关信息,每一个 dao 接口方法在执行的时候到底是如何定位找到对应的 MapperStatement 的呢?
源码逻辑图:
什么是mybatis 为什么要使用my batis
MyBatis 是一个可以自定义SQL、存储过程和高级映射的持久层框架。MyBatis 摒除了大部分的JDBC代码、手工设置参数和结果集重获。MyBatis 只使用简单的XML 和注解来配置和映射基本数据类型、Map 接口和POJO 到数据库记录。相对Hibernate和Apache OJB等“一站式”ORM解决方案而言,Mybatis 是一种“半自动化”的ORM实现。
需要使用的Jar包:mybatis-3.0.2.jar(mybatis核心包)。mybatis-spring-1.0.0.jar(与Spring结合包)。
MyBatis的前身是ibatis,但是在配置sql的语法上有明显的区别,并且spring目前的版本封装mybatis,至于mybatis-spring.jar文件也是mybatis团队复杂开发的jar包,用于和spring整合。之前ibatis的源码托管方是apache,而mybatis是google。
1、定义
MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。
2、使用原因
MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
3、总体流程
(1)加载配置并初始化
触发条件:加载配置文件
处理过程:将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
(2)接收调用请求
触发条件:调用Mybatis提供的API
传入参数:为SQL的ID和传入参数对象
处理过程:将请求传递给下层的请求处理层进行处理。
(3)处理操作请求
触发条件:API接口层传递请求过来
传入参数:为SQL的ID和传入参数对象
处理过程:
(A)根据SQL的ID查找对应的MappedStatement对象。
(B)根据传入参数对象解析MappedStatement对象,得到最终要执行的SQL和执行传入参数。
(C)获取数据库连接,根据得到的最终SQL语句和执行传入参数到数据库执行,并得到执行结果。
(D)根据MappedStatement对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理结果。
(E)释放连接资源。
(4)返回处理结果将最终的处理结果返回。
MyBatis-Plus 使用这么方便,底层是如何处理的呢?
MyBatis-plus是完全基于MyBatis开发的一个增强工具,是在MyBatis的基础上做增强的框架,为简化开发、提高效率而生。
它在MyBatis原本的框架上增加了很多实用性功能,比如乐观锁插件、字段自动填充功能、分页插件、条件构造器、sql 注入器等等。使用 MyBatis-plus 可以完全不写任何 XML 文件,直接使用继承了BaseMapper 接口的类对象完成对数据库的映射操作
基于映射的原理,MyBatis-plus 必然要实现 Mapper中的方法与 SQL 语句的对应转化,以下即为 MyBatis-plus 重要流程图例
1.在 MyBatis-plus 中, MybatisPlusAutoConfiguration 自动配置类的 sqlSessionFactory() 方法为 Spring提供创建 sqlSession 的工厂类对象,对 sqlSessionFactory 进行定义的定义类变为了 MybatisSqlSessionFactoryBean 。
在 sqlSessionFactory() 方法中,除了注入 MyBatis本身的组件,还会注入MyBatis-plus 的 主键生成器、SQL 注入器等组件,最后通过 MybatisSqlSessionFactoryBean#getObject() 方法获取到 sqlSessionFactory 对象
2. MybatisSqlSessionFactoryBean#getObject() 执行懒加载策略,最后通过 buildSqlSessionFactory() 方法创建 SqlSessionFactory 工厂类对象。这个方法的流程很长,不过大致可以分为两个步骤:
3. MybatisXMLConfigBuilder#parse() 会去解析配置文件,最后会调用到其内部方法 mapperElement() 。这个方法完成解析 Mapper工作,并将其添加到配置类 MybatisConfiguration 中
4. MybatisConfiguration#addMapper() 方法其实是去调用 MybatisMapperRegistry#addMapper() 方法,其核心是 MybatisMapperAnnotationBuilder#parse()
5. MybatisMapperAnnotationBuilder#parse() 方法真正开始完成 Mapper 接口中的方法与 SQL 语句的映射,其中 parseStatement() 方法是解析 @Select/@Update 等注解写入的 SQL语句,而代码 GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type ) 通过 MaBatis-plus的 SQL 注入器完成 Mapper 方法与 SQL 语句的转化
6. AbstractSqlInjector#inspectInject() 会完成 BaseMapper 接口中提供的通用方法对应的 SQL 语句准备,这部分主要通过 AbstractMethod#inject() 方法完成
7. AbstractMethod#inject() 方法并没有什么特别的操作,只是调用其子类实现 injectMappedStatement() 方法。以 SelectOne#injectMappedStatement() 为例,其 SQL 语句的核心在于 SqlMethod 类,这个枚举类中缓存了可以动态拼接的 SQL 语句脚本,只需要填上参数 format 就可以得到 SQL 语句的执行脚本。
以上过程结束,只需要将所有信息通过 addInsertMappedStatement() 方法封装成 MappedStatement 对象并将其加入到容器中,这样 Mapper接口方法调用时,就可以通过 动态代理 的方式找到其对应执行的 SQL 脚本,至此 SQL 语句准备及配置解析就完成了。
最后拼接的 SQL 语句 脚本形式如下示例,实际执行数据库操作时会解析这个脚本完成变量替换,从而得到可执行的 SQL 语句
8. SqlSessionFactory 对象的创建需要回到 MybatisSqlSessionFactoryBean#buildSqlSessionFactory() 方法中,很容易追踪到 MybatisSqlSessionFactoryBuilder#build() 方法,最后其实是通过 SqlSessionFactoryBuilder#build() 方法创建了一个 DefaultSqlSessionFactory 对象返回
1. @MapperScan 注解通过 @Import(MapperScannerRegistrar.class) 引入扫描注册的类 MapperScannerRegistrar ,该类实现了 ImportBeanDefinitionRegistrar 接口并重写 registerBeanDefinitions() 方法,在该方法中注册了 MapperScannerConfigurer 类
2. MapperScannerConfigurer 是 Mapper接口的扫描配置类,实现了 BeanDefinitionRegistryPostProcessor 接口,其 postProcessBeanDefinitionRegistry() 方法会在容器启动过程中被回调,通过 ClassPathMapperScanner#scan() 方法完成 Mapper 的扫描注册
3. ClassPathMapperScanner#processBeanDefinitions() 将扫描到的 Mapper接口生成的对应 BeanDefinition 的 beanClass 属性替换为 MapperFactoryBean ,这样每次获取 Mapper 实例实际是通过 MapperFactoryBean 的实例去获取
此处体现了 FactoryBean 的定位,即用于获取同一类 bean 的工厂 bean。
4. @Autowired 自动注入 Mapper 触发容器获取 bean 的方法,调用到 MapperFactoryBean#getObject() 方法,最终调用到 sqlSessionTemplate#getMapper() 方法
5.MyBatis-plus 使用的配置类是 MybatisConfiguration ,最终调用到 MybatisMapperRegistry#getMapper() 方法,这里就进入了动态代理获取 MapperProxy 实例的流程
6. MybatisMapperProxyFactory#newInstance() 方法给自动注入返回一个 MybatisMapperProxy 代理对象
7.调用 Mapper 接口的方法触发代理对象的 MybatisMapperProxy#invoke() ,此时根据 Mapper 对象被调用的方法生成 MybatisMapperMethod 对象,通过 MybatisMapperMethod#execute() 去真正地执行 SQL 语句,从而完成数据库操作。
Mybatis中@Mapper与@MapperScan配置及注入原理解析
问题背景:
执行流程:
1.发现Bean定义:首先根据@MapperScan中的basePackage或者@Mapper所在的package取得需要扫描的包,之后通过ClassPathMapperScaner获取包下所有Mapper接口类的BeanDefinition;
2.注册Bean:设置beanClass为MapperFactoryBean,再设置MapperFactoryBean的构造参数为实际的Mapper接口类,然后通过ClassPathBeanDefinitionScanner父类进行Bean注册
3.调用Bean:自动注入时,通过调用MapperFactoryBean的getObject获取实例
原理解析:
mybatis中动态sql执行原理
解释器模式: 初始化过程中构建出抽象语法树,请求处理时根据参数对象解释语法树,生成sql语句。
工厂模式: 为动态标签的处理方式创建工厂类(SqlTagHandlerFactory),根据标签名称获取对应的处理方式。
策略模式: 将动态标签处理方式抽象为接口,针对不同标签有相应的实现类。解释抽象语法树时,定义统一的解释流程,再调用标签对应的处理方式完成解释中的各个子环节
ssm框架原理及流程是什么?
SSM全称是Spring+SpringMVC+MyBatis。
SSM框架集由Spring、MyBatis两个开源框架整合而成(SpringMVC是Spring中的部分内容)。常作为数据源较简单的web项目的框架。
1、Spring
Spring就像是整个项目中装配bean的大工厂,在配置文件中可以指定使用特定的参数去调用实体类的构造方法来实例化对象。也可以称之为项目中的粘合剂。
2、SpringMVC
SpringMVC在项目中拦截用户请求,它的核心Servlet即DispatcherServlet承担中介或是前台这样的职责。
3、mybatis
mybatis是对jdbc的封装,它让数据库底层操作变的透明。
SSM框架集是软件架构的一个部分。以下是软件架构的种类:
1、逻辑架构
软件系统系统当中的各个元件之间所存在的关系,比如外部系统接口、用户界面、商业逻辑元件、数据库等。
2、物理架构
究竟是怎样做到在硬件当中放置软件元件。例如处于上海与北京进行分布的分布式系统的物理架构,这也就是说全部的元件都是属于物理设备,主要的有主机、整合服务器、应用服务器、代理服务器、存储服务器、报表服务器、Web服务器、网络分流器等。
struts2+spring+mybatis的工作流程和工作机制
我没有使用过mybatis,但是作为ORM框架,它和hibernate差不多。struts2+spring+mybatis组合一般来说是struts2主要负责url的拦截、处理然后根据action的配置选择返回页面。对bean(这里姑且将model,dao,service等java类统称为bean)的管理一般是委托给spring来处理,这里主要是为了使用spring控制反转(ioc)。当然数据库事务处理等由mybatis来实现。
其流程我总结如下:当来一个页面请求(也就是一个url),由FilterDispatcher进行拦截,在urlmapping中(或者说是配置文件中)查找对这个url进行处理的action实例类,将请求交给找到的action实例类进行处理,处理以后(这里的处理包括对数据库的操作这时便用到了mybatis)根据处理后的返回值再次查找配置文件,找到返回页面展示给用户。
这里只是最粗略的工作流程,中间可能还有其他操作如:编码过滤,权限控制,自己定义的struts2的拦截器等等。
Mybatis插件和通用Mapper使用
1、mybatis执行过程分析
2、mybatis插件
3、通用Mapper使用
1.1 getMapper的到MapperProxy实例:
部分源码:
DefaultSqlSession类:
Configuration类:
MapperRegistry类:
MapperProxyFactory类:
1.2 Executor执行的过程:
部分源码:
MapperProxy类:
MapperMethod类:
DefaultSqlSession类:
BaseExecutor类:执行器的实现类:
SimpleExecutor类:
1.3 执行流程描述
执行器:
SimpleExecutor:默认的执行器
BatchExecutor:批处理的执行器
ReuseExecutor:预处理,重用的执行器
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
1、创建拦截器类:
调试效果:
2、在全局配置文件中注册拦截器:
源码分析:
Plugin类:
3、测试;
2.2、pagehelper分页插件使用:
官网:https://pagehelper.github.io/
1、引入jar包:
2、注册拦截器:
参考官网用法
3、使用:
通用Mapper简介:
通用Mapper都可以极大的方便开发人员。可以随意的按照自己的需要选择通用方法,还可以很方便的开发自己的通用方法。
极其方便的使用MyBatis单表的增删改查。
支持单表操作,不支持通用的多表联合查询。
官方文档参考
1、引入依赖:
2、定义实体类型:
泛型实体类的要求:
示例:
3、创建Dao接口:
4、测试:
部分接口说明:
Select
接口:SelectMapper
方法:List
select(T record);
说明:根据实体中的属性值进行查询,查询条件使用等号
接口:SelectByPrimaryKeyMapper
方法:T selectByPrimaryKey(Object key);
说明:根据主键字段进行查询,方法参数必须包含完整的主键属性,查询条件使用等号
Insert
接口:InsertMapper
方法:int insert(T record);
说明:保存一个实体,null的属性也会保存,不会使用数据库默认值
接口:InsertSelectiveMapper
方法:int insertSelective(T record);
说明:保存一个实体,null的属性不会保存,会使用数据库默认值
Update
接口:UpdateByPrimaryKeyMapper
方法:int updateByPrimaryKey(T record);
说明:根据主键更新实体全部字段,null值会被更新
接口:UpdateByPrimaryKeySelectiveMapper
方法:int updateByPrimaryKeySelective(T record);
说明:根据主键更新属性不为null的值
Example 方法
接口:SelectByExampleMapper
方法:List
selectByExample(Object example);
说明:根据Example条件进行查询
重点:这个查询支持通过Example类指定查询列,通过selectProperties方法指定查询
测试之前的准备:
示例1:根据主键进行查询:
没有设置@Id的结果:
设置@Id后的结果1:
示例2:录入测试:
示例3:query by criteria
查询命令如下:
通用Mapper也可以使用MBG自动生成;
1、Mybatis的执行流程分析
2、Mybatis的插件机制
3、通用Mapper使用
mybatis如何读取clob数据 详细过程
例子:
表结构
CREATE TABLE USERINFO(USERID VARCHAR2(5), USERNAME VARCHAR2(20), MEMO CLOB, constraint PK_USERINFO primary key(USERID));java代码:
public class OracleClobTypeHandlerCallback implements TypeHandlerCallback { public void setParameter(ParameterSetter setter, Object obj) throws SQLException { CLOB clob = CLOB.empty_lob(); clob.setString(1, (String)obj); setter.setClob(clob); } public Object getResult(ResultGetter getter) throws SQLException { CLOB clob = (CLOB) getter.getClob(); return (clob == null || clob.length() == 0 )? null :clob.getSubString((long)1, (int)clob.length()); } public Object valueOf(String param) { return null; }}sqlmap配置:
1、MyBatis介绍
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
2、CLOB
SQL CLOB 是内置类型,它将字符大对象 (Character Large Object) 存储为数据库表某一行中的一个列值。默认情况下,驱动程序使用 SQL locator(CLOB) 实现 Clob 对象,这意味着 CLOB 对象包含一个指向 SQL CLOB 数据的逻辑指针而不是数据本身。Clob 对象在它被创建的事务处理期间有效。
3、MyBatis对CLOB类型数据实现增删改查
oracle表结构
create table T_USERS ( ID NUMBER not null, NAME VARCHAR2(30), SEX VARCHAR2(3), BIRS DATE, MESSAGE CLOB ) create sequence SEQ_T_USERS_ID minvalue 1 maxvalue 99999999 start with 1 increment by 1 cache 20;配置mybatis配置文件UsersMapper.xml
t_users
id,name,sex,birs,message
where 1=1
and id = #{id}
and name like concat(concat('%', '${name}'), '%')
and sex like concat(concat('%', '${sex}'), '%')
and birs = #{birs}
and message = #{message}
select from t_users
Mapper类接口
package examples.mapper;import java.util.List;public interface UsersMapper
{ public List
queryBySelective(T t); public List
queryByList(T t); }类型转换工具类
package examples.service;import java.sql.CallableStatement;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import oracle.sql.CLOB;import org.apache.ibatis.type.JdbcType;import org.apache.ibatis.type.TypeHandler;public class OracleClobTypeHandler implements TypeHandler
oracle.jdbc.driver.OracleDriver
jdbc:oracle:thin:@127.0.0.1:1521:pms
pms
pms
<!-- -->
classpath:examples/mybatis/oracle/UsersMapper.xml
测试类
package examples.service;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.List;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import examples.bean.Users;import examples.mapper.UsersMapper;public class TestUsersService { @SuppressWarnings("unchecked") public static void main(String[] args) throws ParseException { ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:/examples/service/spring.xml"); UsersMapper
dao = (UsersMapper
)ac.getBean("dao"); //查询 Users nullBean = new Users(); List
list = dao.queryByList(nullBean); if(list != null) { for(Users user : list) { System.out.println(user); } } }}