当前位置: 首页 > news >正文

mybatis基本使用

mybatis基本使用流程

    • 基本使用
    • 封装工具类
    • mybatis配置
      • 开启日志和驼峰映射
    • `#{}`与`${}`区别
      • `#{}`(预编译参数)
      • `${}`(字符串替换)
      • 总结区别
    • mybatis接收参数语法
      • 单个参数
      • 多个参数
      • map作为参数
      • 实体类作为参数
    • resultMap
      • 如何自定义resultMap?
      • 复杂场景映射
        • 场景 1:一对一关联(association)
        • 场景 2:一对多关联(collection)
      • 高级映射特性
        • 继承 resultMap
        • 多层嵌套示例
    • 核心动态 SQL 标签
      • SQL 片段基本概念
      • `<if>` 标签
      • `<choose>`, `<when>`, `<otherwise>` 标签
      • `<where>` 标签
      • `<set>` 标签
      • `<trim>` 标签
      • `<foreach>` 标签
      • `<bind>` 标签
    • 特殊字符
      • 转义字符
        • 基础比较运算
        • 复杂条件组合
        • 在动态 SQL 的 test 属性中使用
      • CDATA
        • 基础使用 CDATA
        • 比较运算
        • 复杂查询条件
        • 在 SQL 片段中使用 CDATA
      • 适用场景对比

