1. 程式人生 > >spring hibernate 整合:spring使用容器JTA事務管理器

spring hibernate 整合:spring使用容器JTA事務管理器

說明:

1.EJB的事務是方法級別的隔離,而spring的攔截事務是類級別的,所以spring管理的jta事務在同一個類的不同方法設定不同的事務傳播策略是無效的。
2.使用了spring的事務,就不能顯示地使用hibernate的開啟事務,提交事務等,spring通過攔截器自動實現這些操作。

正文開始:

spring hibernate 整合,hibernate操作多個數據庫需要 jta 事務管理器,spring提供了對容器jta事務管理器的支援,同時hibernate也提供了對容器jta事務管理器的支援,注意兩者不能同時使用,只能選擇其一來管理事務,如果要使用spring則可以通過配置事務代理或者攔截器的方式實現,如果要使用hibernate則可以配置session工廠使用jta事務管理器同事編碼過程中要顯示開啟事務/提交或回滾事務,顯然使用spring管理事務更加優越:1.宣告式事務管理,對程式碼無侵入;2.攔截器使用aop的方式,可以批量對多個業務服務bean植入事務支援,下面是一個完整的使用spring管理事務的例子:

專案的pom檔案:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <parent>
        <artifactId>eden</artifactId>
        <groupId>com.conquer.eden</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>springtx</artifactId>
    <packaging>war</packaging>
    <name>springtx</name>
    <properties>
        <springVersion>2.0.1</springVersion>
    </properties>
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.transaction</groupId>
            <artifactId>javax.transaction-api</artifactId>
            <version>1.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring</artifactId>
            <version>2.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate</artifactId>
            <version>3.1.3</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.transaction</groupId>
                    <artifactId>jta</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <build>
        <finalName>springtx</finalName>
    </build>
</project>

注意這裡:由於web層沒有使用spring管理,所以這裡沒有配置spring的請求轉發器,所以需要手工使用spring的api來初始化spring容器,web.xml程式碼:
<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <servlet>
        <servlet-name>test</servlet-name>
        <servlet-class>com.test.TestServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>test</servlet-name>
        <url-pattern>/test</url-pattern>
    </servlet-mapping>
</web-app>
Servlet程式碼:
package com.test;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String data = req.getParameter("data");
        if (data == null) data = "00000";

        try {
            SpringTest.testSpring(data);
            PrintWriter writer = resp.getWriter();
            writer.println("ok, check the console output ...");
            writer.flush();
        } catch (Exception e) {
            throw new ServletException(e);
        }
    }
}

下面是spring事務測試的入口,由於沒有使用spring管理web,故在這裡進行spring容器的初始化:
package com.test;

