Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA Table per Class Inheritance with different Id names

I have the following mapping:

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Vehicle {
    @Id
    @GeneratedValue
    Long id;
}

@Entity
@Table(name = "car")
@AttributeOverride(name = "id", column = @Column(name = "car_id"))
public class Car extends Vehicle {
}

@Entity
@Table(name = "bus")
@AttributeOverride(name = "id", column = @Column(name = "bus_id"))
public class Bus extends Vehicle {
}

And what I'm trying to achieve is to query different tables for retrieving both Car and Bus entities. For this sake I've created the following Spring Data repository

public interface VehicleRepository extends CrudRepository<Vehicle, Long> {
}

and try to use it like this: vehicleRepository.findAll();

However, in this case I get java.sql.SQLSyntaxErrorException: ORA-00904: "KEY": invalid identifier. Seems like using @Inheritance together with @AttributeOverride for @Id field doesn't work.

What I'd like to point out is that if Car and Bus entities had the same mapping for @Id it would work perfectly (but it's not the case: "car_id" and "bus_id")

Also, I've tried to move @Id field from Vehicle class to subclasses, however it turned out that every @Entity should contain an @Id.

One more thing I'd like to mention is that I've tried using @MappedSuperclass instead of @Inheritance but in this case I'm not able to query with abstact Vehicle type.

Could anyone help me with that?

Thanks

like image 381
StasKolodyuk Avatar asked Jan 16 '18 15:01

StasKolodyuk


1 Answers

You say,

And what I'm trying to achieve is to query different tables for retrieving both Car and Bus entities.

, but as a first consideration, you should evaluate whether you really want to do that. Think about this:

  • The single-table inheritance strategy is generally the fastest for whole-hierarchy queries such as you imagine performing. It can perform both whole-hierarchy and concrete-entity operations with single queries, without joins or unions.

  • The single-table and joined inheritance strategies ensure that all entities in the hierarchy have distinct keys, which is not necessarily the case for the table-per-class strategy.

  • The single-table and joined inheritance strategies facilitate relationships involving the abstract superclass; these are not well supported by the table-per-class strategy.

  • Support for the table-per-class strategy is optional. JPA providers are not required to support it, and the default provider in the GlassFish reference implementation in fact does not support it. Applications that rely on table-per-class therefore are not guaranteed to be portable. (Your provider, Hibernate, does support it.)

You go on to say,

However, in this case I get java.sql.SQLSyntaxErrorException: ORA-00904: "KEY": invalid identifier. Seems like using @Inheritance together with @AttributeOverride for @Id field doesn't work.

@AttributeOverride is only specified to work for overriding the attributes of mapped superclasses and fields and properties of embedded classes. It does work for @Id properties if they appear in those contexts. It is not specified to work (though neither is it specified to not work) for persistent fields and properties inherited from an entity superclass, but do observe that it cannot work for such properties with either the single-table or the joined inheritance strategy.

If @AttributeOverride did happen to work for you, that use would be non-portable. On the other hand, JPA has nothing else to accomplish what you want. A particular persistence provider could have an extension that supports it, but Hibernate has not historically done so -- all properties inherited from an entity superclass are mapped with the same names.

You also say,

One more thing I'd like to mention is that I've tried using @MappedSuperclass instead of @Inheritance but in this case I'm not able to query with abstact Vehicle type.

JPA does not provide a solution for your particular combination of requirements:

  • Mapping each concrete entity class to a separate table,
  • Naming the ID to a different column name in each entity table, and
  • Supporting polymorphic queries on the abstract supertype.

If you are unwilling to change any of those then you'll have to rely on an extension. And in that case you're in luck: Hibernate supports polymorphic queries where the polymorphic type is not mapped as an entity. Thus, if you're willing to make your application explicitly dependent on Hibernate, you can probably get where you want to be.

Specifically, to do this in Hibernate you would rely on "implicit polymorphism". To do this, you would avoid mapping the superclass as an entity, and from your experience, I guess it should not be a mapped superclass, either. It can be an ordinary class, though its properties would not be persistent, or you could use an interface instead. If your Vehicle class has properties that you want to make persistent, then you could change it to an embeddable class. You would furthermore annotate each of the vehicle entities to specify implicit polymorphism, for example:

@Entity
@Polymorphism(type = PolymorphismType.IMPLICIT)
// ...
public class Car implements Vehicle {
    // ...
}

The Hibernate docs claim that implicit polymorphism is the default, but I recommend applying the @Polymorphism annotation anyway, for clarity.

like image 138
John Bollinger Avatar answered Oct 06 '22 16:10

John Bollinger