In our team we decided to upgrade from Hibernate 5 to Hibernate 6. We have hundreds of JPA entities in our project. Some of them aren't working any more due to a new validation in Hibernate. I will explain it in detail with just a minimum of attributes:
Table Adapter:
Table AdapterParam:
Table AdapterParamSelectionList:
JPA entity of Adapter:
@Entity
public class Adapter implements Serializable {
@Id
@Column(name = "ADID")
private Integer adapterId;
@OneToMany(mappedBy = "adapter", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<AdapterParam> adapterParams = new HashSet<>();
// Getter and Setter...
}
JPA entity of AdapterParam:
@Entity
@IdClass(AdapterParamId.class)
public class AdapterParam implements Serializable {
public static class AdapterParamId implements Serializable {
private static final long serialVersionUID = 1L;
private Adapter adapter;
private String parameterName;
@Override
public int hashCode() {
return Objects.hash(adapter, parameterName);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AdapterParamId other = (AdapterParamId) obj;
return Objects.equals(adapter, other.adapter) && Objects.equals(parameterName, other.parameterName);
}
}
@Id
@Column(name = "PAN")
private String parameterName;
@Id
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ADID", referencedColumnName = "ADID")
private Adapter adapter;
@OneToMany(mappedBy = "adapterParam", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<AdapterParamSelectionList> adapterParamSelectList = new HashSet<>();
// Getter and Setter...
}
JPA entity of AdapterParamSelectionList:
@Entity
@IdClass(AdapterParamSelectionListId.class)
public class AdapterParamSelectionList implements Serializable {
public static class AdapterParamSelectionListId implements Serializable {
private static final long serialVersionUID = 1L;
private AdapterParam adapterParam;
private String code;
@Override
public int hashCode() {
return Objects.hash(adapterParam, code);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AdapterParamSelectionList other = (AdapterParamSelectionList) obj;
return Objects.equals(adapterParam, other.adapterParam)
&& Objects.equals(code, other.code);
}
}
@Id
@Column(name = "VAL")
private String code;
@Id
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ADID", referencedColumnName = "ADID") // this turns into error
@JoinColumn(name = "PAN", referencedColumnName = "PAN")
private AdapterParam adapterParam;
// Getter and Setter...
}
The exception of Hibernate is the following. It occurs while starting our application:
org.hibernate.AnnotationException: Referenced column 'ADID' in '@JoinColumn' for 'de.example.AdapterParamSelectionList.id.adapterParam' is not mapped by any property of the target entity
at org.hibernate.boot.model.internal.BinderHelper.findPropertiesByColumns(BinderHelper.java:501)
at org.hibernate.boot.model.internal.BinderHelper.createSyntheticPropertyReference(BinderHelper.java:160)
at org.hibernate.boot.model.internal.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:114)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processFkSecondPassesInOrder(InFlightMetadataCollectorImpl.java:1850)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1764)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:334)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:129)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:449)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:101)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:966)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1016)
In Hibernate 5.6 the relationship from AdapterParamSelectionList to AdapterParamBO worked as expected. I don't know what changed. What is the best way to define this?
Just to clarify: we are using IdClass instead of EmbeddedId because we are mapping the entites to DTOs.
Starting from Hibernate 5.3, composite identifiers using @IdClass
must directly map the columns. In your case, this does not happen because you use a ManyToOne relationship.
One solution would be to switch from @IdClass to @EmbeddedId
and @Embeddable
. This is the recommended option and looks like it fits your needs perfectly.
The AdapterParamId will change from @IdClass
to @Embeddable
:
@Embeddable
public class AdapterParamId implements Serializable {
@Column(name = "ADID")
private Integer adapterId;
@Column(name = "PAN")
private String parameterName;
// equals() and hashCode()
}