使用休眠模式时,AUD表中的所有@Version字段均为空,但在实体中-表填充为okey?

时间:2019-01-18 12:30:39

标签: java spring hibernate jpa hibernate-envers

有应用程序spring + jpa + envers(hibernate) envers需要将实体的历史记录保存在特殊表中。

在保存了几次实体之后,我希望看到USER表中的填充版本字段和USER_AUT中的填充版本字段。 但是实际结果是USER表中的正确值,但添加了REV_TYPE,REV列(在字段中所有行仅是计算机的字段),而在版本列中为null。

我使用4.0.1。最终休眠状态

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-envers</artifactId>
    <version>4.0.1.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
    <version>4.0.1.Final</version>
</dependency>

但是,当我查看表时,“版本”字段中的所有值都为空

我的实体是

import lombok.*;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.envers.Audited;

import javax.persistence.*;

@Entity
@Audited
@Table(name = "User", uniqueConstraints = {
        @UniqueConstraint(columnNames = { "prKey"})})
@NoArgsConstructor(access = AccessLevel.PUBLIC)
@AllArgsConstructor
@Getter
@Setter
public class User {
    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    @Column(name = "PR_KEY", unique = true)
    private String prKey;

    @Column(name = "name", length = 100, unique = false)
    private String name;

    @Version
    private int version;

    public User(String name){
        this.name = name;
    }
}

当我使用审计获取实体时:

 public List<User> getHistory(String id) {
        AuditReader auditReader = AuditReaderFactory.get(entityManagerFactory.createEntityManager());

        List<Number> auditVersions = auditReader.getRevisions(User.class, id);
        List<User> users = auditVersions.stream().map(item -> auditReader.find(User.class, id, item.intValue())).collect(Collectors.toList());

        return extractRiskMetrics(riskMetricRecords);
    }

所以,我的坚持-配置是

@Configuration
@EnableTransactionManagement
@EnableJpaAuditing
@EnableJpaRepositories(basePackages = {"persistence"})
@ComponentScan(basePackages = {"persistence", "model"})
public class PersistenceConfig {
    private static final String PACKAGE_WITH_JPA_ENTITIES = "persistence";
    private final Logger log = Logger.getLogger(getClass());

    @Bean
    @Resource(type = DataSource.class, lookup = "jdbc/MyDatasource", name = "jdbc/MyDatasource")
    public DataSource dataSource() {
        final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
        dsLookup.setResourceRef(true);
        DataSource dataSource = dsLookup.getDataSource("java:comp/env/jdbc/MyDatasource");
        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
        entityManager.setDataSource(dataSource());
        entityManager.setPackagesToScan(PACKAGE_WITH_JPA_ENTITIES);
        entityManager.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        entityManager.setJpaProperties(getHibernateProperties());
        log.info("Entity Manager configured.");
        return entityManager;
    }

    @Bean
    public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    }

    //Set properties hibernate
    private Properties getHibernateProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.dialect", "org.hibernate.dialect.OracleDialect");
        properties.put("hibernate.show_sql", "true");
        properties.put("hibernate.hbm2ddl.auto", "none");
        properties.put("org.hibernate.envers.do_not_audit_optimistic_locking_field", false);

        properties.put("verifyServerCertificate", false);
        properties.put("useSSL", false);
        properties.put("requireSSL", false);
        properties.put("useLegacyDatetimeCode", false);
        properties.put("useUnicode", "yes");
        properties.put("characterEncoding", "UTF-8");
        properties.put("serverTimezone", "UTC");
        properties.put("useJDBCCompliantTimezoneShift", true);
        return properties;
    }
}

更新:

org.hibernate.envers.do_not_audit_optimistic_locking_field设置为false,但是版本字段仍然为空。

可能与Spring Data Jpa和Hibernate冲突相关吗?

实际上,已执行查询(已更改且f.c。)

[1/22/19 14:04:51:996 MSK] 00000096 SystemOut     O Hibernate: update UserRecord set User=?, version=? where PR_KEY=? and version=?
[1/22/19 14:04:51:998 MSK] 00000096 SystemOut     O Hibernate: select hibernate_sequence.nextval from dual
[1/22/19 14:04:52:000 MSK] 00000096 SystemOut     O Hibernate: insert into REVINFO (REVTSTMP, REV) values (?, ?)
[1/22/19 14:04:52:002 MSK] 00000096 SystemOut     O Hibernate: insert into UserRecord_AUD (REVTYPE, busId, User, UserType, someInfo, PR_KEY, REV) values (?, ?, ?, ?, ?, ?, ?)

因此,在AUD表中没有where version =?

2 个答案:

答案 0 :(得分:1)

查看配置设置org.hibernate.envers.do_not_audit_optimistic_locking_field

此配置设置控制Hibernate Envers是否将在审核模式中包括@Version带注释的字段。默认情况下,此设置设置为true,这意味着不会审核乐观锁定字段。通过将此设置为false,您将审核列的值。

关于此字段设置为false,我确实要提醒您。

如果您的应用程序执行显式的乐观锁定增量功能,那么即使在业务流程中没有更改其他数据库列的情况下,这也会导致将其他行添加到审核历史记录表中。这是因为一旦启用了@Version字段的跟踪,Hibernate Envers便会将其视为实体上的任何其他基本属性。因此,强制乐观锁递增将触发审核更改。

答案 1 :(得分:0)

您提到-REVINFO是所有审核实体的集中表。

下面介绍的基本思想是将修订版号重新映射为整数序列-因此RevNumber(2,5,31,125)将被重新映射到customVersion(1,2,3,4)

假设您拥有EntityA,并且想要为其获取所有修订版(并将每个修订版数据映射到自定义类RevisionEntityDto)。

使用Envers中的AuditReader,您可以执行以下操作:

AuditReader auditReader = AuditReaderFactory.get(entityManager);
//getRevisions() returns revisions sorted in ascending order (older revisions come first)
List<Number> entityARevisions = auditReader.getRevisions(EntityA.class, primaryKeyOfEntityA);

//entityARevisions is already sorted;
for (int customVersion = 0; customVersion < entityARevisions.size(); customVersion++) {
   createRevisionEntityDto(primaryKeyOfEntityA, auditReader, revision, customVersion);
}

private RevisionEntityDto createRevisionEntityDto(Long primaryKeyOfEntityA, AuditReader, Number revision) {
  EntityA revisionOfEntityA = auditReader.find(EntityA.class, primaryKey, revision);
  Date revDate = auditReader.getRevisionDate(revision);
  // at this point you have a single revision of EntityA
  return toRevisionEntityDto(revision, revisionOfEntityA, revDate);
}

private RevisionEntityDto toRevisionEntityDto(Number revision, EntityA revisionOfEntityA, Date revisionDate, int customVersion) {
  //here you do the mapping logic;
  RevisionEntityDto revEntityDto = new RevisionEntityDto();
  revEntityDto.setFieldA(revisionOfEntityA.getFieldA);
  revEntityDto.setDate(revisionDate); // you can use the date to sort if you want at a later stage;
  revEntityDto.setCustomVersion(customVersion);
  return revEntityDto;
}