事務管理入門-JDBC/Hibernate事務管理器/Spring註解 3種方式

http://coderdream.iteye.com/blog/434561

在軟件開發過程中,經常會遇到事務問題,下面我們來看看最簡單的JDBC和Spring分別如何處理事務。

 

關於事務控制的場景當然是轉賬,我們使用的數據庫是MySQL。

打開test數據庫後,運行下面的數據庫腳本:

Sql代碼  收藏代碼
  1. DROP TABLE IF EXISTS account;  
  2. CREATE TABLE account (  
  3. accountId int primary key auto_increment,  
  4. accountname varchar(20),  
  5. money int not null  
  6. );  
  7.   
  8. INSERT INTO ACCOUNT(ACCOUNTNAME,MONEY) VALUES('zhangsan',100);  
  9. INSERT INTO ACCOUNT(ACCOUNTNAME,MONEY) VALUES('lisi',100);  
 

1、JDBC中的事務控制

 

代碼1:AccountDAO.java

Java代碼  收藏代碼
  1. package com.coderdream;  
  2.   
  3. import java.sql.Connection;  
  4. import java.sql.DriverManager;  
  5. import java.sql.ResultSet;  
  6. import java.sql.SQLException;  
  7. import java.sql.Statement;  
  8.   
  9. public class AccountDAO {  
  10.   
  11.     public void transfer(Integer fromAccountId, Integer toAccountId, int money) {  
  12.         try {  
  13.             // 1. 註冊驅動  
  14.             Class.forName("com.mysql.jdbc.Driver");  
  15.   
  16.             // 2. 獲取數據庫的連接  
  17.             Connection conn = DriverManager.getConnection(  
  18.                     "jdbc:mysql://localhost/test""root""1234");  
  19.   
  20.             // 3. 獲取表達式  
  21.             Statement stmt1 = conn.createStatement();  
  22.             Statement stmt2 = conn.createStatement();  
  23.             Statement stmt3 = conn.createStatement();  
  24.             Statement stmt4 = conn.createStatement();  
  25.   
  26.             // 執行插入數據的 SQL  
  27.             ResultSet rs1 = stmt1  
  28.                     .executeQuery("SELECT MONEY FROM ACCOUNT WHERE ACCOUNTID="  
  29.                             + fromAccountId);  
  30.             // 5. 顯示結果集裏面的數據  
  31.             int money1 = 0;  
  32.             while (rs1.next()) {  
  33.                 System.out.println(rs1.getInt(1));  
  34.                 money1 = rs1.getInt(1);  
  35.             }  
  36.   
  37.             // 修改  
  38.             money1 -= money;  
  39.             System.out.println("money1: " + money1);  
  40.             stmt2.executeUpdate("UPDATE ACCOUNT SET MONEY=" + money1  
  41.                     + " WHERE ACCOUNTID=" + fromAccountId);  
  42.   
  43.             // 執行插入數據的 SQL  
  44.             ResultSet rs2 = stmt3  
  45.                     .executeQuery("SELECT MONEY FROM ACCOUNT WHERE ACCOUNTID="  
  46.                             + toAccountId);  
  47.             // 5. 顯示結果集裏面的數據  
  48.             int money2 = 0;  
  49.             while (rs2.next()) {  
  50.                 System.out.println(rs2.getInt(1));  
  51.                 money2 = rs2.getInt(1);  
  52.             }  
  53.   
  54.             // 修改  
  55.             money2 += money;  
  56.             System.out.println("money2: " + money2);  
  57.             stmt2.executeUpdate("UPDATE ACCOUNT SET MONEY=" + money2  
  58.                     + " WHERE ACCOUNTID=" + toAccountId);  
  59.   
  60.             // 6. 釋放資源  
  61.             rs1.close();  
  62.             rs2.close();  
  63.             stmt1.close();  
  64.             stmt2.close();  
  65.             stmt3.close();  
  66.             stmt4.close();  
  67.             conn.close();  
  68.         } catch (ClassNotFoundException e) {  
  69.             e.printStackTrace();  
  70.         } catch (SQLException e) {  
  71.             e.printStackTrace();  
  72.         }  
  73.   
  74.     }  
  75. }  
 

