RESTEasy 服务中 EntityManager 的正确用法是什么? (SEAM、JBoss)
我的项目是一个用seam-gen生成的WAR项目。它包含一个像这样的 RESTEasy Web 服务类(简化,仅相关部分):
@Scope(ScopeType.APPLICATION)
public abstract class RestService {
@In
protected EntityManager entityManager;
@GET
@POST
@Produces("application/json")
public Object proxy() {
// invokes various subclass methods
// based on request parameters
// and returns the result
}
// further method and logic
}
并且:
@Path("/my")
@Name("myRestService")
public class MyRestService extends RestService {
public Object login(/*...*/) {
User user = getUser(email);
// ...
Token token = user.getToken();
if (token != null) {
entityManager.remove(token);
}
token = new Token();
entityManager.persist(token);
user.setToken(token);
user.setLastlogin(new Date());
entityManager.persist(user);
// ...
}
private User getUser(String email) {
try {
return (User) entityManager
.createQuery("FROM User WHERE UPPER(email) = UPPER(:email)")
.setParameter("email", email)
.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
}
如果我通过 Web 浏览器调用登录方法,它会找到正确的用户(基于获取参数),为其实例化一个令牌(我可以在 Hibernate 的 STDOUT 中看到向数据库询问下一个序列),但是 persist() 方法不会将 Token 保存到数据库中,也不会修改 User 对象(令牌 id、上次登录) 日期)。
我已经在 google 上搜索了两天,这是我能弄清楚的:
我的项目使用 SEAM 托管事务 (components.xml):
我的项目使用 JTA 进行事务处理 (persistence.xml):
org.hibernate.ejb.HibernatePersistence ...EntityManager.persist() 不会将更改提交到数据库,只是将更改排队到当前事务(?)
SEAM 管理的事务默认绑定到对话
我尝试使用 .flush(),抛出异常,表示没有正在进行的事务。
我尝试使用 .joinTransaction() 和 .getTransaction().begin(),抛出另一个异常,表示 JTA EntityManager 无法访问事务。
还尝试在类上使用不同的作用域类型,或者在我的 login() 方法上使用 @Transactional 注释,但没有成功。
还尝试使用 @PersistenceContext 注释注入 EntityManager,这导致出现异常,表明 @PersistenceContext 只能与会话 bean 一起使用。
还尝试将我的班级标记为@Stateless,这导致我无法访问我的服务(404)。
我应该如何使用 EntityManager 将我的实体保留在 RESTEasy 服务中?
系统规格:
- JBoss 5.1.0 GA
- SEAM 2.2.1 Final
- Postgres 8.3
请注意,我对 JavaEE/JBoss/SEAM 完全陌生且缺乏经验。
任何评论都会有用!谢谢。
My project is a WAR project generated with seam-gen. It contains a RESTEasy web service class like this (simplified, only the relevant parts):
@Scope(ScopeType.APPLICATION)
public abstract class RestService {
@In
protected EntityManager entityManager;
@GET
@POST
@Produces("application/json")
public Object proxy() {
// invokes various subclass methods
// based on request parameters
// and returns the result
}
// further method and logic
}
And:
@Path("/my")
@Name("myRestService")
public class MyRestService extends RestService {
public Object login(/*...*/) {
User user = getUser(email);
// ...
Token token = user.getToken();
if (token != null) {
entityManager.remove(token);
}
token = new Token();
entityManager.persist(token);
user.setToken(token);
user.setLastlogin(new Date());
entityManager.persist(user);
// ...
}
private User getUser(String email) {
try {
return (User) entityManager
.createQuery("FROM User WHERE UPPER(email) = UPPER(:email)")
.setParameter("email", email)
.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
}
If i invoke the login method through a web browser, it finds the correct user (based on the get params), instantiates a Token for it (i can see at the STDOUT of Hibernate asking the databse for the next sequence), but the persist() method does not save the Token to the database, neither the modifications of the User object (token id, last login date).
I have googled this for two days now, here's what i could figure out:
my project uses SEAM managed transactions (components.xml):
<persistence:managed-persistence-context name="entityManager" auto-create="true" persistence-unit-jndi-name="java:/MyEntityManagerFactory"/>
my project uses JTA for transaction handling (persistence.xml):
<persistence-unit name="MyProject" transaction-type="JTA"> <provider>org.hibernate.ejb.HibernatePersistence</provider> ...
the EntityManager.persist() does NOT commit changes to the database, just queues changes to the current transaction (?)
the SEAM managed Transactions are tied to conversations by default
I tried to use .flush(), an exception was thrown saying that there is no transaction in progress.
I tried to use .joinTransaction() and .getTransaction().begin(), another exception was thrown saying that a JTA EntityManager can not access transactions.
Also tried to use different scope types on the class, or use the @Transactional annotation on my login() method, no luck.
Also tried to inject the EntityManager with the @PersistenceContext annotation, this resulted in an exception saying @PersistenceContext can only be used with session beans.
Also tried to mark my class as @Stateless, this resulted that i could not reach my service (404).
How should i persist my Entities within a RESTEasy service with EntityManager?
System specs:
- JBoss 5.1.0 GA
- SEAM 2.2.1 Final
- Postgres 8.3
Please note that i'm totally new and inexperienced with JavaEE/JBoss/SEAM.
Any comment would be useful! Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您正在分离一个托管字段:
创建一个新字段:
但是在这里:
令牌与用户有关系(我不知道这种关系是什么 - oneToMany、oneToOne,您是从 User 级联吗?、获取等),但只是调用保留令牌不会重新建立该关系。
看看这里
http://www.objectdb.com/java/jpa/persistence/crud
You are detaching a managed field:
Creating a new one:
But here:
Token has a relationship with User (I don't what that relationship is - oneToMany, oneToOne, are you cascading from User?, fetching, etc), but just calling persist on token does not re-establish that relationship.
Take a look here
http://www.objectdb.com/java/jpa/persistence/crud
事务注释对于登录方法很重要。这将确保事务拦截器在必要时创建事务。 (如果还没有交易)。确定是否应用拦截器的最简单方法是调试登录方法并检查堆栈。我不确定这个类是如何调用的,但我会在工作后立即更新这篇文章以查看它。
如果这个拦截器不存在,则意味着您没有使用Seam事务。 Components.xml 的摘录并未显示您这样做。
马丁
更新:
这是堆栈跟踪。看看 TransactionInterceptor,如果你的堆栈中没有这个,那么你就没有事务管理。
The transactional annotation is important on the login method. This will ensure that the transaction interceptor is creating a transaction if neccessary. (if there is no transaction yet). The easiest way to find out if the interceptor is applied would be to debug into the login method and check the stack. I'm not sure how the class is called but i will update this post as soon as i'm at work to check it out.
If this interceptor is not there it means that you are not using seam transactions. The extract of your components.xml is not showing that you do so.
Martin
Updated:
So here's the stacktrace. Take a look at the TransactionInterceptor if you do not have this in your stack you have no transaction management.