Los cambios de datos que una transacción anidada e interna había cometido deberían, de hecho, estar visibles de manera instantánea en la transacción padre.
Y realmente no sé por qué son no en el contexto transaccional de un GroovyTestCase
. Others don't know, as well, and are using similar approaches to mine.
Considere el siguiente caso de prueba. El caso de prueba, en sí mismo, es no transaccional, pero invoca un método transaccional. - Esto funciona como se esperaba.
class TransactionalMethodTest extends GroovyTestCase {
static transactional = false // test case is not transactional
def customerService
void testTransactionsCommit() {
// start a new transaction,
// setting order 1 inactive
setOrderInactive()
assert ! Order.get(1).isActive
}
@Transactional(propagation = Propagation.REQUIRED)
private void setOrderInactive() {
// make sure that order 1 is active
Order order = Order.get(1)
order.isActive = true
order.save(flush:true)
assert Order.get(1).isActive
// the following method acts in isolation level
// Propagation.REQUIRES_NEW, which means,
// a new, nested, transaction is started
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
customerService.cancelOrders([1])
// changes from the nested transaction are
// visible, instantly
assert ! Order.get(1).isActive
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
}
Considere lo siguiente, "normal", transaccional, caso de prueba. Los cambios de datos desde dentro de la transacción anidada son no visibles en la transacción padre.
Todo lo que puedo decir es, los casos de prueba transaccionales no funcionan con transacciones anidadas, por lo utilizan el caso de prueba no transaccional por encima de.
Si no entendemos la causa, podemos, al menos, conocer nuestras opciones.
class TransactionalTestCaseTests extends GroovyTestCase {
static transactional = true // default; Propagation.REQUIRED
def customerService
void testTransactionsCommit() {
// make sure that order 1 is active
Order order = Order.get(1)
order.isActive = true
order.save(flush:true)
assert Order.get(1).isActive
// the following method acts in isolation level
// Propagation.REQUIRES_NEW, which means,
// a new, nested, transaction is started
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
customerService.cancelOrders([1])
// the changes from the inner transaction
// are not yet visible
assert Order.get(1).isActive
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
@Override
protected void tearDown() throws Exception {
// the changes from the inner transaction
// are still not visible
assert Order.get(1).isActive
super.tearDown();
}
}
no relacionadas con su pregunta principal, pero a su intención general, aquí es un caso de prueba que comprueba si la transacción anidada se deshace, adecuadamente:
class NestedTransactionRolledBackTests extends GroovyTestCase {
static transactional = false // test case is not transactional
def customerService
void testTransactionsCommit() {
// start a new transaction,
// setting order 1 active
setOrderActive()
assert Order.get(1).isActive
}
@Transactional(propagation = Propagation.REQUIRED)
private void setOrderActive() {
// make sure that order 1 is active
Order order = Order.get(1)
order.isActive = true
order.save(flush:true)
assert Order.get(1).isActive
// the following method acts in isolation level
// Propagation.REQUIRES_NEW, which means,
// a new, nested, transaction is started.
// This transaction will fail, and be rolled back.
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
shouldFail(NullPointerException) {
customerService.cancelOrders([1, -999])
}
// changes from the nested transaction are
// visible, instantly.
// The changes have been rolled back
assert Order.get(1).isActive
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
}
Finalmente, algunas notas al margen más generales, no es boolean transactional = true
(que parece funcionar, sin embargo), pero static transactional = true
. Sus pruebas de integración también deberían extend
GroovyTestCase
, no su subclase GrailsUnitTestCase
, ya que no necesita las capacidades de burla de este último. El campo isActive
debe llamarse active
, entonces se generará automáticamente un getter isActive()
al nombrar la convención.
gracias, por lo que parece que esto es un error con el marco de prueba de integración. es decir, el código/lógica es correcto y funciona en una aplicación en ejecución, pero no en la prueba de integración.Tal vez debería JIRA – Sunny
No creo que sea un error (el problema es demasiado central/prominente) sino un error de diseño en su lugar. - Si debe presentar un problema JIRA (con el marco Spring, no Grails), deje una nota en este lugar. – robbbert
Una corrección en su ejemplo final (¿desactualizado?): 'Propagation.REQUIRES_NEW' comienza una nueva transacción, _unrelated and independent_. Por el contrario, una transacción _nested_ (visible para el padre y con su propio punto de guardado) ocurre bajo 'Propagation.NESTED'. Los Javadocs de Spring hacen un gran trabajo al explicar la diferencia: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/annotation/Propagation.html –