简体   繁体   中英

Spring Data JPA(Hibernate): How do I retrieve a concrete entity using only a field in its abstract superclass?

Consider the following hierarchy, where entities WidgetA and WidgetB extend an abstract Widget superclass:

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

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

and

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

and

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

I need to search for Widgets by serialNumber , but I don't know the concrete type of the Widget I'm searching for at runtime. What is the correct way to search for widgets by serialNumber such that if the serialNumber is that of a WidgetA , then an instance of WidgetA gets returned, and so on?

I am trying to use a findyBySerialNumber() in the Widget DAO, and I'm getting an error telling me I can't instantiate an abstract class, which makes sense, but I thought the persistence provider would know how to look in the concrete child entity tables and return the correct instance. Can I make it do this?

I am using "Spring Data JPA", so the Widget DAO is really simple:

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

It seems you didn't specify a discriminator explicitly for your widget hierarchy. I think you can try to define it explicitly because Spring Data will manipulate bytecode to generate the queries, and so I suspect SpringData need to have those values explicitely defined.

Additionally in subclasses you need to indicate the discriminator value for each subclass.

@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  {
...

Your objects and annotations look fine to me. I was able to take them, save a widget to a database, and fetch it out without a problem.

I think the the problem is in your data. Hibernate is probably finding a row in the Widget table that does not have a corresponding row in any of the sub class tables, and is therefore trying to create an instance of the superclass, and failing.

With InheritanceType.JOINED , you can either specify a discriminator , or let it do it for you (I believe by checking whether a row with that ID exists in the subclass table). But either way, you have to check your data to make sure there's not an entry in the super class table without a matching subclass row.

As a rule, I support @ben75's recommendation that you do explicitly specify @Discriminator for class hierarchies. It's better to have control over your discriminator values, so that later code changes don't alter the values in unexpected ways.

Hibernate supports that query just fine, and will happily return an instance of the correct subclass. Without knowing what it is in the Spring Data implementation that is trying to make an instance of Widget, you should be able to just declare the query and have it run it directly, rather than using the parser.

public interface WidgetDAO extends JpaRepository<Widget, Long> {

    @Query("select w from Widget w where w.serialNumer = ?1")
    public Widget findBySerialNumber(String serialNumber);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM