一、Spring Data 简介
- Sping Data 官网
- 对于数据访问层,无论是 SQL(关系型数据库)还是 NOSQL(非关系型数据库)Spring Boot 底层,都是采用 Spring Data 的方式进行统一处理;
- Spring Boot 底层都是采用 Spring Data 的方式,进行统一处理各种数据库,Spring Data 也是 Spring 中,与 Spring Boot、Spring Cloud 等齐名的知名项目;
- 数据库相关的启动器:官方文档
二、整合 JDBC
2.1 环境搭建
- 导入测试数据库:
MySQL - 8.0.28
/* MySQL - 8.0.28 */
CREATE DATABASE `springboot`;
USE `springboot`;
DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '部门id',
`department_name` varchar(20) NOT NULL COMMENT '部门名字',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=106 DEFAULT CHARSET=utf8mb4;
insert into `department`(`id`,`department_name`) values
(101,'技术部'),
(102,'销售部'),
(103,'售后部'),
(104,'后勤部'),
(105,'运营部');
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '员工id',
`last_name` varchar(100) NOT NULL COMMENT '名字',
`email` varchar(100) NOT NULL COMMENT '邮箱',
`gender` int NOT NULL COMMENT '性别1 男, 0 女',
`department` int NOT NULL COMMENT '部门id',
`birth` datetime NOT NULL COMMENT '生日',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1006 DEFAULT CHARSET=utf8mb4;
insert into
`employee`(`id`,`last_name`,`email`,`gender`,`department`,`birth`) values
(1001,'张三','[email protected]',0,101,'2000-01-06 15:04:33'),
(1002,'李四','[email protected]',1,102,'2002-03-06 15:04:36'),
(1003,'王五','[email protected]',0,103,'2000-05-08 15:04:37'),
(1004,'赵六','[email protected]',0,104,'2001-06-06 15:04:39'),
(1005,'孙七','[email protected]',1,105,'2000-03-03 15:04:45');
2.2 测试数据源
-
新建 Spring Boot 项目,引入相应的模块:web、JDBC API、MySQL Driver;
查看
pom.xml
,自动导入了如下的启动器:
org.springframework.boot
spring-boot-starter-jdbc
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
runtime
- 创建 yaml 配置文件,连接数据库:
application.yaml
# MySQL - 8.0.28
spring:
datasource:
username: 数据库用户名
password: 数据库密码
# serverTimezone=UTC解决时区报错
url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8&useSSL=true
driver-class-name: com.mysql.cj.jdbc.Driver
- 配置完成后,就可以直接使用,因为 Spring Boot 已经默认进行了自动配置;
- 在测试类进行测试:
@SpringBootTest
class Springboot05DataApplicationTests {
// 自动装配数据源
@Autowired
DataSource dataSource;
@Test
void contextLoads() throws SQLException {
// 查看默认数据源
System.out.println(dataSource.getClass());
// 获得连接
Connection connection = dataSource.getConnection();
System.out.println(connection);
// 关闭连接
connection.close();
}
}
-
结果:默认配置的数据源
com.zaxxer.hikari.HikariDataSource
查找数据源的自动配置类:DataSourceAutoConfiguration,里面有一个关于数据源的内部类:PooledDataSourceConfiguration
@Configuration(proxyBeanMethods = false)
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
// 导入了默认的数据源
@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration {
}
- 这里导入的类,都在 DataSourceConfiguration 配置类下;
- HikariDataSource 相比于传统的 C3P0、DBCP、Tomcat jdbc 等,速度更快;
- 可以使用
spring.datasource.type
指定自定义的数据源类型,值为要使用的连接池实现的完全限定名;
2.3 JdbcTemplate
- xxxTemplate 是 Spring Boot 已经配置好的模板类,可以直接使用 CRUD;
- 有了数据源
com.zaxxer.hikari.HikariDataSource
,然后可以拿到数据库连接java.sql.Connection
,有了连接,就可以使用原生的 JDBC 语句来操作数据库; - 即使不使用第三方数据库操作框架,如 MyBatis 等,Spring 本身也对原生的 JDBC 做了轻量级的封装,即 JdbcTemplate;
- 数据库操作的所有 CRUD 方法,都在 JdbcTemplate 中;
- Spring Boot 不仅提供了默认的数据源,同时默认已经配置好了 JdbcTemplate 放在了容器中,使用时,只需注入即可;
- JdbcTemplate 的自动配置是依赖
org.springframework.boot.autoconfigure.jdbc
包下的JdbcTemplateConfiguration
类;
JdbcTemplate 主要提供以下几类方法:
- execute 方法:可以用于执行任何SQL语句,一般用于执行 DDL(数据定义) 语句;
- update 方法:用于执行新增、修改、删除等语句;
- batchUpdate 方法:用于执行批处理相关语句;
- query 方法及 queryForXXX 方法:用于执行查询相关语句;
- call 方法:用于执行存储过程、函数相关语句;
JdbcTemplate 测试
- 创建 Controller,注入 JdbcTemplate:(无实体类方式,从数据库返回数据)
// 直接返回字符串
@RestController
public class JdbcController {
/*
Spring Boot 默认提供了数据源和 org.springframework.jdbc.core.JdbcTemplate
JdbcTemplate 中会自己注入数据源,用于简化JDBC操作
还能避免一些常见的错误,使用时会自动关闭数据库连接
*/
@Autowired
JdbcTemplate jdbcTemplate;
// 查询employee表中所有数据
// List中的1个Map对应数据库的1行数据
// Map中的key对应数据库的字段名,value对应数据库的字段值
@GetMapping("/userList")
public List
三、整合 Druid
3.1 Druid 简介
- Druid 是阿里巴巴开源平台上,一个数据库连接池实现,结合了 C3P0、DBCP 等 DB 池的优点,同时加入了日志监控;
- Druid 可以很好的监控 DB 池连接和 SQL 的执行情况,天生就是针对监控而生的 DB 连接池;
- Spring Boot 2.0 以上默认使用 Hikari 数据源,可以说 Hikari 与 Driud 都是当前 Java Web 上最优秀的数据源;
- Github 地址:链接
3.2 基本配置参数
com.alibaba.druid.pool.DruidDataSource
- 链接
配置 | 缺省值 | 说明 |
---|---|---|
name | 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。如果没有配置,将会生成一个名字,格式是:"DataSource-" + System.identityHashCode(this). 另外配置此属性至少在1.0.5版本中是不起作用的,强行设置name会出错。详情-点此处。 | |
url | 连接数据库的url,不同数据库不一样。例如: mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto | |
username | 连接数据库的用户名 | |
password | 连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里 | |
driverClassName | 根据url自动识别 | 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName |
initialSize | 0 | 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 |
maxActive | 8 | 最大连接池数量 |
maxIdle | 8 | 已经不再使用,配置了也没效果 |
minIdle | 最小连接池数量 | |
maxWait | 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 | |
poolPreparedStatements | false | 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。 |
maxPoolPreparedStatementPerConnectionSize | -1 | 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 |
validationQuery | 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 | |
validationQueryTimeout | 单位:秒,检测连接是否有效的超时时间。底层调用jdbc Statement对象的void setQueryTimeout(int seconds)方法 | |
testOnBorrow | true | 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |
testOnReturn | false | 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |
testWhileIdle | false | 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 |
keepAlive | false (1.0.28) | 连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作。 |
timeBetweenEvictionRunsMillis | 1分钟(1.0.14) | 有两个含义: 1) Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。 2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明 |
numTestsPerEvictionRun | 30分钟(1.0.14) | 不再使用,一个DruidDataSource只支持一个EvictionRun |
minEvictableIdleTimeMillis | 连接保持空闲而不被驱逐的最小时间 | |
connectionInitSqls | 物理连接初始化的时候执行的sql | |
exceptionSorter | 根据dbType自动识别 | 当数据库抛出一些不可恢复的异常时,抛弃连接 |
filters | 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall | |
proxyFilters | 类型是List |
3.3 配置数据源
- 添加 Druid 数据源依赖:
com.alibaba
druid-spring-boot-starter
1.2.9
com.alibaba
druid
1.2.9
- 切换数据源:通过
spring.datasource.type
指定数据源;
# MySQL - 8.0.28
spring:
datasource:
username: 数据库用户名
password: 数据库密码
# serverTimezone=UTC解决时区报错
url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8&useSSL=true
driver-class-name: com.mysql.cj.jdbc.Driver
# 自定义数据源 druid
type: com.alibaba.druid.pool.DruidDataSource
-
数据源切换之后,在测试类中注入 DataSource,获取并查看数据源信息:
设置数据源连接初始化大小、最大连接数、等待时间、最小连接数等设置:
# MySQL - 8.0.28
spring:
datasource:
username: 数据库用户名
password: 数据库密码
# serverTimezone=UTC解决时区报错
url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8&useSSL=true
driver-class-name: com.mysql.cj.jdbc.Driver
# 自定义数据源 druid
type: com.alibaba.druid.pool.DruidDataSource
# 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 依赖即可,如使用log4j2,对应导入相应依赖
filters: stat,wall,log4j2
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
- 使用 Log4j2 日志,需要导入 Log4j2 的依赖:
org.springframework.boot
spring-boot-starter-log4j2
2.6.7
log4j
log4j
1.2.17
自定义配置类
- 将 DruidDataSource 的参数,绑定到全局配置文件中,再添加到容器中;
- 创建 config 目录,新建自定义配置类:DruidConfig
// 配置类
@Configuration
public class DruidConfig {
/*
将自定义的Druid数据源添加到容器中,不再由Spring Boot自动创建
绑定全局配置文件中的druid数据源属性,到com.alibaba.druid.pool.DruidDataSource从而让它们生效
@ConfigurationProperties(prefix = "spring.datasource"):作用就是将全局配置文件中,
前缀为spring.datasource的属性值注入到com.alibaba.druid.pool.DruidDataSource的同名参数中
*/
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druidDataSource() {
return new DruidDataSource();
}
}
- 在测试类中测试:
@SpringBootTest
class Springboot05DataApplicationTests {
// 自动装配数据源
@Autowired
DataSource dataSource;
@Test
void contextLoads() throws SQLException {
// 查看默认数据源
System.out.println(dataSource.getClass());
// 获得连接
Connection connection = dataSource.getConnection();
System.out.println(connection);
DruidDataSource druidDataSource = (DruidDataSource) dataSource;
System.out.println("数据源最大连接数:" + druidDataSource.getMaxActive());
System.out.println("数据源初始化连接数:" + druidDataSource.getInitialSize());
// 关闭连接
connection.close();
}
}
-
运行测试:
配置 Druid 数据源监控
- Druid 数据源具有监控的功能,并提供了一个 web 界面,方便用户查看;
- 设置 Druid 的后台管理页面,如:登录账号、密码、后台管理等相关内容:
// 配置Druid监控管理后台的Servlet(固定格式)
// 内置Servlet容器时没有web.xml文件,所以使用Spring Boot注册Servlet方式
@Bean
public ServletRegistrationBean statViewServlet() {
ServletRegistrationBean bean = new ServletRegistrationBean(
// /druid/*:访问路径,可自定义
new StatViewServlet(), "/druid/*");
// 这些参数可以在 com.alibaba.druid.support.http.StatViewServlet
// 的父类 com.alibaba.druid.support.http.ResourceServlet 中找到
Map initParams = new HashMap<>();
// 后台管理界面的登录账号,K 为固定名称
initParams.put("loginUsername", "自定义用户名");
// 后台管理界面的登录密码,K 为固定名称
initParams.put("loginPassword", "自定义密码");
// 后台允许访问
// initParams.put("allow", "localhost"):表示只有本机可以访问
// initParams.put("allow", ""):为空或者为null时,表示允许所有访问
initParams.put("allow", "");
// deny:Druid 后台拒绝谁访问
// initParams.put("test", "192.168.1.20"); 表示禁止此ip访问
// 设置初始化参数
bean.setInitParameters(initParams);
return bean;
}
-
运行测试:访问
http://localhost:8080/druid/login.html
-
登录后,打开新页面,执行上例中的查询操作,里面会有相关的记录:
配置 Druid web 监控 filter 过滤器:
// 配置Druid web监控filter过滤器
// WebStatFilter:用于配置Web和Druid数据源之间的管理关联监控统计
@Bean
public FilterRegistrationBean webStatFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
// exclusions:设置哪些请求进行过滤排除掉,从而不进行统计
Map initParams = new HashMap<>();
initParams.put("exclusions", "*.js,*.css,/druid/*,/jdbc/*");
bean.setInitParameters(initParams);
// "/*" 表示过滤所有请求
bean.setUrlPatterns(Arrays.asList("/*"));
return bean;
}
- 在使用时,按需求进行配置即可,主要用作监控;
四、整合 MyBatis
- 官方文档
- MyBatis 基础
4.1 环境配置
- 导入 MyBatis 所需要的依赖:
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.2.2
- 配置数据库连接:
application.yaml
# MySQL - 8.0.28
spring:
datasource:
username: 数据库用户名
password: 数据库密码
# serverTimezone=UTC解决时区报错
url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8&useSSL=true
driver-class-name: com.mysql.cj.jdbc.Driver
- 如静态资源未导出,需在 maven 中,配置资源过滤:
pom.xml
src/main/java
**/*.properties
**/*.xml
true
src/main/resources
**/*.properties
**/*.xml
true
4.2 整合测试
部门表测试 Department
- 创建实体类:新建 pojo 目录,
Department.java
// 导入 Lombok
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
private Integer id;
private String departmentName;
}
- 创建 mapper 目录,以及对应的 Mapper 接口:
// @Mapper:表示本类是一个MyBatis的Mapper接口类
// 或在主程序类中,使用:@MapperScan("com.study.mapper") 扫描包
@Mapper
// 注册组件:@Component --> dao或mapper的层的表示方式
@Repository
public interface DepartmentMapper {
// 获取所有部门信息
List getDepartmentList();
// 通过id获得部门
Department getDepartmentById(@Param("id") Integer id);
}
- 在 resources 资源目录下,新建
mybatis/mapper
目录,创建对应的 Mapper 映射文件:DepartmentMapper.xml
- 在配置文件中整合 MyBatis:
application.yaml
# 整合 MyBatis
mybatis:
# 对应实体类的路径
type-aliases-package: com.study.pojo
# 指定myBatis的核心配置文件与Mapper映射文件,classpath:后面不需要加 /
mapper-locations: classpath:mybatis/mapper/*.xml
# 开启驼峰命名规范自动映射
configuration:
map-underscore-to-camel-case: true
- 创建 Controller 进行测试:DepartmentController
@RestController
public class DepartmentController {
@Autowired
DepartmentMapper departmentMapper;
// 查询全部部门
@GetMapping("/t1")
public List getDepartmentList() {
return departmentMapper.getDepartmentList();
}
// 通过id获得部门
@GetMapping("/t2/{id}")
public Department getDepartmentById(@PathVariable("id") Integer id) {
return departmentMapper.getDepartmentById(id);
}
}
-
运行测试:
员工表测试 Employee
- 在 pojo 下,创建实体类:Employee
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
private Integer id;
private String lastName;
private String email;
// 性别:0 女 1 男
private Integer gender;
// 部门id
private Integer department;
private Date birth;
// 部门信息(联表查询)
private Department eDepartment;
}
- 创建 EmployeeMapper 接口:
// @Mapper:表示本类是一个MyBatis的Mapper接口类
// 或在主程序类中,使用:@MapperScan("com.study.mapper") 扫描包
@Mapper
// 注册组件:@Component --> dao或mapper的层的表示方式
@Repository
public interface EmployeeMapper {
// 查询全部员工信息
List getAllEmployee();
// 新增一个员工
int addEmployee(Employee employee);
// 修改员工信息
int updateEmployee(Employee employee);
// 通过id获得员工信息
Employee getEmployeeById(@Param("id") Integer id);
// 通过id删除员工
int deleteEmployeeById(@Param("id") Integer id);
}
- 创建对应的 Mapper 映射文件:EmployeeMapper.xml
insert into `employee` (`last_name`, `email`, `gender`, `department`, `birth`)
values (#{lastName}, #{email}, #{gender}, #{department}, #{birth});
update `employee`
set `last_name`=#{lastName},
`email`=#{email},
`gender`=#{gender},
`department`=#{department},
`birth`=#{birth}
where id = #{id};
delete
from `employee`
where id = #{id};
- 创建 Controller 进行测试:EmployeeController
// 直接返回字符串
@RestController
public class EmployeeController {
// 按类型自动装配
@Autowired
EmployeeMapper employeeMapper;
// 查询全部员工
@GetMapping("/empAll")
public List getAllEmployee() {
return employeeMapper.getAllEmployee();
}
// 新增一个员工
@GetMapping("/addEmp")
public int addEmployee() {
// 模拟前端数据
Employee emp = new Employee();
emp.setLastName("测试1");
emp.setEmail("[email protected]");
emp.setGender(1);
emp.setDepartment(105);
emp.setBirth(new Date());
return employeeMapper.addEmployee(emp);
}
// 修改员工信息
@GetMapping("/updateEmp/{id}")
public int updateEmployee(@PathVariable("id") Integer id) {
// 根据id查询员工
Employee emp = employeeMapper.getEmployeeById(id);
// 模拟前端数据
// 修改员工信息
emp.setLastName("修改测试");
emp.setEmail("[email protected]");
emp.setGender(0);
emp.setDepartment(101);
emp.setBirth(new Date());
return employeeMapper.updateEmployee(emp);
}
// 通过id获得员工信息
@GetMapping("/getEmp/{id}")
public Employee getEmployeeById(@PathVariable("id") Integer id) {
return employeeMapper.getEmployeeById(id);
}
// 通过id删除员工
@GetMapping("/delEmp/{id}")
public int deleteEmployeeById(@PathVariable("id") Integer id) {
return employeeMapper.deleteEmployeeById(id);
}
}
总结:
-
整合 MyBatis 步骤:
导入 MyBatis 所需要的依赖;
在配置文件中,配置数据库连接和数据源(默认为 Hikari);
-
创建 Mapper 接口类:
- @Mapper:表示本类是一个MyBatis 的 Mapper 接口类;
- @Repository:注册组件:@Component --> dao 或 mapper 的层的表示方式;
创建对应的 Mapper 映射文件(数据库操作);
-
在配置文件中整合 MyBatis:
对应实体类的路径:
mybatis.type-aliases-package
;Mapper 映射文件路径:
mybatis.mapper-locations
;