【Java】关于MyBatis框架的总结
最近在学习 MyBatis 框架,我在这里记录一下学习 MyBatis 过程中的心得体会。
Mybatis是什么?使用它我们可以做什么?
MyBatis 是一个开源的数据持久层框架,它内部封装了通过 JDBC 访问数据库的操作,支持普通的 SQL 查询、存储过程和高级映射,几乎消除了所有的 JDBC 代码和参数的手工设置以及结果集的检索。MyBatis 作为持久层框架,其主要思想是将程序中大量 SQL 语句剥离出来,配置在配置文件中,实现 SQL 的灵活配置。这样做的好处是将 SQL 与程序代码分离,可以在不修改程序代码的情况下,直接在配置文件中修改 SQL。Mybatis 通过简单的 XML 或者注解进行配置和原始映射,将实体类和 SQL 语句之间建立映射关系,是一种半自动化的 ORM 实现。
MyBatis 官网:http://mybatis.org
Github:https://github.com/mybatis
MyBatis环境搭建
这里我们使用的编译程序以 Eclipse 为例。
下载 jar 包 ==> 部署 jar 包 ==> 编写 configuration 核心配置文件 ==> 创建 pojo 实体类 ==> 创建 DAO 接口 ==> 创建 SQL 映射文件 ==> 编写测试类
需要的 jar 包:
创建MyBatis核心配置文件configuration.xml
MyBatis 核心配置文件主要用于配置数据库连接和 MyBatis 运行时所需的各种特性,包含了设置和影响 MyBatis 行为的属性
MyBatis的核心接口和类
1)每个 MyBatis 的应用程序都以一个 SqlSessionFactory 对象的实例为核心。
2)首先获取 SqlSessionFactoryBuilder 对象,可以根据 XML 配置文件或 Configuration 类的实例构建该对象。
3)然后获取 SqlSessionFactory 对象,该对象实例可以通过 SqlSessionFactoryBuilder 对象来获得。
4)使用 SqlSessionFactory 对象获取 SqlSession 实例。
SqlSessionFactoryBuilder
负责构建 SqlSessionFactory,提供多个 build() 方法的重载,主要分为三个:
build(Reader reader,String environment,Properties properties)
build(InputStream inputStream,String environment,Properties properties)
build(Configuration config)
最大的特点是用过即丢,一旦创建了 SqlSessionFactory 对象之后,这个类就不再需要存在了,因此 SqlSessionFactoryBuilder 的最佳作用范围就是存在于方法体内,也就是局部变量。
SqlSessionFactory
相当于创建 SqlSession 实例的工厂,可以通过 SqlSessionFactory 提供的 openSession() 方法来获取 SqlSession 实例。
SqlSessionFactory 对象一旦创建,就会在整个应用运行过程中始终存在,没有理由去销毁或再创建它,并且在应用运行中也不建议多次创建 SqlSession。因此 SqlSessionFactory 的最佳作用与是 Application,即随着应用的生命周期一同存在。这就是所谓的单例模式(指在应用运行期间有且仅有一个实例)。
这里把 SqlSessionFactory 的代码进行优化,最简单的实现方式就是放在静态代码块下:
这里使用了 ThreadLocal 管理 Mybatis 中 SqlSession 对象,保证 SqlSession 在线程(准确的说是一次请求)中的唯一性。
SqlSession
SqlSession 是用于执行持久化操作的对象,类似于 JDBC 中的 Connection。它提供了面向数据库执行 SQL 命令所需的所有方法,可以通过 SqlSession 实例直接运行已映射的 SQL 语句。
SqlSession 对应着一次数据库回话。由于数据库会话不是永久的,因此 SqlSession 的生命周期也不应该是永久的。相反,在每次访问数据库时都需要创建它。
需要注意的是,每个线程都有自己的 SqlSession 实例,SqlSession 实例不能被共享,也不是线程安全的。因此最佳的作用域范围是 request 作用域或者方法体作用域内。
SqlSession 的两种使用方式
1)通过 SqlSession 实例来直接执行已映射的 SQL 语句。
2)基于 mapper 接口方式操作数据。(接口的方法必须与SQL映射文件中SQL语句的ID一一对应)
MyBatis核心配置文件
properties元素
properties 元素描述的都是外部化,可替代的属性。有以下两种方式实现:
1)可通过外部指定的方式,即配置在典型的 Java 属性配置文件中,并使用这些属性对配置项实现动态配置。
2)直接配置为 xml,并使用这些属性对配置项实现动态配置。
settings元素
settings 元素的作用是设置一些非常重要的设置选项,用于设置和改变 MyBatis 运行中的行为。
settings 元素支持的属性 |
|||
设置项 |
描述 |
允许值 |
默认值 |
cacheEnabled |
对在此配置文件下的所有 cache 进行全局性开 / 关设置 |
true | false |
true |
lazyLoadingEnabled |
全局性设置懒加载。如果设置为 false,则所有相关联的都会被初始化加载 |
true | false |
true |
autoMappingBehavior |
MyBatis 对于 resultMap 自动映射的匹配级别 |
NONE|PARTIAL|FULL |
PARTIAL |
typeAliases 元素
typeAliases 元素的作用是配置类型别名,通过与 MyBatis 的 SQL 映射文件相关联,减少输入多余的完整类名,以简化操作。
environments元素
MyBatis 可以配置多套运行环境,我们可以灵活选择不同的配置,从而将 SQL 映射应用到不同的数据库环境上。不管增加几套运行环境,都必须要明确选择出当前的唯一一个运行环境。
mappers 元素
mappers:映射器,用来定义 SQL 的映射语句。
<mappers> <mapper resource="url"> </mappers>
1)使用类资源路径获取资源
2)使用 URL 获取资源
SQL 映射文件
下面是关于 SQL 映射文件的几个顶级元素:
mapper:映射文件的根元素节点,只有一个属性 namespace(命名空间),作用:
用于区分不同的 mapper,全局唯一。
绑定 DAO 接口,即面向接口编程。当 namespace 绑定某一接口之后,可以不用写该接口的实体类,MyBatis 会通过接口的完整限定名查找到对应的 mapper 配置来执行 SQL 语句。因此 namespace 的命名必须要和接口同名。
cache:配置给定命名空间的缓存。(性能较低,不推荐使用)
cache-ref:从其他命名空间引用缓存配置。
resultMap:用来描述数据库结果集和对象的对应关系。
sql:可以重用的 SQL 块,也可以被其他语句引用。
insert:映射插入语句。
update:映射更新语句。
delete:映射删除语句。
select:映射查询语句。
namespace 的命名必须跟某个 DAO 接口同名,通属于 DAO 层,所以映射文件与该 DAO 接口应放置在同一 package 下。
在不同的 mapper 文件中,子元素的 id 可以相同,MyBatis 通过 namespace 和子元素的 id 联合区分。接口中的方法与映射文件中的 SQL 语句 id 应一一对应。
resultType
resultType 直接表示返回类型,包括基础数据类型和复杂数据类型。
resultMap
id 属性:唯一标识,此 id 值用于 select 元素的 resultMap 属性的引用。
type 属性:表示该 resultMap 的映射结果类型。
result 子节点:用于标识一些简单属性,其中 column 属性表示从数据库中查询的字段名,property 则表示查询出来的字段对应的值赋给实体对象的哪个属性。
resultMap 是对外部 resultMap 定义的引用,对应外部 resultMap 的 id,表示返回结果映射到某一个 resultMap 上。主要应用:数据库字段信息与对象属性不一致或者需要做复杂的联合查询以便自由控制映射结果。
resultType 和 resultMap 本质上是一样的,都是 Map 数据结构。但是不能同时存在,只是二者选其一使用。
association
association:映射到 JavaBean 的某个复杂类型属性,即 JavaBean 内部嵌套一个复杂数据类型属性。association 仅处理一对一的关联关系。
属性:
javaType:完整 Java 类名或者别名。
property:映射数据库列的实体对象的属性。
id
result:
property:映射数据库列的实体对象的属性。
column:数据库列名或别名。
collection
collection 元素的作用和 association 元素的作用类似,只不过 collection 元素映射的属性是一个集合列表,即 JavaBean 内部嵌套一个复杂数据类型(集合)属性。
属性:
ofType:完整 Java 类型或者别名,即集合所包含的类型。
property:映射数据库列的实体对象的属性。
collection 的子元素与 association 基本一致。
resultMap 自动映射级别
在 MyBatis 核心配置文件中添加标签:
<settings> <setting name="autoMappingBehavior" value="FULL" /> </settings>
MyBatis 对 resultMap 自动映射有三个匹配级别:
NONE:禁止自动匹配。
PARTIAL(默认):自动匹配所有属性,有内部嵌套(association、collection)的除外。
FULL:自动匹配所有。
使用动态 SQL 完成多条件查询
if:利用 if 实现简单的条件选择。
choose(when,otherwise):相当于 Java 中的 switch,通常与 when 和 otherwise 搭配。
where:简化 SQL 语句中 where 的条件判断,并能智能地处理 and 和 or,不必担心多余关键字导致的语法错误。
set:解决动态更新语句。主要功能和 where 类似,是在包含的语句前输出一个 set,若包含的语句是以逗号结束的,会自动把该逗号忽略掉,再配合 if 元素就可以动态地更新需要修改的字段。
trim:可以灵活地去除多余的关键字。
属性:
prefix:前缀,作用是通过自动识别是否有返回值后,在 trim 包含的内容上加上前缀。
suffix:后缀,作用是在 trim 包含的内容上加上后缀。
prefixOverrides:对于 trim 包含的首部进行指定内容的忽略。
suffixOverrides:对于 trim 包含内容的首尾部进行指定内容的忽略。
foreach:迭代一个集合,通常用于 in 条件。foreach 主要用在构建 in 条件中,它可以在 SQL 语句中迭代一个集合。
属性:
item:表示集合中每一个元素进行迭代时的别名。
index:指定一个名称,用于表示在迭代过程中,每次迭代到的位置。
open:表示该语句以什么开始。(如果是 in 条件语句,以“(”开始)
separator:表示在每次进行迭代之间以什么符号作为分隔符。(如果是 in 条件语句,以“,”作为分隔符)
close:表示该语句以什么结束。(如果是 in 条件语句,以“)”结束)
collection:该属性必须指定,不同情况下,该属性的值是不一样的。主要有三种情况:
若入参为单参数且参数类型是一个 List 的时候,collection 属性值为 list。
若入参为单参数且参数类型是一个数组的时候,collection 属性值为 array。
若传入参数为多参数,就需要把它们封装为一个 Map 进行处理。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
MyBatis 框架的优点:
1)与 JDBC 相比,减少了 50% 以上的代码量。
2)MyBatis 是最简单的持久化框架,小巧并且简单易学。
3)MyBatis 相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL 写在 XML 里,从程序代码中彻底分离,降低耦合度,便于统一管理和优化,并可重用。
4)提供 XML 标签,支持编写动态 SQL 语句。
5)提供映射标签,支持对象与数据库的 ORM 字段关系映射。
MyBatis 框架的缺点:
1)SQL 语句的编写工作量较大,对开发人员编写 SQL 语句的功底有一定要求。
2)SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
补充:MyBatis 中 #{} 和 ${} 的区别
#{}: 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符,一个 #{} 被解析为一个参数占位符 。
${}: 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换。以 ${} 格式传入入参后的执行 sql 打乱了我们的预期 sql 格式及查询条件,从而实现 sql 注入。
使用 ${} 的场景:
1 作为 in 条件时,
2 参数为 int 类型并且数据库中字段的类型是 number,
3 表名
4 order by ${},排序字段
其余情况一般不使用 ${}。
简单来说,就是能用 #{} 就不要用 ${}。