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]>


Reply via email to