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

<span class="hljs-type">Logger</span> <span class="hljs-variable">logger</span> <span class="hljs-operator">=</span> LoggerFactory.getLogger(Application.class);

<span class="hljs-meta">@Autowired</span>
DataSource dataSource;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {
    SpringApplication.run(Application.class, args);

}


<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">(String... args)</span> <span class="hljs-keyword">throws</span> Exception {
    System.out.println(<span class="hljs-string">"&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;服务启动执行"</span>);
    showConnection();
}

<span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">showConnection</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> SQLException {
    logger.info(<span class="hljs-string">"dataSource:{}"</span>, dataSource.getClass().getName());
    <span class="hljs-type">Connection</span> <span class="hljs-variable">connection</span> <span class="hljs-operator">=</span> dataSource.getConnection();
    logger.info(<span class="hljs-string">"connection:{}"</span>, 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>
    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-jdbc<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>mysql<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mysql-connector-java<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>runtime<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    
<span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>

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() {
        }
    <span class="hljs-meta">@Bean</span>
    <span class="hljs-meta">@ConfigurationProperties(
        prefix = "spring.datasource.tomcat"
    )</span>
    <span class="hljs-keyword">public</span> org.apache.tomcat.jdbc.pool.DataSource <span class="hljs-title function_">dataSource</span><span class="hljs-params">(DataSourceProperties properties)</span> {
        org.apache.tomcat.jdbc.pool.<span class="hljs-type">DataSource</span> <span class="hljs-variable">dataSource</span> <span class="hljs-operator">=</span> (org.apache.tomcat.jdbc.pool.DataSource)DataSourceConfiguration.createDataSource(properties, org.apache.tomcat.jdbc.pool.DataSource.class);
        <span class="hljs-type">DatabaseDriver</span> <span class="hljs-variable">databaseDriver</span> <span class="hljs-operator">=</span> DatabaseDriver.fromJdbcUrl(properties.determineUrl());
        <span class="hljs-type">String</span> <span class="hljs-variable">validationQuery</span> <span class="hljs-operator">=</span> databaseDriver.getValidationQuery();
        <span class="hljs-keyword">if</span> (validationQuery != <span class="hljs-literal">null</span>) {
            dataSource.setTestOnBorrow(<span class="hljs-literal">true</span>);
            dataSource.setValidationQuery(validationQuery);
        }

        <span class="hljs-keyword">return</span> 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>
    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    <span class="hljs-comment">&lt;!--默认配置 start--&gt;</span>
    <span class="hljs-comment">&lt;!--&lt;dependency&gt;--&gt;</span>
    <span class="hljs-comment">&lt;!--&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;--&gt;</span>
    <span class="hljs-comment">&lt;!--&lt;artifactId&gt;spring-boot-starter-jdbc&lt;/artifactId&gt;--&gt;</span>
    <span class="hljs-comment">&lt;!--&lt;/dependency&gt;--&gt;</span>
    <span class="hljs-comment">&lt;!--默认配置 end--&gt;</span>

    <span class="hljs-comment">&lt;!--使用tomcat数据源 方式 start--&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-jdbc<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">exclusions</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">exclusion</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.zaxxer<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>HikariCP<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">exclusion</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">exclusions</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.tomcat<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>tomcat-jdbc<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    <span class="hljs-comment">&lt;!--使用tomcat数据源 方式 end--&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>mysql<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mysql-connector-java<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>runtime<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>

  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>
    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    <span class="hljs-comment">&lt;!--默认配置 start--&gt;</span>
    <span class="hljs-comment">&lt;!--&lt;dependency&gt;--&gt;</span>
    <span class="hljs-comment">&lt;!--&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;--&gt;</span>
    <span class="hljs-comment">&lt;!--&lt;artifactId&gt;spring-boot-starter-jdbc&lt;/artifactId&gt;--&gt;</span>
    <span class="hljs-comment">&lt;!--&lt;/dependency&gt;--&gt;</span>
    <span class="hljs-comment">&lt;!--默认配置 end--&gt;</span>

    <span class="hljs-comment">&lt;!--使用tomcat数据源 方式 start--&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-jdbc<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.tomcat<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>tomcat-jdbc<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    <span class="hljs-comment">&lt;!--使用tomcat数据源 方式 end--&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>mysql<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mysql-connector-java<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>runtime<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>

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 {

<span class="hljs-type">Logger</span> <span class="hljs-variable">logger</span> <span class="hljs-operator">=</span> LoggerFactory.getLogger(Application.class);

<span class="hljs-meta">@Autowired</span>
DataSource dataSource;



<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {
    SpringApplication.run(Application.class, args);

}


<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">(String... args)</span> <span class="hljs-keyword">throws</span> Exception {
    System.out.println(<span class="hljs-string">"&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;服务启动执行"</span>);
    showConnection();
}

<span class="hljs-meta">@Bean</span>
<span class="hljs-keyword">public</span> DataSource <span class="hljs-title function_">dataSource</span><span class="hljs-params">()</span> {
    <span class="hljs-type">DruidDataSource</span> <span class="hljs-variable">dataSource</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">DruidDataSource</span>();
    dataSource.setUrl(<span class="hljs-string">"jdbc:mysql://localhost:3306/readinglist?characterEncoding=utf8&amp;useSSL=false"</span>);
    dataSource.setUsername(<span class="hljs-string">"root"</span>);
    dataSource.setPassword(<span class="hljs-string">"12345678"</span>);
    <span class="hljs-keyword">return</span> dataSource;
}


<span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">showConnection</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> SQLException {
    logger.info(<span class="hljs-string">"dataSource:{}"</span>, dataSource.getClass().getName());
    <span class="hljs-type">Connection</span> <span class="hljs-variable">connection</span> <span class="hljs-operator">=</span> dataSource.getConnection();
    logger.info(<span class="hljs-string">"connection:{}"</span>, 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 {
<span class="hljs-meta">@Autowired</span>
DataOpeService service;

<span class="hljs-meta">@GetMapping("/api/queryData")</span>
<span class="hljs-keyword">public</span> String <span class="hljs-title function_">queryData</span><span class="hljs-params">()</span> {
    <span class="hljs-keyword">return</span> service.queryData();
}

<span class="hljs-meta">@PostMapping("/api/addData")</span>
<span class="hljs-keyword">public</span> String <span class="hljs-title function_">addData</span><span class="hljs-params">()</span> {
    <span class="hljs-keyword">try</span> {
        service.addData();
        <span class="hljs-keyword">return</span> <span class="hljs-string">"add data success"</span>;
    } <span class="hljs-keyword">catch</span> (Exception e) {
        e.printStackTrace();
        <span class="hljs-keyword">return</span> <span class="hljs-string">"add data fail"</span>;
    }
}

}

Service 通过 JdbcTemplate 执行 sql

@Service
public class DataOpeServiceImpl implements DataOpeService {
<span class="hljs-keyword">private</span> <span class="hljs-type">Logger</span> <span class="hljs-variable">logger</span> <span class="hljs-operator">=</span> LoggerFactory.getLogger(DataOpeServiceImpl.class);

<span class="hljs-meta">@Autowired</span>
<span class="hljs-keyword">private</span> JdbcTemplate template;


<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> String <span class="hljs-title function_">queryData</span><span class="hljs-params">()</span> {
    <span class="hljs-type">String</span> <span class="hljs-variable">sql</span> <span class="hljs-operator">=</span> <span class="hljs-string">"select * from t where id=1"</span>;
    RowMapper&lt;T&gt; data = <span class="hljs-keyword">new</span> <span class="hljs-title class_">BeanPropertyRowMapper</span>&lt;&gt;(T.class);
    <span class="hljs-type">T</span> <span class="hljs-variable">t</span> <span class="hljs-operator">=</span> template.queryForObject(sql, data);
    <span class="hljs-keyword">return</span> t.toString();
}

<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> String <span class="hljs-title function_">addData</span><span class="hljs-params">()</span> {
    List&lt;T&gt; data = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span>&lt;&gt;();
    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">2</span>; i++) {
        <span class="hljs-type">T</span> <span class="hljs-variable">item</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">T</span>();
        item.setA(i);
        item.setB(i);
        data.add(item);
    }
    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; data.size(); i++) {
        <span class="hljs-type">String</span> <span class="hljs-variable">sql</span> <span class="hljs-operator">=</span> <span class="hljs-string">"insert into t(a,b) values ("</span> + data.get(i).getA() + <span class="hljs-string">","</span> + data.get(i).getB() + <span class="hljs-string">")"</span>;
        logger.info(<span class="hljs-string">"sql:{}"</span>, sql);
        template.execute(sql);
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
}

}

运行时异常

修改代码,人为在添加第二条记录时抛出异常。

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