@OneToOne on an entity with discriminated id value

I try to create a @OneToOne relationship with an entity whose id is composed of 2 properties and where one of them is fixed by a discriminator annotation.

@Entity
@Table(name = "LIBELLE")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
@DiscriminatorOptions(force = true, insert = false)
public class Libelle {

    private LibelleId id;


    public Libelle() {
    }

    @EmbeddedId
    public LibelleId getId() {
        return id;
    }

    public void setId(final LibelleId id) {
        this.id = id;
    }


    @Embeddable
    public static class LibelleId implements Serializable {

        private String code;
        private String type;

        public LibelleId(String code, String type) {
            this.code = code;
            this.type = type;
        }

        public LibelleId() {
        }

        @Column(length = LIBELLE_CODE_MAX_SIZE)
        public String getCode() {
            return code;
        }

        public void setCode(String code) {
            this.code = code;
        }

        @Column(length = LIBELLE_TYPE_MAX_SIZE)
        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            LibelleId libelleId = (LibelleId) o;
            return Objects.equals(code, libelleId.code) &&
                    Objects.equals(type, libelleId.type);
        }

        @Override
        public int hashCode() {
            return Objects.hash(code, type);
        }

    }
}

@Entity
@DiscriminatorValue("A")
public class LibelleA extends Libelle {

}


@Entity
@Table(name = "Test")
public class Test implements Serializable {

        private LibelleA libelleA;
        
        @OneToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "code_value", referencedColumnName = "code")
        public LibelleA getLibelleA () {
            return libelleA;
        }
	
        public void setLibelleA(final LibelleA libelleA) {
            this.libelleA = libelleA;
        }

}

With this configuration the @OneToOne fails with the exception : AnnotationException: referencedColumnNames(code) of Test.libelleA referencing LibelleA not mapped to a single property.

It looks like the discrimator value is ignored on the relation and to make it work I need to specify it manually replacing the @JoinColumn with a formula :

@JoinColumnsOrFormulas({
       @JoinColumnOrFormula(column = @JoinColumn(name = "code_value", referencedColumnName = "code")),
       @JoinColumnOrFormula(formula = @JoinFormula(value = "'A'", referencedColumnName = "type"))
    })

The problem of using such formula is that it prevent me from using Envers to track this field changes.

So I was wondering if you knew a better configuration to make this relation work without a formula.

You should omit the discriminator from the embeddable LibelleId since the information is available through the concrete type of the Libelle object. The mapping you have should be fine. You just need to force the discriminator usage like you already do.

Thanks for the answer!
The mapping worked when I change my class according to your input :

@Entity
@Table(name = "LIBELLE")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
@DiscriminatorOptions(force = true)
public class Libelle {

    private String code;
    private String type;


        public Libelle() {
        }

	@Id
	@Column(length = LIBELLE_CODE_MAX_SIZE)
	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	@Column(length = LIBELLE_TYPE_MAX_SIZE, insertable = false, updatable = false)
	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

}

My concerned is that now my applicative id constraint is only on the code attribute that wasn’t meant to be unique.

In that case your original mapping was fine and you just need to use a @DiscriminatorFormula instead.

Thanks for response but I’m stuck trying to implement it.

@DiscriminatorFormula seems to be used when the model fails to have a discriminator column and when we want to simulate one with an sql clause.
Here I already have my column ‘type’ dedicated for this and I don’t really know how to write the formula to return it (case type is not null then type end ?).

Switching from @DiscriminatorColumn to @DiscriminatorFormula also didn’t removed the AnnotationException described in my first message preventing me from testing.

So if you have any tips or explanation left to spare, it would be much appreciated.

Use this:

@Entity
@Table(name = "LIBELLE")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorFormula("type")
@DiscriminatorOptions(force = true)
public class Libelle { .. }

The intention is to make the discriminator non-writable by Hibernate through the type of the object, but instead let this be based on the value that you set.

Thanks again for the explanation.

So I tried this mapping with the initial relation definition (without formula) and I’m getting the AnnotationException during the ApplicationContext loading.
With the formula everything works as well as with @DiscriminatorColumn.