2011-01-25 5 views
10

Я пытаюсь создать многопоточный сервер. Проблема в том, что я получаю следующую ошибку: play.exceptions.JPAException: Контекст JPA не инициализирован. JPA Entity Manager автоматически запускается, когда один или несколько классов, аннотированных с помощью аннотации @ javax.persistence.Entity, находятся в приложении.JPA и Threads in play framework

То, что я пытаюсь сделать, это получить доступ к БД из нового потока вот код

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(); 
     } 
    } 
} 
} 

Любые идеи?

ответ

12

Не используйте обычную нить, использовать задания вместо:

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

И 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

Я предполагаю, что ваша тема началась, прежде чем у Play появится возможность запустить диспетчер Entity JPA.

Если ваш класс модели аннотируется с @Entity, тогда менеджер объектов был бы создан, и ваша ошибка не появится.

Итак, у вас есть несколько вариантов. Либо,

  1. Вы можете создать PlayPlugin с меньшим приоритетом, чем в стандарте Play onApplicationStart.
  2. Вы можете запускать свою нить из начальной загрузки. Это обеспечит правильную загрузку Play, прежде чем вы начнете взаимодействовать с сервером. Чтобы узнать больше о рабочих бутстраповских см http://www.playframework.org/documentation/1/jobs#concepts

Лично я бы с вариантом 2. Она лучше документально, и играть плагины предназначены больше для расширения рамок, а не изменять порядок обработки.

4

Update

Это аккуратнее, чем то, что ниже:

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

У меня была аналогичная проблема сегодня.

Вы должны создать новый EntityManager и транзакции для каждого потока и установить его в JPA класса.

Play использует ThreadLocal держать это EntityManager в JPA, так что нуль для созданного потока. К сожалению, вы не можете использовать вспомогательные методы в JPA, чтобы сделать это (они являются частными), и вы должны использовать ThreadLocal напрямую. Вот как вы можете это сделать:

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(); 
    } 
} 

Я использую его с одного потока исполнителя от java.util.concurrent без каких-либо проблем.

+2

Обновление: вы можете удалить все материалы JPA и заменить их на JPAPlugin.startTx и JPAPlugin.closeTx. –

+0

Я отредактировал ваш ответ, чтобы добавить комментарий. – ripper234

+0

Как работать с JPA.local с play framework 1.4 ?, он полностью меняется – javaboygo

Смежные вопросы