一生猿,一世猿。 —— 猿医生·yys
一、前言
二、原生 mybatis
三、核心对象介绍
四、核心配置介绍
你还是只会用 Mybatis,并不知其原理么?
相信很多开发小伙伴们,大都停留在会使用mybatis的基础上,至于原理及内部实现,并没有过多关注。
注意,这里我说的会使用,也仅仅是被我们伟大的 spring集成之后的 mybatis,而对于原生态的 mybatis而言,相信更多的小伙伴的表情此时已渐渐凝固 ~
信不信由你,原谅配图时的我不厚道的被这个老外哥逗笑了 ~ (有同感的,点个赞吧 ~)
话题转移成功,言归正传,最近呢 ,笔者也正在研究 mybatis源码,总结了一些学习心得以及重点知识。
本篇重点讲解 mybatis的核心对象以及核心配置,后续博文将持续更新 mybatis源码剖析,缓存机制,执行流程....
在此,记录一下,分享给大家。
文章最后有彩蛋哦,不看会后悔哈 ~
// 1.加载注册JDB驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.创建数据库连接,连接参数:地址/用户名/密码
conn = DriverManager.getConnection(URL, USER, PASS);
// 3.执行查询
String sql = " SELECT * FROM yys_user ";
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
// 4.遍历结果集
while (rs.next()) {
// 参数获取
Long uid = rs.getLong("uid");
String name = rs.getString("name");
Integer age = rs.getInt("age");
// 对象封装
user.setUid(uid);
user.setName(name);
user.setAge(age);
}
System.out.println(user);
// 5.释放资源 - 自下而上的关闭
初入职场A猿,略带疑惑的目光,轻言道:
这?....擦了擦双眼,又仔细看了看,这...是原生JDBC呐,这怎么能难倒我,工具类都是我抽取的 ~
有经验的开发B猿,摸了摸略带胡渣的下巴,沉吟道:
不错..不错,笔者思维缜密,逻辑严谨,先从原生JDBC入手,即视的代入感,不慌不忙的点了个赞 ~
沉默的笔者:
言归正传,其实对于市面上已经非常成熟的ORM框架或者工具类包,如 Apache的 DBUtils,Spring的 JdbcTemplate,Hibernate, Mybatis,其实它们底层实现都是对 JDBC的封装,我们去查看源码,最后一定会看到上面代码中的核心对象。
而现在大多数公司,大都再使用 Mybatis来作为持久层框架,笔者也是 mybatis爱好者之一,至于原因么,很简单... 就是,公司用的是mybatis, 没办法 ~
一般玩笑过后,不是总结,就是总结 ~ 我们来总结下 mybatis的一些特性及优点:
1、 使用连接池对连接进行管理
2、 SQL 和代码分离,集中管理
3、 结果集映射
4、 参数映射和动态 SQL
5、 重复 SQL 的提取
6、 缓存管理
7、 插件机制
接下来,回到此篇文章的重点,原生Mybatis闪亮登场 ~
1.先引入 mybatis jar包
2.创建 mybatis核心配置文件,定义了对 mybatis核心行为的控制, mybatis-config.xml
3.定义 mapper文件,一般一张表对应一个mapper.xml,其中配置 curd的 sql,以及参数和返回结果的映射关系
准备工作完成,终于到本章重点,关门...上...案例 ~
方式一:通过 SqlSession调用API方式
// 读取 mybatis配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 通过 SqlSessionFactoryBuilder 创建 SqlSessionFactory工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 创建 SqlSession连接
SqlSession session = sqlSessionFactory.openSession();
try {
// 通过 SqlSession调用API方式,执行查询
User user = (User) session.selectOne("com.yys.mapper.UserMapper.selectById", 1);
System.out.println(user);
} finally {
// 释放资源
session.close();
}
缺点:硬编码,编译时不能进行类型检查,小众不推荐 ~
方式二:Mapper接口方式
// 读取 mybatis配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 通过 SqlSessionFactoryBuilder 创建 SqlSessionFactory工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 创建 SqlSession连接
SqlSession session = sqlSessionFactory.openSession();
try {
// 通过 Mapper接口方式,执行查询
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectUserById(1);
System.out.println(user);
} finally {
// 释放资源
session.close();
}
看到这里,建议大家,带着四个对象(sqlSessionFactoryBuiler、sqlSessionFactory、sqlSession、mapper)再看一遍案例代码
思考下,这四个对象分别的生命周期以及它们之间关系 ~
重点介绍核心对象的 1、生命周期 2、作用
(1)、SqlSessionFactoryBuiler
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
显而易见,SqlSessionFactoryBuiler是用来创建 SqlSessionFactory对象的,而在创建SqlSessionFactory工厂对象完成后,它的使命也就完成了,所以它的生命周期只存在于方法的局部。
(2)、SqlSessionFactory
SqlSession session = sqlSessionFactory.openSession();
可以看出,SqlSessionFactory工厂对象是用来创建 SqlSession对象的,再说说其生命周期,每次应用程序访问数据库,都需要创建一个会话,所以SqlSessionFactory工厂对象应该存在于应用的的整个生命周期中,而且一次访问数据库中,一个SqlSession实例来做这件事就可以,这里采用单例模式。
(3)、SqlSession
可以看出,SqlSession是一个会话,非线程安全的,故不能再线程中共享,所以在方法开始或请求开始时创建一个 SqlSession对象,在方法结束或请求结束时及时关闭它。
UserMapper mapper = session.getMapper(UserMapper.class);
从SqlSession对象中获取的 Mapper对象实际上是一个代理对象,它的作用是发送 sql来操作数据库的,它的生命周期是在一个SqlSession事务方法内。
总结:
核心对象 |
生命周期 |
SqlSessionFactoryBuiler |
方法局部(Method) |
SqlSessionFactory (单例) |
应用级别(Application) |
SqlSession |
请求/方法(Request/Method) |
Mapper |
方法(Method) |
之前在配置 mybatis的准备工作中,提到mybatis的核心配置文件(mybatis-config.xml),接下来,介绍下重点配置标签下:
(1)、configuration
是 mybatis核心配置文件的根标签,也对应着 mybatis中最重要的配置类Configuration;
注意:MyBatis 全局配置文件顺序是固定的,否则启动的时候会报错
(2)、properties
用来配置参数相关信息,eg:最常见的数据库连接信息。
如代码看到的,db.properties中配置得是数据库的连接信息,通过properties标签引入即可,其优势如下:
1、 配置提取,有利于多处引用,便于维护;
2、 项目发布时,配置文件放在外部,避免修改后重新编译打包,此种做法只需要重启应用;
3、 程序和配置分离,提升数据的安全性,比如生产环境的密码只有管理人员/运维人员知道。
(3)、settings
settings标签很重要,Mybatis中非常关键的配置都在这个标签中
属性名 |
含义 |
简介 |
枚举值 |
默认值 |
useGeneratedKeys |
是否支持 JDBC 自动生成主键
|
设置之后,将会强制使用自动生成主键的策略
|
true / false
|
false |
cacheEnabled |
是否使用缓存
|
是整个工程中所有映射器配置缓存的 开关,即是一个全局缓存开关
|
true / false
|
true |
lazyLoadingEnabled |
是否开启延迟加载
|
控制全局是否使用延迟加载 (association、collection)。当有 特殊关联关系需要单独配置时,可以使 用 fetchType 属性来覆盖此配置
|
true / false
|
false |
aggressiveLazyLoading |
是否需要侵入式延迟加载 |
开启时,无论调用什么方法加载某个对 象,都会加载该对象的所有属性,关闭 之后只会按需加载
|
true / false
|
false |
defaultExecutorType |
设置默认的执行器
|
有三种执行器:SIMPLE 为普通执行器; REUSE 执行器会重用与处理语句; BATCH 执行器将重用语句并执行批量 更新
|
SIMPLE REUSE BATCH
|
SIMPLE |
lazyLoadTriggerMethods |
指定哪个对象的方法触发一次延迟加载 |
配置需要触发延迟加载的方法的名字, 该方法就会触发一次延迟加载
|
一个逗号分隔的 方法名称列表
|
Equals clone hashCode toString
|
localCacheScope |
MyBatis 利用本地缓存机制(LocalCache)防止循环引用(circularreferences)和加 速重复嵌套查询
|
默认值为 SESSION,这种情况下会缓存 一个会话中执行的所有查询。若设置值 为 STATEMENT,本地会话仅用在语句执 行上,对相同 SqlSession 的不同调用 将不会共享数据
|
SESSION STATEMENT
|
SESSION |
logImpl |
日志实现
|
指定 MyBatis 所用日志的具体实现,未 指定时将自动查找
|
SLF4J / LOG4J LOG4J2 JDK_LOGGING COMMONS_LOGGING STDOUT_LOGGING NO_LOGGING
|
无 |
(4)、typeAliases
typeAliases标签顾名思义,用来给类全路径配置别名的,具体案例如下:
I、用户自定义别名
1.mybatis的 核心配置文件(mybatis-config.xml)配置如下:
2.mapper.xml配置
II、MyBatis 中系统预先定义好的类型别名
mybatis配置类Configuration.java
(5)、typeHandlers
typeHandlers标签有点东西了哈,再讲之前,提问个问题:
为什么 java对象里的一个String类型,可以保存成数据库中的varchar、char字段呢?
其实这个问题,仔细想下,肯定是有一个配置可以让String类型转换为varchar,很显然,mybatis的作者已经提前定义很多TypeHandler,都继承了抽象类 BaseTypeHandler,泛型就是要处理的 Java 数据类型,当我们做数据类型转换的时候,就会自动调用对应的 TypeHandler 的方法,如下图:
正如所图,我们需要冷静一下,大量知识拥入,容易堵塞... 深呼吸一口,我们继续前行 ~
接下来,我们不去看TypeHandler的源码实现,我们来自定义一个TypeHandler :
老办法,我们先来深呼吸,俗话说,照猫画虎,对于程序员来说,我们可是攻城“狮” ~
1.定义YysTypeHandler类,继承抽象类BaseTypeHandler
/**
* Mybatis
* 自定义TypeHandler
*
* @author zhaojun
*/
public class YysTypeHandler extends BaseTypeHandler {
/**
* Java类型 -> JDBC类型
* 设置String类型参数时调用
*/
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
String parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter);
}
/**
* JDBC类型 -> Java类型
* 根据列名 获取String类型参数时调用
*/
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getString(columnName);
}
/**
* JDBC类型 -> Java类型
* 根据下标 获取String类型参数时调用
*/
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getString(columnIndex);
}
/**
* JDBC类型 -> Java类型
* 存储过程方式时调用
*/
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getString(columnIndex);
}
}
2.核心配置文件(mybatis-config.xml)增加配置:
3.在我们需要使用的字段上指定,比如:
insert into user (uid, name, age)
values (
#{uid,jdbcType=Long},
#{name,jdbcType=VARCHAR,typeHandler=com.yys.type.YysTypeHandler},
#{age,jdbcType=Integer})
这时候,看完的小伙伴心想:“也就那么回事么”,哈哈,的确这么回事 ~
(6)、environments、environment
environments、environment标签,常用来管理数据库环境,
在日常工作中,我们一般分为开发环境、测试环境、生产环境,每个环境都有不同的数据库配置,
这时我们就就可以利用 environment标签来进行管理
(7)、transactionManager
配置事务管理器
1. JDBC -> 使用 Connection 对象的 commit()、rollback()、close() 管理事务
2.MANAGED -> 交给容器管理,现在为原生Mybatis,故无事务
3.spirng+mybatis -> 一般在applicationContext.xml 里面配置数据源,覆盖 MyBatis 的配置,交给 spring管理
(8)、dataSource
数据源
spirng+mybatis,事务与数据源交给 spring管理
(9)、mappers
这里配置的 目的是让 MyBatis 在启动的时候去扫描这些映射器,创建映射关系。
我们有四种指定 Mapper 文件的方式:
1、使用相对路径的资源引用(resource)
2、使用完全限定资源定位符(绝对路径)(URL)
3、使用映射器接口实现类的完全限定类名
4、将包内的映射器接口实现全部注册为映射器(最常用)
总结:
配置名称 |
配置含义 |
配置简介 |
configuration |
包裹所有配置标签 |
整个配置文件的顶级标签 |
properties
|
属性
|
该标签可以引入外部配置的属性,也可以自己配置。该配置标签所在的同一个配置文件的其他配置均可以引用此配置中的属性 |
setting
|
全局配置参数
|
用来配置一些改变运行时行为的信息,例如是否使用缓存机制,是否使用延迟加载,是否使用错误处理机制等。 |
typeAliases
|
类型别名
|
用来设置一些别名来代替 Java 的长类型声明(如java.lang.int变为 int),减少配置编码的冗余 |
typeHandlers
|
类型处理器
|
将数据库获取的值以合适的方式转换为 Java 类型,或者将 Java类型的参数转换为数据库对应的类型 |
objectFactory |
对象工厂 |
实例化目标类的工厂类配置
|
plugins
|
插件
|
可以通过插件修改 MyBatis 的核心行为,例如对语句执行的某一点进行拦截调用
|
environments
|
环境集合属性对象
|
数据库环境信息的集合。在一个配置文件中,可以有多种数据库环境集合,这样可以使 MyBatis 将 SQL 同时映射至多个数据库
|
environment
|
环境子属性对象
|
数据库环境配置的详细配置
|
transactionManager
|
事务管理
|
指定 MyBatis 的事务管理器
|
dataSource
|
数据源
|
使用其中的 type 指定数据源的连接类型,在标签对中可以使用property 属性指定数据库连接池的其他信息 |
mappers
|
映射器
|
配置 SQL 映射文件的位置,告知 MyBatis 去哪里加载 SQL映射文件
|
此篇文章到这里就暂时告一段落,相信大家都能满载而归 ...
ps: mybatis源码分析还在学习的路上,后续博文将持续更新 mybatis源码剖析,缓存机制,执行流程....
最后,都看到这里了,顺手点个赞,加个收藏吧 ,加个关注那就更好了 ~
— 猿医生.yys
差点忘记件比写博客还要重要的事情 ~
这里有 博哥(梁博)的粉丝么, 听说今年有博哥的演唱会, 私信我,约起哈 ~ ❤