MyBatis 快速入门
什么是 MyBatis?
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
和其它持久化层技术对比
JDBC
优点:
- 灵活性:可以直接使用原生 SQL,对 SQL 有完全的控制。
- 性能:由于没有额外的抽象层,通常性能较高。
缺点:
- 耦合度高:SQL 语句硬编码在 Java 代码中,代码耦合度高,不易维护。
- 冗长:需要编写大量重复的样板代码(如连接、关闭资源等),开发效率低。
- 维护难:SQL 语句的变动需要频繁修改代码,影响维护性。
Hibernate 和 JPA
优点:
- 开发效率高:提供了对象关系映射(ORM)功能,减少了大量的 JDBC 代码,开发效率高。
- 自动化:通过注解或 XML 配置实现数据表和 Java 对象的映射,简化开发。
- 支持缓存:内置一级缓存和二级缓存,能提高性能。
- 跨数据库支持:可以通过配置适配不同的数据库。
缺点:
- 复杂 SQL:对于复杂的查询,可能需要使用 HQL 或原生 SQL,有时绕过 ORM 框架进行优化。
- 性能问题:反射和动态代理等技术可能导致性能下降,尤其是在大规模数据操作时。
- 映射难题:大量字段或复杂的 POJO 映射可能导致配置困难,映射过程中的错误可能难以排查。
MyBatis
优点:
- 轻量级:比 Hibernate 和 JPA 更轻量,不涉及 ORM 复杂度,直接映射 SQL 到 Java 对象。
- 灵活性:允许编写原生 SQL,使得复杂的查询和优化变得更容易。
- 分离关注点:SQL 和 Java 代码分开,保持代码清晰,Java 代码专注于业务逻辑,SQL 专注于数据操作。
缺点:
- 开发效率:需要手动编写 SQL 和映射语句,开发效率可能稍逊于 Hibernate 和 JPA。
- 维护性:虽然 SQL 和 Java 代码分离,但大量 SQL 语句的编写和维护仍然需要额外的工作。
总结
- JDBC 适合对性能有严格要求的应用,或者需要高度自定义 SQL 的场景,但缺乏框架支持,开发维护难度较大。
- Hibernate 和 JPA 更适合需要快速开发且数据库操作较为标准化的场景,适合使用 ORM 的优势,但可能在处理复杂查询时性能和灵活性受限。
- MyBatis 提供了 SQL 和业务逻辑的清晰分离,适合需要复杂 SQL 查询并希望有较高性能控制的场景,同时开发效率较 JDBC 更高,但不及 Hibernate 和 JPA。
安装
要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可。
如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:
<dependencies><!-- 引入Spring Boot Web启动器,用于创建Web应用程序 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 引入Spring Boot测试启动器,用于编写和运行单元测试 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 引入MyBatis Spring Boot启动器,简化MyBatis与Spring Boot的集成 --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version></dependency><!-- 引入MySQL数据库驱动,用于连接MySQL数据库 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version> <!-- 使用对应的数据库驱动 --></dependency><!-- 引入Lombok库,简化Java代码,自动生成getter、setter等 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version><scope>provided</scope></dependency></dependencies>
配置数据库连接字符串和MyBatis
如果配置文件是application.properties,代码如下:
# 数据库配置
# 数据库连接URL
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_test
# 数据库用户名
spring.datasource.username=root
# 数据库密码
spring.datasource.password=123456
# 数据库驱动类名
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver# MyBatis配置
# 指定MyBatis的日志实现方式
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# 开启下划线到驼峰命名的转换支持
mybatis.configuration.map-underscore-to-camel-case=true
# 指定Mapper文件的位置
mybatis.mapper-locations=classpath*:mappers/*.xml
如果配置文件是application.yml,代码如下:
# 配置Spring框架的数据源信息
spring:datasource:# 数据库连接URLurl: jdbc:mysql://localhost:3306/mybatis_test# 数据库用户名username: root# 数据库密码password: 123456# 数据库驱动类名称driver-class-name: com.mysql.cj.jdbc.Driver# 配置MyBatis框架的相关属性
mybatis:configuration:# 日志实现类,这里配置的是输出到控制台的日志实现log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 是否开启下划线到驼峰命名的转换,设为true后,MyBatis会自动将列名从下划线命名转换为驼峰命名map-underscore-to-camel-case: true# 指定Mapper文件的位置,这里使用通配符匹配所有Mapper文件mapper-locations: classpath:mappers/*.xml
MySQL
CREATE DATABASE IF NOT EXISTS mybatis_test;
USE mybatis_test;
-- 创建教师表
CREATE TABLE IF NOT EXISTS teacher (id INT AUTO_INCREMENT PRIMARY KEY, -- 教师的唯一标识符name VARCHAR(255) NOT NULL, -- 教师姓名course_name VARCHAR(255), -- 教师教授的课程名称is_employed TINYINT(1) NOT NULL, -- 是否在职 (1: 在职, 0: 不在职)title TINYINT(1) NOT NULL, -- 教师职称 (0: 初级, 1: 高级)email VARCHAR(255), -- 教师邮箱phone_number VARCHAR(20) -- 教师电话
);-- 创建课程表
CREATE TABLE IF NOT EXISTS course (id INT AUTO_INCREMENT PRIMARY KEY, -- 课程的唯一标识符course_name VARCHAR(255) NOT NULL, -- 课程名称teacher_id INT, -- 负责该课程的教师IDclass_name VARCHAR(255), -- 班级名称schedule VARCHAR(255), -- 上课时间安排location VARCHAR(255), -- 上课地点FOREIGN KEY (teacher_id) REFERENCES teacher(id) -- 外键,引用教师表中的id
);-- 创建学生表
CREATE TABLE IF NOT EXISTS student (id INT AUTO_INCREMENT PRIMARY KEY, -- 学生的唯一标识符name VARCHAR(255) NOT NULL, -- 学生姓名course_name VARCHAR(255), -- 学生所选课程名称teacher_id INT, -- 该课程的教师IDclass_name VARCHAR(255), -- 班级名称enrollment_date DATE, -- 入学日期address VARCHAR(255), -- 学生住址FOREIGN KEY (teacher_id) REFERENCES teacher(id) -- 外键,引用教师表中的id
);-- 插入教师数据
INSERT INTO teacher (name, course_name, is_employed, title, email, phone_number) VALUES
('张三', '数学', 1, 0, 'zhangsan@example.com', '1234567890'),
('李四', '语文', 1, 1, 'lisi@example.com', '0987654321'),
('王五', '英语', 0, 0, 'wangwu@example.com', '1122334455');-- 插入课程数据
INSERT INTO course (course_name, teacher_id, class_name, schedule, location) VALUES
('数学', 1, '一班', '周一 9:00-11:00', '教室101'),
('语文', 2, '二班', '周三 13:00-15:00', '教室102'),
('英语', 3, '三班', '周五 15:00-17:00', '教室103');-- 插入学生数据
INSERT INTO student (name, course_name, teacher_id, class_name, enrollment_date, address) VALUES
('小明', '数学', 1, '一班', '2024-09-01', '北京市朝阳区'),
('小红', '语文', 2, '二班', '2024-09-01', '上海市浦东新区'),
('小李', '英语', 3, '三班', '2024-09-01', '深圳市南山区'),
('小王', '数学', 1, '一班', '2024-09-01', '广州市番禺区'),
('小赵', '语文', 2, '二班', '2024-09-01', '北京市海淀区'),
('小钱', '英语', 3, '三班', '2024-09-01', '上海市徐汇区'),
('小刚', '英语', 3, '三班', '2024-09-01', '广州市天河区');
MyBatis XML基础操作
查询操作(SELECT)
当查询2024-09-01入学学生的学生的唯一标识符、学生姓名、学生所选课程名称、该课程的教师、班级名称、学生住址的时候
<?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,负责数据库中学生信息的操作 -->
<mapper namespace="com.example.mybatisdemo.mapper.StudentMapper"><!-- 根据入学日期查询学生信息 --><!-- 说明:此SQL语句用于根据学生的入学日期获取学生的基本信息、课程名称、教师名称、班级名称和地址 --><select id="getStudentsByEnrollmentDate" resultType="com.example.mybatisdemo.model.StudentInfo">SELECT s.id AS studentId, s.name AS studentName, s.course_name AS courseName, t.name AS teacherName, s.class_name AS className, s.address AS addressFROM student sJOIN teacher t ON s.teacher_id = t.idWHERE s.enrollment_date = #{date}</select></mapper>
package com.example.mybatisdemo.model;import lombok.Data;/*** 学生信息模型类* 用于描述学生的基本信息,包括学生ID、姓名、课程名称、教师名称、班级名称和地址*/
@Data
public class StudentInfo {// 学生ID,用于唯一标识一个学生private Integer studentId;// 学生姓名private String studentName;// 课程名称,表示学生所选的课程private String courseName;// 教师名称,表示授课教师private String teacherName;// 班级名称,表示学生所在的班级private String className;// 学生的地址信息private String address;
}
package com.example.mybatisdemo.mapper;import com.example.mybatisdemo.model.StudentInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;import java.util.List;/*** 学生信息的持久层接口,负责与数据库进行交互*/
@Repository
@Mapper
public interface StudentMapper {/*** 根据入学日期获取学生列表** @param date 入学日期,用于数据库查询条件* @return 匹配入学日期的学生列表*/List<StudentInfo> getStudentsByEnrollmentDate(@Param("date") String date);
}
package com.example.mybatisdemo.service;import com.example.mybatisdemo.mapper.StudentMapper;
import com.example.mybatisdemo.model.StudentInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;/*** 学生服务类* 该类用于提供关于学生信息的服务,包括根据入学日期查询学生信息等*/
@Service
public class StudentService {/*** 注入学生Mapper接口* 用于访问数据库中的学生信息*/@Autowiredprivate StudentMapper studentMapper;/*** 根据入学日期获取学生列表** @param date 入学日期,格式为yyyy-MM-dd* @return 与给定入学日期对应的学生信息列表*/public List<StudentInfo> getStudentsByEnrollmentDate(String date) {// 调用Mapper方法,根据入学日期查询学生信息列表return studentMapper.getStudentsByEnrollmentDate(date);}
}
package com.example.mybatisdemo.controller;import com.example.mybatisdemo.model.StudentInfo;
import com.example.mybatisdemo.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** 学生控制器类,用于处理与学生相关的信息请求*/
@RestController
@RequestMapping("/students")
public class StudentController {/*** 注入的学生服务,用于调用学生相关业务逻辑*/@Autowiredprivate StudentService studentService;/*** 根据入学日期获取学生列表** @param date 入学日期,用于筛选学生的入学时间条件* @return 符合入学日期条件的学生列表*/@GetMapping("/enrollment")public List<StudentInfo> getStudentsByEnrollmentDate(@RequestParam String date) {return studentService.getStudentsByEnrollmentDate(date);}
}
插入操作(INSERT)
当新老师入职时,在MySQL插入一条老师信息
TeacherMapper.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.example.demo.mapper.TeacherMapper"><!-- 定义插入老师信息的 SQL 语句 --><insert id="insertTeacher" parameterType="com.example.demo.model.Teacher">INSERT INTO teacher (name, course_name, is_employed, title, email, phone_number)VALUES (#{name}, #{courseName}, #{isEmployed}, #{title}, #{email}, #{phoneNumber})</insert></mapper>
Java 代码
package com.example.demo.model;import lombok.Data;/*** Teacher类表示教师的信息* 该类使用了Lombok的@Data注解,自动生成getter、setter、toString、equals和hashCode方法*/
@Data
public class Teacher {// 教师的ID,唯一标识private Integer id;// 教师的姓名private String name;// 教师所教授的课程名称private String courseName;// 标识教师是否受雇private Boolean isEmployed;// 标识教师是否有职称private Boolean title;// 教师的电子邮件地址private String email;// 教师的电话号码private String phoneNumber;
}
package com.example.demo.mapper;import com.example.demo.model.Teacher;
import org.apache.ibatis.annotations.Mapper;/*** TeacherMapper 接口定义了与 Teacher 实体相关的操作,专注于数据访问和操作。* 通过 @Mapper 注解,将其指定为 MyBatis 映射器接口。*/
@Mapper
public interface TeacherMapper {/*** 将新的教师记录插入数据库。* 此方法使用传入的 Teacher 对象在数据库中执行插入操作,持久化教师的信息。** @param teacher 包含待插入教师信息的 Teacher 对象*/void insertTeacher(Teacher teacher);
}
package com.example.demo.service;import com.example.demo.model.Teacher;
import com.example.demo.mapper.TeacherMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** TeacherService类用于处理教师相关的业务逻辑*/
@Service
public class TeacherService {@Autowiredprivate TeacherMapper teacherMapper;/*** 添加教师信息** @param teacher 教师对象,包含要插入数据库的教师信息*/public void addTeacher(Teacher teacher) {teacherMapper.insertTeacher(teacher);}
}
package com.example.demo.controller;import com.example.demo.model.Teacher;
import com.example.demo.service.TeacherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;/*** TeacherController类用于处理与老师相关的HTTP请求*/
@RestController
@RequestMapping("/teachers")
public class TeacherController {/*** TeacherService实例,用于调用老师相关的业务逻辑*/@Autowiredprivate TeacherService teacherService;/*** 处理添加老师的请求** @param teacher 要添加的老师对象,从请求体中获取* @return 添加成功后的提示信息*/@PostMapping("/add")public String addTeacher(@RequestBody Teacher teacher) {teacherService.addTeacher(teacher);return "老师添加成功!";}
}
老师添加成功!
更新操作(UPDATE)
当新老师入职时,教师电话填写错了,得修改一下!
得在 TeacherMapper.xml 中添加一个新的 SQL 语句来更新教师的电话信息。
<?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.example.demo.mapper.TeacherMapper"><!-- 定义插入老师信息的 SQL 语句 --><insert id="insertTeacher" parameterType="com.example.demo.model.Teacher">INSERT INTO teacher (name, course_name, is_employed, title, email, phone_number)VALUES (#{name}, #{courseName}, #{isEmployed}, #{title}, #{email}, #{phoneNumber})</insert><!-- 定义更新教师电话的 SQL 语句 --><update id="updateTeacherPhoneNumber" parameterType="com.example.demo.model.Teacher">UPDATE teacherSET phone_number = #{phoneNumber}WHERE name = #{name} AND email = #{email}</update>
</mapper>
package com.example.demo.mapper;import com.example.demo.model.Teacher;
import org.apache.ibatis.annotations.Mapper;/*** TeacherMapper 接口定义了与 Teacher 实体相关的操作,专注于数据访问和操作。* 通过 @Mapper 注解,将其指定为 MyBatis 映射器接口。*/
@Mapper
public interface TeacherMapper {/*** 将新的教师记录插入数据库。* 此方法使用传入的 Teacher 对象在数据库中执行插入操作,持久化教师的信息。** @param teacher 包含待插入教师信息的 Teacher 对象*/void insertTeacher(Teacher teacher);/*** 更新教师的联系电话。* 通过此方法,可以根据 Teacher 对象中的信息更新数据库中教师的联系电话。** @param teacher 包含待更新教师信息的 Teacher 对象*/void updateTeacherPhoneNumber(Teacher teacher);
}
package com.example.demo.service;import com.example.demo.model.Teacher;
import com.example.demo.mapper.TeacherMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** TeacherService类用于处理教师相关的业务逻辑*/
@Service
public class TeacherService {@Autowiredprivate TeacherMapper teacherMapper;/*** 添加教师信息** @param teacher 教师对象,包含要插入数据库的教师信息*/public void addTeacher(Teacher teacher) {teacherMapper.insertTeacher(teacher);}/*** 更新教师的电话号码** @param teacher 教师对象,包含需要更新电话号码的教师信息*/public void updateTeacherPhoneNumber(Teacher teacher) {teacherMapper.updateTeacherPhoneNumber(teacher);}
}
package com.example.demo.controller;import com.example.demo.model.Teacher;
import com.example.demo.service.TeacherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;/*** TeacherController类用于处理与老师相关的HTTP请求*/
@RestController
@RequestMapping("/teachers")
public class TeacherController {/*** TeacherService实例,用于调用老师相关的业务逻辑*/@Autowiredprivate TeacherService teacherService;/*** 处理添加老师的请求** @param teacher 要添加的老师对象,从请求体中获取* @return 添加成功后的提示信息*/@PostMapping("/add")public String addTeacher(@RequestBody Teacher teacher) {teacherService.addTeacher(teacher);return "老师添加成功!";}/*** 处理更新老师电话号码的请求** @param teacher 包含需要更新电话号码的老师对象,从请求体中获取* @return 更新成功后的提示信息*/@PutMapping("/updatePhone")public String updateTeacherPhoneNumber(@RequestBody Teacher teacher) {teacherService.updateTeacherPhoneNumber(teacher);return "教师电话号码更新成功!";}
}
删除操作(DELETE)
当新老师入职后,发现学校太垃圾了,然后跑路了,这时MySQL得删除一下!
<?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.example.demo.mapper.TeacherMapper"><!-- 定义插入老师信息的 SQL 语句 --><insert id="insertTeacher" parameterType="com.example.demo.model.Teacher">INSERT INTO teacher (name, course_name, is_employed, title, email, phone_number)VALUES (#{name}, #{courseName}, #{isEmployed}, #{title}, #{email}, #{phoneNumber})</insert><!-- 定义更新教师电话的 SQL 语句 --><update id="updateTeacherPhoneNumber" parameterType="com.example.demo.model.Teacher">UPDATE teacherSET phone_number = #{phoneNumber}WHERE name = #{name} AND email = #{email}</update><!-- 定义删除老师信息的 SQL 语句 --><delete id="deleteTeacher" parameterType="com.example.demo.model.Teacher">DELETE FROM teacherWHERE name = #{name} AND email = #{email}</delete>
</mapper>
package com.example.demo.mapper;import com.example.demo.model.Teacher;
import org.apache.ibatis.annotations.Mapper;/*** TeacherMapper 接口定义了与 Teacher 实体相关的操作,专注于数据访问和操作。* 通过 @Mapper 注解,将其指定为 MyBatis 映射器接口。*/
@Mapper
public interface TeacherMapper {/*** 将新的教师记录插入数据库。* 此方法使用传入的 Teacher 对象在数据库中执行插入操作,持久化教师的信息。** @param teacher 包含待插入教师信息的 Teacher 对象*/void insertTeacher(Teacher teacher);/*** 更新教师的联系电话。* 通过此方法,可以根据 Teacher 对象中的信息更新数据库中教师的联系电话。** @param teacher 包含待更新教师信息的 Teacher 对象*/void updateTeacherPhoneNumber(Teacher teacher);/*** 删除教师记录。* 此方法允许从数据库中删除一个教师记录,具体实现需要确定删除的逻辑。** @param teacher 需要从数据库中删除的 Teacher 对象*/void deleteTeacher(Teacher teacher);
}
package com.example.demo.service;import com.example.demo.model.Teacher;
import com.example.demo.mapper.TeacherMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** TeacherService类用于处理教师相关的业务逻辑*/
@Service
public class TeacherService {@Autowiredprivate TeacherMapper teacherMapper;/*** 添加教师信息** @param teacher 教师对象,包含要插入数据库的教师信息*/public void addTeacher(Teacher teacher) {teacherMapper.insertTeacher(teacher);}/*** 更新教师的电话号码** @param teacher 教师对象,包含需要更新电话号码的教师信息*/public void updateTeacherPhoneNumber(Teacher teacher) {teacherMapper.updateTeacherPhoneNumber(teacher);}/*** 删除教师信息** @param teacher 教师对象,包含需要删除的教师信息*/public void deleteTeacher(Teacher teacher) {teacherMapper.deleteTeacher(teacher);}
}
package com.example.demo.controller;import com.example.demo.model.Teacher;
import com.example.demo.service.TeacherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;/*** TeacherController类用于处理与老师相关的HTTP请求*/
@RestController
@RequestMapping("/teachers")
public class TeacherController {/*** TeacherService实例,用于调用老师相关的业务逻辑*/@Autowiredprivate TeacherService teacherService;/*** 处理添加老师的请求** @param teacher 要添加的老师对象,从请求体中获取* @return 添加成功后的提示信息*/@PostMapping("/add")public String addTeacher(@RequestBody Teacher teacher) {teacherService.addTeacher(teacher);return "老师添加成功!";}/*** 处理更新老师电话号码的请求** @param teacher 包含需要更新电话号码的老师对象,从请求体中获取* @return 更新成功后的提示信息*/@PutMapping("/updatePhone")public String updateTeacherPhoneNumber(@RequestBody Teacher teacher) {teacherService.updateTeacherPhoneNumber(teacher);return "教师电话号码更新成功!";}/*** 处理删除老师的请求** @param teacher 要删除的老师对象,从请求体中获取* @return 删除成功后的提示信息*/@DeleteMapping("/delete")public String deleteTeacher(@RequestBody Teacher teacher) {teacherService.deleteTeacher(teacher);return "教师删除成功!";}
}
动态SQL
动态 SQL 是指在执行 SQL 语句时,根据不同的条件动态生成 SQL 语句。这样可以提高灵活性和适应性,使得 SQL 操作可以根据实际情况进行调整。MyBatis 提供了多种动态 SQL 生成的方式,通过 <if>、<choose>、<foreach>、<where> 和 <trim> 等标签实现。
<if> 标签
if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之标签中的内容不会执行
例如,可能需要根据用户是否提供了某些参数来决定是否在 WHERE 子句中包含这些条件。
示例
<mapper namespace="com.example.demo.mapper.TeacherMapper"><select id="findTeachers" parameterType="map" resultType="com.example.demo.model.Teacher">SELECT * FROM teacher<where><!-- 条件判断,如果name参数不为空,则添加到WHERE子句中作用:通过#{name}占位符动态设置姓名过滤条件--><if test="name != null">AND name = #{name}</if><!-- 条件判断,如果email参数不为空,则添加到WHERE子句中作用:通过#{email}占位符动态设置电子邮件过滤条件--><if test="email != null">AND email = #{email}</if></where></select></mapper>
<choose>, <when>, 和 <otherwise> 标签
choose、when、otherwise相当于if...else if..else
<mapper namespace="com.example.demo.mapper.TeacherMapper"><select id="findTeacherByType" parameterType="map" resultType="com.example.demo.model.Teacher">SELECT * FROM teacher<where><choose><when test="type == 'full-time'">AND employment_type = 'full-time'</when><when test="type == 'part-time'">AND employment_type = 'part-time'</when><otherwise>AND employment_type IS NOT NULL</otherwise></choose></where></select></mapper>
<foreach> 标签
<foreach> 标签用于动态生成一个 SQL 片段,适用于处理集合数据,如 IN 子句。
属性:
- collection:设置要循环的数组或集合
- item:表示集合或数组中的每一个数据
- separator:设置循环体之间的分隔符
- open:设置foreach标签中的内容的开始符
- close:设置foreach标签中的内容的结束符
示例
<mapper namespace="com.example.demo.mapper.TeacherMapper"><select id="findTeachersByIds" parameterType="list" resultType="com.example.demo.model.Teacher">SELECT * FROM teacherWHERE id IN<!-- 对列表中的每个id生成一个元组,将所有元组用逗号分隔 --><foreach item="id" collection="list" open="(" close=")" separator=",">#{id}</foreach></select></mapper>
<where> 标签
<where> 标签用于自动添加 WHERE 关键字,并处理前面的 AND 或 OR 操作符,避免在条件为空时生成多余的 AND。
where和if一般结合使用:
a>若where标签中的if条件都不满足,则where标签没有任何功能,即不会添加where关键字
b>若where标签中的if条件满足,则where标签会自动添加where关键字,并将条件最前方多余的
and去掉
注意:where标签不能去掉条件最后多余的and
示例
<mapper namespace="com.example.demo.mapper.TeacherMapper"><select id="findTeachers" parameterType="map" resultType="com.example.demo.model.Teacher">SELECT * FROM teacher<where><if test="name != null">name = #{name}</if><if test="email != null">AND email = #{email}</if></where></select></mapper>
<trim> 标签
<trim> 标签用于去掉 SQL 语句中多余的前缀或后缀,例如多余的 AND 或 OR。
常用属性:
- prefix:在trim标签中的内容的前面添加某些内容
- prefixOverrides:在trim标签中的内容的前面去掉某些内容
- suffix:在trim标签中的内容的后面添加某些内容
- suffixOverrides:在trim标签中的内容的后面去掉某些内容
示例
<mapper namespace="com.example.demo.mapper.TeacherMapper"><select id="findTeachers" parameterType="map" resultType="com.example.demo.model.Teacher">SELECT * FROM teacher<trim prefix="WHERE" prefixOverrides="AND |OR "><if test="name != null">AND name = #{name}</if><if test="email != null">AND email = #{email}</if></trim></select></mapper>
#{}和${}的区别
#{}
:用于传递参数值,并自动进行 SQL 注入防护。MyBatis 会将它们替换为参数的占位符,并进行适当的转义。例如:
<select id="findTeacherById" parameterType="int" resultType="Teacher">SELECT * FROM teacher WHERE id = #{id}
</select>
这里 #{id} 会被替换为 ?,并在执行时由 MyBatis 安全地绑定参数。
${}:用于直接插入文本或 SQL 片段,不进行任何转义或防护。例子:
<select id="findTeacherByColumn" parameterType="map" resultType="Teacher">SELECT * FROM teacher WHERE ${columnName} = #{value}
</select>
如何选择 #{} 和 ${}
- 使用 #{} 可以有效避免 SQL 注入,特别是在传递参数值时。而 ${} 适用于动态 SQL 片段,如表名或排序字段,但需谨慎使用。
- @Param 注解确实为参数提供了名称,使得在 SQL 映射文件中可以通过名字来引用参数,更加清晰和安全。
总结:能用 #{} 的地方就用 #{},尽量少用 ${}!!!