语句映射 select select 拥有很多属性,以下是常用属性:
id:命名空间中唯一标识符,可用于被引用。
parameterType:将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认为未设置(unset)。
resultType:期望从这条语句中返回结果的类全限定名或别名。 如果返回集合,那么应设置为集合中元素的类型。 resultType 和 resultMap 之间只能选用一个。
flushCache:如果开启,那么语句被调用时会清空本地缓存和二级缓存。默认为 false。
useCache:如果开启,那么语句调用后返回的结果会缓存存到二级缓存,默认为 true。
timeout:在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
查询某个人的信息:
1 2 3 <select id ="selectPerson" parameterType ="int" resultType ="hashmap" > SELECT * FROM person WHERE id = #{id} </select >
insert, update, delete 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <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 >
插入后获得生成的主键 如果数据库支持自动生成主键的字段,那么可以设置 useGeneratedKeys="true"
,然后再把 keyProperty
设置为目标属性。 例如,如果上面的 Author 表已经在 id 列上使用了自动生成,那么语句可以修改为:
1 2 3 4 5 <insert id ="insertAuthor" useGeneratedKeys ="true" keyProperty ="id" > insert into Author (username,password,email,bio) values (#{username},#{password},#{email},#{bio}) </insert >
如果数据库支持多行插入,也可以返回自动生成的主键:
1 2 3 4 5 6 7 <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 >
动态SQL MyBatis 3 基于 OGNL 的表达式编写动态 SQL。
if if
代表根据条件拼接语句。
例如,根据标题和作者名查找活跃状态的博客,当test
为真就会拼接对应if
元素里的语句:
1 2 3 4 5 6 7 8 9 10 <select id ="findActiveBlogLike" resultType ="Blog" > SELECT * FROM BLOG WHERE state = 'ACTIVE' <if test ="title != null" > AND title like #{title} </if > <if test ="author != null and author.name != null" > AND author_name like #{author.name} </if > </select >
choose, when, otherwise choose
元素代表从多个条件中选择一个使用。
例如,根据标题查找活跃状态的博客,如果标题为空那么根据作者名查找,如果都为空,那么查找精选的博客:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <select id ="findActiveBlogLike" resultType ="Blog" > SELECT * FROM BLOG WHERE state = 'ACTIVE' <choose > <when test ="title != null" > AND title like #{title} </when > <when test ="author != null and author.name != null" > AND author_name like #{author.name} </when > <otherwise > AND featured = 1 </otherwise > </choose > </select >
trim、where、set where
元素只会在子元素返回任何内容 的情况下才插入 WHERE 子句。若子句以AND
或OR
开头,where
元素也会去除它。
set
元素可以用于动态包含需要更新的列,并删掉额外的逗号。
例如,更新作者信息:
1 2 3 4 5 6 7 8 9 10 <update id ="updateAuthorIfNecessary" > update Author <set > <if test ="username != null" > username=#{username},</if > <if test ="password != null" > password=#{password},</if > <if test ="email != null" > email=#{email},</if > <if test ="bio != null" > bio=#{bio}</if > </set > where id=#{id} </update >
trim
元素用来“修剪”拼接的语句。它有如下属性:
prefix:加上前缀
suffix:加上后缀
preffixOverrides:去除指定的前缀
suffixOverrides:去除指定的后缀
例如,和where
元素等价的自定义trim
元素为:
1 2 3 <trim prefix ="WHERE" prefixOverrides ="AND |OR " > ... </trim >
例如,和set
元素等价的自定义trim
元素为:
1 2 3 <trim prefix ="SET" suffixOverrides ="," > ... </trim >
例如,向班级信息表中插入非空数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <insert id ="insert" parameterType ="com.morning.all.entity.ClassInfo" > INSERT INTO tb_class <trim prefix ="(" suffix =")" suffixOverrides ="," > <if test ="className != null" > class_name, </if > <if test ="classAddress != null" > class_address, </if > </trim > VALUES <trim prefix ="(" suffix =")" suffixOverrides ="," > <if test ="className != null" > #{className, jdbcType=VARCHAR}, </if > <if test ="classAddress != null" > #{classAddress, jdbcType=VARCHAR}, </if > </trim > </insert >
foreach foreach
元素用于对集合进行遍历,常用于构建in
条件语句或者批量操作语句。它有如下属性:
collection:迭代集合的名称,比如list
,array
,set
,也可用@Param
注解指定名称;
item:本次迭代获取的元素,若collection
为map
,则代表键值对的值;
open:语句拼接的前缀;
close:语句拼接的后缀;
separator:每次迭代后给 sql 语句附加上的指定的字符,并且不会错误添加多余的分隔符;
index:本次迭代获取的元素的序号,若collection
为map
,则代表键值对的键。
例如,遍历指定 ID 集合对应的发布文章:
1 2 3 4 5 6 7 8 9 10 <select id ="selectPostIn" resultType ="Post" > SELECT * FROM post <where > <foreach item ="item" index ="index" collection ="list" open ="id IN (" separator ="," close =")" nullable ="true" > #{item} </foreach > </where > </select >
例如,批量插入用户信息:
1 2 3 4 5 6 7 <insert id ="insertUsers" > INSERT INTO t_user(username,age,score) VALUES <foreach collection ="users" item ="user" separator ="," > (#{user.username}, #{user.age}, #{user.score}) </foreach > </insert >
嵌套结果映射 联表查询 集合的嵌套结果映射(一对多) 一个博客可以对应多个文章:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <select id ="selectBlog" resultMap ="blogResult" > select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, P.id as post_id, P.subject as post_subject, P.body as post_body, from Blog B left outer join Post P on B.id = P.blog_id where B.id = #{id} </select > <resultMap id ="blogResult" type ="Blog" > <id property ="id" column ="blog_id" /> <result property ="title" column ="blog_title" /> <collection property ="posts" ofType ="Post" > <id property ="id" column ="post_id" /> <result property ="subject" column ="post_subject" /> <result property ="body" column ="post_body" /> </collection > </resultMap >
将上面的结果映射改成可重用的等价形式:
1 2 3 4 5 6 7 8 9 10 11 <resultMap id ="blogResult" type ="Blog" > <id property ="id" column ="blog_id" /> <result property ="title" column ="blog_title" /> <collection property ="posts" ofType ="Post" resultMap ="blogPostResult" columnPrefix ="post_" /> </resultMap > <resultMap id ="blogPostResult" type ="Post" > <id property ="id" column ="id" /> <result property ="subject" column ="subject" /> <result property ="body" column ="body" /> </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 <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 >
分步查询 分步查询在大型数据集或大型数据表上表现不佳。这个问题被称为“N+1 查询问题”。 概括地讲,N+1 查询问题是这样子的:
你执行了一个单独的 SQL 语句来获取结果的一个列表(就是“+1”)。
对列表返回的每条记录,你执行一个 select 查询语句来为每条记录加载详细信息(就是“N”)。
这个问题会导致成百上千的 SQL 语句被执行。有时候,我们不希望产生这样的后果。
好消息是,MyBatis 能够对这样的查询进行延迟加载,因此可以将大量语句同时运行的开销分散开来。 然而,如果你加载记录列表之后立刻就遍历列表以获取嵌套的数据,就会触发所有的延迟加载查询,性能可能会变得很糟糕。
集合的嵌套结果映射(一对多) 一个商品可以对应多个商品详情,在商品实体类中有一个商品详情的集合字段,结果映射如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <resultMap id ="rmGoods1" type ="Goods" > <id column ="goods_id" property ="goodsId" > </id > <collection property ="goodsDetails" select ="goodsDetail.selectByGoodsId" column ="goods_id" /> </resultMap > <select id ="selectOneToMany" resultMap ="rmGoods1" > select * from t_goods limit 0,10 </select >
关联的嵌套结果映射(多对一或一对一) 商品详情对应一个商品,在商品详情实体类中有一个商品ID字段,结果映射如下:
1 2 3 4 5 6 7 8 <resultMap id ="rmGoodsDetail" type ="GoodsDetail" > <id column ="gd_id" property ="gdId" /> <result column ="goods_id" property ="goodsId" /> <association property ="goods" select ="goods.selectById" column ="goods_id" > </association > </resultMap > <select id ="selectManyToOne" resultMap ="rmGoodsDetail" > select * from t_goods_detail limit 0,20 </select >