Single table per class hierarchy without discriminator column - java

I have an existing table without discriminator column and I need to map it with single table hierarchy, how to map this?
here is my table
vehicle
========
vehicle_id
manufacturer
load_capacity
passenger_capacity
I have 3 classes
Vehicle.java
vehicleId;
manufacturer;
TransportationVehicle.java
loadCapacity;
PassengerVehicle.java
passengerCapacity;

If you have no discriminator, then you need another condition to distinguish TransportationVehicle and PassengerVehicle. I guess in your case you could use:
TransportationVehicle: loadCapacity IS NOT NULL
PassengerVehicle: passengerCapacity IS NOT NULL
As JPA inheritance always needs a discriminator, you will have to use #MappedSuperclass and two #Entity classes with a corresponding #Where annotation:
#MappedSuperclass
public abstract class Vehicle {
#Id
private Long vehicleId;
private String manufacturer;
}
#Entity #Where(clause = "loadCapacity IS NOT NULL")
public class TransportationVehicle extends Vehicle {
private int loadCapacity;
}
#Entity #Where(clause = "passengerCapacity IS NOT NULL")
public class PassengerVehicle extends Vehicle {
private int passengerCapacity;
}
Major disadvantage of this approach compared with real JPA inheritance: You can't reference the mapped superclass in JPA - neither in queries (SELECT v FROM Vehicle v) nor in another entity (#OneToOne Vehicle vehicle).

One way to do it is use #MappedSuperclass on the Vehicle class and then map TransportationVehicle and PassengerVehicle to the same table.
This way you will inherit the mapped fields from Vehicle without having to map it as an entity.

Related

Avoid JPA abstract class extra tables

I'm using JPA JOINED inheritance strategy with 3 abstract classes and 2 concrete.
Like this (code reduced for example purpose):
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
abstract class Employee {
#Id
private Long id;
private String firstName;
private String secondName;
}
#Entity
abstract class ProjectManager extends Employee {
#OneToMany(mappedBy="manager")
private List<Developer> developers;
}
#Entity
abstract class Developer extends Employee {
#ManyToOne
#JoinColumn(name="project_manager_id")
private ProjectManager manager;
}
#Entity
class JavaProjectManager extends ProjectManager {
private String someCustomProperty;
}
#Entity
class JavaDeveloper extends Developer {
private String skill;
}
This way, JPA creates 5 tables. But one of this tables, abstract ProjectManager it is empty. I mean, ProjectManager table only have ID column due to inheritance strategy.
My question is: how I can avoid this extra table for ProjectManager abstract class but keeping the same hierarchy of classes ?
I could not remove #Entity on ProjectManager class because I still need bidirectional relationship with Developer class.
Edit:
Also, I don't want move developers OneToMany from ProjectManager to JavaProjectManager, because I have more classes that extends from ProjectManager and need to have developers.
InheritanceType.TABLE_PER_CLASS does exactly what you want.
This type only creates/uses tables for non-abstract tables.
Read more about the various types here:
https://www.thoughts-on-java.org/complete-guide-inheritance-strategies-jpa-hibernate/

JPA discriminator undesired

I know there are several questions on this argument, but I think mine is a bit different.
I'm trying to use JPA notation instead of XML mapping. In my queries always there's an undesired dtype column, and I don't want to use neither discriminator column and formula.
I have four classes as follow:
The first named ObjectUUID. All classes extend this super class.
This class is used only to define id field, as follow:
#MappedSuperclass
public class ObjectUUID {
#Id
#GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy = "uuid")
protected String id;
// getter and setter and other static methods
}
Then I have another class, named TrackSys where I define other fields (as insert date and update date) as follow:
#MappedSuperclass
public class TrackSys extends ObjectUUID{
#Column(name="dt_ins")
private CustomCalendar dtInsert;
#Column(name="dt_upd")
private CustomCalendar dtUpdate;
// getter and setter
}
the third and the forth classes are beans mapped on DB, as follow:
#Entity
#Table(name="patient")
public class PatientUUID extends TrackSys {
}
#Entity
#Table(name="patient")
public class Patient extends PatientUUID {
#Column(name="surname")
private String surname;
#Column(name="name")
private String name;
#Column(name="cf")
private String cf;
// getter and setter
}
I define a repository to query my patient table, as follow:
public interface PatientRepository extends JpaRepository<Patient, Long> {
List<Patient> findBySurname(String surname);
}
When my Patient query runs, the generated SQL is the follow:
select patient0_.id as id2_2_, patient0_.dt_ins as dt_ins3_2_,
patient0_.dt_upd as dt_upd4_2_, patient0_.cf as cf7_2_,
patient0_.surname as surname8_2_, patient0_.name as name11_2_,
from patient patient0_ where patient0_.dtype='Patient'
and patient0_.surname=?
Now...
I don't want dtype column and I don't want to use discriminator formula.
With the XML mapping this is possible without particular properties to specify.
With JPA annotation I try in order:
Use #Inheritance(strategy = InheritanceType.SINGLE_TABLE) but dtype is always present
Use #Inheritance(strategy = InheritanceType.JOINED) but dtype is always present
Now, my feeling versus dtype is only hate, how can I get this simple query, as follow:
select patient0_.id as id2_2_, patient0_.dt_ins as dt_ins3_2_,
patient0_.dt_upd as dt_upd4_2_, patient0_.cf as cf7_2_,
patient0_.surname as surname8_2_, patient0_.name as name11_2_,
from patient patient0_ where patient0_.surname=?

