【源码】Spring Data JPA原理解析之Auditing执行原理

 Spring Data JPA系列

1、SpringBoot集成JPA及基本使用

2、Spring Data JPA Criteria查询、部分字段查询

3、Spring Data JPA数据批量插入、批量更新真的用对了吗

4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作

5、Spring Data JPA自定义Id生成策略、复合主键配置、Auditing使用

6、【源码】Spring Data JPA原理解析之Repository的自动注入(一)

7、【源码】Spring Data JPA原理解析之Repository的自动注入(二)

8、【源码】Spring Data JPA原理解析之Repository执行过程及SimpleJpaRepository源码

9、【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(一)

10、【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(二)

11、【源码】Spring Data JPA原理解析之Repository自定义方法添加@Query注解的执行原理

12、【源码】SpringBoot事务注册原理

13、【源码】Spring Data JPA原理解析之事务注册原理

14、【源码】Spring Data JPA原理解析之事务执行原理

15、【源码】SpringBoot编程式事务使用及执行原理

16、【源码】Spring事务之传播特性的详解

17、【源码】Spring事务之事务失效及原理

18、【源码】Spring Data JPA原理解析之Hibernate EventListener使用及原理

19、【源码】Spring Data JPA原理解析之Auditing执行原理

前言

在Spring Data JPA自定义Id生成策略、复合主键配置、Auditing使用-CSDN博客这篇博文中,跟大家分享了JPA中的Auditing审计功能。JPA通过注解的方式,提供了对数据库表中的数据记录自动添加操作人及操作时间,以及自定义监听器,实现实体插入、编辑、删除的监听,添加日志等。

Auditing使用回顾

2.1 在启动类中添加@EnableJpaAuditing注解,开启Auditing;

2.2 在实体类中添加监听器及操作人、操作时间的注解,代码如下:

