Mybatis
MyBatis 是一个优秀的持久层框架,它简化了 JDBC 的使用,允许开发者通过 XML 或注解方式配置 SQL 语句,并自动将查询结果映射为 Java 对象,从而高效地进行数据库操作。
1. 实体类(Entity Class)
com/itheima/pojo/User.java
package com.itheima.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
private String name;
private Integer age;
}
1.1 实体类的定义
实体类(Entity Class)是用于表示数据库中数据结构的对象模型。它们通常包含与数据库表列相对应的属性,并提供相应的getter和setter方法来访问这些属性。此外,实体类还可能包含一些业务逻辑或验证规则。
1.2 简化编写
其中,@Data
、@AllArgsConstructor
和 @NoArgsConstructor
是 Lombok 提供的注解,用于简化 Java 类的编写。这些注解可以自动生成常见的代码,减少样板代码(boilerplate code),使代码更加简洁和易读。下面是对这三个注解的简要解释:
1.2.1 @Data
@Data
是 Lombok 提供的一个综合性注解,它实际上是以下多个注解的组合:
@Getter
和@Setter
: 自动生成所有字段的 getter 和 setter 方法。@ToString
: 自动生成toString()
方法。@EqualsAndHashCode
: 自动生成equals()
和hashCode()
方法。@RequiredArgsConstructor
: 生成一个包含所有final
字段或标记为@NonNull
的构造函数。
1.2.2 @AllArgsConstructor
@AllArgsConstructor
注解用于生成一个包含所有字段的构造函数。每个字段都会成为构造函数的一个参数。
使用 @AllArgsConstructor
注解后,Lombok 会为 User
类生成如下构造函数:
public User(Integer id, String username, String password, String name, Integer age) {
this.id = id;
this.username = username;
this.password = password;
this.name = name;
this.age = age;
}
1.2.3 @NoArgsConstructor
@NoArgsConstructor
注解用于生成一个无参构造函数。这对于需要反射创建对象的框架(如 Spring、Hibernate 等)非常有用。
使用 @NoArgsConstructor
注解后,Lombok 会为 User
类生成如下无参构造函数:
public User() {
}
2. 创建 Mapper 接口
创建一个 UserMapper
接口,定义与 User
相关的数据库操作方法。
com/itheima/mapper/UserMapper.java
package com.itheima.mapper;
import com.itheima.pojo.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper //应用程序在运行时, 会自动的为该接口创建一个实现类对象(代理对象), 并且会自动将该实现类对象存入IOC容器 - bean
public interface UserMapper {
/**
* 查询所有用户
*/
@Select("select id, username, password, name, age 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);
}
2.1 @Param
@Param
注解用于给方法参数命名,使得在 SQL 映射文件或注解中可以通过名称引用这些参数。这对于有多个参数的方法特别有用,而且在默认情况下,方法的形参名称不会被保留在 .class
字节码中。这意味着在运行时,MyBatis 无法直接获取方法参数的名称,只能通过参数的顺序进行绑定。
- 可读性:通过命名参数,SQL 语句更易于理解和维护。
- 灵活性:允许在同一个方法中有多个参数,并且每个参数都可以通过名称引用,而不是依赖于位置。
- 避免混淆:当有多个参数时,避免因参数顺序错误导致的问题。
然而,若您基于 Spring Boot 框架进行开发,并且您的项目继承自 spring-boot-starter-parent
父级工程,则该父级工程已预配置了以下插件。这使得在编译后能够保留方法的形式参数名称,从而允许框架在运行时访问这些参数名称。
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<parameters>true</parameters>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
2.2 #{} 占位符
#{}
是 MyBatis 中用于传递参数的占位符。它会将传入的参数值进行预编译处理,防止 SQL 注入攻击,并提高查询效率,以下是占位符与拼接符的区别
特性 | #{} 占位符 |
${} 拼接符 |
---|---|---|
预编译 | 支持,防止 SQL 注入 | 不支持,容易导致 SQL 注入 |
类型转换 | 自动进行类型转换 | 不进行类型转换 |
适用场景 | 绑定参数值,如字段值、条件等 | 动态生成 SQL 语句,如表名、列名等 |
安全性 | 高,防止 SQL 注入 | 低,容易受到 SQL 注入攻击 |
性能 | 高,可以利用数据库的执行计划缓存 | 低,每次执行都需要重新解析和编译 |
灵活性 | 较低,适合固定结构的 SQL 语句 | 高,适合动态生成 SQL 语句 |
#{}
占位符:用于传递参数值,支持预编译和类型转换,防止 SQL 注入,适合大多数场景。${}
拼接符:用于直接拼接 SQL 字符串,不进行预编译和转义处理,适合动态生成 SQL 语句,但需特别注意安全性。
2.3 SQL 预编译
SQL 预编译(Prepared Statements)是指在执行 SQL 语句之前,先将其编译成数据库可以识别的执行计划,然后在实际执行时只需要传递参数即可。这种方式不仅可以提高查询效率,还能有效防止 SQL 注入攻击。
- 安全性:通过预编译机制,参数会被安全地传递到数据库,避免了直接拼接 SQL 字符串带来的 SQL 注入风险。
- 性能提升:对于重复执行的 SQL 语句,数据库可以缓存并重用执行计划,减少解析和编译的时间开销。
- 简化开发:开发者无需手动编写复杂的 SQL 语句拼接逻辑,减少了出错的可能性。
3. 配置 MyBatis XML 映射文件(可选)
使用 MyBatis 的注解,主要是来完成一些简单的增删改查功能。如果需要实现复杂的 SQL 功能,建议使用 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.itheima.mapper.UserMapper">
<!-- 根据ID删除用户 -->
<select id="deleteById" parameterType="int" resultType="com.itheima.pojo.User">
SELECT * FROM User WHERE id = #{id}
</select>
<!-- 查询所有用户 -->
<select id="findAll" resultType="com.itheima.pojo.User">
SELECT * FROM User
</select>
3.1 默认规则
XML
映射文件的名称与 Mapper 接口名称一致,并且将XML
映射文件和 Mapper 接口放置在相同包下(同包同名)。XML
映射文件的namespace
属性为 Mapper 接口全限定名一致。XML
映射文件中 sql 语句的id
与 Mapper 接口中的方法名一致,并保持返回类型一致。
4. 配置 MyBatis 主配置文件
application.properties
spring.application.name=springboot-mybatis-quickstart
spring.datasource.url=jdbc:mysql://localhost:3306/userdb
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=mq20011103
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
这段配置文件是典型的 Spring Boot 应用程序中的 application.properties
或 application.yml
文件的一部分,用于配置应用程序的名称、数据源连接信息以及 MyBatis 的日志实现。下面是对每个配置项的详细解析:
4.1 Properties配置项解析
spring.application.name=springboot-mybatis-quickstart
作用:设置应用程序的名称。
解释:这个属性定义了应用的名称,通常用于在日志、监控工具和其他地方标识该应用。在这个例子中,应用的名称为
springboot-mybatis-quickstart
。spring.datasource.url=jdbc:mysql://localhost:3306/userdb
作用:设置数据库连接的 URL。
解释:这个属性指定了要连接的数据库的 JDBC URL。这里的 URL 表示连接到本地 (
localhost
) MySQL 数据库服务器上的userdb
数据库,端口号为 3306。spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
作用:设置数据库驱动类名。
解释:这个属性指定了用于连接数据库的 JDBC 驱动类。对于 MySQL 数据库,驱动类名为
com.mysql.cj.jdbc.Driver
,这是 MySQL Connector/J 提供的驱动类。spring.datasource.username=root
作用:设置数据库用户名。
解释:这个属性指定了连接数据库时使用的用户名。在这个例子中,用户名为
root
。spring.datasource.password=mq20011103
作用:设置数据库密码。
解释:这个属性指定了连接数据库时使用的密码。在这个例子中,密码为
mq20011103
。请注意,实际生产环境中应确保密码的安全性,避免直接硬编码在配置文件中。mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
作用:设置 MyBatis 的日志实现类。
解释:这个属性指定了 MyBatis 使用的日志实现类。
org.apache.ibatis.logging.stdout.StdOutImpl
表示将 MyBatis 的日志输出到标准输出(控制台)。这有助于调试和查看 SQL 执行情况。
4.2 Properties 和 YAML/YML 配置文件的对比
特性 | Properties 文件 | YAML 文件 |
---|---|---|
语法 | 扁平化键值对 | 层次结构化,使用缩进 |
可读性 | 较低,尤其是复杂配置 | 高,结构清晰 |
维护性 | 较低,特别是对于嵌套配置 | 高,嵌套结构易于管理 |
数据类型支持 | 仅支持字符串,需手动转换其他类型 | 支持多种数据类型,自动类型转换 |
缩进敏感性 | 不敏感 | 敏感,必须保持一致的缩进 |
使用场景 | 简单配置,传统应用 | 复杂配置,现代框架如 Spring Boot |
application.yaml/yml
spring:
application:
name: springboot-mybatis-quickstart
datasource:
url: jdbc:mysql://localhost:3306/userdb
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: mq20011103
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
5. 编写测试代码
编写一个简单的测试类来验证这些操作是否正常工作:
package com.itheima;
import com.itheima.mapper.UserMapper;
import com.itheima.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 AliyunMybatisQuickstartApplicationTests {
@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(1,"zhouyu","666888","周瑜", 20);
userMapper.update(user);
}
/**
* 测试查询
*/
@Test
public void testSelect(){
User user = userMapper.findByUsernameAndPassword("zhouyu", "666888");
System.out.println(user);
}
}
5.1 @SpringBootTest
@SpringBootTest
是一个用于集成测试的注解,它会启动整个 Spring Boot 应用程序上下文(ApplicationContext),以便在测试环境中进行依赖注入和其他 Spring 功能的测试。
- 启动整个应用程序:当使用
@SpringBootTest
注解时,Spring Boot 会自动创建并启动一个完整的应用程序上下文,类似于实际运行的应用程序。 - 自动配置:该注解会自动加载应用程序的配置文件(如
application.properties
或application.yml
),并根据这些配置初始化相应的 Bean。 - 依赖注入:允许在测试类中使用
@Autowired
注解来注入需要测试的组件或服务。
5.2 @Autowired
@Autowired
是一个用于依赖注入的注解,它可以自动将所需的依赖项注入到类中。
- 自动注入:通过
@Autowired
注解,Spring 容器会自动查找匹配的 Bean 并将其注入到目标字段、构造函数或方法参数中。 - 字段注入:可以直接在字段上使用
@Autowired
,如示例中的userMapper
字段。 - 构造函数注入:也可以在构造函数上使用
@Autowired
,这种方式通常被认为更安全和推荐。 - Setter 方法注入:可以在 setter 方法上使用
@Autowired
,但这种方式较少使用。
5.3 userList.forEach(System.out::println)
userList.forEach(System.out::println);
这行代码使用了 Java 8 引入的 方法引用 和 Lambda 表达式,目的是对 userList
中的每一个元素调用 System.out.println()
方法进行打印。
- 等效的 Lambda 表达式 如果你不使用方法引用,可以使用 Lambda 表达式来实现相同的功能:
userList.forEach(user -> System.out.println(user));