Day06 Java操作数据库


图1 Java操作数据库的JBDC规范及其基础之上的框架
目录

1. JDBC

1.1 JDBC-介绍

图2 JDBC驱动
1.2 JDBC-入门程序 DML
图3 创建项目
pom.xm 中的依赖
<dependencies>
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.0.33</version>
    </dependency>

    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.9.3</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.30</version>
    </dependency>
</dependencies>
create schema web01 collate utf8mb4_unicode_ci;
create schema web01 collate utf8mb4_general_ci;

create table user(
    id int unsigned primary key auto_increment comment 'ID,主键',
    username varchar(20) comment '用户名',
    password varchar(32) comment '密码',
    name varchar(10) comment '姓名',
    age tinyint unsigned comment '年龄'
) comment '用户表';

insert into user(id, username, password, name, age) values
(1, 'dulaoshi', '123456', '张婧怡', 62),
(2, 'daqiao', '123456', '大乔', 22),
(3, 'xiaoqiao', '123456', '小乔', 18),
(4, 'diaochan', '123456', '貂蝉', 24),
(5, 'lvbu', '123456', '吕布', 28),
(6, 'zhaoyun', '12345678', '赵云', 27);
AI提问词
// JDBC 访问MySQL8,update语句(update user set age = 25 where id= 2)
//1、注册驱动
//2、获取连接
//3、获取SQL语句执行对象
//4、执行SQL
//5、释放资源
src/test/java/cn/zjy/JdbcTest.java
package cn.zjy;

import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class JdbcTest {
    @Test
    public void testUpdate() throws Exception {
        // JDBC 访问MySQL8,update语句(update user set age = 25 where id= 2)
        //1、注册驱动
        //2、获取连接
        //3、获取SQL语句执行对象
        //4、执行SQL
        //5、释放资源

        // 数据库连接信息
        String url = "jdbc:mysql://www.duzhaojiang.cn:3306/web01?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
        String username = "root";
        String password = "***********";

        // SQL更新语句
        String sql = "update user set age = 25 where id = 2";

        Connection conn = null;
        Statement stmt = null;

        // 1、注册驱动 (MySQL 8.0以上使用com.mysql.cj.jdbc.Driver)
        Class.forName("com.mysql.cj.jdbc.Driver");

        // 2、获取连接
        conn = DriverManager.getConnection(url, username, password);

        // 3、获取SQL语句执行对象
        stmt = conn.createStatement();

        // 4、执行SQL
        int rowsAffected = stmt.executeUpdate(sql);
        System.out.println("更新影响的行数: " + rowsAffected);

        // 5、释放资源,先关闭Statement,再关闭Connection
        stmt.close();
        conn.close();

    }
}
图4 update结果验证
图5 JDBC DML小结

1.3 JDBC-查询数据 DQL

HelloController.java
提示词
你是一名java开发工程师,帮我基于JDBC程序来操作MySQL8数据库,执行如下SQL语句:select id,username,password,name,age from user  where username = 'daqiao' and password = '123456';
并将查询的每一行记录,都封装到实体类User中,然后将User对象的数据输出到控制台中。
User 实体类属性如下:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id; //ID
    private String username; //用户名
    private String password; //密码
    private String name; //姓名
    private Integer age; //年龄
}
src/test/java/cn/zjy/JdbcTest.java 添加testSelect()方法
    @Test
    public void testSelect() throws Exception {
        // 数据库连接信息
        String url = "jdbc:mysql://www.duzhaojiang.cn:3306/web01?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
        String username = "root";
        String password = "*********";

        // SQL查询语句
        String sql = "select id, username, password, name, age from user where username = ? and password = ?";

        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        // 1、注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

        // 2、获取连接
        conn = DriverManager.getConnection(url, username, password);

        // 3、获取SQL语句执行对象 (使用PreparedStatement防止SQL注入)
        pstmt = conn.prepareStatement(sql);
        // 设置参数
        pstmt.setString(1, "dulaoshi");
        pstmt.setString(2, "123456");

        // 4、执行查询
        rs = pstmt.executeQuery();

        // 处理查询结果
        while (rs.next()) {
            // 封装到User实体类
            User user = new User(
                    rs.getInt("id"),
                    rs.getString("username"),
                    rs.getString("password"),
                    rs.getString("name"),
                    rs.getInt("age")
            );

            // 输出User对象数据到控制台
            System.out.println(user);//使用Lombok的@Data自动生成的toString方法
        }

        rs.close();
        pstmt.close();
        conn.close();

    }

    @Test
    public void testSelectAll() throws Exception {
        // 数据库连接信息
        String url = "jdbc:mysql://www.duzhaojiang.cn:3306/web01?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
        String username = "root";
        String password = "zjyiang@Ywntj.";

        // SQL查询语句
        String sql = "select id, username, password, name, age from user";

        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        // 1、注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

        // 2、获取连接
        conn = DriverManager.getConnection(url, username, password);

        // 3、获取SQL语句执行对象 (使用PreparedStatement防止SQL注入)
        pstmt = conn.prepareStatement(sql);

        // 4、执行查询
        rs = pstmt.executeQuery();

        // 处理查询结果
        while (rs.next()) {
            // 封装到User实体类
            User user = new User(
                    rs.getInt("id"),
                    rs.getString("username"),
                    rs.getString("password"),
                    rs.getString("name"),
                    rs.getInt("age")
            );

            // 输出User对象数据到控制台
            System.out.print("用户信息:");
            System.out.print("ID: " + user.getId());
            System.out.print(",用户名: " + user.getUsername());
            System.out.print(",密码: " + user.getPassword());
            System.out.print(",姓名: " + user.getName());
            System.out.println(",年龄: " + user.getAge());
        }

        rs.close();
        pstmt.close();
        conn.close();

    }