package com.jingai.jpa.dao.entity;@Data
@Entity
@JsonIgnoreProperties(value = { "hibernateLazyInitializer"})
// 添加AuditingEntityListener实体监听
@EntityListeners(AuditingEntityListener.class)
@Table(name = "tb_user")
public class UserEntity {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String name;private String state;// 添加审计的注解@CreatedByprivate Long createBy;@CreatedDateprivate Date createTime;@LastModifiedByprivate Long modifyBy;@LastModifiedDateprivate Date modifyTime;}

2.3 自定义AuditorAware,重写getCurrentAuditor(),返回审计员主键,实现数据记录的创建人、操作人的自动填充;

通过以上三步,即可实现tb_user表中的createBy、createTime、modifyBy、modifyTime四个字段数据的自动填充。详见:

Spring Data JPA自定义Id生成策略、复合主键配置、Auditing使用-CSDN博客

Auditing的执行原理

Auditing审计功能提供了对数据库表中的数据记录自动添加操作人及操作时间,其核心实现在AuditingEntityListener。

3.1 AuditingEntityListener

AuditingEntityListener的源码如下:

package org.springframework.data.jpa.domain.support;@Configurable
public class AuditingEntityListener {// AuditingHandler处理器private @Nullable ObjectFactory<AuditingHandler> handler;public void setAuditingHandler(ObjectFactory<AuditingHandler> auditingHandler) {Assert.notNull(auditingHandler, "AuditingHandler must not be null");this.handler = auditingHandler;}/*** 用于设置目标对象的修改和创建日期以及审核程序。该方法添加了@PrePersist注解,在新增时回调*/@PrePersistpublic void touchForCreate(Object target) {Assert.notNull(target, "Entity must not be null");if (handler != null) {// 获取AuditingHandler的真实对象AuditingHandler object = handler.getObject();if (object != null) {// 标记新创建object.markCreated(target);}}}/*** 用于设置目标对象的修改和创建日期以及审核程序,方法添加了@PreUpdate注解*/@PreUpdatepublic void touchForUpdate(Object target) {Assert.notNull(target, "Entity must not be null");if (handler != null) {// 获取AuditingHandler的真实对象AuditingHandler object = handler.getObject();if (object != null) {// 标记被修改object.markModified(target);}}}
}

在AuditingEntityListener中,定义了两个方法,分别添加了@PrePersist和@PerUpdate,标记在新增和修改前回调。即AuditingEntityListener也是使用的Hibernate中的EventListener的监听回调注解。详见:

【源码】Spring Data JPA原理解析之Hibernate EventListener使用及原理-CSDN博客

通过上面的博文可知,当执行persist()时,会先执行添加@PrePersist注解的监听器方法,即Auditing中的AuditingEntityListener的touchForCreate()方法会被回调。该方法会判断handler是否为空,如果不为空,则执行handler的markCreated()方法。所以核心实现在handler,而该handler是AuditingHandler对象。

在Auditing的使用中,其中一步是需要添加@EnableJpaAuditing注解,开启Auditing。该注解的作用就在于为AuditingEntityListener的handler赋值。

3.2 EnableJpaAuditing注解

EnableJpaAuditing注解的源码如下:

package org.springframework.data.jpa.repository.config;@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
// 自动引入JpaAuditingRegistrar
@Import(JpaAuditingRegistrar.class)
public @interface EnableJpaAuditing {}

该注解会自动引入JpaAuditingRegistrar。

3.3 JpaAuditingRegistrar

JpaAuditingRegistrar的相关代码如下:

package org.springframework.data.jpa.repository.config;class JpaAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {private static final String BEAN_CONFIGURER_ASPECT_CLASS_NAME = "org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect";@Overrideprotected Class<? extends Annotation> getAnnotation() {return EnableJpaAuditing.class;}@Overrideprotected String getAuditingHandlerBeanName() {return "jpaAuditingHandler";}@Overridepublic void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null");Assert.notNull(registry, "BeanDefinitionRegistry must not be null");// 注册AnnotationBeanConfigurerAspectregisterBeanConfigurerAspectIfNecessary(registry);// 注册审计处理的bean,AuditingHandlersuper.registerBeanDefinitions(annotationMetadata, registry);// 注册AuditingBeanFactoryPostProcessor后置处理器registerInfrastructureBeanWithId(BeanDefinitionBuilder.rootBeanDefinition(AuditingBeanFactoryPostProcessor.class).getRawBeanDefinition(),AuditingBeanFactoryPostProcessor.class.getName(), registry);}/*** 注册AuditingEntityListener监听器*/@Overrideprotected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,BeanDefinitionRegistry registry) {// 如果不存在JpaMetamodelMappingContext,注册JpaMetamodelMappingContextFactoryBean// MappingContext用来映射实体与数据库之间关系的机制if (!registry.containsBeanDefinition(JPA_MAPPING_CONTEXT_BEAN_NAME)) {registry.registerBeanDefinition(JPA_MAPPING_CONTEXT_BEAN_NAME, //new RootBeanDefinition(JpaMetamodelMappingContextFactoryBean.class));}// 创建AuditingEntityListener监听器的beanBeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(AuditingEntityListener.class);// 设置AuditingEntityListener的auditingHandler属性,值为jpaAuditingHandler对象builder.addPropertyValue("auditingHandler",ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), null));// 注册AuditingEntityListenerregisterInfrastructureBeanWithId(builder.getRawBeanDefinition(), AuditingEntityListener.class.getName(), registry);}// 省略其他
}

JpaAuditingRegistrar实现了ImportBeanDefinitionRegistrar,所以在Spring启动的时候,会执行registerBeanDefinitions()方法。在registerBeanDefinitions()方法中,核心是调用父类的AuditingBeanDefinitionRegistrarSupport的registerBeanDefinitions()方法。

3.4  AuditingBeanDefinitionRegistrarSupport

AuditingBeanDefinitionRegistrarSupport的源码如下:

public abstract class AuditingBeanDefinitionRegistrarSupport implements ImportBeanDefinitionRegistrar {private static final String AUDITOR_AWARE = "auditorAware";private static final String DATE_TIME_PROVIDER = "dateTimeProvider";private static final String MODIFY_ON_CREATE = "modifyOnCreation";private static final String SET_DATES = "dateTimeForNow";/*** Spring容器会自动执行ImportBeanDefinitionRegistrar.registerBeanDefinitions(),添加AnnotationAuditingConfiguration装配类* @param annotationMetadata* @param registry*/@Overridepublic void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null");Assert.notNull(registry, "BeanDefinitionRegistry must not be null");// 注册审计处理的bean,AuditingHandlerAbstractBeanDefinition ahbd = registerAuditHandlerBeanDefinition(getConfiguration(annotationMetadata), registry);// 注册AuditingEntityListener监听器registerAuditListenerBeanDefinition(ahbd, registry);}/*** 注册审计处理的bean,AuditingHandler* @param configuration AnnotationAuditingConfiguration对象,记录@EnableJpaAuditing的配置信息* @param registry* @return*/protected AbstractBeanDefinition registerAuditHandlerBeanDefinition(AuditingConfiguration configuration,BeanDefinitionRegistry registry) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");Assert.notNull(configuration, "AuditingConfiguration must not be null");// 使用给定AuditingConfiguration中的默认属性配置AuditingHandler的BeanDefinitionBuilderBeanDefinitionBuilder builder = getAuditHandlerBeanDefinitionBuilder(configuration);postProcess(builder, configuration, registry);// 获取AuditingHandler的beanAbstractBeanDefinition ahbd = builder.getBeanDefinition();registry.registerBeanDefinition(getAuditingHandlerBeanName(), ahbd);return ahbd;}protected void postProcess(BeanDefinitionBuilder builder, AuditingConfiguration configuration,BeanDefinitionRegistry registry) {}/*** 使用给定AuditingConfiguration中的默认属性配置AuditingHandler的BeanDefinitionBuilder*/protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) {Assert.notNull(configuration, "AuditingConfiguration must not be null");return configureDefaultAuditHandlerAttributes(configuration,BeanDefinitionBuilder.rootBeanDefinition(AuditingHandler.class));}/*** 使用给定AuditingConfiguration中的默认属性配置给定的BeanDefinitionBuilder*/protected BeanDefinitionBuilder configureDefaultAuditHandlerAttributes(AuditingConfiguration configuration,BeanDefinitionBuilder builder) {if (StringUtils.hasText(configuration.getAuditorAwareRef())) {// 创建一个懒加载的目标资源的auditorAwareRef的BeanDefinition对象builder.addPropertyValue(AUDITOR_AWARE,createLazyInitTargetSourceBeanDefinition(configuration.getAuditorAwareRef()));} else {builder.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE);}// 设置AuditingHandler的父类AuditingHandlerSupport的dateTimeForNow和modifyOnCreationbuilder.addPropertyValue(SET_DATES, configuration.isSetDates());builder.addPropertyValue(MODIFY_ON_CREATE, configuration.isModifyOnCreate());// 设置AuditingHandler的父类AuditingHandlerSupport的dateTimeProviderif (StringUtils.hasText(configuration.getDateTimeProviderRef())) {builder.addPropertyReference(DATE_TIME_PROVIDER, configuration.getDateTimeProviderRef());} else {builder.addPropertyValue(DATE_TIME_PROVIDER, CurrentDateTimeProvider.INSTANCE);}builder.setRole(AbstractBeanDefinition.ROLE_INFRASTRUCTURE);return builder;}/*** 返回一个AnnotationAuditingConfiguration对象,记录@EnableJpaAuditing的配置信息*/protected AuditingConfiguration getConfiguration(AnnotationMetadata annotationMetadata) {return new AnnotationAuditingConfiguration(annotationMetadata, getAnnotation());}/*** 在JpaAuditingRegistrar中返回@EnableJpaAuditing* @return*/protected abstract Class<? extends Annotation> getAnnotation();protected abstract void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,BeanDefinitionRegistry registry);protected abstract String getAuditingHandlerBeanName();protected void registerInfrastructureBeanWithId(AbstractBeanDefinition definition, String id,BeanDefinitionRegistry registry) {definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registry.registerBeanDefinition(id, definition);}/*** 创建一个懒加载的目标资源的auditorAwareRef的BeanDefinition对象*/private BeanDefinition createLazyInitTargetSourceBeanDefinition(String auditorAwareRef) {BeanDefinitionBuilder targetSourceBuilder = rootBeanDefinition(LazyInitTargetSource.class);targetSourceBuilder.addPropertyValue("targetBeanName", auditorAwareRef);BeanDefinitionBuilder builder = rootBeanDefinition(ProxyFactoryBean.class);builder.addPropertyValue("targetSource", targetSourceBuilder.getBeanDefinition());return builder.getBeanDefinition();}
}

