【问题标题】:Spring Data JPA(Hibernate): How do I retrieve a concrete entity using only a field in its abstract superclass?Spring Data JPA(Hibernate):如何仅使用抽象超类中的字段检索具体实体?
【发布时间】:2012-12-28 23:01:48
【问题描述】:

考虑以下层次结构,其中实体WidgetAWidgetB 扩展了抽象Widget 超类:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Widget implements Serializable  {

    @Column(name="serialNumber", length=64, nullable=false, unique=true)
    private String serialNumber;
    ...

@Entity
public class WidgetA extends Widget implements Serializable  {
...

@Entity
public class WidgetB extends Widget implements Serializable  {
...

我需要通过serialNumber 搜索小部件,但我不知道我在运行时要搜索的小部件的具体类型。通过serialNumber 搜索小部件的正确方法是什么,如果serialNumberWidgetA 的,则返回WidgetA 的实例,等等?

我试图在Widget DAO 中使用findyBySerialNumber(),但我收到一个错误消息,告诉我无法实例化抽象类,这是有道理的,但我认为持久性提供者会知道如何查看具体的子实体表并返回正确的实例。我能做到吗?

我使用的是“Spring Data JPA”,所以 Widget DAO 真的很简单:

public interface WidgetDAO extends JpaRepository<Widget, Long> {
    public Widget findBySerialNumber(String serialNumber);
}

【问题讨论】:

  • 查看 findBySerialNumber() 的实现将有助于发现错误
  • 是的,请发布 findBySerialNumber()。是否有任何不是 WidgetA 或 WidgetB 类型的数据?
  • @Affe 我添加了 findBySerialNumber() 的实现
  • @MarkRobinson 我添加了 findBySerialNumber() 的实现。并且所有数据都是 WidgetA 或 WidgetB 类型。
  • 由于是spring-data-jpa他不会有findBySerialNumber的实现,它是spring-data-jpa使用方法名和参数自动生成的,生成一个“合适的” jpa 查询。

标签: java hibernate inheritance jpa spring-data-jpa


【解决方案1】:

您似乎没有为您的小部件层次结构明确指定鉴别器。我认为您可以尝试显式定义它,因为 Spring Data 将操纵字节码来生成查询,因此我怀疑 SpringData 需要显式定义这些值。

另外,在子类中,您需要为每个子类指明鉴别器值。

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name="WIDGET_TYPE")
public abstract class Widget implements Serializable  {

    @Column(name="serialNumber", length=64, nullable=false, unique=true)
    private String serialNumber;
    ...

-

@Entity
@DiscriminatorValue("A")
public class WidgetA extends Widget implements Serializable  {
...

-

@Entity
@DiscriminatorValue("B")
public class WidgetB extends Widget implements Serializable  {
...

【讨论】:

  • 我的印象是,如果你不指定@Discriminator,JPA 提供者会分配一个(例如,我认为 Hibernate 是根据类名来做的)
  • @sharakan 你是对的。 JPA 提供者应该自动生成它。但我不推荐这样做,因为如果重构代码(更改包和/或类名,因为这是 hibernate 使用的默认鉴别器值)会导致麻烦。另外(这就是我建议这样做的原因),我怀疑 Spring Data 需要这个明确的定义,因为它操纵字节码以产生查询的方式。
  • 有趣,没想到 Spring 可能会导致这样的问题。我完全同意顺便指定鉴别器的一般原因,为此 +1。
【解决方案2】:

您的对象和注释对我来说看起来不错。我能够获取它们,将小部件保存到数据库中,然后毫无问题地取出它。

我认为问题出在您的数据中。 Hibernate 可能在 Widget 表中找到了在任何子类表中都没有对应行的行,因此尝试创建超类的实例,但失败了。

使用InheritanceType.JOINED,您可以指定discriminator,或者让它为您完成(我相信通过检查子类表中是否存在具有该ID的行)。但无论哪种方式,您都必须检查数据以确保超类表中没有没有匹配子类行的条目。

作为一项规则,我支持@ben75 的建议,即为类层次结构明确指定@Discriminator。最好控制您的鉴别器值,以便以后的代码更改不会以意想不到的方式改变值。

【讨论】:

    【解决方案3】:

    Hibernate 很好地支持该查询,并且很乐意返回正确子类的实例。在不知道试图创建 Widget 实例的 Spring Data 实现中是什么的情况下,您应该能够只声明查询并让它直接运行,而不是使用解析器。

    public interface WidgetDAO extends JpaRepository<Widget, Long> {
    
        @Query("select w from Widget w where w.serialNumer = ?1")
        public Widget findBySerialNumber(String serialNumber);
    }
    

    【讨论】:

    • 我尝试按照您的建议将 @Query 注释放在存储库方法上,但没有任何区别。还有其他建议吗?
    猜你喜欢
    • 1970-01-01
    • 2020-05-11
    • 2012-09-14
    • 1970-01-01
    • 2019-09-21
    • 1970-01-01
    • 1970-01-01
    • 2020-12-04
    • 2021-05-22
    相关资源
    最近更新 更多