src/test/java/cn/zjy/JdbcTest.java 添加testSelectAll()方法
    @Test
    public void testSelectAll() throws Exception {
        // 数据库连接信息
        String url = "jdbc:mysql://www.duzhaojiang.cn:3306/web01?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
        String username = "root";
        String password = "zjyiang@Ywntj.";

        // SQL查询语句
        String sql = "select id, username, password, name, age from user";

        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        // 1、注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

        // 2、获取连接
        conn = DriverManager.getConnection(url, username, password);

        // 3、获取SQL语句执行对象 (使用PreparedStatement防止SQL注入)
        pstmt = conn.prepareStatement(sql);

        // 4、执行查询
        rs = pstmt.executeQuery();

        // 处理查询结果
        while (rs.next()) {
            // 封装到User实体类
            User user = new User(
                    rs.getInt("id"),
                    rs.getString("username"),
                    rs.getString("password"),
                    rs.getString("name"),
                    rs.getInt("age")
            );

            // 输出User对象数据到控制台
            System.out.print("用户信息:");
            System.out.print("ID: " + user.getId());
            System.out.print(",用户名: " + user.getUsername());
            System.out.print(",密码: " + user.getPassword());
            System.out.print(",姓名: " + user.getName());
            System.out.println(",年龄: " + user.getAge());
        }

        rs.close();
        pstmt.close();
        conn.close();
    }
图6 运行测试验证
图7 DQL小结

1.4 预编译SQL

图8 SQL注入和防止SQL注入
图9 SQL预编译提高性能
图10 SQL预编译小结

2. MyBatis

什么是MyBatis?
图11 MyBatis持久层框架
图12 MyBatis用于简化JDBC开发

2.1 入门程序

使用Mybatis查询所有用户数据
图13 创建工程
图14 配置依赖
图15 配置属性文件字符编码
applscation.properties
spring.application.name=springboot-mybatis-quickstart

# 配置数据库连接信息
spring.datasource.url=jdbc:mysql://www.duzhaojiang.cn:3306/web01?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=********
cn/zjy/mapper/UserMapper.java
package cn.zjy.mapper;
import cn.zjy.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;

@Mapper
// 应用程序在运行时,会自动的为该接口创建一个实现类对象(代理对象),
// 并且会自动将该实现类对象存入IOc容器-bean
public interface UserMapper {
    // 查询所有用户
    @Select("select * from user")
    public List<User> findAll();
}
src/main/java/cn/zjy/SpringbootMybatisQuickstartApplication.java
package cn.zjy;
import cn.zjy.mapper.UserMapper;
import cn.zjy.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;

@SpringBootTest
class SpringbootMybatisQuickstartApplicationTests {

    @Autowired
    private UserMapper userMapper;
    @Test
    public void testFindAll() {
        List<User> userList = userMapper.findAll();
        userList.forEach(System.out::println);
    }
}
图16 测试结果验证
图17 MyBatis入门程序小结
辅助配置-配置SQL提示
图18 注入SQL语言引用
图19 配置数据库连接用于SQL提示
辅助配置-配置Mybatis的日志输出
# 配置mybatis的日志输出
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
图20 Mybatis的日志输出验证

2.2 JDBC VS Mybatis

图21 JDBC VS Mybatis

2.3 数据库连接池

图22 数据库连接池
切换数据库连接池(从到):

pom.xml

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.19</version>
</dependency>

