使用Hibernate Envers审计可嵌入实体

时间:2019-07-05 11:09:22

标签: java hibernate spring-boot jpa hibernate-envers

我正在尝试使用Hibernate Envers向存储实体关系的表中添加审计支持。

我正在使用Spring Boot 2.1.5和Hibernate 5.3.10。

下面是我的课程:

AuditEntity

import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
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.*;

@Getter
@Setter
@Audited
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class AuditEntity {
    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(
            name = "uuid",
            strategy = "org.hibernate.id.UUIDGenerator",
            parameters = {
                    @Parameter(
                            name = "uuid_gen_strategy_class",
                            value = "org.hibernate.id.uuid.CustomVersionOneStrategy"
                    )
            }
    )
    private String id;

    @Column(name = "created_date", updatable = false, nullable = false)
    @CreatedDate
    private long createdDate;

    @Column(name = "modified_date")
    @LastModifiedDate
    private long modifiedDate;

    @Column(name = "created_by")
    @CreatedBy
    private String createdBy;

    @Column(name = "modified_by")
    @LastModifiedBy
    private String modifiedBy;

    @Override
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        }

        if (!(o instanceof AuditEntity)) {
            return false;
        }

        AuditEntity r = (AuditEntity) o;

        return this.getId().equals(r.getId());
    }
}

角色

import lombok.Getter;
import lombok.Setter;
import org.hibernate.envers.Audited;
import org.hibernate.envers.NotAudited;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Getter
@Setter
@Audited
@Entity
@Table(name = "role")
public class Role extends AuditEntity {
    @Column(name = "name")
    private String name;

    @NotAudited
    private String dontAudit;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.role")
    private Set<UserRole> userRoles = new HashSet<>(0);
}

用户

import lombok.Getter;
import lombok.Setter;
import org.hibernate.envers.Audited;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.util.HashSet;
import java.util.Set;

@Getter
@Setter
@Audited
@Entity
@Table(name = "user")
public class User extends AuditEntity {
    private String sso;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.user")
    private Set<UserRole> userRoles = new HashSet<>(0);
}

UserRole

import lombok.Getter;
import lombok.Setter;
import org.hibernate.envers.Audited;

import javax.persistence.*;

@Getter
@Setter
@Audited
@Entity
@Table(name = "user_roles")
@AssociationOverrides({
        @AssociationOverride(name = "pk.user", joinColumns = @JoinColumn(name = "USER_ID")),
        @AssociationOverride(name = "pk.role", joinColumns = @JoinColumn(name = "ROLE_ID"))
})
public class UserRole extends AuditEntity {
    @EmbeddedId
    private UserRoleId pk = new UserRoleId();

    public User getUser() {
        return getPk().getUser();
    }

    public void setUser(User user) {
        getPk().setUser(user);
    }

    public Role getRole() {
        return getPk().getRole();
    }

    public void setRole(Role role) {
        getPk().setRole(role);
    }
}

UserRoleId

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.Embeddable;
import javax.persistence.ManyToOne;
import java.io.Serializable;

@Getter
@Setter
@Embeddable
@EqualsAndHashCode
public class UserRoleId implements Serializable {
    @ManyToOne
    private User user;
    @ManyToOne
    private Role role;
}

当我运行应用程序时,它将引发以下错误。但是,当我不从AuditEntity扩展UserRole时,应用程序将成功启动,但user_role表上没有审核列。

错误

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.MappingException: Type not supported: org.hibernate.type.ComponentType
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1778) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1105) ~[spring-context-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) ~[spring-context-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142) ~[spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) [spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) [spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) [spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) [spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) [spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
    at com.ercancelik.hibernate.envers.DemoApplication.main(DemoApplication.java:11) [classes/:na]
Caused by: org.hibernate.MappingException: Type not supported: org.hibernate.type.ComponentType
    at org.hibernate.envers.configuration.internal.metadata.IdMetadataGenerator.addIdProperties(IdMetadataGenerator.java:120) ~[hibernate-envers-5.3.10.Final.jar:5.3.10.Final]
    at org.hibernate.envers.configuration.internal.metadata.IdMetadataGenerator.addId(IdMetadataGenerator.java:200) ~[hibernate-envers-5.3.10.Final.jar:5.3.10.Final]
    at org.hibernate.envers.configuration.internal.metadata.AuditMetadataGenerator.generateFirstPass(AuditMetadataGenerator.java:642) ~[hibernate-envers-5.3.10.Final.jar:5.3.10.Final]
    at org.hibernate.envers.configuration.internal.EntitiesConfigurator.configure(EntitiesConfigurator.java:95) ~[hibernate-envers-5.3.10.Final.jar:5.3.10.Final]
    at org.hibernate.envers.boot.internal.EnversServiceImpl.doInitialize(EnversServiceImpl.java:154) ~[hibernate-envers-5.3.10.Final.jar:5.3.10.Final]
    at org.hibernate.envers.boot.internal.EnversServiceImpl.initialize(EnversServiceImpl.java:118) ~[hibernate-envers-5.3.10.Final.jar:5.3.10.Final]
    at org.hibernate.envers.boot.internal.AdditionalJaxbMappingProducerImpl.produceAdditionalMappings(AdditionalJaxbMappingProducerImpl.java:101) ~[hibernate-envers-5.3.10.Final.jar:5.3.10.Final]
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:297) ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:904) ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:935) ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]
    at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:57) ~[spring-orm-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365) ~[spring-orm-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:390) ~[spring-orm-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:377) ~[spring-orm-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341) ~[spring-orm-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1837) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1774) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    ... 16 common frames omitted

1 个答案:

答案 0 :(得分:1)

问题特别在于您如何映射UserRole。如果您看起来已经定义了UserRole来包含@EmbeddedId,但是您还扩展了AbstractEntity类,该类还基于uuid定义了自己的标识符。您根本无法将这两个问题混合在一起。

说实话,我很惊讶您提供的映射实际上没有引发ORM异常,而没有完全进入Envers,因为ORM最初必须将它们解析到最终应该抛出的位置有关如何解析标识符的例外。

作为旁注,除非有特定的业务目的要捕获在主数据表中创建或修改实体的人员/时间,否则您实际上可以将其捕获为Envers修订版实体的一部分,而每次交易中审核数据,这将减少每条记录存储的数据。

看看Envers提供的自定义修订版实体;您可以使用spring-bean钩住那里,并结合使用最新的Spring Framework和Hibernate 5.3+来收集所需的任何信息。