当前位置:网站首页>Auditing相关注解
Auditing相关注解
2022-07-25 17:50:00 【InfoQ】
Auditing 指的是什么?
- @CreatedBy 是哪个用户创建的。
- @CreatedDate 创建的时间。
- @LastModifiedBy 最后修改实体的用户。
- @LastModifiedDate 最后一次修改的时间。
Auditing 如何实现?
第一种方式:直接在实例里面添加上述四个注解
@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString(exclude = "addresses")
@EntityListeners(AuditingEntityListener.class)
public class User implements Serializable {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
private String name;
private String email;
@Enumerated(EnumType.STRING)
private SexEnum sex;
private Integer age;
@OneToMany(mappedBy = "user")
@JsonIgnore
private List<UserAddress> addresses;
private Boolean deleted;
@CreatedBy
private Integer createUserId;
@CreatedDate
private Date createTime;
@LastModifiedBy
private Integer lastModifiedUserId;
@LastModifiedDate
private Date lastModifiedTime;
}
@CreatedBy
private Integer createUserId;
@CreatedDate
private Date createTime;
@LastModifiedBy
private Integer lastModifiedUserId;
@LastModifiedDate
private Date lastModifiedTime;
@EntityListeners(AuditingEntityListener.class)
public class MyAuditorAware implements AuditorAware<Integer> {
//需要实现AuditorAware接口,返回当前的用户ID
@Override
public Optional<Integer> getCurrentAuditor() {
ServletRequestAttributes servletRequestAttributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
Integer userId = (Integer) servletRequestAttributes.getRequest().getSession().getAttribute("userId");
return Optional.ofNullable(userId);
}
}
public interface AuditorAware<T> {
T getCurrentAuditor();
}
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return null;
}
Integer userId = ((LoginUserInfo) authentication.getPrincipal()).getUser().getId();
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(JpaAuditingRegistrar.class)
public @interface EnableJpaAuditing {
//auditor用户的获取方法,默认是找AuditorAware的实现类;
String auditorAwareRef() default "";
//是否在创建修改的时候设置时间,默认是true
boolean setDates() default true;
//在创建的时候是否同时作为修改,默认是true
boolean modifyOnCreate() default true;
//时间的生成方法,默认是取当前时间(为什么提供这个功能呢?因为测试的时候有可能希望时间保持不变,它提供了一种自定义的方法);
String dateTimeProviderRef() default "";
}
@Configuration
@EnableJpaAuditing
public class JpaConfiguration {
@Bean
@ConditionalOnMissingBean(name = "myAuditorAware")
MyAuditorAware myAuditorAware() {
return new MyAuditorAware();
}
}
- 这里说一个 Congifuration 的最佳实践的写法。我们为什么要单独写一个JpaConfiguration的配置文件,而不是把@EnableJpaAuditing 放在 JpaApplication 的类里面呢?因为这样的话 JpaConfiguration 文件可以单独加载、单独测试,如果都放在 Appplication 类里面的话,岂不是每次测试都要启动整个应用吗?
- MyAuditorAware 也可以通过 @Component 注解进行加载,我为什么推荐 @Bean 的方式呢?因为这种方式可以让使用的人直接通过我们的配置文件知道我们自定义了哪些组件,不会让用的人产生不必要的惊讶,这是一点写 framework 的经验,供你参考。
@DataJpaTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Import(JpaConfiguration.class)
public class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
@MockBean
MyAuditorAware myAuditorAware;
@Test
public void testAuditing() {
//由于测试用例模拟web context环境不是我们的重点,我们这里利用@MockBean,mock掉我们的方法,期待返回13这个用户ID
Mockito.when(myAuditorAware.getCurrentAuditor()).thenReturn(Optional.of(13));
//我们没有显式的指定更新时间、创建时间、更新人、创建人
User user = User.builder()
.name("jack")
.email("[email protected]")
.sex(SexEnum.BOY)
.age(20)
.build();
userRepository.save(user);
//验证是否有创建时间、更新时间,UserID是否正确;
List<User> users = userRepository.findAll();
Assertions.assertEquals(13,users.get(0).getCreateUserId());
Assertions.assertNotNull(users.get(0).getLastModifiedTime());
System.out.println(users.get(0));
}
}
- 我们利用 @MockBean 模拟 MyAuditorAware 返回结果 13 这个 UserID;
- 我们测试并验证 create_user_id 是否是我们预期的。
User(id=1, name=jack, [email protected], sex=BOY, age=20, deleted=null, createUserId=13, createTime=Sat Oct 03 21:19:57 CST 2020, lastModifiedUserId=13, lastModifiedTime=Sat Oct 03 21:19:57 CST 2020)
第二种方式:实体里面实现Auditable 接口
@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString(exclude = "addresses")
@EntityListeners(AuditingEntityListener.class)
public class User implements Auditable<Integer,Long, Instant> {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
private String name;
private String email;
@Enumerated(EnumType.STRING)
private SexEnum sex;
private Integer age;
@OneToMany(mappedBy = "user")
@JsonIgnore
private List<UserAddress> addresses;
private Boolean deleted;
private Integer createUserId;
private Instant createTime;
private Integer lastModifiedUserId;
private Instant lastModifiedTime;
@Override
public Optional<Integer> getCreatedBy() {
return Optional.ofNullable(this.createUserId);
}
@Override
public void setCreatedBy(Integer createdBy) {
this.createUserId = createdBy;
}
@Override
public Optional<Instant> getCreatedDate() {
return Optional.ofNullable(this.createTime);
}
@Override
public void setCreatedDate(Instant creationDate) {
this.createTime = creationDate;
}
@Override
public Optional<Integer> getLastModifiedBy() {
return Optional.ofNullable(this.lastModifiedUserId);
}
@Override
public void setLastModifiedBy(Integer lastModifiedBy) {
this.lastModifiedUserId = lastModifiedBy;
}
@Override
public void setLastModifiedDate(Instant lastModifiedDate) {
this.lastModifiedTime = lastModifiedDate;
}
@Override
public Optional<Instant> getLastModifiedDate() {
return Optional.ofNullable(this.lastModifiedTime);
}
@Override
public boolean isNew() {
return id==null;
}
}
第三种方式:利用 @MappedSuperclass 注解

