Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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);
}
like image 473
CFL_Jeff Avatar asked Jan 14 '13 22:01

CFL_Jeff


People also ask

How do you inherit properties from superclasses in hibernate?

You have to annotate the superclass with @MappedSuperclass to enable embedding of its properties in the concrete subclass tables. You can override column mappings from the superclass in a subclass with the @AttributeOverride annotation or several with @AttributeOverrides .

Can we use Spring data JPA and Hibernate together?

You cant actually use both of them in the same application. For backwards compatibility. We are expanding our application and want to start using Spring Data JPA, but still keep the old hibernate implementation. Its better you develop your new application as a separate microservice and use spring data jpa ..

What can we do by using @query JPA annotation?

In order to define SQL to execute for a Spring Data repository method, we can annotate the method with the @Query annotation — its value attribute contains the JPQL or SQL to execute. The @Query annotation takes precedence over named queries, which are annotated with @NamedQuery or defined in an orm.xml file.

What type of request is used to call the Save method from the JPA CRUD repository?

Saving an entity can be performed via the CrudRepository. save(…) -Method. It will persist or merge the given entity using the underlying JPA EntityManager .


2 Answers

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  {
...
like image 157
ben75 Avatar answered Oct 20 '22 00:10

ben75


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.

like image 28
sharakan Avatar answered Oct 20 '22 00:10

sharakan