代碼2:AccountService.java

Java代碼  收藏代碼
  1. package com.coderdream;  
  2.   
  3. public class AccountService {  
  4.   
  5.     private AccountDAO accountDAO;  
  6.   
  7.     /** 
  8.      * 通過 Spring 向 Service ͨszh注入 Dao 
  9.      *  
  10.      * @param accountDAO 
  11.      */  
  12.     public void setAccountDAO(AccountDAO accountDAO) {  
  13.         this.accountDAO = accountDAO;  
  14.     }  
  15.   
  16.     /** 
  17.      * 轉賬 
  18.      *  
  19.      * @param fromAccountId 
  20.      *            轉出帳號 
  21.      * @param toAccountId 
  22.      *            轉入帳號 
  23.      * @param money 
  24.      *            轉賬金額 
  25.      */  
  26.     public void transfer(Integer fromAccountId, Integer toAccountId, int money) {  
  27.         accountDAO.transfer(fromAccountId, toAccountId, money);  
  28.     }  
  29. }  

 

代碼3:Main.java

Java代碼  收藏代碼
  1. package com.coderdream;  
  2.   
  3. import org.springframework.context.ApplicationContext;  
  4. import org.springframework.context.support.FileSystemXmlApplicationContext;  
  5.   
  6. public class Main {  
  7.   
  8.     /** 
  9.      * @param args 
  10.      */  
  11.     public static void main(String[] args) {  
  12.   
  13.         ApplicationContext act = new FileSystemXmlApplicationContext(  
  14.                 "src/applicationContext.xml");  
  15.         AccountService accountService = (AccountService) act  
  16.                 .getBean("accountService");  
  17.         try {  
  18.             // 帳號1轉賬1元至帳號2  
  19.             accountService.transfer(121);//A  
  20.                } catch (Exception e) {  
  21.             System.out.println("轉賬失敗!");  
  22.         }  
  23.     }  
  24. }  
 

代碼4:applicationContext.xml

Xml代碼  收藏代碼
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans   
  5.     http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">  
  6.   
  7.     <bean id="accountDAO" class="com.coderdream.AccountDAO" />  
  8.       
  9.     <bean id="accountService" class="com.coderdream.AccountService">  
  10.         <property name="accountDAO" ref="accountDAO"></property>  
  11.     </bean>  
  12. </beans>  

 

上面的代碼是沒有加事務控制的,如果把‘A’處的代碼換成:

Java代碼  收藏代碼
  1. // 帳號1轉賬1元至帳號3  
  2. accountService.transfer(131);//A  

 

則由於帳號3不存在,所以會出現問題,帳號1的金額會減1,而帳號2的金額不會變,轉賬的1元就“不翼而飛”了,所以必須在Dao層加入事務控制。

 

代碼5:加入事務控制後的AccountDAO

