Search code examples
mysqlhibernatehibernate-mapping

How to store only specific timestamp columns in UTC in hibernate


In my Mysql DB, I have some columns which need to be stored in UTC using hibernate. I can't force my JVM to store all timestamp column in UTC as I have other columns for different time zones. I have found two solutions

1) To configure hostname as jdbc:mysql://hostname/databaseName?useTimezone=true&serverTimezone=UTC 

2)  private static final TimeZone UTC = TimeZone.getTimeZone("UTC");

But both the solution is for forcing timezone in UTC for all the columns where as my requirement is for specific columns.

    @CreatedDate
    @Temporal(TemporalType.TIMESTAMP)
    @Column(nullable = false,updatable = false, name="created_at")
    private Date createdAt;

Solution

  • You can do that manually in your code, for specifically 1 column. Basically, the idea is to update the date to the desired date just before updating in the DB.

    For this, You will need to assign a listener in the Hibernate's EventListenerRegistry. The following class will apply a CreateOrUpdateDateListener listener (created by you) just before it performs the save operation.

    @Component
    public class HibernateEventWiring {
    
        @Autowired
        @Qualifier(value = "your_sessionFactory")           //Your session factory
        private LocalSessionFactoryBean sessionFactory;
    
        @Autowired
        private CreateOrUpdateDateListener listener;
    
        @PostConstruct
        public void registerListeners() {
            EventListenerRegistry registry = ((SessionFactoryImpl) sessionFactory.getObject()).getServiceRegistry().getService(
                    EventListenerRegistry.class);
            registry.getEventListenerGroup(EventType.SAVE_UPDATE).appendListener(listener);
        }
    }
    

    The following class extends DefaultSaveOrUpdateEventListener and adds your custom code of updating the timestamp to what you want. And, we have used it's object as a listener in EventListenerRegistry

    @Component
    public class CreateOrUpdateDateListener extends DefaultSaveOrUpdateEventListener {
    
        @Override
        public void onSaveOrUpdate(SaveOrUpdateEvent event) {
            if (event.getObject() instanceof CreateUpdateTimestamp) {
                CreateUpdateTimestamp record = (CreateUpdateTimestamp) event.getObject();
                record.setUpdatedTimestamp(LocalDateTime.now(Clock.systemUTC()));
            }
            super.onSaveOrUpdate(event);
        }
    }
    
    public interface CreateUpdateTimestamp {
        public void setCreatedTimestamp(LocalDateTime date);
        public void setUpdatedTimestamp(LocalDateTime date);
    }
    

    Following is the entity class. This class will implement CreateUpdateTimestamp so that we can use this class via CreateUpdateTimestamp's ref in CreateOrUpdateDateListener.

    @Entity
    @Table(name = "transactions")
    public class Transaction implements CreateUpdateTimestamp{
    
        private LocalDateTime updatedTimestamp;
    
        @Column(name = "updated_timestamp", insertable = true, updatable = true)
        public LocalDateTime getUpdatedTimestamp() {
            return updatedTimestamp;
        }
        public void setUpdatedTimestamp(LocalDateTime updatedTimestamp) {
            this.updatedTimestamp = updatedTimestamp;
        }
    }