1. 程式人生 > >【Hibernate系列】(二):事務的應用

【Hibernate系列】(二):事務的應用

需求

  新增訂單成功後,需要將購物車中的商品清空,這兩個操作要麼都執行,要麼都不執行。這時,就用到了事務。

使用

1.配置

在hibernate.cfg.xml中新增

<!--使用currentSession配置事務-->
    <property name="hibernate.current_session_context_class">thread</property>

2.開啟,關閉,回滾

    /** 新增訂單資訊 **/
    public Boolean addOrders(SchoolShop_Orders orders,int
[] cids) { Boolean flag = true; Session session=getSession(); try { //開啟事務 session.beginTransaction(); //新增訂單 session.save(orders); //清空購物項 deleteCarts(cids,session); //提交事務 session.getTransaction().commit(); } catch
(Exception e) { flag = false; e.printStackTrace(); //回滾事務 session.getTransaction().rollback(); } return flag; } /** * 批量刪除購物項 * @param cids * @param session * @return */ public Boolean deleteCarts
(int[] cids,Session session) { int num=0; String hql = ""; for(int i=0;i<cids.length;i++) { if(i==0) { hql = "cid="+cids[i]; } else { hql =hql + " or cid="+cids[i]; } } Query q= session.createQuery("delete from SchoolShop_cart where "+hql); num=q.executeUpdate(); if(num>0){ return true; }else{ return false; } }

3.getSession方法

@Transactional
@SuppressWarnings("unchecked")
public class DaoSupportImpl<T> implements DaoSupport<T> {

    @Resource
    private SessionFactory sessionFactory;
    private Class<T> clazz;

    public DaoSupportImpl() {
        // 使用反射技術得到T的真實型別
        ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass(); // 獲取當前new的物件的 泛型的父類 型別
        this.clazz = (Class<T>) pt.getActualTypeArguments()[0]; // 獲取第一個型別引數的真實型別
        System.out.println("clazz ---> " + clazz);
    }
    /**
     * 獲取當前可用的Session
     * 
     * @return
     */
    protected Session getSession() {
        return sessionFactory.getCurrentSession();
    }

}

問題

【錯誤】:WARN JDBCExceptionReporter:233 - SQL Error: 1205, SQLState: 41000
org.hibernate.exception.GenericJDBCException: could not execute update query16:05:37,361

【原因】:鎖等待超時
【解決】:事務範圍內,出現異常時,事務提交不成功,沒有回滾事務,所以必須加上session.getTransaction().rollback();

溫故

  看看在不使用Hibernate框架的時候如何使用事務:

public void addFlowCard(FlowCard flowCard) throws ApplicationException {
        Connection conn=null;
        try{
            conn = ConnectionManager.getConnection();
            //開始事務
            ConnectionManager.beginTransaction(conn);

            String flowCardVouNo=flowCardDao.generateVouNo();
            flowCardDao.addFlowCardMaster(flowCardVouNo, flowCard);
            flowCardDao.addFlowCardDetail(flowCardVouNo, flowCard.getFlowCardDetailList());
            //提交事務
            ConnectionManager.commitTransaction(conn);
        }catch(DaoException e){
            //回滾事務
            ConnectionManager.rollbackTransaction(conn);
            throw new ApplicationException("新增流向單失敗!");
        }finally{
            //關閉Connection並從ThreadLocal中清除
            ConnectionManager.closeConnection();
        }
    }

知新

對比使用Hibernate框架和不使用框架的情況下,事務的使用方法有何不同。發現,基本步驟沒有變(開啟事務,提交事務,回滾事務)。不同的是:


  1. 獲取連線物件的方式
不使用Hibernate的時候,藉助ThreadLocal獲得執行緒內的Connection物件:
package com.tgb.drp.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class ConnectionManager {
    private static ThreadLocal<Connection> connectionHolder=new ThreadLocal<Connection>();

    /**
     * 獲得Connection
     * @return
     */
    public static Connection getConnection(){

        Connection conn=connectionHolder.get(); 
        if(conn==null){
            try{
                JdbcConfig jdbcConfig=XmlConfigReader.getInstance().getJdbcConfig();            
                Class.forName(jdbcConfig.getDriverName());  
                conn= DriverManager.getConnection(jdbcConfig.getUrl(),jdbcConfig.getUsername(),jdbcConfig.getPassword()) ;
                connectionHolder.set(conn);
            }catch (ClassNotFoundException e){
                e.printStackTrace();
                throw new ApplicationException("系統錯誤,請聯絡管理員!");
            } catch (SQLException e) {

                e.printStackTrace();
                throw new ApplicationException("系統錯誤,請聯絡管理員!");
            }
        }
        return conn;
    }
    /**
     * 關閉Connection
     */
    public static void closeConnection(){
        Connection conn = connectionHolder.get();
        if (conn != null) {
            try {
                conn.close();
                //從ThreadLocal中清除Connection
                connectionHolder.remove();
            } catch (SQLException e) {
                e.printStackTrace();
            }   
        }
    }
    /**
     * 開始事務
     * @param conn
     */
    public static void beginTransaction(Connection conn) {
        try {
            if (conn != null) {
                if (conn.getAutoCommit()) {
                    conn.setAutoCommit(false); //手動提交
                }
            }
        }catch(SQLException e) {}
    }
    /**
     * 提交事務
     * @param conn
     */
    public static void commitTransaction(Connection conn) {
        try {
            if (conn != null) {
                if (!conn.getAutoCommit()) {
                    conn.commit();
                }
            }
        }catch(SQLException e) {}
    }

    /**
     * 回滾事務
     * @param conn
     */
    public static void rollbackTransaction(Connection conn) {
        try {
            if (conn != null) {
                if (!conn.getAutoCommit()) {
                    conn.rollback();
                }
            }
        }catch(SQLException e) {}
    }
    /**
     * 關閉命令
     * @param pstmt
     */
    public static void close(Statement pstmt) {
        if (pstmt != null) {
            try {
                pstmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static void close(ResultSet rs ) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

 2.是否顯示的開啟和關閉連線物件

使用Hibernate的currensession時,並不需要我們自己去開啟或關閉物件。

  所以使用Hibernate框架的好處在於框架幫我們封裝好了一些東西,讓我們儘可能少的關心資料庫,減少重複程式碼的編寫。