大家好,我是王有志。今天给大家带来的是一道来自京东的 MyBatis 面试题:MyBatis的映射器(Mapper.xml)中有哪些常见的元素?
MyBatis 的映射器中提供了 9 个顶级元素,按照功能可以分为 3 类:
- SQL 语句相关的元素:sql 元素,select 元素,insert 元素,update 元素和 delete 元素;
- 参数映射与结果集映射相关元素:parameterMap 元素和 resultMap 元素;
- 二级缓存相关元素:cache 元素和 cache-ref 元素。
其中与参数映射有关的 parameterMap 元素,已经被 MyBatis 官方标记为废弃,因此不建议大家在项目中使用(下文也不会展示 parameterMap 元素的用法)。
除了上面 9 个顶级元素外,MyBatis 的映射器中还提供了非常多的二级元素,常用的有以下 6 个元素:
- include 元素
- if 元素
- foreach 元素
- trim 元素,where 元素和 set 元素
下面我们来逐个解释它们的含义,并展示它们的用法。
sql 元素与 include 元素的使用
sql 元素用于定义可重复使用的 SQL 语句片段。
下面我们使用 sql 元素定义一段 SQL 语句片段,代码如下:
<sql id="Base_Column_List">
item_id, order_id, commodity_id, commodity_price, commodity_count
</sql>
include 元素用于引入由 SQL 元素定义的 SQL 语句片段。
下面我们使用 include 元素引入 SQL 语句片段,代码如下:
<select id="selectOrderItemByOrderId" resultType="com.wyz.entity.OrderItemDO">
select
<include refid="Base_Column_List"/>
from order_item where order_id = #{orderId, jdbcType=INTEGER}
</select>
select 元素,if 元素与 where 元素的使用
select 元素用于定义查询语句;if 元素可以在映射器中实现条件判断语句,与 Java 中的关键字 if 的作用相同;where 元素会自动移除条件子句前缀的“and”和“or”,并在条件子句的开始处插入“where”,如果不存在条件子句,则不会插入“where”。
下面我们使用 select 元素实现查询语句,并且将 where 元素和 if 元素组合使用,实现动态的条件查询子句,代码如下:
<select id="selectByItemIdAndOrderId" resultType="com.wyz.entity.OrderItemDO">
select * from order_item
<where>
<if test="orderItem.itemId != null">
and item_id = #{orderItem.itemId, jdbcType=INTEGER}
</if>
<if test="orderItem.orderId != null">
and order_id = #{orderItem.orderId, jdbcType=INTEGER}
</if>
</where>
</select>
insert 元素的使用
insert 元素用于定义插入语句。
下面我们使用 insert 元素实现插入语句,代码如下:
<insert id="insert" parameterType="com.wyz.entity.OrderItemDO">
insert into order_item (item_id, order_id, commodity_id, commodity_price, commodity_count)
value (#{orderItem.itemId, jdbcType=INTEGER},
#{orderItem.orderId, jdbcType=INTEGER},
#{orderItem.commodityId, jdbcType=INTEGER},
#{orderItem.commodityPrice, jdbcType=DECIMAL},
#{orderItem.commodityCount, jdbcType=INTEGER})
</insert>
update 元素和 set 元素的使用
update 元素用于定义更新语句;set 元素会自动将子句结尾处的“,”移除,并在子句的开始处插入“set”,如果不存在子句,则不会插入“set”。
下面我们使用 update 元素实现更新语句,并使用 set 元素实现动态组装被更新的字段,代码如下:
<update id="updateByItemId" parameterType="com.wyz.entity.OrderItemDO">
update order_item
<set>
<if test="orderItem.commodityId != null">
commodity_id = #{orderItem.commodityId, jdbcType=INTEGER},
</if>
<if test="orderItem.commodityPrice != null">
commodity_price = #{orderItem.commodityPrice, jdbcType=DECIMAL},
</if>
<if test="orderItem.commodityCount != null">
commodity_count = #{orderItem.commodityCount, jdbcType=INTEGER}
</if>
</set>
where item_id = #{orderItem.itemId, jdbcType=INTEGER}
</update>
delete 元素和 trim 元素
delete 元素用于定义删除语句;trim 元素用于处理子句,根据 prefixOverrides 属性和 suffixOverrides 属性的配置移除子句前后缀的字符串,最后根据 prefix 属性和 suffix 属性的配置,在子句的开始处和结尾处插入字符串。在 MyBatis 的源码实现中,处理 trim 元素的 TrimSqlNode 是处理 where 元素的 WhereSqlNode 和处理 set 元素的 SetSqlNode 的父类。
下面我们使用 delete 元素实现删除语句,并使用 trim 元素实现动态的条件语句,代码如下:
<delete id="delete">
delete from order_item
where
<trim prefixOverrides="and">
<if test="orderItem.itemId != null">
and item_id = #{orderItem.itemId, jdbcType=INTEGER}
</if>
<if test="orderItem.orderId != null">
and order_id = #{orderItem.orderId,jdbcType=INTEGER}
</if>
<if test="orderItem.commodityId != null">
and commodity_id = #{orderItem.commodityId, jdbcType=INTEGER}
</if>
<if test="orderItem.commodityPrice != null">
and commodity_price = #{orderItem.commodityPrice, jdbcType=DECIMAL}
</if>
<if test="orderItem.commodityCount != null">
and commodity_count = #{orderItem.commodityCount, jdbcType=INTEGER}
</if>
</trim>
</delete>
Tips:上面的例子并不好,因为这里完全可以使用 where 元素代替 trim 元素,这里只是为了展示 trim 元素的用法。
foreach 元素的使用
foreach 元素提供了在 MyBatis 映射器中遍历集合与字典的能力,foreach 元素定义了 6 个属性:
属性 | 说明 |
---|---|
collection | 传入的集合或字典的名称(即入参名称) |
item | 集合中迭代元素的别名 |
index | 当前元素在集合中的下标(从 0 开始) |
open | 指定开始处的字符串 |
close | 指定结尾处的字符串 |
separator | 指定元素间分割的字符串 |
下面我们使用 foreach 元素,实现在 MyBatis 映射器中遍历集合,代码如下:
<select id="selectByItemIds" resultMap="BaseResultMap">
select * from order_item
where item_id in
<foreach collection="itemIds" item="itemId" separator="," open="(" close=")">
#{itemId, jdbcType=INTEGER}
</foreach>
</select>
resultMap 元素的使用
resultMap 元素用于定义数据库结果集与 Java 对象之间的映射规则,resultMap 元素本身非常复杂,不过在这里我们只展示它最基础的用法。
下面我们使用 resultMap 元素来定义数据库结果集与 Java 对象之间的映射规则,代码如下:
<resultMap id="BaseResultMap" type="com.wyz.entity.OrderItemDO">
<id property="itemId" column="item_id" jdbcType="INTEGER"/>
<result property="orderId" column="order_id" jdbcType="INTEGER"/>
<result property="commodityId" column="commodity_id" jdbcType="INTEGER"/>
<result property="commodityPrice" column="commodity_price" jdbcType="DECIMAL"/>
<result property="commodityCount" column="commodity_count" jdbcType="INTEGER"/>
</resultMap>
我们可以在查询语句中直接使用 resultMap 定义的映射规则,稍微修改下“select 元素,if 元素与 where 元素的使用”中的查询语句,代码如下:
<select id="selectByItemIdAndOrderId" resultMap="BaseResultMap">
select * from order_item
<where>
<if test="orderItem.itemId != null">
and item_id = #{orderItem.itemId, jdbcType=INTEGER}
</if>
<if test="orderItem.orderId != null">
and order_id = #{orderItem.orderId, jdbcType=INTEGER}
</if>
</where>
</select>
Tips:注意观察 select 元素中使用的属性是发生了变化的。
cache 元素和 cache-ref 元素的使用
cache 元素用于开启 MyBatis 的二级缓存(需要配置 mybatis-config.xml 中 cacheEnabled 使用),同时它还起到配置 MyBats 二级缓存的作用。
下面我们使用 cache 元素开启 MyBatis 的二级缓存,并且使用 LRU 淘汰策略,每隔 60 秒定时刷新缓存,代码如下:
<mapper namespace="com.wyz.mapper.OrderItemMapper">
<cache eviction="LRU" flushInterval="60000"/>
</mapper>
cache-ref 元素用于引入定义在其它映射器中的二级缓存。
下面我们使用 cache-ref 元素来引入上面的二级缓存,代码如下:
<cache-ref namespace="com.wyz.mapper.OrderItemMapper"/>