问题
I have a batchEdit(List<E> entity)
that calls an edit(E entity)
function in a loop, while each edit() has it's own transaction so that failed edits don't rollback the good edits. I currently have it implemented like so:
Option 1
@Stateless
@TransactionManagement( value = TransactionManagementType.CONTAINER )
public class Service<E> {
@Resource
private SessionContext context;
@Override
@TransactionAttribute( value = TransactionAttributeType.REQUIRES_NEW )
public E edit( E entity ) {
//edit code
}
@Override
public List<E> bulkEdit( List<E> entities ) {
for(E entity : entities){
//case 1: Regular edit, Does not create a new transaction!
//edit(entity);
//case 2: Hacky edit, creates a new transaction
context.getBusinessObject( Service.class ).editPersistNulls( entity );
}
}
}
According to this stackoverflow discussion, The @TransactionAttribute
is ignored in my case 1 because it doesn't cross any EJB boundaries, so batchEdit()
calls edit()
as if it wasn't annotated. Using the context.getBusinessObject()
function in case 2 to grab a reference of the bean causes the TransactionManagement annotation to work, but it seems really weird to go through all of that.
Option 2
The other option I have is to change to bean managed transactions:
@TransactionManagement( value = TransactionManagementType.BEAN )
But then I would lose the "JPA Magic" and have to manage the transactions everywhere. I don't think other people on my team would want to go through that, so if there's a better or standard way to do this, any insight is appreciated.
We are using OpenJPA and EJBs, but we are trying to stay close to the JPA standard.
回答1:
I guess that "hacky" is in the eye of the beholder. context.getBusinessObject
exists precisely so that you can do this kind of thing.
The alternative is to use a second class:
@Stateless
public class BulkService<E> {
@EJB
private Service<E> service;
public List<E> bulkEdit( List<E> entities ) {
for(E entity : entities) {
service.editPersistNulls( entity );
}
}
}
Beware of large entity lists though, as your encompassing transaction can time out.
If you're using a Java EE 7 compliant server implementation then consider using the JSR-352 Batch Applications support.
回答2:
You can just inject EJB in self.
@Stateless
public class Service<E> {
@EJB
private Service<E> self;
@TransactionAttribute(REQUIRES_NEW)
public void edit(E entity) {
// ...
}
@TransactionAttribute(NOT_SUPPORTED)
public void bulkEdit(List<E> entities) {
for (E entity : entities) {
self.edit(entity);
}
}
}
Better would be to make it @Asynchronous
. It's faster.
@Stateless
public class Service<E> {
@EJB
private Service<E> self;
public void edit(E entity) {
// ...
}
@Asynchronous
@TransactionAttribute(REQUIRES_NEW)
public void asyncEdit(E entity) {
// ...
}
@TransactionAttribute(NOT_SUPPORTED)
public void bulkEdit(List<E> entities) {
for (E entity : entities) {
self.asyncEdit(entity);
}
}
}
It also keeps the original edit()
method free from potentially unwanted REQUIRES_NEW
transaction attribute as it might be called from other services which of course should stay in same transaction. For @Asynchronous
it makes more sense to require a new transaction on every call.
来源:https://stackoverflow.com/questions/43855018/whats-a-clean-standard-way-to-get-multiple-transactions-in-an-ejb