0%

SpringBoot 开发实践(3):优雅使用 SpringBoot + Druid + MyBatis 访问数据库

前言

作为 Java 后端开发,我们免不了要和数据库打交道。那么我们如何在 SpringBoot 中优雅地与数据库交互呢?

目前,主流的方式是使用 JPA 或者 MyBatis 作为访问数据库的框架。JPA 的前身是 Hibernate,其宣传的亮点是不需要写 SQL 就能实现数据的交互。对于简单的单表查询来说,JPA 有其优势,可以使开发效率大大提高。但是对于复杂的多表关联查询场景,MyBatis 灵活的优势就凸显出来了。

我在一开始用 SpringBoot 访问数据库的时候,用的就是 MyBatis,所以对 MyBatis 的使用相对熟悉一些。而自工作以来,见到的项目大部分都是 SSM 架构的。所以本章就针对 MyBatis 介绍它与 SpringBoot 如何进行整合。

配置工程环境

首先,我们来梳理下,都需要哪些框架或插件。

MyBatis

既然是 SSM,MyBatis 自然是少不了。

MyBatis 提供了 starter 支持,在 pom.xml 中的 <dependencies> 标签中添加 mybatis-spring-boot-starter 依赖,就能轻松地在项目中使用 MyBatis 了。

1
2
3
4
5
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>

Druid

Druid 是阿里巴巴开源的数据库连接池,是目前较为公认的最好用的连接池。它拥有强大的监控功能,在性能、扩展性方面都表现出色。目前生产中使用 Druid 作为项目的连接池非常普遍,所以这里介绍下如何将 Druid 整合进来。

导入 maven 依赖

同样,在 pom.xml 中导入 druid-spring-boot-starter

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.22</version>
</dependency>

配置 application.yml

由于有了 druid-spring-boot-starter 的支持,SpringBoot 会自动到 yml 中读取相关配置,前提是属性名和层次要按照官方文档的要求来配置。Yml 配置如下:

1
2
3
4
5
6
7
8
spring:
datasource:
name: mysql
type: com.alibaba.druid.pool.DruidDataSource
druid:
url: jdbc:mysql://127.0.0.1:3306/springboot_study?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
username: root
password: 123456
  • name: 给你的数据源命个名。
  • type: SpringBoot 默认的数据源是 org.apache.tomcat.jdbc.pool.DataSource,这里我们要其配置成 Druid。
  • url: 数据库地址
  • username:数据库用户名
  • password:数据库密码

配置到这儿,Druid 就可以正常使用了,其它的配置例如连接个数、超时时间等这些目前都是默认值。当然,一般我们在生产中,会根据实际需要,手动配置其它参数。具体的各参数介绍可以查看官方文档: DruidDataSource配置属性列表

这里也附上一份我们生产中配置的一个例子,供大家参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
druid:
#监控统计拦截的filters
filters: stat, wall,log4j
driver-class-name: com.mysql.jdbc.Driver
#配置初始化大小/最小/最大
initial-size: 5
min-idle: 5
max-active: 20
#获取连接等待超时时间
max-wait: 60000
#间隔多久进行一次检测,检测需要关闭的空闲连接
time-between-eviction-runs-millis: 60000
#一个连接在池中最小生存的时间
min-evictable-idle-time-millis: 300000
validation-query: SELECT 'x'
test-while-idle: true
test-on-borrow: false
test-on-return: false
#打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
pool-prepared-statements: false
max-pool-prepared-statement-per-connection-size: 20

有关更多 Druid Starter 的配置方式,可以参考官方文档:druid-spring-boot-starter

MySQL 驱动

由于我们连接的是 MySQL 数据库,所以需要引入 MySQL 驱动。

pom.xml 中导入 mysql-connector-java

1
2
3
4
5
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>

至此,有关 SpringBoot + Druid + MyBatis 的工程配置都已经结束,接下来就可以专注于 MyBatis 的使用了。

MyBatis 的使用

方法一:使用 XML 编写 SQL

这种方法是 MyBatis 的传统用法,通过编写一个 Mapper 类和一个 XML 文件,其中 XML 中负责编写各种 SQL,然后将 XML 映射到 Mapper 上,每个 SQL 与 Mapper 中的方法一一对应。这样通过调用 Mapper 中方法就可以与数据库交互了。

创建实体类

