it-roy-ru.com

Hibernate Аннотации - Что лучше, доступ к полю или собственности?

Этот вопрос в некоторой степени связан с Вопрос размещения аннотации в спящем режиме .

Но я хочу знать, что лучше? Доступ через свойства или доступ через поля? Каковы преимущества и недостатки каждого?

119
Martin OConnor

Я предпочитаю средства доступа, так как я могу добавить некоторую бизнес-логику в свои средства доступа всякий раз, когда мне нужно .... Вот пример:

@Entity
public class Person {

  @Column("nickName")
  public String getNickName(){
     if(this.name != null) return generateFunnyNick(this.name);
     else return "John Doe";
  }
}

Кроме того, если вы добавите еще одну библиотеку (например, какую-нибудь JSON-конвертирующую библиотеку или BeanMapper или Dozer или другую библиотеку для отображения/клонирования bean-компонентов, основанную на свойствах getter/setter), у вас будет гарантия, что библиотека синхронизируется с постоянством. менеджер (оба используют геттер/сеттер).

31
Miguel Ping

Есть аргументы для обоих, но большинство из них проистекают из определенных пользовательских требований «что делать, если вам нужно добавить логику для», или «xxxx нарушает инкапсуляцию». Тем не менее, никто не прокомментировал теорию и не привел аргументированную аргументацию. 

Что на самом деле делает Hibernate/JPA, когда сохраняет объект - ну, он сохраняет состояние объекта. Это означает, что хранить его так, чтобы его можно было легко воспроизвести. 

Что такое инкапсуляция? Инкапсуляция означает инкапсуляцию данных (или состояния) с помощью интерфейса, который приложение/клиент может использовать для безопасного доступа к данным - поддерживая их согласованность и действительность. 

Думайте об этом как MS Word. MS Word поддерживает модель документа в памяти - документы STATE. Он представляет интерфейс, который пользователь может использовать для изменения документа - набор кнопок, инструментов, команд клавиатуры и т.д. Однако, когда вы решите сохранить (сохранить) этот документ, он сохраняет внутреннее состояние, а не набор нажатий клавиш и щелчки мыши, используемые для его генерации. 

Сохранение внутреннего состояния объекта НЕ нарушает инкапсуляцию - иначе вы не совсем понимаете, что такое инкапсуляция и почему она существует. Это похоже на сериализацию объектов. 

По этой причине в НАИБОЛЕЕ СЛУЧАЯХ уместно сохранять ПОЛЯ, а не АКСЕССУАРЫ. Это означает, что объект может быть точно воссоздан из базы данных точно так, как он был сохранен. Он не должен нуждаться в проверке, потому что это было сделано на оригинале, когда он был создан, и до того, как он был сохранен в базе данных (если, не дай бог, вы храните недействительные данные в БД !!!!). Кроме того, не должно быть необходимости вычислять значения, так как они уже были вычислены до сохранения объекта. Объект должен выглядеть так же, как и до сохранения. Фактически, добавляя дополнительный материал в методы получения/установки, вы фактически увеличиваете риск того, что вы воссоздаете что-то, что не является точной копией оригинала. 

Конечно, эта функциональность была добавлена ​​по причине. Могут быть некоторые допустимые варианты использования для сохранения методов доступа, однако они обычно бывают редкими. Примером может быть то, что вы хотите избежать сохранения рассчитанного значения, хотя вы можете задать вопрос, почему вы не вычисляете его по требованию в получателе значения или лениво инициализируете его в получателе. Лично я не могу придумать ни одного хорошего варианта использования, и ни один из ответов здесь действительно не дает ответа «Разработка программного обеспечения». 

230
Martin

Я предпочитаю доступ к полю, потому что таким образом я не обязан предоставлять getter/setter для каждого свойства.

