梦想还是要有的,万一忘了咋办?

0%

Mybatis映射器

MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。

SQL映射文件顶级元素

  • cache
    该命名空间的缓存配置。
  • cache-ref
    引用其他命名空间的缓存配置。
  • resultMap
    描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
  • parameterMap
    老式风格的参数映射。停用
  • sql
    可被其他语句引用的可重用语句块。
  • insert
    映射插入语句
  • update
    映射更新语句
  • delete
    映射删除语句
  • select
    映射查询语句

select

1
2
3
4
5
6
7
8
9
10
11
12
select
id="selectPerson"
parameterType="int"
parameterMap="deprecated"
resultType="hashmap"
resultMap="personResultMap"
flushCache="false"
useCache="true"
timeout="10"
fetchSize="256"
statementType="PREPARED"
resultSetType="FORWARD_ONLY">

属性说明

insert, update 和 delete

数据变更语句 insert,update 和 delete 的实现非常接近:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<insert
id="insertAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
keyProperty=""
keyColumn=""
useGeneratedKeys=""
timeout="20">

<update
id="updateAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
timeout="20">

<delete
id="deleteAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
timeout="20">

** insert、update、delete属性介绍**

插入主键

数据库支持自动生成主键,使用useGeneratedKeys=true,keyProperty=”id”

