问题
I am currently trying to figure out the best way to get a entity manager and a usertransaction in my application.
In JBoss 5.1 I was able to inject it directly into the JSP file, but this is not permitted anymore:
<%!@PersistenceContext(unitName = "unitname")
public EntityManager em;
@Resource
UserTransaction utx;
%>
I have to access em
and utx
from different locations in my application such as Servlets and Controller classes. So it would be great to have it in one place and to access it globally, but I did not figure out yet how to do it. Any hint would be appreciated.
回答1:
I found out how to get the EntityManager and the UserTransaction in Servlets, Controller Classes and JSP files.
Let's start with SessionBeans. I redefined all my controller classes as Stateless SessionBeans. Session Beans allow ressource injection. This is how I did it:
@Stateless
public class UserHandling {
@PersistenceContext(unitName = "SSIS2")
private static EntityManager em;
@Resource
private UserTransaction utx;
public User getUser(int userId) {
User userObject = em.find(User.class, userId);
return userObject;
}
}
If another Session Bean is needed in a Session Bean Class, it can be injected with the @EJB
annotation:
@Stateless
public class UserHandling {
@PersistenceContext(unitName = "SSIS2")
private static EntityManager em;
@Resource
private UserTransaction utx;
@EJB
UserHandling uh; RoleHandling rh;
public User getUser(int userId) {
User userObject = em.find(User.class, userId);
return userObject;
}
}
In JSP files, you can get the Session Bean Controller classes by lookup the InitialContext:
<%
InitialContext ic = new InitialContext();
UserHandling uh = (UserHandling) ic.lookup("java:app/" + application.getContextPath() + "/UserHandling");
%>
回答2:
The Issue being Solved
Servlets and JSPs must be stateless because they are shared across multiple threads. An EntityManager
does keep state, and so a single instance cannot be shared by concurrent threads.
We'd like a smooth/seamless mechanism for obtaining an EntityManager, preferably managed by the Servlet container.
Servlet-Container Managed Persistence Context
Let's introduce a ContainerManagedPersistenceContext
into the Servlet/JSP runtime.
We'll define it in a moment. Let's first look at how it can be used to inject an EntityManager
into JSP
:
<%! @Inject
@ContainerManagedPersistenceContext.Qualifier
public EntityManager em;
%>
or, better yet into a controller (because we do want to separate data recovery/business logic from our JSP, right?):
@Named
@SessionScoped
public class SessionController implements Serializable
{
...
@Inject
@ContainerManagedPersistenceContext.Qualifier
private EntityManager em;
}
But I don't (yet) have CDI available
If you don't have CDI, but you do have JSF, then the context can be injected as an old-style standard JSF @ManagedProperty
:
@Named
@SessionScoped
public class SessionController implements Serializable
{
...
@ManagedProperty(value = "#{containerManagedPersistenceContext}")
ContainerManagedPersistenceContext cmpContext;
...
public void myMethod() {
EntityManager em = cmpContext.getEntityManager();
try {
...
} finally {
em.close();
}
}
}
Remember that - for all the same reasons that we have to go to this effort in the first place - the EntityManager
must never be cached/preserved anywhere.
Transactions
Use the EntityTransaction
provided by the EntityManager
for begin/commit/rollback:
EntityTransaction transaction = em.getTransaction();
ContainerManagedPersistenceContext
This is defined as an application scoped controller and a PersistenceContext
:
@PersistenceContext(name = ContainerManagedPersistenceContext.NAME,
unitName = ContainerManagedPersistenceContext.UNIT_NAME)
@ApplicationScoped
public class ContainerManagedPersistenceContext implements Serializable
{
private static final long serialVersionUID = 1L;
// UNITNAME must match persistence.xml: <persistence-unit name="myUnitName">
public static final String UNITNAME = "myUnitName";
public static final String NAME = "persistence/" + UNIT_NAME;
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD,
ElementType.PARAMETER, ElementType.TYPE})
public static @interface Qualifier { }
// Servlets must be stateless (shared across multiple threads).
// EntityManager is not stateless (cannot be shared across threads).
// Obtain Container Managed EntityManager - and do NOT cache.
@Produces @Qualifier
public static EntityManager getEntityManager() throws NamingException
{
EntityManager lookup = InitialContext.doLookup("java:comp/env/" + NAME);
return lookup;
}
}
Limitations
As written, this defines a specifically named PersistenceContext for the Servlet container. Since the unitName
isn't parameterized, it doesn't provide the level of flexibility as:
@PersistenceContext(unitName = "unitname")
public EntityManager em;
Alternatives
Define a PersistenceContext on your Servlet, and use JNDI name lookup.
回答3:
Well i think you should see the problem from a different point of view?
Why do you need to call an EJB
from JSP
page?
JSP
page shouldn't contain codes and it is used only for presentation.
I suggest to you to add a Servlet
or a JSF framework
and let the Servlet
or the ManagedBean
to call EJB
and then pass the parameters to the JSP
.
Hope it helps you
回答4:
You can use the following snippet to retrieve EntityManager and/or UserTransaction via JNDI lookup:
try {
Context ic = (Context) new InitialContext();
EntityManager em = (EntityManager) ic.lookup("java:comp/env/*<persistence-context-name>*");
UserTransaction ut = (UserTransaction) ic.lookup("java:comp/env/UserTransaction");
} catch (NamingException ne) {...}
来源:https://stackoverflow.com/questions/11773975/best-practice-to-get-entitymanger-and-usertransaction-from-jsp