基本使用

    @Testpublic void user() throws Exception {// 将配置文件转为输入流InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");// 创建SqlSessionFactoryBuilder,作用是根据配置文件,创建SqlSessionFactory工厂SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();// 根据配置文件,创建工程SqlSessionFactory,全局唯一SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);// 工厂创建SqlSession,建立一次会话SqlSession sqlSession = sqlSessionFactory.openSession();// 使用和指定语句的参数和返回值相匹配的接口,映射器实例UserMapper userMapper = sqlSession.getMapper(UserMapper.class);User user = userMapper.selectUserById(1);System.out.println("user = " + user);sqlSession.close();}

封装工具类

  • 通过静态代码块来创建唯一的SqlSessionFactory工厂。
public class SqlSessionUtils {// 提取出来的目的是,全局只需要一个SqlSessionFactory对象实例private static SqlSessionFactory sqlSessionFactory;static {try {// 配置文件流InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");// 创建SqlSessionFactoryBuilderSqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();// 通过配置文件流,创建SqlSessionFactorysqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);// 通过openSession,创建SqlSessionn} catch (IOException e) {throw new RuntimeException(e);}}/*** 手动提交事务* @return sqlSession*/public static SqlSession openSession() {return sqlSessionFactory.openSession();}/*** 增删改语句,需要手动提交事务,我们在获取sqlSession时,设置自动提交事务* @return sqlSession*/public static SqlSession openAutoCommitSession(){return sqlSessionFactory.openSession(true);}
}

mybatis配置

开启日志和驼峰映射

官网地址:https://mybatis.org/mybatis-3/zh_CN/configuration.html#settings

  • 日志输出: <setting name="logImpl" value="STDOUT_LOGGING"/>

  • 驼峰映射: <setting name="mapUnderscoreToCamelCase" value="true"/>

#{}${}区别

#{}(预编译参数)

  • 安全性:使用#{}会创建预编译语句(PreparedStatement),参数值会被安全地替换,不会导致SQL注入攻击。
  • 性能:由于是预编译,可以重复利用执行计划,提高性能。
  • 用法:适用于传入参数值,例如条件值、插入值等。
  • 示例
    <select id="selectUserById" parameterType="int" resultType="User">SELECT * FROM user WHERE id = #{id}</select>

实际执行时,SQL语句会被编译为:SELECT * FROM user WHERE id = ?,然后通过PreparedStatement的set方法设置参数值。

${}(字符串替换)

  • 安全性:使用${}会直接进行字符串替换,不会对参数值进行转义,存在SQL注入风险。
  • 性能:不会预编译,每次都可能需要重新编译SQL,性能较低。
  • 用法:适用于动态指定表名、列名等非参数值的地方。如果需要动态指定SQL片段,且无法用#{}替代的情况。
  • 示例
    <select id="selectUserByTable" resultType="User">SELECT * FROM ${tableName} WHERE id = #{id}</select>

这里${tableName}会被直接替换为传入的字符串,比如传入tableName为"user",则SQL为:SELECT * FROM user WHERE id = ?

总结区别

特性#{}${}
安全性高,防止SQL注入低,有SQL注入风险
性能高(预编译)低(直接拼接)
使用场景参数值动态表名、列名等
括号内内容可以是任意值,通常为属性名通常是属性名,且要注意注入问题

  • 绝大部分情况下使用#{},特别是当参数是用户输入时。

  • 只有在需要动态指定表名、列名等无法使用预编译参数的地方才使用${},并且要确保这些值不是来自用户输入,或者已经进行了严格的过滤。


mybatis接收参数语法

单个参数

单个参数:在xml中可以写任意名称


  1. Mapper映射器定义方法。
public interface StudentMapper {Student getStudentById(int id);
}
  1. Mapper.xml编写Sql语句
<mapper namespace="com.maven.mapper.StudentMapper"><select id="getStudentById" resultType="com.maven.pojo.Student" parameterType="int">select*from student where id = #{id}</select>
</mapper>
  1. 运行mybatis,执行Sql语句。
    @Testpublic void getStudentById() throws Exception {InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);// 根据Id,查询学生Student student = mapper.getStudentById(1);System.out.println("user = " + student);List<Student> allStudents = mapper.getAllStudents();System.out.println("allStudents = " + allStudents);sqlSession.close();}
  1. 结果返回:
Opening JDBC Connection
Created connection 1261198850.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4b2c5e02]
==>  Preparing: select * from student where id = ?
==> Parameters: 1(Integer)
<==    Columns: id, student_no, name, age, gender, class_name, major, phone, email, address, enrollment_date, birthday, status, create_time, update_time
<==        Row: 1, 2023001001, 张三, 20, 1, 计算机科学与技术1, 计算机科学与技术, 13800138001, zhangsan@example.com, 北京市海淀区, 2023-09-01, 2003-05-15, 1, 2025-10-22 17:14:30, 2025-10-22 17:14:30
<==      Total: 1
user = Student(id=1, studentNo=2023001001, name=张三, age=20, gender=1, className=计算机科学与技术1, major=计算机科学与技术, phone=13800138001, email=zhangsan@example.com, address=北京市海淀区, enrollmentDate=2023-09-01, birthday=2003-05-15, status=1, createTime=2025-10-22T17:14:30, updateTime=2025-10-22T17:14:30)
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4b2c5e02]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4b2c5e02]
Returned connection 1261198850 to pool.

多个参数

  • 多个参数时,需要使用Mybaties注解@Param("id") Integer id, @Param("name") String name注解来与形参对应。

  1. StudentMapper映射器定义方法。
public interface StudentMapper {Student getStudentByIdAndName(@Param("id") Integer id, @Param("name") String name);
}
  1. studentMapper.xml定义SQL语句。
<mapper namespace="com.maven.mapper.StudentMapper"><select id="getStudentByIdAndName" resultType="com.maven.pojo.Student">select * from student where id = #{id} and name = #{name}</select>
</mapper>
  1. 使用工具类调用Mybatis。
    @Testpublic void getStudentByIdAndName() throws Exception {SqlSession sqlSession = SqlSessionUtils.openSession();StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);Student student = mapper.getStudentByIdAndName(1, "张三");System.out.println("student = " + student);}
  1. 返回结果:
