I'm having a problem with Hibernate entities inheritance where it creates a copy of fk column of a many-to-one relation without updating it.
@Data
@Entity
public class Vehicle {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "vehicle")
private Set<Human> owner;
}
@Data
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Integer age;
@ManyToOne
private Vehicle vehicle;
}
@Data
@Entity
public class Human extends Person {
private String fullName;
}
With this entities it creates:
create table human (
full_name varchar(255),
id bigint not null,
vehicle_id bigint,
primary key (id)
) engine=InnoDB
create table person (
id bigint not null auto_increment,
age integer,
vehicle_id bigint,
primary key (id)
) engine=InnoDB
create table vehicle (
id bigint not null auto_increment,
primary key (id)
) engine=InnoDB
alter table human
add constraint FKqctp8ycdo4mm7fsou0v4jalx3
foreign key (id)
references person (id)
alter table person
add constraint FKgo297ke05qjubymwq6unmcnot
foreign key (vehicle_id)
references vehicle (id)
Why it declares Human.vehicle_id? I don't need it and hibernate dosn't sync it with Person.vehicle_id. The worst part is when hibernate Join Human with Vehicle it check Human.vehicle_id = Vehicle.id How can I resolve this issue? I can't change InheritanceType because it will be huge migration.
Test case:
final var car = Vehicle.builder().build();
vehiclesRepo.save(car);
final var mario = Human.builder()
.fullName("Mario Rossi")
.age(20)
.vehicle(car)
.build();
humansRepo.save(mario);
Vehicle
id|
--
1|
Person
id|age|vehicle_id|
-- --- ----------
2| 20| 1|
Human
full_name |id|vehicle_id|
----------- -- ----------
Mario Rossi| 2| |
-- generated query for findAll
select
human0_.id as id1_1_,
human0_1_.age as age2_1_,
human0_1_.vehicle_id as vehicle_3_1_,
human0_.full_name as full_nam1_0_
from
human human0_
inner join
person human0_1_
on human0_.id=human0_1_.id
select
vehicle0_.id as id1_2_0_
from
vehicle vehicle0_
where
vehicle0_.id=?
select
owner0_.vehicle_id as vehicle_3_0_0_,
owner0_.id as id2_0_0_,
owner0_.id as id1_1_1_,
owner0_1_.age as age2_1_1_,
owner0_1_.vehicle_id as vehicle_3_1_1_,
owner0_.full_name as full_nam1_0_1_,
vehicle1_.id as id1_2_2_
from
human owner0_
inner join
person owner0_1_
on owner0_.id=owner0_1_.id
left outer join
vehicle vehicle1_
on owner0_1_.vehicle_id=vehicle1_.id
where
owner0_.vehicle_id=?
owner0_.vehicle_id=?
here is where it fails the join because vehicle_id in human table is never populated, in this test scenario it's a bit better than real one because it actually made the join right and fails only in where conditions, in real use case it just use on 1=1
in join.
CodePudding user response:
There is a discrepancy in database/system design. If only Humans can be Vehicle owners - you need to move Vehicle field into Human class (it solves your issue). Or (in case all Persons can do it) - you need to change Set{Human} to Set{Person} (it solves the issue too)
CodePudding user response:
To avoid the vehicle_id in children tables the association in Vehicle must use Person as base type explicitly in field type or using OneToMany.targetEntity
This resolve my problem partially because it added Robot amoung owners, I don't need that. I can use a discriminator column with a @Where condition or leave children vehicle_id and sync it programatically.