最近给自己报了个java的训练营,开始一点点学习常见的各种技术知识,之前自己学的稀烂,有的时候面试别人时想到的问题,都不知道人家回答的对不对。抱着整理自己的知识的念头,也会
行吧,不装了,我摊牌了,我就是不会呀赶紧过来学的。
Mybatis
持久层相关概念
ORM
一切还要从ORM开始说起,很久之前呢,大家都是通过JDBC来操作数据库的,但是大家写来写去发现关于数据库的操作代码,绝大多数做的都是同样的事情,有区别的也就是写的sql不太一样了。那么经历过一段时间的各个公司自己实现的持久成框架之后,提出了ORM的概念。
ORM全称Object Relation Mapping:对象关系映射。是在面向对象的概念之下才有的一个概念,简单来说就是把对一个表的操作转换为对一个对象的操作,那总得有个地方把表转换成对象吧,一对多,多对一的关系得有个东西来生成面向对象里面的类吧,那这个就是ORM了。
这里得说一点哈,ORM不只是JAVA语言特有的,像Hibernate,Mybatis这种,这个概念原则上并没有指定语言,.Net的EntityFramework也是ORM的一种。
在我当年还在用hibernate的年代,经常听到一种说法是Mybatis是一个半ORM框架,那这种说法该怎么理解呢?这种可以算是一个发展的角度来看的说法,在SSH的年代,Mybatis刚刚起步大家对他不是很了解,做实体和表映射的时候,还要自己手写ResultMap的配置,而hibernate当时已经比较成熟通过简单的配置即可自动做映射。随着Mybatis后面自动映射、Generator什么的功能上的增加,基本上现在很少再能见到半自动的说法了。
还有一个猜想,不一定对,Mybatis并不熟悉,hibernate大家更熟悉,向领导写选型PPT的时候,我这一边是半自动的新技术,一边是全自动的,选哪个一看便知道了呀~
JPA
既然提到了ORM,那顺便也聊聊JPA吧,毕竟经常会在某些资料中见到这个概念,JPA是Java Persistence API(或Jakarta Persistence),是之前J2EE规范中的一部分,相当于是一个JAVA的ORM的规范,并且在ORM的基础上,加入了对实体的操作规范,以及事务等额外功能的规范,甚至还规定了java版的SQL语句JPQL(Java Persistence Query Language)不过没这玩意确实没怎么见过。
JPA本身是一种规范,相当于是接口,各家持久层框架去实现这个规范就相当于写了JPA接口的具体实现类。经常看到一些资料中说jpa和Mybatis两者作比较,这种说法呢,可以认为是错误,也可以认为是一种简写,这里指代是Spring-Data-JPA,一开始只有Spring-Data,后来它实现了JPA规范,才有更多的Spring-Data-JPA和Mybatis的对比文章。
也可以这么说Mybatis,一开始并不是JPA框架,只有出现实现了JPA规范的Mybatis版本开始,才可以说Mybatis是JPA框架。
这两种框架不能说哪个更好,各有千秋,spring-data-jpa更适合严格规范的数据库设计并且很少有级联查询的业务,很适合快速开发单表操作的服务。而mybatis拓展性更好,更适合自己写sql,调优sql的团队,接入通用mapper或者mybatis-plus之后呢,其实单表的操作也不用自己写sql了
整体分析
理解架构
用一句话概括Mybatis想要做的事情,那就是:程序运行时将接口的调用转换为读取对应的xml文件或注解中的sql语句,执行查询并返回结果。
那么为了实现这句话,Mybatis抽象了几部分:
-
接口层:提供给外部使⽤的接⼝ API,开发⼈员通过这些本地API来操纵数据库。接⼝层⼀接收
到调⽤请求就会调⽤数据处理层来完成具体的数据处理。 -
数据处理层:负责具体的SQL查找、SQL解析、SQL执⾏和执⾏结果映射处理等。它主要的⽬的是根据调⽤的请求完成⼀次数据库操作。
-
架构支撑层:负责最基础的功能⽀撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共⽤的东⻄,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的⽀撑。
初始化过程
为了实现上面一句话的效果,Mybatis在项目启动的时候,也是做了一些准备工作:
初始化一个全局的配置,拿到所有的SQL语句
虽然说起来简单,但是为了做这个准备工作Mybatis执行了以下的流程:
启动之后呢,再进行查询就是需要DefaultSQLSessionFactory去创建SqlSession,SQLSession可以直接用拼接好的StatementId来查询,但是一般咱们都用Mapper的方式去执行自己的sql语句,那咱们也来看看这两个过程
openSession()
DefaultSqlSessionFactory通过openSession()方法创建出一个SqlSession来,过程比较简单,就直接看源码吧。
其中比较重要的是创建了一个事务,其他的是获取环境配置、执行查询的执行器之类的然后就可以直接使用了。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
getMapper()
这种方式用的多一些,众所周知这里面有一个代理的过程,通过一层层的调用,最终在MapperProxyFactory中使用动态代理创建的对象
查询过程
执行查询的过程,也可以用简单的几句话来说明:
-
首先,通过SQLSession或者mapper调用的方法知道要查的是哪条SQL,传递给Executor去执行查询。
-
之后执行查询时,把MapperStatement交给StatementHandler去处理,处理的过程中包含了通过ParameterHandler去解析源Sql中的占位符转换为BoundSql,生成一个JDBC的Statement做真正的查询。
-
查询的结果由ResultSetHandler处理,并返回想要的结果。
以上大概就是Mybatis为了减少对JDBC的重复操作所做的事情
主要功能
上面已经在每个分流程中介绍了各个组件的功能,这里在汇总下
组件 | 功能 |
---|---|
SqlSession | 作为MyBatis工作的主要顶层API,表示和数据库交互的会话 |
Executor | MyBatis执行器,负责SQL语句的生成和查询缓存的维护 |
StatementHandler | 封装了JDBC Statement操作,负责对JDBC statement的操作,如设置参数、将Statement结果集转换成List集合 |
ParameterHandler | 负责对用户传递的参数转换成JDBC Statement所需要的参数 |
ResultSetHandler | 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合 |
TypeHandler | 负责java数据类型和jdbc数据类型之间的映射和转换 |
MappedStatement | 维护了select update delete insert节点的封装 |
SqlSource | 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回 |
BoundSql | 表示动态生成的SQL语句以及相应的参数信息 |
最后,说说训练营的感觉,我个人是不喜欢这种每周学习多少小时的课程形式的,大家都会有忙或者闲的时候,尤其这个课程的量还比较多,要一部分一部分的解锁,再加上交作业,估计忙的时候基本就完全落下了。
而进度的落后就意味着一些其他惩罚机制的到来,结果就导致更加反感或进度的积压,恶性循环导致放弃学习了。
但是自己去学习视频之类的,又管控不好自己,第一次参与这种,还是尽量按住自己的感受,去完整的体会这种形式,说不定也会有别的体会。