Для определенного объекта Hibernate у нас есть требование хранить время его создания и время последнего обновления. Как бы вы спроектировали это?
Какие типы данных вы бы использовали в базе данных (предполагая MySQL, возможно, в другом часовом поясе, чем JVM)? Будут ли типы данных учитывать часовой пояс?
Какие типы данных вы будете использовать в Java (
Date
,Calendar
,long
...)?Кого бы вы взяли на себя ответственность за установку меток времени: базы данных, инфраструктуры ORM (Hibernate) или разработчика приложений?
Какие аннотации вы бы использовали для отображения (например
@Temporal
)?
Я ищу не только рабочее решение, но и безопасное и продуманное решение.
Вы можете просто использовать
@CreationTimestamp
и@UpdateTimestamp
:источник
TemporalType.DATE
в первом случае иTemporalType.TIMESTAMP
во втором.@CreationTimestamp
и@UpdateTimestamp
нужно либо@Column(..., columnDefinition = "timestamp default current_timestamp")
, либо использовать@PrePersist
и@PreUpdate
(последний также прекрасно гарантирует, что клиенты не могут установить другое значение).nullable=false
от@Column(name = "create_date" , nullable=false)
работавшегоВзяв ресурсы из этого поста вместе с информацией, взятой слева и справа из разных источников, я пришел с этим элегантным решением и создал следующий абстрактный класс
и пусть все ваши сущности расширяют его, например:
источник
not-null property references a null or transient value: package.path.ClassName.created
@Column(name = "updated", nullable = false, insertable = false)
заставить это работать. Интересно, что этот ответ получил так много голосов ..1. Какие типы столбцов базы данных вы должны использовать
Ваш первый вопрос был:
В MySQL
TIMESTAMP
тип столбца переключается с локального часового пояса драйвера JDBC на часовой пояс базы данных, но он может хранить только временные метки до'2038-01-19 03:14:07.999999
, поэтому это не лучший выбор в будущем.Таким образом, лучше использовать
DATETIME
вместо этого, который не имеет этого ограничения верхней границы. ТемDATETIME
не менее, часовой пояс не знает. Поэтому по этой причине лучше всего использовать UTC на стороне базы данных и использоватьhibernate.jdbc.time_zone
свойство Hibernate.2. Какой тип свойства объекта следует использовать
Ваш второй вопрос был:
На стороне Java вы можете использовать Java 8
LocalDateTime
. Вы также можете использовать устаревшие версииDate
, но типы даты / времени в Java 8 лучше, поскольку они неизменны и не выполняют смещение часового пояса к локальному часовому поясу при их регистрации.Теперь мы также можем ответить на этот вопрос:
Если вы используете
LocalDateTime
илиjava.sql.Timestamp
для сопоставления свойства сущности метки времени, вам не нужно его использовать,@Temporal
поскольку HIbernate уже знает, что это свойство должно быть сохранено как метка времени JDBC.Только если вы используете
java.util.Date
, вам нужно указать@Temporal
аннотацию, например:Но гораздо лучше, если вы отобразите это так:
Как создать значения столбца аудита
Ваш третий вопрос был:
Есть много способов достижения этой цели. Вы можете позволить базе данных сделать это ..
Для
create_on
столбца вы можете использоватьDEFAULT
ограничение DDL, например:Для
updated_on
столбца вы можете использовать триггер БД для установки значения столбца приCURRENT_TIMESTAMP()
каждом изменении данной строки.Или используйте JPA или Hibernate, чтобы установить их.
Предположим, у вас есть следующие таблицы базы данных:
И каждая таблица имеет столбцы, такие как:
created_by
created_on
updated_by
updated_on
Использование Hibernate
@CreationTimestamp
и@UpdateTimestamp
аннотацийHibernate предлагает
@CreationTimestamp
и@UpdateTimestamp
аннотации , которые могут быть использованы для сопоставленияcreated_on
иupdated_on
столбцов.Вы можете использовать
@MappedSuperclass
для определения базового класса, который будет расширен всеми сущностями:И все сущности будут расширять
BaseEntity
, как это:Тем не менее, даже если
createdOn
иupdateOn
свойства устанавливаются с помощью Hibernate-специфических@CreationTimestamp
и@UpdateTimestamp
аннотации, тоcreatedBy
иupdatedBy
требуют регистрации обратного вызова приложения, как показано с помощью следующего раствора JPA.Использование JPA
@EntityListeners
Вы можете инкапсулировать свойства аудита в Embeddable:
И создайте
AuditListener
для установки свойств аудита:Чтобы зарегистрировать
AuditListener
, вы можете использовать@EntityListeners
аннотацию JPA:источник
datetime
болееtimestamp
. Вы хотите, чтобы ваша база данных знала часовой пояс ваших временных меток. Это предотвращает ошибки преобразования часового пояса.timestsmp
Тип не хранит данные часового пояса. Он просто делает разговор из приложения TZ в DB TZ. В действительности вы хотите хранить клиентскую TZ отдельно и вести диалог в приложении до визуализации пользовательского интерфейса.timestamp
всегда в UTC. MySQL преобразуетTIMESTAMP
значения из текущего часового пояса в UTC для хранения и обратно из UTC в текущий часовой пояс для извлечения. Документация по MySQL: типы DATE, DATETIME и TIMESTAMPВы также можете использовать перехватчик для установки значений
Создайте интерфейс с именем TimeStamped, который реализуют ваши сущности
Определить перехватчик
И зарегистрируйте его в фабрике сессий
источник
С помощью решения Оливье во время обновлений вы можете столкнуться с:
Чтобы решить эту проблему, добавьте updatable = false к аннотации @Column атрибута «create»:
источник
@Version
. Когда для объекта задано два вызова, один должен сохранить, а другой - обновить. Я столкнулся с той же проблемой из-за этого. Как только я добавил,@Column(updatable = false)
это решило мою проблему.Спасибо всем, кто помог. Проведя некоторые исследования сам (я парень, который задал вопрос), вот что я нашел наиболее значимым:
Тип столбца базы данных: независимое от часового пояса число миллисекунд с 1970 года, представленное как
decimal(20)
2 ^ 64, имеет 20 цифр, а дисковое пространство дешево; давайте будем простыми. Также я не буду использовать ниDEFAULT CURRENT_TIMESTAMP
триггеры, ни триггеры. Я не хочу магии в БД.Java тип поля:
long
. Отметка времени Unix хорошо поддерживается различными библиотеками,long
не имеет проблем с Y2038, арифметика отметки времени является быстрой и простой (в основном оператор<
и оператор+
, при условии, что в расчетах не участвуют дни / месяцы / годы). И, самое главное, и примитивыlong
s, иjava.lang.Long
s неизменны - эффективно передаются по значению - в отличие отjava.util.Date
s; Я бы очень разозлился,foo.getLastUpdate().setTime(System.currentTimeMillis())
когда нашел что-то подобное при отладке чужого кода.Платформа ORM должна отвечать за автоматическое заполнение данных.
Я еще не проверял это, но только смотрю на документы, которые, как я предполагаю,
@Temporal
сработают; не уверен, могу ли я использовать@Version
для этой цели.@PrePersist
и@PreUpdate
являются хорошими альтернативами, чтобы контролировать это вручную. Добавление этого в супертип слоя (общий базовый класс) для всех сущностей - это милая идея, при условии, что вам действительно нужна временная метка для всех ваших сущностей.источник
Если вы используете Session API, обратные вызовы PrePersist и PreUpdate не будут работать согласно этому ответу .
Я использую метод persist () Hibernate Session в своем коде, поэтому единственный способ выполнить эту работу - использовать приведенный ниже код и следовать этому сообщению в блоге (также опубликованному в ответе ).
источник
updated.clone()
противном случае другие компоненты могут манипулировать внутренним состоянием (дата)Теперь есть также аннотации @CreatedDate и @LastModifiedDate.
=> https://programmingmitra.blogspot.fr/2017/02/automatic-spring-data-jpa-auditing-saving-CreatedBy-createddate-lastmodifiedby-lastmodifieddate-automatics.html
(Весенние рамки)
источник
Следующий код работал для меня.
источник
protected Integer id;
какprotected
в родительском классе, потому что я не мог использовать его в своих тестах, как.getId()
Хороший подход - иметь общий базовый класс для всех ваших сущностей. В этом базовом классе вы можете иметь свойство id, если оно обычно именуется во всех ваших сущностях (общий дизайн), ваших свойствах создания и даты последнего обновления.
На дату создания вы просто сохраняете свойство java.util.Date . Будьте уверены, чтобы всегда инициализировать его с новым Date () .
Для последнего поля обновления вы можете использовать свойство Timestamp, вам нужно сопоставить его с @Version. С этой аннотацией свойство будет автоматически обновляться Hibernate. Помните, что Hibernate также применяет оптимистическую блокировку (это хорошо).
источник
Просто чтобы усилить:
java.util.Calender
это не для отметок времени .java.util.Date
является на мгновение агностиком региональных вещей, таких как часовые пояса. Большинство баз данных хранят вещи таким образом (даже если кажется, что это не так; обычно это настройка часового пояса в клиентском программном обеспечении; данные хороши)источник
В качестве типа данных в JAVA я настоятельно рекомендую использовать java.util.Date. Я столкнулся с довольно неприятными проблемами с часовым поясом при использовании Календаря. Смотрите эту тему .
Для установки меток времени я бы порекомендовал использовать либо подход AOP, либо вы можете просто использовать триггеры на столе (на самом деле это единственное, что я нахожу когда-либо приемлемым для использования триггеров).
источник
Вы можете сохранить время как в формате DateTime и в формате UTC. Я обычно использую DateTime вместо Timestamp из-за того, что MySql преобразует даты в UTC и обратно в местное время при хранении и получении данных. Я бы предпочел хранить любую такую логику в одном месте (бизнес-уровень). Я уверен, что есть и другие ситуации, когда использование Timestamp предпочтительнее.
источник
У нас была похожая ситуация. Мы использовали Mysql 5.7.
Это сработало для нас.
источник
@PrePersist
и@PrePersist
не покрывайте такой случай.Если мы используем @Transactional в наших методах, @CreationTimestamp и @UpdateTimestamp сохранят значение в БД, но вернут значение null после использования save (...).
В этой ситуации, используя saveAndFlush (...), добились цели
источник
Я думаю, что лучше не делать это в коде Java, вы можете просто установить значение столбца по умолчанию в определении таблицы MySql.
источник