Изменение способа доступа к отдельному полю класса предметной области с поля на геттер и обратно в Hibernate с помощью аннотации @Access

По умолчанию при выборе способа доступа к полю Hibernate ориентируется на положение обязательной аннотации @Id. Если @Id стоит над полем, то доступ (установка и чтение значений) ко всем полям будет осуществляться через механизмы рефлексии непосредственно на поле. Если аннотация над геттером, то значения полей будут устанавливаться и считываться Hibernate’ом через геттеры и сеттеры (при этом аннотация над сеттером никогда не ставится).

Изменить способ доступа к отдельному полю можно с помощью аннотации @Access.

Создадим минимальное Spring Boot приложение с поддержкой JPA/Hibernate

Создадим класс предметной области следующего содержания:

Аннотация @Id стоит над полем, а значит по умолчанию Hibernate будет читать и записывать данные полей непосредственно через сами поля.

Над полем name мы ставим аннотацию @Access с параметром AccessType.PROPERTY. Эта аннотация переопределит поведение Hibernate’а по умолчанию для этого поля и сделает так, что Hibernate перед сохранением в БД будет читать данные этого поля через геттер и в БД сохранится то, что вернёт этот геттер. А при обратной записи данных из БД в объект будет использоваться сеттер.

Соответственно, если мы посмотрим на геттер и сеттер поля name, то мы ожидаем, что в БД данные поля будут записаны преобразованными к верхнему регистру. А при отображении данных из БД в объект, к данным поля будет присоединяться восклицательный знак (хотя в БД его нет).

Над полем age мы не ставим никаких аннотаций и поэтому ожидаем, что хотя геттер и сеттер этого поля как-то манипулируют его значением, Hibernate этого никогда не увидит, так как будет работать со значением поля напрямую.

Если бы аннотация @Id стояла над геттером поля id (здесь мы его для краткости опустили), то Hibernate по умолчанию работал бы с геттерами и сеттерами всех полей. Тогда для переопределения поведения с конкретным полем, нужно было использовать аннотацию @Access с парамтером AccessType.FIELD. Таким образом параметры означают:

  • AccessType.PROPERTY — работать с геттерами/сеттерами;
  • AccessType.FIELD — работать с полем напрямую.

Проверим работу аннотации @Access. Создадим репозиторий:

Создадим тест со следующим содержанием:

В тесте мы создаём объект irina, где в поле name устанавливаем значение «Irina», а в age — 30.

При сохранении в базу для чтения поля name Hibernate использует геттер, который в нашем случае приводит сроку к верхнему регистру. При чтении поля age Hibernate будет смотреть в поле напрямую. И хотя геттер поля age тоже что-то модифицирует, Hibernate этого не увидит.

И действительно, если запустить тест, а затем посмотреть содержимое БД,то мы увидим, что всё отработало, как ожидалось:

Затем мы извлекаем данные из таблицы и проверяем их. Здесь сработают те же механизмы: при заполнении объекта данными из БД Hibernate будет использовать поле age напрямую, а в name установит значение через сеттер. Таким образом получится, что в поле age попадёт значение 30, хотя сеттер этого поля что-то модифицирует. А в name попадёт «IRINA» из БД плюс «!», добавленный сеттером, то есть будет «IRINA!».

Ассерты теста подтвердят, что всё работает именно так, как и ожидалось.