Using injected EntityManager in class hierarchies
The following code works:
@Stateless
@LocalBean
public class MyClass
{
@PersistenceContext(name = "MyPU")
EntityManager em;
public void myBusinessMethod(MyEntity e)
开发者_如何学Go {
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.
AFAIK, you can't inject into a super class, so you have to inject into a field or method of the actual EJB. You could do something like this:
public class MyBaseEJB {
public abstract EntityManager getEM();
public void update(Object e) {
getEM().persist(e);
}
}
@Stateless
public class MyEJB extends MyBaseEJB {
@PersistenceContext
EntityManager em;
public EntityManager getEM() { return em;}
}
Update: I was wrong, according to the section 5.2.3 of the Java EE 5 platform specification, injection is allowed in super class fields and methods.
I went a bit further and did a small test on my side using similar code, GlassFish v3 and EclipseLink and I can't reproduce your problem. So I suspect some kind of problem with your persistence.xml
. Could you provide it? Are you using a transaction-type="JTA"
? Just in case, here is the one I used:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence persistence_2_0.xsd" version="2.0">
<persistence-unit name="MyPU" transaction-type="JTA">
<!-- EclipseLink -->
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/q2484443</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
<property name="eclipselink.ddl-generation.output-mode" value="database"/>
</properties>
</persistence-unit>
</persistence>
BTW, I think that it's perfectly fine to skip the DAO pattern for simple data access operations. Have a look at this previous answer.
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);
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.
精彩评论