Created connection 79438382.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4bc222e]
==>  Preparing: select * from student where id = ? and name = ?
==> Parameters: 1(Integer), 张三(String)
<==    Columns: id, student_no, name, age, gender, class_name, major, phone, email, address, enrollment_date, birthday, status, create_time, update_time
<==        Row: 1, 2023001001, 张三, 20, 1, 计算机科学与技术1, 计算机科学与技术, 13800138001, zhangsan@example.com, 北京市海淀区, 2023-09-01, 2003-05-15, 1, 2025-10-22 17:14:30, 2025-10-22 17:14:30
<==      Total: 1
student = Student(id=1, studentNo=2023001001, name=张三, age=20, gender=1, className=计算机科学与技术1, major=计算机科学与技术, phone=13800138001, email=zhangsan@example.com, address=北京市海淀区, enrollmentDate=2023-09-01, birthday=2003-05-15, status=1, createTime=2025-10-22T17:14:30, updateTime=2025-10-22T17:14:30)

map作为参数

  • Map进行传参时,
    • 方式一:必须使用注解@Param(map) Map map为Map定义新的名称,使用时${map.id}调用参数。
    • 方式二:不适用注解,直接将Map中的key,作为xmlsql语句参数的具体名称。

  1. 映射器定义方法。
public interface StudentMapper {// 方式一Student getStudentByMap(@Param("map")Map map);// 方式二Student getStudentByMap2(Map map);
}
  1. xml文件编写sql。
<mapper namespace="com.maven.mapper.StudentMapper"><select id="getStudentByMap" resultType="com.maven.pojo.Student">select * from student where id = #{map.id} and name = #{map.name}</select><select id="getStudentByMap2" resultType="com.maven.pojo.Student">select * from student where id = #{id} and name = #{name}</select>
</mapper>

实体类作为参数

  • 单实体类,可以直接通过类属性字段,进行调用。
  1. mapper映射器定义方法。
public interface StudentMapper {// 使用类属性字段,进行映射Student getStudent(Student student);Student getStudent2(@Param("student") Student student);List<Student> getStudent3(@Param("student") Student student);
}
  1. xml文件编写Sql。
<mapper namespace="com.maven.mapper.StudentMapper"><!--类属性字段,进行映射--><select id="getStudent" resultType="com.maven.pojo.Student" parameterType="com.maven.pojo.Student" >select * from student where id = #{id}</select><select id="getStudent2" resultType="com.maven.pojo.Student" parameterType="com.maven.pojo.Student" >select*from studentwhere 1=1<if test="student.id != null" >id = #{student.id}</if><if test="student.name != null and student.name !=''" >and name = #{student.name}</if></select><select id="getStudent3" resultType="com.maven.pojo.Student" parameterType="com.maven.pojo.Student" >select*from studentwhere 1=1<if test="student.id != null" >id = #{student.id}</if><if test="student.name != null and student.name !=''" >and name = #{student.name}</if><if test="student.gender != null" >and gender = #{student.gender}</if></select>
</mapper>

resultMap

  • Mybatis默认封装规则(resultType):JavaBean职工的属性名去数据库中找对应列名的值,一一映射封装。
  • 自定义规则(resultMap):我们来告诉Mybatis如何把结果封装到Bean中。

MyBatis中,自定义封装resultMap是一种强大的方式,用于将数据库查询结果映射到Java对象。当数据库列名与Java对象属性名不一致,或者需要处理复杂的关联关系(如一对一、一对多)时,使用resultMap非常有用。

如何自定义resultMap?

  1. 在Mapper XML文件中,使用<resultMap>元素定义映射规则。
  2. <resultMap>包含以下属性:
    • id: 当前resultMap的唯一标识
    • type: 映射的Java类型,可以是别名或全限定名
  3. 子元素:
    • <id>: 用于映射主键列
    • <result>: 用于映射普通列
    • <association>: 用于映射一对一关系
    • <collection>: 用于映射一对多关系
  4. 每个和元素包含以下属性:
    • property: Java对象的属性名
    • column: 数据库表的列名
    • jdbcType: 列对应的JDBC类型(可选,MyBatis通常可以自动识别)
    • typeHandler: 类型处理器(可选)
  5. 对于关联关系:
    • <association>用于另一个Java对象作为当前对象的属性。
    • <collection>用于一个Java对象集合作为当前对象的属性,使用ofType属性指定集合内的java对象。

