2009-05-07 5 views
27

Tengo un método que hace un montón de cosas; entre ellos haciendo una serie de inserciones y actualizaciones. Se declara así ...¿hay alguna manera de forzar una reversión transaccional sin encontrar una excepción?

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false) 
public int saveAll(){ 
//do stuff; 
} 

Funciona exactamente como se supone que debe y no tengo problemas con él. Sin embargo, hay situaciones en las que quiero forzar la reversión a pesar de que no haya una excepción ... en este momento, forzo una excepción cuando encuentro las condiciones adecuadas, pero es feo y no me gusta.

¿Puedo invocar activamente la reversión de alguna manera? La excepción lo llama ... Estoy pensando que quizás yo también pueda.

+0

cheque http://static.springsource.org /spring/docs/2.0.x/reference/transaction.html sección 9.5.3 – shrini1000

Respuesta

15

Llame al setRollbackOnly() en el SessionContext si se encuentra en un EJB.

Puede inyectarse SessionContext así:

public MyClass { 
    @Resource 
    private SessionContext sessionContext; 

    @Transactional(propagation = Propagation.REQUIRED, 
        isolation = Isolation.DEFAULT, 
        readOnly = false) 
    public int saveAll(){ 
     //do stuff; 
     if(oops == true) { 
      sessionContext.setRollbackOnly(); 
      return; 
     } 
    } 

setRollbackOnly() es miembro de EJBContext. SessionContext extiende EJBContext: http://java.sun.com/j2ee/1.4/docs/api/javax/ejb/SessionContext.html Nota que solo está disponible en los EJB de sesión.

@Resource es una anotación Java EE estándar, por lo que probablemente debería verificar su configuración en Eclipse. Aquí hay un example de cómo inyectar el SessionContext usando @Resource.

Sospecho que probablemente esta no sea su solución, ya que parece que no está trabajando con EJB, explicando por qué Eclipse no encuentra @Resource.

Si ese es el caso, entonces deberá interactuar directamente con la transacción - vea la plantilla de transacción.

+0

Eclipse no reconoce @Resource como una anotación, y setRollbackOnly() no es miembro de SessionContext (cuando se importa desde org.springframework.orm .hibernate3.SpringSessionContext). Entonces ... estoy más cerca, pero no lo suficientemente cerca :) –

+0

Gracias por la respuesta ... – user961690

3

Debe tener la primavera de inyectar el administrador de transacciones. Luego puede llamar al método de reversión.

+0

que parece ser la forma correcta de hacerlo ... Estoy investigando SavepointManager ahora. –

+0

esto está causando que se emita una excepción al final "Transacción revertida porque se ha marcado como rollback-only" – Guillaume

17

En Spring Transactions, usa TransactionStatus.setRollbackOnly().

El problema que tiene aquí es que está usando @Transactional para demarcar sus transacciones. Esto tiene el beneficio de ser no invasivo, pero también significa que si desea interactuar manualmente con el contexto de la transacción, no puede hacerlo.

Si desea un control estricto del estado de su transacción, debe usar transacciones programáticas en lugar de anotaciones declarativas. Esto significa usar Spring's TransactionTemplate, o usar PlatformTransactionManager directamente. Ver la sección 9.6 del manual de referencia de Spring.

Con TransactionTemplate, proporciona un objeto de devolución de llamada que implementa TransactionCallback, y el código en esta devolución de llamada tiene acceso a los objetos TransactionStatus.

No es tan bueno como @Transactional, pero se obtiene un control más estricto de su estado de tx.

+4

Utilizaría 'TransactionInterceptor.currentTransactionStatus(). SetRollbackOnly()' sería una forma de interactuar manualmente con la transacción en el medio de un método que usa '@ Transactional'? Principalmente para reemplazar los métodos EJB 'setRollbackOnly'. Estaría interesado si tiene un comentario sobre eso. ¡Gracias! –

-3

Lanza una excepción y usa el framework como está diseñado; de lo contrario, no uses gestión de transacciones declarativa y sigue los consejos de skaffman anteriores. Mantenlo simple.

2

Tengo métodos de servicio anotados con @Transactional.Cuando la validación falla, y ya tengo una entidad adjunta a la unidad de trabajo actual, utilizo sessionFactory.getCurrentSession().evict(entity) para asegurarme de que no se escribe nada en la base de datos. De esa forma no necesito lanzar una excepción.

+0

Esto podría ser una solución de compromiso en una sesión larga. – armysheng

15

No usamos EJB, sino simplemente Spring y hemos elegido el enfoque AOP. Hemos implementado la nueva anotación @TransactionalWithRollback y usamos AOP para envolver esos métodos anotados con consejos "cercanos". Para implementar el consejo que utilizamos mencionado TransactionTemplate. Esto significa un poco de trabajo al principio, pero como resultado podemos simplemente anotar un método con @TransactionalWithRollback como usamos @Transactional en otros casos. El código principal se ve limpio y simple.

// 
// Service class - looks nice 
// 
class MyServiceImpl implements MyService { 
    @TransactionalWithRollback 
    public int serviceMethod { 
     // DO "read only" WORK 
    } 
} 

// 
// Annotation definition 
// 
@Retention(RetentionPolicy.RUNTIME) 
@Target({ElementType.METHOD}) 
public @interface TransactionalWithRollback { 
} 

// 
// the around advice implementation 
// 
public class TransactionalWithRollbackInterceptor { 
    private TransactionTemplate txTemplate; 
    @Autowired private void setTransactionManager(PlatformTransactionManager txMan) { 
     txTemplate = new TransactionTemplate(txMan); 
    } 

    public Object doInTransactionWithRollback(final ProceedingJoinPoint pjp) throws Throwable { 
     return txTemplate.execute(new TransactionCallback<Object>() { 
      @Override public Object doInTransaction(TransactionStatus status) { 
       status.setRollbackOnly(); 
       try { 
        return pjp.proceed(); 
       } catch(RuntimeException e) { 
        throw e; 
       } catch (Throwable e) { 
        throw new RuntimeException(e); 
       } 
      } 
     }); 
    } 
} 

// 
// snippet from applicationContext.xml: 
// 
<bean id="txWithRollbackInterceptor" class="net.gmc.planner.aop.TransactionalWithRollbackInterceptor" /> 

<aop:config> 
    <aop:aspect id="txWithRollbackAspect" ref="txWithRollbackInterceptor"> 
     <aop:pointcut 
      id="servicesWithTxWithRollbackAnnotation" 
      expression="execution(* org.projectx..*.*(..)) and @annotation(org.projectx.aop.TransactionalWithRollback)"/> 
     <aop:around method="doInTransactionWithRollback" pointcut-ref="servicesWithTxWithRollbackAnnotation"/> 
    </aop:aspect> 
</aop:config> 
+0

+1 por tomarse el tiempo para responder a una pregunta que hice hace 4 años y que ya tiene una respuesta múltiple aceptada como votada. :) –

+0

Me gusta que las cosas estén completas :-) – Jakub

10

Esto funciona para mí:

TransactionInterceptor.currentTransactionStatus().setRollbackOnly(); 
Cuestiones relacionadas