7、spring與hibernate的集成(spring筆記)

一、spring和Hibernate的集成(編程式事務)(工程spring_hibernate_1

1.1 openSession

就是session = HibernateUtils.getSession();獲得的session。如果有多個類使用該方法獲得session,那么將會有多個session。

1.2 currentSession

就是session = HibernateUtils.getSessionFactory().getCurrentSession();獲得當前線程的session。這樣如果有多個類,就可以使用此方法獲得session達到同步的效果。

1.3 openSession和currentSession的區別

  • openSession必須關閉,而currentSession在事務結束后自動關閉
  • openSession沒有和當前線程綁定,currentSession和當前線程綁定

1.4 如果使用currentSession需要在hibernate.cfg.xml文件中進行配置

  • 如果是本地事務(jdbc事務)
    <property name="hibernate.current_session_context_class">thread</property>

  • 如果是全局事務(jta事務)
    <property name="hibernate.current_session_context_class">jta</property>

1.5 示例

實體類:
User.java

private int id;
private String name;

Log.java

private int id; 
//操作日志、安全日志、事件日志
private String type;
private String detail;
private Date time;

配置文件:
User.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.bjsxt.usermgr.model">
    <class name="User" table="t_user">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name"/>
    </class>
</hibernate-mapping>

Log.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.bjsxt.usermgr.model">
    <class name="Log" table="t_log">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="type"/>
        <property name="detail"/>
        <property name="time"/>
    </class>
</hibernate-mapping>

UserManagerImpl.java

package com.bjsxt.usermgr.manager;
import java.util.Date;
import org.hibernate.Session;
import com.bjsxt.usermgr.model.Log;
import com.bjsxt.usermgr.model.User;
import com.bjsxt.usermgr.util.HibernateUtils;

public class UserManagerImpl implements UserManager {

    public void addUser(User user) {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            //session = HibernateUtils.getSessionFactory().getCurrentSession();
            session.beginTransaction();
            
            session.save(user);

            Integer.parseInt("asdfsdfsfsd");
            Log log = new Log();
            log.setType("安全日志");
            log.setDetail("xxx進入系統");
            log.setTime(new Date());

            LogManager logManager = new LogManagerImpl();
            logManager.addLog(log);

            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }
}

說明:

  • 1.上面我們使用的是傳統方式,當我們存入用戶之后再想要存入日志的時候,使用addLog(log);方法進行保存日志,但是在保存日志的時候我們需要一個session,同時我們注意到這兩個保存操作必須使用同一個session,當然我們可以將session傳遞到日志記錄的業務類中,但是這樣hibernate就侵入到程序中了,在JavaEE中我們是使用的ThreadLocal類來保證這兩個操作使用的是同一個session,那么在hibernate中我們可以使用currentSession,這個類就可以達到同步的效果。

  • 2.在程序中我們這樣得到currentSession:

session = HibernateUtils.getSessionFactory().getCurrentSession();

同時我們此時就不需要顯示關閉session了,同時在日志記錄業務類中我們也可以使用同樣的方法獲得當前線程中的session,實現同步。
LogManagerImpl.java

package com.bjsxt.usermgr.manager;
import com.bjsxt.usermgr.model.Log;
import com.bjsxt.usermgr.util.HibernateUtils;
public class LogManagerImpl implements LogManager {

    public void addLog(Log log) {
        HibernateUtils.getSessionFactory().getCurrentSession().save(log);
    }
}
  • 3.使用currentSession我們需要在hibernate的配置文件中進行配置:
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.url">jdbc:mysql://localhost/spring_hibernate_1</property>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">walp1314</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="hibernate.show_sql">true</property>
        
        <property name="hibernate.current_session_context_class">thread</property>
        <!-- 
        <property name="hibernate.current_session_context_class">jta</property>
         -->        
        <mapping resource="com/bjsxt/usermgr/model/User.hbm.xml"/>
        <mapping resource="com/bjsxt/usermgr/model/Log.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

可以看到我們需要根據事務的類型配置currentSession,所謂本地事務,就是基于JDBC的事務,可以理解為ThreadLocal,或者叫針對一個數據庫的事務;而全局事務可以理解為針對多個數據庫的事務。

注意:本例子是模擬我們進行某種操作時,日志系統進行相應的記錄工作,這兩個工作必須是同步的。

二、spring和Hibernate的集成(采用聲明式事務)(工程spring_hibernate_2

2.1 spring事務的傳播特性

  • <1>PROPAGATION_REQUIRED 如果存在一個事務,則支持當前事務。如果沒有事務則開啟,一般使用此傳播特性。

  • <2>PROPAGATION_SUPPORTS 如果存在一個事務,支持當前事務。如果沒有事務,則非事務的執行,即不用事務。

  • <3>PROPAGATION_MANDATORY 如果已經存在一個事務,支持當前事務。如果沒有一個活動的事務,則拋出異常。

  • <4>PROPAGATION_REQUIRES_NEW 總是開啟一個新的事務。如果一個事務已經存在,則將這個存在的事務掛起。

  • <5>PROPAGATION_NOT_SUPPORTED 總是非事務地執行,并掛起任何存在的事務。

  • <6>PROPAGATION_NEVER 總是非事務地執行,如果存在一個活動事務,則拋出異常。

  • <7>PROPAGATION_NESTED 如果一個活動的事務存在,則運行在一個嵌套的事務中. 如果沒有活動事務, 則按TransactionDefinition.PROPAGATION_REQUIRED屬性執行(spring特有的

2.2 spring的隔離級別

  • <1>ISOLATION_DEFAULT 這是一個PlatfromTransactionManager默認的隔離級別,使用數據庫默認的事務隔離級別。另外四個與JDBC的隔離級別相對應。

  • <2>ISOLATION_READ_UNCOMMITTED 這是事務最低的隔離級別,它充許令外一個事務可以看到這個事務未提交的數據。這種隔離級別會產生臟讀,不可重復讀和幻像讀。

  • <3>ISOLATION_READ_COMMITTED 保證一個事務修改的數據提交后才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的數據。

  • <4>ISOLATION_REPEATABLE_READ 這種事務隔離級別可以防止臟讀,不可重復讀。但是可能出現幻像讀。它除了保證一個事務不能讀取另一個事務未提交的數據外,還保證了避免下面的情況產生(不可重復讀)。

  • <5>ISOLATION_SERIALIZABLE 這是花費最高代價但是最可靠的事務隔離級別。事務被處理為順序執行。除了防止臟讀,不可重復讀外,還避免了幻像讀。

2.3 示例

這里對于上面的示例我們不實用編程式事務了,而是交給spring進行管理。首先需要配置sessionFactory、事務管理器、事務的傳播特性和哪些類的哪些方法參與事務,而這些配置我們一般放在一個公共的配置文件中:
applicationContext-common.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:aop="http://www.springframework.org/schema/aop"
         xmlns:tx="http://www.springframework.org/schema/tx"
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
    <!-- 配置sessionFactory --><!-- 將相關配置注入到spring中 -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="configLocation">
            <value>classpath:hibernate.cfg.xml</value>
        </property> 
    </bean>           
    
    <!-- 配置事務管理器 --><!-- 將sessionFactory注入到spring的事務管理器中 -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property> 
    </bean>
    
    <!-- 配置事務的傳播特性 --><!-- 首先我們需要注入事務管理器 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED"/>
            <tx:method name="del*" propagation="REQUIRED"/>
            <tx:method name="modify*" propagation="REQUIRED"/>
            <tx:method name="*" read-only="true"/><!-- 其他方法設置為只讀,可以提高效率 -->
        </tx:attributes>
    </tx:advice>
    
    <!-- 哪些類的哪些方法參與事務 -->
    <aop:config>
        <aop:pointcut id="allManagerMethod" expression="execution(* com.bjsxt.usermgr.manager.*.*(..))"/>
        <aop:advisor pointcut-ref="allManagerMethod" advice-ref="txAdvice"/>
    </aop:config>
</beans>

說明:對于將hibernate的配置文件注入到spring中時的classpath是spring實現的一個協議,讓spring可以找到hibernate的配置文件,于是我們hibernate的配置文件也可以不用默認的名字。

對于需要注入sessionFactory的業務類我們可以單獨在一個配置文件中進行配置:
applicationContext-beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:aop="http://www.springframework.org/schema/aop"
         xmlns:tx="http://www.springframework.org/schema/tx"
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
    
    <bean id="userManager" class="com.bjsxt.usermgr.manager.UserManagerImpl">
        <property name="sessionFactory" ref="sessionFactory"/>
        <property name="logManager" ref="logManager"/>
    </bean>
    
    <bean id="logManager" class="com.bjsxt.usermgr.manager.LogManagerImpl">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
</beans>

hibernate.cfg.xml

<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
    <property name="hibernate.connection.url">jdbc:mysql://localhost/spring_hibernate_2</property>
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.connection.password">walp1314</property>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="hibernate.show_sql">true</property>
    <property name="hibernate.current_session_context_class">thread</property>
    <!-- 
        <property name="hibernate.current_session_context_class">jta</property>
    -->
    <property name="dialect"></property>
    <mapping resource="com/bjsxt/usermgr/model/User.hbm.xml" />
    <mapping resource="com/bjsxt/usermgr/model/Log.hbm.xml" />
</session-factory>
</hibernate-configuration>

此時我們在業務類中使用的時候就需要繼承spring的HibernateDaoSupport類:
UserManagerImpl.java

package com.bjsxt.usermgr.manager;
import java.util.Date;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import com.bjsxt.usermgr.model.Log;
import com.bjsxt.usermgr.model.User;
import com.bjsxt.usermgr.util.HibernateUtils;

public class UserManagerImpl extends HibernateDaoSupport implements UserManager {
    
    private LogManager logManager;
    
    public void addUser(User user) throws Exception {
        this.getHibernateTemplate().save(user);
        Log log = new Log();
        log.setType("安全日志");
        log.setDetail("xxx進入系統");
        log.setTime(new Date());
        logManager.addLog(log);
        throw new Exception();
    }
    
    public void setLogManager(LogManager logManager) {
        this.logManager = logManager;
    }
}

LogManagerImpl.java

package com.bjsxt.usermgr.manager;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import com.bjsxt.usermgr.model.Log;
import com.bjsxt.usermgr.util.HibernateUtils;

public class LogManagerImpl extends HibernateDaoSupport implements LogManager {

    public void addLog(Log log) {
        this.getHibernateTemplate().save(log);
    }
}

測試:Client.java

package com.bjsxt.usermgr.client;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.bjsxt.usermgr.manager.UserManager;
import com.bjsxt.usermgr.manager.UserManagerImpl;
import com.bjsxt.usermgr.model.User;

public class Client {

    public static void main(String[] args) {
        User user = new User();
        user.setName("張三");
        
        BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext-*.xml");
        UserManager userManager = (UserManager)factory.getBean("userManager");
        try {
            userManager.addUser(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

說明:在實際開發中我們一般使用注解的方式,這在筆記(spring+struts2+Hibernate+maven+EasyUI開發環境搭建)中已經說明了,這里不細說了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容