1、Auditing

  一般我们针对一张表的操作需要记录下来,是谁修改的,修改时间是什么,Spring-Data为我们提供了支持。

  1.1、在实体类中使用Spring-Data为我们提供的四个注解(也可以选择实现Auditable接口或继承AbstractAuditable类,推荐使用注解)

  1.2、在实体上添加@EntityListeners(value = AuditingEntityListener.class)启动对当前实体的监听。

/**
* 测试spring-data为我们提供的审计功能
*
* @author caofanqi
*/
@Data
@Entity
@Builder
@Table(name = "jpa_audit_user")
@NoArgsConstructor
@AllArgsConstructor
@EntityListeners(value = AuditingEntityListener.class)
public class AuditUser { @Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; @Column(unique = true)
private String name; @CreatedDate
private LocalDateTime createdDate; @LastModifiedDate
private LocalDateTime lastModifiedDate; @CreatedBy
@ManyToOne
private AuditUser createdBy; @LastModifiedBy
@ManyToOne
private AuditUser lastModifiedBy;
}

  1.3、如果在实体中使用了@CreatedBy或者@LastModifiedBy需要实现AuditorAware<T>接口,告诉Spring-Data当前审计用户是谁。(一般项目中从spring security或token中获取)

/**
* 获取当前的审计人,实际项目中可以从Spring Security中或Token/{session}中获取,这里只是举个例子进行模拟。
* @author caofanqi
*/
public class AuditorAwareImpl implements AuditorAware<AuditUser> { private Optional<AuditUser> currentUser = Optional.empty(); public void setCurrentUser(AuditUser currentUser){
this.currentUser = Optional.of(currentUser);
} @Override
public Optional<AuditUser> getCurrentAuditor() {
//要使用的当前用户
return currentUser;
} }

  1.4、在启动类上添加@EnableJpaAuditing启动审计功能。

  1.5、如果ApplicationContext中只有一个AuditorAware类型的bean,Spring-Date会自动选择,如果又多个,需要通过@EnableJpaAuditing注解的auditorAwareRef属性进行设置。

/**
* 启动类
* @author caofanqi
*/
@SpringBootApplication
@EnableAsync
@EnableJpaRepositories(
/*queryLookupStrategy = QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND*/
/* ,repositoryImplementationPostfix = "MyPostfix",*/
/*repositoryBaseClass = MyRepositoryImpl.class*/)
@EnableJpaAuditing
public class StudySpringDataJpaApplication { public static void main(String[] args) {
SpringApplication.run(StudySpringDataJpaApplication.class, args);
} /**
* 如果ApplicationContext中只有一个AuditorAware类型的bean,Spring-Date会自动选择,
* 如果又多个,需要通过@EnableJpaAuditing注解的auditorAwareRef属性进行设置。
*/
@Bean
public AuditorAware<AuditUser> auditorProvider() {
return new AuditorAwareImpl();
} }

  1.6、测试用例,及生成的表

@SpringBootTest
class AuditUserRepositoryTest { @Resource
private AuditUserRepository auditUserRepository; @Resource
private AuditorAwareImpl auditorAware; @Test
void testAuditDate(){
/*
*不设置创建和修改时间,由springl-data替我们完成
*/
AuditUser audit = AuditUser.builder().name("张三").build();
AuditUser save = auditUserRepository.save(audit);
System.out.println(save);
} @Test
void testAuditUser(){ /*
* 模拟当前用户
*/
auditorAware.setCurrentUser(auditUserRepository.findByName("张三")); /*
* 这里不设置是谁保存的,看spring-data是否会为我们完成
*/
AuditUser audit = AuditUser.builder().name("李四").build(); AuditUser save = auditUserRepository.save(audit);
System.out.println(save);
} }

  testAuditDate控制台打印:

  

  testAuditUser控制台打印:

  

  数据库表:

  

2、@MappedSuperclass

  指定其映射信息应用于从其继承的实体的类。映射的超类没有为其定义单独的表。与MappedSuperclass注释指定的类可以以与实体相同的方式映射,除了映射仅适用于它的子类之外,因为映射超类本身不存在表。当应用于子类时,继承的映射将应用于子类表的上下文中。(说白了,就是将各实体中相同的属性提取到一个添加该注解的父类中,父类不会生成对应的表,但是各子实体类生成的对应表不变。)

  这样我们就可以将通用的ID和Auditing相关的属性提取出来。

  2.1、id抽象类

/**
* 抽象id父类
*
* @author caofanqi
*/
@Getter
@Setter
@ToString
@MappedSuperclass
public abstract class AbstractID { @Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; }

  2.2、审计功能抽象类

/**
* 审计功能抽象类
* @author caofnqi
*/
@Getter
@Setter
@ToString(callSuper = true)
@MappedSuperclass
@EntityListeners(value = AuditingEntityListener.class)
public abstract class AbstractAuditDomain extends AbstractID { @CreatedDate
private LocalDateTime createdDate; @LastModifiedDate
private LocalDateTime lastModifiedDate; @CreatedBy
@Column(name = "create_by_user_id")
private Long createdByUserId; @LastModifiedBy
@Column(name = "last_modified_by_user_id")
private Long lastModifiedUserBy; }

  2.3、实体类,可以根据是否需要用到选择继承id抽象类,还是审计抽象类

