有应用程序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 =?
答案 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;
}