事务管理
1 事务概述
事务指的是逻辑上的一组操作(多条sql语句),组成这组操作的各个单元要么全都成功,要么全都失败.
事务作用:保证在一个事务中多次操作要么全都成功,要么全都失败.
例如转账:
update account set money=money-100 where name='tom';//tom转出100块
update account set money=money+100 where name='jerry';//jerry收到100块
以上两条sql语句,很明显是一组语句,因为转账要么都成功要么都失败,不应该出现一方成功另一方失败的情况.那么我们就需要把他们看成一个事务.
事务处理
事务处理:保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),要么整个事务回滚(rollback)到最初状态
当一个连接对象被创建时,默认情况下是自动提交事务:每次执行一个 SQL 语句时,如果执行成功,就会向数据库自动提交,而不能回滚。
事务开始于:
• 连接到数据库上,并执行一条DML语句(INSERT、UPDATE或DELETE)。
• 前一个事务结束后,又输入了另外一条DML语句。
事务结束于:
• 执行COMMIT或ROLLBACK语句。
• 执行一条DDL语句,例如CREATE TABLE语句;在这种情况下,会自动执行COMMIT语句。
• 执行一条DCL语句,例如GRANT语句;在这种情况下,会自动执行COMMIT语句。
• 断开与数据库的连接。
• 执行了一条DML语句,该语句却失败了;在这种情况中,会为这个无效的DML语句执行ROLLBACK语句。
事务特性
原子性:强调事务的不可分割.多条语句要么都成功,要么都失败。
一致性:强调的是事务的执行的前后,数据要保持一致.
隔离性:一个事务的执行不应该受到其他事务的干扰.
持久性:事务一旦结束(提交/回滚)数据就持久保持到了数据库.
2 使用JDBC原生的API进行事务管理
为了让多个 SQL 语句作为一个事务执行:
调用 Connection 对象的 setAutoCommit(false); 以取消自动提交事务
在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务
在出现异常时,调用 rollback(); 方法回滚事务
若此时 Connection 没有被关闭, 则需要恢复其自动提交状态
#创建账号表
create table account(
id int primary key auto_increment,
name varchar(20),
money double
);
package cn.njit.aff;
/****
* 使用jdbc实现事务
*
*/
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Testjdbc {
public static void main(String[] args) {
Connection conn=getConnection();
try {
conn.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}//设置成不自动提交
String sql="Update account set money=money-? where name=?";
PreparedStatement psmt;
try {
psmt=conn.prepareStatement(sql);
psmt.setDouble(1, 500);
psmt.setString(2, "螺纹");
psmt.executeUpdate();
System.out.println("----");
//-------------------
int x=1/0;
//制造异常,引起回滚
//-------------------
sql="update account set money=money+? where name=?";
psmt=conn.prepareStatement(sql);
psmt.setDouble(1, 500);
psmt.setString(2, "刘智伟");
psmt.executeUpdate();
System.out.println("----");
conn.commit();
} catch (SQLException e) {
try {
System.out.println("======");
e.printStackTrace();
conn.rollback();
}catch(SQLException e1) {
e1.printStackTrace();
}
}finally {
if(null!=conn) {
try{
conn.close();
}catch (SQLException e2){
e2.printStackTrace();
}
}
}
}
public static Connection getConnection() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String url="jdbc:mysql://localhost:3306/njit?useUnicode=true&characterEncoding=utf-8";
String name="root";
String password="admin";
Connection conn = null;
try {
conn=DriverManager.getConnection(url, name, password);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
}
3 使用DBUtils实现事务管理
要点:
QueryRunner qr = new QueryRunner();//为了支持事务,不能传递连接池
conn = C3P0Utils02.getConnection(); //获取jdbc原生连接
conn.setAutoCommit(false); //开启事务
conn.commit(); //正常,则提交事务
conn.rollback(); //异常或者业务不正确,则回滚
Conn.close(); //最后关闭事务
package cn.njit.aff;
/****
* 使用DBUtils实现事务
*/
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import util.C3P0Utils;
public class TestDBUtils {
public static void main(String[] args) {
testUpdate();
}
static void testUpdate() {
QueryRunner qr = new QueryRunner();
// 不能直接把conn放进去
Connection conn = null;
try {
conn = C3P0Utils.getConnection();
System.out.println("0");
} catch (SQLException e) {
e.printStackTrace();
}
try {
conn.setAutoCommit(false);
String sql = "Update account set money=money-? where name=?";
Object[] o = { 500, "刘智伟" };
qr.update(conn, sql, o);
System.out.println("1");
// ---------------
int x=1/0;
// ---------------
String str = "update account set money=money+? where name=?";
Object[] o2 = { 500, "螺纹" };
qr.update(conn, str, o2);
System.out.println("2");
conn.commit();
} catch (SQLException e) {
try {
System.out.println("===========");
conn.rollback();
e.printStackTrace();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
try {
if (null != conn) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}