[英]JPA not fetching data that reflects state of database
我在编写代码时遇到了一个奇怪的错误或功能。 情况如下:
我们在 JavaEE 项目中使用 PostgreSQL 数据库 EclipseLink。
我在项目的这一部分中所做的是从数据库中获取一个实体,即:
User user = userController.get(userId);
然后转到我们的 controller 并通过 TypedQuery 获取用户:
@Stateless
@LocalBean
public class UserController {
private EntityManager em;
public User get(Integer userId){
User retval = null;
TypedQuery<User> = em.createNamedQuery("User.findByUserId", User.class);
q.setParameter("userId", userId);
retval = q.getSingleResult();
}
public User update(final User modified){...}
}
在我的用户 class 中,我有:
@NamedQuery(name = "User.findByUserId", query = "SELECT u FROM User u WHERE u.id = :userId"),
所以电话开始了,我从数据库中获取了我的用户 object 及其各自的数据。
在我调用userController.get方法的class中我继续修改这个object上的数据,再次调用我们的controller更新数据库上的数据
user.setServiceId(1); //any id (integer) pointing to an existing service, this is a ManyToOne relationship
userController.update(user);
这就是有趣的地方。 In our update method inside the controller class I have my modified User object and using this object I get the primary key userId and fetch the data again from the database to get the original:
@Stateless
@LocalBean
public class userController {
private EntityManager em;
public User get(Integer userId){...}
public User update(final User modified){
User retval = null;
if(modified != null){
try {
User original = get(modified.getId()); //Here I fetch the current state of the DB
if(original != null){
Set<Modifications> modifications = apply(original, modified); //Method to apply modifications
retval = em.merge(original); //Merge changes into database
em.flush(); //Force data to be persisted
catch(Exception e){
}
return retval;
}
}
但是, original
object 中的字段不反映数据库的 state,而是包含与modified
的 object 相同的数据。 在这种情况下,数据库上的serviceId
是null
,在modified
我将它设置为一个 ID。 original
的serviceId
设置为与modified
的 object 相同的值,即使它应该包含从数据库中获取的数据,在本例中null
我当前的解决方案是在从数据库中获取用户后,构造一个新的用户 object,并修改该新 object 上的数据:
User user = userController.get(userId);
User newUser = new User(user);
newUser.setService(service);
userController.update(newUser);
现在我做更新方法的时候, original
反映的是数据库的state。
或者它可能反映了持久性上下文中已经存在的user
object 的 state?
但是为什么会这样呢? 因为我确实在我的更新方法中使用SELECT
语句对数据库进行了新的get
调用。
您对所有内容都使用相同的 EntityManager,包括读取和“合并”,在这种情况下,它是无操作的。 通过 EM 读取的所有内容都是受管理的,因此如果您再次读取它,您将获得相同的实例。 只要用户没有被序列化,它就由读取它的 EntityManager“管理”,因此相同的实例及其更改在对该 ID 的任何 get 调用中都是可见的。
您没有展示如何获得 EntityManagers,但我猜这不是容器管理的,因为它们会为这些调用注入一个新的,然后在完成后为您关闭它们。 您还没有展示任何关于更新和它使用的 em 上下文如何连接的事务逻辑,但我建议您为这些调用创建一个新的 EntityManager。 Flush 似乎也没有必要,就像 update 被包装在事务中一样,应该在没有这个额外调用的情况下处理将 update 语句刷新到数据库。
如果user.setServiceId(1);
在管理“用户”实体时调用,该调用将更新数据库行。
您可以检查管理实体生命周期
保存到数据库后需要refresh
数据,获取object最新的state,如em.refresh(retval)
您可以在下面找到添加的代码。
@Stateless
@LocalBean
public class userController {
private EntityManager em;
public User get(Integer userId){...}
public User update(final User modified){
User retval = null;
if(modified != null){
try {
User original = get(modified.getId()); //Here I fetch the current state of the DB
if(original != null){
Set<Modifications> modifications = apply(original, modified); //Method to apply modifications
retval = em.merge(original); //Merge changes into database
em.flush(); //Force data to be persisted
em.refresh(retval); // This will fetch the updated data from database
catch(Exception e){
}
return retval;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.