I have a service in the business tier of a JEE CDI application that needs to perform an intensive work task that needs to be spawned off on a separate thread.
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.
}
I need to be able to use EntityManager to persist new elements related to MyData and to update MyData attributes. Oddly enough, the em.persist( myDataChild ) works and the new data element is stored, but the reference to it and the changes to the myData attributes are not retained.
The problem that I am encountering is how to use CDI to inject the WorkerService to obtain access to the EntityManager within the scope of the run() operation in the MyThread class.
If I create MyWorkerService in MyService and pass it into the thread, the EM flush apparently does not work such that the changes to properties of MyData are not kept after the @Transactional method is exited and the MyThread.run() is completed.
For example:
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 );
}
}
The above code is a simplified rendition of the actual code
The problem that appears to happen with this is that because the MyWorkerService is injected outside of the thread run() context, and the EntityManager is similarly created in the parent service context, there is some problem when the EntityManager is used within the @Transactional worker operation that executes within the run() context and the changes to MyData are not propagated to the DB and are not retained by the MyData instance.
The following are questions that I am seeking answers:
1) Is the passing of the MyWorkerService to the MyThread (as shown above) a viable way to set up the worker service and EntityManager for use within the thread.run() ? i.e. that assures CDI injection of each.
2) Why are the changes to the MyData instance within the @Transactional doWork() operation not retained or stored?
3) Is there a better way to acquire the EntityManager within the context of the thread.run() operation that assures better thread safety usage?
4) What code needs to be changed to make this work in a manner that is thread safe and employs correct usage of the EntityManager and @Transactional within the thread.run() invocation?