2012-02-12 173 views
1

我使用JPA2與執行的EclipseLink在Glassfish 3.JPA實體更新不及時正確

我有一個簡單的登錄/註銷申請。

這些都是實體Bean的及其映射:

@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上下文/ JPA緩存中的實體對象不會與數據庫信息同步。

作爲一個具體的例子,當我刪除一些用戶的詳細信息(從user_detail表)時,數據庫條目被刪除,但是當我再次讀取信息時,返回的實體仍然具有Details信息。我猜測這個信息保存在JPA緩存中,並且沒有先檢查數據庫就回來了。

這是正確的行爲嗎?有沒有辦法保持緩存信息與數據庫同步?

LE:用戶服務包含調用輔助方法的read(email, password)方法。此幫助程序方法包含CriteriaAPI查詢,該查詢將爲所有用戶提供所需的電子郵件和密碼。 Criteria查詢已經過測試,工作正常。

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(); 
} 

這是從服務中刪除方法:

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; 
} 
+0

你的UserService是什麼樣的? – zellus 2012-02-12 23:51:07

+0

@zellus我更新了問題 – TGM 2012-02-13 07:18:06

+0

你如何刪除UserDetail? – 2012-02-13 08:19:49

回答

1

閱讀中,我看到你正在使用application-managed交易的意見。正如Java EE Tutorial指出,

應用管理的實體管理器不會自動傳播 JTA事務上下文。這些應用程序需要手動獲取對JTA事務管理器的訪問權限,並在執行實體操作時添加事務分割 信息。 javax.transaction.UserTransaction接口定義了開始的方法, 提交和回滾事務。通過創建 @Resource註解一個實例變量注入 UserTransaction的實例...

從您發佈的代碼,你是不是劃分,消除了UserDetail記錄交易。此外,您還沒有顯示您用於獲取EntityManager引用的代碼。您應該堅持使用相同的教程,並使用

em = emf.createEntityManager(); 

獲取實體管理器的更新實例。

否則,切換到Container-Managed交易,使大多數情況下的生活變得更加簡單。