6

Ho una classe di accesso ai dati che viene eseguita come parte di un'applicazione java autonoma. Attualmente sta funzionando, il che significa che è definito un gestore transazioni, ma voglio rifattorizzare la classe per ridurre l'ambito della transazione, ma se lo faccio ottengo org.hibernate.HibernateException: nessuna sessione di ibernazione legata al thread e la configurazione non consentire la creazione di uno non transazionale qui che implica che lo spostamento dello @Transactional ha in qualche modo impedito il riconoscimento.Come refactoring un metodo @Transactional per dividere le parti non transazionali

La mia versione originale aveva i metodi refactored come privati ​​ma ho trovato una raccomandazione per cambiarli in pubblico in quanto in alcuni casi l'annotazione non verrebbe rilevata.

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

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

Quello che voglio fare è refactoring al seguente

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

     doNonDatabaseStuff() 
    } 

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

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

Dov'è il tuo gestore delle transazioni? Puoi aggiungere ulteriori dettagli? – Chris

+0

La tua classe DoStuff implementa qualsiasi interfaccia? –

+0

Nessuna interfaccia e il gestore transazioni è definito come funziona per la classe originale. –

risposta

6

Edit:

è necessario capire how Spring proxying works a capire il motivo per cui il vostro refactoring non funziona.

Metodo invita il riferimento all'oggetto sarà invita il proxy, e come tale il proxy potrà delegare a tutte le intercettori (consulenza) che sono rilevanti per quel particolare metodo di chiamata. Tuttavia, una volta che la chiamata ha finalmente raggiunto l'oggetto di destinazione, qualsiasi metodo chiama che può effettuare su se stesso, verrà invocato contro questo riferimento e non il proxy. Questo ha importanti implicazioni. Significa che l'autoinvocazione non darà come risultato il suggerimento associato a un'invocazione di metodo che avrà la possibilità di eseguire.

@Transazionale utilizza Spring AOP, Spring utilizza i proxy. Ciò significa che quando si chiama un metodo @Transactional da un'altra classe, Spring utilizzerà un proxy, quindi verrà applicato il consiglio transazionale. Tuttavia, se chiami il metodo dalla stessa classe, spring utilizzerà il riferimento "this" al posto del proxy, in modo tale che il consiglio transazionale non venga applicato.

risposta originale:

Ecco che cosa ha funzionato per me in scenario simile.

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() {   
     ...  
    } 
} 

Spiegazione:

  1. rendere la classe ApplicationContextAware, quindi ha un riferimento al contesto
  2. Quando è necessario chiamare un metodo transazionale, prendere il proxy attuale primavera dal contesto
  3. Utilizzare questo proxy per chiamare il metodo, in modo che @Transactional venga effettivamente applicato.
+0

La classe originale riconosce già @Transactional e funziona correttamente. Smette di funzionare solo dopo il refactoring. –

+0

@ Michael Rutherfurd vedi la mia modifica – gresdiplitude

+0

Questo sembra essere il mio problema. Grazie –

0

L'approccio sembra funzionare correttamente, il problema è relativo ai proxy Spring.

Il motivo per cui ho chiesto informazioni sulle interfacce è correlato al metodo predefinito con cui Spring applica il comportamento transazionale - i proxy dinamici JDK.

Se la definizione stessa della classe è:

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

    } 
} 

public interface Doable { 
    public void originalMethod(); 
} 

Se questa è davvero la struttura, quando si è spostato alla nuova struttura di Primavera non è in grado di delega il nuovo metodo doDatabaseStuff.

Le opzioni per risolvere questo:

  • aggiungere i nuovi metodi per l'interfaccia per garantire che la primavera può delega loro
  • Spostarsi utilizzando proxy basati CGLIB (questi non si basano su interfacce)
+0

No, la classe è un POJO, nessuna interfaccia implementata per tutto –

+0

Oh bene. Ancora. È tutto vero :) –

+0

Non ero in disaccordo io (stavo solo dicendo che non si applicava in questo caso :-) –