深入浅出 Spring Boot - 数据访问之JDBC

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介: 深入浅出 Spring Boot - 数据访问之JDBC

代码下载:https://github.com/Jackson0714/study-spring-boot.git

学而不思则罔

一、JDBC是什么?

JDBC API 属于Java APIJDBC用于以下几种功能:连接到数据库、执行SQL语句

二、Spring Boot中如何使用JDBC

2.1 创建 Spring Boot Project 时引入 JDBC API 依赖和 MySQL Driver依赖

mark
可以在POM中找到引入的JDBC依赖和mysql依赖:
JDBC 依赖:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

MySql 驱动依赖:

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
</dependency>

2.2 配置数据库连接

新增配置文件:src/main/resources/application.yml

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/study-spring-boot?serverTimezone=UTC&useUnicode=true&zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=utf-8
    driverClassName: com.mysql.cj.jdbc.Driver

注意:com.mysq.jdbc.Driver 被废弃了,需要使用com.mysql.cj.jdbc.Driver

2.3 查看使用的数据源和数据库连接

package com.jackson0714.springboot;

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 Springboot05DataJdbcApplicationTests {

    @Autowired
    DataSource dataSource; //自动配置数据源,使用yml配置

    @Test
    void contextLoads() throws SQLException {
        System.out.println("数据源:" + dataSource.getClass());

        Connection connection = dataSource.getConnection();
        System.out.println("数据库连接:" + connection);
        connection.close();
    }

}

默认数据源:class com.zaxxer.hikari.HikariDataSource

数据库连接:HikariProxyConnection@1335157064 wrapping com.mysql.cj.jdbc.ConnectionImpl@7ff8a9dc

三、自动配置原理

自动配置文件路径:org.springframework.boot.autoconfigure.jdbc

DataSourceConfiguration用来自动导入数据源(根据各种判断)

/**
     * Tomcat Pool DataSource configuration.
     */
    @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")

3.1 自动选择数据源

如果导入了org.apache.tomcat.jdbc.pool.DataSource数据源,并且配置的spring.datasource.type配置的是org.apache.tomcat.jdbc.pool.DataSource,如果没配置type则使用tomcat数据源

3.2 HikariDataSource数据源也类似这样判断。

3.3 默认使用tomcat数据源

3.4 默认支持以下数据源

org.apache.tomcat.jdbc.pool、HikariDataSource、org.apache.commons.dbcp2

3.5 支持自定义数据源

使用DataSourceBuilder创建数据源,利用反射创建响应type的数据源,并且绑定相关属性

    /**
     * Generic DataSource configuration.
     */
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingBean(DataSource.class)
    @ConditionalOnProperty(name = "spring.datasource.type")
    static class Generic {

        @Bean
        DataSource dataSource(DataSourceProperties properties) {
          //使用DataSourceBuilder创建数据源,利用反射创建响应type的数据源,并且绑定相关属性
            return properties.initializeDataSourceBuilder().build();
        }

    }

3.6 DataSourceInitializerInvoker 运行脚本

/**
 * Bean to handle {@link DataSource} initialization by running {@literal schema-*.sql} on
 * {@link InitializingBean#afterPropertiesSet()} and {@literal data-*.sql} SQL scripts on
 * a {@link DataSourceSchemaCreatedEvent}.
 *
 * @author Stephane Nicoll
 * @see DataSourceAutoConfiguration
 */
class DataSourceInitializerInvoker implements ApplicationListener<DataSourceSchemaCreatedEvent>, InitializingBean {
createSchema() 创建表 (文件名规则 schema-*.sql)
initSchema() 执行数据脚本 (文件名规则 data-*.sql)

getScripts() 来获取需要执行的脚本

private List<Resource> getScripts(String propertyName, List<String> resources, String fallback) {
  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);
}

1) fallback = "schema", platform="all",会自动执行根目录下:schema-all.sql 或schema.sql 文件

