views:

2988

answers:

3

I've set up a JSF application on JBoss 5.0.1GA to present a list of Users in a table and allow deleting of individual users via a button next to each user.

When deleteUser is called, the call is passed to a UserDAOBean which gets an EntityManager injected from JBoss.

I'm using the code

public void delete(E entity)
{
 em.remove(em.merge(entity));
}

to delete the user (code was c&p from a JPA tutorial). Just calling em.remove(entity) has no effect and still causes the same exception.

When this line is reached, I'm getting a TransactionRequiredException:

(skipping apparently irrelevant stacktrace-stuff)

...

20:38:06,406 ERROR [[Faces Servlet]] Servlet.service() for servlet Faces Servlet threw exception javax.persistence.TransactionRequiredException: EntityManager must be access within a transaction at org.jboss.jpa.deployment.ManagedEntityManagerFactory.verifyInTx(ManagedEntityManagerFactory.java:155) at org.jboss.jpa.tx.TransactionScopedEntityManager.merge(TransactionScopedEntityManager.java:192) at at.fhj.itm.utils.DAOImplTemplate.delete(DAOImplTemplate.java:54) at at.fhj.itm.UserBean.delete(UserBean.java:53) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

...

I already tried to wrap a manually managed transaction (em.getTransaction().begin() + .commit() ) around it, but this failed because it is not allowed within JBoss container. I had no success with UserTransaction either. Searches on the web for this issue also turned up no similar case and solution.

Has anyone experienced something similar before and found a solution to this?

A: 

Are you sure that you annotated you bean with @Stateless or register it with xml?

Try add transaction's annotation to you code, this can help you:

@TransactionAttribute(REQUIRED)
public void delete(E entity)
{
        em.remove(em.merge(entity));
}

But it seems strange, because this is default value if you don't set it explicitly.

masterzim
No luck with that @TransactionAttribute either :(The bean is NOT annotated with @Stateless but the EntityManager is with @PersistenceContext(unitName = "test).When I add the @Stateless to the class, the EntityManager is not injected anymore but null.
Kosi2801
EJB automatically creates a transaction at the beginning of any EJBean method call. Therefore, if you are not using an EJB (either `@Stateless`, `@Local`, etc. with the associated container) you will need to create your own transaction.
Drew
If you want that you bean was DAO it is be better if you annotate it with @Stateless, and use @Local interface for it. Because if if bean is not @Stateless or @Statefull then it is not EJB bean, and transaction is not managed as say Drew. Also if you new in EJB take a look at his book "Enterprise JavaBeans, 3.0" By Bill Burke, Richard Monson-Haefel
masterzim
+1  A: 

Found the missing link.

It was indeed a missing transaction but the solution was not to use the EntityManager to handle it but to add an injected UserTransaction.

@Resource
UserTransaction ut;
...
public void delete(E entity)
{
        ut.begin();
        em.remove(em.merge(entity));
        ut.commit();
}

Thanks to all suggestions which somehow over 100 corners lead to this solution.

Kosi2801
It is not very good idea to control transaction manually, this is do you code more verbose, Java2EE server can do it for you. why you don't use annotations for this?
masterzim
Because I tried. I tried very hard to find an annotation and setting which works. I tried countless variations of @Stateless, @Stateful, @Local, @Transactionattribute, etc., none of which worked and in most cases caused the EntityManager to be not injected anymore. I really have no idea, what's causing this because my previous experiences with Hibernate do exactly as you say and let the container manage the transactions. Nevertheless, it seems to be different with JPA and JSF and since this solution is easy and only used for delete and add I'll stay with it and don't spend more time on it.
Kosi2801
A: 

Know this is an old question, but just in case somebody stumbles on this like me.

Try

em.joinTransaction();
em.remove(bean);
em.flush();

That's what we use in all our @Stateful beans.

If you are using Seam, you can also use @Transactional(TransactionPropagationType.REQUIRED) annotation.

chinto