Mybatis源码

回顾

Mybatis是做什么的

mybatis是一个orm类型框架, 解决数据库访问和操作的问题, 是对jdbc技术的封装. 那么什么是orm, orm全名为object relative mapping(对象关系映射),总的来说, orm是用来事项对象操作数据库中的表

Mybatis开发简单回顾

  • 准备jar包
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.48</version>
</dependency>
  • 准备配置文件
  1. mybatis-config.xml
  2. mapper.xml

一个配置文件中可有多个环境配置, 通过使用default指明使用的环境

<environments default="default">
    <environment id="default">
        <transactionManager type="JDBC"></transactionManager>
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/suns?useSSL=false"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </dataSource>
    </environment>
    <environment id="oracle">
        <transactionManager type="JDBC"></transactionManager>
        <dataSource type="POOLED">
            <property name="driver" value="oracle.jdbc.OracleDriver"/>
            <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </dataSource>
    </environment>
</enviroments>

添加typeAliases别名, 这样能在mapper文件中, 使用别名来简化类全路径书写 

 

 mapper文件的设置可以用resource资源路径或者package的包路径

<mappers>
    <mapper resource="UserDaoMapper.xml"/>
    <package name="com.xx.UserDao.class"/>
</mappers>

 如果想要更换数据源, 使用databaseId标签更换

