SpringBoot - 05. 数据访问之JDBC(源码分析+代码下载)

10分钟进阶SpringBoot - 05. 数据访问之JDBC

github代码下载

一、JDBC是什么?

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

二、Spring Boot中如何使用JDBC

2.1 创建 Spring Boot Project 时引入 JDBC API 依赖和 MySQL Driver依赖,以及Spring Web依赖(测试时用到)


可以在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

数据库连接:[email protected] wrapping [email protected]

三、自动配置原理

自动配置文件路径: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;
}


表记录

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

原文地址:https://www.cnblogs.com/jackson0714/p/spring-boot-05-jdbc.html

时间: 2024-10-29 04:44:02

SpringBoot - 05. 数据访问之JDBC(源码分析+代码下载)的相关文章

mysql jdbc源码分析片段 和 Tomcat&#39;s JDBC Pool

32) Tomcat's JDBC Pool Tomcat jdbc pool的使用仅需2个jar包,分别为tomcat-jdbc.jar和tomcat-juli.jar,这两个jar包都可以在tomcat7中找到,tomcat-jdbc.jar在tomcat的lib目录下,tomcat-juli.jar在bin目录下. http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/ org.apache.tomcat.jdbc.pool

IOS开发数据存储篇--FMDB源码分析3(FMDatabaseQueue+FMDatabasePool)

一.前言 如上一章所讲,FMDB源码主要有以下几个文件组成: FMResultSet : 表示FMDatabase执行查询之后的结果集. FMDatabase : 表示一个单独的SQLite数据库操作实例,通过它可以对数据库进行增删改查等等操作. FMDatabaseAdditions : 扩展FMDatabase类,新增对查询结果只返回单个值的方法进行简化,对表.列是否存在,版本号,校验SQL等等功能. FMDatabaseQueue : 使用串行队列 ,对多线程的操作进行了支持. FMDat

zepto源码分析-代码结构【转载】

本来想学习一下jQuery的源码,但由于jQuery的源码有10000多行,设计相当复杂,所以决定从zepto开始,分析一个成熟的框架的代码结构及执行步骤. 网上也有很多zepto的源码分析,有的给源码添加注释,有的谈与jQuery的不同,但是都没有系统的讲解zepto框架的代码结构及初始化Zepto对象的过程. 准备 默认你已经对面向对象有一定的了解,本文是边实践边写的,虽有些乱,但好处是为大家提供了分析的思路. 英文文档. 中文文档 注意在文中$变量表示一个函数对象,而$()表示执行函数,他

雄踞AppStore榜首的游戏&amp;lt;别踩到白块儿&amp;gt;源码分析和下载(一)

AppStore和Android市场情况 莫名其妙爆红的游戏 真的莫名其妙,笔者下这个游戏两次.第一次在豌豆荚排行榜看到这款游戏,名字怪怪的,下载下来尝试一下,没认为有什么新颖的,还在思虑这是不是刷榜刷上去的.果断卸载了.周末的时候逛逛app store.突然看到排行榜首位是Dont Tap The White Tile(后更名panio tiles ),翻译一下不就是别踩到白块儿.笔者震惊了.太莫名其妙了,这东西是真的火,不是刷榜刷出来的!游戏玩家们心理真的难以捉摸,又捧红了一款游戏: 最近爆

MapReduce(十六): 写数据到HDFS的源码分析

1)   LineRecordWriter负责把Key,Value的形式把数据写入到DFSOutputStream 2)   DFSOutputStream负责把LineRecordWriter写入的数据发送到Datanode中.对LineRecordWriter写入的数据首先按照一个个chunk分割,然后打包成Packet发送给datanode,datanode负责对接收的数据备份到其它datanode上.发送数据时,发送线程和结果接收线程分开,对发送队列的数据完成发送后,转移的结果接收队列中

Tomcat源码分析

前言: 本文是我阅读了TOMCAT源码后的一些心得. 主要是讲解TOMCAT的系统框架, 以及启动流程.若有错漏之处,敬请批评指教! 建议: 毕竟TOMCAT的框架还是比较复杂的, 单是从文字上理解, 是不那么容易掌握TOMCAT的框架的. 所以得实践.实践.再实践. 建议下载一份TOMCAT的源码, 调试通过, 然后单步跟踪其启动过程. 如果有不明白的地方, 再来查阅本文, 看是否能得到帮助. 我相信这样效果以及学习速度都会好很多! 1. Tomcat的整体框架结构 Tomcat的基本框架,

jquery2源码分析系列目录

学习jquery的源码对于提高前端的能力很有帮助,下面的系列是我在网上看到的对jquery2的源码的分析.等有时间了好好研究下.我们知道jquery2开始就不支持IE6-8了,从jquery2的源码中可以学到很多w3c新的标准( 如html5,css3,ECMAScript).原文地址是:http://www.cnblogs.com/aaronjs/p/3279314.html 关于1.x.x版的jquery源码分析系列,本博客也转载了一个地址http://www.cnblogs.com/jav

[转]jQuery源码分析系列

文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaron/jQuery 正在编写的书 - jQuery架构设计与实现 本人在慕课网的教程(完结) jQuery源码解析(架构与依赖模块) 64课时 jQuery源码解析(DOM与核心模块)64课时 jQuery源码分析目录(完结) jQuery源码分析系列(01) : 整体架构 jQuery源码分析系列(

NHibernate源码分析

NHibernate源码分析之开篇: 计划和安排 只从使用NHibernate以来,请被其强大的功能和使用的简洁所吸引. 为了进一步研究NHibernate,决定分析其源代码,如有感兴趣者,欢迎一起研究. 这里列出了将要分析的部分: 1.        NHibernate配置和持久对象映射文件 2.        NHibernate架构分析(uml图) 3.        NHibernate源码分析之一: 配置信息 4.        NHibernate源码分析之一(续): 对象映射 5