I'm migrating a project from Spring Boot 2.7.x to 3.0.x. One of my entities has a collection with a composite type:
@Entity
@Audited
public class Specialist {
@Id
@GeneratedValue
private Long id;
@ElementCollection
@CompositeType(QualificationConverter.class)
private Set<Qualification> qualifications;
// ...
}
public class Qualification implements Serializable {
private LocalDate date = LocalDate.now();
private String customData;
// ...
}
public class Expert extends Qualification {
// ...
}
public class Professional extends Qualification {
// ...
}
The converter class (irrelevant parts ommitted):
public class QualificationConverter implements CompositeUserType<Qualification> {
@Override
public Object getPropertyValue(Qualification qualification, int i) throws HibernateException {
return switch (i) {
case 0 -> qualification.getCustomData();
case 1 -> qualification.getDate();
case 2 -> qualification.getClass().getName();
default -> null;
};
}
@Override
public Qualification instantiate(ValueAccess valueAccess, SessionFactoryImplementor sessionFactoryImplementor) {
String type = valueAccess.getValue(2, String.class);
try {
QualificationBuilder builder = (QualificationBuilder) Class.forName(type).getMethod("builder").invoke(null);
return builder.customData(valueAccess.getValue(0, String.class))
.date(valueAccess.getValue(1, LocalDate.class))
.build();
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException ex) {
throw new HibernateException(ex);
}
}
@Override
public Class<?> embeddable() {
return QualificationMapper.class;
}
@Override
public Class<Qualification> returnedClass() {
return Qualification.class;
}
// ...
public static class QualificationMapper {
String customData;
LocalDate date;
String type;
}
}
When I start the application, Hibernate Envers logs the audit mapping for qualifications collection:
<hibernate-mapping xmlns="http://www.hibernate.org/xsd/orm/hbm">
<class table="specialist_qualifications_AUD" entity-name="specialist_qualifications_AUD">
<composite-id name="originalId">
<key-many-to-one class="org.hibernate.envers.DefaultRevisionEntity" name="REV">
<column name="REV"/>
</key-many-to-one>
<key-property name="REVTYPE" type="org.hibernate.envers.internal.entities.RevisionTypeType"/>
<key-property name="Specialist_id">
<column name="specialist_id"/>
<type name="long">
<param name="org.hibernate.type.ParameterType.primaryKey">false</param>
<param name="org.hibernate.type.ParameterType.dynamic">true</param>
<param name="org.hibernate.type.ParameterType.returnedClass">java.lang.Long</param>
<param name="org.hibernate.type.ParameterType.accessType">field</param>
<param name="org.hibernate.type.ParameterType.entityClass">com.example.hibernate6enverscompositeusertype.domain.Specialist</param>
<param name="org.hibernate.type.ParameterType.propertyName">id</param>
</type>
</key-property>
<key-property name="SETORDINAL" type="integer">
<column name="SETORDINAL"/>
</key-property>
</composite-id>
</class>
</hibernate-mapping>
This is not what I expect nor want. Instead of the setordinal column I need the columns customData, date and type.
I read the docs twice, googled, searched this site, and hibernate issues but couldn't find any solution. I tried adding @Colums or @AttributeOverride annotations but all without success.
How can I tell Envers to generate the same columns in the audit table as in the collection table?
Of course, it worked with Spring Boot 2.7.x and the old @Type(Def) annotations and implementations.