I have an entity BlocRecord having a composite code BlocRecordId, and in its composite code there is an @Embedded (relation code ManyToOne) pointing to another entiy Record and want to Audit the entity BlocRecord.
The entity BlocRecord
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "blocRecord")
@Access(value = AccessType.FIELD)
@Audited
public class BlocRecord {
@EmbeddedId
private BlocRecordId blocRecordId = new BlocRecordId();
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumns({
@JoinColumn(name = "record_identifier_", referencedColumnName = "identifier_", unique = false, nullable = false),
@JoinColumn(name = "record_recordType_", referencedColumnName = "recordType_", unique = false, nullable = false)})
@MapsId("record")
private Record record;
...
}
The id class BlocRecordID
@Embeddable
public class BlocRecordId implements Serializable {
@Embedded
private RecordId record;
@Column(name = "source_")
String source ;
@Column(name = "messageType_")
String messageType ;
The entity Record
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "records")
@Access(value = AccessType.FIELD)
@Audited
public class Record {
@EmbeddedId
private RecordId recordId = new RecordId();
@OneToMany(targetEntity = BlocRecord.class, fetch = FetchType.LAZY, mappedBy = "record")
private Set<BlocRecord> blocRecord = new java.util.HashSet<>();
...
}
The idClass of the entity Record
@Embeddable
public class RecordId implements Serializable{
@Column(name = "identifier_")
String identifier ;
@Column(name = "recordType_")
String recordType ;
}
Hibernate-envers fails when trying to generate the metadata of the field record in the embeddable BlocRecordId, the The flowing exception is thrown
org.hibernate.MappingException: Type not supported: org.hibernate.type.ComponentType
at org.hibernate.envers.configuration.internal.metadata.IdMetadataGenerator.addIdProperties(IdMetadataGenerator.java:121)
at org.hibernate.envers.configuration.internal.metadata.IdMetadataGenerator.addId(IdMetadataGenerator.java:230)
at org.hibernate.envers.configuration.internal.metadata.AuditMetadataGenerator.generateFirstPass(AuditMetadataGenerator.java:642)
at org.hibernate.envers.configuration.internal.EntitiesConfigurator.configure(EntitiesConfigurator.java:95)
at org.hibernate.envers.boot.internal.EnversServiceImpl.doInitialize(EnversServiceImpl.java:154)
at org.hibernate.envers.boot.internal.EnversServiceImpl.initialize(EnversServiceImpl.java:118)
at org.hibernate.envers.boot.internal.AdditionalJaxbMappingProducerImpl.produceAdditionalMappings(AdditionalJaxbMappingProducerImpl.java:99)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:288)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:83)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:417)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:86)
at org.hibernate.boot.MetadataSources.buildMetadata(MetadataSources.java:179)
Do you have any idea how to resolve the issue ?
Thanks
At the moment, Envers does not support the idea of nesting an embeddable inside an embeddable when we map the identifier columns like your example illustrates. The only valid mappings that Envers presently does support is if the attribute in the embeddable is a @ManyToOne
or a @Basic
type.
You can work around this problem but it involves being a bit more explicit and not using RecordId
. What I mean is rewrite BlocRecordId
to be the following:
@Embeddable
public class BlocRecordId implements Serializable {
@Column(name = "identifier_")
String identifier;
@Column(name = "recordType_")
String recordType;
@Column(name = "source_")
String source;
@Column(name = "messageType_")
String messageType;
@Transient
private RecordId recordId;
/** Helper method to assign the values from an existing RecordId */
public void setRecordId(RecordId recordId) {
this.identifier = recordId.getIdentifier();
this.recordType = recordId.getRecordType();
}
/** Helper method to get the RecordId, caching it to avoid multiple allocations */
public RecordId getRecordId() {
if ( recordId == null ) {
this.recordId = new RecordId( identifier, recordType );
}
return this.recordId;
}
}
I agree this is less than ideal but it does at least work around the current limitation of the code. I have gone added and added HHH-13361 as an open issue to support this. You're welcomed to contribute if you wish or I'll work in getting this supported for Envers 6.0.