Seperti yang dibutuhkan JPA, @Entity
kelas harus memiliki konstruktor default (non-arg) untuk instantiate objek ketika mengambilnya dari database.
Di Kotlin, properti sangat mudah untuk menyatakan dalam konstruktor utama, seperti dalam contoh berikut:
class Person(val name: String, val age: Int) { /* ... */ }
Tetapi ketika konstruktor non-arg dideklarasikan sebagai konstruktor sekunder, ia membutuhkan nilai untuk konstruktor primer untuk dilewati, sehingga beberapa nilai valid diperlukan untuk mereka, seperti di sini:
@Entity
class Person(val name: String, val age: Int) {
private constructor(): this("", 0)
}
Dalam kasus ketika properti memiliki beberapa tipe yang lebih kompleks daripada hanya String
dan Int
dan mereka tidak dapat dibatalkan, terlihat sangat buruk untuk memberikan nilai untuk mereka, terutama ketika ada banyak kode dalam konstruktor dan init
blok primer dan ketika parameter digunakan secara aktif - - ketika mereka akan dipindahkan melalui refleksi sebagian besar kode akan dieksekusi lagi.
Selain itu, val
-properti tidak dapat dipindahkan setelah konstruktor dijalankan, sehingga ketidakmampuan juga hilang.
Jadi pertanyaannya adalah: bagaimana kode Kotlin dapat diadaptasi untuk bekerja dengan JPA tanpa duplikasi kode, memilih nilai awal "ajaib" dan kehilangan keabadian?
PS Benarkah Hibernate selain dari JPA dapat membangun objek tanpa konstruktor default?
sumber
INFO -- org.hibernate.tuple.PojoInstantiator: HHH000182: No default (no-argument) constructor for class: Test (class must be instantiated by Interceptor)
- jadi, ya, Hibernate dapat berfungsi tanpa konstruktor default.Jawaban:
Pada Kotlin 1.0.6 ,
kotlin-noarg
plugin kompiler menghasilkan konstruktor default default untuk kelas yang telah dijelaskan dengan anotasi yang dipilih.Jika Anda menggunakan gradle, menerapkan
kotlin-jpa
plugin sudah cukup untuk menghasilkan konstruktor default untuk kelas yang dijelaskan dengan@Entity
:Untuk Maven:
sumber
data class foo(bar: String)
tidak berubah". Akan menyenangkan melihat contoh yang lebih lengkap tentang bagaimana ini cocok dengan tempatnya. Terima kasihkotlin-noarg
dankotlin-jpa
dengan tautan yang merinci tujuan mereka blog.jetbrains.com/kotlin/2016/12/kotlin-1-0-6-is-here@Embeddable
atribut bahkan jika Anda tidak membutuhkannya. Dengan begitu, itu akan diambil olehkotlin-jpa
.cukup berikan nilai default untuk semua argumen, Kotlin akan membuat konstruktor default untuk Anda.
lihat
NOTE
kotak di bawah bagian berikut:https://kotlinlang.org/docs/reference/classes.html#secondary-constructors
sumber
@ D3xter memiliki jawaban yang bagus untuk satu model, yang lain adalah fitur yang lebih baru di Kotlin yang disebut
lateinit
:Anda akan menggunakan ini ketika Anda yakin sesuatu akan mengisi nilai pada waktu konstruksi atau segera setelahnya (dan sebelum penggunaan pertama instance).
Anda akan catatan saya berubah
age
kebirthdate
karena Anda tidak dapat menggunakan nilai-nilai primitif denganlateinit
dan mereka juga untuk saat ini harusvar
(pembatasan mungkin akan dirilis di masa depan).Jadi bukan jawaban sempurna untuk kekekalan, masalah yang sama dengan jawaban lain dalam hal itu. Solusi untuk itu adalah plugin ke perpustakaan yang dapat menangani pemahaman konstruktor Kotlin dan memetakan properti ke parameter konstruktor, alih-alih membutuhkan konstruktor default. The Kotlin modul untuk Jackson melakukan hal ini, sehingga sangat jelas mungkin.
Lihat juga: https://stackoverflow.com/a/34624907/3679676 untuk eksplorasi opsi serupa.
sumber
lateinit
ketika Anda memiliki siklus hidup yang menjamin inisialisasi segera setelah konstruksi, ini dimaksudkan untuk kasus-kasus tersebut. Sedangkan delegasi lebih ditujukan untuk "kapan sebelum digunakan pertama kali". Meskipun secara teknis mereka memiliki perilaku dan perlindungan yang serupa, mereka tidak identik.false
untuk Ints dan Booleans masing-masing. Tidak yakin bagaimana itu akan mempengaruhi kode kerangka kerjaNilai awal diperlukan jika Anda ingin menggunakan kembali konstruktor untuk bidang yang berbeda, kotlin tidak diizinkan nol. Jadi, setiap kali Anda berencana menghilangkan bidang, gunakan formulir ini di konstruktor:
var field: Type? = defaultValue
jpa tidak memerlukan konstruktor argumen:
tidak ada duplikasi kode. Jika Anda memerlukan entitas konstruk dan hanya usia setup, gunakan formulir ini:
tidak ada keajaiban (baca dokumentasi saja)
sumber
Tidak ada cara untuk menjaga kekekalan seperti ini. Vals HARUS diinisialisasi ketika membangun instance.
Salah satu cara untuk melakukannya tanpa kekekalan adalah:
sumber
Saya telah bekerja dengan Kotlin + JPA cukup lama dan saya telah membuat ide saya sendiri bagaimana menulis kelas Entity.
Saya hanya sedikit memperluas ide awal Anda. Seperti yang Anda katakan kami dapat membuat konstruktor tanpa argumen pribadi dan memberikan nilai default untuk primitif , tetapi ketika kami mencoba perlu menggunakan kelas lain itu menjadi sedikit berantakan. Ide saya adalah membuat objek STUB statis untuk kelas entitas yang saat ini Anda tulis misalnya:
dan ketika saya memiliki kelas entitas yang terkait dengan TestEntity saya dapat dengan mudah menggunakan rintisan yang baru saja saya buat. Sebagai contoh:
Tentu saja solusi ini tidak sempurna. Anda masih perlu membuat beberapa kode boilerplate yang seharusnya tidak diperlukan. Juga ada satu kasus yang tidak dapat diselesaikan dengan baik dengan stubbing - hubungan orangtua-anak dalam satu kelas entitas - seperti ini:
Kode ini akan menghasilkan NullPointerException karena masalah ayam-telur - kita perlu STUB untuk membuat STUB. Sayangnya kita perlu membuat bidang ini nullable (atau beberapa solusi serupa) untuk membuat kode berfungsi.
Juga menurut saya memiliki Id sebagai bidang terakhir (dan nullable) cukup optimal. Kita seharusnya tidak menetapkannya dengan tangan dan membiarkan database melakukannya untuk kita.
Saya tidak mengatakan bahwa ini adalah solusi yang sempurna, tapi saya pikir itu memanfaatkan pembacaan kode entitas dan fitur Kotlin (mis. Null safety). Saya hanya berharap rilis JPA dan / atau Kotlin di masa depan akan membuat kode kita lebih sederhana dan lebih baik.
sumber
Seperti yang dinyatakan di atas Anda harus menggunakan
no-arg
plugin no yang disediakan oleh Jetbrains.Jika Anda menggunakan Eclispe, Anda mungkin harus mengedit Pengaturan Kompilator Kotlin.
Window> Preferences> Kotlin> Compiler
Aktifkan
no-arg
Plugin di bagian Compiler Plugins.Lihat: https://discuss.kotlinlang.org/t/kotlin-allopen-plugin-doesnt-work-with-sts/13277/10
sumber
Saya seorang nub sendiri tetapi tampaknya Anda harus menginisialisasi eksplisit dan mundur ke nilai nol seperti ini
sumber
Mirip dengan @pawelbial Saya telah menggunakan objek pengiring untuk membuat instance default, namun alih-alih mendefinisikan konstruktor sekunder, cukup gunakan arg konstruktor default seperti @iolo. Ini menghemat Anda harus mendefinisikan banyak konstruktor dan membuat kode lebih sederhana (meskipun diberikan, mendefinisikan objek pendamping "STUB" tidak persis membuatnya sederhana)
Dan kemudian untuk kelas yang berhubungan dengan
TestEntity
Seperti @pawelbial telah menyebutkan, ini tidak akan bekerja di mana
TestEntity
kelas "memiliki"TestEntity
kelas karena STUB tidak akan diinisialisasi ketika konstruktor dijalankan.sumber
Baris build Gradle ini membantu saya:
https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.jpa/1.1.50 .
Setidaknya, itu dibangun di IntelliJ. Gagal pada baris perintah saat ini.
Dan saya punya
dan
var path: LtreeType tidak berfungsi.
sumber
Jika Anda menambahkan plugin gradle https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.jpa tetapi tidak berfungsi, kemungkinan versinya sudah keluar. Saya berada di 1.3.30 dan tidak berhasil untuk saya. Setelah saya memutakhirkan ke 1.3.41 (terbaru saat penulisan), itu berhasil.
Catatan: versi kotlin harus sama dengan plugin ini, mis: ini adalah cara saya menambahkan keduanya:
sumber