• jdbc事务


    买书的例子 程序应该将图书数量的操作和更新account用户余额的操作作为一个事务来处理,只有这两个操作都完成的情况下,才能提交事务,否则就回滚事务。

    本文转自http://blog.chinaunix.net/uid-26284395-id-3037410.html,感谢作者

    1. conn=DriverManager.getConnection(url,user,password);
    2.                 
    3.                 conn.setAutoCommit(false);
    4.                 conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
    5.                 
    6.                 stmt=conn.createStatement();
    7.                 rs=stmt.executeQuery("select price,amount from bookinfo where id=3");
    8.                 rs.next();
      1.   conn.commit();
      2.                 out.println("交易成功!");
      3.                 out.close();
      4.             }
      5.             catch(SQLException se)
      6.             {
      7.                 if(conn!=null)
      8.                 {
      9.                     try
      10.                     {
      11.                         conn.rollback();
      12.                     }
      13.                     catch(SQLException ***)
      14.                     {
      15.                         ***.printStackTrace();
      16.                     }
      17.                 } 
      18.                 se.printStackTrace();
      19.             }
      20.             finally
      21.             {
      22.                 if(rs!=null)
      23.                 {
      24.                     try
      25.                     {
      26.                         rs.close();
      27.                     }
      28.                     catch(SQLException se)
      29.                     {
      30.                         se.printStackTrace();
      31.                     }
      32.                     rs=null;
      33.                 }
        1. (2)设置事务回滚点

          Savepoint sp = conn.setSavepoint();

          Conn.rollback(sp);

          Conn.commit();   //回滚后必须要提交

          package com.itheima.transaction;

          import Java.sql.Connection;

          import java.sql.DriverManager;

          import java.sql.PreparedStatement;

          import java.sql.SQLException;

          import java.sql.Savepoint;

          import org.junit.Test;

          import com.itheima.util.DaoUtil;

          public class Demo1 {

          @Test

          public void test1(){

          Connection conn = null;

          PreparedStatement ps1 = null;

          PreparedStatement ps2 = null;

          Savepoint sp = null;

          try{

          Class.forName("com.MySQL.jdbc.Driver");

          conn = DriverManager.getConnection("jdbc:mysql:///day11", "root", "root");

          conn.setAutoCommit(false);

          ps1 = conn.prepareStatement("update account set money = money+100 where name=?");

          ps1.setString(1, "b");

          ps1.executeUpdate();

          //int i = 1/0;

          ps2 = conn.prepareStatement("update account set money = money-100 where name=?");

          ps2.setString(1, "a");

          ps2.executeUpdate();

          sp = conn.setSavepoint();

          //-----------------------------------

          ps1 = conn.prepareStatement("update account set money = money+100 where name=?");

          ps1.setString(1, "b");

          ps1.executeUpdate();

          int i = 1/0;

          ps2 = conn.prepareStatement("update account set money = money-100 where name=?");

          ps2.setString(1, "a");

          ps2.executeUpdate();

          conn.commit();

          }catch (Exception e) {

          e.printStackTrace();

          try {

          if(sp !=null){

          conn.rollback(sp);

          conn.commit();

          }else{

          conn.rollback();

          }

          } catch (SQLException e1) {

          // TODO Auto-generated catch block

          e1.printStackTrace();

          }

          }finally{

          DaoUtil.close(conn, ps1, null);

          DaoUtil.close(conn, ps2, null);

          }

          }

          }

          JDBC API支持事务对数据库的加锁,并且提供了5种操作支持,2种加锁密度。

          5种加锁支持为:

          static int TRANSACTION_NONE = 0;

          static int TRANSACTION_READ_UNCOMMITTED = 1;

          static int TRANSACTION_READ_COMMITTED = 2;

          static int TRANSACTION_REPEATABLE_READ = 4;

          static int TRANSACTION_SERIALIZABLE = 8;

          1. 5、事务的隔离级别

            (1)多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。

            (2)如果不考虑隔离性,可能会引发如下问题:

            脏读(dirty reads) 

            一个事务读取了另一个未提交的并行事务写的数据。 

            不可重复读(non-repeatable reads) 

            一个事务重新读取前面读取过的数据, 发现该数据已经被另一个已提交的事务修改过。 

            幻读(phantom read) 

            一个事务重新执行一个查询,返回一套符合查询条件的行, 发现这些行因为其他最近提交的事务而发生了改变。

            6、事务的隔离性

            (1)脏读:

            指一个事务读取了另外一个事务未提交的数据。

            这是非常危险的,假设A向B转帐100元,对应sql语句如下所示

            1.update account set money=money+100 while name=‘b’;

            2.update account set money=money-100 while name=‘a’;

            当第1条sql执行完,第2条还没执行(A未提交时),如果此时B查询自己的帐户,就会发现自己多了100元钱。如果A等B走后再回滚,B就会损失100元。

                    a 1000

            b 1000

            a:

            start transaction;

            update account set money=money-100 where name='a';

            update account set money=money+100 where name='b';

            b:

            start transaction;

            select * from account where name='b';

            a:

            rollback;

            b:

            strat transaction;

            select * from account where name='b';

            (2)不可重复读:

            在一个事务内读取表中的某一行数据,多次读取结果不同。

            例如银行想查询A帐户余额,第一次查询A帐户为200元,此时A向帐户存了100元并提交了,银行接着又进行了一次查询,此时A帐户为300元了。银行两次查询不一致,可能就会很困惑,不知道哪次查询是准的。

            和脏读的区别是,脏读是读取前一事务未提交的脏数据,不可重复读是重新读取了前一事务已提交的数据。

            很多人认为这种情况就对了,无须困惑,当然是后面的为准。我们可以考虑这样一种情况,比如银行程序需要将查询结果分别输出到电脑屏幕和写到文件中,结果在一个事务中针对输出的目的地,进行的两次查询不一致,导致文件和屏幕中的结果不一致,银行工作人员就不知道以哪个为准了。

                    a 1000

            b 1000

            start transaction;

            select sum(money) from account; ---- 总存款:2000

            select count(*) from account; ---- 总账户数:2

            -------------------------

            b:

            start transaction;

            update account set money = money-1000 where name='b';

            commit;

            -------------------------

            select avg(money) from account; ---- 账户平均金额:500

            (3)虚读(幻读)

            是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。

            如丙存款100元未提交,这时银行做报表统计account表中所有用户的总额为500元,然后丙提交了,这时银行再统计发现帐户为600元了,造成虚读同样会使银行不知所措,到底以哪个为准。

                        a 1000

            b 1000

            c 1000

            start transaction;

            select sum(money) from account; ---- 总存款:2000

            -------------------------

            c:

            start transaction;

            insert into account values(null,'c',1000);

            commit;

            -------------------------

            select count(*) from account; ---- 总账户数:3

            7、事务隔离性的设置语句

            数据库共定义了四种隔离级别:

            Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)

            Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)不可以避免虚读

            Read committed:可避免脏读情况发生(读已提交)

            Read uncommitted:最低级别,以上情况均无法保证。(读未提交)

            set [global/session]  transaction isolation level 设置事务隔离级别

            select @@tx_isolation查询当前事务隔离级别

            安全性来说:Serializable>Repeatable read>Read committed>Read uncommitted

            效率来说:Serializable<Repeatable read<Read committed<Read uncommitted

            通常来说,一般的应用都会选择Repeatable read或Read committed作为数据库隔离级别来使用。

            mysql默认的数据库隔离级别为:REPEATABLE-READ

            如何查询当前数据库的隔离级别?select @@tx_isolation;

            如何设置当前数据库的隔离级别?set [global/session] transaction isolation level ...;

            ~此种方式设置的隔离级别只对当前连接起作用。

            set transaction isolation level read uncommitted;

            set session transaction isolation level read uncommitted;

            ~此种方式设置的隔离级别是设置数据库默认的隔离级别

            set global transaction isolation level read uncommitted;

            8、事务的丢失更新问题(lost update )

            (1)两个或多个事务更新同一行,但这些事务彼此之间都不知道其它事务进行的修改,因此第二个更改覆盖了第一个修改 

            (2)共享锁:共享锁和共享锁可以共存。共享锁和排他锁不能共存。在Serializable隔离级别下一个事务进行查询操作将会加上共享锁。

            (3)排他锁:排他锁和所有锁都不能共存。无论什么隔离级别执行增删改操作时,会加上排他锁

            (4).数据库设计为Serializable隔离级别,就可以防止更新丢失问题。

             乐观锁和悲观锁并不是数据库中真实存在的锁,是我们如何利用共享和排他锁解决更新丢失问题的两种解决方案,体现了人们看待事务的态度:

            悲观锁:悲观的认为大部分情况下进行操作都会出现更新丢失问题。

            在每次进行查询的时候,都手动的加上一个排他锁。

            select * from table lock in share mode(读锁、共享锁)

                        select * from table for update (写锁、排它锁)

            乐观锁:乐观的认为大部分的情况下都不会有更新丢失问题。通过时间戳字段,

            在表中设计一个版本字段version,当每次对数据库中的数据进行修改操作时,版本号都要进行增加。

            (5)如果我的程序修改比较少查询比较多:乐观锁

            如果我的程序查询比较少修改比较多:悲观锁

  • 相关阅读:
    java生成压缩文件
    设计模式读书笔记-----外观模式
    设计模式读书笔记-----适配器模式
    设计模式读书笔记-----命令模式
    SpringMVC学习笔记七:SpringMVC统一异常处理
    SpringMVC学习笔记六:使用 hibernate-validator注解式数据校验
    SpringMVC学习笔记六:使用Formatter解析或格式化数据
    SpringMVC学习笔记五:使用converter进行参数数据转换
    SpringMVC学习笔记二:常用注解
    SpringMVC学习笔记四:数据绑定
  • 原文地址:https://www.cnblogs.com/panxuejun/p/5971230.html
Copyright © 2020-2023  润新知