src/main/resources/application.properties

# 添加spring.datasource.type设置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# 原有的spring.datasource四大参数
spring.datasource.url=jdbc:mysql://localhost:3306/web
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=1234
图24 从Hikari到Druid数据库连接池
小结
图25 数据库连接池小结

2.4 增删改查操作

① 删除用户-delete
--SQL: 
delete from user where id = 5;
// 不带参变量
@Delete("delete from user where id = 5")
public void deleteById();

// 不带返回值,带参变量
@Delete("delete from user where id = #[id}")
public void deleteById(Integer id);

// 带返回值 影响的记录数
@Delete("delete from user where id = #[id}")
public Integer deleteById(Integer id);
图26 删除用户-delete
Mybatis中的#号与$号:
符号 说明 场景 优缺点
#{…} 占位符。执行时,会将#{…}替换为?,生成预编译SQL 参数值传递 安全、性能高 (推荐)
${…} 拼接符。直接将参数拼接在SQL语句中,存在SQL注入问题 表名、字段名动态设置时使用 不安全、性能低
图27 删除用户-delete
图28 delete小结
② 新增用户-insert
-- SQL:
insert into user(username,password,name,age) values('zhouyu','123456','周瑜',20);
@Insert("insert into user(username,password,name,age) values('zhouyu', '123456', '周瑜', 20)")
public void insert();


@Insert("insert into user(username,password,name,age) values(#{username},#{password},#{name},#{age})")  //对象属性名
public void insert(User user);
图29 insert
③ 修改用户-update
update user set username = 'zhouyu', password = '123456', name = '周瑜', age = 20 where id = 1

Mapper接口:

@Update("update user set username=#{username}, password=#{password}, name=#{name}, age=#{age} where id=#{id}")
public void update(User user);
④ 查询用户-select
select * from user where username = 'zhouyu' and password = '666888';
@Select("select * from user where username=#{username} and password=#{password}")
public User findByUsernameAndPassword(
    @Param("username") String username, @Param("password") String password);

@Param注解的作用是为接口的方法形参起名字的。

