文章内容输出来源:拉勾教育Java就业训练营;
前几天(大概是双十一那几天),班主任karry老师给我们每个人发了三张学习卡,这张学习卡可以用来体验其他训练营,我就尝试着报了一个《JAVA工程师高薪训练营》的一个星期的体验班。
正好前几天,我在就业训练营的学习进入到了第六阶段SSM框架的学习,学习到了MyBatis这个模块。Java高薪训练营的第一阶段模块一也是在讲解这一部分的知识,不过这一模块相比与自己的训练营那里讲的更深入一些,这里多了很多源码剖析的内容,开始听一遍根本没有听懂,之后,又重新的听了一遍,对于MyBatis的源码与组成又有了更深一步认识。
这篇文章中就先来写一些关于Mybatis的基础知识的回顾
ORM
ORM全称Object/Relation Mapping:表示对象-关系映射的缩写
ORM完成面向对象的编程语言到关系数据库的映射。当ORM框架完成映射后,程序员既可以利用面向 对象程序设计语言的简单易用性,又可以利用关系数据库的技术优势。ORM把关系数据库包装成面向对 象的模型。ORM框架是面向对象设计语言与关系数据库发展不同步时的中间解决方案。采用ORM框架 后,应用程序不再直接访问底层数据库,而是以面向对象的放松来操作持久化对象,而ORM框架则将这 些面向对象的操作转换成底层SQL操作。ORM框架实现的效果:把对持久化对象的保存、修改、删除 等 操作,转换为对数据库的操作
Mybatis简介
MyBatis是一款优秀的基于ORM的半自动轻量级持久层框架,它支持定制化SQL、存储过程以及高级映 射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的 XML或注解来配置和映射原生类型、接口和Java的POJO (Plain Old Java Objects,普通老式Java对 象) 为数据库中的记录。
mybatis 主页面MyBatis优势
Mybatis是一个半自动化的持久层框架,对开发人员开说,核心sql还是需要自己进行优化,sql和java编 码进行分离,功能边界清晰,一个专注业务,一个专注数据。
1.首先搭建环境,使用maven在Pom.xml导入我们所需要的MyBtais的坐标和其他相关坐标。
2.在我们的数据库中创建如下的user表格
user对应的sql语句为:
DROP
3.在src目录下对应创建我们需要的User实体类
public
4.接下来在资源(resource)目录下,创建我们所需要的UserMapper 映射文件
5.编写MyBatis 核心文件
//加载核心配置文件
配置成功啦!使用MyBatis 来进行我们的第一个CRUD操作吧
1.先来编写UserMapper的映射文件,在文件中插入
编写插入实体的User 的代码
InputStream
•Sql语句中使用#{实体属性名}方式引用实体中的属性值
修改数据
编写修改实体 User 的代码
InputStream
MyBatis 的删除数据操作
编写 UserMapper 映射文件
编写删除数据的代码
InputStream
•Sql语句中使用#{任意字符串}方式引用传递的单个参数
到这里,使用MyBatis 完成的第一个增删改查操作就完成了,MyBatis 已经入门了,下面我们来了解一下我们编写的配置文件。
MyBatis 的核心配置文件解析
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/springbootdata?serverTimezone=UTC
jdbc.username=root
jdbc.password=libaice
其中,事务管理器(transactionManager)类型有两种:
•JDBC:这个配置就是直接使用了JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作 用域。
•MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生 命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因 此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。
其中,数据源(dataSource)类型有三种:
•UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
•POOLED:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来。
•JNDI:这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置 数据源,然后放置一个 JNDI 上下文的引用。
2)mapper标签
该标签的作用是加载映射的,加载方式有如下几种:
通常,我们习惯用一个包名的方式来映射
Mybatis相应API介绍
SqlSession工厂构建器SqlSessionFactoryBuilder
常用API:SqlSessionFactory build(InputStream inputStream)
通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象
String
SqlSession工厂对象SqlSessionFactory
SqlSessionFactory 有多个个方法创建SqlSession 实例。常用的有如下两个:
openSession()
SqlSession 会话对象
SqlSession 实例在 MyBatis 中是非常强大的一个类。在这里你会看到所有执行语句、提交或回滚事务和
获取映射器实例的方法。
执行语句的方法主要有
<
MyBatis的DAO层实现
UserDao
public
测试传统方法
@Test
采用 Mybatis 的代理开发方式实现 DAO 层的开发,这种方式是我们后面进入企业的主流。
Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis 框架根据接口 定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper 接口开发需要遵循以下规范:
1) Mapper.xml文件中的namespace与mapper接口的全限定名相同
2) Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
3) Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
4) Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
编写UserMapper接口
public
在mapper 中配置其相关
测试代理
@Test
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户
现在重建我们的数据库,相关的sql语句为:
DROP
一对一的查询语句
select
接下来,像User 一样,创建他们各自对应的实体类 Order 和 User
public
创建OrderMapper接口
findAll();
}
配置OrderMapper.xml
其中还可以配置如下:
5.1.6 测试结果
OrderMapper
在最后的结果中可以看到:
一个订单表之后可以查询到其相对应的一个用户信息: 查询成功
查询语句
select
修改User实体
public
创建UserMapper接口
public
配置UserMapper.xml
测试结果
UserMapper
可以看到, 一个用户下面对于着多个订单号,表示我们的一对多操作查询成功
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
多对多查询的需求:查询用户同时查询出该用户的所有角色
查询语句
select
现在,我们需要创建一个新的Role实体类,来保存两个多表的中间表
修改我们的User 表
public
创建一个新的 Role
public
添加UserMapper接口方法
List
配置UserMapper.xml
测试结果
UserMapper
一个用户对应多个id,多个角色,查询成功
MyBatis的常用注解
这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper
映射文件了。我们先围绕一些基本的CRUD来学习,再学习复杂映射多表操作。
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
使用注解来完成基本的CRUD 操作
private
接下来,修改配置文件,我们使用了注解替代的映射文件,所以我们只需要加载使用了注解的mapper接口即可
直接扫描对应的包
实现复杂关系映射之前我们可以在映射文件中通过配置来实现,使用注解开发后,我们可以使用
@Results注解,@Result注解,@One注解,@Many注解组合完成复杂关系的配置
对于的sql 语句
select
创建Order 和 User 实体类
public
创建OrderMapper接口
findAll();
}
使用注解配置Mapper
public
测试结果
public
查询到一个订单表后对于的用户; 这样的使用方法生效了。
select
修改User实体
public
创建UserMapper接口
List
使用注解配置Mapper
public
测试结果
List
到这里,使用注解的方式来完成一对多的查询就已经成功啦
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
多对多查询的需求:查询用户同时查询出该用户的所有角色
对应的sql语句
select
创建Role实体,修改User实体
public
添加UserMapper接口方法
List
使用注解配置Mapper
findAllUserAndRole();}
public interface RoleMapper {
@Select("select * from role r,user_role ur where r.id=ur.role_id and
ur.user_id=#{uid}")
List
UserMapper
到这里,基于多对多的注解查询我们就完成啦!!
最后,让我们来看看MyBatis 与缓存相关的知识
①、在一个sqlSession中,对User表根据id进行两次查询,查看他们发出sql语句的情况
@Test
public void test1(){
//根据 sqlSessionFactory 产生 session
SqlSession sqlSession = sessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//第一次查询,发出sql语句,并将查询出来的结果放进缓存中
User u1 = userMapper.selectUserByUserId( 1 );
System.out.println(u1);
//第二次查询,由于是同一个sqlSession,会在缓存中查询结果
//如果有,则直接从缓存中取出来,不和数据库进行交互
User u2 = userMapper.selectUserByUserId( 1 );
System.out.println(u2);
sqlSession.close();
}
同样是对user表进行两次查询,只不过两次查询之间进行了一次update操作。
@Test
现在再来看一看控制台的打印结果
1、第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从 数据
库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。
2、 如果中间sqlSession去执行commit操作(执行插入、更新、删除),则会清空SqlSession中的 一级
缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
3、 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直 接从
缓存中获取用户信息
现一级缓存更多是用于查询操作,一级缓存也叫做查询缓存。
接下来看一下这个创建的一级缓存去哪里了我们跟踪query方法如下:
Override
如果查不到的话,就从数据库查,在queryFromDatabase中,会对localcache进行写入。 localcache对
象的put方法最终交给Map进行存放
private
二级缓存的原理和一级缓存原理一样,第一次查询,会将数据放入缓存中,然后第二次查询则会直接去
缓存中取。但是一级缓存是基于sqlSession的,而二级缓存是基于mapper文件的namespace的,也 就
是说多个sqlSession可以共享一个mapper中的二级缓存区域,并且如果两个mapper的namespace 相
同,即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域 中
开启二级缓存
和一级缓存默认开启不一样,二级缓存需要我们手动开启
首先在全局配置文件sqlMapConfig.xml文件中加入如下代码:
其次在UserMapper.xml文件中开启缓存
我们可以看到mapper.xml文件中就这么一个空标签,其实这里可以配置,PerpetualCache这个类是 mybatis默认实现缓存功能的类。我们不写type就使用mybatis默认的缓存,也可以去实现Cache接口 来 自定义缓存。
public
我们可以看到二级缓存底层还是HashMap结构
public
测试
测试二级缓存和sqlSession无关
@Test
可以看出上面两个不同的sqlSession,第一个关闭了,第二次查询依然不发出sql查询语句
测试执行commit()操作,二级缓存数据清空
@Test
useCache 和 flushCache
mybatis中还可以配置userCache和flushCache等配置项,userCache是用来设置是否禁用二级缓 存
的,在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出 sql
去查询,默认情况是true,即该sql使用二级缓存
这种情况是针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存,直接从数 据 库中获取。
在mapper的同一个namespace中,如果有其它insert、update, delete操作数据后需要刷新缓 存,如 果不执行刷新缓存会出现脏读。
设置statement配置中的flushCache="true”属性,默认情况下为true,即刷新缓存,如果改成false则 不 会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。
一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏 读。所以我们不用设置,默认即可。
现在的自己会感谢那时候的自己,当时的选择是正确的。
当自己研究学习的时候,总是觉得自己的学到的东西很乱,没有系统化,看到新的东西,不了解的东西都喜欢研究一下,自己却没有耐心认真的研究下去,做的深入。
经过这四个月的学习,不仅自己的知识开始变得系统化起来,自己的时间也开始变得有序起来,每天学习,每天接受新的知识,每天学习仿佛成了吃饭,睡觉一样。
持续不断的练习,自己的编码能力也从开始只会写个"Hello,world",现在初步使用框架做出一点小东西出来,”learning by doing, doing by learning “,仍然是我认为最高效的学习方法,不断的做出点真实的东西,不断的突破自己的舒适区,才能获得更快,更高效的成长。
最后希望拉勾教育做的越来越好,来这里提高自己技术能力的小伙伴也越来越多!
谢谢你,拉勾教育。