Все поля @Version в таблице AUD являются нулевыми при использовании hibernate-envers, но в сущности - таблица заполнена, хорошо?

Есть приложение spring+jpa+envers(hibernate) envers необходимо сохранять историю сущностей в специальной таблице.

После того, как я несколько раз сохранил свой объект, я ожидал увидеть заполненное поле версии в таблице USER и заполненное поле версии в USER_AUT. Но фактический результат - это правильное значение в таблице USER, но добавлены столбцы REV_TYPE, REV (в поле только счетчики для всех строк) и нуль в столбцах версии.

Я использую 4.0.1.Final hibernate

<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 - envers?

Фактически, выполненный запрос (измененный и 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=?

На какой стол смотреть? user или user_AUD?

Ralph 18.01.2019 18:44

@Ralph Я смотрю в таблицу user_AUD, потому что только версия пользовательской таблицы в порядке (увеличивается)

Roberto 20.01.2019 14:37

Столбцы версии не должны подвергаться аудиту по умолчанию. Почему вы хотите проводить аудит столбцов «Версия»? если ваша сущность аннотирована с помощью Audited, при изменении любого поля в таблице *_AUD будет запись аудита. У вас также будет запись в таблице REVINFO, где столбец REV будет в основном представлять «версии» вашего объекта (каждый «моментальный снимок» вашего аудируемого объекта — это отдельная версия).

hovanessyan 23.01.2019 10:54

@VladislavOsipenkov также вы говорите в заголовке, что все столбцы «Версия» пусты, но затем вы упоминаете, что в таблице «ПОЛЬЗОВАТЕЛЬ» столбец «Версия» фактически заполняется, как и ожидалось, но есть проблема со столбцом «Версия» в таблице AUD (где вам на самом деле не нужна версия). столбец).

hovanessyan 23.01.2019 11:01

@hovanessyan спасибо, обновленный заголовок, проблема только в таблице AUD. Мне нужна заполненная версия в AUD, потому что я предоставляю операцию getHistrory и возвращаю все изменения объекта с заполненным столбцом версии.

Roberto 23.01.2019 11:13

Вы можете использовать номер версии из таблицы REVINFO (столбец REV). Версия — это деталь реализации оптимистической блокировки. История аудита, представленная в отчетах/интерфейсе для некоторых пользователей, не должна включать столбец «Версия». В общем случае REV и Version должны иметь одинаковые значения (например, для REV 1 версия равна 1 и т. д.). Я хочу сказать, что столбец REV является фактическим столбцом, который нужно использовать, чтобы показать, что изменилось в Entity при использовании Envers.

hovanessyan 23.01.2019 11:18

@hovanessyan в моем случае, если я добавил 3 раза первый объект (изменил значение) и 2 раза второй объект. REV будет 5 для второго объекта, но должно быть всего 2. Они не равны

Roberto 23.01.2019 11:26

@VladislavOsipenkov да, я понимаю вашу точку зрения. Я добавил пример кода в ответ на это. Тем не менее, я думаю, что вы не должны выставлять поле «Версия» - возможно, выставляете REV как есть или переназначаете на последовательность 1,2,3.

hovanessyan 24.01.2019 09:15
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
10
8
1 729
2

Ответы 2

Посмотрите на настройку конфигурации org.hibernate.envers.do_not_audit_optimistic_locking_field.

Этот параметр конфигурации определяет, будет ли Hibernate Envers включать аннотированное поле @Version в схему аудита или нет. По умолчанию для параметра установлено значение true, что означает, что поле оптимистичной блокировки не будет проверяться. Установив значение false, вы проведете аудит значения столбца.

Я хочу предостеречь вас от установки в этом поле значения false.

Если ваше приложение выполняет явную функцию приращения оптимистической блокировки, это приведет к добавлению дополнительных строк в таблицу журнала аудита, даже если ни один из других столбцов базы данных не будет изменен в рамках вашего бизнес-процесса. Это связано с тем, что после включения отслеживания полей @Version Hibernate Envers просто обрабатывает их как любой другой базовый атрибут объекта. Следовательно, принудительное увеличение оптимистической блокировки вызовет изменение аудита.

обновленная проблема с этим параметром, но проблема все та же

Roberto 22.01.2019 08:18

Поскольку ваша конфигурация все еще использует hibernate.hbm2ddl.auto=none. Вы в основном говорите, что если есть изменения схемы, игнорируйте их. Пока вы разрабатываете (особенно с Envers), вы должны оставить это как минимум как update, пока вы не будете довольны тем, что у вас есть, и что оно стабильно; в противном случае вам нужно будет вручную внести необходимые изменения. В Hibernate 6 мы представляем новую функцию, в которой вы можете сказать, что Envers должен быть update, а сама ORM, возможно, должна быть none, чтобы лучше контролировать, как применяются изменения схемы.

Naros 24.01.2019 08:49

Как вы упомянули, REVINFO — это централизованная таблица для всех проверяемых объектов.

Основная идея, представленная ниже, состоит в том, чтобы переназначить номера ревизий на целочисленную последовательность, поэтому RevNumber(2,5,31,125) будет переназначен на customVersion(1,2,3,4)

Допустим, у вас есть EntityA, и вы хотите получить для него все ревизии (и сопоставить данные каждой ревизии с пользовательским классом RevisionEntityDto).

Используя AuditReader от Envers, вы можете сделать что-то вроде:

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;
}

Это не имеет отношения к поставленному вопросу.

Naros 25.01.2019 07:57

Другие вопросы по теме