乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 MyBatis概述与快速上手

    • 本章定位
    • MyBatis简介
    • 环境搭建
    • 第一个MyBatis程序
    • SqlSessionFactoryBuilder与openSession重载
    • SqlSessionFactory与SqlSession
    • SqlSession核心方法
    • 不使用 XML 构建 SqlSessionFactory
    • Mapper接口与映射方式
    • Java API 目录结构
  • 第2章 全局配置文件详解

    • 本章定位
    • properties
    • settings
    • typeAliases
    • typeHandlers
    • objectFactory
    • plugins
    • environments
    • transactionManager
    • dataSource
    • databaseIdProvider
    • mappers
    • 日志配置
  • 第3章 SQL映射文件基础

    • 本章定位
    • select
    • insert
    • update
    • delete
    • 参数传递与占位符
    • 主键生成策略
    • resultType
    • resultMap
    • 自动映射详解
    • sql片段
    • SQL 语句构建器
  • 第4章 动态SQL

    • 本章定位
    • if
    • choose、when、otherwise
    • where
    • set
    • foreach
    • trim
    • bind
    • script 元素:在注解映射器中启用动态 SQL
    • _databaseId 与动态 SQL 的多数据库支持
    • 动态 SQL 中插入脚本语言
  • 第5章 结果映射与关联查询

    • 本章定位
    • resultMap详解
    • association
    • collection
    • discriminator
    • N+1查询问题
    • 延迟加载
  • 第6章 MyBatis注解开发

    • 本章定位
    • @Select
    • @Insert
    • @Update
    • @Delete
    • @Param
    • @Options
    • @SelectKey
    • @Results
    • @Result
    • @One
    • @Many
    • @SelectProvider
  • 第7章 缓存与性能优化

    • 本章定位
    • 一级缓存
    • 二级缓存
    • 缓存配置详解
    • 自定义缓存
    • Executor执行器类型
    • 分页插件

bind

导学

本节你将掌握 MyBatis 动态 SQL 中用于 OGNL 上下文变量绑定的元素 bind。学习目标是:

  • 理解 bind 元素的核心作用(在 OGNL 上下文中创建新变量)
  • 掌握 name 和 value 两个属性
  • 能够使用 bind 实现模糊查询的通配符拼接
  • 能够使用 bind 复用复杂的 OGNL 表达式

定义

bind 是 MyBatis 动态 SQL 中用于在 OGNL 上下文中创建新变量的元素。它允许开发者在 XML 映射文件中定义一个变量,该变量可以在当前 OGNL 上下文中被后续的 SQL 片段引用。

bind 最典型的应用场景是模糊查询中的 % 通配符拼接。在传统的 MyBatis 写法中,模糊查询通常这样写:

<if test="name != null and name != ''">
    AND name LIKE CONCAT('%', #{name}, '%')
</if>

这种方式依赖数据库的字符串拼接函数(如 MySQL 的 CONCAT),不同数据库的函数名可能不同(Oracle 用 ||,SQL Server 用 +),导致 SQL 的可移植性降低。使用 bind 可以在 XML 层就完成 % 的拼接,生成统一的 LIKE ? 语法:

<bind name="pattern" value="'%' + name + '%'"/>
AND name LIKE #{pattern}

这样生成的 SQL 是数据库无关的 LIKE ?,由 JDBC 预编译参数传递拼接后的字符串值,既简洁又可移植。


适用位置与核心属性

bind 可以嵌套在 select、insert、update、delete 语句内部,或嵌套在 where、set、trim 等容器元素内部。通常放在语句的开头位置,以便后续 SQL 片段引用。

属性是否必填说明
name是变量的名称,在 OGNL 上下文中通过该名称引用变量
value是OGNL 表达式,表达式的求值结果会绑定到 name 指定的变量上

核心原理

变量绑定与引用流程图

bind 在 OGNL 上下文中的作用


完整示例

场景说明

乐途学院的学生管理系统需要实现以下两个使用 bind 的场景:

  1. 模糊查询学生姓名:在 XML 中拼接 % 通配符,生成数据库无关的 LIKE ? 语法
  2. 动态排序字段绑定:将传入的排序字段名绑定到变量中,用于 ORDER BY(注意:排序字段名不能使用 #{ },需要使用 ${ })

操作前的数据库表结构及初始数据

CREATE TABLE student (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(20),
    age INT,
    major VARCHAR(20),
    score DECIMAL(5,2)
);

INSERT INTO student (name, age, major, score) VALUES
('大翔', 22, '计算机科学', 95.5),
('白歌', 21, '软件工程', 88.0),
('小崔', 20, '计算机科学', 92.0),
('黄俪', 21, '信息安全', 90.5),
('李眉', 22, '软件工程', 87.0);

当前表数据:

idnameagemajorscore
1大翔22计算机科学95.50
2白歌21软件工程88.00
3小崔20计算机科学92.00
4黄俪21信息安全90.50
5李眉22软件工程87.00

完整的映射文件片段

StudentMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.flying.mapper.StudentMapper">

    <!-- 示例1:bind 拼接模糊查询通配符 -->
    <select id="findByNameLike" resultType="Student"
            parameterType="com.flying.entity.Student">
        <bind name="pattern" value="'%' + name + '%'"/>
        SELECT id, name, age, major, score
        FROM student
        <where>
            <if test="name != null and name != ''">
                AND name LIKE #{pattern}
            </if>
            <if test="major != null and major != ''">
                AND major = #{major}
            </if>
        </where>
    </select>

    <!-- 示例2:bind 绑定排序字段(使用 ${} 直接替换) -->
    <select id="findWithOrder" resultType="Student"
            parameterType="java.util.HashMap">
        <bind name="orderColumn" value="orderColumn"/>
        SELECT id, name, age, major, score
        FROM student
        <where>
            <if test="major != null and major != ''">
                AND major = #{major}
            </if>
        </where>
        ORDER BY ${orderColumn}
    </select>

    <!-- 示例3:bind 复用复杂 OGNL 表达式 -->
    <select id="findByComplexCondition" resultType="Student"
            parameterType="com.flying.entity.Student">
        <bind name="hasName" value="name != null and name != ''"/>
        <bind name="hasMajor" value="major != null and major != ''"/>
        <bind name="isHighScore" value="minScore != null and minScore >= 90"/>
        SELECT id, name, age, major, score
        FROM student
        <where>
            <if test="hasName">
                AND name LIKE CONCAT('%', #{name}, '%')
            </if>
            <if test="hasMajor">
                AND major = #{major}
            </if>
            <if test="isHighScore">
                AND score >= #{minScore}
            </if>
        </where>
    </select>

</mapper>

StudentMapper.java(接口)

package com.flying.mapper;

import com.flying.entity.Student;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;

public interface StudentMapper {
    List<Student> findByNameLike(Student condition);
    List<Student> findWithOrder(@Param("orderColumn") String orderColumn,
                                @Param("major") String major);
    List<Student> findByComplexCondition(Student condition);
}

实际执行结果

示例1:bind 拼接模糊查询通配符

测试代码:

Student condition = new Student();
condition.setName("翔");
List<Student> list = mapper.findByNameLike(condition);

最终生成的 SQL 语句:

SELECT id, name, age, major, score FROM student WHERE name LIKE ?

参数值: %翔%

查询结果集:

idnameagemajorscore
1大翔22计算机科学95.50

分析: bind 将 '%' + name + '%' 求值为 '%翔%',绑定到变量 pattern 中。后续 #{pattern} 引用该变量,最终 SQL 是数据库无关的 LIKE ?,由 JDBC 传递参数值 %翔%。这种方式不依赖任何数据库特定的字符串拼接函数,具有良好的数据库可移植性。

示例2:bind 绑定排序字段

测试代码:

List<Student> list = mapper.findWithOrder("score", "计算机科学");

最终生成的 SQL 语句:

SELECT id, name, age, major, score FROM student WHERE major = ? ORDER BY score

参数值: 计算机科学

查询结果集:

idnameagemajorscore
1大翔22计算机科学95.50
3小崔20计算机科学92.00

分析: 排序字段名是 SQL 关键字的一部分,不能使用 #{ }(预编译参数占位符),必须使用 ${ }(直接文本替换)。bind 将传入的 orderColumn 绑定到 OGNL 上下文中,${orderColumn} 直接将其值替换到 SQL 中。注意:使用 ${ } 存在 SQL 注入风险,必须在 Java 代码中对 orderColumn 做白名单校验,只允许传入预定义的字段名。

示例3:bind 复用复杂 OGNL 表达式

测试代码:

Student condition = new Student();
condition.setName("白");
condition.setMajor("软件工程");
condition.setMinScore(85.0);
List<Student> list = mapper.findByComplexCondition(condition);

最终生成的 SQL 语句:

SELECT id, name, age, major, score FROM student WHERE name LIKE CONCAT('%', ?, '%') AND major = ? AND score >= ?

参数值: 白, 软件工程, 85.0

查询结果集:

idnameagemajorscore
2白歌21软件工程88.00

分析: 三个 bind 分别将复杂的 OGNL 表达式(name != null and name != '' 等)绑定为简单的布尔变量(hasName、hasMajor、isHighScore)。后续的 if 直接使用这些变量,使 test 表达式更简洁、可读性更高。这在条件判断逻辑复杂时特别有用。


易错场景 / 常见误区

误区错误示例后果正解
在 value 中使用 #{ }value="'%' + #{name} + '%'"语法错误,bind 的 value 是 OGNL 表达式,不是 SQL,不能用 #{ }value="'%' + name + '%'",直接写属性名
用 #{ } 引用排序字段ORDER BY #{orderColumn}排序字段被当作字符串参数处理,SQL 变成 ORDER BY 'score',按常量字符串排序,无意义排序字段用 ${ } 直接替换:ORDER BY ${orderColumn}
使用 ${ } 但不做白名单校验ORDER BY ${orderColumn}如果 orderColumn 被恶意传入 id; DROP TABLE student--,存在 SQL 注入风险Java 层对排序字段做白名单校验,只允许预定义字段
bind 的 name 与参数属性名冲突name 与传入对象的属性同名OGNL 上下文中的变量可能覆盖原始属性,导致意外行为使用不与参数属性冲突的变量名,如 pattern、orderColumn
认为 bind 可以跨语句复用在 statement A 中 bind,在 statement B 中使用bind 的作用域仅限于当前语句内部,跨语句无效每个需要变量的语句内部单独定义 bind

面试考点

Q1:bind 元素的主要用途是什么?

A:bind 的主要用途是在 OGNL 上下文中创建一个新变量,供当前 SQL 语句的后续片段引用。最典型的应用场景是:① 模糊查询时在 XML 层拼接 % 通配符,生成数据库无关的 LIKE ? 语法;② 将复杂的 OGNL 表达式绑定为简单变量,提高 test 表达式的可读性;③ 为 ${ } 直接替换提供变量来源(如动态排序字段)。

Q2:以下两种模糊查询写法有什么区别?

写法 A:<bind name="pattern" value="'%' + name + '%'"/> + name LIKE #{pattern} 写法 B:name LIKE CONCAT('%', #{name}, '%')

A:写法 A 在 XML 层通过 OGNL 表达式拼接 % 通配符,生成的 SQL 是数据库无关的 name LIKE ?,由 JDBC 预编译参数传递完整的模式字符串。写法 B 依赖数据库特定的字符串拼接函数(如 MySQL 的 CONCAT),不同数据库语法不同(Oracle 用 ||,SQL Server 用 +),可移植性较差。写法 A 更推荐,因为它不依赖特定数据库函数。

Q3:#{ } 和 ${ } 有什么区别?bind 与它们如何配合?

A:#{ } 是预编译参数占位符,MyBatis 会将其替换为 ?,参数值由 JDBC 安全传递,可以防止 SQL 注入。${ } 是直接的文本替换,MyBatis 会将其值直接拼接到 SQL 字符串中,不做预编译处理,存在 SQL 注入风险。bind 创建的变量既可以用 #{ } 引用(如 #{pattern},安全),也可以用 ${ } 引用(如 ${orderColumn},需要白名单校验)。

Q4:以下代码能否达到预期效果?

<bind name="name" value="'%' + name + '%'"/>

A:不能,且存在严重问题。bind 的 name 属性指定了变量名为 name,这会覆盖 OGNL 上下文中原始的 name 属性(传入参数对象的 name 字段)。后续如果还需要引用原始的 name 值,会发现它已经被修改了。正确做法是使用不与参数属性冲突的变量名,如 pattern。


小结

bind 是 MyBatis 动态 SQL 中用于 OGNL 上下文变量绑定的实用元素。通过 name 和 value 两个属性,它可以在 XML 映射文件中创建临时变量,供后续 SQL 片段引用。最典型的应用是模糊查询的 % 通配符拼接,它让 SQL 摆脱了对数据库特定字符串函数的依赖,实现了更好的可移植性。

使用 bind 时需要注意:① value 中是 OGNL 表达式,不是 SQL,不能用 #{ };② 变量名不要与参数属性名冲突;③ 配合 ${ } 使用时必须在 Java 层做白名单校验,防止 SQL 注入。

至此,MyBatis 动态 SQL 的七大核心元素(if、choose/when/otherwise、where、set、trim、foreach、bind)已经全部学习完毕。掌握这些元素后,你可以编写出灵活、安全、优雅的动态 SQL,彻底告别 JDBC 时代的字符串拼接噩梦。


下一章引子

动态 SQL 让你能够灵活地构建查询和更新语句,但在实际项目中,你还需要掌握 MyBatis 的高级映射技巧——如何处理一对一、一对多的关联查询?如何使用结果映射(ResultMap)处理复杂的对象嵌套结构?下一章我们将深入探讨 MyBatis 的关联映射与 ResultMap 高级用法,让你的持久层代码更加强大。

上一页
trim
下一页
script 元素:在注解映射器中启用动态 SQL