SpringBoot 整合Druid、MyBatis
简介
对于数据访问层,无论是 SQL(关系型数据库) 还是 NOSQL(非关系型数据库),Spring Boot 底层都是采用 Spring Data 的方式进行统一处理。
Spring Boot 底层都是采用 Spring Data 的方式进行统一处理各种数据库,Spring Data 也是 Spring 中与 Spring Boot、Spring Cloud 等齐名的知名项目。
数据库相关的启动器 : 可以参考官方文档
JDBC
导入依赖
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
连接数据库
首先我们需要连接数据库,我们直接使用IDEA连接即可
在SpringBoot中,我们需要连接数据库,只需要简单的配置即可
在 application.yaml 中:
spring:
datasource:
username: root
password: root
# serverTimezone=UTC:MySQL 8+以上使用
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=ture&characterEncoding=UTF-8&serverTimezone=UTC
# mysql 8+以上使用
driver-class-name: com.mysql.cj.jdbc.Driver
配置完这一些东西后,我们就可以直接去使用了,因为SpringBoot已经默认帮我们进行了自动配置;我们去测试类测试一下
@SpringBootTest
class Springboot03DataApplicationTests {
@Autowired
DataSource dataSource;
@Test
void contextLoads() throws SQLException {
// 查看一下默认的数据源
System.out.println(dataSource.getClass());
System.out.println("==========");
// 获得连接
Connection connection = dataSource.getConnection();
System.out.println(connection);
// 归还连接
connection.close();
}
}
输出结果:
class com.zaxxer.hikari.HikariDataSource
2020-02-24 16:46:56.963 INFO 19388 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-02-24 16:46:59.953 INFO 19388 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
HikariProxyConnection@1336418989 wrapping com.mysql.cj.jdbc.ConnectionImpl@597f0937
从输出结果中可以看出,SpringBoot默认的数据源是:com.zaxxer.hikari.HikariDataSource
,我们并没有手动配置。
Spring Boot 2.1.7 默认使用 com.zaxxer.hikari.HikariDataSource
数据源,而以前版本,如 Spring Boot 1.5 默认使用 org.apache.tomcat.jdbc.pool.DataSource
作为数据源;
HikariDataSource 号称 Java WEB 当前速度最快的数据源,相比于传统的 C3P0 、DBCP、Tomcat jdbc 等连接池更加优秀;
CRUD操作
- 有了数据源(
com.zaxxer.hikari.HikariDataSource
),就可以拿到数据库连接(java.sql.Connection
),有了连接,就可以使用原生的 JDBC 语句来操作数据库。 - 即使不使用第三方的数据库操作框架,如MyBatis等,Spring 本身也对原生的 JDBC 做了轻量级的封装,即:
org.springframework.jdbc.core.JdbcTemplate
- 数据库操作的所有 CRUD 方法都在 JdbcTemplate 中。
- Spring Boot 不仅提供了默认的数据源,同时默认已经配置好了 JdbcTemplate 放在了容器中,程序员只需自己注入即可使用
- JdbcTemplate 的自动配置原理是依赖
org.springframework.boot.autoconfigure.jdbc
包下的org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration
类
JdbcTemplate主要提供以下几类方法:
- execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句;
- update方法及batchUpdate方法:update方法用于执行新增、修改、删除等语句;batchUpdate方法用于执行批处理相关语句;
- query方法及queryForXXX方法:用于执行查询相关语句;
- call方法:用于执行存储过程、函数相关语句。
测试:
@RestController
public class JdbcController {
// JdbcTemplate 是 core 包的核心类,用于简化 JDBC操作,还能避免一些常见的错误,如忘记关闭数据库连接
// Spring Boot 默认提供了数据源,默认提供了 org.springframework.jdbc.core.JdbcTemplate
// JdbcTemplate 中会自己注入数据源,使用起来也不用再自己来关闭数据库连接
@Autowired
JdbcTemplate jdbcTemplate;
@GetMapping("/userList")
public List<Map<String, Object>> userList() {
String sql = "select id, name, pwd from mybatis.user";
List<Map<String, Object>> list_maps = jdbcTemplate.queryForList(sql);
return list_maps;
}
@GetMapping("/updUser/{id}")
public String updateUser(@PathVariable("id") Integer id) {
String sql = "update mybatis.user set name = ?, pwd = ? where id = " + id;
// 数据
Object[] objects = new Object[2];
objects[0] = "王五";
objects[1] = "helloworld";
jdbcTemplate.update(sql, objects);
return "update-ok";
}
@GetMapping("/delUser/{id}")
public String deleteUser(@PathVariable("id") Integer id) {
String sql = "delete from mybatis.user where id = ?";
jdbcTemplate.update(sql, id);
return "delete-ok";
}
@GetMapping("/addUser")
public String addUser() {
String sql = "insert into mybatis.user (name, pwd) values (?, ?)";
// 数据
Object[] objects = new Object[2];
objects[0] = "李华";
objects[1] = "123klajfld";
jdbcTemplate.update(sql, objects);
return "insert-ok";
}
}
原理探究:
org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration
数据源配置类作用 :根据逻辑判断之后,添加数据源;
DataSourceConfiguration
是 DataSourceAutoConfiguration
实际导入的数据源配置类。
tomcat的jdbc连接池:
Hikari:
dbcp2:
自定义数据源:
因为SpringBoot 2.x 默认数据源是:com.zaxxer.hikari.HikariDataSource
,而其他我们没有导入相应的类,所有会爆红!
SpringBoot默认支持以下数据源:
-
com.zaxxer.hikari.HikariDataSource (Spring Boot 2.0 以上,默认使用此数据源)
-
org.apache.tomcat.jdbc.pool.DataSource
-
org.apache.commons.dbcp2.BasicDataSource
可以使用 spring.datasource.type 指定自定义的数据源类型,值为 要使用的连接池实现的完全限定名。默认情况下,它是从类路径自动检测的。
/**
* 通用数据源配置
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class Generic {
@Bean
DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
自定义数据源 DruidDataSource
Druid 简介
Druid 是阿里巴巴开源平台上一个数据库连接池实现,结合了 C3P0、DBCP、PROXOOL 等 DB 池的优点,同时加入了日志监控。
Druid 可以很好的监控 DB 池连接和 SQL 的执行情况,天生就是针对监控而生的 DB 连接池。
Spring Boot 2.0 以上默认使用 Hikari 数据源,可以说 Hikari 与 Driud 都是当前 Java Web 上最优秀的数据源,我们来重点介绍 Spring Boot 如何集成 Druid 数据源,如何实现数据库监控。
配置数据源
Druid依赖:
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
现在我们去切换数据源;
之前已经说过 SpringBoot 2.0以上默认使用 com.zaxxer.hikari.HikariDataSource
数据源,但可以通过 spring.datasource.type
指定数据源
数据源切换之后,同理可以注入 DataSource,测试类不用变,一测便知!
Druid数据库连接池可以配置一些参数:
spring:
datasource:
username: root
password: root
# serverTimezone=UTC:MySQL 8+以上使用
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=ture&characterEncoding=UTF-8&serverTimezone=UTC
# mysql 8+以上使用
driver-class-name: com.mysql.cj.jdbc.Driver
# 切换数据源
type: org.springframework.jdbc.datasource.DriverManagerDataSource
#Spring Boot 默认是不注入这些属性值的,需要自己绑定
#druid 数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority
#则导入 log4j 依赖即可,Maven 地址: https://mvnrepository.com/artifact/log4j/log4j
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
log4j日志依赖
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
现在需要程序员自己为 com.alibaba.druid.pool.DruidDataSource
绑定到全局配置文件中的参数,再添加到容器中,而不再使用 Spring Boot 的自动生成了;
我们需要 自己添加 DruidDataSource
组件到容器中,并绑定属性;
package com.rainszj.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DruidConfig {
/*
将自定义的 Druid数据源添加到容器中,不再让 Spring Boot 自动创建
绑定全局配置文件中的 druid 数据源属性到 com.alibaba.druid.pool.DruidDataSource从而让它们生效
@ConfigurationProperties(prefix = "spring.datasource"):作用就是将 全局配置文件中
前缀为 spring.datasource的属性值注入到 com.alibaba.druid.pool.DruidDataSource 的同名参数中
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource() {
return new DruidDataSource();
}
}
去测试类中测试一下;看是否成功!
@SpringBootTest
class Springboot03DataApplicationTests {
@Autowired
DataSource dataSource;
@Test
void contextLoads() throws SQLException {
// 查看一下驱动类
System.out.println(dataSource.getClass());
System.out.println("==========");
Connection connection = dataSource.getConnection();
System.out.println(connection);
DruidDataSource druidDataSource = (DruidDataSource) this.dataSource;
System.out.println("最大连接数为:" + druidDataSource.getMaxActive());
System.out.println("数据源初始化连接数:" + druidDataSource.getInitialSize());
// 归还连接
connection.close();
}
}
打印结果:
配置 Druid 数据源监控
Druid 数据源具有监控的功能,并提供了一个 web 界面方便用户查看,类似安装 路由器 时,人家也提供了一个默认的 web 页面。
所以第一步需要设置 Druid 的后台管理页面,比如 登录账号、密码 等;配置后台管理
需要在 DruidConfig
中配置如下内容:
// 配置 Druid 监控管理后台的Servlet;
// 内置 Servler 容器时没有web.xml文件,所以使用 SpringBoot的注册 Servlet 方式
@Bean
public ServletRegistrationBean statViewServlet() {
// 创建Servlet注册实体
// /druid/*:后台访问的路径
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
Map<String, String> initParams = new HashMap<>();
initParams.put("loginUsername", "admin"); //后台管理界面的登录账号
initParams.put("loginPassword", "123456"); //后台管理界面的登录密码
//后台允许谁可以访问
//initParams.put("allow", ""127.0.0.1""):表示只有本机可以访问
//initParams.put("allow", ""):为空或者为null时,表示允许所有访问
initParams.put("allow", "");
//deny:Druid 后台拒绝谁访问
//initParams.put("deny", "192.168.1.20");表示禁止此ip访问
//设置初始化参数
bean.setInitParameters(initParams);
return bean;
}
这些参数可以在 com.alibaba.druid.support.http.StatViewServlet
的父类 com.alibaba.druid.support.http.ResourceServlet
中找到
配置完毕后,我们可以选择访问 : http://localhost:8080/druid/login.html
登录界面:
首页:
配置 Druid web 监控 filter
这个过滤器的作用就是统计 web 应用请求中所有的数据库信息,比如 发出的 sql 语句,sql 执行的时间、请求次数、请求的 url 地址、以及seesion 监控、数据库表的访问次数 等等
需要在 DruidConfig
中配置如下内容:
//配置 Druid 监控 之 web 监控的 filter
//WebStatFilter:用于配置Web和Druid数据源之间的管理关联监控统计
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean bean = new FilterRegistrationBean(new WebStatFilter());
// 设置过滤器的过滤路径
bean.addUrlPatterns("/*");
// bean.setUrlPatterns(Arrays.asList("/*"));
Map<String, String> initParameters = new HashMap<String, String>();
// 过滤这些东西不进行统计
initParameters.put("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
bean.setInitParameters(initParameters);
return bean;
}
这些参数在 WebStatFilter
类中
整合MyBatis
版本要求:
第一步:导入Spring Boot 整合 MyBatis的 依赖:
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
第二步:配置数据库连接信息
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
加载类
com.mysql.jdbc.Driver
。不推荐使用。新的驱动程序类为com.mysql.cj.jdbc.Driver
。通过SPI自动注册驱动程序,通常不需要手动加载驱动程序类。
这里使用默认的数据源,测试数据库连接是否成功:
@SpringBootTest
class Springboot03DataApplicationTests {
@Autowired
DataSource dataSource;
@Test
void contextLoads() throws SQLException {
// 查看一下驱动类
System.out.println(dataSource.getClass());
System.out.println("==========");
Connection connection = dataSource.getConnection();
System.out.println(connection);
System.out.println(connection.getMetaData().getURL());
// 归还连接
connection.close();
}
}
连接成功,OK!
第三步:创建实体类:
package com.rainszj.pojo;
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
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 String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
第四步:配置Mapper接口,也就是dao层:
package com.rainszj.mapper;
import com.rainszj.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @Mapper:标记这个类是MyBatis的Mapper,等价于Spring整合MyBatis时的Mapper接口
*/
@Mapper
/**
* @Rpository:将这个接口添加进Spring容器中,和 @Component等价
*/
@Repository
public interface UserMapper {
// 查询所有用户
List<User> selectAllUsers();
// 根据id查找用户
User findUserById(@Param("uid")Integer id);
// 修改用户
int updateUser(User user);
// 添加用户
int addUser(User user);
// 删除用户
int deleteUser(Integer id);
}
第五步:在application.yaml中添加MyBatis的配置
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
# 整合MyBatis
mybatis:
# 配置别名
type-aliases-package: com.rainszj.pojo
# 绑定Mapper配置文件
# 注意:这里的 classpath代指:resources目录
mapper-locations: classpath:mybatis/mapper/*.xml
spring boot 官方并没有提供 myBaits 的启动器,是 myBatis 官方提供的开发包来适配的 spring boot,从 pom.xml 文件中的依赖包名也能看出来,并非是以 spring-boot 开头的;
同理上面全局配置文件中的这两行配置也是以 mybatis 开头 而非 spring 开头也充分说明这些都是 myBatis 官方提供的
可以从 org.mybatis.spring.boot.autoconfigure.MybatisProperties
中查看所有配置项
/**
* Configuration properties for MyBatis.
*
* @author Eddú Meléndez
* @author Kazuki Shimizu
*/
@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
public class MybatisProperties {
public static final String MYBATIS_PREFIX = "mybatis";
private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
/**
* Location of MyBatis xml config file.
*/
private String configLocation;
/**
* Locations of MyBatis mapper files.
*/
private String[] mapperLocations;
/**
* Packages to search type aliases. (Package delimiters are ",; \t\n")
*/
private String typeAliasesPackage;
/**
* The super class for filtering type alias. If this not specifies, the MyBatis deal as type alias all classes that
* searched from typeAliasesPackage.
*/
private Class<?> typeAliasesSuperType;
/**
* Packages to search for type handlers. (Package delimiters are ",; \t\n")
*/
private String typeHandlersPackage;
/**
* Indicates whether perform presence check of the MyBatis xml config file.
*/
private boolean checkConfigLocation = false;
/**
* Execution mode for {@link org.mybatis.spring.SqlSessionTemplate}.
*/
private ExecutorType executorType;
/**
* The default scripting language driver class. (Available when use together with mybatis-spring 2.0.2+)
*/
private Class<? extends LanguageDriver> defaultScriptingLanguageDriver;
/**
* Externalized properties for MyBatis configuration.
*/
private Properties configurationProperties;
/**
* A Configuration object for customize default settings. If {@link #configLocation} is specified, this property is
* not used.
*/
@NestedConfigurationProperty
private Configuration configuration;
}
第六步:编写对应的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.rainszj.mapper.UserMapper">
<select id="selectAllUsers" resultType="user">
select * from mybatis.user
</select>
<select id="findUserById" resultType="user">
select * from mybatis.user where id = #{uid}
</select>
<update id="updateUser" parameterType="user">
update mybatis.user
set name = #{name},
pwd = #{pwd}
where id = #{id}
</update>
<update id="addUser" parameterType="user">
insert into mybatis.user values (#{id},#{name},#{pwd})
</update>
<delete id="deleteUser">
delete from mybatis.user where id = #{id}
</delete>
</mapper>
第七步:service层:
package com.rainszj.service;
import com.rainszj.mapper.UserMapper;
import com.rainszj.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> selectAllUsers() {
return userMapper.selectAllUsers();
}
// 根据id查找用户
public User findUserById(Integer id) {
return userMapper.findUserById(id);
}
// 修改用户
public int updateUser(User user) {
return userMapper.updateUser(user);
}
// 添加用户
public int addUser(User user) {
return userMapper.addUser(user);
}
// 删除用户
public int deleteUser(Integer id) {
return userMapper.deleteUser(id);
}
}
第八步:controller层:
package com.rainszj.controller;
import com.rainszj.pojo.User;
import com.rainszj.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/selectAllUsers")
public List<User> selectAllUsers() {
return userService.selectAllUsers();
}
@GetMapping("/findUserById/{id}")
public String findUserById(@PathVariable Integer id) {
User user = userService.findUserById(id);
System.out.println(user);
return "ok";
}
@GetMapping("/addUser")
public String addUser() {
userService.addUser(new User(4, "张三", "123"));
return "ok";
}
@GetMapping("/updateUser")
public String updateUser() {
userService.updateUser(new User(4, "李四", "123"));
return "ok";
}
@GetMapping("/deleteUser")
public String deleteUser(@RequestParam("uid") Integer id) {
userService.deleteUser(id);
return "ok";
}
}
测试一下就搞定 OK!