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

Этот вопрос несколько связан с вопросом размещения аннотации гибернации.

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

Ответ 1

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

@Entity
public class Person {

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

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

Ответ 2

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

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

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

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

Сохранение внутреннего состояния объекта НЕ ОТКЛЮЧАЕТ инкапсуляцию - иначе вы действительно не понимаете, что такое инкапсуляция, и почему она существует. Это похоже на сериализацию объектов.

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

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

Ответ 3

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

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

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

Был связанный с этим вопрос, который пытался измерить производительность и пришел к выводу, что "разницы нет".

Ответ 4

Здесь ситуация, когда вам нужно использовать устройства доступа к свойствам. Представьте, что у вас есть абстрактный класс 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
 }

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

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

Ответ 5

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

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

Минус:

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

Ответ 6

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

  • setter имеет некоторую логику, которая не должна выполняться во время загрузки экземпляра из базы данных; например, в установщике происходит некоторая проверка достоверности данных, однако данные, поступающие из db, должны быть действительными (в противном случае оно не получило бы там (:); в этом случае доступ к полям наиболее уместен;
  • setter имеет некоторую логику, которая всегда должна вызываться, даже во время загрузки экземпляра из db; например, инициализированное свойство используется при вычислении некоторого вычисленного поля (например, свойство - денежная сумма, вычисленное свойство - всего несколько денежных свойств одного и того же экземпляра); в этом случае требуется доступ к свойствам.
  • Ни один из указанных выше случаев - тогда оба параметра применимы, просто оставайтесь последовательными (например, если доступ к полю является выбором в этой ситуации, тогда используйте его все время в подобной ситуации).

Ответ 7

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

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

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

Ответ 8

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

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

Ответ 9

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

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

<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() не работает.

Ответ 10

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

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

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

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

Ответ 11

Мы еще там

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

Ответ 12

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

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

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

<!-- 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>

Ответ 13

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

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

Ответ 14

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

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

Ответ 15

По умолчанию поставщики JPA получают доступ к значениям полей сущностей и сопоставляют эти поля с столбцами базы данных используя методы сущностей JavaBean accessor (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 вместо полей, имена полей также должны соответствовать JavaBean имена свойств. В этом случае они не должны совпадать, если вы аннотируете поля. Лучше всего быть последовательным и аннотировать аксессуры JavaBean для AccessType.PROPERTY, а поля для AccessType.FIELD.

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

Ответ 18

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

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

В нижней строке я буду склоняться к аннотации полей в будущем.

Ответ 19

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

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

Ответ 20

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

почему?

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

рассматривает

Grubhart

Ответ 21

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

Ответ 22

И то и другое:

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

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

Ответ 23

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

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

Ответ 24

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 25

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