package com.example.jpa.example1.base;
import org.springframework.data.annotation.*;
import javax.persistence.MappedSuperclass;
import java.time.Instant;
@Data
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity {
@CreatedBy
private Integer createUserId;
@CreatedDate
private Instant createTime;
@LastModifiedBy
private Integer lastModifiedUserId;
@LastModifiedDate
private Instant lastModifiedTime;
}
@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString(exclude = "addresses")
public class User extends BaseEntity {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
private String name;
private String email;
@Enumerated(EnumType.STRING)
private SexEnum sex;
private Integer age;
@OneToMany(mappedBy = "user")
@JsonIgnore
private List<UserAddress> addresses;
private Boolean deleted;
}
- 去掉了 @EntityListeners(AuditingEntityListener.class);
- 去掉了 @CreatedBy、@CreatedDate、@LastModifiedBy、@LastModifiedDate 四个注解的公共字段。
边栏推荐
- Idea essential plug-ins
- Redis source code and design analysis -- 18. Analysis of redis network connection Library
- How to rectify the unqualified EMC of electronic products?
- Memory and packet buffer management of LwIP
- Hit the test site directly: summary of common agile knowledge points in PMP examination
- 计算日期或日期格式化
- Brief introduction of bubble sort and quick sort
- PHP解决并发问题的几种实现
- Go language context control function execution timeout return
- RestTemplate通过泛型实现POST、PUT、DELETE、GET、集合请求以及文件上传(可批量文件、可带参数)的统一封装(可打印日志)
猜你喜欢

Food safety | eight questions and eight answers take you to know crayfish again! This is the right way to eat!

11、照相机与透镜

Take you to a preliminary understanding of multiparty secure computing (MPC)

Memory and packet buffer management of LwIP

带你初步了解多方安全计算(MPC)

交友活动记录

Nineteen year old summary

关于云XR介绍,以及5G时代云化XR的发展机遇

Redis source code and design analysis -- 15. RDB persistence mechanism

OSPF comprehensive experiment
随机推荐
WPF implements user avatar selector
自动化测试 PO设计模型
Go defer and recover simple notes
go channel简单笔记
Go language context control function execution timeout return
【解决方案】Microsoft Edge 浏览器 出现“无法访问该页面”问题
Mock服务moco系列(三)- 重定向、正则表达式、延迟、模板、事件、分模块设计
[Hardware Engineer] can't select components?
Mock service Moco series (I) - introduction, first demo, get request, post request
Itextpdf realizes the merging of multiple PDF files into one PDF document
如何选择数字孪生可视化平台
I'm also drunk. Eureka delayed registration and this pit!
Memory and packet buffer management of LwIP
【硬件工程师】元器件选型都不会?
2022/7/23
Dating activity records
Brief introduction of bubble sort and quick sort
【硬件工程师】DC-DC隔离式开关电源模块为什么会用到变压器?
go接口变量的类型断言
Postman快速上手