在registerBeanDefinitions()方法中,执行如下:

1)执行registerAuditHandlerBeanDefinition(),通过@EnableJpaAuditing注解的中配置信息,传入AuditHandler中,注册Audit的处理器AuditHandler的bean到Spring容器;

2)执行registerAuditListenerBeanDefinition(),该方法在子类JpaAuditingRegistrar中实现。该方法注册Auditing的监听器AuditingEntityListener,并将1)中的AuditHandler设置给AuditingEntityListener;

3.5 AuditHandler

AuditHandler的源码如下:

package org.springframework.data.auditing;public class AuditingHandler extends AuditingHandlerSupport implements InitializingBean {private static final Log logger = LogFactory.getLog(AuditingHandler.class);private Optional<AuditorAware<?>> auditorAware;public AuditingHandler(PersistentEntities entities) {super(entities);Assert.notNull(entities, "PersistentEntities must not be null");this.auditorAware = Optional.empty();}public static AuditingHandler from(MappingContext<?, ?> mappingContext) {return new AuditingHandler(PersistentEntities.of(mappingContext));}/*** 在创建AuditingHandler时,设置了builder.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE),* 所以会将Spring容器中的AuditorAware自动调用该方法进行设置。AuditorAware用户获取当前审计人信息* @param auditorAware*/public void setAuditorAware(AuditorAware<?> auditorAware) {Assert.notNull(auditorAware, "AuditorAware must not be null");this.auditorAware = Optional.of(auditorAware);}/*** 标记实体被创建*/public <T> T markCreated(T source) {Assert.notNull(source, "Entity must not be null");// 执行父类的方法return markCreated(getAuditor(), source);}/*** 标记实体被更新*/public <T> T markModified(T source) {Assert.notNull(source, "Entity must not be null");// 执行父类的方法return markModified(getAuditor(), source);}/*** 如果存在auditorAware,则执行auditorAware.getCurrentAuditor(),获取审计人;否则为Auditor.none()*/Auditor<?> getAuditor() {return auditorAware.map(AuditorAware::getCurrentAuditor).map(Auditor::ofOptional) //.orElse(Auditor.none());}public void afterPropertiesSet() {if (!auditorAware.isPresent()) {logger.debug("No AuditorAware set; Auditing will not be applied");}}
}

在AuditHandler中,核心实现在父类AuditingHandlerSupport。

3.6 AuditingHandlerSupport

AuditingHandlerSupport的源码如下:

package org.springframework.data.auditing;public abstract class AuditingHandlerSupport {private static final Log logger = LogFactory.getLog(AuditingHandlerSupport.class);// 审计bean的包装工厂,该类的实现为DefaultAuditableBeanWrapperFactoryprivate final AuditableBeanWrapperFactory factory;private DateTimeProvider dateTimeProvider = CurrentDateTimeProvider.INSTANCE;private boolean dateTimeForNow = true;private boolean modifyOnCreation = true;public AuditingHandlerSupport(PersistentEntities entities) {Assert.notNull(entities, "PersistentEntities must not be null");this.factory = new MappingAuditableBeanWrapperFactory(entities);}public void setDateTimeForNow(boolean dateTimeForNow) {this.dateTimeForNow = dateTimeForNow;}public void setModifyOnCreation(boolean modifyOnCreation) {this.modifyOnCreation = modifyOnCreation;}public void setDateTimeProvider(@Nullable DateTimeProvider dateTimeProvider) {this.dateTimeProvider = dateTimeProvider == null ? CurrentDateTimeProvider.INSTANCE : dateTimeProvider;}protected final boolean isAuditable(Object source) {Assert.notNull(source, "Source entity must not be null");return factory.getBeanWrapperFor(source).isPresent();}<T> T markCreated(Auditor<?> auditor, T source) {Assert.notNull(source, "Source entity must not be null");return touch(auditor, source, true);}<T> T markModified(Auditor<?> auditor, T source) {Assert.notNull(source, "Source entity must not be null");return touch(auditor, source, false);}/*** 处理* @param auditor:审计人信息* @param target:目标实体,即实体类* @param isNew:是否新增*/private <T> T touch(Auditor<?> auditor, T target, boolean isNew) {// 创建实体类的包装类,用于设置审计相关的属性值,// 在getBeanWrapperFor()方法中,解析实体类定义的审计相关的注解的元数据,// 包装成DefaultAuditableBeanWrapperFactory.ReflectionAuditingBeanWrapperOptional<AuditableBeanWrapper<T>> wrapper = factory.getBeanWrapperFor(target);// 对实体解包return wrapper.map(it -> {// 设置审计人信息。@createBy、@lastModifiedBytouchAuditor(auditor, it, isNew);// 设置日期Optional<TemporalAccessor> now = dateTimeForNow ? touchDate(it, isNew) : Optional.empty();if (logger.isDebugEnabled()) {Object defaultedNow = now.map(Object::toString).orElse("not set");Object defaultedAuditor = auditor.isPresent() ? auditor.toString() : "unknown";logger.debug(LogMessage.format("Touched %s - Last modification at %s by %s", target, defaultedNow, defaultedAuditor));}// 返回设置了审计信息的对象return it.getBean();}).orElse(target);}/*** 设置修改和创建审核员。仅在新的可审核对象上设置创建审核器*/private void touchAuditor(Auditor<?> auditor, AuditableBeanWrapper<?> wrapper, boolean isNew) {if(!auditor.isPresent()) {return;}Assert.notNull(wrapper, "AuditableBeanWrapper must not be null");// 如果是新增,修改createByif (isNew) {wrapper.setCreatedBy(auditor.getValue());}// 如果不是新增 || 配置了modifyOnCreation为true,修改lastModifiedByif (!isNew || modifyOnCreation) {wrapper.setLastModifiedBy(auditor.getValue());}}/*** 设置操作时间*/private Optional<TemporalAccessor> touchDate(AuditableBeanWrapper<?> wrapper, boolean isNew) {Assert.notNull(wrapper, "AuditableBeanWrapper must not be null");// 获取当前时间Optional<TemporalAccessor> now = dateTimeProvider.getNow();Assert.notNull(now, () -> String.format("Now must not be null Returned by: %s", dateTimeProvider.getClass()));// 设置创建时间now.filter(__ -> isNew).ifPresent(wrapper::setCreatedDate);// 设置修改时间now.filter(__ -> !isNew || modifyOnCreation).ifPresent(wrapper::setLastModifiedDate);return now;}
}

在AuditingHandlerSupport中,无论是新增还是编辑,最后都会调用touch()方法,该方法主要执行如下:

1)执行factory.getBeanWrapperFor(target),获取一个AuditableBeanWrapper对象,该对象为DefaultAuditableBeanWrapperFactory.ReflectionAuditingBeanWrapper;

