事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由高级数据库操纵语言或编程语言
(如SQL,C++或Java)书写的用户程序的执行所引起,并用形如begin transaction和end transaction语句(或函数调用)来界定。
事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。
例如:在关系数据库中,一个事务可以是一条SQL语句,一组SQL语句或整个程序。
我对事务的理解最深的还是原子性;即,要么全部操作成功,要么都失败。
在数据库操作中,有时候一个功能的实现要涉及到多个表的操作,我们就以此为一个事务,必须保证多个表的变化都执行,
那么该怎么处理呢?
在这里以java、mysql数据库为例,银行转账问题(事务)
事务描述:用户a给用户b汇款x,数据库表中会有两个操作,a用户的账目数减少x,b用户账目数增加x。
只有当b用户收到钱时,两个操作才回执行,否则都不执行,两人账户不变。
模拟操作:
public class BankExample {
private static String conStr = "jdbc:mysql://localhost/test";
private String root = "root";
private String pwd = "123";
static{
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public Connection getCon(){
try {
return DriverManager.getConnection(conStr, root, pwd);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
BankExample be = new BankExample();
Connection con = null;
Savepoint sp = null;
int x = 0;
try {
con = be.getCon();
con.setAutoCommit(false);//设置自动递交关闭
//操作一 a用户减去x
String sql = "insert into a_bank values(null,?)";
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.setInt(1, 1000 - x);
pstmt.executeUpdate();
//操作二 b用户 加上 x
String sql1 = "insert into b_bank values(null,?)";
PreparedStatement pstmt1 = con.prepareStatement(sql1);
pstmt1.setInt(1, 1000 + x);
pstmt1.executeUpdate();
sp = con.setSavepoint();//设置断点
//打印凭条
PreparedStatement ps = con.prepareStatement("seltct * from result id=?");
ps.setString(1, "33");
ResultSet rs = ps.executeQuery();
System.out.println(rs.getString("name"));
} catch (SQLException e) {
try {
con.rollback(sp);//滚回断点
} catch (SQLException e1) {
e1.printStackTrace();
}
}
try {
con.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
保证一个事务的原子性的方法就是统一提交setAutoCommit(false),此方法只有当调用commit()方法之后才会提交,
从而保证事务的原子性。
我在上面加了一个打印凭条的例子,以便了解SavePoint,不管有没有凭条转账都可以完成,所以转账和打印凭条是两个事务,
我在打印凭条的操作之前设置一个保存点SavePoint,如果没有凭条了会抛出SQLException异常,我在此处理保存点让进度”滚回到“
设置的地方然后执行就可以了!