Этот вопрос в некоторой степени связан с вопросом размещения аннотации в спящем режиме .
Но я хочу знать, что лучше ? Доступ через свойства или доступ через поля? Каковы преимущества и недостатки каждого?
Этот вопрос в некоторой степени связан с вопросом размещения аннотации в спящем режиме .
Но я хочу знать, что лучше ? Доступ через свойства или доступ через поля? Каковы преимущества и недостатки каждого?
Ответы:
Я предпочитаю аксессоры, так как я могу добавлять бизнес-логику в мои аксессоры, когда мне это нужно. Вот пример:
@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 на основе свойств получателя / установщика), вы получите гарантию, что библиотека синхронизирована с постоянством manager (оба используют геттер / сеттер).
Есть аргументы в пользу того и другого, но большинство из них проистекает из определенных требований пользователя: «что, если вам нужно добавить логику для», или «xxxx нарушает инкапсуляцию». Однако никто не прокомментировал эту теорию и не привел должным образом аргументированных аргументов.
Что на самом деле делает Hibernate / JPA, когда он сохраняет объект - ну, он сохраняет СОСТОЯНИЕ объекта. Это означает, что хранить его так, чтобы его можно было легко воспроизвести.
Что такое инкапсуляция? Инкапсуляция означает инкапсуляцию данных (или состояния) с помощью интерфейса, который приложение / клиент может использовать для безопасного доступа к данным - поддерживая их согласованность и действительность.
Думайте об этом как MS Word. MS Word хранит в памяти модель документа - документы СОСТОЯНИЕ. Он представляет собой интерфейс, который пользователь может использовать для изменения документа - набор кнопок, инструментов, команд клавиатуры и т. Д. Однако, когда вы решите сохранить (сохранить) этот документ, он сохраняет внутреннее состояние, а не набор нажатий клавиш и щелчки мыши, используемые для его генерации.
Сохранение внутреннего состояния объекта НЕ нарушает инкапсуляцию - иначе вы не совсем понимаете, что такое инкапсуляция и почему она существует. На самом деле это похоже на сериализацию объектов.
По этой причине в НАИБОЛЕЕ СЛУЧАЯХ уместно сохранять ПОЛЯ, а не АКСЕССУАРЫ. Это означает, что объект можно точно воссоздать из базы данных в том виде, в котором он был сохранен. Он не должен нуждаться в какой-либо проверке, потому что это было сделано на оригинале, когда он был создан, и до того, как он был сохранен в базе данных (если, не дай бог, вы храните неверные данные в БД !!!!). Точно так же не должно быть необходимости вычислять значения, поскольку они уже были рассчитаны до того, как объект был сохранен. Объект должен выглядеть так же, как и до сохранения. Фактически, добавляя дополнительный материал в методы получения / установки, вы фактически увеличиваете риск того, что вы воссоздаете что-то, что не является точной копией оригинала.
Конечно, эта функциональность была добавлена неспроста. Могут быть некоторые допустимые варианты использования для сохранения средств доступа, однако, как правило, они будут редкими. Примером может быть то, что вы хотите избежать сохранения вычисленного значения, хотя вы можете задать вопрос, почему вы не вычисляете его по запросу в получателе значения или лениво инициализируете его в получателе. Лично я не могу придумать ни одного хорошего варианта использования, и ни один из ответов здесь не дает ответа на вопрос «Разработка программного обеспечения».
Я предпочитаю доступ к полю, потому что таким образом я не обязан предоставлять getter / setter для каждого свойства.
Быстрый опрос через Google показывает, что большинство пользователей имеют доступ к полям (например, http://java.dzone.com/tips/12-feb-jpa-20-why-accesstype ).
Я полагаю, что полевой доступ - это идиома, рекомендованная Spring, но я не могу найти ссылку, подтверждающую это.
Есть связанный вопрос SO, который пытался измерить производительность и пришел к выводу, что «нет разницы».
Вот ситуация, когда вам НЕОБХОДИМО использовать средства доступа к свойствам. Представьте, что у вас есть абстрактный класс 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
}
Как именно аннотировать этот класс? Ответ: ВЫ НЕ МОЖЕТЕ аннотировать его с помощью доступа к полю или свойству, потому что вы не можете указать целевую сущность на этом этапе. Вы ДОЛЖНЫ аннотировать конкретные реализации. Но поскольку постоянные свойства объявлены в этом суперклассе, вы ДОЛЖНЫ использовать доступ к свойствам в подклассах.
Доступ к полям невозможен в приложении с абстрактными универсальными суперклассами.
abstract T getOneThing()
, и abstract void setOneThing(T thing)
, и использовать доступ к полю.
Я склонен предпочесть и использовать средства доступа к собственности:
foo.getId()
без инициализации прокси (важно при использовании Hibernate, пока не будет разрешен HHH-3718 ).недостаток:
@Transient
где-то.Это действительно зависит от конкретного случая - оба варианта доступны не зря. ИМО это сводится к трем случаям:
Я настоятельно рекомендую доступ к полям, а НЕ аннотации к геттерам (доступ к свойствам), если вы хотите сделать что-то большее в установщиках, чем просто установить значение (например, шифрование или вычисление).
Проблема с доступом к свойствам заключается в том, что установщики также вызываются при загрузке объекта. У меня это работало много месяцев, пока мы не захотели ввести шифрование. В нашем случае мы хотели зашифровать поле в установщике и расшифровать его в получателе. Теперь проблема с доступом к свойствам заключалась в том, что, когда Hibernate загружал объект, он также вызывал сеттер для заполнения поля и, таким образом, снова зашифровывал зашифрованное значение. В этом сообщении также упоминается следующее: Java Hibernate: различное поведение функции набора свойств в зависимости от того, кто ее вызывает.
Это вызывало у меня головные боли, пока я не вспомнил разницу между доступом к полю и доступом к собственности. Теперь я переместил все свои аннотации из доступа к свойствам в доступ к полям, и теперь он работает нормально.
Я предпочитаю использовать доступ к полю по следующим причинам:
Доступ к свойству может привести к очень неприятным ошибкам при реализации Equals / хэша - коде и ссылок на поля непосредственно (в противоположности через их добытчик). Это связано с тем, что прокси-сервер инициализируется только при доступе к геттерам, а прямой доступ к полю просто вернет null.
Для доступа к свойству необходимо аннотировать все служебные методы (например, addChild / removeChild) как @Transient
.
С помощью доступа к полю мы можем скрыть поле @Version, вообще не выставляя геттер. Получатель также может привести к добавлению установщика, и version
поле никогда не следует устанавливать вручную (что может привести к очень неприятным проблемам). Все версии инкрементация должна быть вызвана через OPTIMISTIC_FORCE_INCREMENT или PESSIMISTIC_FORCE_INCREMENT явной блокировку.
version
поля часто полезно в ситуациях, когда DTO используются вместо отдельных сущностей.
Я думаю, что аннотирование свойства лучше, потому что обновление полей напрямую нарушает инкапсуляцию, даже когда ваш ORM делает это.
Вот отличный пример того, где он вас сожжет: вы, вероятно, захотите, чтобы ваши аннотации для валидатора спящего режима и сохраняемости были в одном месте (либо поля, либо свойства). Если вы хотите протестировать валидации с поддержкой спящего режима, которые аннотируются в поле, вы не можете использовать имитацию своей сущности, чтобы изолировать свой модульный тест только для валидатора. Уч.
Я полагаю, что доступ к свойству и доступ к полю немного отличаются в отношении отложенной инициализации.
Рассмотрим следующие сопоставления для двух основных bean-компонентов:
<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()
этого - нет.
Позвольте мне попытаться обобщить наиболее важные причины выбора полевого доступа. Если вы хотите погрузиться глубже, прочтите эту статью в моем блоге: Стратегии доступа в JPA и Hibernate - что лучше, доступ к полю или к свойству?
Полевой доступ - безусловно, лучший вариант. Вот 5 причин для этого:
Причина 1: лучшая читаемость вашего кода
Если вы используете доступ на основе полей, вы аннотируете свои атрибуты объекта с помощью аннотаций сопоставления. Поместив определение всех атрибутов сущности в начало вашего класса, вы получите относительно компактное представление всех атрибутов и их сопоставлений.
Причина 2: пропустите методы получения или установки, которые не должны вызываться вашим приложением
Еще одно преимущество доступа на основе полей заключается в том, что ваш поставщик постоянства, например Hibernate или EclipseLink, не использует методы получения и установки атрибутов вашей сущности. Это означает, что вам не нужно предоставлять какой-либо метод, который не должен использоваться вашим бизнес-кодом. Чаще всего это касается методов установки сгенерированных атрибутов первичного ключа или столбцов версии. Ваш поставщик сохраняемости управляет значениями этих атрибутов, и вам не следует устанавливать их программно.
Причина 3: Гибкая реализация методов получения и установки
Поскольку ваш поставщик сохраняемости не вызывает методы получения и установки, они не обязаны выполнять какие-либо внешние требования. Вы можете реализовать эти методы любым удобным для вас способом. Это позволяет реализовать бизнес-правила проверки, запускать дополнительную бизнес-логику или преобразовывать атрибут сущности в другой тип данных.
Вы можете, например, использовать это, чтобы обернуть необязательную ассоциацию или атрибут в Java Optional
.
Причина 4: нет необходимости отмечать служебные методы как @Transient
Еще одно преимущество стратегии доступа на основе полей состоит в том, что вам не нужно аннотировать свои служебные методы с помощью @Transient
. Эта аннотация сообщает вашему провайдеру постоянства, что метод или атрибут не является частью постоянного состояния сущности. И поскольку при доступе к типу поля постоянное состояние определяется атрибутами вашей сущности, ваша реализация JPA игнорирует все методы вашей сущности.
Причина 5: Избегайте ошибок при работе с прокси
Hibernate использует прокси для ленивых однозначных ассоциаций чтобы контролировать инициализацию этих ассоциаций. Такой подход отлично работает почти во всех ситуациях. Но это создает опасную ловушку, если вы используете доступ на основе свойств.
Если вы используете доступ на основе свойств, Hibernate инициализирует атрибуты прокси-объекта при вызове метода получения. Это всегда так, если вы используете прокси-объект в своем бизнес-коде. Но довольно много реализаций equals и hashCode обращаются к атрибутам напрямую. Если вы обращаетесь к любому из атрибутов прокси впервые, эти атрибуты все еще не инициализированы.
По умолчанию провайдеры JPA получают доступ к значениям полей сущностей и сопоставляют эти поля со столбцами базы данных, используя методы доступа к свойствам JavaBean (getter) и мутатора (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 в одной и той же сущности. Это приведет к неопределенному поведению и, скорее всего, приведет к ошибкам.
Это старая презентация, но Род предполагает, что аннотации при доступе к собственности поощряют анемичные модели предметной области и не должны быть способом аннотирования «по умолчанию».
Еще один аргумент в пользу доступа к полям заключается в том, что в противном случае вы также будете вынуждены открывать сеттеры для коллекций, что, на мой взгляд, является плохой идеей, так как изменение экземпляра постоянной коллекции на объект, не управляемый Hibernate, определенно нарушит согласованность ваших данных.
Поэтому я предпочитаю иметь коллекции в качестве защищенных полей, инициализированных пустыми реализациями в конструкторе по умолчанию, и открывать только их геттеры. Тогда возможны только такие управляемые операции, как clear()
, remove()
и removeAll()
т. Д., Которые никогда не заставят Hibernate не знать об изменениях.
Я предпочитаю поля, но я столкнулся с одной ситуацией, которая, кажется, заставляет меня размещать аннотации в геттерах.
С реализацией Hibernate JPA, @Embedded
похоже, не работает с полями. Так что это должно идти на добытчик. И как только вы поместите это в получатель, тогда различные @Column
примечания также должны появиться и в получателе. (Я думаю, Hibernate не хочет смешивать здесь поля и геттеры.) И если вы добавляете @Column
геттеры в один класс, вероятно, имеет смысл делать это повсюду.
Я предпочитаю полевые средства доступа. Код намного чище. Все аннотации могут быть размещены в одном разделе класса, и код намного легче читать.
Я обнаружил еще одну проблему со средствами доступа к свойствам: если у вас есть методы 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>
Вы должны выбрать доступ через поля вместо доступа через свойства. С помощью полей вы можете ограничить отправленные и полученные данные. С помощью свойств через, вы можете отправить больше данных в качестве хоста, и установить G деноминации (какой завод в целом установил большинство свойств).
У меня был тот же вопрос относительно accesstype в спящем режиме, и я нашел здесь несколько ответов .
Я решил здесь ленивую инициализацию и доступ к полям Hibernate один к одному: getId () без получения всего объекта
Мы создали бины сущностей и использовали аннотации геттеров. Проблема, с которой мы столкнулись, заключается в следующем: у некоторых сущностей есть сложные правила для некоторых свойств, касающихся того, когда они могут быть обновлены. Решение заключалось в том, чтобы иметь некоторую бизнес-логику в каждом установщике, которая определяет, изменилось ли фактическое значение и, если да, должно ли изменение быть разрешено. Конечно, Hibernate всегда может устанавливать свойства, поэтому мы получили две группы установщиков. Довольно уродливые.
Читая предыдущие сообщения, я также вижу, что ссылка на свойства изнутри сущности может привести к проблемам с загрузкой коллекций.
В итоге, я бы предпочел аннотировать поля в будущем.
Обычно bean-компоненты являются POJO, поэтому у них в любом случае есть аксессоры.
Так что вопрос не в том, «что лучше?», А просто в том, «когда использовать доступ к полям?». И ответ - «когда вам не нужен сеттер / геттер для поля!».
я думаю об этом и выбираю метод accesor
Зачем?
потому что поле и метод доступа совпадают, но если позже мне понадобится немного логики в поле загрузки, я сохраню все аннотации, помещенные в поля
С уважением
Grubhart
Обе :
Спецификация EJB3 требует, чтобы вы объявляли аннотации к типу элемента, к которому будет осуществляться доступ, то есть к методу получения, если вы используете доступ к свойствам, и к полю, если вы используете доступ к полю.
https://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#entity-mapping
AccessType.PROPERTY: Реализация персистентности EJB загрузит состояние в ваш класс с помощью методов "setter" JavaBean и получит состояние из вашего класса с помощью методов "getter" JavaBean. Это по умолчанию.
AccessType.FIELD: состояние загружается и извлекается непосредственно из полей вашего класса. Вам не нужно писать "геттеры" и "сеттеры" JavaBean.