Search code examples
jakarta-eeejbbulktransactional

What's a clean, standard way to get multiple transactions in an EJB?


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.


Solution

  • 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.