【问题标题】:JPA MappedSuperClass common audit value for all entities所有实体的 JPA MappedSuperClass 通用审计值
【发布时间】:2022-01-19 04:54:34
【问题描述】:

我有几个 JPA 实体,每个实体都有一个数据库用户列,在该列中我必须存储对表中特定行进行更改的用户。

我创建了一个'MappedSuperclass' Bean,所有实体都将从中扩展,这就是 MappedSuperclass。

@MappedSuperclass
public abstract class AuditableBean {

    @Column(name = "modifier_user", nullable = false)
    private String modifier_user;

    // Getters and Setters
}

这是一个从“MappedSuperclass”扩展而来的实体。

@Entity
@Table(name = "nm_area_ref")
public class ServingAreaReferenceBean extends AuditableBean {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "nm_area_ref_id")
    private UUID id;

    @Column(name = "nm_srv_area_desc", nullable = false)
    private String description;

    @Column(name = "nm_retired", nullable = false)
    private boolean retired;

    // Getters and Setters
}

并且,所有的 Bean 都有一个对应的服务方法,用于将数据保存到数据库中,这是服务类之一(每个服务都注入一个存储库用于 CRUD 操作)。

// Service
@Component
public class ServingAreaReferenceBO {

    @Inject private ServingAreaReferenceRepository repository; //repository injection
    @Inject private CloudContextProvider cloudContextProvider;

    public List<ServingAreaReferenceBean> getAllServingAreaReferences() {
        return Lists.newArrayList(repository.findAll());
    }

    public Optional<ServingAreaReferenceBean> findById(UUID id) {
        return repository.findById(id);
    }

    public ServingAreaReferenceBean create(ServingAreaReferenceBean servingAreaReference) {
        Optional<CloudContext> cloudContext = Optional.ofNullable(cloudContextProvider.get());// line 1
        servingAreaReference.setUpdaterUser(cloudContext.map(CloudContext::getUserId).orElse(null));// line 2
        return repository.save(servingAreaReference);// line 3
    }

}

// Repository - It extends from CrudRepository (insert, update, delete operations)
@Repository
public interface ServingAreaReferenceRepository extends CrudRepository<ServingAreaReferenceBean, UUID> {

    boolean existsByDescription(String description);

    boolean existsByDescriptionAndIdIsNot(String description, UUID id);
}

当 'repository.save()'(第 3 行)执行时,它成功存储了用户,但我将用户逻辑放在执行保存方法之前(第 1、2 行)。所以我不认为在每个服务上重复这两行是最好的方法,相反,我想实现一个通用方法或一个通用类,在执行保存方法之前为所有 Bean 实体设置用户。

这可能吗?什么是更好的方法?

我想实现这样的东西,但不知道如何使其通用?

@Component
public class AuditableBeanHandler {

    @Inject private CloudContextProvider cloudContextProvider;

    public AuditableBean populateAuditInformation(AuditableBean auditableBean) {
        Optional<CloudContext> cloudContext = Optional.ofNullable(CloudContextProvider.get());
        auditableBean.setUpdaterUser(CloudContext.map(cloudContext::getUserId).orElse(null));
        return auditableBean;
    }

}

【问题讨论】:

    标签: java spring hibernate jpa mappedsuperclass


    【解决方案1】:

    我的理解是,您必须在实体的每次保存调用之前设置用户:

    这可以通过使用称为“模板方法设计模式”的众所周知的设计模式来解决。

    只需为服务类创建一个父类:

    public abstract class AbstractService<T extends AuditableBean> {
    
            public AuditableBean populateAuditInformation(AuditableBean auditableBean) {
                Optional<CloudContext> cloudContext = Optional.ofNullable(CloudContextProvider.get());
    auditableBean.setLastUpdatedByUser(CloudContext.map(cloudContext::getUserId).orElse(null));
                        return auditableBean;
                    }
    
    public absract T save(T areaReference);
    
    public final T create(T t) {
                 t = populateAuditInformation(t);
                 return save(t);
                }
    

    并在您的服务类中扩展此抽象服务并添加保存方法:

    public class AreaReferenceService extends AbstractService<AreaReferenceBean> {
    
    public AreaReferenceBean save(AreaReferenceBean AreaReference) {
            return repository.save(AreaReference);
        }
    }
    

    调用服务方法时,调用create()方法。 希望这能解决您的问题,您也可以阅读更多有关模板方法设计模式的信息。

    【讨论】:

    • 我试图遵循您的建议,但是当我尝试在 ServingAreaReferenceBO 中实现“保存”抽象方法时,它使用 AuditableBean 参数实现。但是我想保留这个服务方法和所有服务方法不变(我的意图不是侵入性)并且不要更改名称而只是扩展您建议的 AbstractService 。并在那里委派功能。但是,当我更改保存抽象方法的签名时,该类无法识别其实现。
    • @ChristianMarceloReinozoCues 您能否分享确切的错误,以及您尝试过的内容,一定会针对您的情况为您提供解决方案。
    • 我与我尝试处理的场景以及我在您的解决方案中试验的错误分享了此文档。 docs.google.com/document/d/…
    • 不要在存储库级别进行更改,在服务层进行处理
    【解决方案2】:

    我认为您与 AuditableBean 非常接近。

    添加以下配置以启用审核:

    // Annotate a configuration class with this
    @EnableJpaAuditing(auditorAwareRef = "auditAware")
    

    添加这个“auditAware”bean,您需要调整它以匹配您正在使用的任何身份验证机制。它的唯一目的是返回经过身份验证的用户的用户名,Spring 将使用它。

    @Bean
    public AuditorAware<String> auditAware() {
    
    return () -> {
       Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
       return Optional.of(authentication.getPrincipal());
    };
    

    }

    在您的 modified_user 字段中再添加一个注释 (@LastModifiedBy)。这告诉 Spring,当实体发生更新时,将此字段设置为从 AuditAware bean 返回的值。

    @Column(name = "modifier_user", nullable = false)
    @LastModifiedBy
    private String modifier_user;
    

    有关可用审计字段的更多信息,请参阅Spring Data JPA Documentation

    【讨论】:

      猜你喜欢
      • 2017-01-13
      • 1970-01-01
      • 2022-06-30
      • 2018-08-13
      • 1970-01-01
      • 2012-08-10
      • 1970-01-01
      • 2015-12-14
      • 1970-01-01
      相关资源
      最近更新 更多