Hibernate Envers项目旨在实现对持久类的简单审计。它完全消除了审计实体的麻烦。

以下部分概述了使用自定义修订实体通过 Spring 引导配置 Envers 的高级步骤。它演示了在涉及多个数据源时如何配置 Envers。

Envers 入门

如果您使用的是 Maven,请在 pom 中添加以下 Envers 配置.xml

org.hibernate

hibernate-envers

创建自定义修订实体

在许多情况下,您需要自定义修订实体,因为默认修订实体字段可能不够。下面是创建自定义修订实体的示例。

该示例对 id 使用序列,但可以使用不同的策略来生成 id。

请注意 Envers 用于创建修订实体并将值保留在数据库中的@RevisionNumber和@RevisionEntity。

package com.example.service.audit.entity

@Table(name = "app_user_rev_entity", schema = "application")

@Entity

@RevisionEntity(UserRevisionListener.class)

public class UserRevEntity implements Serializable {

private static final long serialVersionUID = 1L;

@Id

@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_rev_generator")

@SequenceGenerator(name = "user_rev_generator", allocationSize = 10,sequenceName = "app_userrev_seq")

@RevisionNumber

private int id;

@RevisionTimestamp

@Temporal(TemporalType.TIMESTAMP)

private Date date;

@Column(name = "user_name")

private String userName;

@Column(name = "user_id")

private Long userId;

// Getters, setters, equals, hashcode ….

UserRevisionListener 是填充 UserRevEntity 的所有自定义属性的类。

下面的示例使用 Spring Boot 主体用户来获取用户名。同样,也可以填充其他属性。该类应从 Hibernate Envers 实现 RevisionListener 接口。

package com.example.service.audit.entity

public class UserRevisionListener implements RevisionListener {

/**

* @see org.hibernate.envers.RevisionListener#newRevision(java.lang.Object)

*/

@Override

public void newRevision(Object userRevision) {

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

User authenticatedUser = (User) authentication.getPrincipal();

UserRevEntity userRevEntity = (UserRevEntity) userRevision;

userRevEntity.setUserName(authenticatedUser.getUsername());

userRevEntity.setDate(Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTime());

}

配置应用程序属性

以下是一些特定于Hibernate Envers的配置。可在此处找到有关配置属性的更多详细信息。

spring.jpa.properties.org.hibernate.envers.revision_type_field_name=revision_type

spring.jpa.properties.org.hibernate.envers.revision_field_name=revision_id

spring.jpa.properties.org.hibernate.envers.modified_flag_suffix=_mod

spring.jpa.properties.org.hibernate.envers.audit_strategy=org.hibernate.envers.strategy.ValidityAuditStrategy

如果您希望Hibernate为您创建自定义修订实体和其他审计表,则可以使用spring.jpa.hibernate.ddl-auto=update,但这不是我建议用于生产环境的内容。最好在生产环境中自行创建表。

下一节是关于审计策略的。默认值很好,但有效性审计策略是一种更高级的策略。

配置要审核的实体

下面是如何审核实体类的示例。我们需要做的就是在类级别添加@Audited注释(如果必须审核所有属性)或将注释添加为单个属性级别。

@Entity

@Table(name = "app_user", schema = "application")

public class UserEntity implements Serializable {

private static final long serialVersionUID = 1L;

@Id

@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_generator")

@SequenceGenerator(name = "user_generator", allocationSize = 1, sequenceName = "application.app_userid_seq")

@Column(name = "user_id")

private Long userId;

@NotNull

@Column(name = "first_name")

@Audited

private String firstName;

使用多个数据源配置环境

在许多应用程序中,您将拥有多个数据源,因为您可能希望在不同的架构甚至不同的数据库中存储不同的数据。以下部分概述了使用多个数据源配置 Envers 所涉及的步骤。

在应用程序属性文件中配置多个数据源。

application.spring.datasource.url = jdbc:postgresql://xxxx

application.spring.datasource.username = xxx

application.spring.datasource.password=xxx application.spring.datasource.testWhileIdle = true

application.spring.datasource.validationQuery = SELECT 1

application.spring.datasource.schema=application

application.spring.datasource.driver-class-name=org.postgresql.Driver

example.spring.datasource.url = jdbc:postgresql://xxx

example.spring.datasource.username = xxx

example.spring.datasource.password=xxx

example.spring.datasource.testWhileIdle = true

example.spring.datasource.validationQuery = SELECT 1

example.spring.datasource.schema=public

example.spring.datasource.driver-class-name=org.postgresql.Driver

以下是 Spring 引导识别和加载要使用的数据源所需的配置。

@Configuration

@EnableTransactionManagement

@EnableJpaRepositories(entityManagerFactoryRef = "exampleEntityManagerFactory", transactionManagerRef = "exampleTransactionManager", basePackages = {

"com.example.service.example.entity" })

public class ExampleDatabaseConfig {

@Bean(name = "exampleDataSource")

@ConfigurationProperties(prefix = "example.spring.datasource")

public DataSource dataSource() {

return DataSourceBuilder.create().build();

}

@Bean(name = "exampleEntityManagerFactory")

public LocalContainerEntityManagerFactoryBean exampleEntityManagerFactory(EntityManagerFactoryBuilder builder,

@Qualifier("exampleDataSource") DataSource dataSource) {

return builder.dataSource(dataSource).packages("com.example.service.audit.entity").persistenceUnit("example").build();

}

@Bean(name = "exampleTransactionManager")

public PlatformTransactionManager exampleTransactionManager(

@Qualifier("exampleEntityManagerFactory") EntityManagerFactory exampleEntityManagerFactory) {

return new JpaTransactionManager(exampleEntityManagerFactory);

}

}

@Configuration

@EnableTransactionManagement

@EnableJpaRepositories(entityManagerFactoryRef = "applicationEntityManagerFactory", transactionManagerRef = "applicationTransactionManager", basePackages = {

"com.application.service.example.entity "

})

public class ApplicationDatabaseConfig {

@Primary

@Bean(name = "applicationDataSource")

@ConfigurationProperties(prefix = "application.spring.datasource")

public DataSource dataSource() {

return DataSourceBuilder.create().build();

}

@Primary

@Bean(name = "applicationEntityManagerFactory")

public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder,

@Qualifier("applicationDataSource") DataSource dataSource) {

return builder.dataSource(dataSource)

.packages("com.example.service.audit.entity")

.persistenceUnit("application").build();

}

@Primary

@Bean(name = "applicationTransactionManager")

public PlatformTransactionManager transactionManager(

@Qualifier("applicationEntityManagerFactory") EntityManagerFactory entityManagerFactory) {

return new JpaTransactionManager(entityManagerFactory);

}

}

在上述两种配置中,要记住的重要部分是添加实现 Envers 自定义修订实体类的包 — 在本例中,将“com.example.service.audit.entity”添加到两个数据源配置文件中,以便 Entity Scan by Envers。

如果您错过在其中一个配置文件中添加包,则在尝试保留正在审核的实体时,您可能会突然开始看到以下错误。代码可能未更改,但您可能会开始看到失败。

这取决于在引导时首先加载数据源的类加载器。如果最后加载缺少实体扫描包的数据源,您将看到以下错误。

这可能会导致在调试问题时浪费大量时间,并且您可能会被误导创建缺少的hibernate_sequence,这是Hibernate Envers使用的通用序列。

Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet

at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:106)

at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)