Java代碼  收藏代碼
  1. public class AccountDAO {  
  2.   
  3.     public void transfer(Integer fromAccountId, Integer toAccountId, int money) {  
  4.         Connection conn = null;  
  5.         ResultSet rs1 = null;  
  6.         Integer rs2 = null;  
  7.         ResultSet rs3 = null;  
  8.         Integer rs4 = null;  
  9.         Statement stmt1 = null;  
  10.         Statement stmt2 = null;  
  11.         Statement stmt3 = null;  
  12.         Statement stmt4 = null;  
  13.         // 1. 註冊驅動  
  14.         try {  
  15.             Class.forName("com.mysql.jdbc.Driver");  
  16.   
  17.             // 2. 獲取數據庫的連接  
  18.             conn = DriverManager.getConnection("jdbc:mysql://localhost/test",  
  19.                     "root""1234");  
  20.   
  21.             conn.setAutoCommit(false);  
  22.   
  23.             // 3. 獲取表達式  
  24.             stmt1 = conn.createStatement();  
  25.             stmt2 = conn.createStatement();  
  26.             stmt3 = conn.createStatement();  
  27.             stmt4 = conn.createStatement();  
  28.   
  29.             // 執行插入數據的 SQL  
  30.             rs1 = stmt1  
  31.                     .executeQuery("SELECT MONEY FROM ACCOUNT WHERE ACCOUNTID="  
  32.                             + fromAccountId);  
  33.             // 5. 顯示結果集裏面的數據  
  34.             int money1 = 0;  
  35.             while (rs1.next()) {  
  36.                 System.out.println(rs1.getInt(1));  
  37.                 money1 = rs1.getInt(1);  
  38.             }  
  39.   
  40.             // 修改  
  41.             money1 -= money;  
  42.             System.out.println("money1: " + money1);  
  43.             rs2 = stmt2.executeUpdate("UPDATE ACCOUNT SET MONEY=" + money1  
  44.                     + " WHERE ACCOUNTID=" + fromAccountId);  
  45.             if (1 != rs2) {  
  46.                 throw new Exception(" 轉出失敗,帳號: " + fromAccountId);  
  47.             }  
  48.   
  49.             // 執行插入數據的 SQL  
  50.             rs3 = stmt3  
  51.                     .executeQuery("SELECT MONEY FROM ACCOUNT WHERE ACCOUNTID="  
  52.                             + toAccountId);  
  53.             // 5. 顯示結果集裏面的數據  
  54.             int money2 = 0;  
  55.             while (rs3.next()) {  
  56.                 System.out.println(rs3.getInt(1));  
  57.                 money2 = rs3.getInt(1);  
  58.             }  
  59.   
  60.             // 修改  
  61.             money2 += money;  
  62.             System.out.println("money2: " + money2);  
  63.             rs4 = stmt2.executeUpdate("UPDATE ACCOUNT SET MONEY=" + money2  
  64.                     + " WHERE ACCOUNTID=" + toAccountId);  
  65.             if (1 != rs4) {  
  66.                 throw new Exception(" 轉入失敗,帳號: " + toAccountId);  
  67.             }  
  68.   
  69.             conn.commit();  
  70.             System.out.println("轉帳成功!");  
  71.         } catch (Exception e) {  
  72.             try {  
  73.                 conn.rollback();  
  74.             } catch (Exception e1) {  
  75.                 e1.printStackTrace();  
  76.             }  
  77.             e.printStackTrace();  
  78.         }  
  79.         // 6. 釋放資源  
  80.         finally {  
  81.             try {  
  82.                 if (rs1 != null) {  
  83.                     rs1.close();  
  84.                 }  
  85.                 if (rs3 != null) {  
  86.                     rs3.close();  
  87.                 }  
  88.                 if (stmt1 != null) {  
  89.                     stmt1.close();  
  90.                 }  
  91.                 if (stmt2 != null) {  
  92.                     stmt2.close();  
  93.                 }  
  94.                 if (stmt3 != null) {  
  95.                     stmt3.close();  
  96.                 }  
  97.                 if (stmt4 != null) {  
  98.                     stmt4.close();  
  99.                 }  
  100.                 if (conn != null) {  
  101.                     conn.close();  
  102.                 }  
  103.             } catch (SQLException e) {  
  104.                 e.printStackTrace();  
  105.             }  
  106.         }  
  107.     }  
  108. }  
 

2、Spring中的事務控制方式一:Hibernate的事務管理器託管

我們先來看看通過Spring+Hibernate來操作數據庫的簡單示例。

先通過 MyEclipse 生成Hibernate 需要的 Bean 及 hbm.xml文件:

 

代碼6:Account.java

