您现在的位置是:首页 > 文章详情

JDBC学习:事务

日期:2019-01-17点击:228

什么是事务

数据库中一些操作的集合是一个独立的单元,事务就是构成单一逻辑工作单位的集合。

为什么需要事务

事务是为解决数据安全操作提出的,事务控制实际上就是控制数据的安全访问。
比如:银行转帐业务,账户A给账户B转帐100元,需要账户A余额减100元,账户B余额加100元,两个需要同时发生。完成这种操作需要保证要么全部成功,要么全部失败。

什么是回滚

未能成功完成的事务成为中止事务,对中止事务造成的变更需要进行撤销处理,称为事务回滚。

事务的特性(ACID 原则)

  • 原子性(atomicity):对事务中的全部操作是不可分割的,要么全部完成,要么都不执行。
  • 一致性(consistency):事务执行之前和执行之后,数据库都必须处于一致性状态。
  • 隔离性(isolation):事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的。
  • 持久性(durability):对于任意已提交的事务,系统必须保证该对数据库的改变不丢失,即使数据库出现故障。

Java JDBC 事务机制
比如有个业务:当我们修改一个信息后再去查询这个信息。这是一个简单的业务,实现起来也非常容易,但是当这个业务放在多线程高并发的平台下,问题自然就出现了。
比如当执行了一个修改后,在查询之前有一个线程也执行了修改语句,这时再执行查询,看到的信息就有可能和我们修改的不同。为了解决这一问题,就引入了引入JDBC事务机制。

如何操作

把事务操作设置为不自动提交,通过手动提交就能实现事务的处理。

搭建实验环境实验,在当前数据库中创建一个测试表:

CREATE TABLE tb ( id INT UNSIGNED NOT NULL AUTO_INCREMENT KEY COMMENT '编号', name VARCHAR(20) NOT NULL COMMENT '姓名', sex VARCHAR(4) NOT NULL COMMENT '性别', phone VARCHAR(11) NOT NULL COMMENT '手机号码' );

插入几条测试数据:

INSERT INTO tb (name, sex, phone) VALUES ("张三", "男", "139****2234"), ("李四", "女", "130****3239"), ("王二", "男", "136****1234"), ("小王", "女", "137****1735"), ("赵云", "男", "131****1255"), ("关羽", "男", "139****1930");

实验代码
transactionDemo

相关延伸

事务并发处理可能引起的问题

  • 脏读(dirty read):一个事务读取了另一个事务尚未提交的数据。
  • 不可重复读(non-repeatable read):一个事务的操作导致另一个事务前后两次读取到不同的数据。
  • 幻读(phantom read):一个事务的操作导致另一个事务前后两次查询到的结果数据量不同。

举例:

  • 事务A、B并发执行,当A事务update后,B事务select读取到A尚未提交的数据,此时A事务rollback,则B读取到的数据是无效的“脏”数据。
  • 当B事务select读取数据后,A事务update操作更改B事务select到的数据,此时B事务再次读取该数据,发现前后两次的数据不一样。
  • 当B事务select读取数据后,A事务insert或delete了一条满足A事务的select条件的记录,此时B事务再次select,发现查询到前不存在的数据(“幻影”),或者前面的某个记录不见了。

JDBC的事务支持

JDBC对事务的支持体现在三个方面:

1.自动提交模式(Auto-commit mode)

Connection提供了一个auto-commit的属性来指定事务何时结束。

  • a.当auto-commit为true时,当每个独立的SQL操作执行完毕,事务立即自动提交,也就是说每个SQL操作都是一个事务的。auto-commit默认为true

一个独立SQL操作什么时候什么执行完毕呢?
JDBC规范这样规定

对数据操作语言(DML如insert,update,delete)和数据定义语言(DDL如create,drop),语句一执行完就视为执行完毕。

对select语句,当与它关联的ResultSet对象关闭时,视为执行完毕。
对存储过程或者其他返回多个结果的语句,当与它关联的所有ResultSet对象全部关闭,所有update count(update,delete等语句操作影响的行数)和output parameter(存储过程的输出参数)都已经获取之后,视为执行完毕。

  • b.当auto-commit为false时,每个事务都必须显式调用commit方法进行提交,或者显式调用commit方法进行回滚。

