Създаване на времеви отпечатък и последна актуализирана времева маркировка с хибернация и MySQL

За конкретен обект за хибернация имаме изискване да запазим времето, което е създадено, и последния път, когато е бил актуализиран. Как бихте го проектирали?

  • Какви типове данни бихте използвали в базата данни (приемайки MySQL, може би в различна часова зона, JVM)? Дали типовете данни ще са наясно с часовата зона?

  • Какви типове данни използвате в Java ( Date , Calendar , long , ...)?

  • На кого не бихте отговорили за поставянето на времеви печати, база данни, ORM структура (Hibernate) или програмист?

  • Какви пояснения бихте използвали за съвпадение (например, @Temporal )?

Търся не само работно решение, но и безопасно и добре развито решение.

181
21 окт. настроен на ngn 21 oct. 2008-10-21 15:06 '08 в 15:06 2008-10-21 15:06
@ 13 отговора

Ако използвате анотации от JPA, можете да използвате @PrePersist с @PrePersist и @PreUpdate :

 @Entity @Table(name = "entities") public class Entity { ... private Date created; private Date updated; @PrePersist protected void onCreate() { created = new Date(); } @PreUpdate protected void onUpdate() { updated = new Date(); } } 

или можете да използвате анотацията @EntityListener в класа и да поставите кода на събитието във външен клас.

217
21 окт. отговор, даден от Guðmundur Bjarni 21 октомври 2008-10-21 16:14 '08 в 16:14 2008-10-21 16:14

Вземайки ресурси в това съобщение, заедно с информация, получена отляво и отдясно от различни източници, измислих това елегантно решение, създавайки следния абстрактен клас:

 import java.util.Date; import javax.persistence.Column; import javax.persistence.MappedSuperclass; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import javax.persistence.Temporal; import javax.persistence.TemporalType; @MappedSuperclass public abstract class AbstractTimestampEntity { @Temporal(TemporalType.TIMESTAMP) @Column(name = "created", nullable = false) private Date created; @Temporal(TemporalType.TIMESTAMP) @Column(name = "updated", nullable = false) private Date updated; @PrePersist protected void onCreate() { updated = created = new Date(); } @PreUpdate protected void onUpdate() { updated = new Date(); } } 
border=0

и всичките му същности го разширяват, например:

 @Entity @Table(name = "campaign") public class Campaign extends AbstractTimestampEntity implements Serializable { ... } 
86
28 окт. Отговорът е даден от Оливие Рефало на 28 октомври. 2010-10-28 01:49 '10 в 1:49 2010-10-28 01:49

Можете просто да използвате @CreationTimestamp и @UpdateTimestamp :

 @CreationTimestamp @Temporal(TemporalType.TIMESTAMP) @Column(name = "create_date") private Date createDate; @UpdateTimestamp @Temporal(TemporalType.TIMESTAMP) @Column(name = "modify_date") private Date modifyDate; 
58
10 сент. Отговорът е даден от idmitriev Sep 10 2016-09-10 19:01 '16 в 19:01 2016-09-10 19:01

Можете също да използвате прехващача за задаване на стойностите

Създайте TimeStamped интерфейс, който изпълнява вашите обекти.

 public interface TimeStamped { public Date getCreatedDate(); public void setCreatedDate(Date createdDate); public Date getLastUpdated(); public void setLastUpdated(Date lastUpdatedDate); } 

Идентифицирайте прехващача

 public class TimeStampInterceptor extends EmptyInterceptor { public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) { if (entity instanceof TimeStamped) { int indexOf = ArrayUtils.indexOf(propertyNames, "lastUpdated"); currentState[indexOf] = new Date(); return true; } return false; } public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { if (entity instanceof TimeStamped) { int indexOf = ArrayUtils.indexOf(propertyNames, "createdDate"); state[indexOf] = new Date(); return true; } return false; } } 

И го регистрирайте, като използвате фабричната сесия.

15
21 окт. отговор, даден от Kieren Dixon 21 октомври 2011-10-21 04:32 '11 в 4:32 2011-10-21 04:32

Благодаря на всички, които помогнаха. След самообучение (аз съм човекът, задал въпроса), това е най-полезното:

  • Тип колона на базата данни: обобщеният брой милисекунди по посока на часовниковата стрелка от 1970 г. е представен като decimal(20) , защото 2 ^ 64 има 20 цифри, а дисковото пространство е евтино; нека бъде лесно. Освен това няма да използвам DEFAULT CURRENT_TIMESTAMP или тригерите. Не искам магия в базата данни.

  • Тип поле на Java: long . Времевата таблица на Unix е добре поддържана в различни библиотеки, long няма проблеми с Y2038, аритметиката на времевата маркировка е бърза и проста (най-вече < и оператор + , ако в изчисленията не участват дни / месеци / години). И най-важното е, че примитивите long и java.> са неизменни - ефективно преминават по стойност - за разлика от java.util.Date ; Ще бъда много ядосан, за да намеря нещо като foo.getLastUpdate().setTime(System.currentTimeMillis()) при отстраняване на грешки в друг код.

  • Структурата на ORM трябва да отговаря за автоматичното попълване на данни.

  • Още не съм го тествал, но само като гледам документите, предполагам, че @Temporal ще свърши работата; Не знам дали мога да използвам @Version за тази цел. @PrePersist и @PreUpdate са добри алтернативи за ръчно управление на това. Добавянето на това към нивото на супертип (общ базов клас) за всички обекти е хубава идея, при условие, че наистина имате нужда от времева справка за всички ваши обекти.

