Mybatis动态SQL深度解析与实战:让你的SQL“活”起来

Mybatis动态SQL深度解析与实战:让你的SQL“活”起来

动态SQL是Mybatis最强大、最富有魅力的功能之一。如果说Mybatis的基础功能是让你告别了繁琐的JDBC,那么动态SQL就是让你在面对复杂多变的业务需求时,能够写出优雅、灵活且极易维护的SQL的“神兵利兵”。

本教程将带你领略动态SQL的魔力,从简单的if判断到复杂的循环,让你彻底掌握这项企业级开发必备的核心技能。

Mybatis动态SQL深度解析与实战:让你的SQL“活”起来

引子:当SQL遇到“选择困难症”

想象一下,你正在开发一个电商网站的商品搜索功能。产品经理提出了这样的需求:

用户可以只输入商品名称进行模糊搜索。

用户可以只选择一个价格区间进行筛选。

用户可以只选择一个商品分类。

当然,用户也可以同时输入商品名称、选择价格区间和分类进行组合搜索!

哦对了,用户还可以选择按价格升序或降序排序...

如果为每一种组合都写一个独立的SQL查询方法,你可能会写出findByName、findByPrice、findByNameAndPrice、findByNameAndPriceAndCategory... 你的代码会瞬间爆炸,维护起来简直是噩梦。

这时,你多么希望SQL能像Java代码一样,拥有if-else、switch、for这样的逻辑判断能力,根据不同的输入,智能地“组装”出自己想要的样子。

Mybatis的动态SQL,就是为了解决这个“选择困难症”而生的!它赋予了SQL逻辑判断和动态拼接的能力,让你的SQL从死板的字符串,变成一个能思考、会变形的“智能体”。

第一部分:核心动态SQL标签与基础实践

动态SQL的所有魔法都蕴藏在Mapper.xml文件中的几个核心标签里。我们逐一来看。

1. 项目结构与准备

我们将创建一个新的ProductMapper来演示商品搜索的场景。

项目结构

mybatis-dynamic-sql-demo/

└── src/

└── main/

├── java/

│ └── com/

│ └── example/

│ ├── entity/

│ │ └── Product.java // 商品实体类

│ ├── mapper/

│ │ └── ProductMapper.java // Mapper接口

│ └── test/

│ └── DynamicSqlTest.java // 动态SQL测试类

└── resources/

├── mappers/

│ └── ProductMapper.xml // 【核心】动态SQL在这里

└── mybatis-config.xml

代码准备

Product.java (实体类)

// src/main/java/com/example/entity/Product.java

package com.example.entity;

import lombok.Data;

import java.math.BigDecimal;

@Data

public class Product {

private Integer id;

private String name;

private Integer categoryId;

private BigDecimal price;

}

ProductMapper.java (接口)

// src/main/java/com/example/mapper/ProductMapper.java

package com.example.mapper;

import com.example.entity.Product;

import org.apache.ibatis.annotations.Param;

import java.util.List;

import java.math.BigDecimal;

public interface ProductMapper {

// 这是我们将要实现的动态查询方法

List findProducts(

@Param("name") String name,

@Param("minPrice") BigDecimal minPrice,

@Param("maxPrice") BigDecimal maxPrice

);

// 用于测试标签

int updateProduct(Product product);

// 用于测试标签

List findProductsByIds(List ids);

}

2. if标签:最基础的条件判断

if标签是最常用的动态SQL标签,它的作用是:如果满足test属性中的条件,就将标签内的SQL片段包含进来。

ProductMapper.xml

痛点分析:上面的写法有个小问题。如果所有if条件都不满足,SQL会变成SELECT * FROM product WHERE 1=1,虽然能运行,但WHERE 1=1有点多余。如果第一个if条件满足,SQL会变成WHERE AND ...,这会导致语法错误。为了解决这个问题,where标签登场了。

3. where标签:智能处理WHERE和AND

where标签会自动判断其内部是否有SQL片段被生成。

如果有,它会自动添加一个WHERE关键字,并且智能地去掉第一个AND或OR。

如果没有,它什么也不做。

ProductMapper.xml (优化版)

现在,代码既安全又优雅!

4. choose, when, otherwise:多选一的switch

这组标签类似于Java的switch-case-default,用于实现“多选一”的逻辑。

企业级例子:复杂的排序需求

"如果用户指定了排序方式,就按他说的办;否则,如果商品名称不为空,就按相关度(模拟)排;再否则,就默认按ID排。"

ProductMapper.xml (添加choose逻辑)

第二部分:进阶动态SQL标签与企业级实践

1. set标签:智能处理UPDATE语句

在UPDATE语句中,我们常常只更新传入对象中不为空的字段。如果手动拼接逗号,,会非常麻烦。set标签就是为了解决这个问题。

