2016-08-08 43 views
0

Wie können Sie Akka verwenden, um eine Operation auf der db mit Hibernate auszuführen und den Webclient nicht zu blockieren?Play Framework 2.5.4 Asynchrone Ruhezustandsoperationen

UPDATE:

Stellt sich heraus, der Fehler durch die dao.get() Methode verursacht wurde. Ich änderte die start() -Methode, um ein tatsächliches Objekt und nicht die ID von der Datenbank zu nehmen, und jetzt bekomme ich keinen Fehler, aber nichts passiert (es bleibt auf em.merge() hängen, wie ich vorher sagte).

public CompletionStage<Result> start(SomeObject object) { 
    ExecutionContext ec = Akka.system().dispatchers().lookup("akka.actor.db-context"); 
    return CompletableFuture.supplyAsync(() -> doStuff(object), play.libs.concurrent.HttpExecution.fromThread(ec)) 
      .thenApply(i -> ok("Got result: " + i)); 
} 

OUTDATED:

Wenn ich versuche, so etwas wie:

@Transactional 
public CompletionStage<Result> start(Long id) { 
    ExecutionContext ec = Akka.system().dispatchers().lookup("akka.actor.db-context"); 
    return CompletableFuture.supplyAsync(() -> doStuff(dao.get(id)), play.libs.concurrent.HttpExecution.fromThread(ec)) 
      .thenApply(i -> ok("Got result: " + i)); 
} 

Wo doStuff führt nur eine entityManager.merge(), erhalte ich:

[Abschluss Ausnahme: org .hibernate.SessionException: Session wird geschlossen]

oder

[CompletionException: java.lang.IllegalStateException: EntityManager geschlossen ist]

Wenn ich den Code unten um den obigen Prozess zu starten:

@Transactional 
public Result mainMethod() { 
    List<SomeObject> allObjects= dao.getAll(); 
    int size = allObjects.size(); 
    for(int i = 0; i < size; i++) { 
     start(allObjects.get(i).getId()); 
    } 
    return ok("Started"); 
} 

Die neu erstellten Threads (Aktoren) geraten beim Versuch einer Datenbankoperation in eine Endlosschleife.

Danke!

komplette Stack-Trace:

play.api.http.HttpErrorHandlerExceptions $$ Anon $ 1: Ausführung Ausnahme [[CompletionException: org.hibernate.SessionException: Session geschlossen ist]] bei Spiel. api.http.HttpErrorHandlerExceptions $ .throwableToUsefulException (HttpErrorHandler.scala: 280) bei play.api.http.DefaultHttpErrorHandler.onServerError (HttpErrorHandler.scala: 206) bei play.api.GlobalSettings $ class.onError (GlobalSettings.scala: 160) bei play.api.DefaultGlobal $ .onError (GlobalSettings.scala : 188) bei play.api.http.GlobalSettingsHttpErrorHandler.onServerError (HttpErrorHandler.scala: 98) bei play.core.server.netty.PlayRequestHandler $$ anonfun $ 2 $$ anonfun $ $ 1.applyOrElse (PlayRequestHandler.scala gelten: 100) bei play.core.server.netty.PlayRequestHandler $$ anonfun $ 2 $$ anonfun $ gelten $ 1.applyOrElse (PlayRequestHandler.scala: 99) bei scala.concurrent.Future $$ anonfun $ recoverWith $ 1.Apply (Future.scala: 344) um scala.concurrent.Future $$ anonfun $ recover Mit $ 1.apply (Future.scala: 343) bei scala.concurrent.impl.CallbackRunnable.run (Promise.scala: 32) um spielen .api.libs.iteratee.Execution $ trampoline $ .executeScheduled (Ausführung.Scala: 109) bei play.api.libs.iteratee.Execution $ $ Trampolin .Execute (Execution.scala: 71) bei scala.concurrent.impl.CallbackRunnable.executeWithValue (Promise.scala: 40) bei scala.concurrent.impl.Promise $ DefaultPromise.tryComplete (Promise.scala: 248) bei scala.concurrent.Promise $ class.complete (Promise.scala: 55) um scala.concurrent.impl.Promise $ DefaultPromise.complete (Promise.scala: 153) bei scala.concurrent.java8.FuturesConvertersImpl $ P.accept (FutureConvertersImpl.scala: 94) bei scala.concurrent.java8.FuturesConvertersImpl $ P.accept (FutureConvertersImpl.scala: 89) bei java.util.concurrent.CompletableFuture.uniWhenComplete (CompletableFuture.java:760) bei java.util.concurrent.CompletableFuture $ UniWhenComplete.tryFire (CompletableFuture.java:736) bei java.util.concurrent.CompletableFuture.postComplete (CompletableFuture.java:474) bei java.util.concurrent.CompletableFuture $ AsyncSupply.run (CompletableFuture.java:1595) bei play.core.j.HttpExecutionContext $$ $ Anon 2.run (HttpExecutionContext.scala: 56) bei akka.dispatch.TaskInvocation.run (AbstractDispatcher.scala: 39) bei akka.dispatch.ForkJoinExecutorConfigurator $ AkkaForkJoinTask.exec (AbstractDispatcher.scala: 405) bei scal a.concurrent.forkjoin.ForkJoinTask.doExec (ForkJoinTask.java:260) bei scala.concurrent.forkjoin.ForkJoinPool $ WorkQueue.runTask (ForkJoinPool.java:1339) bei scala.concurrent.forkjoin.ForkJoinPool.runWorker (ForkJoinPool.java:1979) bei scala.concurrent.forkjoin.ForkJoinWorkerThread.run (ForkJoinWorkerThread.java:107) verursacht durch: java.util.concurrent.CompletionException: org.hibernate.SessionException: Session ist beendet! bei java.util.concurrent.CompletableFuture.encodeThrowable (CompletableFuture.java:273) bei java.util.concurrent.CompletableFuture.completeThrowable (CompletableFuture.java:280) bei java.util.concurrent.CompletableFuture $ AsyncSupply. run (CompletableFuture.java:1592) ... 7 gemeinsame Rahmen ausgelassen Verursacht von: org.hibernate.SessionException: Sitzung ist geschlossen! bei org.hibernate.internal.AbstractSessionImpl.errorIfClosed (AbstractSessionImpl.java:133) bei org.hibernate.internal.SessionImpl.setCacheMode (SessionImpl.java:1455) bei org.hibernate.jpa.spi.AbstractEntityManagerImpl. finden (AbstractEntityManagerImpl.java:1144) bei org.hibernate.jpa.spi.AbstractEntityManagerImpl.find (AbstractEntityManagerImpl.java:1068) bei daos.dao.get (dao.java:45) bei controllers.DemoController.lambda $ start $ 0 (DemoController.java:195) um java.util.concurrent.CompletableFuture $ AsyncSupply.run (CompletableFuture.java:1590) ... 7 gemeinsame Frames weggelassen

