Це питання дещо пов’язане із запитанням про розміщення в сплячому режимі з анотацією .
Але я хочу знати, що краще ? Доступ через властивості чи доступ через поля? Які переваги та недоліки кожного?
Це питання дещо пов’язане із запитанням про розміщення в сплячому режимі з анотацією .
Але я хочу знати, що краще ? Доступ через властивості чи доступ через поля? Які переваги та недоліки кожного?
Відповіді:
Я віддаю перевагу аксесуарам, оскільки можу додати деяку ділову логіку до своїх аксесуарів, коли мені потрібно. Ось приклад:
@Entity
public class Person {
@Column("nickName")
public String getNickName(){
if(this.name != null) return generateFunnyNick(this.name);
else return "John Doe";
}
}
Крім того, якщо ви кинете в суміш ще одну мочку (наприклад, яку-небудь конвертуючу JSON або BeanMapper, Dozer або іншу картографічну / картувальну базу на основі властивостей геттера / сеттера), ви отримаєте гарантію того, що ліб синхронізується зі стійкістю менеджер (обидва використовують getter / setter).
Існують аргументи для обох, але більшість з них випливає з певних вимог користувачів "що робити, якщо вам потрібно додати логіку для", або "xxxx порушує інкапсуляцію". Однак ніхто насправді ніхто не коментував цю теорію і не наводив належним чином аргументований аргумент.
Що насправді робить Hibernate / JPA, коли він зберігає об'єкт - добре, він зберігає СТАТЕЮ об’єкта. Це означає, що зберігати його можна таким чином, щоб його можна було легко відтворити.
Що таке інкапсуляція? Інкапсуляція означає інкапсуляцію даних (або стану) за допомогою інтерфейсу, який програма / клієнт може використовувати для безпечного доступу до даних - зберігаючи їх послідовні та дійсні.
Подумайте про це як MS Word. MS Word підтримує модель документа в пам'яті - документи ДЕРЖАВНІ. У ньому представлений інтерфейс, який користувач може використовувати для зміни документа - набір кнопок, інструментів, команд клавіатури тощо. Однак, коли ви вирішите зберегти (Зберегти) цей документ, він зберігає внутрішній стан, а не набір натискань клавіш і клацання миші, що використовується для його створення.
Збереження внутрішнього стану об'єкта НЕ порушує інкапсуляцію - інакше ви насправді не розумієте, що означає інкапсуляція та чому вона існує. Це насправді як об'єктна серіалізація.
З цієї причини В НАЙБІЛЬШИХ СЛУЧАХ доречно зберігати ПОЛІ, а не АКСЕСОРИ. Це означає, що об’єкт можна точно відтворити з бази даних саме таким чином, як він був збережений. Це не повинно потребувати ніякої перевірки, оскільки це робилося на оригіналі, коли він був створений, і до того, як він зберігався в базі даних (якщо тільки, не дай Бог, ви зберігаєте недійсні дані в БД !!!!). Так само не повинно бути обчислення значень, оскільки вони були вже обчислені до зберігання об'єкта. Об'єкт повинен виглядати так само, як це було зроблено до його збереження. Насправді, додаючи в геттери / сетери додаткові речі, ви фактично збільшуєте ризик того, що ви відтворите щось, що не є точною копією оригіналу.
Звичайно, ця функціональність була додана не просто так. Можливо, є кілька дійсних випадків використання для збереження доступу, однак вони, як правило, є рідкісними. Прикладом може бути те, що ви хочете уникнути збереження обчисленої величини, хоча ви можете задати питання, чому ви не обчислюєте його на вимогу в геттері значення або ліниво ініціалізуєте його в геттері. Особисто я не можу придумати жодного корисного випадку, і жодна з відповідей тут не дає відповіді на тему «Програмне забезпечення інженерії».
Я вважаю за краще доступ до поля, тому що я не змушений надавати геттер / сетер для кожної власності.
Швидке опитування через Google свідчить про те, що більшість доступ є на місцях (наприклад, http://java.dzone.com/tips/12-feb-jpa-20-why-accesstype ).
Я вважаю, що доступ до поля - це ідіома, яку рекомендує Spring, але я не можу знайти посилання на це.
Є пов’язане питання ПУ, яке намагалося виміряти продуктивність і дійшло висновку, що "різниці немає".
Ось така ситуація, коли ВИ ХОТЛИ використовувати доступ до майнових аксесуарів. Уявіть, що у вас є загальний абстрактний клас із великою кількістю можливостей для успадкування у 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
там.Це дійсно залежить від конкретного випадку - обидва варіанти доступні не просто так. IMO він зводиться до трьох випадків:
Я настійно рекомендую доступ до поля, а НЕ анотації на getters (доступ до власності), якщо ви хочете зробити щось більше в сеттерах, ніж просто встановити значення (наприклад, шифрування чи обчислення).
Проблема з доступом до властивостей полягає в тому, що сетери також викликаються при завантаженні об'єкта. Це працювало для мене чудово протягом багатьох місяців, поки ми не хотіли ввести шифрування. У нашому випадку використання ми хотіли зашифрувати поле в сетері та розшифрувати його в геттері. Проблема з доступом до власності полягала в тому, що коли Hibernate завантажував об'єкт, він також закликав сеттера заповнити поле і, таким чином, знову зашифрував зашифроване значення. У цій публікації також згадується це: Java Hibernate: Різне поведінка функції набору властивостей залежно від того, хто її викликає
Це викликало у мене головний біль, поки я не згадав про різницю між полем доступу та доступом до власності. Тепер я перемістив усі свої примітки з доступу власності до доступу до поля, і це працює нормально.
Я вважаю за краще використовувати доступ із поля з наступних причин:
Доступ до властивості може привести до дуже неприємних помилок при реалізації Equals / хеша - коді і посилань на поля безпосередньо (в протилежності через їх годувальник). Це пов’язано з тим, що проксі-сервер ініціалізується лише тоді, коли доступ до гетерів доступний, а доступ з прямого поля просто повернеться в нулевий.
Доступ до властивості вимагає анотувати всі допоміжні методи (наприклад , AddChild / RemoveChild) як @Transient
.
За допомогою доступу до поля ми можемо приховати поле @Version, взагалі не виставляючи геттера. Геттер також може призвести до додавання сеттера, і version
поле ніколи не слід встановлювати вручну (що може призвести до дуже неприємних проблем). Усі збільшення версій повинні бути запущені через явне блокування OPTIMISTIC_FORCE_INCREMENT або PESSIMISTIC_FORCE_INCREMENT .
version
поля часто корисно в ситуаціях, коли замість відокремлених об'єктів використовуються DTO.
Я думаю, що коментувати властивість краще, оскільки оновлення полів безпосередньо порушує інкапсуляцію, навіть коли це робить ваш ORM.
Ось чудовий приклад того, де це спалить вас: ви, мабуть, хочете, щоб ваші примітки щодо переходу на сплячку та збереження в одному місці (або поля, або властивості) хотіли. Якщо ви хочете перевірити ваші перевірки на хибернальний валідатор, які позначені на полі, ви не можете використовувати макет вашої сутності, щоб ізолювати ваш тест одиниці лише на валідатор. Ой.
Я вважаю, що доступ до власності та доступ до поля дещо відрізняється щодо лінивої ініціалізації.
Розглянемо наступні карти для 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()
ні.
Дозвольте спробувати узагальнити найважливіші причини вибору доступу на місцях. Якщо ви хочете зануритися глибше, будь ласка, прочитайте цю статтю на моєму блозі: Стратегії доступу в JPA та Зимова сплячка - Що краще, доступ у поле чи власність?
Польовий доступ - це набагато кращий варіант. Ось 5 причин для цього:
Причина 1: Краща читаність вашого коду
Якщо ви використовуєте доступ на місцях, ви коментуєте атрибути вашої сутності за допомогою анотацій на карті. Розміщуючи визначення всіх атрибутів сутності у верхній частині свого класу, ви отримуєте відносно компактне уявлення про всі атрибути та їх відображення.
Причина 2. Опустіть способи отримання або встановлення, які не повинні викликати вашою заявкою
Ще одна перевага польового доступу полягає в тому, що ваш постачальник завзятості, наприклад, Hibernate або EclipseLink, не використовує методів отримання та встановлення ваших атрибутів сутності. Це означає, що вам не потрібно надавати жоден метод, який не повинен використовуватися у вашому бізнес-коді. Найчастіше це стосується методів встановлення створених атрибутів первинного ключа або стовпців версій. Ваш постачальник завзятості керує значеннями цих атрибутів, і ви не повинні їх встановлювати програмно.
Причина 3: Гнучка реалізація методів геттера та сетера
Оскільки ваш постачальник завзятості не викликає способи отримання та встановлення, вони не змушені виконувати жодні зовнішні вимоги. Ви можете реалізувати ці методи будь-яким способом. Це дає змогу реалізувати специфічні для бізнесу правила перевірки, запустити додаткову логіку бізнесу або перетворити атрибут сутності в інший тип даних.
Наприклад, ви можете використовувати це, щоб обернути необов'язкове об'єднання або атрибут у Java Optional
.
Причина 4: Не потрібно маркувати корисні методи як @Transient
Ще одна перевага стратегії доступу на місцях полягає в тому, що вам не потрібно коментувати свої корисні методи @Transient
. Ця примітка повідомляє вашого постачальника наполегливості, що метод або атрибут не є частиною стійкого стану сутності. Оскільки завдяки доступу до польового типу стійкий стан визначається атрибутами вашої сутності, ваша реалізація JPA ігнорує всі методи вашої сутності.
Причина 5: Уникайте помилок під час роботи з проксі
Hibernate використовує проксі для ліниво пов'язаних один до одного асоціацій, щоб він міг контролювати ініціалізацію цих асоціацій. Такий підхід працює чудово майже у всіх ситуаціях. Але це створює небезпечну проблему, якщо ви користуєтесь доступом на основі власності.
Якщо ви використовуєте доступ на основі властивостей, Hibernate ініціалізує атрибути проксі-об'єкта під час виклику методу getter. Це завжди так, якщо ви використовуєте об'єкт проксі у своєму коді бізнесу. Але досить багато рівних та хеш-код-реалізацій мають доступ до атрибутів безпосередньо. Якщо ви вперше отримуєте доступ до будь-якого з атрибутів проксі, ці атрибути залишаються неініціалізованими.
За замовчуванням постачальники JPA отримують доступ до значень полів сутності та відображають ці поля в стовпці бази даних, використовуючи доступ до власності JavaBean властивості (getter) та мутатор (setter). Таким чином, назви та типи приватних полів в об'єкті не мають значення для JPA. Натомість JPA розглядає лише імена та типи повернення аксесуарів властивостей JavaBean. Ви можете змінити це за допомогою @javax.persistence.Access
примітки, яка дозволяє чітко вказати методологію доступу, яку повинен використовувати постачальник JPA.
@Entity
@Access(AccessType.FIELD)
public class SomeEntity implements Serializable
{
...
}
Доступними параметрами для перерахунку AccessType є ВЛАСНІСТЬ (за замовчуванням) та FIELD. Завдяки ВЛАСНІСТЬ постачальник отримує та встановлює значення полів, використовуючи методи властивості 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
примітки також повинні надходити на них. (Я думаю, що сплячий не хоче тут змішувати поля та гетери.) А коли ви @Column
надягаєте гетерів в одному класі, це, мабуть, має сенс робити це впродовж усього.
Я віддаю перевагу польовим аксесуарам. Код набагато чистіший. Усі примітки можна розмістити в одному розділі класу, і код читається набагато простіше.
Я знайшов ще одну проблему з доступом до властивостей: якщо у вашому класі є методи getXYZ, які НЕ відзначаються як асоційовані з стійкими властивостями, сплячий режим генерує 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>
Ви повинні вибрати доступ через поля над доступом через властивості. За допомогою полів ви можете обмежити надіслані та отримані дані. Завдяки властивостям via можна надіслати більше даних у якості хоста та встановити номінали G (які заводу встановлюють більшість властивостей загалом).
Я вирішив ліниву ініціалізацію та доступ до поля Зимувати один на один: getId () без вилучення всього об’єкта
Ми створили об'єкти бобів та використали анотації для отримання «атрибутів» Проблема, з якою ми стикалися, полягає в наступному: деякі об'єкти мають складні правила щодо деяких властивостей щодо того, коли вони можуть бути оновлені. Рішення полягало в тому, щоб у кожному сеттеррі була деяка логіка бізнесу, яка визначала, чи змінюється фактичне значення чи ні, і якщо так, чи слід допускати зміну. Звичайно, Hibernate завжди може встановити властивості, тому ми закінчили дві групи сетерів. Досить потворно.
Читаючи попередні публікації, я також бачу, що посилання на властивості зсередини об'єкта може призвести до проблем із завантаженням колекцій.
Знизу я схиляюся до того, щоб коментувати поля в майбутньому.
Зазвичай боби є POJO, тому в будь-якому випадку вони мають приналежності.
Тож питання не в тому, "хто краще?", А просто "коли використовувати доступ до поля?". А відповідь - "коли вам не потрібен сетер / геттер для поля!".
Я думаю про це, і я вибираю метод аксесуара
чому?
тому що акселератор поля та методу - це те саме, але якщо пізніше мені потрібна логіка в навантажувальному полі, я зберігаю переміщення всіх анотацій, розміщених у полях
з повагою
Грубхарт
Обидва:
Специфікація EJB3 вимагає, щоб ви оголосили анотації про тип елемента, до якого будете отримувати доступ, тобто метод getter, якщо ви використовуєте доступ до властивостей, поле, якщо ви використовуєте поле доступу.
https://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#entity-mapping
AccessType.PROPERTY: Реалізація стійкості EJB завантажує стан у ваш клас за допомогою методів "setter" JavaBean та виводить стан із класу за допомогою методів "getter" JavaBean. Це за замовчуванням.
AccessType.FIELD: стан завантажується та отримується безпосередньо з полів вашого класу. Не потрібно писати JavaBean "getters" та "setters".