JPA实体未正确更新 [英] JPA Entity not updating correctly

查看:112
本文介绍了JPA实体未正确更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用JPA2和Glassfish 3上的Eclipselink实现.

I'm using JPA2 with Eclipselink implementation on Glassfish 3.

我有一个简单的登录/注销应用程序.

I have a simple login/logout application.

这些是实体bean及其映射:

These are the entity beans an their mappings:

@Entity
@Table(name = "user_account")
public class UserAccount implements Serializable{
    @Id
    private Integer id;
    private String email;
    private String password;
    private Integer role;

    @OneToOne(fetch=FetchType.EAGER)
    @PrimaryKeyJoinColumn
    private UserDetail userDetail;

    // get+set
}

@Entity
@Table(name = "user_detail")
public class UserDetail implements Serializable{
    private static final long serialVersionUID = 1L;
    @Id
    private Integer id;
    @Temporal(TemporalType.DATE)
    private Date birth;
    @Column(name="origin_city")
    private String originCity;
    @Column(name="residence_city")
    private String residenceCity;
    private String description;

    @OneToOne(mappedBy="userDetail")
    private UserAccount userAccount;
}

UserService类中,我有一个简单的CRUD实现.所有方法都可以正常工作,但是问题在于有时保存在Persistance Context/JPA Cache中的Entity Object不会与数据库信息同步.

In a UserService class I have a simple CRUD implementation. All the methods work just fine but the problem is that sometimes the Entity Object saved in the Persistance Context / JPA Cache doesn't synchronize with the database information.

作为一个特定示例,当我(从user_detail表中)删除一些用户详细信息时,数据库条目将被删除,但是当我再次读取该信息时,返回的实体仍然具有详细信息"信息.我猜想此信息将保留在JPA缓存中,并带回而无需先检查数据库.

As a specific example, when I delete some user details (from the user_detail table) the database entry is deleted but when I read the information again, the returned entity still has the Details information. I'm guessing that this info was kept in the JPA cache and brought back without checking the database first.

这是正确的行为吗?有没有办法使Cache信息与数据库保持同步?

Is this the correct behaviour? Is there a way to keep the Cache information synchronized with the database?

LE :用户服务包含read(email, password)方法,该方法调用助手方法.此帮助器方法包含CriteriaAPI查询,该查询为所有用户提供所需的电子邮件和密码. Criteria查询经过测试,可以正常工作.

LE : The user Service contains the read(email, password) method which calls a helper method. This helper method contains the CriteriaAPI query which brings all the users with the desired email and password. The Criteria query is tested and works fine.

public UserAccount read(String email, String password){
    List<UserAccount> userList = getUserList(null, email, password, Constants.RESULT_SINGLE);
    return (userList.isEmpty() ? null : userList.get(0));
}

private List<UserAccount> getUserList(Integer id, String email, String password, String resultType){
    CriteriaBuilder builder = em.getCriteriaBuilder();
    CriteriaQuery<UserAccount> shell = builder.createQuery(UserAccount.class);
    Root<UserAccount> entity = shell.from(UserAccount.class);
    shell.select(entity);
    shell.distinct(true);
    List<Predicate> predicateList = new ArrayList<Predicate>();
    if (id != null){
        ParameterExpression<Integer> param = builder.parameter(Integer.class, "id");
        predicateList.add(builder.equal(entity.get("id"), param));
    }
    if (email != null){
        ParameterExpression<String> param = builder.parameter(String.class, "email");
        predicateList.add(builder.equal(entity.get("email"), param));
    }
    if (password != null){
        ParameterExpression<String> param = builder.parameter(String.class, "password");
        predicateList.add(builder.equal(entity.get("password"), param));
    }

    if (predicateList.size() == 1){
        shell.where(predicateList.get(0));
    } else {
        Predicate[] p = new Predicate[predicateList.size()];
        p = predicateList.toArray(p);
        shell.where(builder.and(p));
    }   
    TypedQuery<UserAccount> selectQuery =  em.createQuery(shell);
    if (id != null) selectQuery.setParameter("id", id);
    if (email != null) selectQuery.setParameter("email", email);
    if (password != null) selectQuery.setParameter("password", password);

    return selectQuery.getResultList();
}

这是从服务中删除的方法:

This is the delete method from the service:

public boolean deleteDetail(Integer userId) {
    boolean ok = true;
    try {
        UserDetail userDetails = em.find(UserDetail.class, userId);
        System.out.println("Trying to delete the detail. The address of the object is: " + userDetails);
        em.remove(userDetails);
        System.out.println("Object deleted. The address is: " + userDetails);
    } catch (Exception e) {
        e.printStackTrace();
        ok = false;
    }

    return ok;
}

推荐答案

阅读注释,我发现您正在使用application-managed事务.如 Java EE教程

Reading the comments I see that you are using application-managed transactions. As stated in the Java EE Tutorial,

由应用程序管理的实体管理器不会自动传播 JTA事务上下文.此类应用需要手动获取 访问JTA事务管理器并添加事务划分 执行实体操作时的信息.这 javax.transaction.UserTransaction接口定义了开始的方法, 提交并回滚事务.注入一个实例 通过创建带有注释的实例变量进行UserTransaction @资源...

Application-managed entity managers don’t automatically propagate the JTA transaction context. Such applications need to manually gain access to the JTA transaction manager and add transaction demarcation information when performing entity operations. The javax.transaction.UserTransaction interface defines methods to begin, commit, and roll back transactions. Inject an instance of UserTransaction by creating an instance variable annotated with @Resource...

根据您发布的代码,您未在划分删除UserDetail记录的事务上划定界限.此外,您尚未显示用于获取EntityManager参考的代码.您应该坚持使用同一教程并使用

From the code you have posted, you are not demarcating the transaction that removes the UserDetail record. Moreover, you have not shown the code that you are using for getting the EntityManager reference. You should stick to the same tutorial and use

em = emf.createEntityManager();

用于获取实体管理器的更新实例.

for getting an updated instance of the Entity Manager.

否则,请切换到Container-Managed交易,这会使大多数情况下的生活变得更加简单.

Otherwise, switch to Container-Managed transactions that make life much simpler in most of the situations.

这篇关于JPA实体未正确更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