主要内容
1. JDBC
2. 整和Druid
1. Spring Boot 内部集成的 JDBC 模板访问 Mysql 数据库
1.1 Spring Boot访问 Mysql 数据库
1. 创建一个项目:
2. 连接虚拟机的数据库:mysql02
创建一个测试数据库:jdbc
3. 获取数据源
1. 配置文件 application.yml
spring:
datasource:
username: root
password: root
url: jdbc:mysql://192.168.92.130:3306/jdbc
driver-class-name: com.mysql.jdbc.Driver
注意:
driver 字符串原理用的 com.mysql.jdbc.Driver,
新版已经变为 com.mysql.cj.jdbc.Driver。
spring:
datasource:
username: root
password: root
url: jdbc:mysql://192.168.92.130:3306/jdbc
driver-class-name: com.mysql.cj.jdbc.Driver
4. 注意
默认使用的是 class com.zaxxer.hikari.HikariDataSource 数据源:
class com.zaxxer.hikari.HikariDataSource
默认的连接从HikariPool从取
HikariProxyConnection@167670282 wrapping com.mysql.cj.jdbc.ConnectionImpl@149274cb
5. 测试一下
@SpringBootTest
class Day0702Springboot06DataJdbcApplicationTests {
@Autowired
DataSource dataSource;
@Test
void contextLoads() throws SQLException {
System.out.println(dataSource.getClass());
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
}
2 探究一下数据源的自动配置的原理。
2.1 DataSourceConfiguration
1. 背景相关
1. 自动配置的数据源是:class com.zaxxer.hikari.HikariDataSource
2. 数据源的相关配置都在这个类里面:DataSourceProperties
2. 找到相关的自动配置类
1. org.springframework.book:spring-boot-autoconfigure:2.3.1.RELEASE
2. org.springframework.boot.autoconfigure.jdbc
3. org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration
4. org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
3. 打开数据源配置类:DataSourceConfiguration
org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration
1. 关键注解:
如果配置的spring.datasource.type是com.zaxxer.hikari.HikariDataSource,那么使用
com.zaxxer.hikari.HikariDataSource:数据源。
matchIfMissing = true,如果没有,默认你配了
这就是为什么默认使用的是HikariDataSource数据源。
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true)
2. 相关源码
abstract class DataSourceConfiguration {
//tomcat.jdbc.pool.DataSource:数据源
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.tomcat.jdbc.pool.DataSource",
matchIfMissing = true)
static class Tomcat {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.tomcat")
org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties) {
org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource(properties,
org.apache.tomcat.jdbc.pool.DataSource.class);
return dataSource;
}
}
//com.zaxxer.hikari.HikariDataSource:数据源
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
//如果配置的spring.datasource.type是com.zaxxer.hikari.HikariDataSource,那么使用
//com.zaxxer.hikari.HikariDataSource:数据源,matchIfMissing = true,如果没有,默认你配了
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true)
static class Hikari {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
return dataSource;
}
}
3. 我们可以通过: Spring.datasource.type指定数据源类型。
4. 数据源配置类:DataSourceConfiguration
根据配置创建数据源,默认使用HikariDataSource数据源。
5. 指定自定义的数据源
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class Generic {
@Bean
DataSource dataSource(DataSourceProperties properties) {
//使用构建者模式创建数据源
return properties.initializeDataSourceBuilder().build();
}
}
6. 点进build()看一下
BeanUtils工具类利用反射创建相应type的数据源,并且绑定相关属性
public T build() {
Class<? extends DataSource> type = this.getType();
DataSource result = (DataSource)BeanUtils.instantiateClass(type);
this.maybeGetDriverClassName();
//绑定相关属性
this.bind(result);
return result;
}
2.2 DataSourceAutoConfiguration
1. 打开这个数据源自动配置类
引入了一个:DataSourceInitializationConfiguration.class
2. 点进去,来到了DataSourceInitializationCon
引入了一个:DataSourceInitializerInvoker.class
数据源初始化器的增强类
3. 点进去来到了,DataSourceInitializerInvoker.class
它是一个监听器:监听数据源创建事件的。
方法一:afterPropertiesSet()
会调用DataSourceInitializer类中的createSchema()方法
作用:
创建建表语句:createSchema()
运行建表语句:runScripts(scripts, username, password);
方法二:onApplicationEvent(DataSourceSchemaCreatedEvent event)
监听到事件后,会执行这个方法
调用DataSourceInitializer类中的initSchema()方法
作用:
调用initSchema()方法,初始化Schema
runScripts(scripts, username, password); 运行插入数据的sql语句
4. 那么我们将建表和插入数据的相关sql文件放进来就行了。
5. 默认的规则:
注意:DataSourceInitializerInvoker上的注释
1. 建表的sql:命名为schema-*.sql
2. 数据的sql:命名为data-*.sql
6. 测试一下建表语句
怎么样才能让我们的建表文件department.sql被加载呢
在分析第3步时,我们找到DataSourceInitializerInvoker中的一个方法:afterPropertiesSet()
它的作用:
创建建表语句:createSchema()
运行建表语句:runScripts(scripts, username, password);
7. 我们点进createSchema();看一下
创建建表语句时createSchema()的方法中,有一个获取相关建表资源的方法:getScripts()方法
8. 点进getScripts()方法
2.3 注意:这就是规则
这里面就指明了我们的建表文件department.sql被加载的规则:
this.properties.getSchema()
它是dataSourceProperties中的属性:private List<String> schema;
传给了getScripts()方法中的第二个参数:List<String> resources
如果resources为空,SpringBoot就去这两个路径找资源:
"classpath*:" + fallback + "-" + platform + ".sql"
"classpath*:" + fallback + ".sql"
传过来的fallback值:schema
即如果没有sql语句文件,去类路径下找
schema-all.sql:DDL和DML语句
schema.sql: DDL语句
如果不为空:就直接返回这个资源路径。
所以我们可以指定sql文件的路径位置List<String> resources,如果能获取到就直接返回。
private List<Resource> getScripts(String propertyName, List<String> resources, String fallback) {
//文件路径位置不为空,就直接返回。所以我么可以定义这个resources
//它正好是dataSourceProperties中的schema属性。
if (resources != null) {
return getResources(propertyName, resources, true);
}
//如果位置为空,就去类路径下这些文件找
String platform = this.properties.getPlatform();
List<String> fallbackResources = new ArrayList<>();
fallbackResources.add("classpath*:" + fallback + "-" + platform + ".sql");
fallbackResources.add("classpath*:" + fallback + ".sql");
return getResources(propertyName, fallbackResources, false);
}
fallbackResources.add("classpath*:" + fallback + "-" + platform + ".sql");
fallbackResources.add("classpath*:" + fallback + ".sql");
其中platform是DataSourceProperties中的属性:值为"all"
代表了DDL DML等sql语句
9. 所以规则就是:
1. 如果没有sql语句文件,去类路径下找
schema-all.sql:DDL和DML语句
schema.sql: DDL语句
2. 如果我们想自己指定规则(用自己的命名规则)
定义DataSourceProperties中的private List<String> schema;属性。
10. 改名测试一下:
2.4 注意:*******************************
配置中要开启一下:弹幕大神在线指导
spring:
datasource:
initialization-mode: always
11. 成功创建departement表
12. 指定加载的位置
如果我们就想加载命名为department.sql的文件名怎么办?
定义DataSourceProperties中的private List<String> schema;属性。
注意:
schema是一个list属性,写法规则
注意空格:空格不对会报错
结果:建表成功
2.5 JdbcTemplateAutoConfiguration:导入操作数据库的两个工具
如果有数据源Dataource和JdbcTemplate类时,会给我们import两个配置类
JdbcTemplateConfiguration:
NamedParameterJdbcTemplateConfiguration
JdbcTemplateConfiguration:
如果没有JdbcOperations类时,JdbcTemplateConfiguration会给容器添加一个jdbcTemplate组件
NamedParameterJdbcTemplateConfiguration同理
2.6 测试一些数据库操作
1. 注意如果sql文件还在类路径下,那么启动时还会在重新执行sql。就会将我们之前的数据覆盖掉。
2. HelloController
@Controller
public class HelloController {
//自动注入:因为@ConditionalOnClass({ DataSource.class, JdbcTemplate.class })条件满足
@Autowired
JdbcTemplate jdbcTemplate;
@ResponseBody //把数据写出去
@GetMapping("/query")
public Map<String, Object> map(){
List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from department");
return list.get(0);
}
}
3. 因为表中数据被刷新了,我们再重新添加
4. 执行主方法
5. 发送请求测试
return list.get(0);
3 测试其他数据源---使用druid数据源
3.1 背景
HikariDataSource数据源虽说比druid数据源性能要高一些,但是druid有成套的数据源
比如监控,包括安全的整个解决方法,所以用到druid也非常多。
我们下面就来整和druid数据源
3.2 整和druid数据源
1. 引入druid数据源坐标
2. 在没有配置数据源时,默认使用HikariDataSource数据源
在配置文件中指定数据源类型
3. 测试一下有没有用druid数据源
3.3 对数据源进行配置
注意
现在大多数Druid配置都是log4j作为logger,但是logback作为新一代的日志框架,
我们有理由使用logback配置Druid Filter,之前的配置是:
spring.datasource.filters: stat,wall,log4j
Druid支持配置多种Filter,配置信息保存在druid-xxx.jar!/META-INF/druid-filter.properties下面,
具体如下:
druid.filters.default=com.alibaba.druid.filter.stat.StatFilter
druid.filters.stat=com.alibaba.druid.filter.stat.StatFilter
druid.filters.mergeStat=com.alibaba.druid.filter.stat.MergeStatFilter
druid.filters.counter=com.alibaba.druid.filter.stat.StatFilter
druid.filters.encoding=com.alibaba.druid.filter.encoding.EncodingConvertFilter
druid.filters.log4j=com.alibaba.druid.filter.logging.Log4jFilter
druid.filters.slf4j=com.alibaba.druid.filter.logging.Slf4jLogFilter
druid.filters.commonlogging=com.alibaba.druid.filter.logging.CommonsLogFilter
druid.filters.commonLogging=com.alibaba.druid.filter.logging.CommonsLogFilter
druid.filters.wall=com.alibaba.druid.wall.WallFilter
druid.filters.config=com.alibaba.druid.filter.config.ConfigFilter
Druid支持配置多种Filter,其中就有Slf4jLogFilter
众所周知,logback是slf4j的实现类,按照规定格式,改成下面就可以了:
spring.datasource.filters=stat,wall,slf4j
# 数据源其他配置
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,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,logback
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
1. DataSourceProperties中没有这些属性
所以直接这样配置是不能生效的
2. 测试一下发现没有生效
3. 要专门写一个配置类
作用:
将DruidDataSource中的属性用@ConfigurationProperties(prefix = "spring.datasource")和
主配置文件中前缀是spring.datasource.属性名的配置,进行绑定。
再用@Configuration将这个配置类生效
那么主配置文件中的配置就会生效。
@Configuration
public class DruidConfig {
//将druiddatasource中的属性和spring.datasource.属性名,配置进行绑定
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druid(){
return new DruidDataSource();
}
}
4. 测试一下发现配置生效了
4. SpringBoot整合Druid小结
导入druid依赖
1. 注入数据源:
spring:
#数据源基本配置
datasource:
username: root
password: root
url: jdbc:mysql://192.168.92.130:3306/jdbc
driver-class-name: com.mysql.cj.jdbc.Driver
2. 指明数据源类型:Druid
# 指明数据源类型
type: com.alibaba.druid.pool.DruidDataSource
3. 配置自动执行schema.sql
# 开启后就能执行sql语句文件
initialization-mode: always
4. 自定义加载的schema.sql语句文件的位置和名字
schema:
- classpath*:department.sql
5. 数据源的其他配置
# 数据源其他配置
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
6. 配置监控统计拦截的filter
filters: stat,wall,slf4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
7. 编写配置类,将上面的5,6步的配置和DruidDataSource绑定
@Configuration
public class DruidConfig {
//将配置的spring.datasource属性和它进行绑定
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druid(){
return new DruidDataSource();
}
8. 配置druid的监控(和第7步在一个类中)
//配置druid的监控
/**
* 1. 配置一个管理后台的
* Servlet:ServletRegistrationBean,来注册StatViewServlet
* 这个Servlet就是帮我们控制管理后台的Servlet
*/
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
//初始化StatViewServlet的属性
Map<String, String> initParams = new HashMap<String, String>();
initParams.put("loginUsername","admin");
initParams.put("loginPassword","123456");
initParams.put("allow",""); //默认是允许所有访问
initParams.put("deny","192.168.15.21");
bean.setInitParameters(initParams);
return bean;
}
/**
* 2. 配置一个监控的filter
*/
@Bean
public FilterRegistrationBean webStatFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
//初始化WebStatFilter的属性参数
Map<String, String> initParams = new HashMap<String, String>();
initParams.put("exclusion", "*.js, *.css, /druid/*");
//设置FilterRegistrationBean的属性
bean.setInitParameters(initParams);
bean.setUrlPatterns(Arrays.asList("/*"));
return bean;
}
}
9. 测试