특정 최대 절전 모드 엔터티의 경우 생성 시간과 마지막 업데이트 시간을 저장해야합니다. 이것을 어떻게 설계 하시겠습니까?
-
데이터베이스에서 어떤 데이터 유형을 사용 하시겠습니까 (MySQL, 아마도 다른 시간대의 JVM이라고 가정)? 데이터 유형이 시간대를 인식합니까?
-
당신이 자바에서 어떤 데이터 유형을 사용 (
Date
,Calendar
,long
, …)? -
타임 스탬프 (데이터베이스, ORM 프레임 워크 (Hibernate) 또는 애플리케이션 프로그래머)를 설정하는 담당자는 누구입니까?
-
매핑에 어떤 주석을 사용
@Temporal
하시겠습니까 (예 🙂 ?
나는 작업 솔루션을 찾고있을뿐만 아니라 안전하고 잘 설계된 솔루션을 찾고 있습니다.
답변
JPA 주석을 사용하는 경우 이벤트 후크를 사용 @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
클래스 에서 주석을 사용하고 이벤트 코드를 외부 클래스에 배치 할 수 있습니다 .
답변
당신은 사용할 수 있습니다 @CreationTimestamp
및 @UpdateTimestamp
:
@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "create_date")
private Date createDate;
@UpdateTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "modify_date")
private Date modifyDate;
답변
이 게시물의 리소스를 다른 소스에서 왼쪽과 오른쪽으로 가져온 정보와 함께이 우아한 솔루션과 함께 다음과 같은 추상 클래스를 만들었습니다.
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();
}
}
다음과 같이 모든 엔티티를 확장하십시오.
@Entity
@Table(name = "campaign")
public class Campaign extends AbstractTimestampEntity implements Serializable {
...
}
답변
1. 사용해야 할 데이터베이스 열 유형
첫 번째 질문은
데이터베이스에서 어떤 데이터 유형을 사용 하시겠습니까 (MySQL, 아마도 다른 시간대의 JVM이라고 가정)? 데이터 유형이 시간대를 인식합니까?
MySQL에서 TIMESTAMP
열 유형은 JDBC 드라이버 로컬 시간대에서 데이터베이스 시간대로 이동하지만 최대 시간 소인까지만 타임 스탬프를 저장할 수 있습니다.'2038-01-19 03:14:07.999999
있으므로 향후 최선의 선택은 아닙니다.
따라서이 DATETIME
상한을 갖지 않는 대신 사용 하는 것이 좋습니다 . 그러나 DATETIME
시간대를 인식하지 못합니다. 따라서 데이터베이스 측면에서 UTC를 사용하고 hibernate.jdbc.time_zone
Hibernate 속성을 사용하는 것이 가장 좋습니다 .
2. 어떤 엔티티 속성 유형을 사용해야합니까
두 번째 질문은 다음과 같습니다.
Java (Date, Calendar, long, …)에서 어떤 데이터 유형을 사용 하시겠습니까?
Java 측에서는 Java 8을 사용할 수 있습니다 LocalDateTime
. 레거시를 사용할 수도 Date
있지만 Java 8 날짜 / 시간 유형은 변경 불가능하므로 더 좋으며 로깅 할 때 시간대를 로컬 시간대로 이동하지 않습니다.
Hibernate가 지원하는 Java 8 날짜 / 시간 유형에 대한 자세한 내용은 이 기사를 확인 하십시오 .
이제이 질문에 대답 할 수도 있습니다.
매핑에 어떤 주석을 사용
@Temporal
하시겠습니까 (예 🙂 ?
당신은을 사용하는 경우 LocalDateTime
또는 java.sql.Timestamp
타임 스탬프 개체 속성을 매핑, 당신은 사용할 필요가 없습니다@Temporal
Hibernate는 이미이 속성은 JDBC 타임 스탬프로 저장하는 것을 알고 있기 때문이다.
을 사용하는 경우에만 다음 과 같이 주석 java.util.Date
을 지정해야합니다 @Temporal
.
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_on")
private Date createdOn;
그러나 다음과 같이 매핑하면 훨씬 좋습니다.
@Column(name = "created_on")
private LocalDateTime createdOn;
감사 열 값을 생성하는 방법
세 번째 질문은 다음과 같습니다.
타임 스탬프 (데이터베이스, ORM 프레임 워크 (Hibernate) 또는 애플리케이션 프로그래머)를 설정하는 담당자는 누구입니까?
매핑에 어떤 주석을 사용 하시겠습니까 (예 : @Temporal)?
이 목표를 달성 할 수있는 방법은 여러 가지가 있습니다. 데이터베이스가 그렇게하도록 허용 할 수 있습니다.
를 들어 create_on
열, 당신은 사용할 수 DEFAULT
, DDL 제약을 같이 :
ALTER TABLE post
ADD CONSTRAINT created_on_default
DEFAULT CURRENT_TIMESTAMP() FOR created_on;
를 들어 updated_on
열, 당신과 함께 열 값을 설정하는 DB 트리거를 사용할 수 있습니다CURRENT_TIMESTAMP()
주어진 행이 수정 될 때마다.
또는 JPA 또는 Hibernate를 사용하여 설정하십시오.
다음과 같은 데이터베이스 테이블이 있다고 가정합니다.
그리고 각 테이블에는 다음과 같은 열이 있습니다.
created_by
created_on
updated_by
updated_on
최대 절전 모드 @CreationTimestamp
및 @UpdateTimestamp
주석 사용
최대 절전 모드는 및 열 을 매핑하는 데 사용할 수있는 @CreationTimestamp
및 @UpdateTimestamp
주석을 제공합니다 .created_on
updated_on
@MappedSuperclass
모든 엔티티에 의해 확장 될 기본 클래스를 정의하는 데 사용할 수 있습니다 .
@MappedSuperclass
public class BaseEntity {
@Id
@GeneratedValue
private Long id;
@Column(name = "created_on")
@CreationTimestamp
private LocalDateTime createdOn;
@Column(name = "created_by")
private String createdBy;
@Column(name = "updated_on")
@UpdateTimestamp
private LocalDateTime updatedOn;
@Column(name = "updated_by")
private String updatedBy;
//Getters and setters omitted for brevity
}
그리고 모든 엔티티는 다음 BaseEntity
과 같이 확장됩니다 .
@Entity(name = "Post")
@Table(name = "post")
public class Post extend BaseEntity {
private String title;
@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
@OneToOne(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY
)
private PostDetails details;
@ManyToMany
@JoinTable(
name = "post_tag",
joinColumns = @JoinColumn(
name = "post_id"
),
inverseJoinColumns = @JoinColumn(
name = "tag_id"
)
)
private List<Tag> tags = new ArrayList<>();
//Getters and setters omitted for brevity
}
그러나,해도 createdOn
와 updateOn
속성이 최대 절전 모드 전용으로 설정 @CreationTimestamp
하고 @UpdateTimestamp
주석의 createdBy
및updatedBy
다음 JPA 용액에 의해 도시 된 바와 같이, 애플리케이션 콜백 등록을 필요로한다.
JPA 사용 @EntityListeners
감사 속성을 Embeddable에 캡슐화 할 수 있습니다.
@Embeddable
public class Audit {
@Column(name = "created_on")
private LocalDateTime createdOn;
@Column(name = "created_by")
private String createdBy;
@Column(name = "updated_on")
private LocalDateTime updatedOn;
@Column(name = "updated_by")
private String updatedBy;
//Getters and setters omitted for brevity
}
AuditListener
감사 속성을 설정하려면을 만듭니다 .
public class AuditListener {
@PrePersist
public void setCreatedOn(Auditable auditable) {
Audit audit = auditable.getAudit();
if(audit == null) {
audit = new Audit();
auditable.setAudit(audit);
}
audit.setCreatedOn(LocalDateTime.now());
audit.setCreatedBy(LoggedUser.get());
}
@PreUpdate
public void setUpdatedOn(Auditable auditable) {
Audit audit = auditable.getAudit();
audit.setUpdatedOn(LocalDateTime.now());
audit.setUpdatedBy(LoggedUser.get());
}
}
를 등록 AuditListener
하기 위해 @EntityListeners
JPA 주석을 사용할 수 있습니다 .
@Entity(name = "Post")
@Table(name = "post")
@EntityListeners(AuditListener.class)
public class Post implements Auditable {
@Id
private Long id;
@Embedded
private Audit audit;
private String title;
@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
@OneToOne(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY
)
private PostDetails details;
@ManyToMany
@JoinTable(
name = "post_tag",
joinColumns = @JoinColumn(
name = "post_id"
),
inverseJoinColumns = @JoinColumn(
name = "tag_id"
)
)
private List<Tag> tags = new ArrayList<>();
//Getters and setters omitted for brevity
}
JPA로 감사 속성을 구현하는 방법에 대한 자세한 내용은 이 문서를
@EntityListener
확인 하십시오 .
답변
인터셉터를 사용하여 값을 설정할 수도 있습니다
엔티티가 구현하는 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;
}
}
그리고 세션 팩토리에 등록하십시오
답변
Olivier의 솔루션을 사용하면 업데이트 문 중에 다음과 같은 문제가 발생할 수 있습니다.
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException : ‘created’열은 null 일 수 없습니다.
이 문제를 해결하려면 “작성된”속성의 @Column 주석에 updatable = false를 추가하십시오.
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created", nullable = false, updatable=false)
private Date created;
답변
도와 주신 모든 분들께 감사드립니다. 나 자신에게 약간의 연구를 한 후에 (나는 질문을 한 사람입니다), 여기 내가 가장 이해하는 것이 있습니다.
-
데이터베이스 열 유형 : 1970 년 이후 시간대에 관계없이 밀리 초 (
decimal(20)
2 초) 는 2 자리 64 개에 20 자리가 있고 디스크 공간이 저렴하기 때문에 나타납니다 . 솔직 해지자 또한,DEFAULT CURRENT_TIMESTAMP
트리거도 사용하지 않습니다. 나는 DB에서 마술을 원하지 않는다. -
Java 필드 유형 :
long
. Unix 타임 스탬프는 다양한 라이브러리에서 잘 지원되며long
Y2038 문제가 없으며 타임 스탬프 산술이 빠르고 쉽습니다 (주로 연산자<
와 연산자+
는 일 / 월 / 년이 계산에 관여하지 않는다고 가정). 그리고 가장 중요한 것은, 프리미티브long
와java.lang.Long
s 모두 s와 달리 불변 이며 (가치에 의해 효과적으로 전달됨)java.util.Date
;foo.getLastUpdate().setTime(System.currentTimeMillis())
다른 사람의 코드를 디버깅 할 때 와 같은 것을 찾기 위해 정말로 화가 났을 것 입니다. -
ORM 프레임 워크는 데이터를 자동으로 채울 책임이 있습니다.
-
나는 이것을 아직 테스트하지는 않았지만 내가 할 것이라고 생각하는 문서 만보 고있다
@Temporal
.@Version
이 목적으로 사용할 수 있는지 확실하지 않습니다 .@PrePersist
및@PreUpdate
수동으로 제어하는 좋은 대안이다. 모든 엔티티에 대한 레이어 슈퍼 타입 (공통 기본 클래스)에 그 추가, 당신이 정말로 위해 타임 스탬프를 원하는 귀여운 아이디어가 제공되며 모든 당신의 엔티티.