引言
在復雜業務場景中,SQL查詢往往需要動態拼接條件、復用代碼片段,并支持批量操作。MyBatis的動態SQL功能提供了強大的解決方案,本文將深入解析<choose>
條件分支、<sql>
片段復用、批量操作優化等核心技巧,助你寫出高效、可維護的SQL映射。
一、條件分支:choose / when / otherwise標簽
1.1 場景說明
假設需要實現一個商品查詢接口,支持以下條件組合:
- 按名稱模糊查詢
- 按價格區間查詢
- 按狀態精確查詢
- 若無條件則返回所有商品
1.2 動態SQL實現
<select id="selectGoods" resultType="Goods">SELECT * FROM goods<where><choose><when test="name != null">AND name LIKE CONCAT('%', #{name}, '%')</when><when test="minPrice != null and maxPrice != null">AND price BETWEEN #{minPrice} AND #{maxPrice}</when><when test="status != null">AND status = #{status}</when><otherwise>AND is_deleted = 0 <!-- 默認未刪除 --></otherwise></choose></where>
</select>
1.3 執行流程解析
<choose>
標簽內按順序匹配第一個滿足的<when>
條件- 若所有
<when>
均不滿足,則執行<otherwise>
<where>
標簽自動處理前綴AND/OR及空條件
二、SQL片段復用:sql標簽與include標簽
2.1 場景說明
多個查詢需要復用以下內容:
- 基礎字段列表(id, name, price)
- 公共過濾條件(未刪除)
2.2 定義與引用
<!-- 定義SQL片段 -->
<sql id="Base_Column_List">id, name, price, status, create_time
</sql><sql id="Common_Where">AND is_deleted = 0
</sql><!-- 引用片段 -->
<select id="selectGoodsList" resultType="Goods">SELECT <include refid="Base_Column_List"/>FROM goods<where><include refid="Common_Where"/><if test="categoryId != null">AND category_id = #{categoryId}</if></where>
</select>
2.3 進階用法:帶參數的SQL片段
<sql id="Price_Filter"><if test="minPrice != null">AND price >= #{minPrice}</if><if test="maxPrice != null">AND price <= #{maxPrice}</if>
</sql><!-- 使用時 -->
<select id="selectByPrice" resultType="Goods">SELECT * FROM goods<where><include refid="Common_Where"/><include refid="Price_Filter"/></where>
</select>
三、批量操作優化
3.1 批量插入優化
傳統單條插入(低效)
<insert id="insertGoods" parameterType="Goods">INSERT INTO goods (name, price) VALUES (#{name}, #{price})
</insert>
Java調用:
for (Goods goods : list) {goodsMapper.insertGoods(goods);
}
批量插入(高效)
<insert id="batchInsert" parameterType="java.util.List">INSERT INTO goods (name, price) VALUES<foreach collection="list" item="item" separator=",">(#{item.name}, #{item.price})</foreach>
</insert>
3.2 批量更新優化
<update id="batchUpdatePrice" parameterType="java.util.List"><foreach collection="list" item="item" separator=";">UPDATE goodsSET price = #{item.newPrice}WHERE id = #{item.id}</foreach>
</update>
3.3 性能關鍵配置
在JDBC URL中添加批處理參數:
jdbc.url=jdbc:mysql://localhost:3306/mydb?rewriteBatchedStatements=true
MyBatis全局配置啟用批量模式:
<settings><setting name="defaultExecutorType" value="BATCH"/>
</settings>
四、性能優化建議
-
減少動態SQL嵌套:
- 避免在
<foreach>
中嵌套其他動態標簽 - 復雜邏輯優先在Java層處理
- 避免在
-
合理使用緩存:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
-
批量操作最佳實踐:
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {GoodsMapper mapper = session.getMapper(GoodsMapper.class);for (Goods goods : list) {mapper.update(goods);}session.commit(); }
五、完整示例代碼
// GoodsMapper.java
public interface GoodsMapper {List<Goods> selectGoods(@Param("name") String name,@Param("minPrice") BigDecimal minPrice,@Param("maxPrice") BigDecimal maxPrice,@Param("status") Integer status);int batchInsert(@Param("list") List<Goods> goodsList);
}
<!-- GoodsMapper.xml -->
<mapper namespace="com.example.mapper.GoodsMapper"><sql id="Base_Column_List">id, name, price, status, create_time</sql><select id="selectGoods" resultType="Goods">SELECT <include refid="Base_Column_List"/>FROM goods<where><choose><when test="name != null">name LIKE CONCAT('%', #{name}, '%')</when><when test="minPrice != null and maxPrice != null">price BETWEEN #{minPrice} AND #{maxPrice}</when><otherwise>status = 1</otherwise></choose></where></select><insert id="batchInsert" parameterType="list">INSERT INTO goods (name, price) VALUES<foreach collection="list" item="item" separator=",">(#{item.name}, #{item.price})</foreach></insert>
</mapper>
六、總結
技術點 | 適用場景 | 性能影響 | 最佳實踐 |
---|---|---|---|
<choose> | 多條件分支選擇 | 低 | 優先處理高頻條件 |
<sql> 復用 | 字段/條件復用 | 中 | 避免過度抽象 |
批量插入 | 大數據量寫入 | 高 | 配合JDBC批處理參數 |
動態SQL緩存 | 重復執行的動態查詢 | 高 | 設置合理的flushInterval |
通過靈活運用MyBatis的動態SQL特性,可顯著提升復雜查詢場景的開發效率和運行性能。實際開發中需根據數據量、查詢復雜度、并發量等因素綜合選擇優化策略。
mybatis基礎專欄就結束啦,期待下我下專欄mybatis進階,感情到一定階段了,可以深入了解一下了!🤔