SpringBoot - 05. 數據訪問之JDBC(源碼分析+代碼下載)

1、JDBC是什麼?

JDBC API 屬於Java APIJDBC用於如下幾種功能:鏈接到數據庫、執行SQL語句html

2、Spring Boot中如何使用JDBC

2.1 建立 Spring Boot Project 時引入 JDBC API 依賴和 MySQL Driver依賴,以及Spring Web依賴(測試時用到)

能夠在POM中找到引入的JDBC依賴和mysql依賴: JDBC 依賴:java

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

MySql 驅動依賴:mysql

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

2.2 配置數據庫鏈接

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

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.Drivergithub

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.HikariDataSourceweb

數據庫鏈接:HikariProxyConnection@1335157064 wrapping com.mysql.cj.jdbc.ConnectionImpl@7ff8a9dcspring

3、自動配置原理

自動配置文件路徑:org.springframework.boot.autoconfigure.jdbcsql

DataSourceConfiguration用來自動導入數據源(根據各類判斷)shell

/**
	 * 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

4、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的方式來測試

5、配置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、測試

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;
}

7、報錯和解決方案:

7.1 問題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

以下圖所示:

7.2 問題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

關注公衆號:悟空聊架構,回覆pmp,領取pmp資料!回覆悟空,領取架構師資料! 悟空聊架構

關注我,帶你天天進步一點點!

相關文章
相關標籤/搜索