Mybatis
是⼀个半
ORM
(对象关系映射)框架,它内部封装了
JDBC
,开发时只需要关注
SQL
语句本⾝,不需要花费精⼒去处理加载驱动、创建连接、创建
statement
等繁杂的过
程。程序员直接编写原⽣态
sql
,可以严格控制
sql
执⾏性能,灵活度⾼。
MyBatis
可以使⽤
XML
或注解来配置和映射原⽣信息,将
POJO
映射成数据库中的记
录,避免了⼏乎所有的
JDBC
代码和⼿动设置参数以及获取结果集。
再说⼀下缺点
SQL
语句的编写⼯作量较⼤,尤其当字段多、关联表多时,对开发⼈员编写
SQL
语句的功
底有⼀定要求
SQL
语句依赖于数据库,导致数据库移植性差,不能随意更换数据库
为什么说
Mybatis
是半⾃动
ORM
映射⼯具?它与全⾃动的区别在哪⾥?
Hibernate
属于全⾃动
ORM
映射⼯具,使⽤
Hibernate
查询关联对象或者关联集合对象时,
可以根据对象关系模型直接获取,所以它是全⾃动的。
⽽
Mybatis
在查询关联对象或关联集合对象时,需要⼿动编写
SQL
来完成,所以,被称之
为半⾃动
ORM
映射⼯具。
1
、数据连接创建、释放频繁造成系统资源浪费从⽽影响系统性能
解决:在
mybatis-config.xml
中配置数据链接池,使⽤连接池统⼀管理数据库连接。
2
、
sql
语句写在代码中造成代码不易维护
解决:将
sql
语句配置在
XXXXmapper.xml
⽂件中与
java
代码分离。
3
、向
sql
语句传参数⿇烦,因为
sql
语句的
where
条件不⼀定,可能多也可能少,占位符需
要和参数⼀⼀对应。
解决:
Mybatis
⾃动将
java
对象映射⾄
sql
语句。
4
、对结果集解析⿇烦,
sql
变化导致解析代码变化,且解析前需要遍历,如果能将数据库
记录封装成
pojo
对象解析⽐较⽅便。
解决:
Mybatis
⾃动将
sql
执⾏结果映射⾄
java
对象。
1
、 创建
SqlSessionFactory
可以从配置或者直接编码来创建
SqlSessionFactory
String
resource
=
"org/mybatis/example/mybatis-config.xml"
;
InputStream inputStream
=
Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory
=
new
SqlSessionFactoryBuilder().build(inputStream);
2
、 通过
SqlSessionFactory
创建
SqlSession
SqlSession
(会话)可以理解为程序和数据库之间的桥梁
SqlSession session
=
sqlSessionFactory.openSession();
3
、 通过
sqlsession
执⾏数据库操作
可以通过
SqlSession
实例来直接执⾏已映射的
SQL
语句:
Blog blog
=
(
Blog
)
session
.
selectOne
(
"org.mybatis.example.BlogMapper.selectBlog"
,
101
);
更常⽤的⽅式是先获取
Mapper(
映射
)
,然后再执⾏
SQL
语句:
BlogMapper mapper
=
session
.
getMapper
(
BlogMapper
.
class
);
Blog blog
=
mapper
.
selectBlog
(
101
);
4
、 调⽤
session.commit()
提交事务
如果是更新、删除语句,我们还需要提交⼀下事务。
5
、 调⽤
session.close()
关闭会话
最后⼀定要记得关闭会话。
当然,万物皆可集成
Spring
,
MyBatis
通常也是和
Spring
集成使⽤,
Spring
可以帮助我们创建线
程安全的、基于事务的
SqlSession
和映射器,并将它们直接注⼊到我们的
bean
中,我们不需
要关⼼它们的创建过程和⽣命周期,那就是另外的故事了。
第⼀种⽅法:使⽤
foreach
标签
foreach
的主要⽤在构建
in
条件中,它可以在
SQL
语句中进⾏迭代⼀个集合。
foreach
标签的属性
主要有
item
,
index
,
collection
,
open
,
separator
,
close
。
item
表⽰集合中每⼀个元素进⾏迭代时的别名,随便起的变量名;
index
指定⼀个名字,⽤于表⽰在迭代过程中,每次迭代到的位置,不常⽤;
open
表⽰该语句以什么开始,常⽤
“(”
;
separator
表⽰在每次进⾏迭代之间以什么符号作为分隔符,常⽤
“,”
;
close
表⽰以什么结束,常⽤
“)”
。
在使⽤
foreach
的时候最关键的也是最容易出错的就是
collection
属性,该属性是必须指定的,
但是在不同情况下,该属性的值是不⼀样的,主要有以下
3
种情况:
1.
如果传⼊的是单参数且参数类型是⼀个
List
的时候,
collection
属性值为
list
2.
如果传⼊的是单参数且参数类型是⼀个
array
数组的时候,
collection
的属性值为
array
3.
如果传⼊的参数是多个的时候,我们就需要把它们封装成⼀个
Map
了,当然单参数也可以
封装成
map
,实际上如果你在传⼊参数的时候,在
MyBatis
⾥⾯也是会把它封装成⼀个
Map
的,
map
的
key
就是参数名,所以这个时候
collection
属性值就是传⼊的
List
或
array
对象在⾃⼰封
装的
map
⾥⾯的
key
获取配置
获取配置这⼀步经过了⼏步转化,最终由⽣成了⼀个配置类
Configuration
实例,这个配置
类实例⾮常重要,主要作⽤包括:
读取配置⽂件,包括基础配置⽂件和映射⽂件
初始化基础配置,⽐如
MyBatis
的别名,还有其它的⼀些重要的类对象,像插件、映射
器、
ObjectFactory
等等
提供⼀个单例,作为会话⼯⼚构建的重要参数
它的构建过程也会初始化⼀些环境变量,⽐如数据源
会话运⾏
会话运⾏是
MyBatis
最复杂的部分,它的运⾏离不开四⼤组件的配合:
1.
读取
MyBatis
配置⽂件
——mybatis-config.xml
、加载映射⽂件
——
映射⽂件即
SQL
映射
⽂件,⽂件中配置了操作数据库的
SQL
语句。最后⽣成⼀个配置对象。
2.
构造会话⼯⼚:通过
MyBatis
的环境等配置信息构建会话⼯⼚
SqlSessionFactory
。
3.
创建会话对象:由会话⼯⼚创建
SqlSession
对象,该对象中包含了执⾏
SQL
语句的所有
⽅法。
4.
Executor
执⾏器:
MyBatis
底层定义了⼀个
Executor
接⼜来操作数据库,它将根据
SqlSession
传递的参数动态地⽣成需要执⾏的
SQL
语句,同时负责查询缓存的维护。
5.
StatementHandler
:数据库会话器,串联起参数映射的处理和运⾏结果映射的处理。
6.
参数处理:对输⼊参数的类型进⾏处理,并预编译。
7.
结果处理:对返回结果的类型进⾏处理,根据对象映射规则,返回相应的对象。
我们⼀般把
Mybatis
的功能架构分为三层:
API
接⼜层:提供给外部使⽤的接⼜
API
,开发⼈员通过这些本地
API
来操纵数据库。接⼜
层⼀接收到调⽤请求就会调⽤数据处理层来完成具体的数据处理。
数据处理层:负责具体的
SQL
查找、
SQL
解析、
SQL
执⾏和执⾏结果映射处理等。它主要
的⽬的是根据调⽤的请求完成⼀次数据库操作。
基础⽀撑层:负责最基础的功能⽀撑,包括连接管理、事务管理、配置加载和缓存处理,
这些都是共⽤的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基
础的⽀撑。
如何编写⼀个插件?
我们⾃⼰编写
MyBatis
插件,只需要实现拦截器接⼜
Interceptor (org.apache.ibatis. plugin
Interceptor
),在实现类中对拦截对象和⽅法进⾏处理。
实现
Mybatis
的
Interceptor
接⼜并重写
intercept()
⽅法
这⾥我们只是在⽬标对象执⾏⽬标⽅法的前后进⾏了打印;
public class
MyInterceptor
implements
Interceptor
{
Properties props
=
null
;
@Override
public
Object
intercept
(
Invocation invocation
)
throws
Throwable
{
System
.
out
.
println
(
"before……"
);
//
如果当前代理的是⼀个⾮代理对象,那么就会调⽤真实拦截对象的⽅法
//
如果不是它就会调⽤下个插件代理对象的
invoke
⽅法
Object
obj
=
invocation
.
proceed
();
System
.
out
.
println
(
"after……"
);
return
obj
;
}
}
然后再给插件编写注解,确定要拦截的对象,要拦截的⽅法
@Intercepts
({
@Signature
(
type
=
Executor
.
class
,
//
确定要拦截的对象
method
=
"update"
,
//
确定要拦截的⽅法
args
=
{
MappedStatement
.
class
,
Object
.
class
}
//
拦截⽅法的参数
)})
public class
MyInterceptor
implements
Interceptor
{
Properties props
=
null
;
@Override
public
Object
intercept
(
Invocation invocation
)
throws
Throwable
{
System
.
out
.
println
(
"before……"
);
//
如果当前代理的是⼀个⾮代理对象,那么就会调⽤真实拦截对象的⽅法
//
如果不是它就会调⽤下个插件代理对象的
invoke
⽅法
Object
obj
=
invocation
.
proceed
();
System
.
out
.
println
(
"after……"
);
return
obj
;
}
}
20.MyBatis
是如何进⾏分页的?分页插件的原理是什么?
MyBatis
是如何分页的?
MyBatis
使⽤
RowBounds
对象进⾏分页,它是针对
ResultSet
结果集执⾏的内存分页,⽽⾮物理
分页。可以在
sql
内直接书写带有物理分页的参数来完成物理分页功能,也可以使⽤分页插件
来完成物理分页。
分页插件的原理是什么?
分页插件的基本原理是使⽤
Mybatis
提供的插件接⼜,实现⾃定义插件,拦截
Executor
的
query
⽅法
在执⾏查询的时候,拦截待执⾏的
sql
,然后重写
sql
,根据
dialect
⽅⾔,添加对应的物理分
页语句和物理分页参数。
举例:
select * from student
,拦截
sql
后重写为:
select t.* from (select * from student) t limit
0, 10
可以看⼀下⼀个⼤概的
MyBatis
通⽤分页拦截器: