数据库事务

事务集应该作为一个整体进行操作,而不是单个语句的执行,一个逻辑操作单元。

要么所有的事物都被提交,否则就发生事务回滚

那些操作发生数据提交:

  1. 增删改一旦执行,就会提交
  2. 关闭链接,也会自动
1
2
3
4
5
6
第一步:事务是自动提交的我们 手动关闭自动提交
conn.setAutoCommit(false);
第二步:如果没有异常就提交事务
conn.commit();
第三步:如果抛出异常 我们我们就让他回滚
conn.rollback();

在以下这个表格中,AA与BB进行相互转账,为保证操作的可靠性,我们需要通过事务保障操作的可行性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;

/**
* @author zss
*/
public class TransferAccount {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
Properties properties=new Properties();
properties.load(new FileReader("src\\Information.properties"));
String jdbc= properties.getProperty("jdbcJar");
String url= properties.getProperty("url");
String password= properties.getProperty("password");
String user= properties.getProperty("user");
Class.forName(jdbc);
Connection connection= DriverManager.getConnection(url,user,password);
String sql1="update user_table set balance=balance-100 where user=(?) ";
String sql2="update user_table set balance=balance+100 where user=(?) ";
PreparedStatement preparedStatement1= connection.prepareStatement(sql1);
PreparedStatement preparedStatement2= connection.prepareStatement(sql2);
preparedStatement1.setString(1,"AA");
preparedStatement2.setString(1,"BB");
//AA转账
preparedStatement1.executeUpdate();
//发生故障
System.out.println(10/0);
//BB接受账单
preparedStatement2.executeUpdate();
preparedStatement1.close();
preparedStatement2.close();
connection.close();
}
}

image-20220330160902140

我们可以发现一笔钱无缘无故的消失了,但是当我们使用事务进行处理时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;

/**
* @author zss
*/
public class TransferAccount {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
Properties properties=new Properties();
properties.load(new FileReader("src\\Information.properties"));
String jdbc= properties.getProperty("jdbcJar");
String url= properties.getProperty("url");
String password= properties.getProperty("password");
String user= properties.getProperty("user");
Class.forName(jdbc);
Connection connection= DriverManager.getConnection(url,user,password);
connection.setAutoCommit(false);
String sql1="update user_table set balance=balance-100 where user=(?) ";
String sql2="update user_table set balance=balance+100 where user=(?) ";
PreparedStatement preparedStatement1= connection.prepareStatement(sql1);
PreparedStatement preparedStatement2= connection.prepareStatement(sql2);
preparedStatement1.setString(1,"AA");
preparedStatement2.setString(1,"BB");
try {


//AA转账
preparedStatement1.executeUpdate();
//发生故障
//System.out.println(10/0);
//BB接受账单
preparedStatement2.executeUpdate();}catch (ArithmeticException arithmeticException){
System.out.println("交易发生异常,事务进行回滚");
connection.rollback();
}
//如果没有发生异常则提交数据
connection.commit();
preparedStatement1.close();
preparedStatement2.close();
connection.close();
}
}

image-20220330161443587

我们可以看出数据交易正常进行

事务的属性

ACID:

  1. 原子性:事务不可分割,要么都发生,要么都不发生。
  2. 一致性:必须使数据库从一个一致性状态变换到另外一个一致性状态,比如金钱的总数
  3. 隔离性:事务不可以被其他事务干扰,内部操作必须是不被其他事务影响
  4. 持久性:数据一旦提交,数据中的改变是永久性的,其他操作均无法对其产生影响

数据库的并发问题

  1. 脏读:1与2两个事务,1读取了2已经更改但是还没有提交的字段,但是随后2发生了回滚,1读取的内容就变的无根据
  2. 不可重复读:1读取一个字段,2对其进行了更改,后来1再次重新读取时,值不相同
  3. 幻读:1对表中的全部行进行了同一操作,2又插入了一行,但是新插入的行并没有更改,和幻觉一样,也包含两次读取表格的数据不一致