2011-01-25 5 views
10

Sto provando a creare un server multithreading. Il problema è che ottengo il seguente errore: play.exceptions.JPAException: il contesto JPA non è inizializzato. JPA Entity Manager si avvia automaticamente quando una o più classi annotate con l'annotazione @ javax.persistence.Entity vengono trovate nell'applicazione.JPA e thread nella struttura di gioco

Quello che sto cercando di fare è accedere al db dal nuovo thread ecco il codice

package controllers; 
import java.util.Iterator; 
import java.util.List; 
import models.Ball; 


public class MainLoop extends Thread { 

@Override 
public void run() { 
    List<Ball> balls; 
    new Ball(5,5,2,2,10,15); 
    while (true){ 
     balls = Ball.all().fetch(); //Here throws an exception 

     for (Iterator iterator = balls.iterator(); iterator.hasNext();) { 
      Ball ball = (Ball) iterator.next(); 
      ball.applyForces(); 
     } 
    } 
} 
} 

Tutte le idee?

risposta

12

Non usare filo di pianura, utilizzare i lavori invece:

@OnApplicationStart 
public class MainLoop extends Job { 
     public void doJob() { 
       new BallJob().now(); 
     } 
} 

E BallJob:

public class BallJob extends Job { 
public void doJob() { 
    List<Ball> balls; 
    new Ball(5,5,2,2,10,15); 
    while (true){ 
     balls = Ball.all().fetch(); 
     for (Iterator iterator = balls.iterator(); iterator.hasNext();) { 
      Ball ball = (Ball) iterator.next(); 
      ball.applyForces(); 
     } 
    } 
} 
1

Immagino che il thread venga avviato prima che Play abbia la possibilità di avviare JPA Entity Manager.

Se la classe del modello è annotata con @Entity, il gestore dell'entità sarebbe stato creato e il tuo errore non compariva.

Quindi, hai un paio di opzioni. O,

  1. È possibile creare un PlayPlugin, con una priorità inferiore rispetto ai processi Play standard su Application Start.
  2. È possibile avviare il thread da un processo di bootstrap. Ciò assicurerà che Play abbia avuto la possibilità di avviarsi correttamente prima di iniziare a interagire con il server. Per vedere di più posti di lavoro di bootstrap, vedere http://www.playframework.org/documentation/1/jobs#concepts

Personalmente, vorrei andare con l'opzione 2. E 'meglio documentato, e giocare i plugin sono pensati più per estendere il quadro piuttosto che modificare l'ordine di elaborazione.

4

Aggiornamento

Questo è più ordinato di quello che c'è sotto:

JPAPlugin.startTx(false); 
// Do your stuff 
JPAPlugin.endTx(false); 

Aveva un problema simile oggi.

È necessario creare nuovi EntityManager e delle transazioni per ogni thread e impostarlo in JPA classe.

Play utilizza ThreadLocal per mantenere è EntityManager in APP, quindi è nullo per il tuo thread creato. Purtroppo non è possibile utilizzare i metodi di supporto in JPA per farlo (sono pacchetti privati) e devi utilizzare direttamente ThreadLocal. Ecco come si può fare questo:

class Runner extends Runnable { 
    @Override 
    public void run() { 
     if (JPA.local.get() == null) { 
      EntityManager em = JPA.newEntityManager(); 
      final JPA jpa = new JPA(); 
      jpa.entityManager = em; 
      JPA.local.set(jpa); 
     } 

     JPA.em().getTransaction().begin(); 
     ... DO YOUR STUFF HERE ... 
     JPA.em().getTransaction().commit(); 
    } 
} 

Io lo uso con singolo esecutore filo da java.util.concurrent senza problemi.

+2

Aggiornamento: è possibile rimuovere tutto il materiale JPA e sostituirlo con JPAPlugin.startTx e JPAPlugin.closeTx. –

+0

Ho modificato la tua risposta per includere il tuo commento. – ripper234

+0

Come lavorare con JPA.local con Play Framework 1.4 ?, cambia completamente – javaboygo