從jdbc到spring-boot-starter-jdbc

從jdbc到spring-boot-starter-jdbchtml

jdbc 是什麼

JDBC是一種用於執行SQL語句的API,能夠爲多種關係數據庫提供統一訪問,它是由一組用Java語言編寫的類和接口。是Java訪問數據庫的標準規範。java

JDBC是Java提供的一種標準規範,具體的實現由各個數據庫廠商去實現。對開發者來講屏蔽了不一樣數據庫之間的區別,可使用相同的方式(Java API)去操做不一樣的數據庫。兩個設備之間要進行通訊須要驅動,不一樣數據庫廠商對JDBC的實現類就是去鏈接數據庫的驅動。如mysql-connector-java 鏈接mysql數據庫的驅動。mysql

使用JDBC鏈接數據庫的步驟

  1. 註冊驅動,這裏的執行 就須要驅動jar包
// mysql 數據庫:「com.mysql.jdbc.Driver」
Class.forName(driver);
  1. 創建數據庫鏈接 Connection
Connection conn=DriverManager.getConnection(url,userName,password);
  1. 建立Statement對象 用來執行SQL語句
Statement statement =conn.createStatement();
  1. 執行SQL語句
ResultSet rs =statement.executeQuery(sql);
  1. 處理結果
  2. 釋放資源

數據庫鏈接池

在使用JDBC進行數據庫操做過程當中,每次使用就要建立鏈接,同時使用完畢還必須得關閉鏈接,操做繁瑣容易出錯,而且Connection的取得和釋放是代價比較高的操做。解決這個問題的方法就是鏈接池。鏈接池就是事先取得必定數量的Connection,程序執行處理的時候不是新建Connection,而是取得預先準備好的Connection。web

DataSource

提供鏈接池機能的技術叫作DataSource。DataSource是JDK提供一個標準接口在javax.sql.DataSource包下。常見的DBCP、C3P0、druid等。spring

spring-boot-starter-jdbc

spring-boot-starter-jdbc主要提供了三個功能,第一個就是對數據源的裝配,第二個就是提供一個JdbcTemplate簡化使用,第三個就是事務sql

數據源相關使用

查看數據源和鏈接信息

package com.lucky.spring;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

@SpringBootApplication
public class Application implements CommandLineRunner {

    Logger logger = LoggerFactory.getLogger(Application.class);

    @Autowired
    DataSource dataSource;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);

    }


    @Override
    public void run(String... args) throws Exception {
        System.out.println(">>>>>>>>>>>>>>>>>服務啓動執行");
        showConnection();
    }

    private void showConnection() throws SQLException {
        logger.info("dataSource:{}", dataSource.getClass().getName());
        Connection connection = dataSource.getConnection();
        logger.info("connection:{}", connection.toString());
    }
}

代碼邏輯以下:數據庫

  1. 經過CommandLineRunner監聽服務的啓動,在啓動後調用showConnection方法
  2. 在showConnection方法裏打印出當前的DataSource實現類,獲取一個鏈接並打印該鏈接的基本信息

打印結果以下apache

>>>>>>>>>>>>>>>>>服務啓動執行
2020-07-12 07:38:42.076  INFO 8144 --- [           main] com.lucky.spring.Application             : dataSource:com.zaxxer.hikari.HikariDataSource
2020-07-12 07:38:42.077  INFO 8144 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-07-12 07:38:42.274  INFO 8144 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2020-07-12 07:38:42.277  INFO 8144 --- [           main] com.lucky.spring.Application             : connection:HikariProxyConnection@1366499339 wrapping com.mysql.jdbc.JDBC4Connection@25c5e994

能夠看到api

  1. dataSource使用的是:com.zaxxer.hikari.HikariDataSource
  2. connection信息是:HikariProxyConnection@1366499339 wrapping com.mysql.jdbc.JDBC4Connection@25c5e994

當前的pom文件中僅僅配置了spring-boot-starter-jdbc和mysql數據庫驅動tomcat

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

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

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

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

Springboot支持的數據源

DataSourceAutoConfiguration類的內部類PooledDataSourceConfiguration標識了默認支持的數據源。

@Configuration
    @Conditional({DataSourceAutoConfiguration.PooledDataSourceCondition.class})
    @ConditionalOnMissingBean({DataSource.class, XADataSource.class})
    @Import({Hikari.class, Tomcat.class, Dbcp2.class, Generic.class, DataSourceJmxConfiguration.class})
    protected static class PooledDataSourceConfiguration {
        protected PooledDataSourceConfiguration() {
        }
    }

默認支持Hikari、Tomcat、Dbcp二、Generic、DataSourceJmxConfiguration這五種數據源。從上面的數據源和鏈接信息的打印能夠知道默認狀況下Springboot裝配的是Hikari數據源。

自動裝配Tomcat數據源

DataSourceConfigurationTomcat數據源的實現以下