2) fallback = "data", platform="all",会自动执行根目录下:data-all.sql 或data.sql 文件

isEnabled() 方法判断是否开启了自动执行脚本

有三种模式:NEVER,EMBEDDED(默认),Always

疑问:用EMBEDDED模式返回false,开关关闭,不执行脚本,这是为啥呢?

用Always模式则每次启动spring boot重复执行脚本(创建表脚本都是先判断有没有表,有则删除后重建)

private boolean isEnabled() {
  DataSourceInitializationMode mode = this.properties.getInitializationMode();
  if (mode == DataSourceInitializationMode.NEVER) {
    return false;
  }
  if (mode == DataSourceInitializationMode.EMBEDDED && !isEmbedded()) {
    return false;
  }
  return true;
}

3.7 通过配置文件指定需要执行脚本

schema:
  - classpath:department.sql

创建出的 department

四、JdbcTemplate

JdbcTemplateAutoConfiguration.java 文件 自动注入了JdbcTemplate。(JdbcTemplate用来操作数据库)

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
@ConditionalOnSingleCandidate(DataSource.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties(JdbcProperties.class)
@Import({ JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class })
public class JdbcTemplateAutoConfiguration {

}

我们用Swagger的方式来测试

五、配置Swagger用来测试

5.1 pom.xml文件 添加swagger依赖

<!-- swagger -->
<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-swagger2</artifactId>
  <version>2.9.2</version>
</dependency>
<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-swagger-ui</artifactId>
  <version>2.9.2</version>
</dependency>

5.2 添加SwaggerConfig.java文件

package com.jackson0714.springboot.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket createRestApi(){
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any()).build();
    }

    private ApiInfo apiInfo(){
        return new ApiInfoBuilder()
                .title("玩转Spring Boot 接口文档")
                .description("This is a restful api document of Spring Boot.")
                .version("1.0")
                .build();
    }

}

5.3 访问Swagger文档

http://localhost:8081/swagger-ui.html

六、测试

6.1 新增部门

@ApiOperation(value = "1.新增部门")
@ApiImplicitParams({
  @ApiImplicitParam(name = "name", value = "部门名称")
})
@PostMapping("/create")
public int createDepartment(@RequestParam String name) {
  String sql = String.format("insert into department(departmentName) value('%s')", name);
  int result = jdbcTemplate.update(sql);
  return result;
}

表记录

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vSXwzVym-1652094311265)(..\images\spring-boot-05-data-jdbc\create_table.png)]

6.2 查询所有部门

@ApiOperation(value = "2.查询所有部门")
@GetMapping("/getAllDepartment")
public List<Map<String, Object>> getAllDepartment() {
  List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from department");
  return list;
}

6.3 根据id查询某个部门

@ApiOperation(value = "3.根据id查询某个部门")
@ApiImplicitParams({
  @ApiImplicitParam(name = "id", value = "需要查询的部门id")
})
@GetMapping("/{id}")
public Map<String, Object> getDepartmentById(@PathVariable Long id) {
  String sql = "select * from department where id = " + id;
  List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
  return list.get(0);
}

6.4 根据id更新部门名称

@ApiOperation(value = "根据id更新部门名称")
@ApiImplicitParams({
  @ApiImplicitParam(name = "id", value = "需要更新的部门id"),
  @ApiImplicitParam(name = "name", value = "需要更新的部门名称")
})
@PostMapping("/update")
public int updateDepartmentById(@RequestParam Long id, @RequestParam String name) {
  String sql = String.format("update department set departmentName = '%s' where id = %d", name, id);
  int result = jdbcTemplate.update(sql);
  return result;
}

6.5 根据id删除部门

@ApiOperation(value = "根据id删除部门")
@ApiImplicitParams({
  @ApiImplicitParam(name = "id", value = "需要删除的部门id")
})
@PostMapping("/delete")
public int deleteDepartment(@RequestParam Long id) {
  String sql = String.format("delete from department where id = %d", id);
  int result = jdbcTemplate.update(sql);
  return result;
}

