views:

275

answers:

3

The following code works:

@Stateless
@LocalBean
public class MyClass 
{
       @PersistenceContext(name = "MyPU")
       EntityManager em;


       public void myBusinessMethod(MyEntity e)
       {
          em.persist(e);
       }
 }

But the following hierarchy gives a TransactionRequiredException in Glassfish 3.0 (and standard JPA annotations with EclipseLink.) at the line of persist.

 @Stateless
 @LocalBean
public class MyClass extends MyBaseClass
{
       public void myBusinessMethod(MyEntity e)
       {
          super.update(e);
       }
 }



public abstract class MyBaseClass
{
       @PersistenceContext(name = "MyPU")
       EntityManager em;

       public void update(Object e)
       {
          em.persist(e);
       }
 }   

For my EJB's I collected common code in an abstract class for cleaner code. (update also saves who did the operation and when, all my entities implement an interface.)

This problem is not fatal, I can simply copy update and sister methods to subclasses but I would like to keep all of them together in a single place.

I didn't try but this may be because my base class is abstract, but I would like to learn a proper method for such a (IMHO common) use case.

A: 
Pascal Thivent
I did exactly what you suggested, implemented BaseEJB's insert and update by subclass implemented getEM(). The problem was not from persistence, but my lack of understanding of EJB transaction semantics. Thank you very much.
Emre Sahin
@Emre As I wrote, your code works fine (at least the part you showed). In other words, injection in super class just works.
Pascal Thivent
A: 

Your approach isn't wrong (if it works)

However it is more common to either use (inject) a Dao and call methods on it, or if the Dao is a redundant layer that only wraps the EntityManager, you can simply call the methods on EntityManager directly. Of course, exposing the EntityManager to subclasses via a protected getter.

getEntityManager().persist(e);
Bozho
Superclass' insert, update and delete does some extra work other than just calling EM, thus I don't want to repeat myself in the subclasses. I know it's possible to do this via Listeners, but instead I made my all entities to implement an interface and made them to have certain fields. Then BaseClass can fill these extra fields in insert/update/delete and persist entities.
Emre Sahin
A: 

The problem was not using superclass' injected entity manager, but calling another EJB's method: e.g.

@Stateless
@LocalBean
public class MyBean extends MySuperBean
{
  @EJB
  com.example.project.MyOtherBean otherBean;

  public boolean myService(String userName, MyEntity entity)
  {
     if(otherBean.checkAuthority(userName))
     { 
        super.insert(entity);
     }
   }
 }

I was using this pattern when OtherBean was not a bean and checkAuthority was a static method using (non-JTA) EntityManagerFactory. Then I changed OtherBean to extend MySuperBean too. I think, in this case, when OtherBean ends checkAuthority, JTA ends the transaction and MySuperBean's insert can't find a transaction to persist entity. Understandably Stateless EJB's don't let fellow EJB's to proceed the transaction.

As Pascal, I initially thought that injection does not work with inheritance but this problem continued when I directly called em.persist() in the subclass. After this I finally was able to check other possible causes.

Thanks for all the input.

Emre Sahin
Don't use `EntityManagerFactory` in a managed context, get an `EntityManager` injected.
Pascal Thivent