/**
* @author caofanqi
*/
@Getter
@Setter
@Entity
@Builder
@Table(name = "jpa_audit_person")
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
public class AuditPerson extends AbstractAuditDomain { private String personName; }

  2.4、修改对应的AuditorAware实现,并指定auditorAwareRef

/**
* AuditorAware实现示例,根据自己业务进行实现
* @author caofanqi
*/
public class IdAuditorAwareImpl implements AuditorAware<Long> { private Optional<AuditUser> currentUser = Optional.empty(); public void setCurrentUser(AuditUser currentUser){
this.currentUser = Optional.of(currentUser);
} @Override
public Optional<Long> getCurrentAuditor() {
return currentUser.map(AuditUser::getId);
} }

  

  

  测试类似上面,这里就不贴了。

3、自定义实体监听

  Auditing是通过JPA提供的@EntityListeners和@PrePersist、@PreUpdate来完成的。

  @EntityListeners,指定要用于实体或映射超类的回调侦听器类。此注释可以应用于实体类或映射的超类。
    属性:value,回调侦听器类。
  以下注解为相应的生命周期事件指定回调方法。此注释可以应用于实体类、映射超类或回调侦听器类的方法。都是同步机制使用时要注意,可以在使用时,可以在方法中开启异步线程或消息队列。
  @PrePersist,新增之前;@PostPersist,新增之后。
  @PreUpdate,更新之前;@PostUpdate,更新之后。
  @PreRemove,删除之前;@PostRemove,删除之后。
  @PostLoad,加载之后。

  我们以订单为例:

/**
*
* @author caofanqi
*/
@Slf4j
@Getter
@Setter
@Entity
@Builder
@Table(name = "jpa_order")
@NoArgsConstructor
@AllArgsConstructor
@EntityListeners(value = OrderEntityListener.class)
public class Order extends AbstractAuditDomain{ @Column(unique = true)
private String orderNo; @Column(nullable = false)
private OrderStatus orderStatus; @Column(nullable = false)
private BigDecimal price; //其他属性.... /*
* 以下方法也可以写在监听类中
*/ // @PrePersist
// public void prePersist(){
// this.setOrderStatus(OrderStatus.NEW);
// log.info("orderNo: {},status :{},新增之前修改订单状态为NEW",this.getOrderNo(),this.getOrderStatus());
// }
//
// @PostPersist
// public void postPersist(){
// log.info("orderNo: {},status :{},新增之后,异步通知仓库进行处理",this.getOrderNo(),this.getOrderStatus());
// }
//
// @PostLoad
// public void postLoad(){
// log.info("orderNo: {},status :{},加载之后...",this.getOrderNo(),this.getOrderStatus());
// }
//
// @PreUpdate
// public void preUpdate(){
// log.info("orderNo: {},status :{},修改之前.....",this.getOrderNo(),this.getOrderStatus());
// }
//
// @PostUpdate
// public void postUpdate(){
// log.info("orderNo: {},status :{},修改之后根据订单状态进行不同的判断",this.getOrderNo(),this.getOrderStatus());
// }
//
// @PreRemove
// public void preRemove(){
// log.info("orderNo: {},status :{},删除之前.....",this.getOrderNo(),this.getOrderStatus());
// }
//
//
// @PostRemove
// public void postRemove(){
// log.info("orderNo: {},status :{},删除之后.....",this.getOrderNo(),this.getOrderStatus());
// } }
/**
* 订单实体监听类
* @author caofanqi
*/
@Slf4j
public class OrderEntityListener { @PrePersist
public void prePersist(Order order){
order.setOrderStatus(OrderStatus.NEW);
log.info("orderNo: {},status :{},新增之前修改订单状态为NEW",order.getOrderNo(),order.getOrderStatus());
} @PostPersist
public void postPersist(Order order){
log.info("orderNo: {},status :{},新增之后,异步通知厂库进行处理",order.getOrderNo(),order.getOrderStatus());
} @PostLoad
public void postLoad(Order order){
log.info("orderNo: {},status :{},加载之后...",order.getOrderNo(),order.getOrderStatus());
} @PreUpdate
public void preUpdate(Order order){
log.info("orderNo: {},status :{},修改之前.....",order.getOrderNo(),order.getOrderStatus());
} @PostUpdate
public void postUpdate(Order order){
log.info("orderNo: {},status :{},修改之后根据订单状态进行不同的判断",order.getOrderNo(),order.getOrderStatus());
} @PreRemove
public void preRemove(Order order){
log.info("orderNo: {},status :{},删除之前.....",order.getOrderNo(),order.getOrderStatus());
} @PostRemove
public void postRemove(Order order){
log.info("orderNo: {},status :{},删除之后.....",order.getOrderNo(),order.getOrderStatus());
} }

  测试新增:

  

  测试查询和修改(图中红框中的为jpa save方法更新前自己运行的查询):

  