复杂场景映射

场景 1:一对一关联(association)
  • User实体类内,引用了UserProfile实体类
// 用户详情实体
public class UserProfile {private Long id;private Long userId;private String realName;private String address;private String phone;
}// 用户实体包含详情
public class User {private Long id;private String username;private String email;private UserProfile profile; // 一对一关联
}
<!-- 方式1:嵌套结果映射 -->
<resultMap id="UserWithProfileResultMap" type="User"><id property="id" column="id"/><result property="username" column="username"/><result property="email" column="email"/><association property="profile" javaType="UserProfile"><id property="id" column="profile_id"/><result property="userId" column="id"/><result property="realName" column="real_name"/><result property="address" column="address"/><result property="phone" column="phone"/></association>
</resultMap><select id="selectUserWithProfile" resultMap="UserWithProfileResultMap">SELECT u.id, u.username, u.email,p.id as profile_id, p.real_name, p.address, p.phoneFROM user uLEFT JOIN user_profile p ON u.id = p.user_idWHERE u.id = #{id}
</select>
场景 2:一对多关联(collection)
// 订单实体
public class Order {private Long id;private String orderNo;private BigDecimal amount;private LocalDateTime createTime;
}// 用户实体包含订单列表
public class User {private Long id;private String username;private String email;private List<Order> orders; // 一对多关联
}
<!-- 一对多映射 -->
<resultMap id="UserWithOrdersResultMap" type="User"><id property="id" column="id"/><result property="username" column="username"/><result property="email" column="email"/><collection property="orders" ofType="Order"><id property="id" column="order_id"/><result property="orderNo" column="order_no"/><result property="amount" column="amount"/><result property="createTime" column="order_create_time"/></collection>
</resultMap><select id="selectUserWithOrders" resultMap="UserWithOrdersResultMap">SELECT u.id, u.username, u.email,o.id as order_id, o.order_no, o.amount, o.create_time as order_create_timeFROM user uLEFT JOIN orders o ON u.id = o.user_idWHERE u.id = #{id}
</select>

高级映射特性

继承 resultMap
<!-- 基础 resultMap -->
<resultMap id="BaseUserResultMap" type="User"><id property="id" column="id"/><result property="username" column="username"/><result property="email" column="email"/>
</resultMap><!-- 扩展的 resultMap -->
<resultMap id="ExtendedUserResultMap" type="User" extends="BaseUserResultMap"><result property="age" column="age"/><result property="createTime" column="create_time"/><association property="profile" javaType="UserProfile"><!-- 关联映射 --></association>
</resultMap>
多层嵌套示例
// 多层嵌套的实体结构
public class User {private Long id;private String username;private List<Order> orders;
}public class Order {private Long id;private String orderNo;private List<OrderItem> items;
}public class OrderItem {private Long id;private String productName;private Integer quantity;private Product product;
}public class Product {private Long id;private String name;private BigDecimal price;
}
<!-- 多层嵌套 resultMap -->
<resultMap id="UserDetailResultMap" type="User"><id property="id" column="user_id"/><result property="username" column="username"/><collection property="orders" ofType="Order"><id property="id" column="order_id"/><result property="orderNo" column="order_no"/><collection property="items" ofType="OrderItem"><id property="id" column="item_id"/><result property="productName" column="product_name"/><result property="quantity" column="quantity"/><association property="product" javaType="Product"><id property="id" column="product_id"/><result property="name" column="product_name"/><result property="price" column="price"/></association></collection></collection>
</resultMap><select id="selectUserDetail" resultMap="UserDetailResultMap">SELECT u.id as user_id, u.username,o.id as order_id, o.order_no,oi.id as item_id, oi.product_name, oi.quantity,p.id as product_id, p.name as product_name, p.priceFROM user uLEFT JOIN orders o ON u.id = o.user_idLEFT JOIN order_item oi ON o.id = oi.order_idLEFT JOIN product p ON oi.product_id = p.idWHERE u.id = #{id}
</select>