at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:109)

at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:95)

at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:79)

at org.hibernate.id.enhanced.SequenceStructure$1.getNextValue(SequenceStructure.java:96)

at org.hibernate.id.enhanced.NoopOptimizer.generate(NoopOptimizer.java:40)

at org.hibernate.id.enhanced.SequenceStyleGenerator.generate(SequenceStyleGenerator.java:412)

at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:101)

at org.hibernate.jpa.event.internal.core.JpaSaveEventListener.saveWithGeneratedId(JpaSaveEventListener.java:56)

at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:192)

at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:38)

at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:177)

at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:32)

at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)

at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:679)

at org.hibernate.internal.SessionImpl.save(SessionImpl.java:671)

at org.hibernate.envers.internal.revisioninfo.DefaultRevisionInfoGenerator.saveRevisionData(DefaultRevisionInfoGenerator.java:75)

at org.hibernate.envers.internal.synchronization.AuditProcess.getCurrentRevisionData(AuditProcess.java:119)

at org.hibernate.envers.internal.synchronization.AuditProcess.executeInSession(AuditProcess.java:96)

... 141 common frames omitted

Caused by: org.postgresql.util.PSQLException: ERROR: relation "hibernate_sequence" does not exist

Position: 17

结论

Hibernate Envers是Hibernate提供的成熟审计模块。它是高度可配置的,节省了构建审计框架的工作量。

但是,使用多个数据源时,请记住在两个数据源中配置实体扫描包,否则会误导您并占用您调试问题的大量时间,尤其是当工作代码突然开始失败时。

推荐阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: