前言:
小伙伴们,大家好,我是狂奔の蜗牛rz,当然你们可以叫我蜗牛君,我是一个学习Java半年多时间的小菜鸟,同时还有一个伟大的梦想,那就是有朝一日,成为一个优秀的Java架构师。
这个SpringBoot基础学习系列用来记录我学习SpringBoot框架基础知识的全过程 (这个系列是参照B站狂神的SpringBoot最新教程来写的,由于是之前整理的,但当时没有发布出来,所以有些地方可能有错误,希望大家能够及时指正!)
之后我将会以一天一更的速度更新这个系列,还没有学习SpringBoot的小伙伴可以参照我的博客学习一下;当然学习过的小伙伴,也可以顺便跟我一起复习一下基础。
最后,希望能够和大家一同进步吧!加油吧!少年们!
6.1 整合JDBC框架 6.1.1 Spring Data简介废话不多说,让我们开始今天的学习内容吧,由于今天我们来到了SpringBoot基础学习的第六站:整合JDBC框架!
对于数据访问层,无论是SQL (关系型数据库) 还是NOSQL (非关系型数据库),Spring Boot底层都是采用Spring Data 的方式进行统一处理各种数据库,SpringData 也是Spring框架系列中与Spring Boot、Spring Cloud 等知名框架齐名的
Spring Data官网:https://spring.io/projects/spring-data
6.1.2 搭建基本环境 1.创建SpringBoot项目 1-1 选择Spring initializr项目 1-2 设置项目基本信息 1-3 引入相关资源依赖- 引入Spring Web、Thymeleaf和JDBC API、Spring Data JDBC及MySQL Driver相关资源依赖
- 修改数据库驱动版本,默认引入的是8.0版本的驱动
2-2 查看资源依赖 3.连接MySQL数据库 3-1 编写核心配置文件4.0.0 org.springframework.boot spring-boot-starter-parent 2.4.5 com.kuang springboot-04-data 0.0.1-SNAPSHOT springboot-04-data Demo project for Spring Boot 1.8 org.springframework.boot spring-boot-starter-data-jdbc org.springframework.boot spring-boot-starter-jdbc org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot-starter-web mysql mysql-connector-java compile 5.1.46 org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin
核心配置文件使用properties格式或者yaml格式都可以,看个人习惯,这里推荐使用yml格式
- 使用properties格式编写核心配置文件
# 设置Tomcat服务器端口号 server.port=8888 # 设置数据库登录用户 spring.datasource.username=root # 设置数据库登录密码 spring.datasource.password=123456 # 设置数据库url连接 spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=true # 设置数据库驱动 spring.datasource.driver-class-name=com.mysql.jdbc.Driver
- 使用yml格式编写核心配置文件
# 设置数据库驱动
spring:
datasource:
username: root
password: 123456
# MySQL 8.0版本以上的url链接格式
# url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
# MySQL 8.0版本以下的url链接格式
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=true
# 注意: com.mysql.cj.jdbc.Driver是8.0版本以上的数据库驱动, 而com.mysql.jdbc.Driver是8.0以下版本的数据库驱动
# driver-class-name: com.mysql.cj.jdbc.Driver
driver-class-name: com.mysql.jdbc.Driver
# 设置服务器端口号
server:
port: 8888
3-2 连接MySQL数据库
- 添加数据源,这里选择MySQL数据库
- 设置数据库的登录用户名和密码,进行测试连接
- 选择要连接的具体数据库
- 数据库连接成功,右侧显示数据库表
- 测试连接过程中遇到了
解决方法:设置MySQL数据库的时区
1.连接mysql数据库mysql -u root -p2.查看系统默认的时区
show variables like'%time_zone';3.设置新的时区
set global time_zone = '+8:00';4.查看修改后的时区
- 这里注意要退出命令窗口后重新进入才可以看到修改成功后的时区
show variables like'%time_zone';6.1.3 查看默认数据源和数据源自动配置原理 1.查看默认数据源 1-1 修改Springboot04DataApplicationTests测试类
package com.kuang;
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;
@SpringBootTest
class Springboot04DataApplicationTests {
// 使用@Autowired注解将DataSource自动装配到Spring容器中
@Autowired
DataSource dataSource; // 数据源
@Test
void contextLoads() throws SQLException {
// 查看默认的数据源:class com.zaxxer.hikari.HikariDataSource,相当于DBCP等数据源
System.out.println(dataSource.getClass());
// 获取数据库连接
Connection connection = dataSource.getConnection();
// 打印connection连接信息
System.out.println(connection);
// xxx Template:SpringBoot已经配置好模板bean,拿来即用 CRUD
// 关闭数据库连接
connection.close();
}
}
1-2 首次测试结果
结果:测试失败,出现测试引擎的ID’ junit_jupiter’不能找到的错误
1-3 解决测试引擎ID无法找到问题- 在pom.xml配置文件的测试资源依赖中添加vintage引擎
4.0.0 org.springframework.boot spring-boot-starter-parent 2.4.5 com.kuang springboot-04-data 0.0.1-SNAPSHOT springboot-04-data Demo project for Spring Boot 1.8 org.springframework.boot spring-boot-starter-data-jdbc org.springframework.boot spring-boot-starter-jdbc org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot-starter-web mysql mysql-connector-java compile 5.1.46 org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine org.springframework.boot spring-boot-maven-plugin
- 在右侧的Maven管理中使用clean清除,重新导入资源依赖
结果:SpringBoot默认的数据源是class com.zaxxer.hikari.HikariDataSource
2.数据源自动配置的原理 2-1 jdbc数据源位置- 首先在项目左侧的外部资源中找到spring-boot.autoconfigure-2.45的资源jar包
- 然后在org.springframework.boot.autoconfigure包路径下找到jdbc包,最后在metadata包下的JdbcTemplateAutoConfiguration配置类
- 查看DataSourceProperties数据源属性类源码
// 使用@ConfigurationProperties注解, 设置可有效绑定到此对象的属性前缀, 这里设置为"spring.datasource"(prefix前缀的value值一般使用"."进行分隔)
@ConfigurationProperties(prefix = "spring.datasource")
// DataSourceProperties(数据源属性)类, 实现BeanClassLoaderAware(Bean类加载器软件)和InitializingBean(初始化Bean)接口
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
private ClassLoader classLoader; // 类加载器
private String name; // 数据库名称
private boolean generateUniqueName = true; // 是否生成随机的数据源名称
// 要使用连接池实现类的全限定名,默认情况下它从类路径自动检测
private Class extends DataSource> type;
// JDBC驱动的全限定名,默认情况下根据URL自动检测
private String driverClassName;
private String url; // 数据库JDBC的链接
private String username; // 数据库的登录用户名
private String password; // 数据库的登录密码
// 设置jndiName(jndi表示Java本地目录接口)后,数据源JNDI类的位置,url链接,用户名和密码都会被忽略
private String jndiName;
//确定是否应使用可用的DDL(全称为Data definition language, 表示数据定义语言)和DML(全称为Data Manipulation Language, 即数据操作语言)脚本执行数据源初始化时,应用该模式
private DataSourceInitializationMode initializationMode = DataSourceInitializationMode.EMBEDDED;
private String platform = "all";
private List schema;
private String schemaUsername;
private String schemaPassword;
private List data;
private String dataUsername;
private String dataPassword;
private boolean continueOnError = false;
private String separator = ";";
private Charset sqlscriptEncoding;
// 内置的数据库连接
private EmbeddedDatabaseConnection embeddedDatabaseConnection = EmbeddedDatabaseConnection.NONE;
// 获取Xa的实例化对象(Xa表示具体的数据源设置信息)
private Xa xa = new Xa();
//唯一名
private String uniqueName;
//...(省略中间部分代码)...
// 获取Xa值的get方法
public Xa getXa() {
return this.xa;
}
// 设置Xa值的set方法
public void setXa(Xa xa) {
this.xa = xa;
}
public static class Xa {
private String dataSourceClassName;
private Map properties = new linkedHashMap<>();
// 属性对应的get和set方法
public String getDataSourceClassName() {
return this.dataSourceClassName;
}
public void setDataSourceClassName(String dataSourceClassName) {
this.dataSourceClassName = dataSourceClassName;
}
public Map getProperties() {
return this.properties;
}
public void setProperties(Map properties) {
this.properties = properties;
}
}
//...(省略后面部分代码)...
}
- 查看@ConfigurationProperties注解源码
package org.springframework.boot.context.properties;
import java.lang.annotation.documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
// 使用@Target注解, 设置作用目标, 元素类型选择类型和方法, 多个属性使用{}包裹, 使用","进行分隔
@Target({ ElementType.TYPE, ElementType.METHOD })
// 使用@Retention注解, 设置保留时间, 保留策略选择运行期间
@Retention(RetentionPolicy.RUNTIME)
// 使用@documented注解, 该注解将由javadoc和类型工具记录, 并且成为注解元素的公共API的一部分
@documented
public @interface ConfigurationProperties {
@AliasFor("prefix")
String value() default "";
@AliasFor("value")
String prefix() default "";
boolean ignoreInvalidFields() default false;
boolean ignoreUnknownFields() default true;
}
- 查看@AliasFor注解源码
package org.springframework.core.annotation;
import java.lang.annotation.Annotation;
import java.lang.annotation.documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@documented
public @interface AliasFor {
@AliasFor("attribute")
String value() default "";
@AliasFor("value")
String attribute() default "";
Class extends Annotation> annotation() default Annotation.class;
}
- 查看DataSourceAutoConfiguration配置类的源码
// 使用@Configuration注解, 将当前类注册为配置类, 交由Spring的IOC容器统一管理, 将proxyBeanMethods(动态Bean方法)的属性值设置为false, 表示不使用动态代理
@Configuration(proxyBeanMethods = false)
// 使用@ConditionalOnClass注解, 表示Spring的IOC容器中包含指定类: DataSource(数据源)类和EmbeddedDatabaseType(内置的数据库类型)类
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
// 使用@ConditionalOnMissingBean注解, 表示Spring的IOC容器中不存在指定Bean, 属性类型为io.r2dbc.spi.ConnectionFactory(连接工厂)
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
// 使用@EnableConfigurationProperties注解, 表示开启配置属性
@EnableConfigurationProperties(DataSourceProperties.class)
// 使用@import注解, 表示在该类中导入其他类: DataSourcePoolmetadataProvidersConfiguration(数据源池元数据提供者配置)类和DataSourceInitializationConfiguration(数据源初始化配置)类
@import({ DataSourcePoolmetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
// 数据源自动配置类
public class DataSourceAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@Conditional(EmbeddedDatabaseCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@import(EmbeddedDataSourceConfiguration.class)
// 内置的数据库配置类
protected static class EmbeddedDatabaseConfiguration {
}
@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 {
}
// 池数据源条件:检查是否存在其他数据源类型
static class PooledDataSourceCondition extends AnyNestedCondition {
PooledDataSourceCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
// 条件成立才添加到容器中
@ConditionalOnProperty(prefix = "spring.datasource", name = "type")
static class ExplicitType {
}
@Conditional(PooledDataSourceAvailableCondition.class)
static class PooledDataSourceAvailable {
}
}
//...(省略后面部分代码)...
}
- 查看DataSourceConfiguration配置类源码
// 抽象类数据源配置类
abstract class DataSourceConfiguration {
// 使用@SuppressWarnings注解, 镇压未检查的警告
@SuppressWarnings("unchecked")
protected static T createDataSource(DataSourceProperties properties, Class extends DataSource> type) {
return (T) properties.initializeDataSourceBuilder().type(type).build();
}
// Tomcat数据源配置
@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);
DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(properties.determineUrl());
String validationQuery = databaseDriver.getValidationQuery();
if (validationQuery != null) {
dataSource.setTestOnBorrow(true);
dataSource.setValidationQuery(validationQuery);
}
return dataSource;
}
}
// Hikari数据源配置
@Configuration(proxyBeanMethods = false)
// 判断Hikari数据源类是否存在
@ConditionalOnClass(HikariDataSource.class)
// 判断是否缺失数据源类
@ConditionalOnMissingBean(DataSource.class)
// 判断是否在配置文件中设置属性数据源类型
@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);
if (StringUtils.hasText(properties.getName())) {
dataSource.setPoolName(properties.getName());
}
return dataSource;
}
}
// DBCP数据源的配置
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(org.apache.commons.dbcp2.BasicDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.commons.dbcp2.BasicDataSource",
matchIfMissing = true)
static class Dbcp2 {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.dbcp2")
org.apache.commons.dbcp2.BasicDataSource dataSource(DataSourceProperties properties) {
return createDataSource(properties, org.apache.commons.dbcp2.BasicDataSource.class);
}
//...(省略后面部分代码)...
}
}
结论:我们可以发现当前的 Spring Boot 2.4.5版本默认使用的是com.zaxxer.hikari.HikariDataSource,即Hikari数据源;而之前老版本,如Spring Boot 1.5版本, 默认org.apache.tomcat.jdbc.pool.DataSource,即Tomact数据源
2-3 查看dbcTemplateAutoConfiguration模板自动配置类源码HikariDataSource号称Java Web 当前速度最快的数据源,相比与传统的C3P0、DBCP、Tomcat、JDBC等连接池更加优秀
- 查看JdbcTemplateAutoConfiguration自动配置类源码
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
@ConditionalOnSingleCandidate(DataSource.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties(JdbcProperties.class)
// 导入JDBC模板配置类
@import({ JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class })
// JDBC模板自动配置类
public class JdbcTemplateAutoConfiguration {
}
- 查看JdbcTemplateConfiguration配置类源码
@Configuration(proxyBeanMethods = false)
// 如果JdbcOperations(JDBC操作类)不存在,那么下面的JDBC模板就不生效
@ConditionalOnMissingBean(JdbcOperations.class)
class JdbcTemplateConfiguration {
// 将jdbcTemplate注册到IOC容器中
@Bean
@Primary
// 注入DataSource(数据源)和JdbcProperties(JDBC配置类),SpringBoot已经帮我们搞定了
JdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
JdbcProperties.Template template = properties.getTemplate();
jdbcTemplate.setFetchSize(template.getFetchSize());
jdbcTemplate.setMaxRows(template.getMaxRows());
if (template.getQueryTimeout() != null) {
jdbcTemplate.setQueryTimeout((int) template.getQueryTimeout().getSeconds());
}
return jdbcTemplate;
}
}
6.1.4 使用JDBC进行增删改查
1.使用JDBC实现查询用户
1-1 编写控制器JdbcController
package com.kuang.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
// 使用@RestController注解,实现Controller接口并且返回值为字符串
@RestController
public class JdbcController {
// 使用@Autowired注解,自动装配JdbcTemplate(JDBC模板)类到Spring容器中
@Autowired
JdbcTemplate jdbcTemplate;
// 查询用户列表信息
// 没有实体类,数据库中的内容,怎么获取?可以使用万能的Map
// 真实访问路径:http://localhost:8080/getList
// 使用@RequestMapping注解,设置请求映射路径
@RequestMapping("/getList")
public List
1-2 测试结果
2.使用JDBC实现增加用户
2-1 编写控制器JdbcController
package com.kuang.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
// 使用@RestController注解,实现Controller接口并且返回值为字符串
@RestController
public class JdbcController {
// 使用@Autowired注解,自动装配JdbcTemplate(JDBC模板)类到Spring容器中
@Autowired
JdbcTemplate jdbcTemplate;
// 增加用户信息
// 真实访问路径:http://localhost:8080/addUser
// 使用@RequestMapping注解,设置请求映射路径
@RequestMapping("/addUser")
public String addUser() {
// 封装SQL增加语句到字符串sql中去
String sql = "insert into mybatis.user(id,name,pwd) values(7,'陈奕迅','cyx123456')";
// JDBC模板调用update方法进行插入数据
jdbcTemplate.update(sql);
// 返回一个"Add-OK"
return "Add-OK";
}
}
2-2 测试结果
- 页面输入请求
- 查看数据库表
package com.kuang.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
// 使用@RestController注解,实现Controller接口并且返回值为字符串
@RestController
public class JdbcController {
// 使用@Autowired注解,自动装配JdbcTemplate(JDBC模板)类到Spring容器中
@Autowired
JdbcTemplate jdbcTemplate;
// 修改用户信息
// 真实访问路径:http://localhost:8080/updateUser
// 使用@RequestMapping注解,设置请求映射路径
@RequestMapping("/updateUser/{userId}")
public String updateUser(@PathVariable("userId") int id) {
// 封装SQL修改语句到字符串sql中去
String sql = "update mybatis.user set name=?,pwd=? where id="+id;
// 封装数据
Object[] objects = new Object[2];
objects[0] = "张学友";
objects[1] = "zxy123456";
// JDBC模板调用update方法进行修改数据
jdbcTemplate.update(sql,objects);
// 返回一个"Update-OK"
return "Update-OK";
}
}
3-2 测试结果
- 页面输入请求
- 查看数据库表
package com.kuang.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
// 使用@RestController注解,实现Controller接口并且返回值为字符串
@RestController
public class JdbcController {
// 使用@Autowired注解,自动装配JdbcTemplate(JDBC模板)类到Spring容器中
@Autowired
JdbcTemplate jdbcTemplate;
// 修改用户信息
// 真实访问路径:http://localhost:8080/deleteUser
// 使用@RequestMapping注解,设置请求映射路径
@RequestMapping("/deleteUser/{userId}")
public String delteUser(@PathVariable("userId") int id) {
// 封装SQL修改语句到字符串sql中去
String sql = "delete from mybatis.user where id=?";
// JDBC模板调用update方法进行修改数据
jdbcTemplate.update(sql,id);
// 返回一个"Update-OK"
return "Delete-OK";
}
}
4-2 测试结果
- 页面输入请求
- 查看数据库表
好了,今天的有关 SpringBoot基础学习之整合JDBC框架 的学习就到此结束啦,欢迎小伙伴们积极学习和讨论,喜欢的可以给蜗牛君点个关注,顺便来个一键三连,我们下期见,拜拜啦!
参考视频链接:https://www.bilibili.com/video/BV1PE411i7CV(【狂神说Java】SpringBoot最新教程IDEA版通俗易懂)