Hibernate and #AttributeOverride

I have 3 classes:
#MappedSuperclass
public class BaseEntity {
#Id
#GeneratedValue
private Long id;
...
}
#Entity
public class Person extends BaseEntity {
private String name;
...
}
#Entity
#AttributeOverride(name = "id", #Column = (name = "idStudent"))
public class Student extends Person {
private float avgGrades;
...
}
I would like to override ID property so in Student table there would be a idStudent column instead of id. Unfortunately, the code above doesn't work. It looks like #AttributeOverride only works for the class you extending (and no one step further). Is there any way to override attribute name in situation I've descried (override attribute in class which exteds our class being extended) ?
Your problem is very easy to understand, if you know what the default inheritance type is: SINGLE_TABLE.
That means all entities that extending Person are in the same table. And thus Person already defines the ID column. Because you would otherwise violate the contract of the primary key column of your Person table.
I cite the JavaDoc of #AttributeOverride as well:
May be applied to an entity that extends a mapped superclass or to an embedded field or property to override a basic mapping or id mapping defined by the mapped superclass or embeddable class (or embeddable class of one of its attributes).
It always helps to read the JavaDoc first, before asking questions here.
What can you do about it? Make your Person a #MappedSuperclass (or create a BasePerson that is one).

Primary Key Column Name with Spring Data JPA and Hibernate

I have the following setup with Spring Data JPA and Hibernate as the persistence provider. All of my entities inherit from a base class
#MappedSuperclass
public class BaseEntity {
#Id
private id;
#Version
private String version;
//more common fields
}
For example:
#Entity
public class Foo extends BaseEntity {
}
This leads to a primary key column with name "ID" to be generated on the "FOO" table. I would like to change the naming of the primary key column. It should reflect the name of class or table. So it should be "FOO_ID" instead of just "ID".
I know that I could do this statically by using #Column(name = "FOO_ID"). But that would mean I have to do this for every Entity. Is there a more dynamic way to achieve this?
I know this is an old question, but stumbled across this looking for an answer... Eventually found this solution elsewhere:
#Entity
#AttributeOverride(name="id", column=#Column(name="FOO_ID"))
public class Foo extends BaseEntity {
}
All your subClasses will have the same ID column name because of the inheritance, you can specify a common id colum name for all subClasses in the Base entity Class
Why use inheritance then? Just do it without inheritance.
You could use getters/setters to rename your fields
Ex:
class Foo {
private Long id;
public Long getFooId() {
return this.id;
}
public void setFooId(Long fooId) {
this.id = fooId;
}
}

Retrieve Collection of Subclasses Based on Column Value

The title is a bit weird, so let me clarify.
I have two objects, Garage and Vehicle, in a one-to-many relationship. There are multiple types of vehicles, such as car and truck; the type is persisted as a string in the Vehicle table.
Here's the two classes:
#Entity
#Table(...)
public class Garage {
....
#Column(name = "garageId")
private Integer garageId;
#OneToMany
#JoinColumn(name = "garageId")
private Set<Vehicle> vehicles;
....
}
#Entity
#Table(...)
public class Vehicle {
....
#Column(name = "garageId")
private Integer garageId;
//could be "Truck" or "Car"
#Column(name = "type")
private String type;
....
}
In order to differentiate between types of Vehicles, currently we have to look at the type column and use an enum. What I'd like to do is have subclasses of Vehicle, such are Car and Truck, to represent the different types rather than relying on the Type field. Then my code can instanceof to determine the type instead of using that field.
However, I don't know how to tell Hibernate how to instantiate subclasses based on the value of the Type column, or if it even can.
I'd appreciate any help you guys can give. Thanks in advance!
If you want to save the current database schema, you need to use SINGLE_TABLE inheritence strategy with type column as a discriminator:
#Entity
#Table(...)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type")
public abstract class Vehicle { ... }
#Entity
#DiscriminatorValue("...")
public class Car extends Vehicle { ... }
#Entity
#DiscriminatorValue("...")
public class Truck extends Vehicle { ... }

Categories

Resources