该方法会执行AnnotationAuditingMetadata.getMetadata(it.getClass()),解析实体类中定义的审计相关注解的元数据,存放对应的注解的Field对象,并将信息添加到ReflectionAuditingBeanWrapper中。

2)执行touchAuditor(),根据新增或修改,调用ReflectionAuditingBeanWrapper的对应审计方法,为对应审计的审计人Field赋值;

3)执行touchDate(),根据新增或修改,调用ReflectionAuditingBeanWrapper的对应审计方法,为对应审计的时间Field赋值;

4)返回包装后的实体对象;

3.7 ReflectionAuditingBeanWrapper

ReflectionAuditingBeanWrapper的代码如下:

package org.springframework.data.auditing;class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory {/*** 保存实体类的审计相关注解的元数据*/static class ReflectionAuditingBeanWrapper<T> extends DateConvertingAuditableBeanWrapper<T> {private final AnnotationAuditingMetadata metadata;private final T target;/*** 保存实体类的审计相关注解的元数据*/public ReflectionAuditingBeanWrapper(ConversionService conversionService, T target) {super(conversionService);Assert.notNull(target, "Target object must not be null");this.metadata = AnnotationAuditingMetadata.getMetadata(target.getClass());this.target = target;}/*** 为属性Field赋值*/@Overridepublic Object setCreatedBy(Object value) {return setField(metadata.getCreatedByField(), value);}@Overridepublic TemporalAccessor setCreatedDate(TemporalAccessor value) {return setDateField(metadata.getCreatedDateField(), value);}@Overridepublic Object setLastModifiedBy(Object value) {return setField(metadata.getLastModifiedByField(), value);}@Overridepublic Optional<TemporalAccessor> getLastModifiedDate() {return getAsTemporalAccessor(metadata.getLastModifiedDateField().map(field -> {Object value = org.springframework.util.ReflectionUtils.getField(field, target);return value instanceof Optional ? ((Optional<?>) value).orElse(null) : value;}), TemporalAccessor.class);}@Overridepublic TemporalAccessor setLastModifiedDate(TemporalAccessor value) {return setDateField(metadata.getLastModifiedDateField(), value);}@Overridepublic T getBean() {return target;}private <S> S setField(Optional<Field> field, S value) {field.ifPresent(it -> ReflectionUtils.setField(it, target, value));return value;}private TemporalAccessor setDateField(Optional<Field> field, TemporalAccessor value) {field.ifPresent(it -> ReflectionUtils.setField(it, target, getDateValueToSet(value, it.getType(), it)));return value;}}
}