说明:基于官方骨架创建的springboot项目中,接口编译时会保留方法形参名,@Param注解可以省略 (#{形参名})。

@Select("select * from user where username=#{uname} and password=#{pwd}")
public User findByUsernameAndPassword(String uname, String pwd);
图30 @Select和@Param注解
cn/zjy/mapper/UserMapper.java
package cn.zjy.mapper;

import cn.zjy.pojo.User;
import org.apache.ibatis.annotations.*;
import java.util.List;

@Mapper
// 应用程序在运行时,会自动的为该接口创建一个实现类对象(代理对象),
// 并且会自动将该实现类对象存入IOc容器-bean
public interface UserMapper {
    // 查询所有用户
    @Select("select * from user")
    public List<User> findAll();

    /**
     * 根据ID删除用户
     */
    @Delete("delete from user where id = #{id}")
    //public void deleteById(Integer id);
    public Integer deleteById(Integer id);
    /**
     * 新增用户
     */
    @Insert("insert into user(username, password, name, age) values (#{username},#{password}, #{name}, #{age})")
    public void insert(User user);

    /**
     * 更新用户
     */
     @Update("update user set username = #{username}, password = #{password}, name = #{name}, age = #{age} where id = #{id}")
     public void update(User user);

     /**
     * 根据用户名和密码查询用户
     */
     @Select("select * from user where username = #{username} and password = #{password}")
     public User findByUsernameAndpassword(@Param("username") String username, @Param("password") String password);

    /**
     * 根据用户名和密码查询用户
     */
    @Select("select * from user where username = #{uname} and password = #{pword}")
    public User findByUsernameAndpassword2(String uname, String pword);

}
src/test/java/cn/zjy/SpringbootMybatisQuickstartApplicationTests.java
package cn.zjy;
import cn.zjy.mapper.UserMapper;
import cn.zjy.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;

@SpringBootTest
class SpringbootMybatisQuickstartApplicationTests {

    @Autowired
    private UserMapper userMapper;
    @Test
    public void testFindAll() {
        List<User> userList = userMapper.findAll();
        userList.forEach(System.out::println);
    }

    @Test
    public void testDeleteById() {
        Integer i = userMapper.deleteById(4);
        System.out.println("执行完毕,影响的记录数:" + i);
    }

    /**
     * 测试新增
     */
    @Test
    public void testInsert() {
        User user = new User(null,"gaoyuanyuan","666888","高圆圆",18);
        userMapper.insert(user);
    }

    /**
     * 测试更新
     */
    @Test
    public void testUpdate() {
        User user = new User(2, "zhouyu", "666888", "周瑜", 8);
        userMapper.update(user);
    }

    /**
     * 测试查询
     */
    @Test
    public void testFindByUsernameAndPassword(){
        User user= userMapper.findByUsernameAndpassword("zhouyu","666888");
        System.out.println(user);
    }

    /**
     * 测试查询
     */
    @Test
    public void testFindByUsernameAndPassword2(){
        User user= userMapper.findByUsernameAndpassword2("zhouyu","666888");
        System.out.println(user);
    }
}
图31 MyBatis增删改查测试验证
@Param注解的使用场景?
图32 @Param注解小结

2.5 XML映射配置

图33 XML映射配置
src/main/java/cn/zjy/mapper/UserMapper.java 定义了下列方法
    // 查询所有用户
    // src/main/resources/cn/zjy/mapper/UserMapper.xml 配置了映射关系
    public List<User> findAll2();
src/main/resources/cn/zjy/mapper/UserMapper.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">
<!-- namespace:绑定对应的Java接口全类名 -->
<mapper namespace="cn.dzj.mapper.UserMapper">
    <!-- 所有SQL标签(select/insert/update/delete)写在这里 -->
    <select id="findAll2" resultType="cn.dzj.pojo.User">
        select ID, USERNAME, PASSWORD, NAME, AGE from user
    </select>
</mapper>
src/test/java/cn/dzj/SpringbootMybatisQuickstartApplicationTests.java 添加了下列的测试方法
    @Test
    public void testFindAll2() {
        List<User> userList = userMapper.findAll();
        userList.forEach(System.out::println);
    }
图34 XML映射配置验证
小结
图35 MyBatis 的 XML 映射文件小结
XML映射文件--辅助配置
application.properties
#指定XML映射配置文件的位置
mybatis.mapper-locations=classpath:mapper/*.xml
图36 XML映射文件--辅助配置

3.SpringBoot配置文件

3.0 问题的提出
图37 springboot配置文件存在的问题

3.1 配置文件格式

图38 application.properties和application.yml的区别

3.2 yml配置文件

图39 yml格式
application.yml
spring:
  application:
    name: springboot-mybatis-quickstart
  #数据库的连接信息
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/web01
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 1234

#Mybatis的相关配置
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:mapper/*.xml

# 定义对象/Map集合
user:
  name: 张三
  age: 18
  password: 123456

# 定义数组/List/Set集合
hobby:
  - java
  - game
  - sport
图40 yml小结

3.3 springboot application配置文件的优先级

在 Spring Boot 中,application.propertiesapplication.yml 是两种常用的配置文件格式,它们的优先级规则主要取决于文件位置文件类型,核心结论如下:

1. 同目录下:application.properties 优先级高于 application.yml
2. 不同目录下:遵循「位置优先级」,与文件类型无关
3. 总结

①②③④⑤⑥⑦⑧⑨⑩

MyBatis 的 XML 映射文件


MyBatis 的 XML 映射文件核心是通过 <mapper> 根节点组织 SQL 语句,每个 SQL 操作对应特定标签(如 <select><insert>),并通过属性与 Java 接口 / 方法绑定。以下是完整的格式规范、核心标签及示例:

一、整体结构(根节点 <mapper>

所有 SQL 语句必须包裹在 <mapper> 标签内,其唯一核心属性 namespace 用于绑定 Java 接口全类名(确保接口与 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">

<!-- namespace:绑定对应的Java接口全类名 -->
<mapper namespace="com.example.mapper.UserMapper">

    <!-- 所有SQL标签(select/insert/update/delete)写在这里 -->

</mapper>

二、核心 SQL 标签(CRUD 操作)

每个标签对应一种数据库操作,需通过 id 绑定接口中的方法名,并通过 parameterType(可选)、resultType/resultMap(必选)定义参数和返回值。

1. 查询:<select>

用于执行 SELECT 语句,核心是指定返回类型(简单类型用 resultType,复杂对象映射用 resultMap)。

属性 说明
id 与接口方法名完全一致,唯一标识
parameterType 传入参数类型(全类名或别名,如 java.lang.Long/String,MyBatis 可自动推断,可选)
resultType 返回值类型(简单类型 / JavaBean 全类名,适用于字段名与属性名完全匹配的场景)
resultMap 引用自定义的 <resultMap>(适用于字段名与属性名不匹配、关联查询等复杂场景)

示例 1:查询单个对象(字段名与属性名一致)

<!-- 接口方法:User selectById(Long id); -->
<select id="selectById" parameterType="java.lang.Long" resultType="com.example.entity.User">
    SELECT id, username, age, create_time 
    FROM user 
    WHERE id = #{id}  <!-- #{}:参数占位符(预编译,防止SQL注入) -->
</select>

示例 2:查询列表(返回 List

<!-- 接口方法:List<User> selectByAge(Integer age); -->
<select id="selectByAge" parameterType="java.lang.Integer" resultType="com.example.entity.User">
    SELECT id, username, age, create_time 
    FROM user 
    WHERE age > #{age}
</select>

2. 新增:<insert>

用于执行 INSERT 语句,支持返回自增主键(如 MySQL 的 AUTO_INCREMENT)。

特殊属性 说明
useGeneratedKeys 是否使用自增主键(true/false,仅支持支持自增的数据库,如 MySQL)
keyProperty 自增主键对应的 JavaBean 属性名(如 id),用于将主键值回写到对象中

示例:新增用户并返回自增主键

<!-- 接口方法:int insertUser(User user); -->
<insert id="insertUser" parameterType="com.example.entity.User" 
        useGeneratedKeys="true" keyProperty="id">
    INSERT INTO user (username, age, create_time)
    VALUES (#{username}, #{age}, #{createTime})  <!--  #{属性名}:匹配User类的getter/setter -->
</insert>

3. 更新:<update>

用于执行 UPDATE 语句,无特殊属性,核心是通过参数动态更新字段。

示例:根据 ID 更新用户信息

<!-- 接口方法:int updateUserById(User user); -->
<update id="updateUserById" parameterType="com.example.entity.User">
    UPDATE user 
    SET username = #{username}, 
        age = #{age}, 
        create_time = #{createTime}
    WHERE id = #{id}
</update>

4. 删除:<delete>

用于执行 DELETE 语句,结构最简单,仅需绑定参数和 SQL 逻辑。

示例:根据 ID 删除用户

<!-- 接口方法:int deleteById(Long id); -->
<delete id="deleteById" parameterType="java.lang.Long">
    DELETE FROM user 
    WHERE id = #{id}
</delete>

三、复杂场景:<resultMap> 与动态 SQL

1. <resultMap>:自定义结果映射

当数据库字段名与 JavaBean 属性名不匹配(如字段 create_time vs 属性 createTime),或需要关联查询(如一对一、一对多)时,需定义 <resultMap>

示例:解决字段名与属性名不匹配
<!-- 1. 定义resultMap:映射user表字段到User类属性 -->
<resultMap id="UserResultMap" type="com.example.entity.User">
    <!-- id:主键映射 -->
    <id column="id" property="id"/>
    <!-- result:普通字段映射(column=数据库字段名,property=JavaBean属性名) -->
    <result column="username" property="username"/>
    <result column="age" property="age"/>
    <result column="create_time" property="createTime"/>  <!-- 字段名与属性名不一致 -->
</resultMap>

<!-- 2. 在select中引用resultMap -->
<select id="selectById" parameterType="java.lang.Long" resultMap="UserResultMap">
    SELECT id, username, age, create_time 
    FROM user 
    WHERE id = #{id}
</select>

2. 动态 SQL:条件拼接

MyBatis 提供动态标签解决「条件不确定」的 SQL 拼接(如多条件查询、非空字段更新),常用标签如下:

动态标签 说明
<if> 单条件判断(test 属性写判断表达式,如 test="username != null"
<where> 自动处理 AND/OR 前缀,避免 SQL 语法错误(替代 WHERE 1=1
<foreach> 遍历集合(如 List/Array,用于 IN 条件或批量操作)

示例:多条件动态查询(用户名 + 年龄筛选)

<!-- 接口方法:List<User> selectByCondition(User user); -->
<select id="selectByCondition" parameterType="com.example.entity.User" resultMap="UserResultMap">
    SELECT id, username, age, create_time 
    FROM user 
    <where>  <!-- 自动移除多余的AND/OR -->
        <if test="username != null and username != ''">
            AND username LIKE CONCAT('%', #{username}, '%')  <!-- 模糊查询 -->
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
    </where>
</select>

四、关键注意事项

  1. 参数占位符:优先使用 #{}, 生成预编译 SQL(防止注入);${} 直接拼接字符串(仅用于非用户输入的场景,如表名动态切换)。
  2. 标签闭合:所有 XML 标签必须正确闭合(如 <select>...</select>,不可省略结束标签)。
  3. 命名规范namespace 必须与接口全类名一致,id 必须与接口方法名一致,否则 MyBatis 无法绑定。
  4. 注释:支持 XML 标准注释 <!-- 注释内容 -->,建议标注 SQL 功能和参数说明。

返回