<mapper namespace="xxx">
    <insert id ="save" prameterType="User" databaseId="oracle">
        insert into t_user (name) values (#{name})
    </insert>
</mapper>

多数据源事务

其实看见上面的两个数据源的时候, 使用两个数据源,连接不同, 如何管理事务呢

class XXXService{
    ADAO.m1();//default
    BDAO.m2();//oracle
}

SqlSession的用法

ibatis时期 

public void static main(){
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactoryBuilder().build(is);
    List<User> users = sqlSession.selectList("com.dao.UserDAO.queryAllUsers");
}

通过使用namespace+id得出唯一MappedStatement去执行

mybatis时期

UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
List<User> users = userDAO.queryAllUsers();

这两种方式是等价的,那么使用哪种方式更好?

使用封装过的较好一些, 封装它可以较好的体现出类型信息,  更符合面向对象的含义, 表达的意思更为清楚.

二者之间的关系

第二种其实是第一种的封装, 通过代理模式对第一种进行封装

mybatis的核心对象

Mybatis源码核心对象分析

Mybatis都包括什么, 前面说, mybatis是对jdbc的一种封装 , 但从之前的代码不难看出jdbc的操作基本上都是由SqlSession做的

mybatis有两个核心

数据存储对象

为了减少io次数, mybatis的配置信息并不是使用的时候读取一次,而是将读取到的信息保存起来,包括mybatis-config.xml,xxxDaoMapper.xml,分别用Configuration和MappedStatement包装起来

那如何验证呢? 通过下面两张图片, 可以比对

再举个例子

Configuration中有一些属性, resultMaps, parameterMaps等, resultMap,parameterMap这些属性他在xml中存在 ,为什么会在mybatis-config中, 但是我这个map是加了复数的集合, 这些map是对所有mapper文件的map做集合统计操作,那如何区分不同的mapper文件的map集合?

map使用 namespace+id作为map的key值, 保证唯一

Map<String,ResultMap> resultMaps = new StrictMap<ResultMap>();
Environment environmentenvironment标签
TypeAliasRegistry typeAliasRegistrytypeAliases标签
Set<String> loadedResourcesmapper标签

Configuration可以说是mybatis的核心, 无论是mybatis-conf.xml还是mapper.xml, 在他的类中都有体现, 其中, mapper.xml就体现在mappedStatement.

 为了使用方便,Configuration用resultMaps,parmaeterMaps.和caches等对mappedStatements做了汇总

总结起来就是, Configuration

  1. 封装了mybatis-config.xml
  2. 封装了mapper文件 
  3. 创建了Mybatis其他相关的对象

那MappedStatement和mapper文件有什么关系 ? MappedStatement并不是一个mapper文件一个MappedStatement的关系, 而是一个语句一个Statement

也就是说, 一个mybatis应用中, 会有n个MappedStatement对象

mybatis中的Statement有多个实现类, PreparedStatement,CallableStatement,mybatis中的Statement默认使用PreparedStatement.普通的Statement不具有预编译功能, Callable使用的是存储过程, 基本上不会使用,显然使用的是PreparedStatement.

如果想要更改Statement的实现, 使用statementType标签可以更改Statement的实现

我们之前有提到过Configuration中有MappedStatements属性配置, 事实上, MappedStatement中也包含了Configuration属性,也就是说,他们之间可以相互找到彼此,相互引用

任何一个mybatis应用都有一个Configuration和n个MappedStatment

BoundSql

在java中一切皆对象, mapper的sql语句结合sql参数以及其他属性也是封装为了一个对象(BoundSql),MappedStatment中封装了SQL语句 ==>BoundSql

 

Executor

我们在数据库操作中,除了实体存数据, 还需要操作类型的对象, 使用操作对象和存储对象结合起来才能完成数据库操作,这些操作类对象也是由Configuration进行创建的.

  1. Excutor
  2. StatementHandler
  3. ParameterHandler
  4. ResultSetHandler
  5. TypeHandler
  6. ...

executor是mybatis中执行功能的核心, Executor对增删改和查分别提供了相应的方法, 在executor中, update代表了对数据库的改动(包括增删改),query则是查, 查看源码得知Executor是一个接口,但是为什么这种类型要设计为一个接口, 而数据类型不用设计, mybatis为了日后更好扩展, 将一切操作类型对象都设计为一个接口, 也能够对外屏蔽内部实现.

Excutor接口实现: 

  1. BatchExcutor jdbc中批处理操作,什么是批处理,为什么需要批处理? 批处理字面意思就是一次性处理多个操作, 在java程序和DB连接的时候, 他们之间的通信是需要网络连接的, 而网络连接会特别影响资源, 如果每处理一个sql建立一个连接, 新能影响就非常大, 为了缓解性能问题, 在一次连接的基础上.一次处理多个sql请求可以很好的节约资源开销问题.
  2. ReuseExcutor 复用 statement,什么是复用statement, 在sql语句相同的情况下, 使用同一个statement一定是达到相同效果的 , 为了减少对象创建的对性能的影响, 可以对statement重复利用
  3. SimpleExcutor 最常用的executor

默认情况下, Executor的类型是simpleExecutor 

StatementHandler

既然executor是通过jdbc对数据库操作, 那executor是如何和jdbc建立练习的呢,实际上executor是通过statementHandler和jdbc建立联系的

sqlSession的statementHandler才是真正和jdbc建立练习的对象,也可以看作statementHandler通过和Statement建立联系

之前说Executor是完成操作的, 现在又写真正完成数据库操作的是StatmentHandler. 那我为什么不能直接用StatementHandler对数据库操作,而是又包了一层Executor. 在mybatis中推行责任单一原则, Executor中有三个主要功能: 

  1. 增删改 查
  2. 事务操作 提交 回滚
  3. 缓存相关的操作

StatementHandler将Executor中最为复杂的增删改查进行操作, Executor只负责调用即可,Statement只做sql语句操作

StatementHandler的实现类: 

  1. SimpleStatementHandler
  2. PreparedStatementHandler
  3. CallableStatementHandler

ParameterHandler

ParameterHandler用于将mybatis的参数转换为jdbc相关参数, @Param ==> #{} ==> ?

public interface ParameterMapper{
    User check(@Param("username")String username,@Param("password")String password);
}
<select id="check" resultType="com.atguigu.mybatis.pojo.User">
   select * from t_user where username=#{username}  and password= #{password};
</select> 

ResultSetHandler

ResultSetHandler封装了jdbc的ResultSet值, 在jdbc中查询的结果集用ResultHandler封装, 在mybatis中使用ResultSetHandler封装

既然ResultSetHandler是对ResultSet结果集的封装,为什么参数是Statement而不是ResultSet,在jdbc的操作过程中, 需要有一个statement才能获取一个ResultSet

TypeHandler

类型处理, 使用java程序操作数据库, 会出现两种类型, 他们存在类型映射转换的问题,TypeHandler的作用也就体现在ParameterHandler和ResultSetHandler的数据类型转换之间 

SqlSession, Executor ,StatementHandler, JDBC

如何证明他们之间的联系呢?

mybatis源码中这些核心对象 ,如何看见他们之间的联系呢

SqlSession调用Executor

Executor调用StatementHandler 

总结起来就是

SqlSession.insert() ==> DefaultSqlSession ==> Executor ==> StatementHandler

那这两个是如何建立联系的 , 代理, 代理有两种选择, 一种是基于接口重写实现类, 另一种是基于父类写子类. 那他是哪种代理, UserDAO是一个类接口, 那一定是第一种方式重写实现类

public interface UserDAO{
    
}

通过代理类来调用SqlSession的方法

Proxy.newProxyIntance(ClassLoader,Interfaces,InvocationHandler);

其中, InvocationHandler对应着要增强的功能,在sqlSession中的Mapper使用方法需要将sqlSession作为参数传入才能调用SqlSession的方法, 因为SqlSession调用方法需要传入statement, 再将接口信息作为第二个参数,使用反射机制将statement填充

//这个MapperProxy是自定义的class
public class MyMapperProxy implements InvocationHandler{
    private SqlSession sqlSession;
    private Class daoClass;
    public MyMapperProxy(SqlSession sqlSession,Class daoClass){
        this.sqlSession = sqlSession;
        this.daoClass = daoClass;
    }
    @Override
    public Object invoke(Object proxy,Method method,Object[]args){
         return sqlSession.selectList(daoClass.getName() + "." + method.getName());
    }
}
UserDAO userDAO = (UserDAO )Proxy.newProxyInstance(TestMybatis.class.getClassLoader,new Class[]{UserDAO.class},new MyMapperProxy(sqlSession,UserDAO.class));

再SqlSession中,强制提供了UserDAO.class的类以及自身的sqlSession 

MapperProxyFactory

mybatis通过将Proxy代理的操作封装在MapperProxyFactory中, 将代理的具体实现过程封装起来,用MapperProxyFactory获得具体Mapper

在一个对象中, 当匹配到UserDAO自己的方法的时候, 需要代理实现方法, 但是所有类的父类都是object, 有些方法是不需要被代理的, 比如toString,equals等, 这些方法就按照原来那个处理逻辑即可, 不需要被代理,通过if-else对方法进行剔除chachedMappedMethod获取MapperMethod,  为什么会有MapperMethod, 原来的method不行吗

我们先来看一下MapperMethod类,这个类有两个成员变量, 一是SqlCommand,二是MethodSignature

 

在注解中加入@Param ,使用@Param作为sql语句的参数

Mybatis完成代理总结

MapperProxy implements InvocationHandler

  • invoke
    • SqlSession.insert
    • update
    • delete
    • selectOne
    • selectList 
  • SqlCommand
    • id
    • type

代理 装饰器

什么时候使用代理. 在为原始目标对象增加额外功能,什么是额外功能, 额外功能是和本功能不相关的, 如果和相关功能有联系的, 就不能使用代理而是装饰器

RPC

什么是rpc ,rpc就是一种远程调用的一种技术,假设我有两个虚拟机, 在第一个jvm的login方法调用第二个jvm的login方法 

解析xml

通过getResource解析mybatis-config.xml以及xxxmapper.xml,表面上, 只是读取了mybatis-config.xml,实际上还有mappers注册的mapper文件,一次io读取多个文件

InputStream inputStream = Resources.getResource("mybatis-config.xml");
<mappers>
    <mapper resource="UserDAOMapper.xml"/>
    <mapper resource="AccountDAOMapper.xml"/>
</mappers>

OXM

Object XML Mapping 对象xml文件映射,一切皆对象,

mybatis-config.xml ---- Configuration对象

程序是如何将xml封装为对象? XML解析

xml解析有几种解析方式呢 ? DOM SAX XPath, mybatis采用的是XPath,通过XPathParser,将mybatis-config.xml封装为一个个XNode,一个标签就是一个XNode, XNode里面也可以有子XNode

//inputStream ===> xxx.xml
XPathParser xpathParser = new XPathParser(inputStream);
  • name对应<xx>里的xx
  • body对应<xx>mmm</xx> 的mmm
  • attributes对应<xx a="x"/>的a

 

<users>
    <user>
        <name>xiaoli</name>
        <password>123</password>
    </user>
    <user>
        <name>xiaohong</name>
        <password>123</password>
    </user>
</users>

使用XPatherParser

evelaNodes是用来解析根标签

这些evalNode都是对 标签的解析

 这里是对mapper文件的操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值