12
23 окт. отговорът е даден ngn 23 oct. 2008-10-23 17:22 '08 в 17:22 2008-10-23 17:22

Когато използвате решението на Olivier по време на операциите по обновяване, може да срещнете:

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Колоната "създадена" не може да бъде нула

За да разрешите този проблем, добавете updatable = false в анотацията @Column на атрибута "създаден":

 @Temporal(TemporalType.TIMESTAMP) @Column(name = "created", nullable = false, updatable=false) private Date created; 
7
25 апр. отговор, даден endriju 25 Apr 2014-04-25 08:25 '14 в 8:25 2014-04-25 08:25

Ако използвате API на сесията, функциите PrePersist и PreUpdate няма да функционират според този отговор.

Използвам метода persist () за Hibernate Session в моя код, така че единственият начин да направя тази работа е кодът по-долу и следния блог (също публикуван в отговор).

 @MappedSuperclass public abstract class AbstractTimestampEntity { @Temporal(TemporalType.TIMESTAMP) @Column(name = "created") private Date created=new Date(); @Temporal(TemporalType.TIMESTAMP) @Column(name = "updated") @Version private Date updated; public Date getCreated() { return created; } public void setCreated(Date created) { this.created = created; } public Date getUpdated() { return updated; } public void setUpdated(Date updated) { this.updated = updated; } } 
5
14 окт. отговор, даден vicch 14 окт. 2015-10-14 19:20 '15 в 19:20 2015-10-14 19:20

Само за подобрение: java.util.Calender не се прилага за времеви печати . java.util.Date за известно време, агностични регионални неща, като часови зони. Повечето бази данни съхраняват нещата по този начин (дори ако не изглеждат така, обикновено това е настройката на часовата зона в клиентския софтуер, данните са добри)

2
21 окт. отговорът е даден davetron5000 21 октомври. 2008-10-21 16:02 '08 в 16:02 ч. 2008-10-21 16:02

Добър подход е да имате общ базов клас за всички ваши обекти. В този базов клас можете да имате свойство id, ако обикновено се нарича във всичките ви обекти (цялостен дизайн), вашите свойства за създаване и последната актуализация.

За датата на създаване просто запазвате свойството java.util.Date. Уверете се, че винаги го инициализирате с нова дата ().

В последното поле за актуализация можете да използвате свойството Timestamp, което трябва да се съпостави с @Version. С това пояснение собствеността автоматично се актуализира с хибернация. Не забравяйте, че Hibernate също ще приложи оптимистично заключване (това е добро).

2
21 окт. отговор, даден на bernardn 21 октомври 2008-10-21 15:27 '08 в 15:27 2008-10-21 15:27

Като тип данни в JAVA, силно препоръчвам да използвате java.util.Date. Когато използвах Календар, срещнах доста неприятни проблеми с часовата зона. Виж Тема .

За да зададете времеви печати, бих препоръчал да използвате или подход AOP, или просто да използвате тригерите в таблицата (всъщност това е единственото нещо, което някога съм открил, че използването на тригери е приемливо).

1
21 окт. Отговор, даден huo73 Окт 21 2008-10-21 15:30 '08 в 15:30 часа 2008-10-21 15:30

Може да искате да запишете времето като дата на времето в UTC. Обикновено използвам DateTime вместо Timestamp, защото MySql преобразува датите в UTC и обратно към местното време, когато съхранява и извлича данни. Предпочитам да се придържам към тази логика на едно място (бизнес ниво). Сигурен съм, че има други ситуации, при които се предпочита използването на времевата отметка.

1
21 окт. Дайте отговор mmacaulay 21 октомври 2008-10-21 16:16 '08 в 16:16 2008-10-21 16:16

Решение с функционалност на MySQL: Актуализиране и създаване на времеви отпечатъци с MySQL Работи чудесно с java и hibernate.

0
30 апр. Отговорът е даден от Богдан Гусиев на 30 април. 2009-04-30 10:53 '09 в 10:53 AM 2009-04-30 10:53

Имахме подобна ситуация. Използвахме Mysql 5.7.

 CREATE TABLE my_table ( ... updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); 

Той работи за нас.

0
24 июня '17 в 10:35 2017-06-24 10:35 Отговорът е даден amdg 24 юни '17 в 10:35 часа 2017-06-24 10:35

Други въпроси относно маркерите за или Задайте въпрос