JDBC(六)—— 数据库事务

数据库事务

事务

一组逻辑操作单元,使数据从一种状态变换到另一种状态

事务处理

保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。

当在一个事务中执行多个操作时,要么所有事务都提交,要么数据库管理系统放弃所有修改,整个事务回宫到最初状态。

数据一旦提交就不可回滚

  • DDL操作执行自动提交

  • DML默认情况下一旦执行,自动提交。可通过set autocommit = false的方式取消DML操作的自动提交

  • 关闭连接时会自动提交

支持事物的增删改操作

/**
* 支持事务的增删改操作
* @param conn
* @param sql
* @param args
*/
public void update(Connection conn,String sql,Object ...args){
   PreparedStatement ps = null;
   try {
       //2.预编译sql语句,返回preparedStatement实例
       ps = conn.prepareStatement(sql);
       //3.填充占位符
       //sql占位符的个数与可变形参的长度相同,可变形参相当于一个数组
       for (int i = 0; i < args.length ; i++) {
           ps.setObject(i+1,args[i]);
      }
       //4.执行
       ps.execute();
  } catch (Exception e) {
       e.printStackTrace();
  } finally {
       //5.资源关闭
       JdbcUtils.closeResource(null,ps);
  }
}

使用

public void testTransaction() {
   Connection conn = null;
   try {
       String sql1 = "update `transaction` set money = money - 100 where name = ?";
       String sql2 = "update `transaction` set money = money + 100 where name = ?";
       CommonUtils commonUtils = new CommonUtils();
       conn = JdbcUtils.getConnection();

       //取消数据的自动提交功能
       conn.setAutoCommit(false);

       commonUtils.update(conn,sql1,"why");

       commonUtils.update(conn,sql2,"wyy");
       System.out.println("转账成功");
       
       //提交数据
       conn.commit();
       
  } catch (Exception e) {
       e.printStackTrace();
       try {
           //回滚数据
           conn.rollback();
      } catch (SQLException throwables) {
           throwables.printStackTrace();
      }
  } finally {
       JdbcUtils.closeResource(conn,null);
  }
}

事务的ACID属性及四种隔离级别

ACID属性

  1. 原子性(Atomicity):指事务是不可分割的单位

  2. 一致性(Consistency):事务必须使数据库从一个一致性转移到另一个一致性状态

  3. 隔离性(Isolation):指一个事务的执行不能被其他事务干扰,一个事务内部的操作及使用的数据对并发的其他事务是隔离的

  4. 持久性(Durability):指事务一旦被提交,对数据库的改变即为永久性的

数据库并发问题

  • 脏读

    事务T1,T2

    T1读取了已经被T2更新但是没有提交的字段

    T2回滚,T1读取的内容无效

  • 不可重复读

    事务T1,T2

    T1读取了一个字段,然后T2更新了该字段.

    之后T1再次读取同一字段,值不同

  • 幻读

    事务T1,T2

    T1读取了这个表中的某些字段,T2插入了数据

    T1再次相同表的同一字段 增加了几行

四种隔离级别

  1. READ UNCOMMITED(读未提交数据)

    允许事务读取未被其他事务提交的变更.

    问题:脏读,不可重复读,幻读

  2. READ COMMITED(读已提交数据)

    只允许事务读取已经被其他事务提交的变更,避免脏读.

    问题:不可重复的,幻读

  3. REPEATABLE READ(可重复读)

    雀稗史无可多次从一个字段中读取相同的值,在此期间,禁止其他事务对这个字段进行更新.

    问题:幻读

  4. SERIALIZABLE(串行化)

    确保十五从一个表读取相同的行,在此期间,禁止其他事务对该表进行更新操作,所有并发问题都可避免,但是性能低下.

注:

  1. Oracle数据库支持2种事务隔离:READ UNCOMMITED(读未提交数据)和SERIALIZABLE(串行化),默认为READ UNCOMMITED(读未提交数据)

  2. Mysql 支持4种事务隔离,默认:REPEATABLE READ(可重复读)

在MySQL用命令行设置隔离级别

Mysql常用命令

进入mysql

命令行输入:mysql -u用户名 -p密码

修改密码

mysqladmin -u用户名 -p旧密码 password 新密码

添加新用户并赋予相应权限

grant select on 数据库.* to 用户名@登录主机 identified by “密码”

显示所有数据库

show databases

删除数据库

drop database <数据库名>

设置隔离级别

设置当前连接的隔离级别

set transaction isolation level read committed;

设置全局隔离级别

set global transaction isolation level read committed;

Java代码设置隔离级别

conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
public void testSelect() throws Exception {
   Connection conn = JdbcUtils.getConnection();
   //获取当前隔离级别
   System.out.println(conn.getTransactionIsolation());
   //取消自动提交数据
   conn.setAutoCommit(false);
   //设置数据库隔离级别
   conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
   String sql = "select username,password,reg_date regDate from user where username = ?";
   User user = commonUtils.queryForDifference(conn, User.class, sql, "why");
   System.out.println(user);
}

@Test
public void testUpdae() throws Exception {
   Connection conn = JdbcUtils.getConnection();
   //取消自动提交
   conn.setAutoCommit(false);
   String sql = "update user set username = ? where username = ?";
   commonUtils.update(conn,sql,"love","why");
   Thread.sleep(15000);
   System.out.println("修改结束");
}

 

原文地址:https://www.cnblogs.com/whystudyjava/p/14135410.html