import com.test.service.Service1;
import com.test.service.Service2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {
    private static Service1 service1;
    private static Service2 service2;

    static {
        /**
         * 測試使用:
         * 保證例項是在spring的管理範圍內,即在spring的IOC容器中,在spring mvc 的 web 架構中,我們不需要這樣顯示寫出來,注入是自動的
         * 這裡因為我們的 測試 Servlet 沒有被spring 管理,也就不能觸發spring的注入機制,所以這裡使用程式設計的方式測試。
         */
//    使用 代理bean方式 支援事務
//        ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring/*.xml", "spring/tx/tx_proxy.xml"});
//    使用 攔截bean方法方式(攔截器) 支援事務
        ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring/*.xml", "spring/tx/tx_interceptor.xml"});


        //直接引用,會導致事務不起作用,必需使用spring的事務代理類,注意:使用攔截器時使用這個,不需要改變,攔截器的實現會自動改變記憶體裡的實現
        //攔截器是推薦使用的事務支援方式,好處:
        // 一:不需要使用新的代理bean(不用擔心引用錯誤造成事務無效)
        // 二:可以批量配置bean的事務屬性(避免事務bean的配置膨脹)
        service1 = (Service1) context.getBean("service1");
        service2 = (Service2) context.getBean("service2");

        //使用spring的事務代理類,可使用事務支援
//        service1 = (Service1) context.getBean("myTxProxy1");
//        service2 = (Service2) context.getBean("myTxProxy2");
    }

    public static void testSpring(String data) {
        service1.twoTX(data);
    }
}
好了,現在看下spring需要的配置檔案:

base.xml:引用容器提供的jndi資源,主要是資料來源和JTA事務管理器

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
        "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    <!--通過JNDI查詢的物件,最終會強制轉為為需要的型別,如這個會強轉為 TransactionManager-->
    <bean id="twJtaTm" class="org.springframework.jndi.JndiObjectFactoryBean">
        <!--tw6-->
        <property name="jndiName" value="java:appserver/TransactionManager"/>
        <!--twe5-->
        <!--<property name="jndiName" value="java:internal/TransactionManager"/>-->
        <!--jboss-->
        <!--<property name="jndiName" value="java:/TransactionManager"/>-->
        <!--weblogic-->
        <!--<property name="jndiName" value="javax.transaction.TransactionManager"/>-->
        <!--<property name="resourceRef" value="true" />TODO-->
    </bean>
    <!--通過JNDI查詢的物件,最終會強制轉為為需要的型別,如這個會強轉為 DataSource-->
    <bean id="myDataSource1" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="DataSource1"/>
        <!--<property name="resourceRef" value="true" />TODO-->
    </bean>
    <!--通過JNDI查詢的物件,最終會強制轉為為需要的型別,如這個會強轉為 DataSource-->
    <bean id="myDataSource2" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="DataSource1"/>
        <!--<property name="resourceRef" value="true" />TODO-->
    </bean>
</beans>

business_beans.xml:業務bean的配置檔案:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
        "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    <!--              以下是具體業務相關              -->
    <!--業務類,需要事務配置的類,特別注意,業務中不要直接使用此例項,而應該使用其事務代理:basicTxProxy -->
    <!-- NOTE:非嵌入式的,命名的,此目標業務 bean 可被直接引用,直接引用將導致 無事務,所以應該使用代理事務bean的引用或使用事務攔截器來處理-->

    <bean id="baseDAO" abstract="true">
        <property name="sessionFactory1" ref="sessionFactory1"/>
        <property name="sessionFactory2" ref="sessionFactory2"/>
    </bean>

    <!--如果使用代理,直接引用這裡的bean會導致事務失效,應該引用其代理,而且事務應該加在 服務層(service)-->
    <bean id="dao1" parent="baseDAO" class="com.test.dao.impl.DAOImpl1"/>
    <bean id="dao2" parent="baseDAO" class="com.test.dao.impl.DAOImpl2"/>

    <!--事務應該加在 服務層(service):如果使用代理,直接引用這裡的bean會導致事務失效,應該引用其代理-->
    <bean id="service1" class="com.test.service.impl.Service1Impl">
        <property name="dao1" ref="dao1"/>
        <property name="dao2" ref="dao2"/>
        <!--如果使用代理,直接引用這裡的bean會導致事務失效,應該引用其代理,但是,如果使用的事務攔截器,這裡不需要變動,攔截器會自動改掉記憶體裡的實現-->
        <property name="service2" ref="service2"/>

        <!--引用事務代理代理,不使用攔截器時使用這個-->
        <!--<property name="service2" ref="myTxProxy2"/>-->
    </bean>
    <bean id="service2" class="com.test.service.impl.Service2Impl">
        <property name="dao2" ref="dao2"/>
    </bean>
</beans>

hibernate.xml:hibernate session工廠的配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
        "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    <!--spring 對 hibernate的支援,用於建立 hibernate的SessionFactory-->
    <bean id="sessionFactory1" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="hibernateProperties">
            <value>
                <!--這裡填寫hibernate特定屬性,注意不要在這裡寫 hibernate.transaction.factory_class 和 jta.UserTransaction,因為事務需要交給spring管理
                這裡寫了,只會對程式碼呼叫 org.hibernate.Session#getTransaction()方法起效,對於spring配置的事務策略無效,使用spring的事務會忽略此設定
                -->
                hibernate.dialect=org.hibernate.dialect.OracleDialect
                hibernate.show_sql=true
            </value>
        </property>
        <!--配置hibernate的對映檔案,這裡配置的是整個 hbm 資料夾下的檔案-->
        <property name="mappingDirectoryLocations">
            <list>
                <value>
                    classpath*:/hbm/
                </value>
            </list>
        </property>
        <!--JNDI 查詢到的 myDataSource1 是DataSource型別的,所以可以在這裡注入-->
        <property name="dataSource" ref="myDataSource1"/>
        <!--JNDI 查詢到的 twJtaTm 是TransactionManager型別的,所以可以在這裡注入 TODO-->
        <!--<property name="jtaTransactionManager" ref="twJtaTm"/>-->
    </bean>
    <!--spring 對 hibernate的支援,用於建立 hibernate的SessionFactory-->
    <bean id="sessionFactory2" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="hibernateProperties">
            <value>
                <!--這裡填寫hibernate特定屬性,注意不要在這裡寫 hibernate.transaction.factory_class 和 jta.UserTransaction,因為事務需要交給spring管理
                這裡寫了,只會對程式碼呼叫 org.hibernate.Session#getTransaction()方法起效,對於spring配置的事務策略無效,使用spring的事務會忽略此設定
                -->
                hibernate.dialect=org.hibernate.dialect.OracleDialect
                hibernate.show_sql=true
            </value>
        </property>
        <!--配置hibernate的對映檔案,這裡配置的是指定的檔案-->
        <property name="mappingResources">
            <list>
                <value>hbm/Student.hbm.xml</value>
                <value>hbm/Teacher.hbm.xml</value>
            </list>
        </property>
        <!--JNDI 查詢到的 myDataSource2 是DataSource型別的,所以可以在這裡注入-->
        <property name="dataSource" ref="myDataSource2"/>
        <!--JNDI 查詢到的 twJtaTm 是TransactionManager型別的,所以可以在這裡注入 TODO-->
        <!--<property name="jtaTransactionManager" ref="twJtaTm"/>-->
    </bean>
</beans>

tx_common.xml:事務公共配置,注意是一些公共bean
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
        "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    <!--
     要使用 JTA 事務管理,需要配置 JtaTransactionManager,他需要 TransactionManager 型別的例項,twJtaTm正好是一個
     它本身是 PlatformTransactionManager 的實現,可以配合完成 spring 的多種事務配置
    -->
    <bean id="mySpringTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager" ref="twJtaTm"/>
    </bean>
</beans>

下面就是重頭戲了,spring有至少兩種方式配置事務,代理bean和攔截器,下面是這兩種的不同配置檔案,實際使用其中一種即可:

tx_proxy.xml:使用代理bean支援事務

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
        "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    <!--設定為 abstract,這樣可以通過繼承的方式,簡化子類的程式碼,如果沒有子類就不初始化-->
    <bean id="baseTxProxyBean" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
          abstract="true">
        <!--需要的 transactionManager 是 PlatformTransactionManager 型別,所以不能直接注入 twJtaTm,需要使用 JtaTransactionManager 這個實現類,此實現類依賴 twJtaTm -->
        <property name="transactionManager" ref="mySpringTransactionManager"/>
        <property name="transactionAttributes">
            <!--Spring中Propagation類的事務屬性詳解:
                PROPAGATION_REQUIRED:支援當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。
                PROPAGATION_SUPPORTS:支援當前事務,如果當前沒有事務,就以非事務方式執行。
                PROPAGATION_MANDATORY:支援當前事務,如果當前沒有事務,就丟擲異常。
                PROPAGATION_REQUIRES_NEW:新建事務,如果當前存在事務,把當前事務掛起。
                 PROPAGATION_NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
                PROPAGATION_NEVER:以非事務方式執行,如果當前存在事務,則丟擲異常。
                PROPAGATION_NESTED:支援當前事務,如果當前事務存在,則執行一個巢狀事務,如果當前沒有事務,就新建一個事務。
                -->
            <props>
                <!--<prop key="*">PROPAGATION_REQUIRED</prop>-->
                <prop key="*">PROPAGATION_REQUIRED</prop>
                <!--<prop key="testTX">PROPAGATION_REQUIRED</prop>-->
                <!--<prop key="insert*">PROPAGATION_REQUIRED</prop>-->
                <!--<prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>-->
            </props>
        </property>
        <!--非嵌入式的,命名的,目標業務bean可被直接引用導致無事務-->
        <!--<property name="target" ref="springTest"/>-->
        <!--嵌入式的,匿名的,避免目標業務bean被直接引用-->
        <!--<property name="target">-->
        <!--<bean class="com.test.SpringTest"/>-->
        <!--</property>-->
    </bean>
    <!-- 代理物件 -->
    <bean id="myTxProxy1" parent="baseTxProxyBean">
        <!--非嵌入式的,命名的,目標業務bean可被直接引用導致無事務,所以應該引用此:baseTxProxyBean-->
        <property name="target" ref="service1"/>
    </bean>
    <!-- 代理物件 -->
    <bean id="myTxProxy2" parent="baseTxProxyBean">
        <property name="transactionAttributes">
            <props>
                <prop key="twoTX">PROPAGATION_REQUIRES_NEW</prop>
            </props>
        </property>
        <!--非嵌入式的,命名的,目標業務bean可被直接引用導致無事務,所以應該引用此:baseTxProxyBean-->
        <property name="target" ref="service2"/>
        <!--嵌入式的,匿名的,避免目標業務bean被直接引用-->
        <!--<property name="target">-->
        <!--<bean parent="baseDAO" class="com.test.dao.impl.DAOServiceImpl"/>-->
        <!--</property>-->
    </bean>
    <!--以上的代理事務,如果業務類較多會出現配置膨脹,即每個需要事務的業務bean都需要配置一次,雖然使用了 繼承 ,減少了程式碼量,但配置膨脹問題還沒有解決
    從根本上解決就是用使用 攔截器的配置方式,看 tx_interceptor.xml 定義的的攔截器配置事務方式-->
</beans>

tx_interceptor.xml:使用攔截器支援事務:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
        "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    <!--tx_proxy.xml中使用的代理事務,如果業務類較多會出現配置膨脹,即每個需要事務的業務bean都需要配置一次,雖然使用了 繼承 ,減少了程式碼量,但配置膨脹問題還沒有解決
    從根本上解決就是用使用 攔截器的配置方式,看下面的攔截器配置事務方式:利用切面程式設計思想,自動新增事務支援-->
    <!-- 定義事務 -->
    <bean id="txInterceptor"
          class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <property name="transactionManager" ref="mySpringTransactionManager" />
        <!-- 配置事務屬性 -->
        <property name="transactionAttributes">
            <!--Spring中Propagation類的事務屬性詳解:
                PROPAGATION_REQUIRED:支援當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。
                PROPAGATION_SUPPORTS:支援當前事務,如果當前沒有事務,就以非事務方式執行。
                PROPAGATION_MANDATORY:支援當前事務,如果當前沒有事務,就丟擲異常。
                PROPAGATION_REQUIRES_NEW:新建事務,如果當前存在事務,把當前事務掛起。
                 PROPAGATION_NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
                PROPAGATION_NEVER:以非事務方式執行,如果當前存在事務,則丟擲異常。
                PROPAGATION_NESTED:支援當前事務,如果當前事務存在,則執行一個巢狀事務,如果當前沒有事務,就新建一個事務。
                -->
            <props>
                <prop key="*">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="beanNames">
            <list>
                <!--matches service1 & service2 & service*-->
                <value>service*</value>
            </list>
        </property>
        <property name="interceptorNames">
            <list>
                <value>txInterceptor</value>
            </list>
        </property>
    </bean>
</beans>

由於在上面使用了hibernate,所以需要提供下hibernate的對映檔案,連個 hbm 檔案:

Student.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.test.model">
    <class name="Student" table="STUDENT">
        <id name="id">
            <generator class="uuid"/>
        </id>
        <property name="name"/>
    </class>
</hibernate-mapping>

Teacher.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.test.model">
    <class name="Teacher" table="TEACHER">
        <id name="id">
            <generator class="uuid"/>
        </id>
        <property name="name"/>
    </class>
</hibernate-mapping>

注意:以上spring的配置檔案都放到類路徑的 spring 目錄下,hibernate的兩個hbm放到類路徑的hbm目錄下。

好了,到這裡就可以看看具體的java程式碼了,值得說明的是:spring提供的切面程式設計支援事務的能力,對程式碼零侵入,使得我們在編碼過程中根本不用關心事務功能,編寫好的程式最後通過一個簡單的AOP事務配置就支援事務,感慨一下spring的魅力,切面程式設計的魅力吧!!!

dao層程式碼:此層直接進行資料庫操作,不關心業務,不關心事務,為service層提供支援

service層程式碼:此層用於完成具體的業務單元,一般一個業務單元需要同時進行多項資料庫操作,所以往往事務也在這一次加入

controller層程式碼:此層用於控制業務邏輯的導向,並完成頁面(檢視層)互動,本示例沒有涉及到此層

dao層程式碼:

package com.test.dao;

public interface DAO2 {
    void tx1(String data);

    void tx2(String data);
}
package com.test.dao;

public interface DAO1 {
    void tx1(String data);

    void tx2(String data);
}

package com.test.dao.impl;

import org.hibernate.SessionFactory;

public class BaseDAO {
    protected SessionFactory sessionFactory1;
    protected SessionFactory sessionFactory2;

    public void setSessionFactory1(SessionFactory sessionFactory1) {
        this.sessionFactory1 = sessionFactory1;
    }

    public void setSessionFactory2(SessionFactory sessionFactory2) {
        this.sessionFactory2 = sessionFactory2;
    }
}

package com.test.dao.impl;

import com.test.dao.DAO1;
import com.test.model.Student;
import com.test.model.Teacher;
import org.hibernate.Query;
import org.hibernate.Session;

import java.util.Date;
import java.util.List;

public class DAOImpl1 extends BaseDAO implements DAO1 {
    public void tx1(String data) {
        System.out.println("=========第 一 次寫資料========");
        Session studentSession = sessionFactory1.openSession();
        insertStudent(studentSession);
        List<Student> students = queryStudent(studentSession);
        for (Student u : students) {
            System.out.println(u);
        }
        if (data.equals("1")) throw new RuntimeException("111111111111");
    }

    public void tx2(String data) {
        System.out.println("=========第 二 次寫資料========");
        Session teacherSession = sessionFactory2.openSession();
        insertStudent(teacherSession);
        List<Student> students2 = queryStudent(teacherSession);
        for (Student u : students2) {
            System.out.println(u);
        }
        if (data.equals("2")) throw new RuntimeException("222222222222");
    }

    public void insertStudent(Session session) {
        Student s = new Student();
        s.setName("SpringTest1:" + new Date().toString());
        session.save(s);
    }

    public List<Student> queryStudent(Session session) {
        Query query = session.createQuery("from Student");
        return query.list();
    }

    public void insertTeacher(Session session) {
        Teacher s = new Teacher();
        s.setName(new Date().toString());
        session.save(s);
    }

    public List<Student> queryTeacher(Session session) {
        Query query = session.createQuery("from Teacher");
        return query.list();
    }
}
package com.test.dao.impl;

import com.test.dao.DAO2;
import com.test.model.Student;
import org.hibernate.Query;
import org.hibernate.Session;

import java.util.Date;
import java.util.List;

public class DAOImpl2 extends BaseDAO implements DAO2 {
    public void tx1(String data) {
        System.out.println("=========第 一 次寫資料========");
        Session studentSession = sessionFactory1.openSession();
        insertStudent(studentSession);
        List<Student> students = queryStudent(studentSession);
        for (Student u : students) {
            System.out.println(u);
        }
        if (data.equals("3")) throw new RuntimeException("111111111111");
    }

    public void tx2(String data) {
        System.out.println("=========第 二 次寫資料========");
        Session teacherSession = sessionFactory2.openSession();
        insertStudent(teacherSession);
        List<Student> students2 = queryStudent(teacherSession);
        for (Student u : students2) {
            System.out.println(u);
        }
        if (data.equals("4")) throw new RuntimeException("222222222222");
    }

    private void insertStudent(Session session) {
        Student s = new Student();
        s.setName("SpringTest2:" + new Date().toString());
        session.save(s);
    }

    private List<Student> queryStudent(Session session) {
        Query query = session.createQuery("from Student");
        return query.list();
    }
}
service層程式碼:
package com.test.service;

public interface Service1 {
    void twoTX(String data);
}

package com.test.service;

public interface Service2 {
    void twoTX(String data);
}

package com.test.service.impl;

import com.test.dao.DAO1;
import com.test.dao.DAO2;
import com.test.service.Service1;
import com.test.service.Service2;

public class Service1Impl implements Service1 {
    private DAO1 dao1;
    private DAO2 dao2;

    private Service2 service2;

    public void setDao1(DAO1 dao1) {
        this.dao1 = dao1;
    }

    public void setDao2(DAO2 dao2) {
        this.dao2 = dao2;
    }

    public void setService2(Service2 service2) {
        this.service2 = service2;
    }

    public void twoTX(String data) {
        dao1.tx1(data);
        dao1.tx2(data);
        service2.twoTX(data);
    }
}

package com.test.service.impl;

import com.test.dao.DAO1;
import com.test.dao.DAO2;
import com.test.service.Service2;

public class Service2Impl implements Service2 {
    private DAO1 dao1;
    private DAO2 dao2;

    public void setDao1(DAO1 dao1) {
        this.dao1 = dao1;
    }

    public void setDao2(DAO2 dao1) {
        this.dao2 = dao1;
    }

    public void twoTX(String data) {
        dao2.tx1(data);
        dao2.tx2(data);
    }
}

model類:
package com.test.model;

public class Student {
    private String id;
    private String name;

    public String getName() {
        return name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}
package com.test.model;

public class Teacher {
    private String id;
    private String name;

    public String getName() {
        return name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}


最後說明:

1.EJB的事務是方法級別的隔離,而spring的攔截事務是類級別的,所以spring管理的jta事務在同一個類的不同方法設定不同的事務傳播策略是無效的。
2.使用了spring的事務,就不能顯示地使用hibernate的開啟事務,提交事務等,spring通過攔截器自動實現這些操作。