ReflectionAuditingBeanWrapper提供了setField的方法,为对应的审计字段赋值。

3.7 AnnotationAuditingMetadata

AnnotationAuditingMetadata的源码如下:

package org.springframework.data.auditing;final class AnnotationAuditingMetadata {// 定义注解属性过滤器private static final AnnotationFieldFilter CREATED_BY_FILTER = new AnnotationFieldFilter(CreatedBy.class);private static final AnnotationFieldFilter CREATED_DATE_FILTER = new AnnotationFieldFilter(CreatedDate.class);private static final AnnotationFieldFilter LAST_MODIFIED_BY_FILTER = new AnnotationFieldFilter(LastModifiedBy.class);private static final AnnotationFieldFilter LAST_MODIFIED_DATE_FILTER = new AnnotationFieldFilter(LastModifiedDate.class);private static final Map<Class<?>, AnnotationAuditingMetadata> metadataCache = new ConcurrentHashMap<>();private final Optional<Field> createdByField;private final Optional<Field> createdDateField;private final Optional<Field> lastModifiedByField;private final Optional<Field> lastModifiedDateField;/*** Creates a new {@link AnnotationAuditingMetadata} instance for the given type.** @param type must not be {@literal null}.*/private AnnotationAuditingMetadata(Class<?> type) {Assert.notNull(type, "Given type must not be null");// 解析审计的注解,获取对应属性Field对象this.createdByField = Optional.ofNullable(ReflectionUtils.findField(type, CREATED_BY_FILTER));this.createdDateField = Optional.ofNullable(ReflectionUtils.findField(type, CREATED_DATE_FILTER));this.lastModifiedByField = Optional.ofNullable(ReflectionUtils.findField(type, LAST_MODIFIED_BY_FILTER));this.lastModifiedDateField = Optional.ofNullable(ReflectionUtils.findField(type, LAST_MODIFIED_DATE_FILTER));assertValidDateFieldType(createdDateField);assertValidDateFieldType(lastModifiedDateField);}/*** 解析实体类的审计相关注解元数据,并保存到缓存中*/public static AnnotationAuditingMetadata getMetadata(Class<?> type) {return metadataCache.computeIfAbsent(type, AnnotationAuditingMetadata::new);}// 省略其他}

AnnotationAuditingMetadata主要解析实体类中添加的审计相关注解的属性的Field。

小结

以上为本篇分享的全部内容,以下做一个小结:

1)JPA的提供了持久化的监听回调注解;

