Embeddable inheritance but still being able to reference specific terminal subtypes directly

Hello! Please ignore the weird model - it’s just to demonstrate.

I’m using Hibernate 6.6.45.

I have this discriminator-based embeddable hierarchy:

@Embeddable
@DiscriminatorColumn
public abstract class Car {

    @Column(nullable = false)
    private String color;

}

@Embeddable
@DiscriminatorValue("VW")
public class Volkswagen extends Car {

    @Column
    private Boolean electric;

}

@Embeddable
@DiscriminatorValue("TO")
public class Toyota extends Car {

    @Column
    private Integer numberOfWheels;

}

ParkingPlace supports different types of cars:

@Entity
public class ParkingPlace {

    @Id
    @GeneratedValue
    private Long id;

    @Embedded
    private Car car;

}

But VolkswagenDealer references the Volkswagen type directly - my expectation is that this should work without having a discriminator column:

@Entity
public class VolkswagenDealer {

    @Id
    @GeneratedValue
    private Long id;

    @Embedded
    private Volkswagen specialDeal;

}

Creating, persisting, reading ParkingPlace instances with different types of Car works perfectly.

But if I were to create and read back a VolkswagenDealer with its Volkswagen:

@Test
void terminalEmbeddableReferenceTest(SessionFactoryScope scope) throws Exception {
    scope.inTransaction(session -> {
        var pp = createVolkswagenDealer(session);
        Assertions.assertNotNull(pp.getId());

        session.flush();
        session.refresh(pp);

        Assertions.assertInstanceOf(Volkswagen.class, pp.getSpecialDeal());
        Assertions.assertEquals("red", pp.getSpecialDeal().getColor()); // FAILS HERE
        Assertions.assertEquals(true, pp.getSpecialDeal().getElectric());
    });
}

private VolkswagenDealer createVolkswagenDealer(Session session) {
    var vw = newVolkswagen();

    var dealer = new VolkswagenDealer();
    dealer.setSpecialDeal(vw);
    session.persist(dealer);

    return dealer;
}

private Volkswagen newVolkswagen() {
    var vw = new Volkswagen();
    vw.setColor("red");
    vw.setElectric(true);
    return vw;
}

It fails to persist and read the inherited Car#color property:

org.opentest4j.AssertionFailedError: 
Expected :red
Actual   :null

If I try querying the same color property:

    @Test
    void terminalEmbeddableReferenceQueryTest(SessionFactoryScope scope) throws Exception {
        scope.inTransaction(session -> {
            var pp = createVolkswagenDealer(session);
            Assertions.assertNotNull(pp.getId());

            session.flush();

            var query = session.createQuery("SELECT DISTINCT vd.specialDeal.color FROM VolkswagenDealer vd");
            var result = query.getSingleResult();

            Assertions.assertEquals("red", result);
        });
    }

Again it fails, this time with:

java.lang.IllegalArgumentException: org.hibernate.query.sqm.UnknownPathException: Could not resolve attribute 'color' of 'org.hibernate.bugs.impl.embeddables.Volkswagen' [SELECT DISTINCT vd.specialDeal.color FROM VolkswagenDealer vd]

In fact if I look at the auto generated schema, you can see that the inherited color property is missing completely:

Hibernate: 
    create table VolkswagenDealer (
        id bigint not null,
        electric boolean,
        primary key (id)
    )

Is there any way to fix this, to achieve the model I want? I really want to preserve Java type compatibility and not introduce any more classes than I have to.

Thank you.

(Also posted at https://stackoverflow.com/q/79948638/1341535 with an expanded premise.)

Alternatively is there a way to have a Car-typed field, but instead of the dtype database column to specify a fixed type/discriminator value with an annotation on the field?

Sidenote: the user guide says:

By default, the discriminator column will be STRING typed and named like <property_name>_DTYPE, where property_name is the name of the @Embedded property in the respective entity mapping.

But in reality it’s just DTYPEas you can see in the auto-generated schema:

Hibernate: 
    create table ParkingPlace (
        id bigint not null,
        DTYPE varchar(31) not null,
        color varchar(255) not null,
        numberOfWheels integer,
        electric boolean,
        primary key (id)
    )

Hi and thanks for reporting the problem. Please try to create a reproducer with our test case template and if you are able to reproduce the issue, create a bug ticket in our issue tracker and attach that reproducer.

I guess XML mapping could be the answer: map the discriminated hierarchy of embeddables under one set of names, and then again under different set of names with Car as a mapped superclass instead.

I’m not gonna introduce XML mapping just for this though… Resorting to this instead for now:

@Entity
public class VolkswagenDealer {

    @Id
    @GeneratedValue
    private Long id;

    @Embedded
    // We don't support other types of cars here, so let's remap their
    // properties to whatever in order to avoid introducing redundant DB fields
    @AttributeOverride(
        name = "numberOfWheels",
        column = @Column(name = "dtype", insertable = false, updatable = false)
    )
    private Car specialDeal;

    public Volkswagen getSpecialDeal() {
        return (Volkswagen) specialDeal;
    }

}

This isn’t great, but it’ll have to do.