Spring Data JPA教程:审计(二)
来源:互联网 发布:化学物质索引数据库 编辑:程序博客网 时间:2024/05/22 03:26
书接上文
本文解决前面两个问题中的第二个问题,我们将为实体加上创建者和修改者的信息
首先创建一个返回授权用户信息的组件
获取授权用户信息
Spring Data JPA使用AuditorAware<T>
接口获取用户信息,AuditorAware
接口的泛型参数T描述了实体类中审计人的类型
现在开始创建一个返回用户信息的类:
- 创建
UsernameAuditorAware
类实现AuditorAware
接口,我们想存储String
类型的用户名,所以参数T设置为String
- 实现
getCurrentAuditor()
方法:- 从
SecurityContext
获取Authentication
对象 - 如果得到的授权对象为
null
或者未经认证,返回null
- 返回username
- 从
UsernameAuditorAware
类源码如下:
import org.springframework.data.domain.AuditorAware;import org.springframework.security.core.Authentication;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.security.core.userdetails.User;public class UsernameAuditorAware implements AuditorAware<String> { @Override public String getCurrentAuditor() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null || !authentication.isAuthenticated()) { return null; } return ((User) authentication.getPrincipal()).getUsername(); }}
配置Application Context
下面我们将UsernameAuditorAware
配置成一个bean,修改PersistenceContext
类,步骤如下:
- 创建
auditorProvider()
方法返回一个AuditorAware<String>
对象 - 方法实现里new一个
UsernameAuditorAware
对象返回 - 给方法加上
@Bean
注解 - 加上
@EnableJpaAuditing
注解
PersistenceContext
类代码如下:
import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.auditing.DateTimeProvider;import org.springframework.data.domain.AuditorAware;import org.springframework.data.jpa.repository.config.EnableJpaAuditing;import org.springframework.data.jpa.repository.config.EnableJpaRepositories;import org.springframework.transaction.annotation.EnableTransactionManagement;@Configuration@EnableJpaAuditing(dateTimeProviderRef = "dateTimeProvider")@EnableJpaRepositories(basePackages = { "net.petrikainulainen.springdata.jpa.todo"})@EnableTransactionManagementclass PersistenceContext { @Bean AuditorAware<String> auditorProvider() { return new UsernameAuditorAware(); } @Bean DateTimeProvider dateTimeProvider(DateTimeService dateTimeService) { return new AuditingDateTimeProvider(dateTimeService); }}
因为我们只有一个AuditorAware
bean,所以Spring需要的时候会自动找到它,如果有多个bean,可以通过配置@EnableJpaAuditing
注解的auditorAwareRef属性值来设置。
如果使用XML配置,参照https://github.com/pkainulainen/spring-data-jpa-examples/blob/master/query-methods/src/main/resources/applicationContext-persistence.xml
下面修改实体类
修改实体类
有如下两个需求:
- 确保
createdByUser
字段在实体第一次存储时被设置 - 确保
modifiedByUser
字段在实体以一次存储和以后被修改时被设置
具体步骤如下:
创建一个
String
类型的createdByUser
字段:- 加上
@Column
注解,配置字段名为created_by_user,非空 - 加上
@CreatedBy
注解,说明这个字段表示的是谁创建了这个实体
- 加上
创建一个
String
类型的modifiedByUser
字段:- 加上
@Column
注解,配置字段名为modified_by_user,非空 - 加上
@LastModified
注解,说明这个字段表示的是谁最近修改了这个实体
- 加上
修改后的Todo
实体类如下:
import org.hibernate.annotations.Type;import org.springframework.data.annotation.CreatedBy;import org.springframework.data.annotation.CreatedDate;import org.springframework.data.annotation.LastModifiedBy;import org.springframework.data.annotation.LastModifiedDate;import org.springframework.data.jpa.domain.support.AuditingEntityListener;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.EntityListeners;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;import javax.persistence.Table;import javax.persistence.Version;import java.time.ZonedDateTime;@Entity@EntityListeners(AuditingEntityListener.class)@Table(name = "todos")final class Todo { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column(name = "created_by_user", nullable = false) @CreatedBy private String createdByUser; @Column(name = "creation_time", nullable = false) @Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime") @CreatedDate private ZonedDateTime creationTime; @Column(name = "description", length = 500) private String description; @Column(name = "modified_by_user", nullable = false) @LastModifiedBy private String modifiedByUser; @Column(name = "modification_time") @Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime") @LastModifiedDate private ZonedDateTime modificationTime; @Column(name = "title", nullable = false, length = 100) private String title; @Version private long version;}
如果有多个实体类需要审计,也可以将审计字段移到一个抽象基类中,就像下面:
import org.hibernate.annotations.Type;import org.springframework.data.annotation.CreatedBy;import org.springframework.data.annotation.CreatedDate;import org.springframework.data.annotation.LastModifiedBy;import org.springframework.data.annotation.LastModifiedDate;import org.springframework.data.jpa.domain.support.AuditingEntityListener;import javax.persistence.Column;import javax.persistence.MappedSuperClass@EntityListeners(AuditingEntityListener.class)@MappedSuperClasspublic abstract class BaseEntity { @Column(name = "created_by_user", nullable = false) @CreatedBy private String createdByUser; @Column(name = "creation_time", nullable = false) @Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime") @CreatedDate private ZonedDateTime creationTime; @Column(name = "modified_by_user", nullable = false) @LastModifiedBy private String modifiedByUser; @Column(name = "modification_time") @Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime") @LastModifiedDate private ZonedDateTime modificationTime;}
如果不想使用注解,可以实现Auditable
接口或者继承AbstractAuditable
类。Auditable
接口声明了所有的审计字段的getter和setter方法,AbstractAuditable
类提供了这些方法的实现,但有个劣势是你的实体类和Spring Data就产生了耦合
让我们看看为什么使用Spring Data JPA提供的审计支持而不是使用Java Persistence API提供的回调方法
为什么使用Spring Data JPA提供的审计支持?
我们还可以通过使用JPA的生命周期注解来标注一些方法,这些方法会在存储和更新时执行回调,例如:
import org.springframework.data.annotation.CreatedBy;import org.springframework.data.annotation.LastModifiedBy;import org.springframework.security.core.Authentication;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.security.core.userdetails.User;import javax.persistence.Column;import javax.persistence.MappedSuperClassimport javax.persistence.PrePersist;import javax.persistence.PreUpdate;@MappedSuperClasspublic abstract class BaseEntity { @Column(name = "created_by_user", nullable = false) @CreatedBy private String createdByUser; @Column(name = "modified_by_user", nullable = false) @LastModifiedBy private String modifiedByUser; @PrePersist public void prePersist() { String createdByUser = getUsernameOfAuthenticatedUser(); this.createdByUser = createdByUser; this.modifiedByUser = createdByUser; } @PreUpdate public void preUpdate() { String modifiedByUser = getUsernameOfAuthenticatedUser(); this.modifiedByUser = modifiedByUser; } private String getUsernameOfAuthenticatedUser() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null || !authentication.isAuthenticated()) { return null; } return ((User) authentication.getPrincipal()).getUsername(); }}
即使这个方式比使用Spring Data JPA提供的基础架构更简单和直接一些,仍然有两个原因支持我们使用更复杂的解决方案:
- 使用回调方法会和Spring Security产生耦合
- 如果我想用Spring Data JPA记录创建时间和修改时间的信息,而设置修改人还是用上面那种方式,用两种方式设置审计信息是没有什么意义的
总结一下
本篇说了下面三个事:
- Spring Data JPA使用
AuditorAware<T>
接口获取用户信息 - 我们可以通过注解,或者实现
Auditable
接口,或者继承AbstractAuditable
类来实现审计功能 - 通过回调lifecycle的方法比较简单,但劣势是我们的抽象基类和Spring Security产生了耦合
项目代码在作者的github上:https://github.com/pkainulainen/spring-data-jpa-examples/tree/master/query-methods
原文连接:http://www.petrikainulainen.net/programming/spring-framework/spring-data-jpa-tutorial-auditing-part-two
- Spring Data JPA教程:审计(二)
- Spring Data JPA教程:审计(一)
- spring data jpa(二)
- spring data jpa 使用(二)
- spring data jpa 简单教程
- spring boot(二)spring data jpa以及事物配置
- Spring Data JPA教程, 第五部分: Querydsl(未翻译)
- Spring Data JPA教程, 第六部分: Sorting(未翻译)
- Spring Data JPA教程, 第七部分: Pagination(未翻译)
- 一步步学习 Spring Data 系列之JPA(二)
- JQuery DataTable 结合SpringMVC+Spring Data JPA应用(二)
- Spring Data JPA 教程: 介绍篇
- Spring Boot 菜鸟教程 2 Data JPA
- Spring Data Jpa开发学习笔记(二)
- Spring Data JPA实战(二)-扩展开发
- springboot中使用spring -data-jpa (二)
- Spring Data JPA教程, 第四部分: JPA Criteria Queries(未翻译)
- Spring Data Jpa 概述(Spring Data Jpa 1)
- 关于《UNIX环境高级编程(第三版)》中apue.h头文件的设置
- 如何编译Notepad++
- 解决Win7下IEtester无法调出Debugbar面板和IE里Debugbar工具卡死问题
- thread.join()作用
- static的用法
- Spring Data JPA教程:审计(二)
- 【心情】今天才发现,自己很菜!!!
- 大端模式和小端模式
- CF 632F(Magic Matrix-MST)
- 个人知识点总结
- SSL 1507 棋盘 状压dp
- java web中如何处理表单中的中文
- ViewResolver引起的内存泄漏
- Lua_第 13 章环境