2)Spring解析实体类时,会解析实体类中添加的监听回调注解的监听器;或者实体类中的@EntityListeners注解中的监听器,并归类存放在FastSessionServices中,然后将FastSessionServices传给SessionImpl对象;

对于Auditing,监听器为AuditingEntityListener,添加了@PrePersist和@PreUpdate,在新增和修改之前回调。

3)Auditing审计功能通过添加@EnableJpaAuditing注解,自动为AuditingEntityListener添加AuditHandler对象;

4)AuditHandler对象提供了对当前执行持久化对象的审计相关注解的属性获取、审计人获取;

5)JPA通过SessionImpl执行新增或修改操作时,会调用FastSessionServices中对应操作类型的监听器,从而执行AuditingEntityListener的方法;

6)AuditingEntityListener通过AuditHandler及当前的实体类,通过反射,为实体类的审计属性赋值;

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/33118.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

7.系统工具——黑马程序员Java最新AI+若依框架项目

目录 前言一、表单构建任务&#xff1a;设计添加课程表单 二、 代码生成1.任务&#xff1a;将部门表在页面端显示改为树形结构 三、系统接口任务&#xff1a;使用sagger进行接口测试 前言 提示&#xff1a;本篇讲解若依框架 系统工具 一、表单构建 功能&#xff1a;完成前端…

