我在Glassfish 3上使用JPA2和Eclipselink实现。
我有一个简单的登录/注销应用程序。
这些是实体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 Context / JPA Cache中的Entity Object不会与数据库信息同步。
作为一个具体的例子,当我删除一些用户详细信息(来自user_detail表)时,数据库条目被删除,但是当我再次读取信息时,返回的实体仍然具有详细信息。我猜这个信息保存在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 :(得分:1)
阅读评论我发现您正在使用application-managed
次交易。如Java EE Tutorial中所述,
应用程序管理的实体管理器不会自动传播 JTA事务上下文。此类应用程序需要手动获取 访问JTA事务管理器并添加事务划分 执行实体操作时的信息。该 javax.transaction.UserTransaction接口定义要开始的方法, 提交和回滚事务。注入一个实例 UserTransaction通过创建用注释注释的实例变量 @Resource ...
根据您发布的代码,您没有划分删除UserDetail
记录的事务。此外,您尚未显示用于获取EntityManager引用的代码。你应该坚持使用相同的教程并使用
em = emf.createEntityManager();
获取实体管理器的更新实例。
否则,切换到Container-Managed
交易,这些交易可以在大多数情况下简化生活。