從jdbc到spring-boot-starter-jdbc


從jdbc到spring-boot-starter-jdbc

jdbc 是什么

JDBC是一種用於執行SQL語句的API,可以為多種關系數據庫提供統一訪問,它是由一組用Java語言編寫的類和接口。是Java訪問數據庫的標准規范。

JDBC是Java提供的一種標准規范,具體的實現由各個數據庫廠商去實現。對開發者來說屏蔽了不同數據庫之間的區別,可以使用相同的方式(Java API)去操作不同的數據庫。兩個設備之間要進行通信需要驅動,不同數據庫廠商對JDBC的實現類就是去連接數據庫的驅動。如mysql-connector-java 連接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。

DataSource

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

spring-boot-starter-jdbc

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

數據源相關使用

查看數據源和連接信息

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實現類,獲取一個連接並打印該連接的基本信息

打印結果如下

>>>>>>>>>>>>>>>>>服務啟動執行
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

可以看到

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

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

   <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、Dbcp2、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默認支持的五種數據源,可以通過上面兩種方式(一、排除默認數據源,添加使用的數據源;二、添加使用的數據源,使用配置文件指定使用的數據源) 進行選擇使用數據源。如果是其他開源的數據源呢?比如阿里的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;
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM