目录
事务的基本概念
事务ACID特性
案例
需求
数据库设计
Java代码
工具类
事务操作类
测试类
实现方式二(工具不同)
db.properties
工具类
事务操作类
事物是指一组最小的逻辑操作单元,里面由多个操作组成。组成事务的所有操作必须都成功;如果有存在一个操作失败,则整个操作都失败(也叫事物回滚)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
需求: 张三给李四转账
设计: 账户表
技术:
Connection
void setAutoCommit(boolean autoCommit) ; 设置事务是否自动提交
如果设置为false,表示手动提交事务。
void commit(); 手动提交事务
void rollback() ; 回滚(出现异常时候,所有已经执行成功的代码需要回退到事务开始前的状态。)
Savepoint setSavepoint(String name)
/*建库*/
create database demo18
use demo18
/*账户表*/
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
accountName VARCHAR(20) NOT NULL,
money DOUBLE
);
/*添加测试数据*/
INSERT INTO account(accountName,money) VALUES('张三','10000');
INSERT INTO account(accountName,money) VALUES('李四','10000');
select * from account /*查看测试数据是否添加成功*/
/*转账*/
UPDATE account SET money=money-1000 WHERE accountName='张三';
UPDATE account SET money=money+1000 WHERE accountName='李四';
package cn.itcase.account;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/*工具类
*Connection 连接对象
*Statement 执行命令对象:把sql语句发送到数据库执行
*ResultSet (在线式)结果集接口,必须要保持与数据库的连接
*PreparedStatement 预编译对象
*/
public class JdbcUtil1 {
// static 修饰的成员属性 是共享数据 类加载时加载 类文件消失时消失
private static String url = "jdbc:mysql://localhost:3306/demo18"; // 连接数据库
// 等价上句代码 private staitc String url ="jdbc:mysql:///demo17";
private static String user = "root"; // 数据库的登录用户名
private static String password = "root"; // 数据库的登录密码
/**
* 获取连接的方法封装
*/
public static Connection getConnection() {
try {
// 加载驱动
Class.forName("com.mysql.jdbc.Driver");
return DriverManager.getConnection(url, user, password);
} catch (Exception e) {
throw new RuntimeException();
}
}
/**
* 释放资源的方法封装
*
*
*/
public static void closeAll(Connection conn, PreparedStatement pstmt, ResultSet rs) {
try {
if (rs != null) {
rs.close();
rs = null;
}
if (pstmt != null) {
pstmt.close();
pstmt = null;
}
if (conn != null && conn.isClosed()) {
conn.close();
conn = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
package cn.itcase.account;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Savepoint;
public class AccountDao {
// 全局参数
private Connection con;
private PreparedStatement pstmt;
// 1. 转账,没有使用事务
public void trans1() {
String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
String sql_ls = "UPDATE account SET money=money+1000 WHERE accountName='李四';";
try {
con = JdbcUtil1.getConnection(); // 默认开启的隐士事务
con.setAutoCommit(true);
/*** 第一次执行SQL ***/
pstmt = con.prepareStatement(sql_zs);
pstmt.executeUpdate();
/*** 第二次执行SQL ***/
pstmt = con.prepareStatement(sql_ls);
pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil1.closeAll(con, pstmt, null);
}
}
// 2. 转账,使用事务
public void trans2() {
String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
String sql_ls = "UPDATE account SET money=money+1000 WHERE accountName='李四';";
try {
con = JdbcUtil1.getConnection(); // 默认开启的隐士事务
// 一、设置事务为手动提交
con.setAutoCommit(false);
/*** 第一次执行SQL ***/
pstmt = con.prepareStatement(sql_zs);
pstmt.executeUpdate();
/*** 第二次执行SQL ***/
pstmt = con.prepareStatement(sql_ls);
pstmt.executeUpdate();
} catch (Exception e) {
try {
// 二、 出现异常,需要回滚事务
con.rollback();
} catch (SQLException e1) {
}
e.printStackTrace();
} finally {
try {
// 三、所有的操作执行成功, 提交事务
con.commit();
JdbcUtil1.closeAll(con, pstmt, null);
} catch (SQLException e) {
}
}
}
// 3. 转账,使用事务, 回滚到指定的代码段
public void trans() {
// 定义个标记
Savepoint sp = null;
// 第一次转账
String sql_zs1 = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
String sql_ls1 = "UPDATE account SET money=money+1000 WHERE accountName='李四';";
// 第二次转账
String sql_zs2 = "UPDATE account SET money=money-500 WHERE accountName='张三';";
String sql_ls2 = "UPDATE account SET money=money+500 WHERE accountName='李四';";
try {
con = JdbcUtil1.getConnection(); // 默认开启的隐士事务
con.setAutoCommit(false); // 设置事务手动提交
/*** 第一次转账 ***/
pstmt = con.prepareStatement(sql_zs1);
pstmt.executeUpdate();
pstmt = con.prepareStatement(sql_ls1);
pstmt.executeUpdate();
// 回滚到这个位置?
sp = con.setSavepoint();
/*** 第二次转账 ***/
pstmt = con.prepareStatement(sql_zs2);
pstmt.executeUpdate();
pstmt = con.prepareStatement(sql_ls2);
pstmt.executeUpdate();
} catch (Exception e) {
try {
// 回滚 (回滚到指定的代码段)
con.rollback(sp);
} catch (SQLException e1) {
}
e.printStackTrace();
} finally {
try {
// 提交
con.commit();
} catch (SQLException e) {
}
JdbcUtil1.closeAll(con, pstmt, null);
}
}
}
package cn.itcase.account;
import org.junit.Before;
import org.junit.Test;
/**
* 测试类
* @author Administrator
*
*/
public class Accounttest {
/**
* 初始化对象
*/
AccountDao ad = null;
@Before
public void init(){
ad = new AccountDao();
}
@Test
public void trantest(){
ad = new AccountDao();
ad.trans2();
}
}
url=jdbc:mysql://localhost:3306/demo18
user=root
password=root
driverClass=com.mysql.jdbc.Driver
package cn.itcase.account;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* 工具类
* @author Administrator
*Connection 连接对象
*Statement 执行命令对象:把sql语句发送到数据库执行
*ResultSet (在线式)结果集接口,必须要保持与数据库的连接
*PreparedStatement 预编译对象
*
*/
public class JDBCUtil {
private static String url = null;
private static String user = null;
private static String password = null;
private static String driverClass = null;
/**
* 加载配置 静态代码块中(只加载一次) 随类的创建而加载
*/
static {
try {
// 读取db.properties文件
Properties props = new Properties();
InputStream in = JDBCUtil.class
.getResourceAsStream("/db.properties");// 类加载器加载文件
// 加载文件
props.load(in);
// 读取信息
url = props.getProperty("url");
user = props.getProperty("user");
password = props.getProperty("password");
driverClass = props.getProperty("driverClass");
// 1.加载驱动程序
Class.forName(driverClass);
} catch (Exception e) {
e.printStackTrace();
System.out.println("驱程程序注册出错");
}
}
/**
* 抽取获取连接对象的方法
*/
public static Connection getConnection() {
// 2.创建连接
try {
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 释放资源的方法 3.释放资源 后打开先关闭
*
* 方法重载
*/
public static void close(Connection conn, Statement stmt) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
public static void close(Connection conn, Statement stmt, ResultSet rs) {
if (rs != null)
try {
rs.close();
} catch (SQLException e1) {
e1.printStackTrace();
throw new RuntimeException(e1);
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
public static void close(Connection conn, PreparedStatement pstmt) {
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
}
/*
*
* 构造代码:类中直接用{}定义,每一次创建对象时执行。静态代码块中的变量是局部变量,与普通函数中的局部变量性质没有区别
*
* 静态代码块 :用staitc声明,jvm加载类时执行,仅执行一次静态代码块其实就是给类初始化的,而构造代码块是给对象初始化的
*
* 执行顺序优先级:静态块,main(),构造块,构造方法。
*/
package cn.itcase.account;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Savepoint;
public class AccountDao {
// 全局参数
private Connection con;
private PreparedStatement pstmt;
// 1. 转账,没有使用事务
public void trans1() {
String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
String sql_ls = "UPDATE account SET money=money+1000 WHERE accountName='李四';";
try {
con = JDBCUtil.getConnection(); // 默认开启的隐士事务
con.setAutoCommit(true);
/*** 第一次执行SQL ***/
pstmt = con.prepareStatement(sql_zs);
pstmt.executeUpdate();
/*** 第二次执行SQL ***/
pstmt = con.prepareStatement(sql_ls);
pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(con, pstmt);
}
}
// 2. 转账,使用事务
public void trans2() {
String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
String sql_ls = "UPDATE account SET money=money+1000 WHERE accountName='李四';";
try {
con = JDBCUtil.getConnection(); // 默认开启的隐士事务
// 一、设置事务为手动提交
con.setAutoCommit(false);
/*** 第一次执行SQL ***/
pstmt = con.prepareStatement(sql_zs);
pstmt.executeUpdate();
/*** 第二次执行SQL ***/
pstmt = con.prepareStatement(sql_ls);
pstmt.executeUpdate();
} catch (Exception e) {
try {
// 二、 出现异常,需要回滚事务
con.rollback();
} catch (SQLException e1) {
}
e.printStackTrace();
} finally {
try {
// 三、所有的操作执行成功, 提交事务
con.commit();
JDBCUtil.close(con, pstmt);
} catch (SQLException e) {
}
}
}
// 3. 转账,使用事务, 回滚到指定的代码段
public void trans() {
// 定义个标记
Savepoint sp = null;
// 第一次转账
String sql_zs1 = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
String sql_ls1 = "UPDATE account SET money=money+1000 WHERE accountName='李四';";
// 第二次转账
String sql_zs2 = "UPDATE account SET money=money-500 WHERE accountName='张三';";
String sql_ls2 = "UPDATE account SET money=money+500 WHERE accountName='李四';";
try {
con = JDBCUtil.getConnection(); // 默认开启的隐士事务
con.setAutoCommit(false); // 设置事务手动提交
/*** 第一次转账 ***/
pstmt = con.prepareStatement(sql_zs1);
pstmt.executeUpdate();
pstmt = con.prepareStatement(sql_ls1);
pstmt.executeUpdate();
// 回滚到这个位置?
sp = con.setSavepoint();
/*** 第二次转账 ***/
pstmt = con.prepareStatement(sql_zs2);
pstmt.executeUpdate();
pstmt = con.prepareStatement(sql_ls2);
pstmt.executeUpdate();
} catch (Exception e) {
try {
// 回滚 (回滚到指定的代码段)
con.rollback(sp);
} catch (SQLException e1) {
}
e.printStackTrace();
} finally {
try {
// 提交
con.commit();
} catch (SQLException e) {
}
JDBCUtil.close(con, pstmt);
}
}
}