【问题标题】:JPA: Equality assertions with Proxy objectsJPA:代理对象的平等断言
【发布时间】:2012-04-13 19:32:10
【问题描述】:

我有一个类 User,它充当数据库中用户表的持久性模型:

public class User {
@Id
@Basic(optional = false)
@Column(name = "USER_ID")
private Long userId;

@JoinColumn(name = "LAST_CHANGED_USER_ID", referencedColumnName = "USER_ID")
@ManyToOne(fetch = FetchType.LAZY)
private User lastChangedUser;
...

@Override
public int hashCode() {
    int hash = 0;
    hash += (userId != null ? userId.hashCode() : 0);
    return hash;
}

@Override
public boolean equals(Object object) {
    if (!(object instanceof User)) {
        return false;
    }

    User other = (User) object;
    if ((this.userId == null && other.userId != null) || 
        (this.userId != null && !this.userId.equals(other.userId))) {
        return false;
    }
    return true;
}

@Override
public String toString() {
    return "User[userId=" + userId + "]";
}
}

每当对 User 实例进行更改时,都会更新字段 lastChangedUser。考虑以下方法:

private void setUsername(string username, User lastChangedUser){
    this.username = username;
    this.lastChangedUser = lastChangedUser;
}

public void updateUserName(Long userId, Long lastChangedUserId, String username){
    EntityManager em = PersistenceManager.getPersistenceManager()
       .getEntityManager();
    EntityTransaction transaction = em.getTransaction();

    User user = em.find(User.class, userId);
    User lastChangedUser = em.find(User.class, lastChangedUserId);

    transaction.begin();
    user.setUsername(username, lastChangedUser);
    transaction.commit();
}

和单元测试:

public void testUpdateUsername(){
    UserFacade instance = new UserFacade();
    User john = instance.getUserByUserId(new Long(1));
    User jane = instance.getUserByUserId(new Long(2));

    // make the update and persist
    instance.updateUsername("JANE M. DOE", jane.userId, john.userId);

    // assert that john made the change to jane
    jane = instance.getUserByUserId(new Long(2));

    assertTrue(jane.username.equals("JANE M. DOE"));

    User actual = jane.lastChangedUser;
    User expected = john;

    // this assertion passes...
    assertTrue(actual.equals(expected));

    // yet this assertion fails!
    assertTrue(expected.equals(actual));

    // this fails as well.
    assertEquals(expected, actual);
}

使用Netbeans 7.1.1,测试结果界面返回错误信息:

FAILED: expected: User[id=1], but was: User[id=1].

经检查,对象actual 不是User 类型,而是代理对象的类型。我认为这是由于 LAZY fetch 注释。因此,对象的用户 ID 的值为 nullexpected.equals(actual) 返回 false。然而,actual.equals(expected) 返回true。更让我困惑的是,失败消息显示actual.userId 不是null,而是等于预期的 id!

Hibernate 或 JUnit 的什么怪癖导致实际的 User 对象在失败后出现?

【问题讨论】:

  • 为清楚起见,假设setUsername 方法位于User 类中。
  • PersistenceManager.getPersistenceManager().getEntityManager(); 在这种情况下是否总是返回相同的实体管理器?来自不同 EntityManagers 实例的实体可能彼此不喜欢。
  • 我猜你已经在你的实体上覆盖了equals()。你的equals() 方法是什么样的?
  • ɲeuroburɳ,getEntityManager() 方法并不总是返回相同的 EntityManager 实例。这个因素如何影响这种情况? Tim,equals 方法被覆盖;请参阅实现的第一个代码块。

标签: hibernate jakarta-ee jpa junit persistence


【解决方案1】:

感谢a question about obtaining ids without fetching the entire object,我和一些朋友找到了解决方案。由于对象是代理,直接引用equals 中的字段userid 会产生空值。但是,对用户 ID 使用 getter 方法 会导致从数据库中获取实际值。因此,equals 方法应如下所示:

@Override
public boolean equals(Object object){
    if (!(object instanceof User)) {
        return false;
    }
    User other = (User) object;
    if ((this.getUserId() == null && other.getUserId() != null) || 
        (this.getUserId() != null && 
        !this.getUserId().equals(other.getUserId()))) {
        return false;
    }
    return true;
}

现在断言将成立。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-05-01
    • 2022-04-20
    • 2016-03-27
    • 2018-04-06
    • 2016-03-04
    • 1970-01-01
    • 1970-01-01
    • 2021-02-22
    相关资源
    最近更新 更多