Pertanyaan ini agak terkait dengan Pertanyaan Penempatan Anotasi Hibernasi .
Tapi saya ingin tahu mana yang lebih baik ? Akses melalui properti atau akses melalui bidang? Apa kelebihan dan kekurangan masing-masing?
Pertanyaan ini agak terkait dengan Pertanyaan Penempatan Anotasi Hibernasi .
Tapi saya ingin tahu mana yang lebih baik ? Akses melalui properti atau akses melalui bidang? Apa kelebihan dan kekurangan masing-masing?
Jawaban:
Saya lebih suka accessors, karena saya dapat menambahkan beberapa logika bisnis ke accessors saya kapan pun saya butuhkan. Ini sebuah contoh:
@Entity
public class Person {
@Column("nickName")
public String getNickName(){
if(this.name != null) return generateFunnyNick(this.name);
else return "John Doe";
}
}
Selain itu, jika Anda melempar lib lain ke dalam campuran (seperti beberapa lib konversi JSON atau BeanMapper atau Dozer atau lib pemetaan / kloning kacang lainnya berdasarkan sifat pengambil / penyetel) Anda akan memiliki jaminan bahwa lib sinkron dengan persistensi. manajer (keduanya menggunakan pengambil / penyetel).
Ada argumen untuk keduanya, tetapi sebagian besar berasal dari persyaratan pengguna tertentu "bagaimana jika Anda perlu menambahkan logika untuk", atau "xxxx jeda enkapsulasi". Namun, tidak ada yang benar-benar berkomentar tentang teori ini, dan memberikan argumen yang beralasan.
Apa yang sebenarnya Hibernate / JPA lakukan ketika itu mempertahankan suatu objek - yah, itu mempertahankan STATE objek tersebut. Itu berarti menyimpannya dengan cara yang mudah direproduksi.
Apa itu enkapsulasi? Enkapsulasi berarti enkapsulasi data (atau status) dengan antarmuka yang dapat digunakan aplikasi / klien untuk mengakses data dengan aman - menjaganya tetap konsisten dan valid.
Pikirkan ini seperti MS Word. MS Word mempertahankan model dokumen dalam memori - NEGARA dokumen. Ini menyajikan antarmuka yang dapat digunakan pengguna untuk mengubah dokumen - satu set tombol, alat, perintah keyboard dll. Namun, ketika Anda memilih untuk bertahan (Simpan) dokumen itu, ia menyimpan keadaan internal, bukan set penekanan tombol dan klik mouse yang digunakan untuk menghasilkannya.
Menyimpan keadaan internal objek JANGAN merusak enkapsulasi - jika tidak, Anda tidak benar-benar mengerti apa arti enkapsulasi, dan mengapa itu ada. Ini seperti serialisasi objek sebenarnya.
Untuk alasan ini, DALAM KASUS PALING, itu pantas untuk tetap BIDANG dan bukan ACCESSORS. Ini berarti bahwa suatu objek dapat dibuat kembali secara akurat dari database persis seperti itu disimpan. Seharusnya tidak perlu validasi apa pun, karena ini dilakukan pada yang asli ketika dibuat, dan sebelum disimpan dalam database (kecuali, Tuhan melarang, Anda menyimpan data yang tidak valid dalam DB !!!!). Demikian juga, seharusnya tidak perlu menghitung nilai, karena mereka sudah dihitung sebelum objek disimpan. Objek harus terlihat seperti yang dilakukan sebelum disimpan. Bahkan, dengan menambahkan barang-barang tambahan ke getter / setter Anda sebenarnya meningkatkan risiko bahwa Anda akan menciptakan kembali sesuatu yang bukan salinan asli dari aslinya.
Tentu saja, fungsi ini ditambahkan karena suatu alasan. Mungkin ada beberapa kasus penggunaan yang valid untuk mempertahankan aksesor, namun biasanya jarang terjadi. Contohnya adalah Anda ingin menghindari mempertahankan nilai yang dihitung, meskipun Anda mungkin ingin mengajukan pertanyaan mengapa Anda tidak menghitungnya berdasarkan permintaan dalam pengambil nilai, atau dengan malas menginisialisasinya dalam pengambil. Secara pribadi saya tidak bisa memikirkan kasus penggunaan yang baik, dan tidak ada jawaban di sini yang benar-benar memberikan jawaban "Rekayasa Perangkat Lunak".
Saya lebih suka akses bidang, karena dengan begitu saya tidak dipaksa untuk menyediakan pengambil / penyetel untuk setiap properti.
Survei cepat melalui Google menunjukkan bahwa akses bidang adalah yang terbanyak (mis., Http://java.dzone.com/tips/12-feb-jpa-20-why-accesstype ).
Saya percaya akses lapangan adalah idiom yang direkomendasikan oleh Spring, tetapi saya tidak dapat menemukan referensi untuk mendukungnya.
Ada pertanyaan SO terkait yang mencoba mengukur kinerja dan sampai pada kesimpulan bahwa "tidak ada perbedaan".
Berikut adalah situasi di mana Anda HARUS menggunakan aksesor properti. Bayangkan Anda memiliki kelas abstrak GENERIC dengan banyak implementasi kebaikan untuk diwariskan ke dalam 8 subkelas beton:
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
}
Sekarang tepatnya bagaimana Anda membubuhi keterangan kelas ini? Jawabannya adalah ANDA TIDAK BISA membubuhi keterangan sama sekali dengan bidang atau akses properti karena Anda tidak dapat menentukan entitas target pada saat ini. Anda HARUS membuat anotasi implementasi konkret. Tetapi karena properti yang bertahan dideklarasikan dalam superclass ini, Anda HARUS menggunakan akses properti di subclass.
Akses lapangan bukan merupakan opsi dalam aplikasi dengan superkelas generik abstrak.
abstract T getOneThing()
, dan abstract void setOneThing(T thing)
, dan menggunakan akses bidang.
Saya cenderung lebih suka dan menggunakan aksesor properti:
foo.getId()
tanpa menginisialisasi proxy (penting saat menggunakan Hibernate, sampai HHH-3718 diselesaikan).Kekurangan:
@Transient
sekitar sana.Itu benar-benar tergantung pada kasus tertentu - kedua opsi tersedia karena suatu alasan. IMO itu bermuara pada tiga kasus:
Saya akan sangat menyarankan akses bidang dan BUKAN anotasi pada getter (akses properti) jika Anda ingin melakukan sesuatu yang lebih dalam setter daripada hanya mengatur nilai (misalnya Enkripsi atau perhitungan).
Masalah dengan akses properti adalah bahwa setter juga dipanggil ketika objek dimuat. Ini telah berhasil bagi saya selama berbulan-bulan sampai kami ingin memperkenalkan enkripsi. Dalam kasus penggunaan kami, kami ingin mengenkripsi bidang dalam setter dan mendekripsi dalam pengambil. Masalahnya sekarang dengan akses properti adalah bahwa ketika Hibernate memuat objek itu juga memanggil setter untuk mengisi bidang dan dengan demikian mengenkripsi nilai terenkripsi lagi. Posting ini juga menyebutkan ini: Java Hibernate: Perilaku fungsi kumpulan properti yang berbeda tergantung pada siapa yang memanggilnya
Ini telah menyebabkan saya sakit kepala sampai saya ingat perbedaan antara akses lapangan dan akses properti. Sekarang saya telah memindahkan semua anotasi saya dari akses properti ke akses lapangan dan berfungsi dengan baik sekarang.
Saya lebih suka menggunakan akses lapangan karena alasan berikut:
The akses properti dapat menyebabkan bug yang sangat jahat ketika menerapkan equals / hashCode dan referensi bidang langsung (sebagai lawan melalui getter mereka). Ini karena proxy hanya diinisialisasi ketika getter diakses, dan akses bidang langsung hanya akan mengembalikan nol.
The akses properti mengharuskan Anda untuk membubuhi keterangan semua metode utilitas (misalnya addChild / removeChild) sebagai @Transient
.
Dengan akses bidang kita bisa menyembunyikan bidang @Version dengan tidak mengekspos pengambil sama sekali. Seorang pengambil juga dapat menyebabkan menambahkan setter juga, dan version
bidang tidak boleh diatur secara manual (yang dapat menyebabkan masalah yang sangat buruk). Semua peningkatan versi harus dipicu melalui OPTIMISTIC_FORCE_INCREMENT atau PESSIMISTIC_FORCE_INCREMENT penguncian eksplisit.
version
bidang sering berguna dalam situasi di mana DTO digunakan sebagai pengganti entitas yang terpisah.
Saya pikir annotating properti lebih baik karena memperbarui bidang secara langsung memecah enkapsulasi, bahkan ketika ORM Anda melakukannya.
Berikut ini adalah contoh yang bagus dari mana ia akan membakar Anda: Anda mungkin ingin anotasi Anda untuk hibernate validator & kegigihan di tempat yang sama (baik bidang atau properti). Jika Anda ingin menguji validasi bertenaga validator hibernate yang dianotasi pada bidang, Anda tidak bisa menggunakan tiruan entitas Anda untuk mengisolasi unit test Anda menjadi validator saja. Aduh.
Saya percaya akses properti vs akses lapangan agak berbeda sehubungan dengan inisialisasi malas.
Pertimbangkan pemetaan berikut untuk 2 kacang polong:
<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>
Dan tes unit berikut:
@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();
}
Anda akan melihat perbedaan halus dalam pemilihan yang diperlukan:
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
Artinya, panggilan fb.getId()
membutuhkan pilih, sedangkan pb.getId()
tidak.
Biarkan saya mencoba merangkum alasan paling penting untuk memilih akses berbasis lapangan. Jika Anda ingin menyelam lebih dalam, baca artikel ini di blog saya: Strategi Akses di JPA dan Hibernate - Mana yang lebih baik, akses bidang atau properti?
Akses berbasis lapangan sejauh ini merupakan pilihan yang lebih baik. Berikut 5 alasan untuk itu:
Alasan 1: keterbacaan kode Anda lebih baik
Jika Anda menggunakan akses berbasis bidang, Anda memberi anotasi atribut entitas Anda dengan anotasi pemetaan Anda. Dengan menempatkan definisi semua atribut entitas di bagian atas kelas Anda, Anda mendapatkan tampilan yang relatif kompak dari semua atribut dan pemetaannya.
Alasan 2: Hapus metode pengambil atau penyetel yang tidak boleh dipanggil oleh aplikasi Anda
Keuntungan lain dari akses berbasis lapangan adalah bahwa penyedia ketekunan Anda, misalnya, Hibernate atau EclipseLink, tidak menggunakan metode pengambil dan penyetel atribut entitas Anda. Itu berarti Anda tidak perlu memberikan metode apa pun yang tidak boleh digunakan oleh kode bisnis Anda. Ini paling sering terjadi pada metode setter dari atribut primary key atau kolom versi yang dihasilkan . Penyedia ketekunan Anda mengelola nilai atribut ini, dan Anda tidak harus mengaturnya secara terprogram.
Alasan 3: Implementasi metode pengambil dan penyetel yang fleksibel
Karena penyedia kegigihan Anda tidak memanggil metode pengambil dan penyetel, mereka tidak dipaksa untuk memenuhi persyaratan eksternal apa pun. Anda dapat menerapkan metode ini dengan cara apa pun yang Anda inginkan. Itu memungkinkan Anda untuk menerapkan aturan validasi khusus bisnis, untuk memicu logika bisnis tambahan atau untuk mengubah atribut entitas menjadi tipe data yang berbeda.
Anda dapat, misalnya, menggunakannya untuk membungkus asosiasi opsional atau atribut menjadi Java Optional
.
Alasan 4: Tidak perlu menandai metode utilitas sebagai @Transient
Manfaat lain dari strategi akses berbasis lapangan adalah Anda tidak perlu membuat anotasi dengan metode utilitas Anda @Transient
. Anotasi ini memberi tahu penyedia ketekunan Anda bahwa metode atau atribut bukan bagian dari status persisten entitas. Dan karena dengan akses tipe bidang status persisten akan ditentukan oleh atribut entitas Anda, implementasi JPA Anda mengabaikan semua metode entitas Anda.
Alasan 5: Hindari bug saat bekerja dengan proxy
Hibernate menggunakan proksi untuk secara malas mengambil ke-satu asosiasi sehingga dapat mengontrol inisialisasi asosiasi ini. Pendekatan itu bekerja dengan baik di hampir semua situasi. Tapi itu menimbulkan jebakan berbahaya jika Anda menggunakan akses berbasis properti.
Jika Anda menggunakan akses berbasis properti, Hibernate menginisialisasi atribut objek proxy ketika Anda memanggil metode pengambil. Itu selalu terjadi jika Anda menggunakan objek proxy dalam kode bisnis Anda. Tetapi cukup banyak implementasi yang sama dan hashCode mengakses atribut secara langsung. Jika ini adalah pertama kalinya Anda mengakses salah satu atribut proksi, atribut ini masih belum diinisialisasi.
Secara default, penyedia JPA mengakses nilai-nilai bidang entitas dan memetakan bidang-bidang tersebut ke kolom basis data menggunakan metode accessor (getter) properti JavaBean dan metode mutator (setter). Dengan demikian, nama dan tipe bidang pribadi dalam suatu entitas tidak menjadi masalah bagi JPA. Alih-alih, JPA hanya melihat nama dan mengembalikan tipe pengakses properti JavaBean. Anda dapat mengubah ini menggunakan @javax.persistence.Access
anotasi, yang memungkinkan Anda untuk secara eksplisit menentukan metodologi akses yang harus digunakan penyedia JPA.
@Entity
@Access(AccessType.FIELD)
public class SomeEntity implements Serializable
{
...
}
Pilihan yang tersedia untuk AccessType enum adalah PROPERTI (standar) dan FIELD. Dengan PROPERTI, penyedia mendapat dan menetapkan nilai bidang menggunakan metode properti JavaBean. FIELD membuat penyedia mendapatkan dan menetapkan nilai bidang menggunakan bidang contoh. Sebagai praktik terbaik, Anda harus tetap berpegang pada default dan menggunakan properti JavaBean kecuali Anda memiliki alasan kuat untuk melakukan sebaliknya.
Anda dapat menempatkan anotasi properti ini di bidang pribadi atau metode pengakses publik. Jika Anda menggunakan AccessType.PROPERTY
(default) dan membubuhi keterangan bidang pribadi alih-alih aksesor JavaBean, nama bidang harus cocok dengan nama properti JavaBean. Namun, nama tidak harus cocok jika Anda memberi anotasi pada pengakses JavaBean. Demikian juga, jika Anda menggunakan AccessType.FIELD
dan memberi anotasi pengakses JavaBean alih-alih bidang, nama bidang juga harus cocok dengan nama properti JavaBean. Dalam hal ini, mereka tidak harus cocok jika Anda memberi anotasi pada bidang. Yang terbaik adalah konsisten dan membuat anotasi untuk JavaBean AccessType.PROPERTY
dan bidang untuk
AccessType.FIELD
.
Adalah penting bahwa Anda tidak boleh mencampur anotasi properti JPA dan anotasi bidang JPA dalam entitas yang sama. Melakukannya menghasilkan perilaku yang tidak ditentukan dan sangat mungkin menyebabkan kesalahan.
Itu presentasi lama tetapi Rod menyarankan bahwa anotasi pada akses properti mendorong model domain anemia dan tidak boleh menjadi cara "default" untuk membuat anotasi.
Hal lain yang mendukung akses bidang adalah bahwa jika tidak, Anda dipaksa untuk mengekspos setter untuk koleksi juga, bagi saya, adalah ide yang buruk karena mengubah instance koleksi persisten ke objek yang tidak dikelola oleh Hibernate pasti akan merusak konsistensi data Anda.
Jadi saya lebih suka memiliki koleksi sebagai bidang yang dilindungi diinisialisasi ke implementasi kosong di konstruktor default dan hanya mengekspos getter mereka. Operasi itu, hanya berhasil seperti clear()
, remove()
, removeAll()
dll adalah mungkin bahwa tidak akan pernah membuat Hibernate tidak menyadari perubahan.
Saya lebih suka bidang, tapi saya pernah mengalami satu situasi yang tampaknya memaksa saya untuk menempatkan anotasi pada pengambil.
Dengan implementasi Hibernate JPA, @Embedded
sepertinya tidak berfungsi di bidang. Jadi itu harus pergi pada rajin dan giat. Dan begitu Anda meletakkannya di rajin, maka berbagai @Column
anotasi juga harus di rajin . (Saya pikir Hibernate tidak ingin mencampur bidang dan getter di sini.) Dan begitu Anda memakai @Column
getter dalam satu kelas, mungkin masuk akal untuk melakukan itu sepanjang.
Saya menyukai pengakses lapangan. Kode jauh lebih bersih. Semua anotasi dapat ditempatkan di satu bagian kelas dan kodenya lebih mudah dibaca.
Saya menemukan masalah lain dengan accessors properti: jika Anda memiliki metode getXYZ di kelas Anda yang TIDAK dianotasi terkait dengan properti persisten, hibernate menghasilkan sql untuk mencoba mendapatkan properti tersebut, menghasilkan beberapa pesan kesalahan yang sangat membingungkan. Dua jam terbuang. Saya tidak menulis kode ini; Saya selalu menggunakan pengakses lapangan di masa lalu dan tidak pernah mengalami masalah ini.
Versi hibernasi yang digunakan dalam aplikasi ini:
<!-- 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>
Anda harus memilih akses melalui bidang daripada akses melalui properti. Dengan bidang Anda dapat membatasi data yang dikirim dan diterima. Dengan via properti, Anda dapat mengirim lebih banyak data sebagai tuan rumah, dan mengatur denominasi G (pabrik yang mengatur sebagian besar properti secara total).
Saya memiliki pertanyaan yang sama mengenai accesstype di hibernate dan menemukan beberapa jawaban di sini .
Saya telah menyelesaikan inisialisasi malas dan akses bidang di sini Hibernasi satu-ke-satu: getId () tanpa mengambil seluruh objek
Kami membuat kacang entitas dan menggunakan anotasi pengambil. Masalah yang kami hadapi adalah ini: beberapa entitas memiliki aturan kompleks untuk beberapa properti mengenai kapan mereka dapat diperbarui. Solusinya adalah memiliki beberapa logika bisnis di setiap penyetel yang menentukan apakah nilai aktual berubah atau tidak, dan jika demikian, apakah perubahan tersebut harus diizinkan. Tentu saja, Hibernate selalu dapat mengatur properti, jadi kami berakhir dengan dua kelompok setter. Cukup jelek.
Membaca posting sebelumnya, saya juga melihat bahwa referensi properti dari dalam entitas dapat menyebabkan masalah dengan koleksi yang tidak dimuat.
Intinya, saya akan condong ke arah anotasi bidang di masa depan.
Biasanya kacang adalah POJO, jadi mereka tetap memiliki aksesor.
Jadi pertanyaannya bukan "mana yang lebih baik?", Tetapi hanya "kapan harus menggunakan akses lapangan?". Dan jawabannya adalah "ketika Anda tidak membutuhkan setter / pengambil untuk bidang!".
Saya memikirkan hal ini dan saya memilih metode accesor
Mengapa?
karena bidang dan metos accesor adalah sama tetapi jika nanti saya memerlukan beberapa logika di bidang beban, saya menyimpan semua anotasi memindahkan ditempatkan di bidang
salam
Grubhart
Keduanya:
Spesifikasi EJB3 mengharuskan Anda mendeklarasikan anotasi pada tipe elemen yang akan diakses, yaitu metode pengambil jika Anda menggunakan akses properti, bidang jika Anda menggunakan akses bidang.
https://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#entity-mapping
AccessType.PROPERTY: Implementasi EJB yang persisten akan memuat status ke kelas Anda melalui metode "setter" JavaBean, dan mengambil status dari kelas Anda menggunakan metode "pengambil" JavaBean. Ini standarnya.
AccessType.FIELD: Negara dimuat dan diambil langsung dari bidang kelas Anda. Anda tidak harus menulis "getter" dan "setter" JavaBean.