Home > Back-end >  fk column created in child entity with ManyToOne in parent for InheritanceType.JOINED
fk column created in child entity with ManyToOne in parent for InheritanceType.JOINED

Time:08-24

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)

ER model

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 example

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.

  • Related