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

Этот вопрос несколько связан с вопросом о размещении annotations Hibernate .

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

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

@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 синхронизируется с сохранением менеджер (оба используют геттер / сеттер).

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

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

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

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

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

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

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

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

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

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

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

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

 public abstract class Foo { T oneThing; T anotherThing; // getters and setters ommited for brevity // Lots and lots of implementation regarding oneThing and anotherThing here } 

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

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

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

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

Минус:

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

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

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

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

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

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

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

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

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

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

                 

И следующие модульные тесты:

 @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() – нет.

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

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

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

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

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

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

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

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

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

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

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

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

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

  3.3.2.GA 3.4.0.GA 3.1.0.GA 3.4.0.GA 

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

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

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

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

Итог, я буду склоняться к annotations полей в будущем.

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

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

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

Вы можете поместить эти annotations свойств либо в частные поля, либо в общедоступные методы доступа. Если вы используете AccessType.PROPERTY (по умолчанию) и аннотируете частные поля вместо аксессуаров JavaBean, имена полей должны соответствовать именам свойств JavaBean. Тем не менее, имена не должны совпадать, если вы аннотируете JavaBean accessors. Аналогично, если вы используете AccessType.FIELD и аннотируете AccessType.FIELD JavaBean вместо полей, имена полей также должны соответствовать именам свойств JavaBean. В этом случае они не должны совпадать, если вы аннотируете поля. Лучше просто быть последовательным и комментировать AccessType.PROPERTY JavaBean-аксессоры для AccessType.PROPERTY и полей для AccessType.FIELD .

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

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

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

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

Зачем?

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

С уважением

Grubhart

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

И то и другое :

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

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

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

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

  • В чем разница между fetch = "EAGER" и fetch = "LAZY" в доктрине
  • .net ORM Сравнение
  • Подсоническое Vs NHibernate
  • Какова лучшая страtagsя для модульных приложений, управляемых базами данных?
  • В чем разница между persist () и merge () в Hibernate?
  • Как вы можете делать пейджинг с NHibernate?
  • Когда и почему сущности JPA должны реализовывать интерфейс Serializable?
  • Как удалить объект с отношениями ManyToMany в JPA (и соответствующие строки таблицы соединений)?
  • Объекты ORM и сущности домена в Entity Framework 6.0
  • Использование Hibernate последовательности PostgreSQL не влияет на таблицу последовательности
  • Как использовать Hibernate @ Любые связанные annotations?
  • Interesting Posts

    Как я могу подсчитать прогоны в последовательности?

    Включить чистый URL-адрес в Yii2

    Удалите защищенные предупреждения (_CRT_SECURE_NO_WARNINGS) из проектов по умолчанию в Visual Studio

    Не удается выполнить миграцию базы данных после эшафота. Раздел 2.2 Учебник по Ruby on Rails Майкл Хартл

    Почему внезапно я не могу получить доступ к удаленному SVN?

    Почему мы копируем, а затем двигаемся?

    эквивалент mod_rewrite для IIS 7.0

    Как запустить Opera 10 без каких-либо открытых вкладок?

    Переполнение буфера работает в gdb, но не без него

    Как исправить мое приглашение в режиме оболочки emacs?

    Зарядное устройство заблокировано, я мог бы использовать это другое зарядное устройство? Детали внутри

    Заменить часть строки другой строкой

    Есть ли атрибут, связанный с AJAX, который должен быть установлен для действий controllerа ASP.NET MVC?

    Должны ли все события jquery быть привязаны к $ (документу)?

    Статические блоки инициализации

    Давайте будем гением компьютера.