Good day,
I'm using the @EntityListeners(AuditEntityListener.class) and
@CreatedOn, @ModifiedOn and @ModifiedBy annotations on an Entity bean
with the difference that I have a custom implementation of
java.time.ChronoLocalDateTime which converts any LocalDateTime +
ZoneOffset OR a ZonedDateTime to be the UTC date & time.
public class UTCDateTime implements ChronoLocalDateTime<LocalDate>,
Serializable {
private static final long serialVersionUID = 6492792765662073566L;
private static final ZoneOffset UTC = ZoneOffset.UTC;
private final LocalDateTime datetime;
// a lot of other details left out
}
The entity bean parts
@MappedSuperclass
public class InsertableAuditableBean extends BaseBean implements
InsertableAuditable {
@NotNull
@Size(min = 1, max = 50)
@Column(name = "zz_inserted_by", length = 50, nullable = false)
private String insertedBy;
@CreatedOn
@NotNull
@Temporal(value = TemporalType.TIMESTAMP)
@Column(name = "zz_inserted_on", nullable = false)
private UTCDateTime insertedOn;
// getters & setters
}
@MappedSuperclass
public class UpdateableAuditableBean extends InsertableAuditableBean
implements UpdateableAuditable {
@ModifiedBy
@Size(min = 1, max = 50)
@Column(name = "zz_updated_by", length = 50, nullable = true)
private String updatedBy;
@ModifiedOn
@Temporal(value = TemporalType.TIMESTAMP)
@Column(name = "zz_updated_on", nullable = true)
private UTCDateTime updatedOn;
// getters & setters
}
@Entity
@EntityListeners(AuditEntityListener.class)
@Table(schema = "data", name = "manufacturer", uniqueConstraints = {
@UniqueConstraint(columnNames = { "man_name", "man_country" })
})
@AttributeOverrides({
@AttributeOverride(name = "primaryKey", column = @Column(name =
"man_serial")),
@AttributeOverride(name = "insertedBy", column = @Column(name =
"man_inserted_by")),
@AttributeOverride(name = "insertedOn", column = @Column(name =
"man_inserted_on")),
@AttributeOverride(name = "updatedBy", column = @Column(name =
"man_updated_by")),
@AttributeOverride(name = "updatedOn", column = @Column(name =
"man_updated_on"))
})
@SequenceGenerator(name = "default_seq", schema = "data",
sequenceName = "manufacturer_man_serial_seq",
allocationSize = 1)
public class Manufacturer extends MirroredUpdateableAuditableBean
implements IManufacturer {
// nothing special here
}
There is also a custom AttributeConverter for the UTCDateTime class
because the epoch value is saved in the database.
@Converter(autoApply = true)
public class UTCDateTimePersistenceConverter implements
AttributeConverter<UTCDateTime, Long> {
@Override
public Long convertToDatabaseColumn(final UTCDateTime entityValue) {
Long res = null;
if (entityValue != null) {
res = entityValue.toMillis();
}
return res;
}
@Override
public UTCDateTime convertToEntityAttribute(final Long
databaseValue) {
UTCDateTime res = null;
if (databaseValue != null) {
res = new UTCDateTime(Instant.ofEpochMilli(databaseValue));
}
return res;
}
}
Now when I persist the entity I get the following exception (the last
bit with the real cause):
Caused by: org.apache.deltaspike.data.api.QueryInvocationException:
Failed calling Repository:
[Repository=systems.apace.data.manufacturer.model.dao.ManufacturerDAO,entity=systems.apace.data.manufacturer.model.Manufacturer,method=persist,exception=class
java.lang.reflect.InvocationTargetException,message=null
at
systems.apace.data.manufacturer.services.ManufacturerServiceBeanIntegrationTest.testInsertBean(ManufacturerServiceBeanIntegrationTest.java:55)
Caused by: java.lang.reflect.InvocationTargetException
at
systems.apace.data.manufacturer.services.ManufacturerServiceBeanIntegrationTest.testInsertBean(ManufacturerServiceBeanIntegrationTest.java:55)
Caused by:
org.apache.deltaspike.data.impl.audit.AuditPropertyException: Failed
to set property Manufacturer.insertedOn, is this a temporal type?
at
systems.apace.data.manufacturer.services.ManufacturerServiceBeanIntegrationTest.testInsertBean(ManufacturerServiceBeanIntegrationTest.java:55)
Caused by: java.lang.IllegalArgumentException: Annotated field is
not a date class: class za.co.t9.common.utils.time.UTCDateTime
at
systems.apace.data.manufacturer.services.ManufacturerServiceBeanIntegrationTest.testInsertBean(ManufacturerServiceBeanIntegrationTest.java:55)
Is there a way to implement my own
org.apache.deltaspike.data.impl.audit.PrePersistAuditListener and
org.apache.deltaspike.data.impl.audit.PreUpdateAuditListener and use
them to create the instance of UTCDateTime?
Would it be correct to write my own EntityListener -->
UTCDateTimeAuditListener and use it
@EntityListeners(UTCDateTimeAuditEntityListener.class) where
UTCDateTimeAuditListener follows the
org.apache.deltaspike.data.impl.audit.AuditEntityListener approach?
Secondly, do I need to use a CDI Qualifier somewhere to make sure that
my UTCDateTimeAuditEntityListener gets a reference to the correct
PrePersistAuditListener and PreUpdateAuditListener that knows how to
construct the UTCDateTime instance?
Lastly, I don't know if it is relevant, but where does
org.apache.deltaspike.data.impl.audit.TimestampsProvider fit into this
scenario?
Thank you for your time
Kind regards,
Nico Schlebusch
[email protected] <mailto:[email protected]>