Hi all,
I’ve been working with entities that use @Embedded
classes, and that need to reference each other with several properties.
In order to achieve this, I used a @OneToMany
relationship and @JoinColumns
annotations. And got the error:
Referenced column 'field2' mapped by target property 'embeddedEntity' occurs out of order in the list of '@JoinColumn's
I did a few experiments, to see what worked and what didn’t :
The base case :
@Table(name = "entity1")
@Entity
public class Entity1 {
@Id
private Long id;
@Column
private String field1;
@Column
private String field2;
@OneToMany
@JoinColumns({
@JoinColumn(name = "field1", referencedColumnName = "field1", insertable = false, updatable = false),
@JoinColumn(name = "field2", referencedColumnName = "field2", insertable = false, updatable = false),
})
private List<Entity2> entities2;
}
/////////////////
@Table(name = "entity2")
@Entity
public class Entity2 {
@Id
private Long id;
@Column
private String field1;
@Column
private String field2;
}
This does work, and permuting the @JoinColumn
annotations has no negative effect.
On the other side, this :
@Table(name = "entity1")
@Entity
public class Entity1 {
@Embeddable
public static class EmbeddedEntity1 {
@Column(name = "field1")
private String field1;
@Column(name = "field2")
private String field2;
}
@Id
private Long id;
@Embedded
private EmbeddedEntity1 embeddedEntity1;
@OneToMany
@JoinColumns({
@JoinColumn(name = "field2", referencedColumnName = "field2", insertable = false, updatable = false),
@JoinColumn(name = "field1", referencedColumnName = "field1", insertable = false, updatable = false),
})
private List<Entity2> entities2;
Doesn’t work. And I get the error :
Caused by: org.hibernate.AnnotationException: Referenced column 'field2' mapped by target property 'embeddedEntity1' occurs out of order in the list of '@JoinColumn's at org.hibernate.boot.model.internal.BinderHelper.findPropertiesByColumns(BinderHelper.java:545) at org.hibernate.boot.model.internal.BinderHelper.createSyntheticPropertyReference(BinderHelper.java:161) at org.hibernate.boot.model.internal.CollectionBinder.bindCollectionSecondPass(CollectionBinder.java:2701) at org.hibernate.boot.model.internal.CollectionBinder.bindOneToManySecondPass(CollectionBinder.java:1701) at org.hibernate.boot.model.internal.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:1599) at org.hibernate.boot.model.internal.CollectionBinder$1.secondPass(CollectionBinder.java:1588) at org.hibernate.boot.model.internal.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:45) at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1815) at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1774) at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:332) at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1432) at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1503) at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390) at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:366) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1833) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1782)
But if I change the EmbeddedEntity1 class to this :
@Embeddable
public static class EmbeddedEntity1 {
@Column(name = "field1")
private String z_alphabeticallyLast_field1;
@Column(name = "field2")
private String field2;
}
The error disappear !
It seems like Hibernate is sorting the embedded fields by alphabetical order.
This leads to very confusing errors, since nothing indicate that the name of my fields matter, and that it could be the cause of errors !
This behavior doesn’t happen when the JOIN is a @OneToOne