gpt4 book ai didi

JPA transient 信息在创建时丢失

转载 作者:行者123 更新时间:2023-12-03 21:20:30 24 4
gpt4 key购买 nike

我有一个带有 transient 字段的实体。当我想创建对象的新实例时,我会丢失我的 transient 信息。以下示例演示了该问题。为了这个例子,假设 barness 是一个 transient 场。

FooEntity fooEntity = new FooEntity();
fooEntity.setFoobosity(5);
fooEntity.setBarness(2);
fooEntity = fooEntityManager.merge(fooEntity);
System.out.println(fooEntity.getFoobosity()); //5
System.out.println(fooEntity.getBarness()); //0 (or whatever default barness is)

有什么方法可以维护我的 transient 信息吗?

最佳答案

加入讨论晚了,但这就是我使用 实现它的方式spring AOP 和 JPA 提供了@PreUpdate 注解 (添加详细版本)

用例

  • 如果从 UI 进行更改,我们应该使用 Spring 为实体提供的审计
  • 如果更改是通过 API 而不是通过前端服务完成的,我们希望值(@LastModifiedBy 和 @LastModifiedDate)被客户端提供的我们自己的值覆盖
  • 实体具有需要在保存后合并的 transient 值(backendModifiedDate、backendAuditor)(不幸的是,规范不保证这一点)。
    这两个字段将保存来自外部服务的审计数据
  • 在我们的案例中,我们需要一个通用的解决方案来审计所有实体。

  • 数据库配置
        package config;

    import io.github.jhipster.config.JHipsterConstants;
    import io.github.jhipster.config.liquibase.AsyncSpringLiquibase;

    import liquibase.integration.spring.SpringLiquibase;
    import org.h2.tools.Server;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Profile;
    import org.springframework.core.env.Environment;
    import org.springframework.core.task.TaskExecutor;
    import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    import org.springframework.transaction.annotation.EnableTransactionManagement;

    import javax.sql.DataSource;
    import java.sql.SQLException;

    @Configuration
    @EnableJpaRepositories("repository")
    @EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
    @EnableTransactionManagement
    public class DatabaseConfiguration {

    private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class);

    private final Environment env;

    public DatabaseConfiguration(Environment env) {
    this.env = env;
    }
    /* Other code */
    }

    SpringSecurityAuditorAware 用于注入(inject)用户名
    package security;

    import config.Constants;

    import org.springframework.data.domain.AuditorAware;
    import org.springframework.stereotype.Component;

    /**
    * Implementation of AuditorAware based on Spring Security.
    */
    @Component
    public class SpringSecurityAuditorAware implements AuditorAware<String> {

    @Override
    public String getCurrentAuditor() {
    String userName = SecurityUtils.getCurrentUserLogin();
    return userName != null ? userName : Constants.SYSTEM_ACCOUNT;
    }
    }

    使用 JPA @PreUpdate 的抽象实体
    这实际上将设置 @LastModifiedBy 和 @LastModifiedDate 字段的值
    package domain;

    import com.fasterxml.jackson.annotation.JsonIgnore;
    import org.hibernate.envers.Audited;
    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.EntityListeners;
    import javax.persistence.MappedSuperclass;
    import javax.persistence.PreUpdate;
    import java.io.Serializable;
    import java.time.Instant;

    /**
    * Base abstract class for entities which will hold definitions for created, last modified by and created,
    * last modified by date.
    */
    @MappedSuperclass
    @Audited
    @EntityListeners(AuditingEntityListener.class)
    public abstract class AbstractAuditingEntity implements Serializable {

    private static final long serialVersionUID = 1L;

    @CreatedBy
    @Column(name = "created_by", nullable = false, length = 50, updatable = false)
    @JsonIgnore
    private String createdBy;

    @CreatedDate
    @Column(name = "created_date", nullable = false)
    @JsonIgnore
    private Instant createdDate = Instant.now();

    @LastModifiedBy
    @Column(name = "last_modified_by", length = 50)
    @JsonIgnore
    private String lastModifiedBy;

    @LastModifiedDate
    @Column(name = "last_modified_date")
    @JsonIgnore
    private Instant lastModifiedDate = Instant.now();

    private transient String backendAuditor;

    private transient Instant backendModifiedDate;

    public String getCreatedBy() {
    return createdBy;
    }

    public void setCreatedBy(String createdBy) {
    this.createdBy = createdBy;
    }

    public Instant getCreatedDate() {
    return createdDate;
    }

    public void setCreatedDate(Instant createdDate) {
    this.createdDate = createdDate;
    }

    public String getLastModifiedBy() {
    return lastModifiedBy;
    }

    public void setLastModifiedBy(String lastModifiedBy) {
    this.lastModifiedBy = lastModifiedBy;
    }

    public Instant getLastModifiedDate() {
    return lastModifiedDate;
    }

    public void setLastModifiedDate(Instant lastModifiedDate) {
    this.lastModifiedDate = lastModifiedDate;
    }

    public String getBackendAuditor() {
    return backendAuditor;
    }

    public void setBackendAuditor(String backendAuditor) {
    this.backendAuditor = backendAuditor;
    }

    public Instant getBackendModifiedDate() {
    return backendModifiedDate;
    }

    public void setBackendModifiedDate(Instant backendModifiedDate) {
    this.backendModifiedDate = backendModifiedDate;
    }

    @PreUpdate
    public void preUpdate(){
    if (null != this.backendAuditor) {
    this.lastModifiedBy = this.backendAuditor;
    }
    if (null != this.backendModifiedDate) {
    this.lastModifiedDate = this.backendModifiedDate;
    }
    }
    }

    合并后保留数据的方面
    这将拦截对象(实体)并重置字段
    package aop.security.audit;


    import domain.AbstractAuditingEntity;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;

    import java.time.Instant;

    @Aspect
    @Component
    public class ExternalDataInflowAudit {
    private final Logger log = LoggerFactory.getLogger(ExternalDataInflowAudit.class);

    // As per our requirements, we need to override @LastModifiedBy and @LastModifiedDate
    // https://stackoverflow.com/questions/2581665/jpa-transient-information-lost-on-create?answertab=active#tab-top
    @Around("execution(public !void javax.persistence.EntityManager.merge(..))")
    private Object resetAuditFromExternal(ProceedingJoinPoint joinPoint) throws Throwable {
    Object[] args = joinPoint.getArgs();
    AbstractAuditingEntity abstractAuditingEntity;
    Instant lastModifiedDate = null;
    String lastModifiedBy = null;
    if (args.length > 0 && args[0] instanceof AbstractAuditingEntity) {
    abstractAuditingEntity = (AbstractAuditingEntity) args[0];
    lastModifiedBy = abstractAuditingEntity.getBackendAuditor();
    lastModifiedDate = abstractAuditingEntity.getBackendModifiedDate();
    }
    Object proceed = joinPoint.proceed();
    if (proceed instanceof AbstractAuditingEntity) {
    abstractAuditingEntity = (AbstractAuditingEntity) proceed;
    if (null != lastModifiedBy) {
    abstractAuditingEntity.setLastModifiedBy(lastModifiedBy);
    abstractAuditingEntity.setBackendAuditor(lastModifiedBy);
    log.debug("Setting the Modified auditor from [{}] to [{}] for Entity [{}]",
    abstractAuditingEntity.getLastModifiedBy(), lastModifiedBy, abstractAuditingEntity);
    }
    if (null != lastModifiedDate) {
    abstractAuditingEntity.setLastModifiedDate(lastModifiedDate);
    abstractAuditingEntity.setBackendModifiedDate(lastModifiedDate);
    log.debug("Setting the Modified date from [{}] to [{}] for Entity [{}]",
    abstractAuditingEntity.getLastModifiedDate(), lastModifiedDate, abstractAuditingEntity);
    }
    }
    return proceed;
    }
    }

    用法
    如果实体设置了 backendAuditor 和/或 backendModifiedDate,则将使用此值,否则将采用 Spring Audit 提供的值。

    最后感谢 Jhipster这简化了很多事情,以便您可以专注于业务逻辑。

    免责声明:我只是 Jhipster 的粉丝,与它没有任何关系。

    关于JPA transient 信息在创建时丢失,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2581665/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com