数据库事务

什么叫数据库事务

  • 事务:一组逻辑操作单元,使数据从一种状态到另外一种状态,一组逻辑操作单元,一个或者多个DML操作

事务处理的原则:

  • 保证所有事务都为一个工作单元执行 即使出现了故障,都不能改变这种方式当一个事务中执行了多个操作时,要么所有的事务都被提交(commit) , 那么这些修改就永久保存下来: 要么数据库管理系统将放弃所作的所有修改,整个事务回滚(roolBack) 到最初始状态

JDBC事务处理

  • 数据一旦提交 就不会发生回滚

那些操作会导致数据的自动提交?

DDL数据定义语言,用于定义和管理 SQL 数据库中的所有对象的语言

  • DDL操作一旦执行,就会自动提交set autocommit = false 对DDL操作失效

DML数据操作语言,SQL中处理数据等操作统称为数据操纵语言

  • DML默认情况下,一旦执行,就会自动提交

我们通过set autocommit= false 的方式 取消DML操作的自动提交

  • 默认在关闭连接时,会自动的提交数据

JDBC程序中为了让多个 SQL 语句作为一个事务执行

  • 调用Connection对象的setAutoCommit(flase): 以取消自动提交事务
  • 在所有的sql语句都成功执行后,调用commit(); 方法提交事务
  • 出现异常时.调用rollback(); 进行回滚事务

若此时connection没有被关闭,还可能被被重复使用,则需要恢复其自动提交的状态

setAutoCommit(true) 尤其时使用数据库连接池技术时,执行close()方法前,建议恢复自动提交状态

案例

用户AA向用户BB转账100
未考虑数据库事务情况下的转账操作:
@Test
public void testUpdate() {
String sql1 = "update user_table set balance = balance - 100 where user = ?";
update(sql1, "AA");

//模拟网络异常
// System.out.println(10 / 0);

String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(sql2, "BB");
System.out.println("转账成功");
}

// 通用的增删改操作---version 1.0
public int update(String sql, Object... args) {
Connection conn = null;
PreparedStatement ps = null;
try {
//1, 获取数据库连接
conn = JDBCUtils.getConnection();
//2 预编译sql语句 返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
//修改其为自动提交数据
//主要针对于使用数据库连接池的使用
try {
conn.setAutoCommit(true);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
//关闭资源
JDBCUtils.closeResource(conn, ps);
}
return 0;
}

出现问题:当模拟网络异常时,AA的钱扣除了,但是没有加到B中

解决问题:考虑数据库事务后进行转账操作

 *1.什么叫数据库事务
* 事务:一组逻辑操作单元,使数据从一种状态到另外一种状态
* > 一组逻辑操作单元,一个或者多个DML操作
*2. 事务处理的原则:保证所有事务都为一个工作单元执行 即使出现了故障,都不能改变这种方式
* 当一个事务中执行了多个操作时,要么所有的事务都被提交(commit) 那么这些修改就永久保存下来
* : 要么数据库管理系统将放弃所作的所有修改,整个事务回滚(roolBack) 到最初始状态


考虑数据库事务后的转账操作

注意:

为了数据报错正常产生回滚

  • ​update1(Connection conn, String sql, Object... args)​​​ 连接数据库由外部测试提供,也即是关闭连接是​​JDBCUtils.closeResource(null, ps);​
  • .取消自动提​​conn.setAutoCommit(false)​​;
  • 所有的 SQL 语句都成功执行后,调用​​commit();​​方法提交事务
  • 出现异常 调用​​rollback();​​ 方法回滚事务

@Test
public void testUpdateWithTx() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();

//1.取消自动提交
conn.setAutoCommit(false);

String sql1 = "update user_table set balance = balance - 100 where user = ?";
update1(conn, sql1, "AA");

//模拟网路异常
// System.out.println(10 / 0);

String sql2 = "update user_table set balance = balance + 100 where user = ?";
update1(conn, sql2, "BB");

System.out.println("转账成功");

//2 提交数据
conn.commit();
} catch (Exception e) {
e.printStackTrace();

//3 数据回滚
try {
conn.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
} finally {
JDBCUtils.closeResource(conn, null);
}

}

// 通用的增删改操作---version 2.0
public int update1(Connection conn, String sql, Object... args) {
PreparedStatement ps = null;
try {

//1 预编译sql语句 返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//2 填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//3 执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
//4 关闭资源
JDBCUtils.closeResource(null, ps);
}
return 0;
}

连接数据库相关操作

操作数据库的工具类

package com.nie1.transaction.util;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
* @Description 操作数据库的工具类
*/
public class JDBCUtils {

/**
* @return
* @throws Exception
* @Description 获取数据库的连接
*/
public static Connection getConnection() throws Exception {
// 1.读取配置文件中的4个基本信息
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
//2获取四个配置
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");

// 3.加载驱动
Class.forName(driverClass);

// 4.获取连接
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}

/**
* @Description 关闭连接和Statement的操作
*/
public static void closeResource(Connection conn, Statement ps) {
try {
if (ps != null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

/**
* @Description 关闭资源操作
*/
public static void closeResource(Connection conn, Statement ps, ResultSet rs) {
try {
if (ps != null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (rs != null)
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

配置文件jdbc.properties

#获取
user=root
password=123123
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
driverClass=com.mysql.jdbc.Driver