简体   繁体   English

JPA和Threads在play框架中

[英]JPA and Threads in play framework

I'm trying to create a multithreading server. 我正在尝试创建一个多线程服务器。 The problem is that I get the following error: play.exceptions.JPAException: The JPA context is not initialized. 问题是我收到以下错误: play.exceptions.JPAException:JPA上下文未初始化。 JPA Entity Manager automatically start when one or more classes annotated with the @javax.persistence.Entity annotation are found in the application. 当在应用程序中找到使用@ javax.persistence.Entity批注注释的一个或多个类时,JPA实体管理器会自动启动。

What I'm trying to do is access the db from the new thread here's the code 我要做的是从新线程访问数据库这里是代码

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

Any ideas? 有任何想法吗?

Don't use plain thread, use jobs instead : 不要使用纯线程,而是使用job:

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

And BallJob : 和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();
        }
    }
}

Update 更新

This is neater than what's below: 这比下面的内容更整洁:

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

Had similar problem today. 今天有类似的问题。

You have to create new EntityManager and transaction for each thread and set it in JPA class. 您必须为每个线程创建新的EntityManager和事务,并在JPA类中设置它。

Play uses ThreadLocal to keep it's EntityManager in JPA , so it's null for your created thread. Play使用ThreadLocal将它的EntityManagerJPA中 ,因此对于您创建的线程它是null。 Unfortunately you cannot use helper methods in JPA to do it (they are package private) and you have to use ThreadLocal directly. 遗憾的是,您无法在JPA中使用帮助程序方法(它们是包私有),您必须直接使用ThreadLocal Here's how you can do this: 这是你如何做到这一点:

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

I use it with single thread executor from java.util.concurrent without any problems. 我使用java.util.concurrent中的单线程执行器没有任何问题。

I am guessing your thread is kicked off before Play has a chance to start the JPA Entity Manager. 我猜你的线程在Play有机会启动JPA实体经理之前就开始了。

If your Model class is annotated with @Entity, then the entity manager would have been created and your error would not appear. 如果您的Model类使用@Entity注释,那么将创建实体管理器并且不会出现您的错误。

So, you have a couple of options. 所以,你有几个选择。 Either, 要么,

  1. You can create a PlayPlugin, with a lower priority than the Play standard onApplicationStart processes. 您可以创建一个PlayPlugin,其优先级低于Play标准onApplicationStart进程。
  2. You can kickstart your thread from a bootstrap job. 您可以从引导作业启动线程。 This will ensure that Play has had a chance to start up correctly before you start interacting with the server. 这将确保Play在您开始与服务器交互之前有机会正确启动。 To see more about bootstrap jobs, see http://www.playframework.org/documentation/1/jobs#concepts 要了解有关引导作业的更多信息,请参阅http://www.playframework.org/documentation/1/jobs#concepts

Personally, I would go with option 2. It is better documented, and play plugins are meant more for extending the framework rather than changing the order of processing. 就个人而言,我会选择选项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();
     }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM