JAVA事务操作

来源:互联网 发布:amarra 4 for mac 编辑:程序博客网 时间:2024/05/29 13:05

有这样一张名叫test_user的表:uid为1的money为0元,uid为2的money为300元,uid为3的money为800元。现在uid为3的人向uid为1的人转账,每次50元。当uid为3的人的账户中的money少于或等于700的元的时候,则停止转账。也就是说,最后的uid为1的人账户为100元,uid为3的账户700元。

事务操作的基本流程是:当达到某个条件时抛出一个异常,在这个异常处理中回滚操作。事务最大的一个特点是,要么全部执行,要么不执行。但是,对于使用JAVA来操作数据库事务来说,并没用我们想的那么简单:下面这段代码故意将某个字段写错,来检验java是如何操作数据库的事务的:

public class TransactionTest {public static void main(String[] args) throws Exception  {Connection conn = ConnectionFactory.getInstance().getLocalConnection();int money=0;ResultSet rt=null;try {conn.setAutoCommit(false);Statement st =conn.createStatement();String addMoneySql = "update test_user set money=money+50 where uid=1;";int flag0=st.executeUpdate(addMoneySql);System.out.println(flag0);String reduceMoneySql = "update test_user set money=money-50 where uid=3";int flag1=st.executeUpdate(reduceMoneySql);System.out.println(flag1);String queryMoneySql="select money from test_user where uid=3";rt=st.executeQuery(queryMoneySql);if(rt.next()){money=rt.getInt("mone");//这里故意将字段打错,检验java如何执行事务的,原字段为moneySystem.out.println(money);}if(money<=700){throw new RuntimeException("3号的钱已经不够了,不要汇款了");//抛出一个异常,在异常处理中回滚操作}              conn.commit();} catch (Exception e) {e.printStackTrace();//打印出上面抛出来的异常,否则是不会显示的。if(conn!=null){conn.rollback();}}}}
执行这段代码。发现结果为:

1

1

java.sql.SQLException: Column 'mone' not found.
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:910)
at com.mysql.jdbc.ResultSet.findColumn(ResultSet.java:987)
at com.mysql.jdbc.ResultSet.getInt(ResultSet.java:2749)
at TransactionTest.main(TransactionTest.java:26)
在异常发生后,catch住异常,执行了回滚操作,查看数据库,数据库数据并没有发生变化。但是用update执行的结果来看,update是执行了的。这就说明java处理数据库的事务的执行策略:先挨句执行(下一句的基本数据是上一句执行完毕的数据,而不是数据库真实的数据),当出现回滚时候,直接通知数据库回滚。数据库回滚以后并不会通知java,所以就会出现打印执行结果与数据库数据不一致的情况。

那么,这一段代码中money(假设字段已经修改正常)的值会是多少呢?

首先 uid 1 加50,uid 3 减50,成功执行,打印出750

然后 uid 1加 50   uid 3减 50,此时 uid 3中的money为 700(发现异常,回滚)结果数据库中的uid还是750,而打印出来的值却是 700.

再执行。发现依旧会抛出3号钱已经不够的异常。

当然,我们会思考,我们重新执行这段代码的话,java是不会从数据库中间取得数据,而是直接使用脏数据。而且关闭连接等资源,关闭eclispe都不起作用,只有重新启动数据库,才能使得java从数据库读数据。

当然了,将事务按照合理的方式编排就不会出现这种错误。比如,将select语句放在uid 3之前,就不会出错了。(其实对于事务操作,依旧读的是脏数据,只不过这个脏数据与实际数据一致罢了。)

欢迎各位大虾指正:-D