Java代碼  收藏代碼
  1. package com.coderdream;  
  2.   
  3. /** 
  4.  * Account entity. 
  5.  *  
  6.  * @author MyEclipse Persistence Tools 
  7.  */  
  8. public class Account implements java.io.Serializable {  
  9.   
  10.     /** 
  11.      *  
  12.      */  
  13.     private static final long serialVersionUID = 909891879728703117L;  
  14.   
  15.     private Integer accountId;  
  16.     private String accountname;  
  17.     private Integer money;  
  18.   
  19.     // Property accessors  
  20.   
  21.     public Integer getAccountId() {  
  22.         return this.accountId;  
  23.     }  
  24.   
  25.     public void setAccountId(Integer accountId) {  
  26.         this.accountId = accountId;  
  27.     }  
  28.   
  29.     public String getAccountname() {  
  30.         return this.accountname;  
  31.     }  
  32.   
  33.     public void setAccountname(String accountname) {  
  34.         this.accountname = accountname;  
  35.     }  
  36.   
  37.     public Integer getMoney() {  
  38.         return this.money;  
  39.     }  
  40.   
  41.     public void setMoney(Integer money) {  
  42.         this.money = money;  
  43.     }  
  44.   
  45.     // Constructors  
  46.   
  47.     /** default constructor */  
  48.     public Account() {  
  49.     }  
  50.   
  51.     /** full constructor */  
  52.     public Account(String accountname, Integer money) {  
  53.         this.accountname = accountname;  
  54.         this.money = money;  
  55.     }  
  56.   
  57. }  
 

代碼7:Account.hbm.xml

Xml代碼  收藏代碼
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
  4. <!--  
  5.     Mapping file autogenerated by MyEclipse Persistence Tools 
  6. -->  
  7. <hibernate-mapping>  
  8.     <class name="com.coderdream.Account" table="account" catalog="test">  
  9.         <id name="accountId" type="java.lang.Integer">  
  10.             <column name="accountId" />  
  11.             <generator class="native" />  
  12.         </id>  
  13.         <property name="accountname" type="java.lang.String">  
  14.             <column name="accountname" length="20" />  
  15.         </property>  
  16.         <property name="money" type="java.lang.Integer">  
  17.             <column name="money" length="20" />  
  18.         </property>  
  19.     </class>  
  20. </hibernate-mapping>  
 

代碼8:

Java代碼  收藏代碼
  1. package com.coderdream;  
  2.   
  3. import org.springframework.orm.hibernate3.support.HibernateDaoSupport;  
  4.   
  5. public class AccountDAO extends HibernateDaoSupport {  
  6.   
  7.     public void addMoney(Integer accountId, int money) {  
  8.         Account account = (Account) getHibernateTemplate().get(Account.class,  
  9.                 accountId);  
  10.         account.setMoney(account.getMoney() + money);  
  11.         getHibernateTemplate().saveOrUpdate(account);  
  12.     }  
  13.   
  14.     public void subMoney(Integer accountId, int money) {  
  15.         Account account = (Account) getHibernateTemplate().get(Account.class,  
  16.                 accountId);  
  17.         account.setMoney(account.getMoney() - money);  
  18.         getHibernateTemplate().saveOrUpdate(account);  
  19.     }  
  20. }  
 

代碼9:

Java代碼  收藏代碼
  1. package com.coderdream;  
  2.   
  3. public class AccountService {  
  4.   
  5.     private AccountDAO accountDAO;  
  6.   
  7.     /** 
  8.      * 通過 Spring 將 DAO 注入到 Service 
  9.      *  
  10.      * @param accountDAO 
  11.      */  
  12.     public void setAccountDAO(AccountDAO accountDAO) {  
  13.         this.accountDAO = accountDAO;  
  14.     }  
  15.   
  16.     /** 
  17.      * 轉賬方法包括兩個原子方法:轉出方法和轉入方法 
  18.      *  
  19.      * @param fromAccountId 
  20.      * @param toAccountId 
  21.      * @param money 
  22.      */  
  23.     public void transfer(Integer fromAccountId, Integer toAccountId, int money) {  
  24.         accountDAO.subMoney(fromAccountId, money);  
  25.         accountDAO.addMoney(toAccountId, money);  
  26.     }  
  27. }  
 

