将 SQL 语句与程序代码进行分离,降低了耦合,便于管理。
提供映射标签,支持 Java 对象与数据库 ORM 字段的映射关系。
MyBatis 实践
1. 引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springboot-mybatis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-mybatis</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2. 配置 MySQL 和 MyBatis
# 配置 MySQL
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
# 配置 MyBatis
mybatis:
mapper-locations: classpath:mapper/*
type-aliases-package: com.example.entity
configuration:
map-underscore-to-camel-case: true
MyBatis 的配置项中:
:用来设置别名,它的作用是告诉 MyBatis 需要设置别名的实体类的所在的包。默认情况下,MyBatis 会使用实体类的非限定类名来作为它的别名,如将 的别名设置为 或 (别名不区分大小写)。当然,MyBatis 也支持自定义别名,这个我们在后文中再聊。
3. 实体类
package com.example.entity;
import lombok.Data;
import java.util.Date;
/**
* @Author john
* @Date 2021/11/14
*/
@Data
public class User {
private long id;
private String userName;
private int age;
private String address;
private Date createTime;
private Date updateTime;
}
User 类中封装了用户的 id、姓名、年龄、地址、创建时间以及修改时间等信息。
4. 创建 user 表
5. 编写 Mapper 接口和 mapper 文件
package com.example.mapper;
import com.example.entity.User;
/**
* @Author john
* @Date 2021/11/16
*/
public interface UserMapper {
void insertUser(User user);
User findUserById(long id);
}
接口中定义了两个方法,insertUser 用来向数据表中插入一条记录,findUserById 用来通过 id 查询 User。
<?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.mapper.UserMapper">
<sql id="insertFields">
user_name, age, address, gmt_create, gmt_modified
</sql>
<sql id="selectFields">
id, user_name, age, address, gmt_create, gmt_modified
</sql>
<resultMap id="UserMap" type="User">
<result column="id" jdbcType="INTEGER" property="id"/>
<result column="user_name" jdbcType="VARCHAR" property="userName"/>
<result column="age" jdbcType="INTEGER" property="age"/>
<result column="address" jdbcType="VARCHAR" property="address"/>
<result column="gmt_create" jdbcType="DATE" property="createTime" />
<result column="gmt_modified" jdbcType="DATE" property="updateTime" />
</resultMap>
<select id="findUserById" parameterType="Long" resultMap="UserMap">
select
<include refid="selectFields"/>
from user
where id = #{id}
</select>
<insert id="insertUser" parameterType="User" keyProperty="id">
insert into user (<include refid="insertFields"/>)
values(#{userName}, #{age}, #{address}, UTC_TIMESTAMP(), UTC_TIMESTAMP())
</insert>
</mapper>
可以看到,Mapper 接口中定义的是 CRUD 相关的方法,mapper.xml 文件中定义的是具体的 SQL 语句。MyBatis 允许我们将 Mapper 接口与 mapper.xml 文件关联在一起,这样当调用 Mapper 接口中的方法时,实际的处理逻辑为执行 mapper.xml 文件中对应的 SQL 语句。关联 Mapper 接口和 mapper.xml 文件时需要保证:
Mapper 接口的方法名对应 statement(每一个 SQL 就是一个 statement)的 id 值。
Mapper 接口中方法的返回值对应 statement 的出参。
标签:用于执行查询操作。
标签:用于定义复用的 SQL 片段,如果多个 SQL 需要操作相同的字段集,那么就可以使用 标签将这些字段提取出来,然后在 SQL 语句中直接引用即可。引用的语法为,其中 refid 的值就是 的 id 值。
实际上,MyBatis 赋值时不一定会调用实体类属性的 setter 方法,因为我们在编码时可能并没有添加该方法。以 User 类的属性 id 为例,如果我们添加了 setId 方法,那么 MyBatis 会通过反射获取到 setId 对应的 MethodInvoker,然后调用 setId 方法为 id 赋值;如果未设置 setId 方法,那么 MyBatis 会获取属性 id 对应的 SetFieldInvoker,然后为属性赋值。详见 MetaObject 类的 setValue 方法。
keyProperty:用于指定主键在 POJO 中对应的属性名,需要配合数据库的自增主键来使用。以 user 表为例,我们在建表的时候将表的主键 id 设置为了数据库自增 id,因此在将 User 对象持久化到数据库之前不需要为属性 id 设置初始值,MySQL 会自动帮我们赋值,keyProperty 的作用就是告诉 MyBatis 哪个属性是主键。
parameterType:用于指定 SQL 语句的入参类型(可以是基本数据类型或者 JavaBean),该类型需要与对应的接口方法的入参类型一致。如果我们设置了别名,那么也可以使用别名作为参数,例如使用 或 代替 。
<resultMap id="UserMap" type="User">
<result column="gmt_create" jdbcType="DATE" property="createTime" />
<result column="gmt_modified" jdbcType="DATE" property="updateTime" />
</resultMap>
因为其他字段会自动映射,不需要额外书写。
6. 编写 Service
package com.example.service;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Author john
* @Date 2021/11/16
*/
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public void insertUser(User user) {
userMapper.insertUser(user);
}
public User findUserById(long id) {
return userMapper.findUserById(id);
}
}
在 UserService 中注入 UserMapper 对象,并调用相关方法来添加/查询 User。
package com.example;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.mapper")
public class SpringbootMybatisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisApplication.class, args);
}
}
包下的所有 Mapper 接口都会被 Spring 扫描。
7. 测试
package com.example;
import com.example.entity.User;
import com.example.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringbootMybatisApplicationTests {
@Autowired
private UserService service;
@Test
public void addUser(){
User user = new User();
user.setUserName("John");
user.setAge(24);
user.setAddress("BUPT");
service.insertUser(user);
}
@Test
public void findUser(){
System.out.println(service.findUserById(1));
}
}
首先执行 addUser() 方法,执行成功后查询数据表,得到如下信息:
MyBatis 设置别名的方式
方式一:在配置文件 application.yml 中添加配置。
mybatis:
type-aliases-package: com.example.entity
本实验采用此种方式设置别名,默认情况下实体类的别名为其类名,严格来说是首字母小写的非限定类名,由于别名不区分大小写,所以 、、 的效果都是相同的。
package com.example.entity;
import lombok.Data;
import org.apache.ibatis.type.Alias;
import java.util.Date;
/**
* @Author john
* @Date 2021/11/14
*/
@Data
@Alias("hello")
public class User {
private long id;
private String userName;
private int age;
private String address;
private Date createTime;
private Date updateTime;
}
上述代码中,我们将 User 类的别名设置为了 。注意,若要使 @Alias 注解生效,必须配置 来指定实体类的包路径。另外,@Alias 会使默认的别名变得无效,例如在本实验中,User 类的别名只能是,而不能是 或 等。
方式二:使用 MyBatis 的配置文件 filename.xml。
# 配置MyBatis
mybatis:
mapper-locations: classpath:mapper/*
config-location: classpath:mybatis/mybatis-config.xml #MyBatis配置文件
然后在 resource 文件夹下创建 MyBatis 的配置文件 mapper/mybatis-config.xml(路径和文件名在 config-location 中设置),配置文件内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<package name="com.example.entity"/>
</typeAliases>
</configuration>
几个重要标签的含义为:
标签:用于配置别名,子标签 可以让 MyBatis 扫描指定包下的实体类,其效果与在 yml 文件中配置 是相同的。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<typeAlias type="com.example.entity.User" alias="hello"/>
</typeAliases>
</configuration>
标签不需要配置 就可以生效,且该标签与 标签并不冲突,也就是说如果我们添加了,那么 User 类的别名既可以是,也可以是 或 等。当然,方式二中也可以添加 @Alias 注解,但添加了该注解后,User 类的别名只能为 或 @Alias 注解指定的别名。