Wie mache ich einen Hintergrund-Thread richtig, wenn ich Spring Data und Hibernate verwende?

8

Ich erstelle eine einfache Tomcat-Webanwendung, die Spring Data und Hibernate verwendet. Es gibt ein Endpunkt, der viel Arbeit macht, daher möchte ich die Arbeit in einen Hintergrund-Thread verlagern, so dass die Web-Anfrage nicht länger als 10 Minuten hängen bleibt, während die Arbeit erledigt wird. Also habe ich einen neuen Service in einem component-scan'd Paket geschrieben:

%Vor%

Dann haben Sie die ThreadPoolTaskExecutor im Frühjahr konfiguriert:

%Vor%

Das funktioniert alles super. Das Problem kommt jedoch von Hibernate. Innerhalb meiner Runable, Abfragen nur die Hälfte arbeiten. Ich kann tun:

%Vor%

Aber wenn ich Felder lazy geladen habe, schlägt es fehl:

%Vor%

Ich erhalte den folgenden Fehler:

%Vor%

So sieht es für mich aus (Hibernate-Neuling), dass der neue Thread keine Sitzung mit diesen selbst erstellten Threads hat, aber Spring Data automatisch neue Sitzungen für HTTP-Request-Threads erstellt.

  • Gibt es eine Möglichkeit, eine neue Sitzung manuell innerhalb der Sitzung zu starten?
  • Oder eine Möglichkeit, dem Thread-Pool mitzuteilen, dass er es für mich erledigt?
  • Was ist die übliche Praxis für diese Art von Arbeit?

Ich konnte etwas damit umgehen, indem ich alles innerhalb einer @Transactional -Methode mache, aber ich lerne schnell, dass das keine sehr gute Lösung ist, da ich dadurch keine Methoden verwenden kann, die einfach funktionieren gut für Web-Anfragen.

Danke.

    
Joel 23.07.2014, 16:34
quelle

3 Antworten

19

Mit Spring brauchst du keinen eigenen Executor. Eine einfache Anmerkung @Async erledigt die Arbeit für Sie. Beschriften Sie einfach Ihre heavyMethod in Ihrem Dienst damit und geben Sie void oder ein Future -Objekt zurück und Sie erhalten einen Hintergrund-Thread. Ich würde vermeiden, die asynchrone Annotation auf der Controller-Ebene zu verwenden, da dies einen asynchronen Thread im Executor des Anfragepools erzeugt und Sie möglicherweise keine 'Anforderungsakzeptoren' mehr haben.

Das Problem mit Ihrer verzögerten Ausnahme kommt, wie Sie vermutet haben, vom neuen Thread, der keine Sitzung hat. Um dieses Problem zu vermeiden, sollte Ihre asynchrone Methode die gesamte Arbeit behandeln. Stellen Sie keine zuvor geladenen Entitäten als Parameter bereit. Der Service kann einen EntityManager verwenden und kann auch transaktional sein.

Ich selbst füge @Async und @Transactional nicht zusammen, damit ich den Service auf jede Art ausführen kann. Ich erstelle nur einen asynchronen Wrapper um den Service herum und benutze diesen stattdessen, falls nötig. (Dies vereinfacht zum Beispiel das Testen)

%Vor%     
Martin Frey 23.07.2014 17:35
quelle
0

Wahrscheinlich haben Sie eine Transaktion in Ihrem DAO-Code und Spring schließt die Sitzung beim Schließen der Transaktion.

Sie sollten Ihre gesamte Geschäftslogik in eine einzige Transaktion komprimieren.

Sie können SessionFactory in Ihren Code einfügen und SessionFactory.openSession() method verwenden.
Das Problem ist, dass Sie Ihre Transaktionen verwalten müssen.

    
alobodzk 23.07.2014 16:50
quelle
-1

Methode # 1: JPA Entity Manager

Im Hintergrund-Thread: Injizieren Sie den Entity Manager oder holen Sie ihn aus dem Spring-Kontext oder übergeben Sie ihn als Referenz:

%Vor%

Erstellen Sie anschließend einen neuen Entity Manager, um die Verwendung eines gemeinsamen Entity Managers zu vermeiden:

%Vor%

Jetzt können Sie die Transaktion starten und Spring DAO, Repository, JPA, etc

verwenden %Vor%

Methode 2: JdbcTemplate

Falls Sie Änderungen auf niedriger Ebene benötigen oder Ihre Aufgabe einfach genug ist, können Sie dies manuell mit JDBC und Abfragen tun:

%Vor%

und dann irgendwo in deiner Methode:

%Vor%

Randnotiz: Ich würde empfehlen, @Transactional nicht zu verwenden, es sei denn, Sie verwenden JTA oder vertrauen auf den JpaTransactionManager.

    
alexzender 23.02.2018 20:50
quelle