6

Tengo una clase de acceso a datos que se ejecuta como parte de una aplicación java independiente. Actualmente está trabajando, lo que significa que se ha definido un administrador de transacciones, pero quiero refactorizar la clase para reducir el alcance de la transacción, pero si lo hago obtengo org.hibernate.HibernateException: ninguna sesión de Hibernate vinculada al hilo, y la configuración no permite la creación de uno no transaccional aquí que implica que mover el @Transactional de alguna manera ha impedido que se reconozca.Cómo se refactoriza un método @Transactional para dividir partes no transaccionales

Mi versión original tenía los métodos reajustados siendo privados pero encontré una recomendación para cambiar eso a público ya que en algunos casos la anotación no se recogió.

public class DoStuff { 
    @Transactional 
    public void originalMethod() { 
     // do database stuff 
     ... 

     // do non-database stuff that is time consuming 
     ... 
    } 
} 

Lo que quiero hacer es perfeccionar por la siguiente

public class DoStuff { 
    public void originalMethod() { 
     doDatabaseStuff() 

     doNonDatabaseStuff() 
    } 

    @Transactional 
    public void doDatabaseStuff() { 
     ... 
    } 

    public void doNonDatabaseStuff() { 
     ... 
    } 
} 
+0

¿Dónde está su administrador de transacciones? ¿Puedes agregar más detalles? – Chris

+0

¿Su clase DoStuff implementa alguna interfaz? –

+0

Sin interfaces, y el administrador de transacciones se define como funciona para la clase original. –

Respuesta

6

Editar:

Es necesario comprender how Spring proxying works a entender por qué su refactorización no funciona.

Las llamadas de método a la referencia de objeto serán llamadas en el proxy, y como tal el proxy podrá delegar a todos los interceptores (consejo) que son relevantes para esa llamada de método particular. Sin embargo, una vez que la llamada finalmente ha llegado al objeto de destino, cualquier método que se llame puede hacerse sobre sí mismo, se invocará contra esta referencia, y no el proxy. Esto tiene implicaciones importantes. Significa que la auto invocación no dará como resultado el asesoramiento asociado con una invocación a un método que tenga la oportunidad de ejecutarse.

@Transactional utiliza Spring AOP, Spring usa proxies. Esto significa que cuando llamas a un método @Transactional de otra clase, Spring usará un proxy, por lo que se aplicará el consejo transaccional. Sin embargo, si llama al método desde la misma clase, Spring utilizará la referencia "this" en lugar del proxy, para que no se aplique el asesoramiento transaccional.

respuesta original:

Aquí es lo que funcionó para mí en escenario similar.

public class DoStuff implement ApplicationContextAware {  
private ApplicationContext CONTEXT; 
public void setApplicationContext(ApplicationContext context) throws BeansException { 
    CONTEXT = context; 
} 

    public void originalMethod() {   
     getSpringProxy().doDatabaseStuff()    
     doNonDatabaseStuff()  
    } 

    private DoStuff getSpringProxy() { 
     return context.getBean(this.getClass());  
    } 
    @Transactional  
    public void doDatabaseStuff() {   
     ...  
    }   

    public void doNonDatabaseStuff() {   
     ...  
    } 
} 

Explicación:

  1. hacer que la clase ApplicationContextAware, por lo que tiene una referencia al contexto
  2. Cuando tenga que llamar a un método transaccional, ir a buscar el proxy real de la primavera del contexto
  3. Utilice este proxy para llamar a su método, de modo que @Transactional se aplique realmente.
+0

La clase original ya reconoce @Transactional y funciona correctamente. Solo deja de funcionar después de refactorizar. –

+0

@Michael Rutherfurd ver mi edición – gresdiplitude

+0

Este parece ser mi problema. Gracias –

0

Su enfoque parece que debería funcionar bien, supongo que el problema está relacionado con los proxies de Spring.

La razón por la que pregunté sobre las interfaces está relacionada con el método predeterminado por el cual Spring aplica el comportamiento transaccional: los proxys dinámicos JDK.

Si la definición real de su clase es:

public class DoStuff implements Doable { 
    public void originalMethod() { 

    } 
} 

public interface Doable { 
    public void originalMethod(); 
} 

Si esto es de hecho la estructura, cuando se trasladó a la nueva estructura de la primavera no es capaz de aproximar el nuevo método doDatabaseStuff.

Sus opciones para solucionar este problema:

  • Añadir los nuevos métodos para su interfaz para asegurarse de que la primavera Puede actuar como apoderado de ellos
  • Mover para el uso de proxies basados ​​CGLIB (estos no dependen de las interfaces)
+0

No, la clase es un POJO, no hay interfaces implementadas en absoluto –

+0

Oh, bueno. Todavía. Todo es verdad :) –

+0

No estaba en desacuerdo (solo decía que no se aplicaba en este caso :-) –

Cuestiones relacionadas