2012-05-18 21 views
5

En uncómo configurar mutliple administradores de transacciones con Spring + + DBUnit JUnit

Mi línea de comandos copias de aplicaciones Java datos de resumen de una fuente de datos a otro sin usar XA. He configurado dos fuentes de datos separadas y me gustaría una prueba JUnit que pueda retrotraer datos en ambos orígenes de datos. Yo uso DBUnit para cargar datos en la base de datos "fuente", pero no puedo hacer que esto se revierta. Puedo hacer que el origen de datos "objetivo" se revierte.

Mi Código

Dada esta configuración ...

<tx:annotation-driven /> 

<!-- note the default transactionManager name on this one --> 
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
    <property name="dataSource"  ref="dataSourceA" /> 
</bean> 

<bean id="transactionManagerTarget" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
    <property name="dataSource"  ref="dataSourceB" /> 
</bean> 

y este código ...

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations={"classpath:resources/spring-context.xml", 
           "classpath:resources/spring-db.xml"}) 
@Transactional 
@TransactionConfiguration(transactionManager = "transactionManagerTarget", defaultRollback = true) 
public class MyIntegrationTest { 

    @Autowired 
    private MyService service; 

    @Autowired 
    @Qualifier("dataSourceA") 
    private DataSource dataSourceA; 

    private IDataSet loadedDataSet; 

    /** 
    * Required by DbUnit 
    */ 
    @Before 
    public void setUp() throws Exception { 
     SybaseInsertIdentityOperation.TRUNCATE_TABLE.execute(getConnection(), getDataSet()); 
     SybaseInsertIdentityOperation.INSERT.execute(getConnection(), getDataSet()); 
    } 

    /** 
    * Required by DbUnit 
    */ 
    protected IDataSet getDataSet() throws Exception { 
     loadedDataSet = DbUnitHelper.getDataSetFromFile(getConnection(), "TestData.xml"); 
     return loadedDataSet; 
    } 

    /** 
    * Required by DbUnit 
    */ 
    protected IDatabaseConnection getConnection() throws Exception{ 
     return new DatabaseConnection(dataSourceA.getConnection()); 
    } 

    @Test 
    public void testSomething() { 

     // service.doCopyStuff(); 

    } 

} 

El problema que yo veo, es que sólo los Estados @TransactionConfiguration el origen de datos objetivo para permitir una reversión. DBUnit se está pasando dataSourceA explícitamente y está recogiendo el gestor de transacciones predeterminado llamado transactionManager (no estoy seguro de cómo) que no se le indicó deshacer.

Pregunta

¿Cómo puedo saber los dos administradores de transacciones para deshacer?

¿Puedo usar un solo administrador de transacciones cuando mis fuentes de datos no son compatibles con las transacciones XA?

Nota: La aplicación no requiere un administrador de transacciones en dataSourceA cuando se ejecuta en producción, ya que solo será de solo lectura. Este problema es solo para mis clases de exámenes.

Respuesta

0

Una posible solución sería introducir un grano ayudante anotado como @Transactional("transactionManagerTarget") y dejar su prueba anotada como @Transactional("transactionManager"), configurar tanto con defaultRollback = true. Su prueba tendría que llamar al bean auxiliar, que a su vez llamaría a su bean de servicio bajo prueba. Esto debería provocar que la transacción alrededor de su servicio se retrotraiga, luego la transacción alrededor de DBUnit.

Aunque es un poco desordenado.

Otros enfoques posibles:

  • Uso de una base de datos en memoria, tales como H2 en lugar de su producción de base de datos- podría configurar este renuncie a todos sus datos cuando sea necesario.
  • Permita que DBUnit confirme, y tenga una transacción de compensación en su método de desmontaje para borrar los datos.
+0

Traté de factorizar un frijol auxiliar, pero esto no ayuda a la situación. En algún lugar bajo el capó DBUnit no se revertirá cuando haya múltiples transactionManagers. Voy a aceptar su respuesta para usar HSQL como el camino a seguir, sin embargo, en mi caso, sigo estancado porque utilizo Sybase para crear una tabla temporal en esta fuente de datos y la sintaxis no es compatible con HSQL. – Brad

1

Utilice el elemento <qualifier> dentro de la definición de su administrador de transacciones.

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
    <property name="dataSource" ref="dataSourceA" /> 
    <qualifier value="transactionManager" /> 
</bean> 

<bean id="transactionManagerTarget" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
    <property name="dataSource" ref="dataSourceB" /> 
    <qualifier value="transactionManagerTarget" /> 
</bean> 

A continuación, puede hacer referencia a cuál de ellos desea utilizar directamente en la @Transactional anotación, es decir,

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations={"classpath:resources/spring-context.xml", 
           "classpath:resources/spring-db.xml"}) 
@Transactional("transactionManagerTarget") 
@TransactionConfiguration(defaultRollback = true) 
public class MyIntegrationTest { 
... 
+0

Mi comprensión de por qué esto no funciona es que los métodos '@ Before' y' @ Test' se ejecutarán en la misma transacción y no puedo especificar más de una anotación @Transactional. En su respuesta, no puedo ver cómo le dice 'transactionManager' a la reversión. ¿Me estoy perdiendo de algo? Buen punto sobre el uso del '' embargo. – Brad

+0

Probé varios usos de '@ Transactional' en mi clase de prueba, pero lamentablemente sospeché que el método' @ Test' inicia una transacción y '@ Before' no puede iniciar otro para DBUnit, por lo que continúa ejecutándose con transactionManagerTarget y, por lo tanto, no revertir los datos de DBUnit. Creo que intenté todo lo posible con las anotaciones, pero no tuve suerte. – Brad

1

he utilizado transacciones XA y reversiones en pruebas JUnit utilizando el código abierto TM atomikos. Una buena característica es que Atomikos permite el uso de fuentes de datos no compatibles con XA para participar en transacciones XA. Consulte este enlace para ver un ejemplo: http://www.atomikos.com/Documentation/NonXaDataSource

Por otro lado, si XA es una solución decente para sus problemas JUnit es otra historia. ¿Sus pruebas se enfocan mucho en la implementación de la base de datos (Sybase) o es más sobre la lógica de Java? Normalmente configuro bases de datos incrustadas como Apache Derby o HQSQL para pruebas JUnit. Entonces no tengo que preocuparse mucho por limpiezas, ya que se encargará de GC :)

+0

Gracias por su respuesta. DBUnit solo se usa para cargar datos de prueba debido a que esa fuente de datos ya habrá sido poblada en producción por otro servicio. Así que no tengo problemas de XA en producción, ya que solo escribo en el origen de datos de un objetivo. Me gusta su idea de usar HSQL para mi fuente de datos "fuente". Solo tengo que descubrir cómo almacenar mi esquema para que HSQL cargue y mantenerlo consistente con el esquema de Sybase. – Brad

+0

Cualquier JTA TM no es bueno para mí porque mis dataSources no son XA. La mayoría de las JTA TM le permiten involucrar ** un ** origen de datos no XA pero no más – Brad

+0

Un plano muy lejano, pero lo que ... Omita HQSQL, use Apache Derby para una de las fuentes de datos (fuente). ¿No puedes almacenar el esquema en como test/resources/source_db_setup.sql? Derby está habilitado para XA. Entonces podrías usar sybase como tu fuente no xa. Creo que debería haber alguna otra opción para ti, aunque, no tengo una buena idea en este momento. –

Cuestiones relacionadas