为了做这个实验,设计个场景,要把用户2 上账户的22块钱打一块钱到账户1的账上去。要么全成功,全失败,也就是账要做平。
代码如下:
其中最关键的是:
updateMoneyByUserId(1, getMoneyByUserId(1) + 1,false); //这条语句是否回滚
updateMoneyByUserId(2, getMoneyByUserId(2) - 1,true); //设置抛出异常,
当用户2在打钱时抛出异常,没成功时,用户1的更新要回滚回来。在mysql connection中默认是自动提交的,需要设置为手动提交,然后才可用rollback方法进行回滚。
package com.test.transaction;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
CREATE TABLE `usermoney` (
`user_id` int(11) NOT NULL DEFAULT '0',
`moneyNum` int(11) DEFAULT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='user for money';
INSERT INTO `usermoney` VALUES (1,11),(2,22),(3,33),(4,44),(5,55);
* */
public class TranTest {
static Connection connection;
static {
try {
// 加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
// 连续数据库
connection = DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/banker", "root", "123456");
if (!connection.isClosed()) {
System.out.println("created connetion...:" + connection);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
try {
//手工设置数据库为一个初始状态,并提交
connection.setAutoCommit(false);//默认为自动提交
updateMoneyByUserId(1, 11,false);
updateMoneyByUserId(2, 22,false);
connection.commit();
//查询是否是设置正确1 --11 ; 2 --22
System.out.println("userID=1 & moneyNum=" + getMoneyByUserId(1));
System.out.println("userID=2 & moneyNum=" + getMoneyByUserId(2));
//如果更新是抛异常,需要注意回滚
connection.setAutoCommit(false);
updateMoneyByUserId(1, getMoneyByUserId(1) + 1,false); //这条语句是否回滚
updateMoneyByUserId(2, getMoneyByUserId(2) - 1,true); //设置抛出异常,
//如果抛异常,下面语句不再执行
System.out.println("userID=1 & moneyNum=" + getMoneyByUserId(1));
System.out.println("userID=2 & moneyNum=" + getMoneyByUserId(2));
//如果不抛异常,不提交会怎样?(结束时还是会做一次提交)
connection.commit();
} catch (SQLException e) {
try {
e.printStackTrace();
//开始回滚
connection.rollback();
//数据库是否还是正确的呢?1==11 ; 2==22
System.out.println("check if userId=1 and moneyNum==11-->userID=1 & moneyNum=" + getMoneyByUserId(1));
System.out.println("check if userId=2 and moneyNum==22-->userID=2 & moneyNum=" + getMoneyByUserId(2));
} catch (SQLException e1) {
e.printStackTrace();
}
}
Thread.sleep(10000L);
}
/**
* 通过UserId获取Money number
* */
public static int getMoneyByUserId(int userId) throws SQLException {
// 执行SQL语句
Statement statement = connection.createStatement();
// 要执行的SQL语句
String sql = "select user_id,moneyNum from usermoney where user_id="
+ userId;
statement.execute(sql);
ResultSet rs = statement.executeQuery(sql);
rs.next();
return new Integer(rs.getString("moneyNum"));
}
/**
* 更新用户金额
* */
public static void updateMoneyByUserId(int userId, int moneyNum,boolean showThrow)
throws SQLException {
// 执行SQL语句
if (showThrow) {
throw new SQLException("不让你存");
}
Statement statement = connection.createStatement();
// 要执行的SQL语句
String sql = "update usermoney set moneyNum=" + moneyNum
+ " where user_id=" + userId;
System.out.println(sql);
statement.executeUpdate(sql);
}
}
通过以上代码实验,我们还可以得出一个经验。
在一个方法中同时操作本地数据库和调用远程接口时,要先把本地数据库事务操作完成后,再调用远程接口。
因为如果先调用接口,再操作本地数据库事务是抛出异常,再回滚时,本地数据库就和远程接口数据不一致了。
如果先操作本地数据库,抛出异常还可以自己决定一次是否要调用远程接口。
现在比较大的项目,常用spring来做事务管理,接下来做更多的这方面研究。