核心动态 SQL 标签

MyBatis 动态SQL允许我们在XML映射文件中,根据条件动态地构建SQL语句。它通过一系列标签来实现,如if、choose、when、otherwise、trim、where、set、foreach等。

SQL 片段基本概念

SQL 片段 是 MyBatis 中用于重用 SQL 代码块的特性,通过 <sql> 标签定义,通过 <include> 标签引用。


SQL 片段示例:

1. 基础字段列表

<!-- 定义用户表字段片段 -->
<sql id="userBaseColumns">id, username, password, email, age, create_time, update_time
</sql><!-- 定义用户表名片段 -->
<sql id="userTable">user
</sql><!-- 使用片段 -->
<select id="selectAllUsers" resultType="User">SELECT <include refid="userBaseColumns"/>FROM <include refid="userTable"/>
</select>

生成的 SQL:

SELECT id, username, password, email, age, create_time, update_time FROM user

2. 带参数的 SQL 片段

  • 使用属性传递参数
<!-- 定义带参数的片段 -->
<sql id="userColumnsWithPrefix">${prefix}.id,${prefix}.username,${prefix}.email,${prefix}.create_time
</sql><!-- 使用片段并传递参数 -->
<select id="selectUserWithOrders" resultType="map">SELECT <include refid="userColumnsWithPrefix"><property name="prefix" value="u"/></include>,o.order_no,o.amountFROM user uLEFT JOIN orders o ON u.id = o.user_id
</select>

生成的 SQL:

SELECT u.id, u.username, u.email, u.create_time,o.order_no, o.amount
FROM user u
LEFT JOIN orders o ON u.id = o.user_id

3. 复杂 SQL 片段

  • 条件片段
