JDBC事务管理


如果还不清楚什么是事务,可以先看一下MySQL事务管理(初步了解)这篇文章



cd4356
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();


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



cd4356
设计数据库表
JDBC事务管理_第1张图片

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);

导入依赖jar包
<dependencies>
	
	<dependency>
	    <groupId>junitgroupId>
	    <artifactId>junitartifactId>
	    <version>4.12version>
	dependency>
	
	<dependency>
	    <groupId>mysqlgroupId>
	    <artifactId>mysql-connector-javaartifactId>
	    <version>5.1.47version>
	dependency>
dependencies>


cd4356
不通过事务方式管理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
JDBC事务管理_第2张图片

运行测试后,再查看一下数据库中,商品表和订单表的数据。此时,订单表中新增了一条订单记录(订购5件‘100001’号商品),商品表中‘100001’号商品的库存量相应的减少了5。

这就是使用事务管理SQL的好处,它保证的数据库中数据的一致性。避免某些操作失误,导致下了订单后,商品库存未做出相应修改,或者商品库存减少后,没有订单显示。
JDBC事务管理_第3张图片



前面的案例中,由于所有SQL语句都是正确的,所以看不出加事务管理不加事务管理的区别!

   将其中的一条SQL语句改错(比如将update products set stock=stock-5 where id='100001' 改成 update productsss set stock=stock-5 where id='100001'),然后分别在用加事务管理不加事务管理环境下进行测试,然后查商品表和订单表的数据,对比它们之间的区别,就可以清晰地看出用事务管理SQL的作用了。

你可能感兴趣的:(JDBC,JDBC事务处理,事务隔离级别)