Hibernate Envers doesn't write anything in audit tables with SpringMVC config

I am trying to use Enverse to check tables when saving, updating or deleting a record in my db.

Envers is configured as follows:

pom.xml

        <!-- Spring -->
        <org.springframework-version>4.1.6.RELEASE</org.springframework-version>

        <!-- Hibernate  -->
        <hibernate.version>4.3.5.Final</hibernate.version>
[...]

    <!-- Hibernate -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-envers</artifactId>
            <version>${hibernate.version}</version>
        </dependency>

      

My jpa-tx-config.xml

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
    </property>
    <property name="packagesToScan" value="my.domain"/>

    <property name="persistenceUnitName" value="persistenceUnit"/>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
            <prop key="hibernate.hbm2ddl.auto">update</prop> 
            <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
            <prop key="hibernate.connection.charSet">UTF-8</prop>

            <prop key="hibernate.max_fetch_depth">3</prop>
            <prop key="hibernate.jdbc.fetch_size">50</prop>
            <prop key="hibernate.jdbc.batch_size">20</prop>
            <prop key="hibernate.show_sql">false</prop>
            <prop key="hibernate.format_sql">true</prop>

            <prop key="org.hibernate.envers.audit_table_suffix">_H</prop>
            <prop key="org.hibernate.envers.revision_field_name">AUDIT_REVISION</prop>
            <prop key="org.hibernate.envers.revision_type_field_name">ACTION_TYPE</prop>
            <prop key="org.hibernate.envers.audit_strategy">org.hibernate.envers.strategy.ValidityAuditStrategy</prop>
            <prop key="org.hibernate.envers.audit_strategy_validity_end_rev_field_name">AUDIT_REVISION_END</prop>
            <prop key="org.hibernate.envers.audit_strategy_validity_store_revend_timestamp">True</prop>
            <prop key="org.hibernate.envers.audit_strategy_validity_revend_timestamp_field_name">AUDIT_REVISION_END_TS</prop>               
            <prop key="jadira.usertype.databaseZone">jvm</prop>
        </props>
    </property>
</bean>

      

Each class of my domains has an annotation @Audited

, but these fields are always in the database null

.

I don't understand what is wrong, any suggestion?

EDIT

In response to comment: Each entity class extends an abstract domain that implements the Auditable method. Follow the code:

   @SuppressWarnings("serial")
@MappedSuperclass
@Audited
public abstract class AbstractDomain implements Auditable<String, Long>, Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Version
    private int version;

    @JsonIgnore
    @Column(updatable=false)
    private String createdBy;

    @Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
    @DateTimeFormat(iso=ISO.DATE_TIME)
    @JsonIgnore
    @Column(updatable=false)
    private DateTime createdDate;

    @JsonIgnore
    private String lastModifiedBy;

    @Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
    @DateTimeFormat(iso=ISO.DATE_TIME)
    @JsonIgnore
    private DateTime lastModifiedDate;

      

The only field changed is the version, the rest of the fields are ignored.

+3


source to share


1 answer


From what I can see, you don't need it at all envers

. Envers is more about keeping revisions

your data (which can be used for auditing purposes, of course), but if you just want to have these fields createdBy

, createdDate

, lastModifiedBy

, lastModifiedDate

, easier way.

Since you are already using spring-data-jpa

, I suggest you include JPA auditing

with annotation @EnableJpaAuditing

.

Then you can drop @Audited

from your class AbstractDomain

and add @EntityListeners(AuditingEntityListener.class)

which will make hibernate save the audit information every time the entity is saved.

Last but not least, define the AuditorAware

bean. He will tell you exactly who is manipulating this object at every moment, so the auditor must know what data should be set in the createdBy

and fields lastModifiedBy

. It's pretty simple, here's an example:

@Bean
AuditorAware auditor() {
    // return () -> "system";  // Fixed principal
    // and for spring-security
    return () -> {
        Authentication authentication = SecurityContextHolder.getContext()
                .getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            return null;
        }
        return authentication.getPrincipal().toString();
    }
}

      



And here it is.

Here's a complete example:

@SpringBootApplication
@EnableJpaAuditing
public class So45347635Application {
    public static void main(String[] args) { SpringApplication.run(So45347635Application.class, args); }

    @MappedSuperclass
    @EntityListeners(AuditingEntityListener.class)
    public static abstract class AbstractDomain extends AbstractPersistable<Long> implements Auditable<String, Long> {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        @Version
        private int version;

        @Column(updatable = false)
        private String createdBy;

        @Temporal(TemporalType.TIMESTAMP)
        @Column(updatable = false)
        private Date createdDate;

        private String lastModifiedBy;

        @Temporal(TemporalType.TIMESTAMP)
        @Column(insertable = false)
        private Date lastModifiedDate;

        @Override
        public String getCreatedBy() {
            return this.createdBy;
        }

        @Override
        public void setCreatedBy(String createdBy) {
            this.createdBy = createdBy;
        }

        @Override
        public DateTime getCreatedDate() {
            return null == this.createdDate ? null : new DateTime(this.createdDate);
        }

        @Override
        public void setCreatedDate(DateTime createdDate) {
            this.createdDate = createdDate.toDate();
        }

        @Override
        public String getLastModifiedBy() {
            return this.lastModifiedBy;
        }

        @Override
        public void setLastModifiedBy(String lastModifiedBy) {
            this.lastModifiedBy = lastModifiedBy;
        }

        @Override
        public DateTime getLastModifiedDate() {
            return null == this.lastModifiedDate ? null : new DateTime(this.lastModifiedDate);
        }

        @Override
        public void setLastModifiedDate(DateTime lastModifiedDate) {
            this.lastModifiedDate = lastModifiedDate.toDate();
        }
    }

    @Entity
    @Table(name = "users")
    public static class User extends AbstractDomain {
        private String username = "anonymous";

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        @Override
        public String toString() {
            return String.format("User{id=%d, createdBy='%s', createdDate=%s, lastModifiedBy='%s', lastModifiedDate=%s, username='%s'}",
                    getId(), getCreatedBy(), getCreatedDate(), getLastModifiedBy(), getLastModifiedDate(), username);
        }
    }

    @Bean
    AuditorAware auditor() { return () -> "system"; }

    @Bean
    CommandLineRunner start(UserRepository userRepository, TransactionTemplate tx) {
        return args -> tx.execute(ctx -> {
            final User user = userRepository.save(new User());
            user.setUsername("admin");
            System.out.println(">>>> " + userRepository.save(user));
            return null;
        });
    }
}

interface UserRepository extends CrudRepository<So45347635Application.User, Long> {
}

      

Output:

create table users (id bigint generated by default as identity, created_by varchar(255), created_date timestamp, last_modified_by varchar(255), last_modified_date timestamp, version integer not null, username varchar(255), primary key (id));
insert into users (id, created_by, created_date, last_modified_by, version, username) values (null, 'system', '28-Jul-17', 'system', 0, 'anonymous');
update users set last_modified_by='system', last_modified_date='28-Jul-17', version=1, username='admin' where id=1 and version=0;

      

+2


source







All Articles