Быстрый опрос через Google показывает, что доступ к полям составляет большинство (например, http://Java.dzone.com/tips/12-feb-jpa-20-why-accesstype ).

Я считаю, что полевой доступ - это идиома, рекомендованная Spring, но я не могу найти ссылку, подтверждающую это.

Существует связанный SO вопрос , который пытался измерить производительность и пришел к выводу, что «нет разницы».

76
duffymo

Вот ситуация, когда вы должны использовать средства доступа к свойствам. Представьте, что у вас есть абстрактный класс GENERIC с большим количеством реализаций для наследования в 8 конкретных подклассов:

public abstract class Foo<T extends Bar> {

    T oneThing;
    T anotherThing;

    // getters and setters ommited for brevity

    // Lots and lots of implementation regarding oneThing and anotherThing here
 }

Теперь, как именно вы должны аннотировать этот класс? Ответ: ВЫ НЕ МОЖЕТЕ аннотировать его с помощью доступа к полю или свойству, потому что вы не можете указать целевую сущность на этом этапе. Вы должны аннотировать конкретные реализации. Но поскольку постоянные свойства объявлены в этом суперклассе, вы ДОЛЖНЫ использовать доступ к свойствам в подклассах.

Доступ к полю не является опцией в приложении с абстрактными обобщенными суперклассами.

37
HDave

Я склонен предпочесть и использовать методы доступа к собственности:

  • Я могу добавить логику, если возникнет такая необходимость (как указано в принятом ответе).
  • это позволяет мне вызывать foo.getId()без инициализации прокси (важно при использовании Hibernate, пока HHH-3718 не будет решен).

Минус:

  • это делает код менее читабельным, вам нужно, например, просмотреть весь класс, чтобы увидеть, есть ли там @Transient.
33
Pascal Thivent

Это действительно зависит от конкретного случая - оба варианта доступны по причине. ИМО сводится к трем случаям:

  1. сеттер имеет некоторую логику, которая не должна выполняться во время загрузки экземпляра из базы данных; например, некоторая проверка значения происходит в установщике, однако данные, поступающие из базы данных, должны быть действительными (в противном случае они туда не попадут (:); в этом случае доступ к полю является наиболее подходящим;
  2. сеттер имеет некоторую логику, которая всегда должна вызываться, даже во время загрузки экземпляра из БД; например, инициализируемое свойство используется при вычислении некоторого вычисляемого поля (например, свойство - денежная сумма, вычисляемое свойство - всего несколько денежных свойств одного и того же экземпляра); в этом случае доступ к собственности не требуется.
  3. Ни один из вышеперечисленных случаев - тогда оба варианта применимы, просто оставайтесь последовательными (например, если доступ к полю является выбором в этой ситуации, используйте его все время в аналогичной ситуации).
13
01es

Я бы настоятельно рекомендовал доступ к полям, а НЕ аннотации для геттеров (доступ к свойствам), если вы хотите сделать в установщиках нечто большее, чем просто установка значения (например, шифрование или вычисление). 

Проблема с доступом к свойству заключается в том, что сеттеры также вызываются при загрузке объекта. У меня это работало много месяцев, пока мы не захотели ввести шифрование. В нашем случае мы хотели зашифровать поле в установщике и расшифровать его в получателе. Теперь проблема с доступом к свойству заключалась в том, что когда Hibernate загружал объект, он также вызывал установщик для заполнения поля и, таким образом, снова шифровал зашифрованное значение. В этом посте также упоминается следующее: Java Hibernate: поведение функции набора различных свойств зависит от того, кто ее вызывает

Это вызывало у меня головную боль, пока я не вспомнил разницу между доступом к полю и доступом к собственности. Теперь я переместил все свои аннотации из доступа к свойству в доступ к полю, и теперь он работает нормально. 

12
Christoph

Я предпочитаю использовать полевой доступ по следующим причинам:

  1. Свойство access может привести к очень неприятным ошибкам при реализации equals/hashCode и ссылки на поля напрямую (в отличие от их получателей). Это связано с тем, что прокси инициализируется только при обращении к геттерам, а прямой доступ к полю просто возвращает ноль.

  2. Свойство access требует, чтобы вы аннотировали все служебные методы (например, addChild/removeChild) как @Transient.

  3. С помощью доступа к полю мы можем скрыть поле @Version, не выставляя получателя вообще. Получатель также может привести к добавлению установщика, и поле version никогда не следует устанавливать вручную (что может привести к очень неприятным проблемам). Все приращения версии должны запускаться через OPTIMISTIC_FORCE_INCREMENT или PESSIMISTIC_FORCE_INCREMENT явная блокировка.

8
Vlad Mihalcea

Я думаю, что аннотирование свойства лучше, потому что обновление полей напрямую нарушает инкапсуляцию, даже когда ваш ORM делает это.

Вот отличный пример того, как он вас сожжет: вы, вероятно, хотите, чтобы ваши аннотации для средства проверки и сохранения в спящем режиме находились в одном месте (либо в полях, либо в свойствах). Если вы хотите проверить свои валидированные валидаторы, которые аннотируются на поле, вы не можете использовать макет вашей сущности, чтобы изолировать свой модульный тест только от валидатора. Уч.

7
Justin Standard

Я полагаю, что доступ к свойству и доступ к полю немного отличаются в отношении отложенной инициализации.

Рассмотрим следующие сопоставления для 2 основных компонентов:

<hibernate-mapping package="org.nkl.model" default-access="field">
  <class name="FieldBean" table="FIELD_BEAN">
    <id name="id">
      <generator class="sequence" />
    </id>
    <property name="message" />
  </class>
</hibernate-mapping>

<hibernate-mapping package="org.nkl.model" default-access="property">
  <class name="PropBean" table="PROP_BEAN">
    <id name="id">
      <generator class="sequence" />
    </id>
    <property name="message" />
  </class>
</hibernate-mapping>

И следующие юнит-тесты:

@Test
public void testFieldBean() {
    Session session = sessionFactory.openSession();
    Transaction tx = session.beginTransaction();
    FieldBean fb = new FieldBean("field");
    Long id = (Long) session.save(fb);
    tx.commit();
    session.close();

    session = sessionFactory.openSession();
    tx = session.beginTransaction();
    fb = (FieldBean) session.load(FieldBean.class, id);
    System.out.println(fb.getId());
    tx.commit();
    session.close();
}

@Test
public void testPropBean() {
    Session session = sessionFactory.openSession();
    Transaction tx = session.beginTransaction();
    PropBean pb = new PropBean("prop");
    Long id = (Long) session.save(pb);
    tx.commit();
    session.close();

    session = sessionFactory.openSession();
    tx = session.beginTransaction();
    pb = (PropBean) session.load(PropBean.class, id);
    System.out.println(pb.getId());
    tx.commit();
    session.close();
}

Вы увидите тонкую разницу в необходимом выборе:

Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    insert 
    into
        FIELD_BEAN
        (message, id) 
    values
        (?, ?)
Hibernate: 
    select
        fieldbean0_.id as id1_0_,
        fieldbean0_.message as message1_0_ 
    from
        FIELD_BEAN fieldbean0_ 
    where
        fieldbean0_.id=?
0
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    insert 
    into
        PROP_BEAN
        (message, id) 
    values
        (?, ?)
1

То есть вызов fb.getId() требует выбора, а pb.getId() - нет.

6
toolkit

Я предпочитаю полевые средства доступа. Код намного чище. Все аннотации могут быть помещены в один раздел класса .__, и код будет намного легче читать. 

Я обнаружил еще одну проблему со средствами доступа к свойствам: если в вашем классе есть методы getXYZ, которые НЕ аннотированы как связанные с постоянными свойствами, hibernate генерирует sql, чтобы попытаться получить эти свойства, что приводит к некоторым очень запутанным сообщениям об ошибках. Два часа впустую. Я не писал этот код; Я всегда использовал полевые методы доступа в прошлом и никогда не сталкивался с этой проблемой.

Версии Hibernate, используемые в этом приложении:

<!-- hibernate -->
<hibernate-core.version>3.3.2.GA</hibernate-core.version>
<hibernate-annotations.version>3.4.0.GA</hibernate-annotations.version>
<hibernate-commons-annotations.version>3.1.0.GA</hibernate-commons-annotations.version>
<hibernate-entitymanager.version>3.4.0.GA</hibernate-entitymanager.version>
2
John Goyer

Мы уже на месте

Это старая презентация, но Род предлагает, чтобы аннотации по доступу к свойствам поощряли анемичные модели предметной области и не должны быть способом аннотирования по умолчанию.

2
Manuel Palacio

Я предпочитаю поля, но я столкнулся с одной ситуацией, которая, кажется, заставляет меня размещать аннотации на геттерах.

С реализацией Hibernate JPA, @Embedded, похоже, не работает с полями. Так что это должно идти на добытчик. И как только вы поместите это в получатель, различные аннотации @Column тоже должны появиться в получателе. (Я думаю, что Hibernate не хочет смешивать поля и геттеры здесь.) И когда вы помещаете @Column в геттеры в одном классе, возможно, имеет смысл делать это повсеместно.

2
Willie Wheeler

По умолчанию JPA-провайдеры получают доступ к значениям полей сущностей и сопоставляют эти поля со столбцами базы данных , Используя методы доступа к свойствам JavaBean (getter) и mutator (setter). Таким образом, имена И типы приватных полей в сущности не имеют значения для JPA. Вместо этого JPA просматривает только Имена и типы возвращаемых методов доступа к свойствам JavaBean. Вы можете изменить это, используя аннотацию @javax.persistence.Access, которая позволяет вам явно указать методологию доступа , Которую должен использовать поставщик JPA.

@Entity
@Access(AccessType.FIELD)
public class SomeEntity implements Serializable
{
...
}

Для перечисления AccessType доступны следующие параметры: PROPERTY (по умолчанию) и FIELD. С помощью PROPERTY поставщик получает и устанавливает значения полей, используя методы свойств JavaBean. FIELD создает Провайдер получает и устанавливает значения полей, используя поля экземпляра. Лучше всего просто придерживаться __. по умолчанию и использовать свойства JavaBean, если у вас нет веских причин поступить иначе.

Вы Можете размещать эти аннотации свойств либо в приватных полях, либо в методах публичного доступа. Если вы используете AccessType.PROPERTY (по умолчанию) и аннотируете закрытые поля вместо средств доступа JavaBean , Имена полей должны совпадать с именами свойств JavaBean. Однако имена не должны совпадать , Если вы аннотируете средства доступа JavaBean. Аналогично, если вы используете AccessType.FIELD и Аннотируют средства доступа JavaBean вместо полей, имена полей также должны совпадать с именами JavaBeanproperty. В этом случае они не должны совпадать, если вы аннотируете поля. Лучше всего просто be быть последовательным и комментировать средства доступа JavaBean для AccessType.PROPERTY и поля для AccessType.FIELD.

Важно, чтобы вы никогда не смешивали аннотации свойств JPA и аннотации полей JPA в одном и том же объекте. Это может привести к неопределенному поведению и, скорее всего, к ошибкам.

2
Faraz Durrani

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

Поэтому я предпочитаю иметь коллекции в качестве защищенных полей, инициализированных пустыми реализациями в конструкторе по умолчанию, и выставлять только их получатели. Тогда возможны только управляемые операции, такие как clear(), remove(), removeAll() и т.д., Которые никогда не сделают Hibernate неосознанным об изменениях.

2
Jiří Vypědřík

Вы должны выбрать доступ через поля вместо доступа через свойства . С помощью полей вы можете ограничить данные, отправляемые и получаемые . С помощью свойств вы можете отправлять больше данных в качестве хоста и Устанавливать G деноминации (какая фабрика установить большинство свойств в общей сложности).

2
user10801787

Я решил ленивую инициализацию и доступ к полям здесь Hibernate один к одному: getId () без выборки всего объекта

1
xmedeko

У меня был тот же вопрос относительно accesstype в спящем режиме и я нашел некоторые ответы здесь .

1
sundary

Мы создали бины сущностей и использовали аннотации геттеров. Проблема, с которой мы столкнулись, заключается в следующем: у некоторых сущностей есть сложные правила для некоторых свойств относительно того, когда их можно обновлять. Решение заключалось в том, чтобы в каждом установщике была определенная бизнес-логика, определяющая, изменилось ли фактическое значение и, если да, то следует ли разрешить изменение. Конечно, Hibernate всегда может устанавливать свойства, поэтому мы получили две группы сеттеров. Довольно уродливые.

Читая предыдущие посты, я также вижу, что ссылки на свойства внутри сущности могут привести к проблемам с загрузкой коллекций.

Итог, я бы склонялся к тому, чтобы аннотировать поля в будущем.

1
Steve11235

я думаю об этом и выбираю метод accesor 

зачем? 

потому что поле и метод accesor одинаковы но если позже мне понадобится немного логики в поле загрузки, я сохраняю перемещать все аннотации, размещенные в полях

с уважением

Grubhart

0
user261548

Обычно бобы являются POJO, поэтому у них все равно есть доступ. 

Таким образом, вопрос не в том, «какой из них лучше?», А просто в том, «когда использовать полевой доступ?». И ответ «когда вам не нужен установщик/получатель для поля!».

0
Vladimir Dyuzhev

Позвольте мне попытаться суммировать наиболее важные причины выбора полевого доступа. Если вы хотите погрузиться глубже, прочитайте эту статью в моем блоге: Стратегии доступа в JPA и Hibernate - Что лучше, доступ к полю или собственности?

Полевой доступ - безусловно, лучший вариант. Вот 5 причин для этого:

Причина 1: лучшая читаемость вашего кода

Если вы используете полевой доступ, вы аннотируете свои атрибуты сущности аннотациями сопоставления. Поместив определение всех атрибутов сущности в начало вашего класса, вы получите относительно компактное представление всех атрибутов и их отображений.

Причина 2: опустите методы получения или установки, которые не должны вызываться вашим приложением

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

Причина 3: Гибкая реализация методов получения и установки

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

Вы можете, например, использовать это, чтобы обернуть необязательную ассоциацию или атрибут в Java Optional.

Причина 4: нет необходимости отмечать служебные методы как @Transient

Еще одно преимущество стратегии полевого доступа заключается в том, что вам не нужно аннотировать свои служебные методы с помощью @Transient. Эта аннотация сообщает поставщику постоянства, что метод или атрибут не являются частью постоянного состояния объекта. А поскольку при доступе по типу поля постоянное состояние определяется атрибутами вашей сущности, ваша реализация JPA игнорирует все методы вашей сущности.

Причина 5: избегайте ошибок при работе с прокси

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

Если вы используете доступ на основе свойств, Hibernate инициализирует атрибуты прокси-объекта при вызове метода getter. Это всегда так, если вы используете прокси-объект в своем бизнес-коде. Но довольно много равных и реализаций hashCode обращаются к атрибутам напрямую. Если вы впервые обращаетесь к любому из атрибутов прокси, эти атрибуты все еще не инициализированы.

0
Thorben Janssen

Чтобы сделать ваши классы чище, поместите аннотацию в поле, затем используйте @Access (AccessType.PROPERTY)

0
ely

И то и другое : 

Спецификация EJB3 требует, чтобы вы объявляли аннотации для элемента тип, к которому будет осуществляться доступ, т.е. метод getter, если вы используете свойство доступ, поле, если вы используете поле доступа.

https://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#entity-mapping

0
moueza

AccessType.PROPERTY: Реализация персистентности EJB загрузит состояние в ваш класс с помощью методов "setter" JavaBean и получит состояние из вашего класса с помощью методов "getter" JavaBean. Это по умолчанию.

AccessType.FIELD: Состояние загружается и извлекается непосредственно из полей вашего класса. Вам не нужно писать "getters" и "setters" JavaBean.

0
Vicky