Reputation: 316
We are using Jboss 7.1.1 in an application mostly generated by Jboss Forge, however we added a repository layer for all domain related code.
I was trying to create a Startup bean to initialize the database state. I would like to use my existing repositories for that.
My repositories all have an extended PersistenceContext injected into them. I use these from my View beans that are @ConversationScoped @Stateful beans, by using the extended context my entities remain managed during a conversation.
First I tried this:
@Startup
@Singleton
public class ConfigBean {
@Inject
private StatusRepository statusRepository;
@Inject
private ZipCode zipCodeRepository;
@PostConstruct
public void createData() {
statusRepository.add(new Status("NEW"));
zipCodeRepository.add(new ZipCode("82738"));
}
}
Example repository:
@Stateful
public class ZipCodeRepository {
@PersistenceContext(PersistenceContextType.EXTENDED)
private EntityManger em;
public void add(ZipCode zipCode) {
em.persist(zipCode);
}
....
}
This ends up throwing an javax.ejb.EJBTransactionRolledbackException on Application startup with the following message:
JBAS011437: Found extended persistence context in SFSB invocation call stack but that cannot be used because the transaction already has a transactional context associated with it. This can be avoided by changing application code, either eliminate the extended persistence context or the transactional context. See JPA spec 2.0 section 7.6.3.1.
I struggled finding a good explanation for this, and actually figured that since EJB's and their injection are handled by proxies all the PersistenceContext injection and propagation would be handled automatically. I guess I was wrong.
However, while on this trail of thought I tried the following:
@Startup
@Singleton
public class ConfigBean {
@Inject
private SetupBean setupBean;
@PostConstruct
public void createData() {
setupBean.createData();
}
@Stateful
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public static class SetupBean {
@Inject
private StatusRepository statusRepository;
@Inject
private ZipCode zipCodeRepository;
public void createData() {
statusRepository.add(new Status("NEW"));
zipCodeRepository.add(new ZipCode("82738"));
}
}
}
This does the trick. All I did was wrap the code in a Stateful SessionBean that is a static inner class of my Singleton bean.
Does anyone understand this behavior? Because while everything works now I'm still a bit estranged as to why it works this way.
Upvotes: 5
Views: 4033
Reputation: 11612
- A container-managed extended persistence context can only be initiated within the scope of a stateful session bean. It exists from the point at which the stateful session bean that declares a dependency on an entity manager of type PersistenceContextType.EXTENDED is created, and is said to be bound to the stateful session bean.
From the posted code, it seems ZipCodeRepository
isn't itself stateful bean, but you're calling it from one such bean.
In this case, you are initiating PersistenceContextType.TRANSACTION
from ConfigBean
& propogates through ZipCodeRepository
having PersistenceContextType.EXTENDED
& it tries to join the transaction, hence the exception.
Invocation of an entity manager defined with PersistenceContext- Type.EXTENDED will result in the use of the existing extended persistence context bound to that component.
When a business method of the stateful session bean is invoked, if the stateful session bean uses container managed transaction demarcation, and the entity manager is not already associated with the current JTA transaction, the container associates the entity manager with the current JTA transaction and calls EntityManager.joinTransaction. If there is a different persistence context already associated with the JTA transaction, the container throws the EJBException.
While in later case, you're creating a new transaction in SetupBean
for each invocation with TransactionAttributeType.REQUIRES_NEW
which is of extended type, as it's a stateful bean.
Therefore, adding SetupBean
as stateful session bean initiating new transaction for each invocation & later calling ZipCodeRepository
doesn't result in exception. ZipCodeRepository
will join the same transaction as initiated by SetupBean
.
Upvotes: 3