它会自动添加SET关键字。

它会自动去掉最后一个多余的逗号。

企业级例子:动态更新商品信息

ProductMapper.xml

UPDATE product

name = #{name},

category_id = #{categoryId},

price = #{price},

WHERE id = #{id}

无论你传入的product对象有几个字段不为空,生成的SQL都会是完美的UPDATE语句,没有多余的逗号。

2. foreach标签:循环处理集合

当需要对一个集合(如List, Set, Array)进行循环,并将其用于IN查询或批量插入时,foreach是你的不二之选。

企业级例子:根据ID列表批量查询商品

ProductMapper.xml

foreach标签属性详解:

collection: 要迭代的集合。如果参数是List,默认值是list;如果是Array,默认是array。如果用@Param注解命名了,就用注解的名字。

item: 每次迭代出的元素的变量名。

open: 在循环开始前拼接的字符串。

close: 在循环结束后拼接的字符串。

separator: 每次迭代之间拼接的分隔符。

上面的配置会生成类似 WHERE id IN (1, 2, 3) 这样的SQL。

3. :重用SQL片段

当多个查询有共同的列或条件时,可以把它们抽取成一个可重用的SQL片段。

: 用于定义一个SQL片段。

: 用于在其他地方引用这个SQL片段。

ProductMapper.xml

id, name, category_id, price

这样做极大地提高了代码的复用性和可维护性。当表结构变化需要增减字段时,只需修改一处片段即可。

第三部分:动手测试

现在,我们来编写测试代码,亲手验证动态SQL的威力。

DynamicSqlTest.java (测试类)

// src/main/java/com/example/test/DynamicSqlTest.java

package com.example.test;

import com.example.entity.Product;

import com.example.mapper.ProductMapper;

import org.apache.ibatis.io.Resources;

import org.apache.ibatis.session.SqlSession;

import org.apache.ibatis.session.SqlSessionFactory;

import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;

import java.io.InputStream;

import java.math.BigDecimal;

import java.util.Arrays;

import java.util.List;

public class DynamicSqlTest {

public static void main(String[] args) throws IOException {

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(

Resources.getResourceAsStream("mybatis-config.xml")

);

try (SqlSession session = sqlSessionFactory.openSession(true)) {

ProductMapper mapper = session.getMapper(ProductMapper.class);

System.out.println("--- 测试 ---");

System.out.println("1. 只按名称搜索:");

List products1 = mapper.findProducts("Laptop", null, null);

products1.forEach(System.out::println);

System.out.println("\n2. 只按价格区间搜索:");

List products2 = mapper.findProducts(null, new BigDecimal("1000"), new BigDecimal("5000"));

products2.forEach(System.out::println);

System.out.println("\n3. 组合搜索:");

List products3 = mapper.findProducts("Phone", new BigDecimal("3000"), null);

products3.forEach(System.out::println);

System.out.println("\n--- 测试 ---");

Product productToUpdate = new Product();

productToUpdate.setId(1); // 假设更新ID为1的商品

productToUpdate.setPrice(new BigDecimal("8999.99")); // 只更新价格

int updatedRows = mapper.updateProduct(productToUpdate);

System.out.println("更新了 " + updatedRows + " 行数据。");

System.out.println("\n--- 测试 ---");

List ids = Arrays.asList(1, 3, 5);

List productsByIds = mapper.findProductsByIds(ids);

System.out.println("根据ID列表查询到的商品:");

productsByIds.forEach(System.out::println);

}

}

}

总结:动态SQL是思想,而非仅仅是标签

掌握动态SQL,不仅仅是记住几个标签的用法,更重要的是建立一种“构建SQL”的思想。当你面对一个复杂的查询需求时,你应该思考:

这个SQL的哪些部分是固定不变的?(如 SELECT * FROM table)

哪些部分是根据条件可选的?(如 AND name = ?,用

哪些部分是多选一的?(如排序,用

哪些部分需要循环生成?(如 IN 查询,用

哪些部分可以被重用?(如列列表,用

将需求拆解成这些可组合的SQL片段,再用Mybatis的动态标签将它们优雅地组织起来,这就是动态SQL的艺术。熟练掌握它,你就能在数据访问层的开发中游刃有余,无往不利。

相关推荐

谷歌地图使用教程(PC端,网页版)
bat365bet

谷歌地图使用教程(PC端,网页版)

📅 06-29 👁️ 1556
3种vivo x7 Plus截屏方法
365bet官方网站是多少

3种vivo x7 Plus截屏方法

📅 07-03 👁️ 9362
小米解锁工具使用教程 小米解锁工具使用教程图解
365bet提款要求

小米解锁工具使用教程 小米解锁工具使用教程图解

📅 07-04 👁️ 4019