欧洲杯赛况@20240623

估计点击标题下「蓝色微信名」可快速关注 老牌劲旅捷克队面对格鲁吉亚&#xff0c;这是两队的首次交锋&#xff0c;格鲁吉亚是很放松的状态&#xff0c;每场比赛对他们都很新鲜&#xff0c;而捷克则谨慎多&#xff0c;至今为止&#xff0c;最倒霉的球员&#xff0c;可能就是捷克…

nvm安装

1、官网下载nvm压缩包 Releases coreybutler/nvm-windows (github.com)https://github.com/coreybutler/nvm-windows/releases 2、安装nvm cmd下查看nvm版本号 nvm version 3、配置镜像 在安装目录下找到settings.txt文件&#xff0c;追加淘宝镜像 node_mirror:https:/…

Java——封装(Encapsulation)

一、封装简介 1、封装是什么 封装&#xff08;Encapsulation&#xff09;是面向对象编程&#xff08;OOP&#xff09;中的一个基本概念&#xff0c;它指的是将对象的状态&#xff08;数据&#xff09;和行为&#xff08;方法&#xff09;绑定在一起&#xff0c;并对外隐藏对象…

websocket 安全通信

WebSocket 协议 WebSocket&#xff1a;在 2008 年诞生&#xff0c;2011 年成为国际标准。它允许服务器主动向客户端推送信息&#xff0c;客户端也可以主动向服务器发送信息&#xff0c;实现了真正的双向平等对话。它是一种在单个 TCP 连接上进行全双工通讯的协议&#xff0c;能…

【Python如何输入升高和体重判断你是偏胖还是偏瘦】

1、求体质指数得Python代码如下&#xff1a; # BMI&#xff08;Body Mass Index&#xff09;指数:简称体质指数&#xff0c; # 是国际上常用的衡量人体胖瘦程度以及是否健康的一个标准。 # 常用指标:BMI<18.5 偏瘦 18.5<MBI<24 正常 MBI>24 偏胖 # 计算公式:BMI…

express+vue 在线五子棋(一)

示例 在线体验地址五子棋&#xff0c;记得一定要再拉个人才能对战 本期难点 1、完成了五子棋的布局&#xff0c;判断游戏结束 2、基本的在线对战 3、游戏配套im(这个im的实现&#xff0c;请移步在线im) 下期安排 1、每步的倒计时设置 2、黑白棋分配由玩家自定义 3、新增旁观…

# Kafka_深入探秘者(3):kafka 消费者

Kafka_深入探秘者&#xff08;3&#xff09;&#xff1a;kafka 消费者 一、kafka 消费者、消费组 1、Kafka 消费者是消费组的一部分&#xff0c;当多个消费者形成一个消费组来消费主题时&#xff0c;每个消费者会收到不同分区的消息。假设有一个 T1 主题&#xff0c;该主题有…

Web应用防火墙(WAF)(上:基础概念篇)

运维专题 Web应用防火墙&#xff08;WAF&#xff09;&#xff08;上&#xff1a;基础概念篇&#xff09; - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. She…