报错:

问题1

java.sql.SQLException:null, message from server: "Host 'Siri' is not allowed to connect to this MySQL server"

解决方案:

执行命令:

use mysql;
select host from user;
update user set host = '%' where user = 'root'

执行结果:

Query OK, 1 row affected

如下图所示:

问题2

Caused by: com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the 'serverTimezone' configuration property) to use a more specifc time zone value if you want to utilize time zone support.

解决方案:

配置spring.datasource.url 时,增加参数:serverTimezone=UTC

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
15天前
|
缓存 Java Sentinel
Springboot 中使用 Redisson+AOP+自定义注解 实现访问限流与黑名单拦截
Springboot 中使用 Redisson+AOP+自定义注解 实现访问限流与黑名单拦截
|
22小时前
|
SQL Java 数据库
springboot用户创建的业务数据只能是同一组织能看的见
springboot用户创建的业务数据只能是同一组织能看的见
|
2天前
|
NoSQL Java Redis
springboot之RedisTemplate的访问单机,哨兵,集群模式
以上是配置RedisTemplate以连接到单机、哨兵和集群模式的示例。在实际应用中,还可以根据需求配置连接池、序列化方式、超时等其他参数。
20 0
|
4天前
|
Java Spring
spring boot访问接口报500
spring boot访问接口报500
11 2
|
9天前
|
JSON JavaScript Java
从前端Vue到后端Spring Boot:接收JSON数据的正确姿势
从前端Vue到后端Spring Boot:接收JSON数据的正确姿势
21 0
|
10天前
|
前端开发 JavaScript Java
SpringBoot解决跨域访问的问题
本文介绍了跨域访问的概念及其解决方案。同源策略规定浏览器限制不符合协议、Host和端口的请求,导致跨域访问被禁止。为解决此问题,文中提出了三种策略:1) 前端利用HTML标签的特性(如script、iframe)和JSONP、postMessage规避同源策略;2) 通过代理,如nginx或nodejs中间件,使得所有请求看似来自同一源;3) CORS(跨域资源共享),通过设置HTTP响应头允许特定跨域请求。在SpringBoot中,实现CORS有四种方式,包括使用CorsFilter、重写WebMvcConfigurer、CrossOrigin注解以及直接设置响应头。
|
10天前
|
SQL Java 数据库连接
Springboot框架整合Spring JDBC操作数据
JDBC是Java数据库连接API,用于执行SQL并访问多种关系数据库。它包括一系列Java类和接口,用于建立数据库连接、创建数据库操作对象、定义SQL语句、执行操作并处理结果集。直接使用JDBC涉及七个步骤,包括加载驱动、建立连接、创建对象、定义SQL、执行操作、处理结果和关闭资源。Spring Boot的`spring-boot-starter-jdbc`简化了这些步骤,提供了一个在Spring生态中更便捷使用JDBC的封装。集成Spring JDBC需要添加相关依赖,配置数据库连接信息,并通过JdbcTemplate进行数据库操作,如插入、更新、删除和查询。
|
10天前
|
SQL Java 数据库连接
Springboot框架整合Spring Data JPA操作数据
Spring Data JPA是Spring基于ORM和JPA规范封装的框架,简化了数据库操作,提供增删改查等接口,并可通过方法名自动生成查询。集成到Spring Boot需添加相关依赖并配置数据库连接和JPA设置。基础用法包括定义实体类和Repository接口,通过Repository接口可直接进行数据操作。此外,JPA支持关键字查询,如通过`findByAuthor`自动转换为SQL的`WHERE author=?`查询。
|
16天前
|
Java 数据库连接 数据库
【SpringBoot系列】微服务数据持久化方案
【4月更文挑战第8天】微服务数据持久化方案Spring Boot JPA + Hiberate
|
22天前
|
安全 Java 应用服务中间件
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
29 0
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置