JPA无法获取反映数据库状态的数据

问题描述

我在编写代码时遇到了一个奇怪的错误或功能。情况如下:

我们在JavaEE项目中使用PostgreSQL数据库EclipseLink。

我在项目的这一部分中正在从数据库中获取一个实体,即:

User user = userController.get(userId);

然后转到我们的控制器并通过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){...}
}

在我的User类中,我有:

@NamedQuery(name = "User.findByUserId",query = "SELECT u FROM User u WHERE u.id = :userId"),

调用结束了,我从数据库中获取了用户对象及其相应的数据。

在我调用userController.get方法的类中,我继续修改此对象上的数据,然后再次调用我们的控制器以更新数据库上的数据

user.setServiceId(1); //any id (integer) pointing to an existing service,this is a ManyToOne relationship
userController.update(user);

这是有趣的地方。在控制器类内部的更新方法中,我修改了User对象,并使用该对象获取主键userId并再次从数据库中获取数据以获取原始数据:

@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对象中的字段不反映数据库的状态,而是包含与modified对象相同的数据。在这种情况下,数据库上的serviceIdnull,在modified中将其设置为ID。 original的{​​{1}}设置为与serviceId对象相同的值,即使它应包含从数据库中获取的数据,在这种情况下为modified

我当前的解决方案是在从数据库中获取用户之后,构造一个新的User对象,并修改该新对象上的数据:

null

现在,当我执行更新方法时,User user = userController.get(userId); User newUser = new User(user); newUser.setService(service); userController.update(newUser); 反映了数据库的状态。

也许它反映了​​持久性上下文中已经存在的original对象的状态?

但是为什么会这样呢?由于我确实在更新方法中使用user语句对数据库进行了新的get调用。

解决方法

您对所有内容(读取和“合并”)都使用相同的EntityManager,因此在这种情况下为空操作。通过EM读入的所有内容均受到管理,因此,如果再次读回它,您将获得相同的实例。只要不对用户进行序列化,就由读取它的EntityManager对其进行“管理”,因此对该ID进行的任何get调用都可以看到同一实例及其更改。

您没有显示如何获取EntityManager,但是我想这不是容器管理的,因为它们将为这些调用注入一个新的对象,然后在完成时为您关闭它们。您尚未显示有关如何连接更新及其所使用的em上下文的任何事务逻辑,但我建议您为这些调用创建一个新的EntityManager。刷新似乎也没有必要,好像更新包装在事务中一样,应该处理刷新更新语句到数据库而无需额外调用。

,

如果在管理“用户”实体时调用user.setServiceId(1);,则该调用将更新数据库行。

您可以检查manage entity lifecycle

,

将数据保存到数据库后,需要refresh,以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;
    }
}

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...