pgAdmin后台命令执行漏洞(CVE-2023-5002)

​ 我们可以看到针对于漏洞 CVE-2022-4223&#xff0c;官方做了一定的修复措施。 web\pgadmin\misc_init_.py#validate_binary_path ​ 首先是添加了 login_required​ 进行权限校验。在 Flask 框架中&#xff0c;login_required​ 装饰器通常与 Flask-Login 扩展一起使用。…

LED恒流调光电路

LED等在工作的时候发热较大&#xff0c;所以通常选用铝基板作为底板&#xff1b;常用白色油墨。 LED必须在恒流源电路下工作&#xff0c;下图为最简单的恒流源&#xff1a;B极电压3.3V不变左下侧蓝色的为稳压二极管&#xff0c;由于BE极可以看做二极管&#xff0c;压降为0.7V&…

OpenCV颜色检测

OpenCV颜色检测 前言策略分析根据颜色检测目标对象相关链接 前言 绿幕技术是一种经典的视频编辑技术&#xff0c;可以用于将人物置于不同的背景中。例如在电影制作中&#xff0c;技术的关键在于演员不能身着特定颜色的衣服(比如绿色)&#xff0c;站在只有绿色的背景前。然后&a…

异地组网如何OEM?

在现代信息社会中&#xff0c;企业越来越需要跨地域进行数据传输与共享。面临的挑战却是如何在不暴露在公网的情况下&#xff0c;实现异地组网并保障数据的安全性。本文将介绍一种名为“异地组网OEM”的解决方案&#xff0c;该方案能够通过私有通道传输数据并对数据进行安全加密…

我的大学生活-人面不知何处去(大三篇)

我的大学生活&#xff08;大三篇&#xff09; 前言推荐大三&#xff08;人面不知何处去&#xff09;2022年8月2022年9月2022年10月2022年11月2022年12月 寒假2023年1月 大三&#xff08;人面不知何处去&#xff09;2023年2月2023年3月2023年4月2023年5月2023年6月 暑假2023年7月…

【LeedCode】二分查找算法(一)

二分查找算法的时间复杂度是O(logN) &#xff0c;更优于传统的遍历数组算法值得我们学习。 注意二分查找一般使用的前提是&#xff1a;待操作的数组的元素有某种规律也就是要有二阶性&#xff0c;二段性就是在数组中选取一点根据该数组元素某种规律可以把数组分为两部分&#x…

国企:2024年6月中国移动相关招聘信息 三

中国移动卓望公司-卓望信息 卓望公司成立于2000年6月,是中国移动的控股子公司,积极拓展互联网、IT、ICT领域,提供平台及应用开发、运营运维等服务。  成立二十余年来,卓望公司逐渐形成包括业务合作管理、内容渠道运营、网络集中运维、企业服务、安全服务、行业DICT服务等…

LDR6500U,让设备爱上“被骗”的充电速度!

在数字设备日新月异的今天&#xff0c;兼容性和充电效率已成为用户关注的核心焦点。尤其是随着电子设备市场的全球化发展&#xff0c;标准化的需求日益凸显。近期&#xff0c;欧洲联盟&#xff08;简称“欧盟”&#xff09;就电子设备充电接口问题做出了重要决策&#xff0c;要…

高校新生如何选择最优手机流量卡?

一年一度的高考已经结束了&#xff0c;愿广大学子金榜题名&#xff0c;家长们都给孩子准备好了手机&#xff0c;那么手机流量卡应该如何选择呢&#xff1f; 高校新生在选择手机流量卡时&#xff0c;需要综合考量流量套餐、费用、网络覆盖、售后服务等多方面因素&#xff0c;以下…

Java开发-实际工作经验和技巧-0001-PostgreSQL数据库存储磁盘满了重启以及应急措施

Java开发-实际工作经验和技巧-0001-PostgreSQL数据库存储磁盘满了重启以及应急措施 更多内容欢迎关注我&#xff08;持续更新中&#xff0c;欢迎Star✨&#xff09; Github&#xff1a;CodeZeng1998/Java-Developer-Work-Note 技术公众号&#xff1a;CodeZeng1998&#xff0…