企业开发基础-4-Mybatis-基础篇
简介
基本介绍
- MyBatis 是一款优秀的持久层框架,它支持 自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML或注解 来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录
数据持久化
- 持久化就是将程序的数据在持久状态和瞬时状态转化的过程
- 内存:断电即失
ORM 思想
ORM 是指(Object Relationship Mapping)对象关系映射
- 对象:Java 的实体类对象
- 关系:关系型数据库
- 映射:二者之间的对应关系
Java概念 | 数据库概念 |
---|---|
类 | 表 |
属性 | 字段/列 |
对象 | 记录/行 |
日志
日志工厂
Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:
- SLF4J
- Apache Commons Logging
- Log4j 2
- Log4j (3.5.9 起废弃)
JDK logging
MyBatis 内置日志工厂基于运行时自省机制选择合适的日志工具。它会使用第一个查找得到的工具(按上文列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。
如果你的应用部署在一个类路径已经包含 Commons Logging 的环境中,而你又想使用其它日志工具,你可以通过在 MyBatis 配置文件 mybatis-config.xml 里面添加一项 setting 来选择别的日志工具。
1 | <configuration> |
配置实例
导入依赖
1 | <dependency> |
log4j.properties
1 | # 将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file 的定义在下面的代码 |
log4j.xml
1 |
|
mybatis-config.xml
1 | <configuration> |
动态SQL
if
if 标签通过 test属性 给出判断的条件,如果条件成立,则将执行标签内的 SQL 语句
1 | <select id="queryBlogIf" parameterType="map" resultType="blog"> |
choose、when、otherwise
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素
1 | <select id="findActiveBlogLike" |
当某个 when 标签的条件满足时将对应的 SQL 语句返回,如果都不满足并且有 otherwise 标签时,才会返回 otherwise 标签中的 SQL 语句
where
考虑 if 标签中的范例出现的一种情况:当 第一个 if 标签条件不成立而第二个条件成立时,拼接成的 SQL 语句中 where 后面连着的是 and,会造成 SQL 语句语法错误,而 where 标签可以解决这个问题
1 | <select id="getEmpByCondition" resultType="Emp"> |
where 标签 只会在子标签返回任何内容的情况下才插入 WHERE 子句。而且,若子句的开头有多余的 and 或者 or,where 标签也会将它们去除,但是子句末尾的 and 或者 or 不能去除
trim
trim 标签用于 去掉或添加 标签中的内容
属性 | 说明 |
---|---|
prefix | 在trim标签中的内容的前面添加某些内容 |
prefixOverrides | 在trim标签中的内容的前面去掉某些内容 |
suffix | 在trim标签中的内容的后面添加某些内容 |
suffixOverrides | 在trim标签中的内容的后面去掉某些内容 |
1 | <select id="getEmpByCondition" resultType="Emp"> |
set
1 | <update id="updateBlog" parameterType="map"> |
sql
sql 元素可以用来 定义可重用的 SQL 代码片段,以便在其它语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值
- 基于 单表 定义 SQL 片段
不能存在 where 标签
用于记录一段通用的 SQL 语句片段,在需要用到该 SQL 语句片段的地方中通过 include 标签将该 SQL 语句片段插入
sql 标签通过 id 属性唯一标识一个 SQL 语句片段,include 标签通过 refid 属性指定使用某个 SQL 片段
1 | <sql id="if-title-author"> |
foreach
它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。
它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符
可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。
当使用 可迭代对象或者数组 时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。
当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
foreach 标签可以用的属性
属性 | 说明 |
---|---|
collection | 指定需要遍历的集合或数组 |
item | 当前遍历到的元素(可迭代对象或者数组);值(Map对象) |
index | 当前遍历到的元素的序号(可迭代对象或者数组);键(Map对象) |
open | 指定遍历开始前添加的字符串 |
close | 指定遍历开始后添加的字符串 |
separator | 指定每次遍历之间的分隔符 |
collection 属性值注意事项
- 如果遍历的是 List 时,属性值为 list
- 如果遍历的是数组时,属性值为 array
- 如果遍历的是 Map 时,属性值可以是 map.keys()、map.values()、map.entrySet()
- 除此之外,还可以在映射方法的参数中使用 @Param() 注解自定义 collection 属性值
批量添加数据
1 | <insert id="addMoreEmp"> |
批量删除数据
1 | <delete id="deleteMoreEmp"> |
SELECT * FROM blog WHERE 1=1 AND (id=1 OR id = 2 OR id = 3)
1 | <!--SELECT * FROM blog WHERE 1=1 AND (id=1 OR id = 2 OR id = 3)--> |
Java API
Resources
Resources 类由 MyBatis 提供用于获取来自核心配置文件的输入流
InputStream getResourceAsStream(String filepath)
,注意这是一个静态方法
SqlSessionFactoryBuilder
由 MyBatis 提供用于获取 SqlSessionFactory 的实例对象,每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的
1 | SqlSessionFactory build(InputStream inputStream) |
- 第一种方法是最常用的,它接受一个指向 XML 文件的 InputStream 实例。可选的参数是 environment 和 properties。environment 决定加载哪种环境,包括数据源和事务管理器
- 如果你调用了带 environment 参数的 build 方法,那么 MyBatis 将使用该环境对应的配置。当然,如果你指定了一个无效的环境,会收到错误。如果你调用了不带 environment 参数的 build 方法,那么就会使用默认的环境配置(在上面的示例中,通过 default=”development” 指定了默认环境)。
- 如果你调用了接受 properties 实例的方法,那么 MyBatis 就会加载这些属性,并在配置中提供使用。绝大多数场合下,可以用 ${propName} 形式引用这些配置值。
属性优先级: 如果一个属性存在于下面的多个位置,那么 MyBatis 将按照以下顺序来加载它们:
- 首先,读取在 properties 元素体中指定的属性;
- 其次,读取在 properties 元素的类路径 resource 或 url 指定的属性,且会覆盖已经指定了的重复属性;
最后,读取作为方法参数传递的属性,且会覆盖已经从 properties 元素体和 resource 或 url 属性中加载了的重复属性。
通过方法参数传递的属性的优先级最高,resource 或 url 指定的属性优先级中等,在 properties 元素体中指定的属性优先级最低。
SqlSessionFactory
由MyBatis提供用于获取SqlSession对象,每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的
1 | SqlSession openSession() |
默认的 openSession() 方法没有参数,它会创建具备如下特性的 SqlSession:
- 事务作用域将会开启(也就是不自动提交)。
- 将由当前环境配置的 DataSource 实例中获取 Connection 对象。
- 事务隔离级别将会使用驱动或数据源的默认设置。
- 预处理语句不会被复用,也不会批量处理更新
参数 | 说明 |
---|---|
autoCommit |
true 值即可开启自动提交功能 |
Connection |
若要使用自己的 Connection 实例,传递一个 Connection 实例给 connection 参数即可 |
TransactionIsolationLevel |
事务隔离级别支持 JDBC 的五个隔离级别(NONE 、READ_UNCOMMITTED 、READ_COMMITTED 、REPEATABLE_READ 和 SERIALIZABLE ),并且与预期的行为一致。 |
ExecutorType.SIMPLE |
该类型的执行器没有特别的行为。它为每个语句的执行创建一个新的预处理语句。 |
ExecutorType.REUSE |
该类型的执行器会复用预处理语句 |
ExecutorType.BATCH |
该类型的执行器会批量执行所有更新语句 |
SqlSession
SqlSession 类由 MyBatis 提供用于执行 SQL、管理事务、接口代理,它包含了所有执行语句、提交或回滚事务以及获取映射器实例的方法。
使用映射器
1 | <T> T getMapper(Class<T> type) |
SqlSession 还有很多有关数据库增删改查的方法,但是这些方法繁琐而且不符合类型安全,所以使用 getMapper() 方法来获取一个 Mapper 接口的代理实现类来执行映射语句是个比较方便的做法
事务控制方法
1 | void commit() |
默认情况下 MyBatis 不会自动提交事务,如果你没有使用这些方法提交修改,那么你可以在 commit 和 rollback 方法参数中传入 true 值,来保证事务被正常提交(注意,在自动提交模式或者使用了外部事务管理器的情况下,设置 force 值对 session 无效)。大部分情况下你无需调用 rollback(),因为 MyBatis 会在你没有调用 commit 时替你完成回滚操作。不过,当你要在一个可能多次提交或回滚的 session 中详细控制事务,回滚操作就派上用场了。
确保 SqlSession 被关闭
1 | void close() |
清空本地缓存
1 | void clearCache() |
生命周期和作用域
SqlSessionFactoryBuilder
一旦创建了 SqlSessionFactory,就不再需要它了。因此 SqlSessionFactoryBuilder 实例的最佳范围是方法范围(也就是局部方法变量)
SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由对它进行清除或重建。最佳范围是应用范围。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式
SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的范围是请求或方法范围。
- 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。
- 绝不能将 SqlSession 实例的引用放在任何类型的管理范围中,比如 Serlvet 架构中的 HttpSession。如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的范围中。换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭
工具类
1 | public class SqlSessionUtil { |
缓存
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制 。
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,需要在你的 SQL 映射文件中添加一行:
1 | <cache/> |
一级缓存
一级缓存是 SqlSession级别 的,通过同一个 SqlSession 查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问,一级缓存是 默认开启的
一级缓存失效的四种情况:
- 使用另一个 SqlSession
- 同一个 SqlSession 但是查询条件不同
- 同一个 SqlSession 但是两次查询中间执行了任何一次增删改操作
- 同一个 SqlSession 但是两次查询中间手动清空了缓存,手动清空缓存的方法是调用 SqlSession 的
clearCache()
方法
二级缓存
二级缓存是 SqlSessionFactory 级别,通过 同一个 SqlSessionFactory 创建 的 SqlSession 查询的结果会被缓存,此后若再次执行相同的查询语句,结果就会从缓存中获取
二级缓存 开启 的条件:
- 在核心配置文件中,设置全局配置属性
cacheEnabled="true"
,默认为true,不需要设置 - 在映射文件中设置标签
<cache/>
; - 二级缓存 必须 在 SqlSession 关闭或提交之后有效
- 查询的数据所转换的实体类类型必须实现序列化的接口
二级缓存 失效 的情况:两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
<cache/>
可以设置的一些属性:
eviction 属性:缓存回收策略,默认的是 LRU
缓存回收策略 | 说明 |
---|---|
LRU(Least Recently Used) | 最近最少使用的:移除最长时间不被使用的对象 |
FIFO(First in First out) | 先进先出:按对象进入缓存的顺序来移除它们 |
SOFT – 软引用 | 移除基于垃圾回收器状态和软引用规则的对象 |
WEAK – 弱引用 | 更积极地移除基于垃圾收集器状态和弱引用规则的对象 |
flushInterval 属性:刷新间隔,单位是毫秒,默认情况下不设置也就是没有刷新间隔,缓存仅仅调用语句时刷新
size 属性:引用数目,正整数代表缓存最多可以存储多少个对象,太大容易导致内存溢出
readOnly 属性:只读, 取值是true/false
- true:只读缓存,会给所有调用者返回 缓存对象的相同实例,因此这些对象不能被修改,这提供了很重要的性能优势
- false:读写缓存,会返回 缓存对象的拷贝(通过序列化),这会慢一些,但是安全,因此 默认是false
缓存顺序
先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用
如果二级缓存没有命中,再查询一级缓存
如果一级缓存也没有命中,则查询数据库
注意 SqlSession 关闭之后,一级缓存中的数据才会写入二级缓存