代碼10:

Java代碼  收藏代碼
  1. package com.coderdream;  
  2.   
  3. import org.springframework.context.ApplicationContext;  
  4. import org.springframework.context.support.FileSystemXmlApplicationContext;  
  5.   
  6. public class Main {  
  7.   
  8.     /** 
  9.      * @param args 
  10.      */  
  11.     public static void main(String[] args) {  
  12.   
  13.         ApplicationContext act = new FileSystemXmlApplicationContext(  
  14.                 "src/applicationContext.xml");  
  15.         AccountService accountService = (AccountService) act  
  16.                 .getBean("accountService");  
  17.         try {  
  18.             // 帳號1轉賬1元至帳號2  
  19.             accountService.transfer(121);// B  
  20.         } catch (Exception e) {  
  21.             System.out.println("轉賬失敗");  
  22.         }  
  23.     }  
  24. }  
 

上面的代碼同樣沒有加入事務控制,如果在‘B’處將轉入的帳號設置爲不存在的帳號3,同樣會有問題,下面我們來加入事務控制,我們需要修改 applicationContext.xml 文件:

 

代碼11:增加事務後的 applicationContext.xml

Java代碼  收藏代碼
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans   
  5.     http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">  
  6.   
  7.     <bean id="dataSource"  
  8.         class="org.apache.commons.dbcp.BasicDataSource">  
  9.         <property name="driverClassName"  
  10.             value="com.mysql.jdbc.Driver">  
  11.         </property>  
  12.         <property name="url" value="jdbc:mysql://localhost:3306/test"></property>  
  13.         <property name="username" value="root"></property>  
  14.         <property name="password" value="1234"></property>  
  15.     </bean>  
  16.   
  17.     <bean id="sessionFactory"  
  18.         class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
  19.         <property name="dataSource">  
  20.             <ref bean="dataSource" />  
  21.         </property>  
  22.         <property name="hibernateProperties">  
  23.             <props>  
  24.                 <prop key="hibernate.dialect">  
  25.                     org.hibernate.dialect.MySQLDialect  
  26.                 </prop>  
  27.                 <prop key="hibernate.show_sql">true</prop>  
  28.             </props>  
  29.         </property>  
  30.         <property name="mappingResources">  
  31.             <list>  
  32.                 <value>com/coderdream/Account.hbm.xml</value>  
  33.             </list>  
  34.         </property>  
  35.     </bean>  
  36.   
  37.     <!-- 引用Hibernate的事務管理器 -->  
  38.     <bean id="transactionManager"  
  39.         class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
  40.         <property name="sessionFactory" ref="sessionFactory"></property>  
  41.     </bean>  
  42.   
  43.     <bean id="accountDAO" class="com.coderdream.AccountDAO">  
  44.         <property name="sessionFactory" ref="sessionFactory"></property>  
  45.     </bean>  
  46.   
  47.     <!-- 通過事務管理器來管理Service -->  
  48.     <bean id="accountService"  
  49.         class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">  
  50.         <property name="transactionManager" ref="transactionManager"></property>  
  51.         <property name="target">  
  52.             <bean class="com.coderdream.AccountService">  
  53.                 <property name="accountDAO" ref="accountDAO"></property>  
  54.             </bean>  
  55.         </property>  
  56.         <property name="transactionAttributes">  
  57.             <props>  
  58.                 <prop key="transfer">PROPAGATION_REQUIRED</prop>  
  59.             </props>  
  60.         </property>  
  61.     </bean>  
  62. </beans>  
 

