概念
springboot集成mybatis
引入相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
编写项目配置文件application.yml
# 配置 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 的配置项中:
mapper-locations
:用来指定 mapper.xml 文件的路径,该文件用于编写 SQL 语句。type-aliases-package
:用来设置别名,它的作用是告诉 MyBatis 需要设置别名的实体类的所在的包。默认情况下,MyBatis 会使用实体类的非限定类名来作为它的别名,如将com.example.entity.User
的别名设置为User
或user
(别名不区分大小写)。当然,MyBatis 也支持自定义别名,这个我们在后文中再聊。map-underscore-to-camel-case
:用来开启驼峰命名自动映射,如将数据表中的字段 user_name 映射到实体对象的属性 userName。
实体类编写
import lombok.Data;
import java.util.Date;
@Data
public class User {
private long id;
private String userName;
private int age;
private String address;
private Date createTime;
private Date updateTime;
}
编写 Mapper 接口
import com.example.springbootdemo.entity.User;
public interface UserMapper {
void insertUser(User user);
User findUserById(long id);
}
编写mapper.xml文件**
在 resources 文件夹中创建 mapper/user-mapper.xml 文件(文件路径在配置文件 application.yml 中设置)。user-mapper.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.springbootdemo.mapper.UserMapper">
<sql id="insertFields">
user_name, age, address, create_time, update_time
</sql>
<sql id="selectFields">
id, user_name, age, address, create_time, update_time
</sql>
<resultMap id="UserMap" type="User">
<id 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="create_time" jdbcType="DATE" property="createTime" />
<result column="update_time" 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}, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP())
</insert>
</mapper>
mapper.xml文件的标签介绍
MyBatis 允许我们将 Mapper 接口与 mapper.xml 文件关联在一起,这样当调用 Mapper 接口中的方法时,实际的处理逻辑为执行 mapper.xml 文件中对应的 SQL 语句。关联 Mapper 接口和 mapper.xml 文件时需要保证:
- Mapper 接口的全限定名对应 mapper.xml 文件的 namespace 值。
- Mapper 接口的方法名对应 statement(每一个 SQL 就是一个 statement)的 id 值。
- Mapper 接口中方法接收的参数对应 statement 的入参。
- Mapper 接口中方法的返回值对应 statement 的出参。
下面介绍一下 mapper.xml 文件中几个重要标签的含义:
<sql>
标签:用于定义复用的 SQL 片段,如果多个 SQL 需要操作相同的字段集,那么就可以使用<sql>
标签将这些字段提取出来,然后在 SQL 语句中直接引用即可。引用的语法为<include refid=" "/>
,其中 refid 的值就是<sql>
的 id 值。<resultMap>
标签:用于创建数据表字段与实体属性的映射关系,在查询操作中,MyBatis 会根据查询到的字段名找到 POJO 对应的属性名,然后调用该属性的 setter 方法进行赋值。如果数据表的字段名与实体类的属性名完全相同,或者符合驼峰式命名映射的规则,那么 MyBatis 可以直接完成赋值操作。否则的话,就需要我们使用<resultMap>
标签创建自定义的映射规则,告诉 MyBatis 字段和属性之间应该如何映射。本实验中,user 表的 id 会自动映射为 User 对象的 id,user 表的 user_name 也会自动映射为 User 对象的 userName。但是 gmt_create 和 gmt_modified 不会映射为 createTime 和 updateTime,因为字段名和属性名既不完全一致,也不符合驼峰式命名映射的规则,所以这里我们需要使用<resultMap>
来创建新的映射关系,其中属性 id 用于指明该 resultMap 的标志,属性 type 用于指明映射的实体类。<select>
标签:用于执行查询操作。<insert>
标签:用于执行插入操作。
实际上,MyBatis 赋值时不一定会调用实体类属性的 setter 方法,因为我们在编码时可能并没有添加该方法。以 User 类的属性 id 为例,如果我们添加了 setId 方法,那么 MyBatis 会通过反射获取到 setId 对应的 MethodInvoker,然后调用 setId 方法为 id 赋值;如果未设置 setId 方法,那么 MyBatis 会获取属性 id 对应的 SetFieldInvoker,然后为属性赋值。详见 MetaObject 类的 setValue 方法。
接下来介绍 SQL 语句中几个重要属性的含义:
- parameterType:用于指定 SQL 语句的入参类型(可以是基本数据类型或者 JavaBean),该类型需要与对应的接口方法的入参类型一致。如果我们设置了别名,那么也可以使用别名作为参数,例如使用
User
或user
代替com.example.entity.User
。 - resultMap:用于指定 SQL 语句的出参类型,以 insertUser 方法为例,在 Mapper 接口中,该方法的返回值为 User 类型,所以对应的 SQL 语句的返回值也应为 User 类型,由于 User 对象需要使用
<resultMap>
进行属性映射,所以我们将自定义的UserMap
来作为 SQL 语句的返回值类型。 - keyProperty:用于指定主键在 POJO 中对应的属性名,需要配合数据库的自增主键来使用。以 user 表为例,我们在建表的时候将表的主键 id 设置为了数据库自增 id,因此在将 User 对象持久化到数据库之前不需要为属性 id 设置初始值,MySQL 会自动帮我们赋值,keyProperty 的作用就是告诉 MyBatis 哪个属性是主键。
除了 resultMap 外,resultType 属性也可用于指定出参类型。如果我们将 user 表中的字段 gmt_create 和 gmt_modified 分别改为 create_time 和 update_time,那么就不需要使用 <resultMap>
标签来配置映射规则,因为 user 表的所有字段都可以和 User 对象的属性一一对应,这样在 SQL 语句中,就可以将 resultMap="UserMap"
替换为 resulType="User"
或 resulType="user"
。另外,在本实验中,resultMap 标签也可以定义为:
<resultMap id="UserMap" type="User">
<result column="gmt_create" jdbcType="DATE" property="createTime" />
<result column="gmt_modified" jdbcType="DATE" property="updateTime" />
</resultMap>
因为其他字段会自动映射,不需要额外书写。
编写 Service
import com.example.springbootdemo.entity.User;
import com.example.springbootdemo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@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。
为了能够正常注入 UserMapper 对象,我们还需要再启动类上添加 @MapperScan 注解,并指定 Mapper 接口所在的包:
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.springbootdemo.mapper")
public class SpringbootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootDemoApplication.class, args);
}
}
com.example.springbootdemo.mapper
包下的所有 Mapper 接口都会被 Spring 扫描。
除了在启动类上添加 @MapperScan 注解外,还可以在 Mapper 接口上直接添加 @Mapper 注解,这种方法相对比较麻烦,因为实际中我们可能会有多个 Mapper 接口,这样就需要添加多个注解。
测试
@SpringBootTest
class SpringbootDemoApplicationTests {
@Test
void contextLoads() {
}
@Autowired
private UserService service;
@Test
public void addUser() {
User user = new User();
user.setUserName("张三");
user.setAge(24);
user.setAddress("村东头");
service.insertUser(user);
}
@Test
public void findUser() {
System.out.println(service.findUserById(1));
}
}
至此,测试完毕。
注意事项
-
#{}和${}的区别
#{}使用字符串传递 ${}直接传递,因此${}会有sql注入的风险,一般建议能用#就使用#号
-
插入返回主键ID用法
-
查询返回hashmap用法