当数据库列名称不同时,在基实体类中定义审计列?

Posted

技术标签:

【中文标题】当数据库列名称不同时,在基实体类中定义审计列?【英文标题】:Define audit columns in base entity class when database column names differ? 【发布时间】:2017-01-18 13:12:07 【问题描述】:

我有一个数据库,每个表中都有公共审计列,但列名不同。

例如 Person 表有以下审计列,

(per_creation_user, per_creation_date, per_update_user, per_update_date),  and the address table has audit columns called  (add_creation_user, add_creation_date, add_update_user, add_update_date).

我正在尝试使用 JPA 注释映射这些,并使用事件侦听器类来自动填充这些审计列,只要它们持久存在于数据库中。

我有一个包含这些审计列的基本抽象类,然后我可以用@MappedSuperclass 对其进行注释,并将实体侦听器注释也放在此处。所有的整洁,不幸的是,每个审计实体的列名都不同。我认为唯一的选择是在每个实体上分别映射审计列?

有人可以提出更好的方法吗?

@EntityListeners(BaseDTOEventListener.class)
@MappedSuperclass
public abstract class BaseDTO 
    private String creationUser;
    private Date creationDate;


@Entity
@Table(name="PERSON")
public class Person extends BaseDTO


@Entity
@Table(name="ADDRESS")
public class Address extends BaseDTO


public class BaseDTOEventListener 
    @PrePersist
    public void onPreInsert(BaseDTO baseDTO)
        baseDTO.setCreationUser("TEST");
        baseDTO.setCreationDate(new Date());

    

【问题讨论】:

您可以使用属性覆盖重新定义从映射超类docs.oracle.com/javaee/6/api/javax/persistence/…扩展的类中的列 【参考方案1】:

@Embeddable@MappedSuperClass 结合使用:

首先定义BaseDTO接口:

@EntityListeners(BaseDTOEventListener.class)
        @MappedSuperclass
        public abstract class BaseDTO 
            public abstract getAuditEmbeddable();

            public void setCreationDate(Date date)
                getAuditEmbeddable().setCreationDate(date);
            

            public void setCreationUser(String user)
                getAuditEmbeddable().setCreationUser(user);    
            
        

然后定义将保存审计字段的嵌入。 此处为用户最常用的列名。

        @Embeddable
        public class AuditEmbeddable

            @Column(name = "creationUser")
            private String creationUser;

            @Column(name = "creationDate")
            private Date creationDate;

            public String getCreationUser() 
                return creationUser;
            

            public void setCreationUser(String creationUser) 
                this.creationUser = creationUser;
            

            public Date getCreationDate() 
                return creationDate;
            

            public void setCreationDate(Date creationDate) 
                this.creationDate = creationDate;
            
        

然后您向每个被审计实体注入嵌入,在必要时覆盖列名:

        @Entity
        @Table(name="PERSON")
        public class Person extends BaseDTO

            @Embedded
            private AuditEmbeddable auditEmbeddable;

            public AuditEmbeddable getAuditEmbeddable() 
                return auditEmbeddable;
            

            public void setAuditEmbeddable(AuditEmbeddable auditEmbeddable) 
                this.auditEmbeddable = auditEmbeddable;
            
        

        @Entity
        @Table(name="ADDRESS")
        public class Address extends BaseDTO

            // lets say here you have custom names for audit fields
            @Embedded
            @AttributeOverrides(
                    @AttributeOverride(name = "creationUser", column = @Column(name = "creationUser123")),
                    @AttributeOverride(name = "creationDate", column = @Column(name = "creationDate123"))
            )
            private AuditEmbeddable auditEmbeddable;

            public AuditEmbeddable getAuditEmbeddable() 
                return auditEmbeddable;
            

            public void setAuditEmbeddable(AuditEmbeddable auditEmbeddable) 
                this.auditEmbeddable = auditEmbeddable;
            
        

最后听者可以保持你写的那样:

        public class BaseDTOEventListener 
            @PrePersist
            public void onPreInsert(BaseDTO baseDTO)
                baseDTO.setCreationUser("TEST");
                baseDTO.setCreationDate(new Date());

            
        

希望对您有所帮助。

【讨论】:

谢谢你,我最终按照艾伦·海伊在他的评论中逃避的方式去做了。我将在下面的答案中记录这一点。它与此非常相似,但不需要使用可嵌入对象。我注意到这两种解决方案可能存在的唯一问题是,如果有人在未指定要覆盖的属性的情况下扩展基类,如果持久化,它会创建与类属性同名的列。 您在 Embeddable 中定义默认列名。它们将在缺少覆盖的情况下使用。 问题是没有默认的审计列名,它们都有预先修复,但是可能通过定义一些默认的列名,这会抛出异常而不是休眠改变表,然后额外的表可以遵循此命名约定。 正确,我想你能在那个场景中做到最好。【参考方案2】:

您可以将休眠环境用于相同目的。您可以使用@Audited 进行注释。将@NotAudited 应用于您不想成为的实体

@Entity
@Table(name="PERSON")
@Audited
public class Person extends BaseDTO


@Entity
@Audited
@Table(name="ADDRESS")
public class Address extends BaseDTO

【讨论】:

【参考方案3】:

感谢 Alan 的提示,通过在每个对象上指定列名,如下所示。这工作:)

@Entity
@AttributeOverrides(@AttributeOverride(name="creationUser", column=@Column(name="PER_CREATION_USER", insertable=true, updatable=false)), 
                 @AttributeOverride(name="creationDate", column=@Column(name="PER_CREATION_DATE" insertable=true, updatable=false)
@Table(name="PERSON")
public class Person extends BaseDTO

【讨论】:

以上是关于当数据库列名称不同时,在基实体类中定义审计列?的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate:自定义实体名称

如何将同一列添加到 EF Core 中的所有实体?

实体框架 - 无效的列名称'* _ID“

springboot jpa oracle实体类中配置注解无法在数据库中生成注释的一种解决方式

实体框架 6:审计/跟踪更改

如何使用 @ManyToMany 审核 @JoinTable