SpringBoot系列之JDBC数据访问
SpringBoot jdbc是比较常用的内容,本博客通过实验并简单跟源码的方式进行介绍,希望可以帮助学习者更好地理解
环境准备:
- IDEA
- Maven
先新建一个项目:
New->Project or Module->Spring Initializer
![0bf833962482208c9bf527bf640ac223.png](https://img-blog.csdnimg.cn/img_convert/0bf833962482208c9bf527bf640ac223.png)
选择JDBC和mysql驱动,为了方便测试web等等也可以选上
![f7c3ac047de9762df5ccca798371d9f6.png](https://img-blog.csdnimg.cn/img_convert/f7c3ac047de9762df5ccca798371d9f6.png)
项目创建成功之后,会自动配上如下的配置:
org.springframework.bootspring-boot-starter-jdbcmysqlmysql-connector-javaruntime
新建一个application.yml配置文件:
spring: datasource: username: root password: root url: jdbc:mysql://127.0.0.1:3306/springboot driver-class-name: com.mysql.jdbc.Driver
新建一个junit测试类进行测试:
package com.example.springboot.jdbc;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import javax.sql.DataSource;import java.sql.Connection;import java.sql.SQLException;@SpringBootTestclass SppringbootJdbcApplicationTests {@AutowiredDataSource dataSource;@Testpublic void contextLoads() throws SQLException {System.out.println(dataSource.getClass());Connection connection = dataSource.getConnection();System.out.println(connection);connection.close();}}
启动调试,发现控制台抛出异常信息:
Caused by: com.mysql.cj.exceptions.UnableToConnectException: CLIENT_PLUGIN_AUTH is required
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:61)
at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:85)
at com.mysql.cj.protocol.a.NativeAuthenticationProvider.connect(NativeAuthenticationProvider.java:205)
at com.mysql.cj.protocol.a.NativeProtocol.connect(NativeProtocol.java:1340)
at com.mysql.cj.NativeSession.connect(NativeSession.java:157)
at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:956)
at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:826)
... 90 more
原因是我的mysql服务器是5.+版本,而最新版2.2.1默认配置的mysql驱动版本是8.+的,8.+的驱动类是com.mysql.cj.jdbc.Driver,而非com.mysql.jdbc.Driver,所以解决方法还是手动加一下mysql驱动版本
mysqlmysql-connector-java5.1.27runtime
ok,现在正常测试,测试通过:
class com.zaxxer.hikari.HikariDataSource
2019-12-14 15:47:25.241 INFO 6772 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2019-12-14 15:47:27.403 INFO 6772 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
HikariProxyConnection@23906266 wrapping com.mysql.jdbc.JDBC4Connection@717ed
可以看出2.2.1版本的Springboot默认数据源类是com.zaxxer.hikari.HikariDataSource,而1.+版本很多都是以org.apache.tomcat.jdbc.pool.DataSource为默认数据源类的,具体哪个版本开始改变的不清楚
既然Springboot默认使用hikari数据源,那就可以加上hikari的一些自定义配置
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/springboot?characterEncoding=utf8&useSSL=true username: root password: root driver-class-name: com.mysql.jdbc.Driver initialization-mode: always type: com.zaxxer.hikari.HikariDataSource hikari: jdbc-url: jdbc:mysql://127.0.0.1:3306/springboot?characterEncoding=utf8&useSSL=true minimum-idle: 5 maximum-pool-size: 15 auto-commit: true idle-timeout: 30000 pool-name: DatebookHikariCP max-lifetime: 1800000 connection-timeout: 30000 connection-test-query: select 1
经过调试,属性配置是有起效的
![b543508f4ebadd8dca506c8f48565e72.png](https://img-blog.csdnimg.cn/img_convert/b543508f4ebadd8dca506c8f48565e72.png)
ok,掌握简单应用之后,还是要跟一下源码,依照经验,找到对应的自动配置类DataSourceAutoConfiguration.java
不需要全部看懂源码,只要找到关键的信息即可,如图在这个自动配置类路其实就可以看出Springboot2.2.1支持的数据源种类有如下:
![582ab3b55d65a7854a73d0fe5219c46e.png](https://img-blog.csdnimg.cn/img_convert/582ab3b55d65a7854a73d0fe5219c46e.png)
点一下DataSourceConfiguration类,里面都是各种DataSource的配置,用了很多@Conditional注解,条件符合才会起效
![dca23e50ee1c8b962130a01864121597.png](https://img-blog.csdnimg.cn/img_convert/dca23e50ee1c8b962130a01864121597.png)
然后为什么说2.2.1版本默认是Hikari作为Datasource的?可以看一下项目的依赖图,可以看出默认引入了Hikari的jar
![3b920bc9e1abf27b61dc00b6f583141f.png](https://img-blog.csdnimg.cn/img_convert/3b920bc9e1abf27b61dc00b6f583141f.png)
而且条件都符合,所以HikariDataSource就会进行自动配置
![a901bbd0e6550de01cbfa7c849aae19c.png](https://img-blog.csdnimg.cn/img_convert/a901bbd0e6550de01cbfa7c849aae19c.png)
源码里用import引入了一个DataSourceInitializationConfiguration类,看其命名,应该是个初始化配置类
![c2d1d142325468d27e9a0df3f55134a1.png](https://img-blog.csdnimg.cn/img_convert/c2d1d142325468d27e9a0df3f55134a1.png)
挑重点,如图,很明显要用了Spring框架很重要的后置处理器,还有一个DataSourceInitializerInvoker类
![3e0236c14c9a798700069272d3c1f915.png](https://img-blog.csdnimg.cn/img_convert/3e0236c14c9a798700069272d3c1f915.png)
DataSourceInitializerInvoker类是做什么的?看其源码应该是一个初始化操作的监听器类,主要操作由DataSourceInitializer执行
![9f4fe6229d124952eb3e228ab8dca110.png](https://img-blog.csdnimg.cn/img_convert/9f4fe6229d124952eb3e228ab8dca110.png)
DataSourceInitializer源码,里面有很多操作都是进行schema脚本的操作,只要非isEnabled就可以执行
![55f2b167b16d1311a77493030b72973a.png](https://img-blog.csdnimg.cn/img_convert/55f2b167b16d1311a77493030b72973a.png)
只要两个条件符合就执行,DataSourceInitializationMode属性,看起来是通过配置文件获取的
![de8c376eab5b223b2aa1133bd6f492ba.png](https://img-blog.csdnimg.cn/img_convert/de8c376eab5b223b2aa1133bd6f492ba.png)
getScripts,获取对应的脚本
![3bd7cef0ef9bed6a807889af851d646e.png](https://img-blog.csdnimg.cn/img_convert/3bd7cef0ef9bed6a807889af851d646e.png)
读取schema脚本,所以可以看出只要将一些sql文件放在resources即可,默认文件命名为schema.sql,schema‐all.sql;
![c6bc2fb3d5594f4316572c655019f8c1.png](https://img-blog.csdnimg.cn/img_convert/c6bc2fb3d5594f4316572c655019f8c1.png)
再通过资料,确认了这个类确实是执行一些初始化schema脚本的,根据application提示,发现有个属性,将其改为always即可
![30806817b20e86d6c1c3e6b1dd7ac50e.png](https://img-blog.csdnimg.cn/img_convert/30806817b20e86d6c1c3e6b1dd7ac50e.png)
改为embedded是不能执行的,虽然源码里代码是这样的
![6ceddc36b136179fdba8b11b16895555.png](https://img-blog.csdnimg.cn/img_convert/6ceddc36b136179fdba8b11b16895555.png)
还有这个关键代码,从配置spring.datasource.schema获取信息,
List scripts = getScripts("spring.datasource.schema", this.properties.getSchema(), "schema");
![e8e6b007c35ccf3e9ccd1e058f377e97.png](https://img-blog.csdnimg.cn/img_convert/e8e6b007c35ccf3e9ccd1e058f377e97.png)
private List getResources(String propertyName, List locations, boolean validate) {List resources = new ArrayList<>();for (String location : locations) {for (Resource resource : doGetResources(location)) {if (resource.exists()) {resources.add(resource);}else if (validate) {throw new InvalidConfigurationPropertyValueException(propertyName, resource,"The specified resource does not exist.");}}}return resources;}
location就是一个字符类型的位置信息,所以可以使用如下配置,显然就可以自定义schema脚本,不需要固定为schema.sql或者schema-all.sql
![5bd70145afb249480452fa7e77ca33f6.png](https://img-blog.csdnimg.cn/img_convert/5bd70145afb249480452fa7e77ca33f6.png)