本文参考mybatis中文官网进行学习总结:http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.htmlhtml
MyBatis 的真正强大在于它的映射语句,这是它的魔力所在。因为它的异常强大,映射器的 XML 文件就显得相对简单。若是拿它跟具备相同功能的 JDBC 代码进行对比,你会当即发现省掉了将近 95% 的代码。MyBatis 为聚焦于 SQL 而构建,以尽量地为你减小麻烦。java
SQL 映射文件只有不多的几个顶级元素(按照应被定义的顺序列出):sql
查询语句是 MyBatis 中最经常使用的元素之一,光能把数据存到数据库中价值并不大,只有还能从新取出来才有用,多数应用也都是查询比修改要频繁。对每一个插入、更新或删除操做,一般间隔多个查询操做。这是 MyBatis 的基本原则之一,也是将焦点和努力放在查询和结果映射的缘由。简单查询的 select 元素是很是简单的。好比:数据库
<resultMap id="BaseResultMap" type="blog"> <id column="bid" property="bid" jdbcType="INTEGER"/> <result column="name" property="name" jdbcType="VARCHAR"/> <result column="author_id" property="authorId" jdbcType="INTEGER"/> </resultMap> <!--****************************************************************************************--> <!--简单查询--> <select id="selectBlogById" resultMap="BaseResultMap" statementType="PREPARED" useCache="false"> select * from blog where bid = #{bid} </select>
#{bid} 这就告诉 MyBatis 建立一个预处理语句(PreparedStatement)参数,在 JDBC 中,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中,就像这样:数组
// 近似的 JDBC 代码,非 MyBatis 代码... String selectBlogById= "SELECT * FROM BLOG WHERE BID=?"; PreparedStatement ps = conn.prepareStatement(selectBlogById); ps.setInt(1,bid);
固然,使用 JDBC 意味着须要更多的代码来提取结果并将它们映射到对象实例中,而这就是 MyBatis 节省你时间的地方。select 元素容许你配置不少属性来配置每条语句的做用细节。缓存
<select id="selectPerson" parameterType="int" parameterMap="deprecated" resultType="hashmap" resultMap="personResultMap" flushCache="false" useCache="true" timeout="10" fetchSize="256" statementType="PREPARED" resultSetType="FORWARD_ONLY">
相关属性描述:mybatis
属性 | 描述 |
---|---|
id | 在命名空间中惟一的标识符,能够被用来引用这条语句。 |
parameterType | 将会传入这条语句的参数类的彻底限定名或别名。这个属性是可选的,由于 MyBatis 能够经过类型处理器(TypeHandler) 推断出具体传入语句的参数,默认值为未设置(unset)。 |
resultType | 从这条语句中返回的指望类型的类的彻底限定名或别名。 注意若是返回的是集合,那应该设置为集合包含的类型,而不是集合自己。可使用 resultType 或 resultMap,但不能同时使用。 |
resultMap | 外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,若是你对其理解透彻,许多复杂映射的情形都能迎刃而解。可使用 resultMap 或 resultType,但不能同时使用。 |
flushCache | 将其设置为 true 后,只要语句被调用,都会致使本地缓存和二级缓存被清空,默认值:false。 |
useCache | 将其设置为 true 后,将会致使本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。 |
timeout | 这个设置是在抛出异常以前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖驱动)。 |
fetchSize | 这是一个给驱动的提示,尝试让驱动程序每次批量返回的结果行数和这个设置值相等。 默认值为未设置(unset)(依赖驱动)。 |
statementType | STATEMENT,PREPARED 或 CALLABLE 中的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
resultSetType | FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖驱动)。 |
databaseId | 若是配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载全部的不带 databaseId 或匹配当前 databaseId 的语句;若是带或者不带的语句都有,则不带的会被忽略。 |
resultOrdered | 这个设置仅针对嵌套结果 select 语句适用:若是为 true,就是假设包含了嵌套结果集或是分组,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的状况。 这就使得在获取嵌套的结果集的时候不至于致使内存不够用。默认值:false。 |
resultSets | 这个设置仅对多结果集的状况适用。它将列出语句执行后返回的结果集并给每一个结果集一个名称,名称是逗号分隔的。 |
数据变动语句 insert,update 和 delete 的实现很是接近:框架
<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">
相关属性描述:dom
属性 | 描述 |
---|---|
id | 命名空间中的惟一标识符,可被用来表明这条语句。 |
parameterType | 将要传入语句的参数的彻底限定类名或别名。这个属性是可选的,由于 MyBatis 能够经过类型处理器推断出具体传入语句的参数,默认值为未设置(unset)。 |
flushCache | 将其设置为 true 后,只要语句被调用,都会致使本地缓存和二级缓存被清空,默认值:true(对于 insert、update 和 delete 语句)。 |
timeout | 这个设置是在抛出异常以前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖驱动)。 |
statementType | STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
useGeneratedKeys | (仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(好比:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。 |
keyProperty | (仅对 insert 和 update 有用)惟一标记一个属性,MyBatis 会经过 getGeneratedKeys 的返回值或者经过 insert 语句的 selectKey 子元素设置它的键值,默认值:未设置(unset)。若是但愿获得多个生成的列,也能够是逗号分隔的属性名称列表。 |
keyColumn | (仅对 insert 和 update 有用)经过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候须要设置。若是但愿使用多个生成的列,也能够设置为逗号分隔的属性名称列表。 |
databaseId | 若是配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载全部的不带 databaseId 或匹配当前 databaseId 的语句;若是带或者不带的语句都有,则不带的会被忽略。 |
下面就是 insert,update 和 delete 语句的示例:ide
<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 了。若是你的数据库还支持多行插入, 你也能够传入一个 Blog数组或集合,并返回自动生成的主键。
<!--批量插入--> <insert id="insertBlogs" useGeneratedKeys="true" keyProperty="bid"> insert into blog (name, author_id) values <foreach item="item" collection="list" separator=","> (#{item.name}, #{item.authorId}) </foreach>
</insert>
这个元素能够被用来定义可重用的 SQL 代码段,这些 SQL 代码能够被包含在其余语句中。它能够(在加载的时候)被静态地设置参数。 在不一样的包含语句中能够设置不一样的值到参数占位符上。mybatis中sql
标签与include
标签进行配合,灵活的查询须要的数据。
<sql id="ref"> bid,name,authorId </sql> <select id="selectbyId" resultMap="BaseResultMap"> select <include refid="ref"/> from blog where bid = #{bid} </select>
sql标签
中id属性对应include标签
中的refid属性。经过include标签将sql片断
和原sql片断进行拼接成一个完成的sql语句进行执行。include标签中还能够用property标签
,用以指定自定义属性。
<select id="selectbyId" resultMap="BaseResultMap"> select <include refid="ref"> <property name="abc" value="bid"/> </include> from blog where bid = #{bid} </select>
此时,能够在sql标签
中取出对应设置的自定义属性中的值,例如接上代码例子:
<sql id="ref"> ${abc},name,authorId </sql> <select id="selectbyId" resultMap="BaseResultMap"> select <include refid="ref"> <property name="abc" value="bid"/> </include> from blog where bid = #{bid} </select>
在sql
标签中经过${}
取出对应include标签中
设置的属性值。
关联(association)元素处理“有一个”类型的关系。 好比,在咱们的示例中,一个博客有一个用户。关联结果映射和其它类型的映射工做方式差很少。 你须要指定目标属性名以及属性的javaType(不少时候 MyBatis 能够本身推断出来),在必要的状况下你还能够设置 JDBC 类型,若是你想覆盖获取结果值的过程,还能够设置类型处理器。关联的不一样之处是,你须要告诉 MyBatis 如何加载关联。MyBatis 有两种不一样的方式加载关联:
<!-- 另外一种联合查询(一对一)的实现,可是这种方式有“N+1”的问题 --> <resultMap id="BlogWithAuthorQueryMap" type="com.wuzz.demo.associate.BlogAndAuthor"> <id column="bid" property="bid" jdbcType="INTEGER"/> <result column="name" property="name" jdbcType="VARCHAR"/> <association property="author" javaType="com.wuzz.demo.entity.Author" column="author_id" select="selectAuthor"/> </resultMap> <!-- 嵌套查询 --> <select id="selectAuthor" parameterType="int" resultType="com.wuzz.demo.entity.Author"> select author_id authorId, author_name authorName from author where author_id = #{authorId} </select> <!-- 根据文章查询做者,一对一,嵌套查询,存在N+1问题,可经过开启延迟加载解决 --> <select id="selectBlogWithAuthorQuery" resultMap="BlogWithAuthorQueryMap" > select b.bid, b.name, b.author_id, a.author_id , a.author_name from blog b left join author a on b.author_id=a.author_id where b.bid = #{bid, jdbcType=INTEGER} </select>
就是这么简单。咱们有两个 select 查询语句:一个用来加载博客(Blog),另一个用来加载做者(Author),并且博客的结果映射描述了应该使用 selectAuthor 语句加载它的 author 属性。其它全部的属性将会被自动加载,只要它们的列名和属性名相匹配。这种方式虽然很简单,但在大型数据集或大型数据表上表现不佳。这个问题被称为“N+1 查询问题”。 归纳地讲,N+1 查询问题是这样子的:
这个问题会致使成百上千的 SQL 语句被执行。有时候,咱们不但愿产生这样的后果。MyBatis 可以对这样的查询进行延迟加载,所以能够将大量语句同时运行的开销分散开来。mybatis.configuration.lazy-loading-enabled=true 能够开启延时加载 mybatis.configuration.aggressive-lazy-loading=true 能够指定哪些方法调用查询, 然而,若是你加载记录列表以后马上就遍历列表以获取嵌套的数据,就会触发全部的延迟加载查询,性能可能会变得很糟糕。因此还有另一种方法。
<!-- 根据文章查询做者,一对一查询的结果,嵌套查询 --> <resultMap id="BlogWithAuthorResultMap" type="com.wuzz.demo.associate.BlogAndAuthor"> <id column="bid" property="bid" jdbcType="INTEGER"/> <result column="name" property="name" jdbcType="VARCHAR"/> <!-- 联合查询,将author的属性映射到ResultMap --> <association property="author" javaType="com.wuzz.demo.entity.Author"> <id column="author_id" property="authorId"/> <result column="author_name" property="authorName"/> </association> </resultMap> <!-- 根据文章查询做者,一对一,嵌套结果,无N+1问题 --> <select id="selectBlogWithAuthorResult" resultMap="BlogWithAuthorResultMap" > select b.bid, b.name, b.author_id, a.author_id , a.author_name from blog b,author a where b.author_id=a.author_id and b.bid = #{bid, jdbcType=INTEGER} </select>
查询文章带评论的结果(一对多)映射:
<!-- 查询文章带评论的结果(一对多) --> <resultMap id="BlogWithCommentMap" type="com.wuzz.demo.associate.BlogAndComment" extends="BaseResultMap" > <collection property="comment" ofType="com.wuzz.demo.entity.Comment"> <id column="comment_id" property="commentId" /> <result column="content" property="content" /> <result column="bid" property="bid" /> </collection> </resultMap> <!-- 根据文章查询评论,一对多 --> <select id="selectBlogWithCommentById" resultMap="BlogWithCommentMap" > select b.bid, b.name, b.author_id , c.comment_id , c.content,c.bid from blog b, comment c where b.bid = c.bid and b.bid = #{bid} </select>
按做者查询文章评论的结果(多对多):
<!-- 按做者查询文章评论的结果(多对多) --> <resultMap id="AuthorWithBlogMap" type="com.wuzz.demo.associate.AuthorAndBlog" > <id column="author_id" property="authorId" jdbcType="INTEGER"/> <result column="author_name" property="authorName" jdbcType="VARCHAR"/> <collection property="blog" ofType="com.wuzz.demo.associate.BlogAndComment"> <id column="bid" property="bid" /> <result column="name" property="name" /> <result column="author_id" property="authorId" /> <collection property="comment" ofType="com.wuzz.demo.entity.Comment"> <id column="comment_id" property="commentId" /> <result column="content" property="content" /> <result column="bid" property="bid" /> </collection> </collection> </resultMap> <!-- 根据做者文章评论,多对多 --> <select id="selectAuthorWithBlog" resultMap="AuthorWithBlogMap" > select b.bid, b.name, a.author_id , a.author_name , c.comment_id , c.content,c.bid from blog b, author a, comment c where b.author_id = a.author_id and b.bid = c.bid </select>
MyBatis 的强大特性之一即是它的动态 SQL。若是你有使用 JDBC 或其它相似框架的经验,你就能体会到根据不一样条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性能够完全摆脱这种痛苦。虽然在之前使用动态 SQL 并不是一件易事,但正是 MyBatis 提供了能够被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。动态 SQL 元素和 JSTL 或基于相似 XML 的文本处理器类似。在 MyBatis 以前的版本中,有不少元素须要花时间了解。MyBatis 3 大大精简了元素种类,如今只需学习原来一半的元素即可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。
其中 choose再实际开发中应用的较少,咱们这里就其余3个标签进行测试
<!--动态sql--> <select id="selectBlogById2" resultMap="BaseResultMap" statementType="PREPARED" useCache="false" parameterType="blog"> select * from blog <trim prefix="WHERE" prefixOverrides="AND |OR "> <if test="bid != null and bid !='' "> bid = #{bid} </if> <if test="name != null and name !='' "> AND name = #{name} </if> <if test="authorId != null and authorId != ''"> AND author_id = #{author_id} </if> </trim> </select>
foreach:动态 SQL 的另一个经常使用的操做需求是对一个集合进行遍历,一般是在构建 IN 条件语句的时候。好比:
<select id="selectPostIn" resultType="domain.blog.Post"> SELECT * FROM POST P WHERE ID in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>