LE:

Versuchte mit:

ExecutionContext ec = Akka.system().dispatchers().lookup("akka.actor.db-context"); 
return CompletableFuture.supplyAsync(() -> jpa.withTransaction("default", true,()-> doStuff(dao.get(id))), play.libs.concurrent.HttpExecution.fromThread(ec)) 
       .thenApply(i -> ok("Got result: " + i)); 

Und ich bekomme den gleichen Fehler.

+0

Vielleicht ist es falsch, ich tue, Also meine einfachere Frage: Wie können Sie verhindern, dass ein Nicht-Web-Client Datenbankoperationen mit Play's Akka Actors blockiert? Starten Sie beispielsweise eine Aktualisierungsoperation, die einige Felder auf bestimmte Werte setzt, ohne dass der Client, der die Controlleraktion gesendet hat, auf das Ergebnis wartet (da dies für den Benutzer nicht relevant ist). – taviss

Antwort

-1

Die @Transactional Annotation ist auf der synchronen Methode. Ich nehme an, dass Sie eine Transaktion innerhalb des asynchronen Blocks benötigen. Sie können für die JPA-Methode mit Transaktion anstelle der Annotation verwenden. Seine Unterschrift (von Play java api)

/** 
* Run a block of code in a JPA transaction. 
* 
* @param name The persistence unit name 
* @param readOnly Is the transaction read-only? 
* @param block Block of code to execute 
* @param <T> type of result 
* @return code execution result 
*/ 
public <T> T withTransaction(String name, boolean readOnly, Supplier<T> block); 

Es soll so etwas wie diese:

  1. Inject JPAApi in der Steuerung:

    @Inject 
    JPAApi jpa; 
    
  2. mit Aufruf zum JPA Implementieren asynchronen Testamentsvollstrecker:

    jpa.withTransaction("default", true,()->doStuff(dao.get(id)), play.libs.concurrent.HttpExecution.fromThread(ec)) 
        .thenApply(i -> ok("Got result: " + i))); 
    

    Beachten Sie das zweite Argument wahr im Aufruf an mitTransaktion ist für readOnly Betrieb (in Ihrem Fall).

  3. Ändern Sie die DAO-Klasse, um JPAAPi ebenfalls zu verwenden.
+0

Hallo, ich kann keine "withTransaction" -Methode mit dieser Signatur finden. – taviss

+0

Ich habe die Antwort bearbeitet - die Signatur und den Link zum API doc hinzugefügt. Ich benutze diese Methode in meinem Projekt, nur ich mache es synchron. Ich ziehe es vor @Transactional Annotation (vergessen Sie nicht, es zu entfernen!). – asch

+0

Ich bekomme immer den gleichen Fehler. – taviss

0

Ich löste es mithilfe was @asch vorgeschlagen:

Controller:

@Inject 
    private JPAApi jpa; 
    [...] 
    return CompletableFuture.supplyAsync(() -> jpa.withTransaction("default", false,()-> doStuff(objectDAO.get(id))), play.libs.concurrent.HttpExecution.fromThread(ec)) 
       .thenApply(i -> ok("Got result: " + i)); 

Und in der doStuff Methode Klasse:

private static final JPAApi jpaApi = Play.current().injector().instanceOf(JPAApi.class); 
    [...] 
    jpaApi.withTransaction(() -> { 
     EntityManager em = jpaApi.em(); 
     em.merge(object); 
    }); 
    [...]