回顾
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>
- 准备配置文件
- mybatis-config.xml
- 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 environment | environment标签 |
TypeAliasRegistry typeAliasRegistry | typeAliases标签 |
Set<String> loadedResources | mapper标签 |
Configuration可以说是mybatis的核心, 无论是mybatis-conf.xml还是mapper.xml, 在他的类中都有体现, 其中, mapper.xml就体现在mappedStatement.
为了使用方便,Configuration用resultMaps,parmaeterMaps.和caches等对mappedStatements做了汇总
总结起来就是, Configuration
- 封装了mybatis-config.xml
- 封装了mapper文件
- 创建了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进行创建的.
- Excutor
- StatementHandler
- ParameterHandler
- ResultSetHandler
- TypeHandler
- ...
executor是mybatis中执行功能的核心, Executor对增删改和查分别提供了相应的方法, 在executor中, update代表了对数据库的改动(包括增删改),query则是查, 查看源码得知Executor是一个接口,但是为什么这种类型要设计为一个接口, 而数据类型不用设计, mybatis为了日后更好扩展, 将一切操作类型对象都设计为一个接口, 也能够对外屏蔽内部实现.
Excutor接口实现:
- BatchExcutor jdbc中批处理操作,什么是批处理,为什么需要批处理? 批处理字面意思就是一次性处理多个操作, 在java程序和DB连接的时候, 他们之间的通信是需要网络连接的, 而网络连接会特别影响资源, 如果每处理一个sql建立一个连接, 新能影响就非常大, 为了缓解性能问题, 在一次连接的基础上.一次处理多个sql请求可以很好的节约资源开销问题.
- ReuseExcutor 复用 statement,什么是复用statement, 在sql语句相同的情况下, 使用同一个statement一定是达到相同效果的 , 为了减少对象创建的对性能的影响, 可以对statement重复利用
- SimpleExcutor 最常用的executor
默认情况下, Executor的类型是simpleExecutor
StatementHandler
既然executor是通过jdbc对数据库操作, 那executor是如何和jdbc建立练习的呢,实际上executor是通过statementHandler和jdbc建立联系的
sqlSession的statementHandler才是真正和jdbc建立练习的对象,也可以看作statementHandler通过和Statement建立联系
之前说Executor是完成操作的, 现在又写真正完成数据库操作的是StatmentHandler. 那我为什么不能直接用StatementHandler对数据库操作,而是又包了一层Executor. 在mybatis中推行责任单一原则, Executor中有三个主要功能:
- 增删改 查
- 事务操作 提交 回滚
- 缓存相关的操作
StatementHandler将Executor中最为复杂的增删改查进行操作, Executor只负责调用即可,Statement只做sql语句操作
StatementHandler的实现类:
- SimpleStatementHandler
- PreparedStatementHandler
- 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文件的操作