繁体   English   中英

JPA 未获取反映数据库 state 的数据

[英]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 相同的数据。 在这种情况下,数据库上的serviceIdnull ,在modified我将它设置为一个 ID。 originalserviceId设置为与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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM