什么是 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:
# 数据库连接URL
url: 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, -- 负责该课程的教师ID
class_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, -- 该课程的教师ID
class_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 address
FROM student s
JOIN teacher t ON s.teacher_id = t.id
WHERE 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接口
* 用于访问数据库中的学生信息
*/
@Autowired
private 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 {
/**
* 注入的学生服务,用于调用学生相关业务逻辑
*/
@Autowired
private 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 {
@Autowired
private 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实例,用于调用老师相关的业务逻辑
*/
@Autowired
private 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 teacher
SET 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 {
@Autowired
private 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实例,用于调用老师相关的业务逻辑
*/
@Autowired
private 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 teacher
SET phone_number = #{phoneNumber}
WHERE name = #{name} AND email = #{email}
</update>
<!-- 定义删除老师信息的 SQL 语句 -->
<delete id="deleteTeacher" parameterType="com.example.demo.model.Teacher">
DELETE FROM teacher
WHERE 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 {
@Autowired
private 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实例,用于调用老师相关的业务逻辑
*/
@Autowired
private 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 teacher
WHERE 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 映射文件中可以通过名字来引用参数,更加清晰和安全。
总结:能用 #{} 的地方就用 #{},尽量少用 ${}!!!