2.事务隔离级别(Transaction Isolation Levels)

JDBC提供了5种不同的事务隔离级别。在Connection中进行了定义。

  • TRANSACTION_NONE:JDBC不支持事务
  • TRANSACTION_READ_UNCOMMITTED:允许脏读、不可重复读和幻读
  • TRANSACTION_READ_COMMITTED:禁止脏读,但允许不可重复读和幻读
  • TRANSACTION_REPEATABLE_READ:禁止脏读和不可重复读,单可以幻读
  • TRANSACTION_SERIALIZABLE:禁止脏读、不可重复读和幻读

3.保存点(SavePoint)

JDBC定义了SavePoint接口,提供在一个更细粒度的事务控制机制。当设置了一个保存点后,可以rollback到该保存点处的状态,而不是rollback整个事务。

连接对象获取和关闭的时机

现状:

  • 连接对象的获取和销毁比较浪费时间
  • 一个事务中多个操作,若每个操作都生成一个连接对象,多用户同时连接数据库时,访问效率会非常的低。

需求:

  • 一个事务中的多个操作应该用同一个连接对象控制,不然无法实现提交和回滚。一个事务开始时获得连接对象,一个事务结束时关闭连接。

解决:

  1. 将连接对象的申请交给专门的连接管理类
    事务申请连接对象时,通过连接管理类申请,而不再通过DriverManager申请
  2. 一个事务一定是由一个线程完成的,使用线程局部变量获得同一个连接对象

接口DataSource

该工厂用于提供到此 DataSource 对象所表示的物理数据源的连接。

作为 DriverManager 工具的替代项,DataSource 对象是获取连接的首选方法。

DataSource接口由驱动程序供应商实现。共有三种类型的实现:

  • 基本实现:生成标准的 Connection 对象
  • 连接池实现:生成自动参与连接池的 Connection 对象。此实现与中间层连接池管理器一起使用。
  • 分布式事务实现:生成一个 Connection 对象,该对象可用于分布式事务,大多数情况下总是参与连接池。此实现与中间层事务管理器一起使用,大多数情况下总是与连接池管理器一起使用。

方法:

  • getConnection():尝试建立与此 DataSource 对象所表示的数据源的连接。
  • getConnection(String username, String password):尝试建立与此 DataSource 对象所表示的数据源的连接。

实例:
先在src目录下新建配置文件config.properties:

driver=com.mysql.jdbc.Driver url=jdbc\:mysql\://127.0.0.1\:3306/empmgs?useUnicode\=true&characterEncoding\=utf-8 username=root password=root

实例代码:

import java.io.FileInputStream; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSourceFactory; public class demo4 { static Properties prop = new Properties(); static DataSource ds = null; static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>(); private static Connection conn = null; private static Statement sm = null; /** * 静态初始化块加载注册驱动 */ static { try { prop.load(new FileInputStream("src/config.properties")); ds = BasicDataSourceFactory.createDataSource(prop); } catch (Exception e) { e.printStackTrace(); } } static public Connection getConnection(){ try { //先看线程局部变量 conn = threadLocal.get(); if(conn == null){//线程局部变量中没有保存连接对象 conn = ds.getConnection(); threadLocal.set(conn);//设置连接对象到线程局部变量 } } catch (SQLException e) { e.printStackTrace(); } return conn; } public static void main(String[] args) { try{ conn = getConnection (); sm = conn.createStatement(); // 关闭自动提交 conn.setAutoCommit(false); sm.executeUpdate("UPDATE tb SET name = '老王' WHERE id = 3 "); sm.executeUpdate("INSERT INTO tb (name, sex, phone) VALUES ('美丽','女','132****5555')"); conn.commit(); System.out.println("运行结束"); } catch (Exception e) { try { conn.rollback(); } catch (Exception ex) { e.printStackTrace(); } } finally { if (sm != null) { try { sm.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); conn = null; } catch (SQLException e) { e.printStackTrace(); } } } } }
原文链接:https://yq.aliyun.com/articles/688008
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章