SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):
查询
<select id="selectPerson" parameterType="int" resultType="hashmap">
SELECT * FROM PERSON WHERE ID = #{id}
select>
下面就是 insert,update 和 delete 语句的示例
<insert id="insertAuthor">
insert into Author (id,username,password,email,bio)
values (#{id},#{username},#{password},#{email},#{bio})
insert>
<update id="updateAuthor">
update Author set
username = #{username},
password = #{password},
email = #{email},
bio = #{bio}
where id = #{id}
update>
<delete id="deleteAuthor">
delete from Author where id = #{id}
delete>
在插入语句里面有一些额外的属性和子元素用来处理主键的生成,有多种生成方式。
如果你的数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),那么你可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上就OK了
<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username,password,email,bio)
values (#{username},#{password},#{email},#{bio})
insert>
对不支持自动生成主键的JDBC驱动,Mybatis有另外一种生成主键。
<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 元素将会首先运行,Author 的 id 会被设置,然后插入语句会被调用。
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password sql>
这个元素可以被用来定义重用的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 属性里
或include内部语句中${prefix}Table
<insert id="insertUser" parameterType="User">
insert into users (id, username, password)
values (#{id}, #{username}, #{password})
insert>
如果User类型的参数对象传递到了语句中,id、username 和 password 属性将会被查找。
大多时候你只须简单地指定属性名,其他的事情 MyBatis 会自己去推断,顶多要为可能为空的列指定 jdbcType。
#{firstName}
#{middleInitial,jdbcType=VARCHAR}
#{lastName}
更多选项设置:http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html#Parameters
默认情况,使用 #{}
会导致PreparedStatement参数创建,并安全设置参数。首选!
有时想插入一个不转义的字符串,如:
ORDER BY ${columnName}
注意:用这种方式不安全,可能导致sql注入,要么不允许用户输入这些字段,要么自行转义验证。
resultMap 元素是 MyBatis 中最重要最强大的元素。用来支持程序使用 JavaBean 或 POJO(Plain Old Java Objects,普通 Java 对象)作为领域模型。
例如下面配置映射到User实体中
<select id="selectUsers" resultType="com.someapp.model.User">
select id, username, hashedPassword
from some_table
where id = #{id}
select>
类型别名可以帮助你不用输入类的完全限定名
<typeAlias type="com.someapp.model.User" alias="User"/>
<select id="selectUsers" resultType="User">
select id, username, hashedPassword
from some_table
where id = #{id}
select>
Mybatis会创建一个ResultMap,映射到javaBean中。如果没有匹配上,可以再select语句中使用别名
<select id="selectUsers" resultType="User">
select
user_id as "id",
user_name as "userName",
hashed_password as "hashedPassword"
from some_table
where id = #{id}
select>
下面是另一种方式来实现映射。
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
resultMap>
引用它的语句时使用resultMap属性(我们去掉了resultType属性)。
<select id="selectUsers" resultMap="userResultMap">
select user_id, user_name, hashed_password
from some_table
where id = #{id}
select>
总结:resultMap可以用来解决数据库列名与属性没有匹配的问题。如果足够了解它,就不需要显示配置它。
<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>
解析:http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html#Result_Maps
public class User {
//...
public User(Integer id, String username, int age) {
//...
}
//...
}
将结果注入构造方法,Mybatis需要通过某种方式定位相应构造方法。constructor 元素就是为此而生的。
<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>
如果类中存在名称和类型相同的属性,那么可以省略 javaType 。
<association property="author" column="blog_author_id" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
association>
<resultMap id="blogResult" type="Blog">
<association property="author" column="author_id" javaType="Author" select="selectAuthor"/>
resultMap>
<select id="selectBlog" resultMap="blogResult">
SELECT * FROM BLOG WHERE ID = #{id}
select>
<select id="selectAuthor" resultType="Author">
SELECT * FROM AUTHOR WHERE ID = #{id}
select>
我们有两个查询语句:一个来加载博客,另外一个来加载作者,而且博客的结果映射描 述了“selectAuthor”语句应该被用来加载它的 author 属性。
其他所有的属性将会被自动加载,假设它们的列和属性名相匹配。
这种方式很简单,但是对于大型数据集合和列表表现不好,原因是我们熟悉的“N+1 查询问题”
- 你执行了一个单独的 SQL 语句来获取结果列表(就是“+1”)。
- 对返回的每条记录,你执行了一个查询语句来为每个加载细节(就是“N”)。
Mybatis能延时加载是好事,但是加载完后立即调用所有延迟加载,就很糟糕。
有另外一种方法。
<select id="selectBlog" resultMap="blogResult">
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
from Blog B left outer join Author A on B.author_id = A.id
where B.id = #{id}
select>
注意这个联合查询, 使用唯一而且清晰的名字来重命名。 使映射非常简单。
<resultMap id="blogResult" type="Blog">
<id property="id" column="blog_id" />
<result property="title" column="blog_title"/>
<association property="author" column="blog_author_id" javaType="Author" resultMap="authorResult"/>
resultMap>
<resultMap id="authorResult" type="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"/>
resultMap>
在上面,authorResult 用来加载作者实例。现在,上面的示例用了外部的结果映射元素来映射关联。这使得 Author 结果映射可以 重用
非常重要: id元素在嵌套结果映射中扮演着非 常重要的角色。 不指定唯一标志属性,会有严重的性能问题。
如果不需要映射到单独的结果映射中,也可以嵌套结果映射。
<resultMap id="blogResult" type="Blog">
<id property="id" column="blog_id" />
<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"/>
association>
resultMap>
<collection property="posts" ofType="domain.blog.Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<result property="body" column="post_body"/>
collection>
和关联相同,不同处:
private List<Post> posts;
首先,让我们看看使用嵌套查询来为博客加载文章。
<resultMap id="blogResult" type="Blog">
<collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>
resultMap>
<select id="selectBlog" resultMap="blogResult">
SELECT * FROM BLOG WHERE ID = #{id}
select>
<select id="selectPostsForBlog" resultType="Post">
SELECT * FROM POST WHERE BLOG_ID = #{id}
select>
关注这段
<collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>
读作:“在Post类型的ArrayList中的posts的集合”。
至此,你可以猜测集合的嵌套结果是如何来工作的,因为它和关联完全相同,除了它应 用了一个“ofType”属性
详细介绍:http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html#Result_Maps
你的应用在找到最佳方法前要一直进行的单元测试和性 能测试。
<discriminator javaType="int" column="draft">
<case value="1" resultType="DraftPost"/>
discriminator>
有时一个单独的数据库查询也许返回很多不同 (但是希望有些关联) 数据类型的结果集。 鉴别器元素就是被设计来处理这个情况的, 还有包括类的继承层次结构。和switch语句类似。
如果数据库字段名和javaBean属性名不一致,要将 mapUnderscoreToCamelCase设置为true。
例如:
<select id="selectUsers" resultMap="userResultMap">
select
user_id as "id",
user_name as "userName",
hashed_password
from some_table
where id = #{id}
select>
<resultMap id="userResultMap" type="User">
<result property="password" column="hashed_password"/>
resultMap>
上面的例子中,id和userName和javaBean中属性一致,被自动映射了。而hashed_password在resultMap中映射了字段和属性,所以在结果中也能取到值。
有三种自动映射等级:
一级缓存是SQLSession级别的缓存,换出的数据只在SQLSession内有效。
二级缓存是mapper级别的缓存,同一个namespace公用这个缓存,所以对SQLSession是共享的
1.第一次执行select完毕会将查到的数据写入SqlSession内的HashMap中缓存起来
2.第二次执行select会从缓存中查数据,如果select相同切传参数一样,那么就能从缓存中返回数据,不用去数据库了,从而提高了效率
注意事项:
二级缓存是mapper级别的缓存。也就是同一个namespace的mapper.xml,当多个SQLSession使用同一个mapper操作数据库,得到的数据会缓存在同一个二级缓存中。
具体流程:
1.当一个sqlseesion执行了一次select后,在关闭此session的时候,会将查询结果缓存到二级缓存
2.当另一个sqlsession执行select时,首先会在他自己的一级缓存中找,如果没找到,就回去二级缓存中找,找到了就返回,就不用去数据库了,从而减少了数据库压力提高了性能
注意事项:
如果SqlSession执行了DML操作(insert、update、delete),并commit了,那么mybatis就会清空当前mapper缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现脏读。
开启二级缓存:
confg.xml
<settings>
<setting name="cacheEnabled" value="true"/>默认是false:关闭二级缓存
<settings>
userMapper.xml
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/> 当前mapper下所有语句开启二级缓存
配置了一个LRU缓存,并每隔60秒刷新,最大存储512个对象,返回的对象是只读。
若想禁用某select语句的二级缓存,添加userCache="false"
。
<select id="getCountByName" parameterType="java.util.Map" resultType="INTEGER" statementType="CALLABLE" useCache="false">
可用的收回策略有:
LRU – 最近最少使用的:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。