对于某个Hibernate实体,我们需要存储它的创建时间和最后一次更新时间。你会怎么设计呢?
您将在数据库中使用什么数据类型(假设MySQL,可能位于与JVM不同的时区)?数据类型是否支持时区? 你会在Java中使用什么数据类型(日期,日历,长,…)? 您会让谁负责设置时间戳——数据库、ORM框架(Hibernate)还是应用程序程序员? 你会为映射使用什么注释(例如@Temporal)?
我不仅在寻找一个可行的解决方案,而且在寻找一个安全、设计良好的解决方案。
对于某个Hibernate实体,我们需要存储它的创建时间和最后一次更新时间。你会怎么设计呢?
您将在数据库中使用什么数据类型(假设MySQL,可能位于与JVM不同的时区)?数据类型是否支持时区? 你会在Java中使用什么数据类型(日期,日历,长,…)? 您会让谁负责设置时间戳——数据库、ORM框架(Hibernate)还是应用程序程序员? 你会为映射使用什么注释(例如@Temporal)?
我不仅在寻找一个可行的解决方案,而且在寻找一个安全、设计良好的解决方案。
当前回答
对于那些想要使用JPA和Spring Data创建或修改用户详细信息以及时间的人,可以遵循这一点。你可以在base域中添加@CreatedDate,@LastModifiedDate,@CreatedBy和@LastModifiedBy。用@MappedSuperclass和@EntityListeners(AuditingEntityListener.class)标记基域,如下所示:
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseDomain implements Serializable {
@CreatedDate
private Date createdOn;
@LastModifiedDate
private Date modifiedOn;
@CreatedBy
private String createdBy;
@LastModifiedBy
private String modifiedBy;
}
因为我们用AuditingEntityListener标记了基本域,所以我们可以告诉JPA当前登录的用户。因此,我们需要提供AuditorAware的实现并重写getCurrentAuditor()方法。在getCurrentAuditor()内部,我们需要返回当前授权的用户Id。
public class AuditorAwareImpl implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication == null ? Optional.empty() : Optional.ofNullable(authentication.getName());
}
}
在上面的代码中,如果Optional不能工作,你可以使用Java 7或更老的版本。在这种情况下,尝试用String改变Optional。
现在使用下面的代码来启用上述审计器实现
@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
public class JpaConfig {
@Bean
public AuditorAware<String> auditorAware() {
return new AuditorAwareImpl();
}
}
现在,您可以将BaseDomain类扩展到您需要创建和修改日期和时间以及用户Id的所有实体类
其他回答
下面的代码对我有用。
package com.my.backend.models;
import java.util.Date;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import lombok.Getter;
import lombok.Setter;
@MappedSuperclass
@Getter @Setter
public class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected Integer id;
@CreationTimestamp
@ColumnDefault("CURRENT_TIMESTAMP")
protected Date createdAt;
@UpdateTimestamp
@ColumnDefault("CURRENT_TIMESTAMP")
protected Date updatedAt;
}
如果你正在使用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注释,并将事件代码放在外部类中。
对于那些想要使用JPA和Spring Data创建或修改用户详细信息以及时间的人,可以遵循这一点。你可以在base域中添加@CreatedDate,@LastModifiedDate,@CreatedBy和@LastModifiedBy。用@MappedSuperclass和@EntityListeners(AuditingEntityListener.class)标记基域,如下所示:
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseDomain implements Serializable {
@CreatedDate
private Date createdOn;
@LastModifiedDate
private Date modifiedOn;
@CreatedBy
private String createdBy;
@LastModifiedBy
private String modifiedBy;
}
因为我们用AuditingEntityListener标记了基本域,所以我们可以告诉JPA当前登录的用户。因此,我们需要提供AuditorAware的实现并重写getCurrentAuditor()方法。在getCurrentAuditor()内部,我们需要返回当前授权的用户Id。
public class AuditorAwareImpl implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication == null ? Optional.empty() : Optional.ofNullable(authentication.getName());
}
}
在上面的代码中,如果Optional不能工作,你可以使用Java 7或更老的版本。在这种情况下,尝试用String改变Optional。
现在使用下面的代码来启用上述审计器实现
@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
public class JpaConfig {
@Bean
public AuditorAware<String> auditorAware() {
return new AuditorAwareImpl();
}
}
现在,您可以将BaseDomain类扩展到您需要创建和修改日期和时间以及用户Id的所有实体类
如果我们在方法中使用@Transactional, @CreationTimestamp和@UpdateTimestamp将值保存在DB中,但在使用save(…)后将返回null。
在这种情况下,使用saveAndFlush(…)达到了目的
利用本文中的资源以及来自不同来源的左右信息,我提出了这个优雅的解决方案,创建以下抽象类
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 {
...
}