带CDI的服务线程中的@Transactional EntityManager

时间:2017-11-06 14:42:54

标签: multithreading java-ee entitymanager transactional

我在JEE CDI应用程序的业务层中有一项服务,需要执行需要在单独的线程上生成的密集型工作任务。

public MyService {
    public void doTask( MyDataKey myDataKey ) {
       MyThread myThread = new MyThread( myDataKey );
       myThread.run();
    }
}

public MyThread : extends Thread {
    MyDataKey myDataKey;
    ...
    public MyThread( MyDataKey inMyDataKey ) {
       this.myDataKey = inMyDataKey;
    }
    public void run() {
        ...
        myWorkerService.doWork( this.myDataKey );
    }
}

public MyWorkerService {
    @PersistenceContext( unitName = "default" ) EntityManager em;
    ...
    @Transactional
    public void doWork( MyDataKey myDataKey ) {
        MyData myData = em.find( myDataKey.getId() );
        myData.setStatus( "Changed" );
        ... (Set values for other properties)
        MyDataChild myDataChild = new MyDataChild( "Some Value" );
        em.persist( myDataChild );
        myData.addChild( myDataChild );
        // According to documentation, flush should update myData
        // and em.merge( myData ) is not needed.
    }
    // When doWork() is exited, the transaction should commit the changes
    // to MyData upon EM flush.
}

我需要能够使用EntityManager来保存与MyData相关的新元素并更新MyData属性。奇怪的是,em.persist(myDataChild)工作并且存储了新的数据元素,但是不保留对它的引用和对myData属性的更改。

我遇到的问题是如何使用CDI注入WorkerService以获取MyThread类中run()操作范围内的EntityManager的访问权。

如果我在MyService中创建MyWorkerService并将其传递给线程,则EM flush显然不起作用,以便在退出@Transactional方法并且MyThread.run()完成后不保留对MyData属性的更改

例如:

public MyService {
    @Inject MyWorkerService myWorkerService;
    ...
    public void doTask( MyDataKey myDataKey ) {
       MyThread myThread = new MyThread( myWorkerService, myDataKey );
       myThread.run();
    }
}


public MyThread : extends Thread {
    MyWorkerService myWorkerService;
    MyDataKey myDataKey;
    ...
    public MyThread( MyWorkerService inMyWorkerService, MyDataKey inMyDataKey ) {
       this.myWorkerService = inMyWorkerService;
       this.myDataKey = inMyDataKey;
    }
    public void run() {
        ...
        myWorkerService.doWork( this.myDataKey );
    }
}

以上代码是实际代码的简化版本

这似乎发生的问题是,因为MyWorkerService是在线程run()上下文之外注入的,并且EntityManager类似地在父服务上下文中创建,所以当在EntityManager中使用EntityManager时会出现一些问题。在run()上下文中执行的@Transactional worker操作以及对MyData的更改不会传播到DB,也不会被MyData实例保留。

以下是我正在寻求答案的问题:

1)将MyWorkerService传递给MyThread(如上所示)是设置工作服务和EntityManager以在thread.run()中使用的可行方法吗?即确保每个人注射CDI。

2)为什么@Transactional doWork()操作中对MyData实例的更改没有保留或存储?

3)有没有更好的方法在thread.run()操作的上下文中获取EntityManager,以确保更好的线程安全使用?

4)需要更改哪些代码才能以线程安全的方式工作,并在thread.run()调用中正确使用EntityManager和@Transactional?

1 个答案:

答案 0 :(得分:0)

Java EE不鼓励使用创建线程,原因很多! Java EE是关于托管对象的, 所以不应该尝试创建和管理线程之类的对象

幸运的是,您并不是第一个需要执行此类任务的人。您可以选择多种方式进行工作,并处于托管环境中,您的注射仍然有效。以下是一些更常见的内容:

祝你好运!回发你最终选择的内容以及为什么