创建一个 entity 包,生成一个 People.java 类,用于存放表中各列的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class People {
/**
* 主键
*/
private int id;

/**
* 姓名
*/
private String name;

/**
* 年龄
*/
private int age;

public People() {
}

public People(String name, int age) {
this.age = age;
this.name = name;
}

public People(int id, String name, int age) {
this.id = id;
this.age = age;
this.name = name;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}

编写 Mapper

新建一个 mapper 包,生成一个 PeopleXmlMapper.java 接口类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Mapper
public interface PeopleXmlMapper {
/**
* 根据 id 查询
*
* @param id
* @return
*/
People selectById(@Param("id") int id);

/**
* 插入数据
*
* @param people
* @return
*/
int insert(People people);

/**
* 更新数据
*
* @param people
* @return
*/
int updateById(People people);

/**
* 根据 id 删除数据
*
* @param id
* @return
*/
int deleteById(@Param("id") Integer id);
}

在这个接口中,我们定义四个方法,分别对应增删改查四种操作。

  • @Mapper: 用于表示该接口类是个 Mapper, 在编译后会生成相应的实现类。
  • @Param: 用于给参数命名,动态替换 SQL 中的参数值。
  • People: MyBatis 支持直接传入和返回对象实体,在 XML 中配置好实体属性和列的关系,就可以在 SQL 中取到实体中的属性值,或者将查得的数据以实体形式返回。

编写 SQL XML

resources 目录下,新建一个 mappers 文件夹,然后在里面新建一个 PeopleMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?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="cn.interhorse.springboot.mybatisdruidmysql.dao.mapper.PeopleXmlMapper">
<resultMap id="BaseResultMap" type="cn.interhorse.springboot.mybatisdruidmysql.entity.People">
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="name" jdbcType="VARCHAR" property="name"/>
<result column="age" jdbcType="INTEGER" property="age"/>
</resultMap>

<select id="selectById" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select id, name, age
from people
where id = #{id}
</select>

<insert id="insert" parameterType="cn.interhorse.springboot.mybatisdruidmysql.entity.People">
insert into people(name, age) values(#{name}, #{age})
</insert>


<update id="updateById" parameterType="cn.interhorse.springboot.mybatisdruidmysql.entity.People">
update people set name = #{name}, age = #{age} where id = #{id}
</update>

<delete id="deleteById" parameterType="java.lang.Integer">
delete from people where id = #{id}
</delete>
</mapper>

一个 XML 与一个 Mapper 类一一对应。在 Mapper 类中的接口的具体实现,都将在 XML 中进行。每条 SQL 的 id 要和 Mapper 中的方法名做对应,#{} 中的值会被替换成入参值。

  • namespace: 将该 XML 与 Mapper 绑定。
  • resultMap: 将实体类中的属性和表中的列做好映射关系。
  • parameterType: 入参类型。

更详细的 MyBatis XML 映射器介绍,请参阅: sqlmap-xml

其它配置

application.yml 中,添加如下配置:

1
2
3
# MyBatis
mybatis:
mapper-locations: classpath:mappers/*.xml

这是为了让 SpringBoot 知道 XML 文件的所在位置。

在之前编写的 Mapper 类中,我们使用 @Mapper 注解来进行标注。如果 Mapper 数量特别多的情况下,每个 Mapper 类都要打上 @Mapper 注解就很不方便了。所以我们可以在 Application 类中,配置 @MapperScan,对 Mapper 统一扫描,这样就不需要每个都打上 @Mapper 注解了。

1
2
3
4
5
6
7
@SpringBootApplication
@MapperScan("cn.interhorse.springboot.mybatisdruidmysql.dao.mapper")
public class MybatisDruidMysqlApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisDruidMysqlApplication.class, args);
}
}
  • @MapperScan: 配置 Mapper 接口类所在的包,该包下所有接口在编译后都会生成相应的实现类。不配的话,SpringBoot 在启动时便无法找到这些 Mapper。

使用

至此,使用 XML 编写 SQL 的基本工作都已完成。我们可以在 MyController 中编写几个接口,来测试下增删改查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
@Controller
public class MyController {
@Resource
private PeopleXmlMapper peopleXmlMapper;

/**
* 根据 id 查询 - xml 方式
* @param id
* @return
*/
@ResponseBody
@RequestMapping(value = "/xml/selectById/{id}", method = RequestMethod.GET)
private Object selectByIdXml(@PathVariable("id") int id) {
return peopleXmlMapper.selectById(id);
}

/**
* 插入数据 - xml 方式
* @param httpServletRequest
* @return
*/
@ResponseBody
@RequestMapping(value = "/xml/insert", method = RequestMethod.GET)
private Object insertXml(HttpServletRequest httpServletRequest) {
String name = httpServletRequest.getParameter("name");
int age = Integer.parseInt(httpServletRequest.getParameter("age"));
People people = new People(name, age);
return peopleXmlMapper.insert(people);
}

/**
* 更新数据 - xml 方式
* @param httpServletRequest
* @return
*/
@ResponseBody
@RequestMapping(value = "/xml/updateById", method = RequestMethod.GET)
private Object updateByIdXml(HttpServletRequest httpServletRequest) {
String name = httpServletRequest.getParameter("name");
int id = Integer.parseInt(httpServletRequest.getParameter("id"));
int age = Integer.parseInt(httpServletRequest.getParameter("age"));
People people = new People(id, name, age);
return peopleXmlMapper.updateById(people);
}

/**
* 根据 id 删除 - xml 方式
* @param id
* @return
*/
@ResponseBody
@RequestMapping(value = "/xml/deleteById/{id}", method = RequestMethod.GET)
private Object deleteByIdXml(@PathVariable("id") int id) {
return peopleXmlMapper.deleteById(id);
}
}
  • @Resource: 注入 PeopleXmlMapper。

分别访问这几个接口,就可以看到数据的变化了。

http://127.0.0.1:8080/xml/insert?name=Jenny&age=28

http://127.0.0.1:8080/xml/selectById/1

1
2
3
4
5
+----+-------+------+
| id | name | age |
+----+-------+------+
| 1 | Jenny | 28 |
+----+-------+------+

http://127.0.0.1:8080/xml/updateById?id=1&name=Jack&age=32

1
2
3
4
5
+----+------+------+
| id | name | age |
+----+------+------+
| 1 | Jack | 32 |
+----+------+------+

http://127.0.0.1:8080/deleteById/1

1
2
mysql> select * from people;
Empty set (0.00 sec)

方法二:使用注解编写 SQL

SpringBoot 官方不建议使用 XML 进行配置,毕竟 SpringBoot 的设计初衷就是想简化开发过程,给项目减负。所以得益于强大的 Java 注解(Java Annotation),MyBatis3 提供了用注解的方式映射 SQL 语句,从而告别繁琐的 XML 配置。

创建 Mapper

mapper 包 下创建 PeopleMapper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public interface PeopleMapper {
/**
* 根据 id 查询
*
* @param id
* @return
*/
@Select("select * from people where id = #{id}")
People selectById(@Param("id") int id);

/**
* 插入数据
*
* @param people
* @return
*/
@Insert("insert into people(name, age) values(#{name}, #{age})")
int insert(People people);

/**
* 更新数据
*
* @param people
* @return
*/
@Update("update people set name = #{name}, age = #{age} where id = #{id}")
int updateById(People people);

/**
* 根据 id 删除
*
* @param id
* @return
*/
@Delete("delete from people where id = #{id}")
int deleteById(@Param("id") Integer id);
}

可以看到,在 PeopleMapper 中,同 PeopleXmlMapper 一样,有增删改查四种接口,但这次,SQL 语句直接配置在注解中。

更多有关 MyBatis 的注解介绍,请参阅: mybatis-java-api

其它配置

方法一中,在 application.yml 配置了 mapper-locations,而现在,因为不需要编写 XML,所以不需要配置该项了。

使用

使用方式和方法一一样,通过注入对应的 Mapper,调用其中的方法即可。这里我就不贴详细测试代码了,具体代码已经上传至 GitHub。

SQL 日志输出

工程默认是不输出 SQL 日志的,可通过配置将 SQL 日志打印出来,方便调试。

application.yml 中添加如下配置

1
2
3
4
5
6
7
8
logging:
level:
cn:
interhorse:
springboot:
mybatisdruidmysql:
dao:
mapper: debug

这样在 console 中,就能看到 SQL 语句了。

1
2
3
2020-06-05 18:04:30.534 DEBUG 5392 --- [nio-8080-exec-1] c.i.s.m.d.m.PeopleXmlMapper.selectById   : ==>  Preparing: select id, name, age from people where id = ? 
2020-06-05 18:04:30.586 DEBUG 5392 --- [nio-8080-exec-1] c.i.s.m.d.m.PeopleXmlMapper.selectById : ==> Parameters: 1(Integer)
2020-06-05 18:04:30.618 DEBUG 5392 --- [nio-8080-exec-1] c.i.s.m.d.m.PeopleXmlMapper.selectById : <== Total: 0

总结

以上就是 SpringBoot + Druid + MyBatis 的整合。

对于两种使用方式,我目前青睐于第一种。主要也是因为一直以来都使用 XML 方式编写 SQL,不过通过本次整理 SpringBoot 系列,也发现了注解开发的优势,即简便、清爽。但需要注意的是,注解方式还没有完全覆盖 XML 所提供的功能,例如注解形式不支持抽取可复用的 SQL 语句。所以在实际开发中,要做好取舍,选择适合自己的开发方式。

本章代码地址:GitHub


我是因特马,一个爱分享的斜杠程序员~

欢迎关注我的公众号:一只因特马

  • 本文作者: 因特马
  • 本文链接: https://www.interhorse.cn/a/3500956215/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!