从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 连接数据库的步骤
- 注册驱动,这里的执行 就需要驱动 jar 包
// mysql 数据库:“com.mysql.jdbc.Driver”
Class.forName(driver);
- 建立数据库连接 Connection
Connection conn=DriverManager.getConnection(url,userName,password);
- 创建 Statement 对象 用来执行 SQL 语句
Statement statement =conn.createStatement();
- 执行 SQL 语句
ResultSet rs =statement.executeQuery(sql);
- 处理结果
- 释放资源
数据库连接池
在使用 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">">>>>>>>>>>>>>>>>>服务启动执行"</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());
}
}
代码逻辑如下:
- 通过 CommandLineRunner 监听服务的启动,在启动后调用 showConnection 方法
- 在 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
可以看到
- dataSource 使用的是:com.zaxxer.hikari.HikariDataSource
- 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"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-starter-test<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">scope</span>></span>test<span class="hljs-tag"></<span class="hljs-name">scope</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-starter-jdbc<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>mysql<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>mysql-connector-java<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">scope</span>></span>runtime<span class="hljs-tag"></<span class="hljs-name">scope</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependencies</span>></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 数据源
在DataSourceConfiguration
中Tomcat
数据源的实现如下
@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"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-starter-test<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">scope</span>></span>test<span class="hljs-tag"></<span class="hljs-name">scope</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-comment"><!--默认配置 start--></span>
<span class="hljs-comment"><!--<dependency>--></span>
<span class="hljs-comment"><!--<groupId>org.springframework.boot</groupId>--></span>
<span class="hljs-comment"><!--<artifactId>spring-boot-starter-jdbc</artifactId>--></span>
<span class="hljs-comment"><!--</dependency>--></span>
<span class="hljs-comment"><!--默认配置 end--></span>
<span class="hljs-comment"><!--使用tomcat数据源 方式 start--></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-starter-jdbc<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">exclusions</span>></span>
<span class="hljs-tag"><<span class="hljs-name">exclusion</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.zaxxer<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>HikariCP<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"></<span class="hljs-name">exclusion</span>></span>
<span class="hljs-tag"></<span class="hljs-name">exclusions</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.apache.tomcat<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>tomcat-jdbc<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-comment"><!--使用tomcat数据源 方式 end--></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>mysql<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>mysql-connector-java<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">scope</span>></span>runtime<span class="hljs-tag"></<span class="hljs-name">scope</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependencies</span>></span>
- 加入 tomcat-jdbc 数据源的依赖
- 排除 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"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-starter-test<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">scope</span>></span>test<span class="hljs-tag"></<span class="hljs-name">scope</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-comment"><!--默认配置 start--></span>
<span class="hljs-comment"><!--<dependency>--></span>
<span class="hljs-comment"><!--<groupId>org.springframework.boot</groupId>--></span>
<span class="hljs-comment"><!--<artifactId>spring-boot-starter-jdbc</artifactId>--></span>
<span class="hljs-comment"><!--</dependency>--></span>
<span class="hljs-comment"><!--默认配置 end--></span>
<span class="hljs-comment"><!--使用tomcat数据源 方式 start--></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-starter-jdbc<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.apache.tomcat<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>tomcat-jdbc<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-comment"><!--使用tomcat数据源 方式 end--></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>mysql<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>mysql-connector-java<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">scope</span>></span>runtime<span class="hljs-tag"></<span class="hljs-name">scope</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependencies</span>></span>
spring:
datasource:
url: jdbc:mysql://localhost:3306/readinglist?characterEncoding=utf8&useSSL=false
username: root
password: 12345678
type: org.apache.tomcat.jdbc.pool.DataSource
- pom 文件中添加 tomcat-jdbc 依赖
- 在 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
- 添加 druid 数据源依赖
- 在 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">">>>>>>>>>>>>>>>>>服务启动执行"</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&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());
}
}
- 添加 druid 依赖
- 创建 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 主要提供了一下五类方法
- execute 方法:可以用于执行任何 SQL 语句,一般用于执行 DDL 语句
- update 方法及 batchUpdate 方法:update 方法用于执行新增、修改、删除等语句;batchUpdate 方法用于执行批处理相关语句
- query 方法及 queryForXXX 方法:用于执行查询相关语句
- 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<T> data = <span class="hljs-keyword">new</span> <span class="hljs-title class_">BeanPropertyRowMapper</span><>(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<T> data = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span><>();
<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 < <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 < 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;
}