2009-10-05 22 views
5

Tôi đang cố thử nghiệm một thực thể EJB3 với Spring.Kiểm tra đơn vị Spring/JTA/JPA: Rollback không hoạt động

Bản thân EJB không sử dụng Mùa xuân và tôi muốn giữ các bản sao của cấu hình JPA sản xuất tối thiểu (tức là không sao chép persistence.xml cho ví dụ).

kiểm tra đơn vị của tôi dường như để làm việc nhưng mặc dù các bài kiểm tra đơn vị của tôi nên transactionnal, dữ liệu được duy trì giữa các phương pháp thử nghiệm khác nhau ...

Đây là thực thể của tôi:

package sample; 

import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.Id; 

@Entity 
public class Ejb3Entity { 

    public Ejb3Entity(String data) { 
     super(); 
     this.data = data; 
    } 
    private Long id; 
    private String data; 

    @Id 
    @GeneratedValue 
    public Long getId() { 
     return id; 
    } 
    public void setId(Long id) { 
     this.id = id; 
    } 

    public String getData() { 
     return data; 
    } 
    public void setData(String data) { 
     this.data = data; 
    } 

} 

kiểm tra đơn vị của tôi :

package sample; 

import static org.junit.Assert.*; 

import javax.persistence.EntityManager; 
import javax.persistence.PersistenceContext; 

import org.junit.Before; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.springframework.test.context.ContextConfiguration; 
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 
import org.springframework.transaction.annotation.Transactional; 

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations={"/appContext.xml"}) 
@Transactional 
public class Ejb3EntityTest { 

    @PersistenceContext 
    EntityManager em; 

    @Before 
    public void setUp() throws Exception { 
     Ejb3Entity one = new Ejb3Entity("Test data"); 
     em.persist(one); 
    } 

    @Test 
    public void test1() throws Exception { 

     Long count = (Long) em.createQuery("select count(*) from Ejb3Entity").getSingleResult(); 
     assertEquals(Long.valueOf(1l), count); 
    } 

    @Test 
    public void test2() throws Exception { 

     Long count = (Long) em.createQuery("select count(*) from Ejb3Entity").getSingleResult(); 
     assertEquals(Long.valueOf(1l), count); 
    } 

} 

và appContext.xml tôi:

<?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:tx="http://www.springframework.org/schema/tx" 
    xmlns:context="http://www.springframework.org/schema/context" 
    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.xsd 
      http://www.springframework.org/schema/context 
      http://www.springframework.org/schema/context/spring-context.xsd"> 

    <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean" /> 

    <bean id="transactionManager" 
     class="org.springframework.transaction.jta.JtaTransactionManager"> 
     <property name="userTransaction" ref="jotm" /> 
     <property name="allowCustomIsolationLevels" value="true" /> 
    </bean> 

    <bean id="dataSource" class="org.enhydra.jdbc.standard.StandardXADataSource"> 
     <property name="driverName" value="org.h2.Driver" /> 
     <property name="url" value="jdbc:h2:mem:unittest;DB_CLOSE_DELAY=-1" /> 
     <property name="user" value="" /> 
     <property name="password" value="" /> 
     <property name="transactionManager" ref="jotm" /> 
    </bean> 

    <bean id="emf" 
     class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
     <property name="persistenceUnitPostProcessors"> 
      <bean class="sample.JtaDataSourcePersistenceUnitPostProcessor"> 
       <property name="jtaDataSource" ref="dataSource" /> 
      </bean> 
     </property> 
     <property name="jpaVendorAdapter"> 
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
       <property name="showSql" value="false" /> 
       <property name="generateDdl" value="true" /> 
       <property name="database" value="H2" /> 
       <property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" /> 
      </bean> 
     </property> 
     <property name="jpaPropertyMap"> 
      <map> 
       <entry key="hibernate.transaction.manager_lookup_class" 
        value="org.hibernate.transaction.JOTMTransactionManagerLookup" /> 
       <entry key="hibernate.transaction.auto_close_session" value="false" /> 
       <entry key="hibernate.current_session_context_class" value="jta" /> 
      </map> 
     </property> 

    </bean> 


</beans> 

Khi tôi chạy thử nghiệm của mình, test2 không thành công vì nó tìm thấy 2 thực thể mà tôi chỉ mong đợi một (vì người đầu tiên phải được khôi phục ...)

Tôi đã thử rất nhiều cấu hình khác nhau là toàn diện nhất tôi có thể nhận được ... Tôi không có ý tưởng nào khác. Bạn có?

+0

Tại sao bạn nghĩ rằng thực thể đầu tiên phải được khôi phục? – janko

+0

Bởi vì tôi đang sử dụng chú thích @Transactional làm cho mỗi lần chạy thử nghiệm sử dụng giao dịch của riêng nó được Spring tự động khôi phục. – Michel

Trả lời

2

Tôi đã quản lý để làm cho nó hoạt động bằng cách sử dụng Bitronix thay vì JOTM. Bitronix cung cấp LrcXADataSource cho phép một cơ sở dữ liệu không phải XA tham gia vào giao dịch JTA.

Tôi nghĩ rằng các vấn đề là H2 không tuân thủ XA và enhydra StandardXADataSource không làm cho nó kỳ diệu như vậy (tôi cũng đã kết thúc bằng HSQLDB nhưng điều đó không liên quan đến vấn đề này).

Đây là bối cảnh mùa xuân của tôi mà làm việc:

