Mybatis总结
Contents
Mybatis 入门
基础
从XML中构建SqlSessionFactory
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。
1 | String resource = "org/mybatis/example/mybatis-config.xml"; |
从 SqlSessionFactory 中获取 SqlSession
既然有了 SqlSessionFactory ,顾名思义,我们就可以从中获得 SqlSession 的实例了。SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。例如:
1 | SqlSession session = sqlSessionFactory.openSession(); |
探究已映射的SQL语句
1 | <?xml version="1.0" encoding="UTF-8" ?> |
在一个 XML 映射文件中,你想定义多少个映射语句都是可以的,这样下来,XML 头部和文档类型声明占去的部分就显得微不足道了。文件的剩余部分具有很好的自解释性。在命名空间“org.mybatis.example.BlogMapper”中定义了一个名为“selectBlog”的映射语句,这样它就允许你使用指定的完全限定名“org.mybatis.example.BlogMapper.selectBlog”来调用映射语句
作用域与声明周期
SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但是最好还是不要让其一直存在以保证所有的 XML 解析资源开放给更重要的事情。
SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由对它进行清除或重建。使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏味道(bad smell)”。因此 SqlSessionFactory 的最佳作用域是应用作用域。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的管理作用域中,比如 Servlet 架构中的 HttpSession。如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的作用域中。换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。
Mapper XML文件
SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):
cache
– 给定命名空间的缓存配置。cache-ref
– 其他命名空间缓存配置的引用。resultMap
– 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。sql
– 可被其他语句引用的可重用语句块。insert
– 映射插入语句update
– 映射更新语句delete
– 映射删除语句select
– 映射查询语句
select
1 | <select id="selectPerson" parameterType="int" resultType="hashmap"> |
#{id}
会告诉 MyBatis 创建一个预处理语句参数,通过 JDBC,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中.
参数:
- id :在命名空间中唯一的标识符,可以被用来引用这条语句。
- parameterType :将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
- resultType: 从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。
- resultMap: 外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,使用 resultMap 或 resultType,但不能同时使用。
- flushCache: 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。
- useCache: 将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。
- timeout: 在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。
insert update 和delete
1 | <insert id="insertAuthor" parameterType="domain.blog.Author" flushCache="true" |
parameterType :将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
useGeneratedKeys: (仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。
keyProperty: (仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认:
unset
。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。如果你的数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),那么你可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上就OK了。例如,如果上面的 Author 表已经对 id 使用了自动生成的列类型,那么语句可以修改为:
1
2
3
4<insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id">
insert into Author (username,password,email,bio)
values (#{username},#{password},#{email},#{bio})
</insert>
sql
这个元素可以被用来定义可重用的 SQL 代码段,可以包含在其他语句中。它可以被静态地(在加载参数) 参数化. 不同的属性值通过包含的实例变化. 比如:
1 | <sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql> |
这个 SQL 片段可以被包含在其他语句中,例如
1 | <select id="selectUsers" resultType="map"> |
ResultMap
resultMap 元素是 MyBatis 中最重要最强大的元素。 ResultMap 的设计就是简单语句不需要明确的结果映射,而很多复杂语句确实需要描述它们的关系。
例如下面的Java Bean
1 | package com.someapp.model; |
这样的一个 JavaBean 可以被映射到结果集,就像映射到 HashMap 一样简单。
1 | <select id="selectUsers" resultType="com.someapp.model.User"> |
这些情况下,MyBatis 会在幕后自动创建一个 ResultMap,基于属性名来映射列到 JavaBean 的属性上。如果列名没有精确匹配,你可以在列名上使用 select 字句的别名(一个 基本的 SQL 特性)来匹配标签。比如
1 | <select id="selectUsers" resultType="User"> |
但是更常用的是下面的自动创建方式
1 | <resultMap id="userResultMap" type="User"> |
高级结果映射
how to do with it ?
1 | <select id="selectBlogDetails" resultMap="detailedBlogResultMap"> |
resultMap中的子标签
constructor
- 类在实例化时,用来注入结果到构造方法中.
idArg
- ID 参数;标记结果作为 ID 可以帮助提高整体效能arg
- 注入到构造方法的一个普通结果
id
– 一个 ID 结果;标记结果作为 ID 可以帮助提高整体效能
result
– 注入到字段或 JavaBean 属性的普通结果
association
– 一个复杂的类型关联;许多结果将包成这种类型
collection
– 复杂类型的集
discriminator
– 使用结果值来决定使用哪个结果映射
id result
1 | <id property="id" column="post_id"/> |
常用属性
属性 | 描述 |
---|---|
property |
映射到列结果的 JavaBean的字段或属性。如果匹配的是存在的,和给定名称相同 的 JavaBeans 的属性,那么就会使用。否则 MyBatis 将会寻找给定名称 property 的字段。 |
column |
从数据库中得到的列名,或者是列名的重命名标签。 |
javaType |
一个 Java 类的完全限定名,或一个类型别名(参考上面内建类型别名 的列表) 。如果你映射到一个 JavaBean,MyBatis 通常可以断定类型。 然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证所需的行为。 |
jdbcType |
支持的 JDBC 类型列表中的类型。JDBC 类型是仅仅需要对插入,更新和删除操作可能为空的列进行处理。这是 JDBC jdbcType 的需要,而不是 MyBatis 的。 |
typeHandler |
我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。这个属性值是类的完全限定名或者是一个类型处理 器的实现,或者是类型别名。 |
关联
1 | <association property="author" column="blog_author_id" javaType="Author"> |
属性 | 描述 |
---|---|
property |
映射到列结果的字段或属性。如果匹配的是存在的,和给定名称相同的 property JavaBeans 的属性, 那么就会使用。 否则 MyBatis 将会寻找给定名称的字段。 |
javaType |
一个 Java 类的完全限定名,或一个类型别名 |
jdbcType |
在这个表格之前的所支持的 JDBC 类型列表中的类型。JDBC 类型是仅仅 需要对插入, 更新和删除操作可能为空的列进行处理。这是 JDBC 的需要, jdbcType 而不是 MyBatis 的。 |
typeHandler |
我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的 typeHandler 类型处理器。 这个属性值是类的完全限定名或者是一个类型处理器的实现, 或者是类型别名。 |
关联中不同的是你需要告诉 MyBatis 如何加载关联。MyBatis 在这方面会有两种不同的 方式:
- 嵌套查询:通过执行另外一个 SQL 映射语句来返回预期的复杂类型。
属性 | 描述 |
---|---|
column |
来自数据库的类名,或重命名的列标签。 |
select |
另外一个映射语句的 ID,可以加载这个属性映射需要的复杂类型。获取的 在列属性中指定的列的值将被传递给目标 select 语句作为参数。 |
fetchType |
可选的。有效值为 lazy 和eager 。 如果使用了,它将取代全局配置参数lazyLoadingEnabled 。 |
示例
1 | <resultMap id="blogResult" type="Blog"> |
- 嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集。
1 | <resultMap id="blogResult" type="Blog"> |
集合
1 | <collection property="posts" ofType="domain.blog.Post"> |
- 嵌套查询
1 | <resultMap id="blogResult" type="Blog"> |
注意那个新的“ofType”属性。这个属性用来区分 JavaBean(或字段)属性类型和集合包含的类型来说是很重要的。所以你可以读出下面这个 映射:
1 | <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/> |
读作: “在 Post 类型的 ArrayList 中的 posts 的集合。”
javaType 属性是不需要的,因为 MyBatis 在很多情况下会为你算出来。所以你可以缩短 写法:
1 | <collection property="posts" column="id" ofType="Post" select="selectPostsForBlog"/> |
- 嵌套结果
1 | <resultMap id="blogResult" type="Blog"> |
缓存
MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。MyBatis 3 中的缓存实现的很多改进都已经实现了,使得它更加强大而且易于配置。
默认情况下是没有开启缓存的,除了局部的 session 缓存,可以增强变现而且处理循环 依赖也是必须的。要开启二级缓存,你需要在你的 SQL 映射文件中添加一行:
1 | <cache/> |
字面上看就是这样。这个简单语句的效果如下:
- 映射语句文件中的所有 select 语句将会被缓存。
- 映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。
- 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
- 根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。
- 缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。
- 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
Author: corn1ng
Link: https://corn1ng.github.io/2018/01/24/Mybatis总结/
License: 知识共享署名-非商业性使用 4.0 国际许可协议