10 Октября 2019

Hibernate Envers и JPA, особенности использования

Hibernate Envers — очень старый проект. Так получилось, что всякие вещи вроде аудировнаия изменений, мне приходилось выполнять триггерами на уровне БД с подхимичиванием переменных сессий и Envers обходил меня сторной.

Выглядит проект неплохо и легко встраивается в spring-boot приложение.

Все зависимости уже включены в BOM и достаточно только включить модуль в проект, спринг сам возьмет нужную версию.

 <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-envers</artifactId>
 </dependency>

После подключения я добавил аннотацию @Audited своему классу, для которого мне нужно было хранить ревизиии. А также AuditingEntityListener чтобы доставать из контекста безопасности Spring данные об авторизации: мне нужно знать кто менял карточку.

Envers, являясь модулем, представляет механизм в котором вы сами должны описать откуда береде данные об авторизации.

@Audited
@EntityListeners(AuditingEntityListener.class)
@NamedEntityGraphs({
    @NamedEntityGraph(
        name = "cardWithLog",
        attributeNodes = {
            @NamedAttributeNode("ccLog")
        }
    )
})
public class Card implements Serializable {

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

    @Column(name = "f_name")
    private String fName;
    
     @Column(name = "reg_doc_date")   
     private LocalDate regDocDate;
    
}

Генератор схемы Hibernate я стараюсь не использовать, были у меня моменты когда я продуктивную базу дропал, думаю что у всех они были, поэтому использую Liquibase. Я счастливо скопировл табличку, была “card” стала “card_aud” и при первом же старте получил проблемы.

Исторически всегда настороженно даю оценку, когда нужно подключать стороннюю открытую библиотеку с которой у меня не было большого опыта работы. Дело в том что в opensource проектах есть баги, которые очень не спешат исправлять, они конечно есть в bug report и на StackOverflow это записано как “known bug”.

Я вот эти “known bug” очень не люблю. С одной стороны мы заплатили целых 0 рублей за библиотеку, а с другой обидно когда комментарий вот такой.

For 4.3 there is a work-around as stated above by using a Type rather than a AttributeConverter

То есть вроде как вместо аннотации @Converter мы можем объявить custom type и все у нас заработает, но с другой надо понимать, как применяется Type. Если вы его добавите через @Type для своего поля даже написав конвертер, Envers как было на него все равно так и будет.

То есть от @Converter(autoApply = true) придется отказаться, в версии 4.3 Envers не умеет с ним работать и будет кидать вам org.hibernate.MappingException для не стандартных типов.

  @Converter(autoApply = true)
    public static class LocalDateConverter implements AttributeConverter<LocalDate, java.sql.Date> {

        @Override
        public java.sql.Date convertToDatabaseColumn(LocalDate date) {
            return date == null ? null : java.sql.Date.valueOf(date);
        }

        @Override
        public LocalDate convertToEntityAttribute(java.sql.Date date) {
            return date == null ? null : date.toLocalDate();
        }
    }

Как починить напишу в продолжении статьи про Envers.