JPA и темы в игровых рамках - PullRequest
10 голосов
/ 25 января 2011

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

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

Есть идеи?

Ответы [ 4 ]

12 голосов
/ 25 января 2011

Не используйте простой поток, используйте задания вместо:

@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();
        }
    }
}
4 голосов
/ 20 августа 2012

Обновление

Это точнее, чем то, что показано ниже:

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

Была похожая проблема сегодня.

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

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 без проблем.

1 голос
/ 25 января 2011

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

Если ваш класс Model помечен @Entity, то менеджер сущностей был бы создан, и ваша ошибка не появилась бы.

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

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

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

0 голосов
/ 13 февраля 2019
class Runner extends Runnable {
     @Override
     public void run() {
        EntityManager em = JPA.newEntityManager();
        em.setFlushMode(FlushModeType.COMMIT);
        JPA jpa = new JPA();
        jpa.bindForCurrentThread(JPA.DEFAULT, em, false);

         em.getTransaction().begin();
        // ... DO YOUR STUFF HERE ...
         em.getTransaction().commit();
     }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...