这是我参与2022首次更文挑战的第28天,活动详情查看:2022首次更文挑战」
MyBatis 作为一款 ORM 框架,封装了 JDBC 参数配置和获取结果等一系列操作,开发者可以通过简单的注解和容器配置实现数据库操作。在 SQL 语句上,MyBatis 并没有限制自定义操作,通过使用相关标签可实现自定义 SQL 语句操作数据。
MyBatis 可以通过在 Mapper 的 XML 配置文件中自定义 SQL,并通过关联关系实现 Mapper 调用时动态调用 SQL 语句, Mapper 配置文件中使用 mapper
标签定义内容,并需要指定与 Mapper 对应的命名空间。
1. SQL 定义标签
1.1 select
select 标签对应查询语句,其中定义的内容最终会被转义成为 SQL 语句执行。select 标签提供定义查询语句的功能,定义标签时可以同时指定相关属性信息:
- id,标签的唯一标识 id,该 id 值要与 Mapper 接口中定义的方法名相同
- parameterType,可选,定义查询时的传参类型,可以指定基本类型或自定义类的全路径名
- resultType,标签对应查询语句最终返回数据的类型,可以指定为基本类型或自定义类的全路径名,如果返回结果为多条数据的集合,需要指定集合中数据的类型
- resultMap,配置文件中自定义的 resultMap 的 id 值,常用于数据返回结果列名与队形属性名不一致时,使用 resultMap 标签定义对应关系,此时返回结果为对应的 resultMap
- fetchsize,默认不设置,可以设置开启 MyBatis 流式查询,定义每次查询返回数据条数,常用于大量数据查询时减少内存消耗
- useCache,查询时默认为true,即该查询语句的结果回被二级缓存
- flushCache,默认为false,设置执行该查询时是否刷新缓存
- statymentType,设置 MyBatis 使用的 statement,STATEMENT、PREPARED、CALLABLE,默认为PREPARED
<select id="selectUserById" parameterType = "int" resultType="com.shone.entity.User" fetchSize="1000">
select id,name from user where id =#{id}
</select>
复制代码
- 如果查询时需要指定参数,MySQl 中使用 where 跟参数条件,MyBatis 中使用 where 标签定义
1.2 insert
insert 标签的语法与 select 标签基本一致,但不同的是,insert 标签会将数据写入到数据表中,因此需要注意的地方有:
- 插入时指定字段对应的数据值,MyBatis 中提供了 set 标签来定义赋值语句
- insert 标签不需要指定返回类型,默认返回影响的数据行数
- 如果自增由数据表处理,而在业务逻辑想要应的自增 id 值,可以使用 useGeneratedKeys 和 keyProperty 属性指定
- useGeneratedKeys,insert 和 update 标签中可用,设置MyBatis操作数据后是否获取对应数据的主键信息,默认为false
- keyProperty,insert 和 update 标签中可用,useGeneratedKeys=true 后,使用 keyProperty 指定哪些列名对应的值会返回,多个列使用
,
间隔
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id" parameterType="User"> insert into user(name,phone,address) values(#{name},#{phone},#{address})
</insert>
复制代码
对于 insert 标签,还可以在插入前后使用 selectKey 标签对数据进行操作,如
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id" parameterType="User">
<!--插入数据之前,获取自定义id值-->
<selectKey keyProperty="id" order="BEFORE" resultType="String">
select uuid()
</selectKey>
<!--插入数据语句-->
insert into user(name,phone,address) values(#{name},#{phone},#{address})
<!--查询新增数据对应的id-->
<selectKey resultClass="long" keyProperty="id" order="after">
SELECT LAST_INSERT_ID()
</selectKey>
</insert>
复制代码
1.3 update 和 delete
更新和删除操作一般都是根据主键或其他条件来筛选数据后进行更新/删除,与查询/插入使用方法相似
- 查询时使用 where 标签定义条件
- 修改时使用 set 标签定义赋值
- 更新和删除标签中不需要定义返回类型,默认返回操作数据的行数
2. SQL 格式化
2.1 where
2.1.1 where 使用
定义 SQL 语句时,对于可能存在无条件查询的情况,直接使用 where 1=1 可能并不那么友好,因此可以使用 where 标签配合 if 条件定义 SQL 语句,where 会根据其中的条件动态的生成合适的查询条件。
- where 元素会在至少有一个子元素的条件返回SQL语句的情况下采取插入 where 子句
- where 标签中使用多个 if 标签动态增加条件时,要为每个条件中增加
and/or
关键字 - where 标签会过滤掉条件语句中的第一个 and 或 or 关键字
2.1.2 标签括号冲突解决
Mybatis 配置文件是使用标签包含的内容,因此对于查询条件中的小于号<
与标签左括号冲突,此时可以使用 MyBatis 提供的转义符号来避免无法识别报错。
- 可以使用指定符号来替换对应的冲突字符
- 原符号:
< <= > >= & ' "
- 对应替换符号:
< <= > >= & ' "
- 原符号:
<!-- 大于等于 -->
<where>
create_date_time >= #{startTime} and create_date_time <= #{endTime}
</where>
复制代码
- 使用包含字符串
- 可以在
<![CDATA[ ]]>
包含冲突内容,并在执行时转义,如大于等于:<![CDATA[ >= ]]>
- 可以在
<where>
create_date_time <![CDATA[ >= ]]> #{startTime} and create_date_time <![CDATA[ <= ]]> #{endTime}
</where>
复制代码
2.2 set
set 标签与 where 类似,但是 set 仅用于 insert 和 update 标签中,用于对数据表字段设置新的数据。 在使用 if 标签动态的为属性赋值时,如果第一个或最后一个条件不满足,最终生成的 SQL 语句可能会因为 ,
不匹配而报错,使用 set 标签就可以解决该问题。
- 如果 set 标签中没有条件满足,则不会更新数据
<update id="modifyUser" parameterType="com.shone.entity.User">
update user
<set>
<if test="name != null and name != ''"> name = #{name}, </if>
<if test="phone != null and phone != ''"> phone = #{phone}, </if>
</set>
where id=#{id}
</update>
复制代码
2.3 trim
where 标签或者 set 标签都可以根据条件的满足情况动态去除不需要的字符,如 where 中的 and/or
和 set 中的 ,
,而 trim 标签则提供了更强大的功能,可以自定义多个条件中增加或删除指定的字符内容。 trim 标签的属性定义如下:
- prefix:增加前缀内容
- prefixOverrides:去掉第一个标记的字符
- suffix:增加后缀内容
- suffixOverrdes:去掉最后标记的字符
trim 标签可以根据定义实现 where 和 set 标签的所有功能
<!--where标签功能-->
<trim prefix="WHERE" prefixOverrides="AND | OR ">
<if test="name != null and name != ''">and name = #{name}</if>
<if test="phone != null and phone != ''">and phone = #{phone}</if>
</trim>
<!--set标签功能-->
<trim prefix="set" prefixOverrides=",">
<if test="name != null and name != ''">name = #{name}, </if>
<if test="phone != null and phone != ''">phone = #{phone}, </if>
</trim>
复制代码
3. 属性定义
3.1 sql 和 include
MyBatis 的 Mapper 配置文件中,定义 SQL 语句时不可以避免存在重复语句定义,可以使用 MyBatis 提供的 sql 标签来抽取重复的语句内容。
<!--抽取定义 SQL 代码片段-->
<sql id="repeatSqlColumn">id,name,age,gender</sql>
<sql id="repeatSqlCondition">where id=#{id}</sql>
复制代码
对于定义的 sql 标签,可以作为属性使用,在需要的位置通过 include 标签引入即可。
<select id="selectUserById" parameterType="int" resultType="com.shone.User">
select
<include refid="repeatSqlColumn"></include>
from user
<include refid="repeatSqlCondition"></include>
</select>
复制代码
3.2 resultMap
sql 标签用于抽取定义普通的 SQL 片段,而 resultMap 可用来定义 JavaBean 实体类与查询返回字段的关联关系。
如果接收数据的 JavaBean 中的属性与数据列名称一致,则可以直接将数据赋值给 Bean 对象,如果不一致,则需要关联对应,否则不能接收到对应属性值。
<resultMap id="userResultMap" type="com.shone.User">
<id property="userId" column="id" />
<result property="userName" column="name"/>
<result property="password" column="password"/>
</resultMap>
复制代码
- 标签中 type 指向引用的 JavaBean 类,通过 Bean 属性与数据表列关联
- 如此之后,在查询标签的返回类型中则需要指定 resultMap 为当前 id 值
<select id="selectUserById" parameterType="int" resultMap="userResultMap">
select id, name, password
from user
where id=#{id}
</select>
复制代码
4. 动态条件
4.1 if
if 标签是 MyBatis 中经常使用的条件标签,也是动态 SQL 中必不可少的组成部分,通过 if 标签中条件逻辑,可以动态拼接最终执行的 SQL 语句。
if 标签中使用 test 属性来指定判断逻辑,判断时可以直接引用参数内容,如果传入参数类型为 JavaBean,则可以直接使用其中的属性值。
- if 标签通常会配合 where 、set 标签共同使用,也可以单独使用
- test 指定判断逻辑时,对于
Integer
的判断,mybatis会默认把0变成 '' - 对于集合类型,则需要判断其元素数量 list.size()
<!--插入和更新时赋值-->
<set>
<if test="name != null and name != ''">user.name=#{name},</if>
</set>
<!--作为查询条件-->
<where>
<if test="name != null and name != ''"> and user.name=#{name}</if>
</where>
<!--根据条件拼接分页-->
<if test="limit != null and limit != ''"> limit 0,limit=#{limit}</if>
复制代码
4.2 choose
choose 标签用于条件判断时通常与 when、otherwise 同时使用,类似 Java 中的 switch-case-default 语法。
- 同样是使用 test 属性值来作为条件判断内容
<select id="selectUser" resultType="com.shone.entity.User">
SELECT id, name FROM user
<where>
<choose>
<when test="id != null and test.trim() != '' ">
id=#{id}
</when>
<when test="name != null and name.trim() != '' ">
name=#{name}
</when>
<otherwise>
password="***"
</otherwise>
</choose>
</where>
</select>
复制代码
4.3 foreach
4.3.1 foreach 使用
foreach 标签是 MyBatis 中用于循环拼接动态 SQL 语句的标签语法,使用时包括如下参数:
- collection:必须指定的属性,在不同情况下使用不同值
- 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
- 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
- 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map或者Object,此时collection属性值为map或bean中对应的集合属性
- item:表示集合中每一个元素进行迭代时的别名
- index:指定一个名字,用于表示在迭代过程中,每次迭代到的位置
- open:表示语句以什么内容开始,会拼接到最终sql的前面
- separator:表示在每次进行迭代之间以什么符号作为分隔符
- close:表示以什么内容结束,会拼接到sql语句的后面
foreach 经常用于动态的拼接 SQL 中的 in 语句,通过传入一个集合数据,获取集合中的元素作为 in 的数据源。
<!--根据id集合查询用户信息列表,传入id集合-->
<select id="selectUserByIdList" resultType="com.shone.entity.User" parameterType="list">
select id,name,phone from user
<where>
id in
<foreach item="item" collection="list" separator="," open="(" close=")" index="">
#{item}
</foreach>
</where>
</select>
复制代码
4.3.2 foreach 实现批量新增
foreach 作为循环拼接 SQL 的标签,不仅可以拼接查询条件,还可以拼接插入语句,在对集合数据进行插入时,也可以使用 foreach 完成。
<insert>
insert user VALUES
<foreach collection="list" item="item" index="index" separator=",">
(
#{item.name},
#{item.phone}
)
</foreach>
</insert>
复制代码