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);
}
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 .
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 ..
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.
Saving an entity can be performed via the CrudRepository. save(…) -Method. It will persist or merge the given entity using the underlying JPA EntityManager .
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With