如果还不清楚什么是事务,可以先看一下MySQL事务管理(初步了解)这篇文章
JDBC是Java数据库连接相关的API,所以Java的事务管理也是在通过该API进行的。JDBC的核心是Connection接口,JDBC的事务管理是基于Connection接口来实现的,通过Connection对象进行事务管理。
JDBC对事务的处理规则,必须是基于同一个Connection对象的
JDBC提供了3个方法来进行事务管理
setAutoCommit() 设置自动提交,方法中需要传入一个boolean类型的参数,true为自动提交,false为手动提交
commit() 提交事务
rollback() 回滚事务
JDBC默认的事务处理行为是自动提交。所以JDBC在进行事务管理时,首先要通过Connection对象调用setAutoCommit(false) 方法, 将SQL语句的提交(commit)由驱动程序转交给应用程序负责。并且调用setAutoCommit(false) 方法后,程序必须调用commit或者rollback方法,否则SQL语句不会被提交或回滚。
con.setAutoCommit();
// SQL语句
// SQL语句
// SQL语句
con.commit();或 con.rollback();
JDBC提供了5中事务隔离级别,它们以常量的形式在Connection接口中定义,除了TRANSACTION_NONE外,其它4种隔离级别 与 MySQL的事务隔离级别一样。
TRANSACTION_NONE(不支持事务)
TRANSACTION_READ_UNCOMMITTED
TRANSACTION_READ_COMMITTED
TRANSACTION_REPEATABLE_READ
TRANSACTION_SERIALIZABLE
JDBC事务隔离级别可通过下面方法设置
getTransactionIsolation() 获取当前隔离级别
setTransactionIsolation() 设置隔离级别
给出Connection接口中部分关于事务方面的源码
package java.sql;
import java.sql.*;
public interface Connection extends Wrapper, AutoCloseable {
void setAutoCommit(boolean autoCommit) throws SQLException;
boolean getAutoCommit() throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
int TRANSACTION_NONE = 0;
int TRANSACTION_READ_UNCOMMITTED = 1;
int TRANSACTION_READ_COMMITTED = 2;
int TRANSACTION_REPEATABLE_READ = 4;
int TRANSACTION_SERIALIZABLE = 8;
void setTransactionIsolation(int level) throws SQLException;
int getTransactionIsolation() throws SQLException;
}
drop database if exists database_transaction;
create database database_transaction;
use database_transaction;
/*===========================================*/
drop table if exists products;
create table products
(
id char(6) not null,
title varchar(20),
price double,
stock int,
status varchar(10),
primary key (id)
);
insert into products values('100001','小米8',2699,100,'正常');
insert into products values('100002','小米8SE',1799,100,'正常');
insert into products values('100003','小米MIX2S',3299,100,'正常');
insert into products values('100004','小米手环3',199,100,'正常');
/*==order是MySQL的关键字,所以表名不能为order,应用orders==*/
drop table if exists orders;
create table orders
(
id char(6) not null,
pid char(6) not null,
number int,
price double,
create_time datetime,
send_time datetime,
confirm_time datetime,
consignee varchar(20),
consignee_phone char(11),
consignee_address varchar(100),
status varchar(10),
primary key (id)
);
alter table orders add constraint fk_pid foreign key (pid) references products (id);
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
dependencies>
不通过事务方式管理SQL语句。此时的SQL语句的提交(commit),是由驱动程序负责管理,在SQL执行完后,就会自动帮我们进行提交,每一条SQL都相当于一个事务。
但执行多条相关联SQL语句,而不通过事务来管理时。如果再某些步骤中中止或操作失误,就会破坏数据库的一致性。如下,由于没有通过事务SQL,若第二条修改商品库存的SQL执行失败,就会导致提交订单了,但商品库存不变。这种感觉就像是有限的商品却永远都卖不完,但这可能吗?
//创建连接
connection= DriverManager.getConnection(url,userName,password);
//创建Statement对象
Statement statement = connection.createStatement();
// 执行SQL
statement.execute("insert into orders values ('10021','100001',5,2699,now(),null ," +
"null ,'王大傻','13535878996','广州番禺','待发货')");
statement.execute("update products set stock=stock-5 where id='100001'");
//关闭语句
statement.close();
所以,在操作多条相关联SQL时,需要通过事务来管理这些SQL,使它们作为一个原子性的工作单元(整体),要么全部执行,要么全部不执行,保证的数据库中数据的一致性。
//创建连接
connection= DriverManager.getConnection(url,userName,password);
//设置手动提交事务
connection.setAutoCommit(false);
//创建Statement对象
Statement statement = connection.createStatement();
// 执行SQL
statement.execute("insert into orders values ('10021','100001',5,2699,now(),null ," +
"null ,'王大傻','13535878996','广州番禺','待发货')");
statement.execute("update products set stock=stock-5 where id='100001'");
//关闭语句
statement.close();
//提交事务
connection.commit();
完整代码(原本生成订单等…操作是放在业务层中进行的,但未了方便测试,就全部都简化到一个测试类中进行)
package com.cd4356.jdbc_transaction.dao;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class OrderTest {
private String driverClass="com.mysql.jdbc.Driver";
private String url="jdbc:mysql://localhost:3306/database_transaction?useUnicode=true&characterEncoding=UTF8";
private String userName="root";
private String password="root";
@Test
public void addOrder(){
try {
//加载驱动
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Connection connection=null;
try {
//创建连接
connection= DriverManager.getConnection(url,userName,password);
//设置手动提交事务
connection.setAutoCommit(false);
//创建Statement对象
Statement statement = connection.createStatement();
// 执行SQL //now()表示当前时间
statement.execute("insert into orders values ('10021','100001',5,2699,now(),null ," +
"null ,'王大傻','13535878996','广州番禺','待发货')");
statement.execute("update products set stock=stock-5 where id='100001'");
//关闭语句
statement.close();
//提交事务
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
try {
//遇到异常,则回滚事务
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally {
try {
//关闭连接
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
在运行测试前,先看一下数据库中,商品表和订单表的数据,订单表中暂无订单记录,商品表中有四种商品,且库存量均为100
运行测试后,再查看一下数据库中,商品表和订单表的数据。此时,订单表中新增了一条订单记录(订购5件‘100001’号商品),商品表中‘100001’号商品的库存量相应的减少了5。
这就是使用事务管理SQL的好处,它保证的数据库中数据的一致性。避免某些操作失误,导致下了订单后,商品库存未做出相应修改,或者商品库存减少后,没有订单显示。
前面的案例中,由于所有SQL语句都是正确的,所以看不出加事务管理和不加事务管理的区别!
将其中的一条SQL语句改错(比如将update products set stock=stock-5 where id='100001'
改成 update productsss set stock=stock-5 where id='100001'
),然后分别在用加事务管理和不加事务管理环境下进行测试,然后查商品表和订单表的数据,对比它们之间的区别,就可以清晰地看出用事务管理SQL的作用了。