1
2
3
4
5
6
7
8
9
10
11
12
13
<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username,password,email,bio)
values (#{username},#{password},#{email},#{bio})
</insert>
//----多行插入
<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username, password, email, bio) values
<foreach item="item" collection="list" separator=",">
(#{item.username}, #{item.password}, #{item.email}, #{item.bio})
</foreach>
</insert>

selectKey生成主键

1
2
3
4
5
6
7
8
9
10
//生成一个随机主键,仅示例,不建议使用。
<insert id="insertAuthor">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
</selectKey>
insert into Author
(id, username, password, email,bio, favourite_section)
values
(#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR})
</insert>

selectKey

1
2
3
4
5
<selectKey
keyProperty="id"
resultType="int"
order="BEFORE"
statementType="PREPARED">

** selectKey属性**

Sql

这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值。比如:

1
2
3
4
5
6
7
8
9
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
//----使用
<select id="selectUsers" resultType="map">
select
<include refid="userColumns"><property name="alias" value="t1"/></include>,
<include refid="userColumns"><property name="alias" value="t2"/></include>
from some_table t1
cross join some_table t2
</select>

也可以在 include 元素的 refid 属性或内部语句中使用属性值,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<sql id="sometable">
${prefix}Table
</sql>

<sql id="someinclude">
from
<include refid="${include_target}"/>
</sql>

<select id="select" resultType="map">
select
field1, field2, field3
<include refid="someinclude">
<property name="prefix" value="Some"/>
<property name="include_target" value="sometable"/>
</include>
</select>

参数

基本使用

1
2
3
//直接被替换为占位符 "?",与${}不同。
#{property}
#{property,javaType=int,jdbcType=NUMERIC}

增加类型处理器

1
#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}

小数点后位数

1
#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}

mode 属性,存储过程相关属性。

1
2
mode=IN|OUT|INOUT
#{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentResultMap}

** 字符串替换**

1
2
3
4
${name} ,直接替换为 对应的字符串,而不是占位符 "?"
//示例
@Select("select * from user where ${column} = #{value}")
User findByColumn(@Param("column") String column, @Param("value") String value);

提示:直接将用户输入内容进行${input}替换存在sql注入的安全问题。

结果映射

简单的映射基本不需要resultMap,直接使用map、pojo就行。
复杂的sql 使用resultMap也会变得非常简单。

resultMap概念图

  • constructor -用于在实例化类时,注入结果到构造方法中
    • idArg -ID参数
    • arg - 将被注入到构造方法中的一个普通结果
  • id - 一个ID结果;
  • result - 注入到字段或javabean属性的普通结果
  • association - 一个复杂类型的关联;许多结果将包装成这种类型
    • 嵌套结果映射 - 关联可以使resultMap元素,或其他结果映射的引用
  • collection - 一个复杂类型集合
    • 嵌套结果映射 - 可以使resultMap或其它引用
  • discriminator - 使用结果值来决定使用那个resultMap
    • case - 基于某些值的结果映射
      • 嵌套结果映射 -case 也是一个结果映射;或其它resultmap及引用

resultMap属性

  • id
    当前命名空间中的一个唯一标识,用于标识一个结果映射。
  • type
    类的完全限定名, 或者一个类型别名(关于内置的类型别名,可以参考上面的表格)。
  • autoMapping
    如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。

id&result

1
2
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>

两个元素共有的属性:

构造方法
构造方法注入允许你在初始化时为类设置属性的值,而不用暴露出公有方法。MyBatis 也支持私有属性和私有 JavaBean 属性来完成注入,但有一些人更青睐于通过构造方法进行注入。 constructor 元素就是为此而生的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class User {
public User(Integer id, String username, int age) {
}
}
-----
//必须按照参数循序写
<constructor>
<idArg column="id" javaType="int"/>
<arg column="username" javaType="String"/>
<arg column="age" javaType="_int"/>
</constructor>
//3.4.3+
//可以在指定参数名称的前提下,以任意顺序编写 arg 元素
//构造器需要对参数添加 @param注解
<constructor>
<idArg column="id" javaType="int" name="id" />
<arg column="age" javaType="_int" name="age" />
<arg column="username" javaType="String" name="username" />
</constructor>

构造方法的属性

关联
关联(association)元素处理“有一个”类型的关系。

如何加载关联:

  • 嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
  • 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。

关联方法的属性

1
2
3
4
<association property="author" column="blog_author_id" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
</association>

关联的多结果集(ResultSet)

多结果集,主查询返回多条数据。

集合
解决1对多的问题 ,与 关联(association)相似。

1
2
3
4
//其中ofType是集合泛型类型
<collection property="posts" column="id" ofType="Post" select="selectPostsForBlog"/>
//-----或者----
<collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>

鉴别器(discriminator)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<resultMap id="vehicleResult" type="Vehicle">
<id property="id" column="id" />
<result property="vin" column="vin"/>
<result property="year" column="year"/>
<result property="make" column="make"/>
<result property="model" column="model"/>
<result property="color" column="color"/>
<discriminator javaType="int" column="vehicle_type">
<case value="1" resultMap="carResult"/>
<case value="2" resultMap="truckResult"/>
<case value="3" resultMap="vanResult"/>
<case value="4" resultMap="suvResult"/>
</discriminator>
</resultMap>

完整示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!-- 非常复杂的语句 -->
<select id="selectBlogDetails" resultMap="detailedBlogResultMap">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio,
A.favourite_section as author_favourite_section,
P.id as post_id,
P.blog_id as post_blog_id,
P.author_id as post_author_id,
P.created_on as post_created_on,
P.section as post_section,
P.subject as post_subject,
P.draft as draft,
P.body as post_body,
C.id as comment_id,
C.post_id as comment_post_id,
C.name as comment_name,
C.comment as comment_text,
T.id as tag_id,
T.name as tag_name
from Blog B
left outer join Author A on B.author_id = A.id
left outer join Post P on B.id = P.blog_id
left outer join Comment C on P.id = C.post_id
left outer join Post_Tag PT on PT.post_id = P.id
left outer join Tag T on PT.tag_id = T.id
where B.id = #{id}
</select>

上面sql对应resultMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!-- 非常复杂的结果映射 -->
<resultMap id="detailedBlogResultMap" type="Blog">
<constructor>
<idArg column="blog_id" javaType="int"/>
</constructor>
<result property="title" column="blog_title"/>
<association property="author" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
<result property="favouriteSection" column="author_favourite_section"/>
</association>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<association property="author" javaType="Author"/>
<collection property="comments" ofType="Comment">
<id property="id" column="comment_id"/>
</collection>
<collection property="tags" ofType="Tag" >
<id property="id" column="tag_id"/>
</collection>
<discriminator javaType="int" column="draft">
<case value="1" resultType="DraftPost"/>
</discriminator>
</collection>
</resultMap>

自动映射

  • 忽略大小写
  • 驼峰支持,设置mapUnderscoreToCamelCase=true

三种自动映射等级:

  • NONE
    禁止自动映射。仅对手动映射的属性进行映射。
  • PARTIAL
    默认状态,对除在内部定义了嵌套结果映射(连接)以外的属性进行映射
  • FULL
    自动映射所有属性

缓存

默认只启动本地会话缓存,只对一个会话中的数据进行缓存。

二级缓存

二级缓存功能

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

启用方式

全局二级缓存需要在SQL映射文件中添加一行

1
<cache/>

配置参数

1
2
3
4
5
6
7
8
9
//创建了一个 FIFO 缓存
//每隔 60 秒刷新
//最多可以存储结果对象或列表的 512 个引用
//而且返回的对象被认为是只读的
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>

清楚策略(eviction)

  • LRU - 最近最少使用,移除最长时间不被使用的对象;
  • FIFO - 先进先出,按对象进入缓存的顺序移除他们
  • SOFT - 软应用,基于垃圾回收器状态和软引用规则移除对象
  • WEAK - 弱引用,更积极的基于垃圾收集器状态和弱引用规则移除对象。

默认策略为:LRU

刷新间隔(flushInterval):正整数,单位:毫秒,默认:unset 不刷新
引用数目(size):缓存对象个数,正整数,默认值1024
只读(readOnly):true时返回对象是共享的同一个实例,不能被修改。false时通过序列化返回对象的拷贝实例。速度慢、但安全。默认是false

提示:二级缓存是事务性的,意思是:当sqlsession完成并提交时或回滚时,但并没有执行flushCache=true的insert/delete/update 语句时,缓存会获得更新。

自定义缓存

  • 首先自定义缓存不支持上面的配置属性
  • 实现 org.apache.ibatis.cache.Cache 接口
  • 提供一个接受 String 参数作为 id 的构造器。
  • cache 配置type属性指向自定义缓存类
  • 支持property进行配置,需要实现name对应的public属性
  • 设置完所有属性后悔指向org.apache.ibatis.builder.InitializingObject 接口下面的initialize方法。需要的话可以实现该方法;
1
2
3
<cache type="com.domain.something.MyCustomCache">
<property name="file" value="/a/b/c.tmp">
</cache>

默认缓存策略会绑定命名空间,如果需要可以独立配置缓存;
各种语句默认缓存策略:

1
2
3
4
<select ... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>

cache-ref

可以通过引用来共享同一个缓存配置

1
<cache-ref namespace="com.someone.application.data.SomeMapper"/>