事务
基本概念:
事务使指一组最小逻辑操作单元,里面有多个操作组成。组成事务的每一部分必须要同时提交成功,如果有一个操作失败,整个操作就回滚。
事务ACID特性
原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
事务的特性:
原子性,是一个最小逻辑操作单元 !
持久性, 事务一旦提交成功,对数据的更改会反映到数据库中。
隔离性, 事务与事务之间是隔离的。
API:
|-- Connection
void setAutoCommit(boolean autoCommit) ; 设置事务是否自动提交,如果设置为false,表示手动提交事务。
void commit() (); 手动提交事务
void rollback() ; 回滚(出现异常时候,所有已经执行成功的代码需要回退到事务开始前的状态。)
Savepoint setSavepoint() 设置回滚点
void rollback(Savepoint sp) 回滚到指定位
Demo如下:
package com.cn.trans;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Savepoint;
import org.junit.Test;
import com.cn.Util.JdbcUtil;
public class AccountDao {
//全局参数
private Connection conn= null;
private PreparedStatement pstmt = null;
//使用事务控制转账业务
@Test
public void trans(){
try {
//获取连接
conn = JdbcUtil.getConnection(); //默认开启的隐式事务
//设置事务为手动提交
conn.setAutoCommit(false);
//转账sql
String sql1 = "UPDATE account SET money=money-1000 WHERE accountName = '木丁西'";
String sql2 = "UPDATE account SET money=money+1000 WHERE accountName = '小刘'";
//预编译sql1
pstmt = conn.prepareStatement(sql1);
//执行sql1
pstmt.executeUpdate();
//预编译sql2
pstmt = conn.prepareStatement(sql2);
//执行sql2
pstmt.executeUpdate();
} catch (Exception e) {
try {
conn.rollback();// 出现异常回滚事务
} catch (Exception e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally{
try {
conn.commit();//所有的操作执行成功,则提交事务
} catch (Exception e) {
e.printStackTrace();
}
JdbcUtil.close(conn, pstmt);
}
}
//使用事务控制转账业务,回滚到指定的代码
@Test
public void trans2(){
Savepoint sp = null;
try {
//获取连接
conn = JdbcUtil.getConnection(); //默认开启的隐式事务
//设置事务为手动提交
conn.setAutoCommit(false);
/*************第一次转账****************/
//转账sql
String sql1 = "UPDATE account SET money=money-1000 WHERE accountName = '木丁西'";
String sql2 = "UPDATE account SET money=money+1000 WHERE accountName = '小刘'";
//预编译sql1
pstmt = conn.prepareStatement(sql1);
//执行sql1
pstmt.executeUpdate();
//预编译sql2
pstmt = conn.prepareStatement(sql2);
//执行sql2
pstmt.executeUpdate();
//回滚到这个位置
sp = conn.setSavepoint();
/*************第二次转账****************/
//转账sql
String sql3 = "UPDATE account SET money=money-50 WHERE accountName = '木丁西'";
String sql4 = "UPDATE account SET money=money+50 WHERE accountName = '小刘'";
pstmt = conn.prepareStatement(sql3);
pstmt.executeUpdate();
pstmt = conn.prepareStatement(sql4);
pstmt.executeUpdate();
} catch (Exception e) {
try {
conn.rollback(sp);// 出现异常回滚事务,回滚到指定位置
} catch (Exception e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally{
try {
conn.commit();//所有的操作执行成功,则提交事务
} catch (Exception e) {
e.printStackTrace();
}
JdbcUtil.close(conn, pstmt);
}
}
}
JdbcUtil.java
package com.cn.Util;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* jdbc的工具类
* @author liuzhiyong
*
*/
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 properties = new Properties();
/**
*
* 加载文件
*
* . 代表java的命令运行的目录
* 在java项目下, . java命令的运行目录从项目的根目录MyEclipse工作空间/bin 目录开始
* 在web项目下, . java命令的运行目录从tomact/bin 目录开始
* 所以不能用.
*/
/**
* 使用类路径的读取方式
* / 斜杠表示classpath的根目录
* 在java项目下,classpath的根目录从bin目录开始
* 在web项目下,classpath的根目录从WEB-INF/classes目录开始
*/
InputStream in = JdbcUtil.class.getResourceAsStream("/db.properties");
properties.load(in);
//读取信息
url = properties.getProperty("url");
user = properties.getProperty("user");
password = properties.getProperty("password");
driverClass = properties.getProperty("driverClass");
// System.out.println(url);
// System.out.println(user);
// System.out.println(password);
// System.out.println(driverClass);
//注册驱动程序
Class.forName(driverClass);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("驱动程序注册出错!");
}
}
/**
* 获取连接对象的方法
*/
public static Connection getConnection(){
try {
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 释放资源的重载方法
*/
public static void close(Connection conn, Statement stmt, ResultSet rs){
//关闭资源(顺序:后打开,先关闭)
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
System.out.println("ResultSet关闭失败!");
throw new RuntimeException(e);
}
}if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
System.out.println("Statement关闭失败!");
throw new RuntimeException(e);
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
System.out.println("Connection关闭失败!");
throw new RuntimeException(e);
}
}
}
/**
* 释放资源的重载方法
*/
public static void close(Connection conn, Statement stmt){
//关闭资源(顺序:后打开,先关闭)
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
System.out.println("Statement关闭失败!");
throw new RuntimeException(e);
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
System.out.println("Connection关闭失败!");
throw new RuntimeException(e);
}
}
}
}