<!-- 定义通用查询条件 -->
<sql id="userWhereCondition"><where><if test="username != null and username != ''">AND username LIKE CONCAT('%', #{username}, '%')</if><if test="email != null and email != ''">AND email = #{email}</if><if test="minAge != null">AND age >= #{minAge}</if><if test="maxAge != null">AND age &lt;= #{maxAge}</if><if test="status != null">AND status = #{status}</if></where>
</sql><!-- 使用条件片段 -->
<select id="findUsers" parameterType="User" resultType="User">SELECT * FROM user <include refid="userWhereCondition"/>ORDER BY create_time DESC
</select>

4. 分页片段

<!-- 定义分页片段 -->
<sql id="pagination"><if test="pageSize != null and pageSize > 0">LIMIT #{pageSize}<if test="offset != null and offset >= 0">OFFSET #{offset}</if></if>
</sql><!-- 使用分页片段 -->
<select id="findUsersByPage" parameterType="map" resultType="User">SELECT * FROM user <include refid="userWhereCondition"/>ORDER BY id DESC<include refid="pagination"/>
</select>

<if> 标签

作用:根据条件判断是否包含 SQL 片段
属性test:必需的 OGNL 表达式,返回 true/false
执行逻辑

  • 每个 <if> 都是独立判断的
  • 多个 <if> 可能同时生效
  • 条件之间没有互斥关系
<select id="findUsers" parameterType="User" resultType="User">SELECT * FROM user WHERE 1=1<if test="username != null and username != ''">AND username = #{username}</if><if test="email != null and email != ''">AND email = #{email}</if><if test="age != null">AND age = #{age}</if>
</select>

<choose>, <when>, <otherwise> 标签

作用:实现多条件选择,类似 Java 的 switch-case

choose的工作机制:
<choose> 类似于 Java 中的 switch-caseif-else if-else 结构:

  • 按顺序检查每个 <when> 的条件
  • 第一个满足条件的 <when> 会生效
  • 后续的 <when> 不再检查
  • 如果所有 <when> 都不满足,则 <otherwise> 生效
<select id="findUsers" parameterType="User" resultType="User">SELECT * FROM user WHERE 1=1<choose><when test="username != null and username != ''">AND username = #{username}</when><when test="email != null and email != ''">AND email = #{email}</when><otherwise>AND status = 1</otherwise></choose>
</select>

<where> 标签

作用:智能处理 WHERE 子句,自动去除多余的 AND/OR

解决的问题

  • 避免 WHERE 后面直接跟 AND/OR 的语法错误
  • 自动管理 WHERE 关键字

智能处理规则

  • 如果内容不为空,自动添加 WHERE 关键字
  • 自动去除第一个条件前的 AND/OR
  • 如果内容为空,不生成 WHERE 子句
<select id="findUsers" parameterType="User" resultType="User">SELECT * FROM user <where><if test="username != null and username != ''">AND username = #{username}</if><if test="email != null and email != ''">AND email = #{email}</if><if test="age != null">AND age = #{age}</if></where>
</select>

生成的 SQL 示例:

  • 如果所有条件都为空:SELECT * FROM user
  • 如果只有 username 有值:SELECT * FROM user WHERE username = ?

不同场景的生成结果

输入条件生成的 SQL说明
所有条件都为空SELECT * FROM user不生成 WHERE
只有 usernameSELECT * FROM user WHERE username = ?自动去除第一个 AND
username 和 ageSELECT * FROM user WHERE username = ? AND age = ?正常连接条件

<set> 标签

作用:智能处理 UPDATE 语句的 SET 子句,自动去除多余的逗号

解决的问题

  • 避免 SET 字段列表末尾有多余逗号
  • 动态构建 UPDATE 语句

智能处理

  • 自动去除最后一个字段后的逗号
  • 如果所有条件都不满足,SET 子句为空(会导致SQL错误)
<update id="updateUser" parameterType="User">UPDATE user <set><if test="username != null and username != ''">username = #{username},</if><if test="email != null and email != ''">email = #{email},</if><if test="age != null">age = #{age},</if><if test="updateTime != null">update_time = #{updateTime}</if></set>WHERE id = #{id}
</update>

<trim> 标签

作用:更灵活地处理 SQL 片段的前缀和后缀

语法

<trim prefix="前缀" prefixOverrides="要移除的前缀" suffix="后缀" suffixOverrides="要移除的后缀"><!-- SQL片段 -->
</trim>

属性说明

  • prefix:在整个内容前添加的前缀
  • suffix:在整个内容后添加的后缀
  • prefixOverrides:移除内容开头指定的字符串(多个用 | 分隔)
  • suffixOverrides:移除内容末尾指定的字符串(多个用 | 分隔)
<!-- 等同于 <where> 标签 -->
<select id="findUsers" parameterType="User" resultType="User">SELECT * FROM user <trim prefix="WHERE" prefixOverrides="AND |OR "><if test="username != null and username != ''">AND username = #{username}</if><if test="email != null and email != ''">AND email = #{email}</if></trim>
</select><!-- 等同于 <set> 标签 -->
<update id="updateUser" parameterType="User">UPDATE user <trim prefix="SET" suffixOverrides=","><if test="username != null">username = #{username},</if><if test="email != null">email = #{email},</if></trim>WHERE id = #{id}
</update>

<foreach> 标签

作用:遍历集合,构建 IN 查询或批量操作
语法

<foreach collection="集合" item="元素变量" index="索引变量" open="开始符号" close="结束符号" separator="分隔符"><!-- 循环内容 -->
</foreach>

属性说明

  • collection:要遍历的集合属性名
  • item:每次遍历时的元素变量名
  • index:索引变量名(可选)
  • open:整个循环开始时的字符串
  • close:整个循环结束时的字符串
  • separator:每次循环之间的分隔符
<!-- IN 查询 -->
<select id="findUsersByIds" resultType="User">SELECT * FROM user WHERE id IN<foreach collection="list" item="id" open="(" separator="," close=")">#{id}</foreach>
</select><!-- 批量插入 -->
<insert id="batchInsertUsers" parameterType="java.util.List">INSERT INTO user (username, email, age) VALUES <foreach collection="list" item="user" separator=",">(#{user.username}, #{user.email}, #{user.age})</foreach>
</insert><!-- 批量更新 -->
<update id="batchUpdateUsers" parameterType="java.util.List"><foreach collection="list" item="user" separator=";">UPDATE user SET username = #{user.username}, email = #{user.email}WHERE id = #{user.id}</foreach>
</update>

<bind> 标签

作用:创建变量并在当前上下文使用
语法

<bind name="变量名" value="OGNL表达式"/>

使用场景

  • 模糊查询参数处理
  • 复杂表达式计算
  • 重复使用的值
<select id="findUsers" parameterType="User" resultType="User"><bind name="pattern" value="'%' + username + '%'" />SELECT * FROM user WHERE username LIKE #{pattern}
</select><!-- 处理复杂逻辑 -->
<select id="findUsers" parameterType="User" resultType="User"><bind name="namePattern" value="username != null ? '%' + username + '%' : '%'" /><bind name="minAge" value="age != null ? age : 0" />SELECT * FROM user WHERE username LIKE #{namePattern}AND age >= #{minAge}
</select>

特殊字符

转义字符

核心概念:

  • 转义字符 是用特定的字符序列来表示 XML 中的特殊字符,让 XML 解析器能够正确识别。
特殊字符转义序列示例说明
<&lt;age &lt; 18小于号
>&gt;age &gt; 65大于号
&&amp;name = 'Tom&amp;Jerry'和号
'&apos;name = &apos;John&apos;s&apos;单引号
"&quot;name = &quot;John&quot;双引号
基础比较运算
<!-- 使用转义字符处理比较运算符 -->
<select id="findUsersByAgeRange" parameterType="map" resultType="User">SELECT * FROM user WHERE age &gt;= #{minAge}     <!-- >= -->AND age &lt;= #{maxAge}       <!-- <= -->AND status &lt;&gt; 0         <!-- <> 不等于 -->
</select>
复杂条件组合
<select id="findProducts" parameterType="map" resultType="Product">SELECT * FROM product <where><!-- 价格范围条件 --><if test="minPrice != null">AND price &gt;= #{minPrice}</if><if test="maxPrice != null">AND price &lt;= #{maxPrice}</if><!-- 库存条件 --><if test="lowStock != null and lowStock == true">AND stock &lt; 10</if><!-- 包含特殊字符的字符串 --><if test="brand != null">AND brand LIKE '%&amp;%Co.'  <!-- 查找包含 & 的品牌 --></if></where>
</select>
在动态 SQL 的 test 属性中使用
<select id="findUsers" parameterType="User" resultType="User">SELECT * FROM user <where><!-- 在 test 属性中使用转义字符 --><if test="age &lt; 18">AND category = 'MINOR'</if><if test="age &gt;= 18 and age &lt; 65">AND category = 'ADULT'</if><if test="age &gt;= 65">AND category = 'SENIOR'</if><!-- 复杂条件 --><if test="(score &lt; 60 and retryCount &gt; 3) or status &lt;&gt; 'ACTIVE'">AND need_review = 1</if></where>
</select>

CDATA

核心概念

  • CDATA(Character Data) 是 XML 中的特殊区块,告诉解析器区块内的内容应作为纯文本处理,不进行 XML 解析。
  • 不能在属性中使用。例如<if test="">,不能再test中使用。

语法结构:

<![CDATA[这里的内容不会被XML解析器解析可以直接使用 < > & 等特殊字符
]]>
基础使用 CDATA
<select id="findProducts" parameterType="map" resultType="Product">SELECT * FROM product WHERE category = #{category}<!-- 只有比较运算符使用 CDATA -->AND price <![CDATA[ > ]]> #{minPrice}AND price <![CDATA[ < ]]> #{maxPrice}<!-- 普通条件不使用 CDATA -->AND status = 'ACTIVE'AND stock > 0
</select>
比较运算
<select id="findUsersByAge" parameterType="map" resultType="User">SELECT * FROM user WHERE 1=1<if test="minAge != null">AND age <![CDATA[ >= ]]> #{minAge}</if><if test="maxAge != null">AND age <![CDATA[ <= ]]> #{maxAge}</if><if test="excludeStatus != null">AND status <![CDATA[ <> ]]> #{excludeStatus}</if>
</select>
复杂查询条件
<select id="findComplexUsers" parameterType="map" resultType="User">SELECT * FROM user <where><!-- 年龄范围条件 --><if test="ageRange != null">AND age <![CDATA[ > ]]> #{ageRange.min} AND age <![CDATA[ < ]]> #{ageRange.max}</if><!-- 分数条件 --><if test="scoreCondition != null">AND (score <![CDATA[ < ]]> 60 OR score <![CDATA[ > ]]> 90)</if><!-- 时间范围条件 --><if test="startDate != null and endDate != null">AND create_time <![CDATA[ >= ]]> #{startDate} AND create_time <![CDATA[ <= ]]> #{endDate}</if></where>
</select>
在 SQL 片段中使用 CDATA
<!-- 定义包含 CDATA 的 SQL 片段 -->
<sql id="ageCondition"><if test="minAge != null">AND age <![CDATA[ >= ]]> #{minAge}</if><if test="maxAge != null">AND age <![CDATA[ <= ]]> #{maxAge}</if>
</sql><!-- 使用片段 -->
<select id="findUsers" parameterType="map" resultType="User">SELECT * FROM user <where><include refid="ageCondition"/><if test="status != null">AND status = #{status}</if></where>
</select>

适用场景对比

场景推荐方案理由
简单比较运算(转义字符)代码简洁,处理简单
复杂SQL逻辑(CDATA)可读性更重要
test属性中的条件(转义字符)CDATA不能在属性中使用
包含多个特殊字符(CDATA)避免多次转义,更清晰
性能敏感场景(转义字符)解析开销稍小
http://www.dtcms.com/a/524218.html

相关文章:

  • Linux网络的应用层协议HTTP
  • SQLite Group By 指令详解
  • 监理建设协会网站wordpress关注微信登陆
  • 常用串行通信协议核心区别(含CAN、SPI、I2C、UART、RS-485、Ethernet、USB)
  • LangChain1.0发布
  • 压缩与缓存调优实战指南:从0到1根治性能瓶颈(五)
  • 使用 RPM 包在 Linux 7 上安装 MySQL 8
  • 云服务器2008做网站wordpress用thinkphp
  • 仓颉标准库std源码深度解析:构建全场景智能应用的基石
  • C4D域力场的应用之粒子随风飘散解析
  • 自己做的网站别人怎么访问安康网站建设公司电话
  • uniapp小程序实现手动向上滑动窗口
  • vue3:uniapp全局颜色变量配置思路:使用js变量
  • wordpress调用 别的网站昆明seo网站排名
  • 网站建设模板素材重庆互联网大厂
  • 网络爬虫指南:从原理到实战
  • 小杰-自然语言处理(four)——transformer系列——注意力机制
  • Java SpringAOP --- AOP的使用,AOP的源码
  • 阿里云渠道商:如何设置阿里云的安全组规则?
  • 网站设计速成如何让百度快速收录网站文章
  • 北京平台网站建设多少钱学院网站建设的特色
  • 外贸soho建站多少钱山东省住房和城乡建设厅官方网站
  • 芯科科技推出智能开发工具Simplicity Ecosystem软件开发套件开启物联网开发的新高度
  • 报错: lfstackPack redeclared in this block / go版本混乱,清理旧版本
  • 和鲸科技入选《大模型一体机产业图谱》,以一体机智驱科研、重塑教学
  • Go语言:关于怎么在线学习go语言的建议
  • 树 B树和B+树
  • 【arXiv2025】Real-Time Object Detection Meets DINOv3
  • 绍兴网站建设专业的公司4000-262-怎么在百度上发帖推广
  • AH2203输入12v输出3v 6v 9v/2A同步降压LED驱动器芯片