<?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:tx="http://www.springframework.org/schema/tx" 
    xmlns:context="http://www.springframework.org/schema/context" 
    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.xsd 
      http://www.springframework.org/schema/context 
      http://www.springframework.org/schema/context/spring-context.xsd"> 

    <context:annotation-config /> 
    <tx:annotation-driven transaction-manager="transactionManager" /> 

    <!-- Bitronix Transaction Manager embedded configuration --> 
    <bean id="btmConfig" factory-method="getConfiguration" 
     class="bitronix.tm.TransactionManagerServices"> 
     <property name="serverId" value="spring-btm" /> 
     <property name="journal" value="null" /> 
    </bean> 

    <!-- create BTM transaction manager --> 
    <bean id="BitronixTransactionManager" factory-method="getTransactionManager" 
     class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig,dataSource" 
     destroy-method="shutdown" /> 

    <bean id="transactionManager" 
     class="org.springframework.transaction.jta.JtaTransactionManager"> 
     <property name="transactionManager" ref="BitronixTransactionManager" /> 
     <property name="userTransaction" ref="BitronixTransactionManager" /> 
     <property name="allowCustomIsolationLevels" value="true" /> 
    </bean> 


    <!-- DataSource definition --> 

    <bean id="dataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" 
     init-method="init" destroy-method="close"> 
     <property name="className" value="bitronix.tm.resource.jdbc.lrc.LrcXADataSource" /> 
     <property name="uniqueName" value="unittestdb" /> 
     <property name="minPoolSize" value="1" /> 
     <property name="maxPoolSize" value="3" /> 
     <property name="allowLocalTransactions" value="true" /> 
     <property name="driverProperties"> 
      <props> 
       <prop key="driverClassName">org.hsqldb.jdbcDriver</prop> 
       <prop key="url">jdbc:hsqldb:mem:unittestdb</prop> 
       <prop key="user">sa</prop> 
       <prop key="password"></prop> 
      </props> 
     </property> 
    </bean> 

    <!-- Entity Manager Factory --> 
    <bean id="emf" 
     class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
     <property name="dataSource" ref="dataSource" /> 
     <property name="jpaVendorAdapter"> 
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
       <property name="showSql" value="true" /> 
       <property name="generateDdl" value="true" /> 
       <property name="database" value="HSQL" /> 
      </bean> 
     </property> 
     <property name="jpaPropertyMap"> 
      <map> 
       <entry key="hibernate.transaction.manager_lookup_class" 
        value="org.hibernate.transaction.BTMTransactionManagerLookup" /> 
       <entry key="hibernate.transaction.auto_close_session" value="false" /> 
       <entry key="hibernate.current_session_context_class" value="jta" /> 
      </map> 
     </property> 

    </bean> 
+0

Chỉ một tuần sau khi đăng bài ở đây, tôi đã gặp phải vấn đề tương tự. Tôi cũng không thể đưa JOTM quay lại đúng cách; nó luôn luôn nói rằng nó đã được quay trở lại, nhưng những thay đổi từ giao dịch vẫn nhấn cơ sở dữ liệu. BTM làm công việc tốt, cả với một MySQL và với một backend. Kỳ dị. – Henning

1

Edit: (.. Xin lỗi, dường như tôi chỉ là nửa tỉnh táo khi tôi đã viết đoạn này Tất nhiên bạn nói đúng, tất cả mọi thứ nên được cuộn lại theo mặc định)

Bạn có thể kiểm tra những gì người quản lý giao dịch thực sự đang làm, ví dụ bằng cách cho phép gỡ lỗi đầu ra cho nó.

log4j Giả sử:

log4j.logger.org.springframework.transaction=DEBUG 

Người quản lý giao dịch cung cấp cho bạn dữ liệu ghi nhận rất tốt đẹp về giao dịch tạo ra và tham gia, và cũng có thể về cam kết và rollbacks. Điều đó sẽ giúp bạn tìm hiểu những gì không hoạt động với thiết lập của bạn.

+0

Cảm ơn bạn đã đề xuất. Có nhiều nhật ký hơn đã giúp ích rất nhiều. – Michel

2

Khi tôi cố gắng tích hợp JOTM và Hibernate, cuối cùng tôi đã phải mã hóa việc triển khai ConnectionProvider của mình. Dưới đây là những gì nó trông giống như ngay bây giờ: http://pastebin.com/f78c66e9c

Sau đó, bạn chỉ định việc triển khai của bạn làm tư cách kết nối trong thuộc tính ngủ đông và giao dịch kỳ diệu bắt đầu hoạt động.

Điều là nhà cung cấp kết nối mặc định gọi getConnection() trên nguồn dữ liệu. Khi bạn thực hiện, bạn gọi getXAConnection(). GetConnection(). Điều này tạo nên sự khác biệt

+0

Xin lỗi tôi đã kết thúc bằng cách sử dụng BTM thay vì JOTM và tôi đã không có cơ hội để làm những gì bạn đề xuất. – Michel

0

Thêm @Rollback chú thích (từ org.springframework.test.annotation), ngay sau chú thích @Transactional như đã đề cập trong tài liệu lò xo.

@Rollback is a test annotation that is used to indicate whether a test- 
managed transaction should be rolled back after the test method has 
completed. 
Consult the class-level Javadoc for 
org.springframework.test.context.transaction.TransactionalTest- 
ExecutionListener for an explanation of test-managed transactions. 

When declared as a class-level annotation, @Rollback defines the default 
rollback semantics for all test methods within the test class hierarchy. When 
declared as a method-level annotation, @Rollback defines rollback semantics 
for the specific test method, potentially overriding class-level default 
commit or rollback semantics. 

As of Spring Framework 4.2, @Commit can be used as direct replacement for 
@Rollback(false). 

Warning: Declaring @Commit and @Rollback on the same test method or on the 
same test class is unsupported and may lead to unpredictable results. 

This annotation may be used as a meta-annotation to create custom composed 
annotations. Consult the source code for @Commit for a concrete example. 
Các vấn đề liên quan