代码改变世界

MySql中的事务问题

2004-09-15 16:03  FantasySoft  阅读(2574)  评论(2编辑  收藏

       今天非常开心,因为终于将昨天一个让我很郁闷的问题解决了。事情是这样的,根据业务逻辑的要求,需要对两张数据表进行更新,为了保证数据的完整性,就需要使用transaction的概念了。如以下代码所示:

conn.setAutoCommit(false);
try
 
{
    PreparedStatement insertStatement 
= conn.prepareStatement("INSERT INTO CUSTOM_ORDER(CUSTOMER, ORDER_LIST) VALUES(?, ?)"
);
    insertStatement.setString(
1
, customer);
    insertStatement.setString(
2
, orderList);
    insertStatement.execute();

    PreparedStatement updateStatement 
= conn.prepareStatement("UPDATE PLAYER SET ACCOUNT = ACCOUNT - ? WHERE ID = ?"
);
    updateStatement.setInt(
1
, sumPrice);
    updateStatement.setString(
2
, customer);
    updateStatement.execute(); 
    
    conn.commit();
}
 
catch
(SQLException se)
{
    conn.rollback();
}

finally
{
    conn.close();
}

        代码很简单,而且根据我以前的经验来看,这样的代码是可以保证数据完整性的,因为对数据表两个连续的更新是一个事务,当发生异常的时候,事务会回滚。然而,测试的结果出乎意料,即使第二个SQL语句执行发生了异常,第一个SQL产生的更新结果也会体现到数据表中。查阅了很多资料,都没有发现这样的代码会有什么问题,而且Java Tutorial对于事务的介绍所给出的例子跟以上代码十分的类似,讲解中也提到两个preparedStatment只有在commit方法被调用的时候,才会作用于数据库。后来我在第一个execute方法之后设置了断点,竟然发现在执行完该方法之后,CUSTOM_ORDER表中已经多了一条记录了。但是此时,我并没有想到这会跟数据库本身有关,拼命在Google上找与JDBC相关的资料。
        最后,在跟一位朋友讨论这个问题的时候,他问我:“你用的什么数据库啊?”我说是MySql,他马上很肯定的告诉我,MySql不支持事务。“不支持事务?!”我立即去查阅了MySql相关的资料[1],果然,MySql在4.0系列之前确实是没有支持事务的。我的朋友曾在3.x系列版本的MySql下面做了两年的开发,因此才会这样告诉我这样的结论。然而4.x系列是可以支持事务的啊,我用的是4.0.16自然也支持了,为什么还会出现这样的情况呢?
        原来,MySql的数据表分为两类,一类是传统的数据表,另一类则是支持事务的数据表。支持事务的数据表分为两种:InnoDB和BerkeleyDB。我们可以通过以下的命令确认安装的MySql Server是否支持这两种数据表:
        SHOW VARIABLES LIKE 'have_innodb';
        SHOW VARIABLES LIKE 'have_bdb';
即使MySql Server本身是支持这两种数据表,但是并不代表着创建新表的时候,新表就是属于这两种的。通常,在创建新表的时候,如果没有加以特别的说明,那么创建的新表则是传统的数据表,是不会支持事务的。因此,我的问题也就解决了,因为以上的示例代码操作的是两张没有支持事务的数据表,怎么可能去处理事务呢?
        通常我们可以通过使用如下的方式来创建支持事务的数据表:
        CREATE TABLE TABLE_NAME(FIELD1, FIELD2.... FIELDn) TYPE=INNODB;
如果原来建好的数据表,我们也可以通过ALTER TABLE命令直接去改变数据表的类型,而不需要备份数据,然后Drop表,再建立新表,再导入数据这样烦琐的步骤。如:
        ALTER TABLE TABLE_NAME TYPE=INNODB;
       
       [1] Using Transaction in MySQL