当在事务上下文中使用equals()方法进行比较时,单端关联在Hibernate中被破坏

当在事务上下文中使用equals()方法进行比较时,单端关联在Hibernate中被破坏,hibernate,jpa,lazy-loading,equals,many-to-one,Hibernate,Jpa,Lazy Loading,Equals,Many To One,下面给出了国家与州之间的一对多关系(表名在数据库中采用state\u table,因为在某些RDBMS中,状态可能是保留字) 下面给出了关联的反面(我假设不要求拥有方出现) 在equals()和hashcode()方法中,是Java 7中引入的一个实用程序类,只包含静态实用程序方法 下面的代码段使用上述实体类中的equals()方法作为示例 StateTable stateTable = entityManager.find(StateTable.class, 1L); Country coun

下面给出了国家与州之间的一对多关系(表名在数据库中采用
state\u table
,因为在某些RDBMS中,状态可能是保留字)

下面给出了关联的反面(我假设不要求拥有方出现)

equals()
hashcode()
方法中,是Java 7中引入的一个实用程序类,只包含静态实用程序方法

下面的代码段使用上述实体类中的
equals()
方法作为示例

StateTable stateTable = entityManager.find(StateTable.class, 1L);
Country country = entityManager.find(Country.class, 1L);

stateTable.getCountryId().hashCode();

System.out.println("countryId = " + stateTable.getCountry().getCountryId());
System.out.println("equals = " + country.equals(stateTable.getCountry()));
第一个stdout语句显示与实体主键(
countryId
类型
Long
)对应的正确值
1

然而,第二个stdout语句返回
false
,尽管在获取时两个对象中的
countryId
(主键)应该是相同的,但返回的结果令人难以置信——它已被破坏

实体类中
equals()
方法中的这两个stdout语句输出以下内容

entity=false中的
equals()
this.countryId=1:other.countryId=null
尽管所提供的country对象不是
null
,如
equals()
中的第一条stdout语句所确认,
((国家)obj)。countryId
意外地返回
null
,因此,
equals()
method始终返回
false
而不管作为参数提供的对象是什么,因为
object
equals
null
从来都不是真的无论如何都不应该发生这种情况。


@ManyToOne
关联被延迟初始化(
fetch=FetchType.LAZY
)。如果它转到
fetch=FetchType.EGAR
,那么一切都会正常进行。
equals()
方法根据提供的对象返回正确的结果

代码在Spring服务中的事务下执行,该事务由以下注释标记

@Service
@Transactional(readOnly = true, propagation = Propagation.REQUIRED)
因此,不应该存在初始化惰性关联的问题。JPA中明确规定了这一点

对于收集关系,发送
size()
通常是最好的方式 以确保实例化惰性关系适用于OneTONE和 多通关系,通常仅访问关系是 足够了(即
employee.getAddress()
,尽管对于一些JPA提供商来说是这样 如果使用代理,则可能需要向对象发送消息 (即
employee.getAddress().hashCode()

最后一条语句表示特定于提供者的行为

尽管对于一些使用代理的JPA提供商,您可能需要发送 对象包含一条消息(即
employee.getAddress().hashCode()

然而,我完成了两种方法的尝试,但都没有用

  • stateTable.getCountry()
    的值赋值给另一个变量,然后将其传递给
    equals()
    )。就是

    Country anotherCountry = stateTable.getCountry();
    country.equals(anotherCountry); // Always returns false same as mentioned above.
    
  • 使用前面指定的使用stateTable.getCountry().hashcode()与此关联关联的
    hashcode()
    ,以确保在调用
    equals()
    并通过country对象之前,惰性关联在事务上下文中初始化

  • 当我尝试使用Hibernate 4.3.6 final(Tomcat 8.0.9.0)时,EclipseLink 2.6.0(GlassFish 4.1)上的这种行为并不完全相同,它的行为可能与人们直觉上预期的一样


    冬眠有什么问题?解决方案是什么?

    不要直接访问字段。而是在
    equals
    方法中使用getter。

    不要直接访问字段。相反,在
    equals
    方法中使用getter。

    您必须了解代理是如何工作的。代理从实际类派生,但不保存其数据。从数据库加载代理时,将创建该类的另一个实例(本例中为Country),这是一个普通实例。它保存数据。代理将所有调用转发到此实例

    您的代码有两个潜在问题

    • 字段不应由另一个对象访问,因为它可能是代理。代理字段未初始化。调用getter,就可以得到值
    • .class
      返回代理类型,而不是实际类型。不一定是
      国家
      。如果(!(Cat的其他实例))返回false,则使用

    请注意,由于代理将所有调用转发给真实对象,因此在您的实体中,this
    永远不会引用代理。但是你得到的每一个参数都可能是一个代理。

    你必须了解代理是如何工作的。代理从实际类派生,但不保存其数据。从数据库加载代理时,将创建该类的另一个实例(本例中为Country),这是一个普通实例。它保存数据。代理将所有调用转发到此实例

    您的代码有两个潜在问题

    • 字段不应由另一个对象访问,因为它可能是代理。代理字段未初始化。调用getter,就可以得到值
    • .class
      返回代理类型,而不是实际类型。不一定是
      国家
      。如果(!(Cat的其他实例))返回false,则使用

    请注意,由于代理将所有调用转发给真实对象,因此在您的实体中,this
    永远不会引用代理。但是您得到的每个参数都可能是一个代理。

    此博客条目应该有帮助此博客条目应该有帮助您是否总是在项目中的每个实体中使用业务密钥、代理密钥或
    equals()
    hashcode()
    中的其他内容来避免自动生成字段
    Country anotherCountry = stateTable.getCountry();
    country.equals(anotherCountry); // Always returns false same as mentioned above.