3. 参考文档
3.1. 什么是 Spring Data Envers?
Spring Data Envers 在 Spring Data JPA 的存储库中提供典型的 Envers 查询。 它与其他 Spring Data 模块的不同之处在于,它总是与另一个 Spring Data 模块结合使用:Spring Data JPA。
3.2. 什么是 Envers?
Envers 是一个 Hibernate 模块,用于向 JPA 实体添加审计功能。 本文档假定您熟悉 Envers,就像 Spring Data Envers 依赖于正确配置 Envers 一样。
3.3. 配置
作为使用 Spring Data Envers 的起点,您需要一个在类路径上具有 Spring Data JPA 的项目,以及一个额外的spring-data-envers
Dependency:
<dependencies>
<!-- other dependency elements omitted -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-envers</artifactId>
<version>2.7.18</version>
</dependency>
</dependencies>
这也带来了hibernate-envers
作为临时依赖项添加到项目中。
要启用 Spring Data Envers 和 Spring Data JPA,我们需要配置两个 bean 和一个特殊的repositoryFactoryBeanClass
:
@Configuration
@EnableEnversRepositories
@EnableTransactionManagement
public class EnversDemoConfiguration {
@Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.HSQL).build();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("example.springdata.jpa.envers");
factory.setDataSource(dataSource());
return factory;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
}
要实际使用 Spring Data Envers,请将一个或多个存储库放入RevisionRepository
通过将其添加为扩展接口:
interface PersonRepository
extends CrudRepository<Person, Long>,
RevisionRepository<Person, Long, Long> (1)
{}
1 | 第一个类型参数 (Person ) 表示实体类型,第二个 (Long ) 表示 id 属性的类型,最后一个 (Long ) 是修订版号的类型。
对于默认配置中的 Envers,修订号参数应为Integer 或Long . |
该存储库的实体必须是启用了 Envers 审计的实体(即,它必须具有@Audited
annotation):
@Entity
@Audited
class Person {
@Id @GeneratedValue
Long id;
String name;
@Version Long version;
}
3.4. 使用
您现在可以使用RevisionRepository
查询实体的修订版本,如下面的测试用例所示:
@ExtendWith(SpringExtension.class)
@Import(EnversDemoConfiguration.class) (1)
class EnversIntegrationTests {
final PersonRepository repository;
final TransactionTemplate tx;
EnversIntegrationTests(@Autowired PersonRepository repository, @Autowired PlatformTransactionManager tm) {
this.repository = repository;
this.tx = new TransactionTemplate(tm);
}
@Test
void testRepository() {
Person updated = preparePersonHistory();
Revisions<Long, Person> revisions = repository.findRevisions(updated.id);
Iterator<Revision<Long, Person>> revisionIterator = revisions.iterator();
checkNextRevision(revisionIterator, "John", RevisionType.INSERT);
checkNextRevision(revisionIterator, "Jonny", RevisionType.UPDATE);
checkNextRevision(revisionIterator, null, RevisionType.DELETE);
assertThat(revisionIterator.hasNext()).isFalse();
}
/**
* Checks that the next element in the iterator is a Revision entry referencing a Person
* with the given name after whatever change brought that Revision into existence.
* <p>
* As a side effect the Iterator gets advanced by one element.
*
* @param revisionIterator the iterator to be tested.
* @param name the expected name of the Person referenced by the Revision.
* @param revisionType the type of the revision denoting if it represents an insert, update or delete.
*/
private void checkNextRevision(Iterator<Revision<Long, Person>> revisionIterator, String name,
RevisionType revisionType) {
assertThat(revisionIterator.hasNext()).isTrue();
Revision<Long, Person> revision = revisionIterator.next();
assertThat(revision.getEntity().name).isEqualTo(name);
assertThat(revision.getMetadata().getRevisionType()).isEqualTo(revisionType);
}
/**
* Creates a Person with a couple of changes so it has a non-trivial revision history.
* @return the created Person.
*/
private Person preparePersonHistory() {
Person john = new Person();
john.setName("John");
// create
Person saved = tx.execute(__ -> repository.save(john));
assertThat(saved).isNotNull();
saved.setName("Jonny");
// update
Person updated = tx.execute(__ -> repository.save(saved));
assertThat(updated).isNotNull();
// delete
tx.executeWithoutResult(__ -> repository.delete(updated));
return updated;
}
}
1 | 这引用了前面介绍的 application context configuration (在 Configuration 部分中)。 |
3.5. 更多资源
您可以在 Spring Data Examples 存储库中下载 Spring Data Envers 示例,并尝试一下该库的工作原理。
您还应该查看Javadoc 的RevisionRepository
和相关类。