JPA和Threads在play框架中

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JPA和Threads在play框架中相关的知识,希望对你有一定的参考价值。

我正在尝试创建一个多线程服务器。问题是我收到以下错误:play.exceptions.JPAException:JPA上下文未初始化。当在应用程序中找到使用@ javax.persistence.Entity批注注释的一个或多个类时,JPA实体管理器会自动启动。

我要做的是从新线程访问数据库这里是代码

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

有任何想法吗?

答案

不要使用纯线程,而是使用job:

@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();
        }
    }
}
另一答案

更新

这比下面的内容更整洁:

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

今天有类似的问题。

您必须为每个线程创建新的EntityManager和事务,并将其设置为JPA类。

Play使用ThreadLocal将它保存在JPA中的EntityManager中,因此对于您创建的线程它是null。不幸的是,您无法在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中的单线程执行器没有任何问题。

另一答案

我猜你的线程在Play有机会启动JPA实体经理之前就开始了。

如果您的Model类使用@Entity注释,那么将创建实体管理器并且不会出现您的错误。

所以,你有几个选择。要么,

  1. 您可以创建一个PlayPlugin,其优先级低于Play标准onApplicationStart进程。
  2. 您可以从引导作业启动线程。这将确保Play在您开始与服务器交互之前有机会正确启动。要了解有关引导作业的更多信息,请参阅http://www.playframework.org/documentation/1/jobs#concepts

就个人而言,我会选择选项2.更好的文档记录,播放插件更多的是扩展框架而不是改变处理顺序。

另一答案
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();
     }
}

以上是关于JPA和Threads在play框架中的主要内容,如果未能解决你的问题,请参考以下文章

如何通过单击适配器类中代码的项目中的删除按钮来删除列表视图中的项目后重新加载片段?

用户类型、jpa 和 Play 的继承问题!框架

在 jpa play 框架中按字段查找

JPA 和 Play 框架:EntityManager 没有名为更新的持久性提供程序

基于深度学习的图像分类中代码的error

使用“in”在 Play Framework 1.x / JPA 中构建查询的更简单方法?