  测试查询和删除(图中红框中的为jpa delete方法更新前自己运行的查询):

   

源码地址:https://github.com/caofanqi/study-spring-data-jpa

学习Spring-Data-Jpa(十五)---Auditing与@MappedSuperclass的更多相关文章

  1. 学习Spring Data JPA

    简介 Spring Data 是spring的一个子项目,在官网上是这样解释的: Spring Data 是为数据访问提供一种熟悉且一致的基于Spring的编程模型,同时仍然保留底层数据存储的特​​殊 ...

  2. 学习-spring data jpa

    spring data jpa对照表 Keyword Sample JPQL snippet And findByLastnameAndFirstname - where x.lastname = ? ...

  3. 【Spring Data 系列学习】了解 Spring Data JPA 、 Jpa 和 Hibernate

    在开始学习 Spring Data JPA 之前,首先讨论下 Spring Data Jpa.JPA 和 Hibernate 之前的关系. JPA JPA 是 Java Persistence API ...

  4. Spring Data Jpa 入门学习

    本文主要讲解 springData Jpa 入门相关知识, 了解JPA规范与Jpa的实现,搭建springboot+dpringdata jpa环境实现基础增删改操作,适合新手学习,老鸟绕道~ 1. ...

  5. spring boot(五):spring data jpa的使用

    在上篇文章springboot(二):web综合开发中简单介绍了一下spring data jpa的基础性使用,这篇文章将更加全面的介绍spring data jpa 常见用法以及注意事项 使用spr ...

  6. 一步步学习 Spring Data 系列之JPA(二)

    继上一篇文章对Spring Data JPA更深( )一步剖析. 上一篇只是简单的介绍了Spring Data JPA的简单使用,而往往在项目中这一点功能并不能满足我们的需求.这是当然的,在业务中查询 ...

  7. Spring Data JPA 学习记录1 -- 单向1:N关联的一些问题

    开新坑 开新坑了(笑)....公司项目使用的是Spring Data JPA做持久化框架....学习了一段时间以后发现了一点值得注意的小问题.....与大家分享 主要是针对1:N单向关联产生的一系列问 ...

  8. spring data jpa入门学习

    本文主要介绍下spring data jpa,主要聊聊为何要使用它进行开发以及它的基本使用.本文主要是入门介绍,并在最后会留下完整的demo供读者进行下载,从而了解并且开始使用spring data ...

  9. 展开被 SpringBoot 玩的日子 《 五 》 spring data jpa 的使用

    在上篇文章< 展开被 SpringBoot 玩的日子 < 二 >WEB >中简单介绍了一下spring data jpa的基础性使用,这篇文章将更加全面的介绍spring da ...

随机推荐

  1. [终极巨坑]golang+vue开发日记【二】,登陆界面制作(一)

    写在前面 本期内容是适合第一次使用vue或者golang开发的,内容会以实战的形式来讲解.看懂本段内容需要了解基础内容有html,css,最好可以看一下vue的基础.并且这里的每个知识点不可能详细解说 ...

  2. SQL Server 2012启动时提示:无效的许可证数据,需要重新安装

    因为手咸,觉得电脑没有VS 2010版本的软件,就把Microsoft Visual C++ 2010某个组件给卸载了. 然后打开Sql Server 2012,就开始报错. 重装之后,也还是报错,将 ...

  3. C# vb .net实现负片特效滤镜

    在.net中,如何简单快捷地实现Photoshop滤镜组中的负片特效呢?答案是调用SharpImage!专业图像特效滤镜和合成类库.下面开始演示关键代码,您也可以在文末下载全部源码: 设置授权 第一步 ...

  4. 使用KONG网关实现接口迁移的灰度验证

    在我们对一个API站点进行微服务化的过程中,使用KONG网关可以实现以下几个效果: 1. 业务线无感知,其实内部已经被Kong转到其他站点上执行了,这对业务线特别友好. 2. 可以实现租户级/接口级灰 ...

  5. 2019 波克城市ava面试笔试题 (含面试题解析)

    本人3年开发经验.18年年底开始跑路找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.波克城市等公司offer,岗位是Java后端开发,最终选择去了波克城市. 面试了很多家公司,感觉大部分公司考察的点 ...

  6. Java实现树的遍历以及打印(递归,非递归)

    import java.util.LinkedList; import java.util.Stack; public class BinarySearchTree1<E extends Com ...

  7. LocalStorageUtils

    对localStorage进行封装: var LocalStorageUtils = new function (){ if(window.localStorage==null){ throw new ...

  8. Python的csv文件(csv模块)和ini文件(configparser模块)处理

    Python的csv文本文件(csv模块)和ini文本文件(configparser模块)处理 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.csv文件 1>.CSV文件 ...

  9. Linux CentOs下安装lnmp

    1.下载源码包 以root目录为例: cd ~ # 下载安装包 wget http://nginx.org/download/nginx-1.17.2.tar.gz # nginx wget http ...

  10. 【实战3】记一次内网中反弹shell的艰难历程

    # 0x00 前言 最近在客户现场对内网服务器进行渗透测试,发现了大量的弱口令,本次历程就是从这里开始··· # 0x01 弱口令 对目标ip进行端口扫描,开放端口为80,445,1433,3389- ...