3、Spring中的事務控制方式二:註解方式

當然,我們還可以通過註解的方式加入事務的控制。

我們需要先在 applicationContext.xml 聲明事務控制器和註解:

 

代碼12:applicationContext.xml

Java代碼  收藏代碼
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xmlns:jee="http://www.springframework.org/schema/jee"  
  5.     xmlns:tx="http://www.springframework.org/schema/tx"  
  6.     xmlns:context="http://www.springframework.org/schema/context"  
  7.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">  
  8.   
  9.     <bean id="dataSource"  
  10.         class="org.apache.commons.dbcp.BasicDataSource">  
  11.         <property name="driverClassName"  
  12.             value="com.mysql.jdbc.Driver">  
  13.         </property>  
  14.         <property name="url" value="jdbc:mysql://localhost:3306/test"></property>  
  15.         <property name="username" value="root"></property>  
  16.         <property name="password" value="1234"></property>  
  17.     </bean>  
  18.   
  19.     <bean id="sessionFactory"  
  20.         class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
  21.         <property name="dataSource">  
  22.             <ref bean="dataSource" />  
  23.         </property>  
  24.         <property name="hibernateProperties">  
  25.             <props>  
  26.                 <prop key="hibernate.dialect">  
  27.                     org.hibernate.dialect.MySQLDialect  
  28.                 </prop>  
  29.                 <prop key="hibernate.show_sql">true</prop>  
  30.             </props>  
  31.         </property>  
  32.         <property name="mappingResources">  
  33.             <list>  
  34.                 <value>com/coderdream/Account.hbm.xml</value>  
  35.             </list>  
  36.         </property>  
  37.     </bean>  
  38.   
  39.     <!-- 引用Hibernate的事務管理器 -->  
  40.     <bean id="transactionManager"  
  41.         class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
  42.         <property name="sessionFactory" ref="sessionFactory"></property>  
  43.     </bean>  
  44.   
  45.     <!-- 使用annotation定義事務 -->  
  46.     <tx:annotation-driven transaction-manager="transactionManager" />  
  47.   
  48.     <bean id="accountDAO" class="com.coderdream.AccountDAO">  
  49.         <property name="sessionFactory" ref="sessionFactory"></property>  
  50.     </bean>  
  51.   
  52.     <bean id="accountService" class="com.coderdream.AccountService">  
  53.         <property name="accountDAO" ref="accountDAO"></property>  
  54.     </bean>  
  55. </beans>  
 

同時需要在Service層的要使用的方法上聲明事務,如:

Java代碼  收藏代碼
  1. @Transactional(readOnly = false)  

 如果只是對數據庫進行查詢操作,這裏的“readOnly = true“,如果是”增/刪/改“操作,則爲 false。

 

代碼13:在方法上加入“註解式”事務控制後的 AccountService.java

Java代碼  收藏代碼
  1. package com.coderdream;  
  2.   
  3. public class AccountService {  
  4.   
  5.     private AccountDAO accountDAO;  
  6.   
  7.     /** 
  8.      * 通過 Spring 將 DAO 注入到 Service 
  9.      *  
  10.      * @param accountDAO 
  11.      */  
  12.     public void setAccountDAO(AccountDAO accountDAO) {  
  13.         this.accountDAO = accountDAO;  
  14.     }  
  15.   
  16.     /** 
  17.      * 轉賬方法包括兩個原子方法:轉出方法和轉入方法 
  18.      *  
  19.      * @param fromAccountId 
  20.      * @param toAccountId 
  21.      * @param money 
  22.      */  
  23.     @Transactional(readOnly = false)  
  24.     public void transfer(Integer fromAccountId, Integer toAccountId, int money) {  
  25.         accountDAO.subMoney(fromAccountId, money);  
  26.         accountDAO.addMoney(toAccountId, money);  
  27.     }  
  28. }  

 


發佈了24 篇原創文章 · 獲贊 5 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章