@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 {
        Tomcat() {
        }

        @Bean
        @ConfigurationProperties(
            prefix = "spring.datasource.tomcat"
        )
        public org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties) {
            org.apache.tomcat.jdbc.pool.DataSource dataSource = (org.apache.tomcat.jdbc.pool.DataSource)DataSourceConfiguration.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;
        }
    }

讓Springboot自動裝配選擇Tomcat的方式有兩種

第一種

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--默認配置 start-->
        <!--<dependency>-->
        <!--<groupId>org.springframework.boot</groupId>-->
        <!--<artifactId>spring-boot-starter-jdbc</artifactId>-->
        <!--</dependency>-->
        <!--默認配置 end-->

        <!--使用tomcat數據源 方式 start-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.zaxxer</groupId>
                    <artifactId>HikariCP</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jdbc</artifactId>
        </dependency>
        <!--使用tomcat數據源 方式 end-->

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

    </dependencies>
  1. 加入tomcat-jdbc數據源的依賴
  2. 排除Hikari的數據源依賴

打印信息以下:

>>>>>>>>>>>>>>>>>服務啓動執行
2020-07-12 08:11:49.761  INFO 8469 --- [           main] com.lucky.spring.Application             : dataSource:org.apache.tomcat.jdbc.pool.DataSource
2020-07-12 08:11:50.058  INFO 8469 --- [           main] com.lucky.spring.Application             : connection:ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@4d6f197e]]

第二種

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--默認配置 start-->
        <!--<dependency>-->
        <!--<groupId>org.springframework.boot</groupId>-->
        <!--<artifactId>spring-boot-starter-jdbc</artifactId>-->
        <!--</dependency>-->
        <!--默認配置 end-->

        <!--使用tomcat數據源 方式 start-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jdbc</artifactId>
        </dependency>
        <!--使用tomcat數據源 方式 end-->

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

    </dependencies>
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/readinglist?characterEncoding=utf8&useSSL=false
    username: root
    password: 12345678
    type: org.apache.tomcat.jdbc.pool.DataSource
  1. pom文件中添加tomcat-jdbc依賴
  2. 在application.yml文件中指定數據源爲org.apache.tomcat.jdbc.pool.DataSource

打印結果以下:

>>>>>>>>>>>>>>>>>服務啓動執行
2020-07-12 08:15:51.746  INFO 8525 --- [           main] com.lucky.spring.Application             : dataSource:org.apache.tomcat.jdbc.pool.DataSource
2020-07-12 08:15:52.152  INFO 8525 --- [           main] com.lucky.spring.Application             : connection:ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@5173200b]]

使用druid數據源

對於Springboot默認支持的五種數據源,能夠經過上面兩種方式(1、排除默認數據源,添加使用的數據源;2、添加使用的數據源,使用配置文件指定使用的數據源) 進行選擇使用數據源。若是是其餘開源的數據源呢?好比阿里的druid數據源。也是有兩種方式。

第一種

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.23</version>
   </dependency>
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/readinglist?characterEncoding=utf8&useSSL=false
    username: root
    password: 12345678
    type: com.alibaba.druid.pool.DruidDataSource
  1. 添加druid數據源依賴
  2. 在application.yml文件中指定數據源爲com.alibaba.druid.pool.DruidDataSource

打印結果以下:

>>>>>>>>>>>>>>>>>服務啓動執行
2020-07-12 08:27:52.523  INFO 8813 --- [           main] com.lucky.spring.Application             : dataSource:com.alibaba.druid.pool.DruidDataSource
2020-07-12 08:27:52.562  INFO 8813 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
2020-07-12 08:27:52.883  INFO 8813 --- [           main] com.lucky.spring.Application             : connection:com.mysql.jdbc.JDBC4Connection@3b0ca5e1

第二種

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.23</version>
   </dependency>
package com.lucky.spring;

import com.alibaba.druid.pool.DruidDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

@SpringBootApplication
public class Application implements CommandLineRunner {

    Logger logger = LoggerFactory.getLogger(Application.class);

    @Autowired
    DataSource dataSource;



    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);

    }


    @Override
    public void run(String... args) throws Exception {
        System.out.println(">>>>>>>>>>>>>>>>>服務啓動執行");
        showConnection();
    }

    @Bean
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/readinglist?characterEncoding=utf8&useSSL=false");
        dataSource.setUsername("root");
        dataSource.setPassword("12345678");
        return dataSource;
    }


    private void showConnection() throws SQLException {
        logger.info("dataSource:{}", dataSource.getClass().getName());
        Connection connection = dataSource.getConnection();
        logger.info("connection:{}", connection.toString());
    }

}
  1. 添加druid依賴
  2. 建立DataSource的bean,進行相關配置後,返回DruidDataSource

打印結果以下:

>>>>>>>>>>>>>>>>>服務啓動執行
2020-07-12 08:37:09.898  INFO 9140 --- [           main] com.lucky.spring.Application             : dataSource:com.alibaba.druid.pool.DruidDataSource
2020-07-12 08:37:09.951  INFO 9140 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
2020-07-12 08:37:10.314  INFO 9140 --- [           main] com.lucky.spring.Application             : connection:com.mysql.jdbc.JDBC4Connection@d400943

JdbcTemplate相關使用

JdbcTemplate 是什麼

Spring對數據庫的操做在jdbc上面作了深層次的封裝。使用Spring的注入功能,能夠把DataSource註冊到JdbcTemplate之中。

JdbcTemplate主要提供了一下五類方法

  1. execute方法:能夠用於執行任何SQL語句,通常用於執行DDL語句
  2. update方法及batchUpdate方法:update方法用於執行新增、修改、刪除等語句;batchUpdate方法用於執行批處理相關語句
  3. query方法及queryForXXX方法:用於執行查詢相關語句
  4. call方法:用於執行存儲過程、函數相關語句

事務的相關使用

Springboot中在須要使用事務的方法上面添加@Transactional,須要注意的是,默認只會對運行時異常進行事務回滾,非運行時異常不會回滾事務。

Controller層定義了兩個接口

@RestController
public class DataOperationController {


    @Autowired
    DataOpeService service;

    @GetMapping("/api/queryData")
    public String queryData() {
        return service.queryData();
    }

    @PostMapping("/api/addData")
    public String addData() {
        try {
            service.addData();
            return "add data success";
        } catch (Exception e) {
            e.printStackTrace();
            return "add data fail";
        }
    }
}

Service經過JdbcTemplate執行sql

@Service
public class DataOpeServiceImpl implements DataOpeService {

    private Logger logger = LoggerFactory.getLogger(DataOpeServiceImpl.class);

    @Autowired
    private JdbcTemplate template;


    @Override
    public String queryData() {
        String sql = "select * from t where id=1";
        RowMapper<T> data = new BeanPropertyRowMapper<>(T.class);
        T t = template.queryForObject(sql, data);
        return t.toString();
    }

    @Override
    public String addData() {
        List<T> data = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            T item = new T();
            item.setA(i);
            item.setB(i);
            data.add(item);
        }
        for (int i = 0; i < data.size(); i++) {
            String sql = "insert into t(a,b) values (" + data.get(i).getA() + "," + data.get(i).getB() + ")";
            logger.info("sql:{}", sql);
            template.execute(sql);
        }
        return null;
    }
}

運行時異常

修改代碼,人爲在添加第二條記錄時拋出異常。

@Transactional
    @Override
    public String addData() {
        List<T> data = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            T item = new T();
            item.setA(i);
            item.setB(i);
            data.add(item);
        }
        for (int i = 0; i < data.size(); i++) {
            String sql = "insert into t(a,b) values (" + data.get(i).getA() + "," + data.get(i).getB() + ")";
            logger.info("sql:{}", sql);
            if (data.get(i).getA() == 1) {
                throw new NullPointerException("人爲拋出運行時異常異常");
            }
            template.execute(sql);
        }
        return null;
    }

調用接口,發現事務生效,即發生運行時異常進行了代碼回滾。

非運行時異常

從新修改代碼,人爲拋出非運行時異常。

@Transactional
    @Override
    public String addData() throws Exception{
        List<T> data = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            T item = new T();
            item.setA(i);
            item.setB(i);
            data.add(item);
        }
        for (int i = 0; i < data.size(); i++) {
            String sql = "insert into t(a,b) values (" + data.get(i).getA() + "," + data.get(i).getB() + ")";
            logger.info("sql:{}", sql);
            if (data.get(i).getA() == 1) {
//                throw new NullPointerException("人爲拋出運行時異常異常");
                throw new FileNotFoundException("人爲拋出非運行時異常");
            }
            template.execute(sql);
        }
        return null;
    }

調用接口,發現事務沒有生效,即第一條數據插入到了數據庫裏。

指定回滾時的異常

若是要使得非運行時期異常也回滾,那麼在使用@Transactional註解時,指定rollbackFor屬性。

@Transactional(rollbackFor = Exception.class)
    @Override
    public String addData() throws Exception{
        List<T> data = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            T item = new T();
            item.setA(i);
            item.setB(i);
            data.add(item);
        }
        for (int i = 0; i < data.size(); i++) {
            String sql = "insert into t(a,b) values (" + data.get(i).getA() + "," + data.get(i).getB() + ")";
            logger.info("sql:{}", sql);
            if (data.get(i).getA() == 1) {
//                throw new NullPointerException("人爲拋出運行時異常異常");
                throw new FileNotFoundException("人爲拋出非運行時異常");
            }
            template.execute(sql);
        }
        return null;
    }
相關文章
相關標籤/搜索