JPA के साथ कोटलिन: डिफ़ॉल्ट कंस्ट्रक्टर नर्क


131

जेपीए की आवश्यकता के रूप में, @Entityकक्षाओं में डेटाबेस से पुनर्प्राप्त करने के लिए ऑब्जेक्ट्स को तुरंत हटाने के लिए एक डिफ़ॉल्ट (गैर-आर्ग) निर्माता होना चाहिए।

कोटलिन में, गुणों को प्राथमिक निर्माता के भीतर घोषित करने के लिए बहुत सुविधाजनक है, निम्न उदाहरण में:

class Person(val name: String, val age: Int) { /* ... */ }

लेकिन जब गैर-अर्ग कंस्ट्रक्टर को द्वितीयक घोषित किया जाता है, तो उसे प्राथमिक कंस्ट्रक्टर को पारित करने के लिए मूल्यों की आवश्यकता होती है, इसलिए उनके लिए कुछ मान्य मान आवश्यक हैं, जैसे:

@Entity
class Person(val name: String, val age: Int) {
    private constructor(): this("", 0)
}

मामले में गुण सिर्फ तुलना में कुछ अधिक जटिल प्रकार है जब Stringऔर Intविशेष रूप से जब प्राथमिक निर्माता और में ज्यादा कोड नहीं है यह पूरी तरह से उनके लिए मान प्रदान करने के लिए बुरा लग रहा है और वे गैर-व्यर्थ कर रहे हैं,, initब्लॉक और जब मापदंडों को सक्रिय रूप से उपयोग किया जाता है - - जब वे प्रतिबिंब के माध्यम से फिर से असाइन किए जाने वाले हों तो अधिकांश कोड को फिर से निष्पादित किया जाएगा।

इसके अलावा, val-प्रोसेसर को निष्पादित करने के बाद -प्रकार नहीं दिया जा सकता है, इसलिए अपरिहार्यता भी खो जाती है।

तो सवाल यह है कि कोटलिन कोड को जेपीए के साथ काम करने के लिए बिना कोड डुप्लीकेशन के कैसे अनुकूलित किया जा सकता है, "जादू" प्रारंभिक मूल्यों और अपरिवर्तनीयता का नुकसान?

PS क्या यह सच है कि JPA से अलग हाइबरनेट कोई डिफ़ॉल्ट कंस्ट्रक्टर वाली वस्तुओं का निर्माण कर सकता है?


1
INFO -- org.hibernate.tuple.PojoInstantiator: HHH000182: No default (no-argument) constructor for class: Test (class must be instantiated by Interceptor)- तो, ​​हां, हाइबरनेट डिफ़ॉल्ट कंस्ट्रक्टर के बिना काम कर सकता है।
माइकल पिफेल

जिस तरह से यह बसने वालों के साथ है - उर्फ: उत्परिवर्तन। यह डिफॉल्ट कंस्ट्रक्टर को इंस्टेंट करता है और फिर सेटर्स की तलाश करता है। मुझे अपरिवर्तनीय वस्तुएं चाहिए। एकमात्र तरीका यह किया जा सकता है कि हाइबरनेट्स कंस्ट्रक्टर को देखना शुरू कर दें। इस hibernate.atlassian.net/browse/HHH-9440
क्रिश्चियन बोंजियोर्नो

जवाबों:


145

कोटलिन 1.0.6 के रूप में , kotlin-noargसंकलक प्लगइन उन कक्षाओं के लिए सिंथेटिक डिफ़ॉल्ट अवरोध उत्पन्न करता है जिन्हें चयनित एनोटेशन के साथ एनोटेट किया गया है।

यदि आप ग्रेडिंग का उपयोग करते हैं, तो kotlin-jpaप्लगइन को लागू करना वर्गों के लिए डिफ़ॉल्ट निर्माणकर्ताओं को उत्पन्न करने के लिए पर्याप्त है @Entity:

buildscript {
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version"
    }
}

apply plugin: "kotlin-jpa"

मावेन के लिए:

<plugin>
    <artifactId>kotlin-maven-plugin</artifactId>
    <groupId>org.jetbrains.kotlin</groupId>
    <version>${kotlin.version}</version>

    <configuration>
        <compilerPlugins>
            <plugin>jpa</plugin>
        </compilerPlugins>
    </configuration>

    <dependencies>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-maven-noarg</artifactId>
            <version>${kotlin.version}</version>
        </dependency>
    </dependencies>
</plugin>

4
क्या आप शायद इस बात पर थोड़ा विस्तार कर सकते हैं कि यह आपके कोटलिन कोड के भीतर कैसे उपयोग किया जाएगा, भले ही यह "आपका data class foo(bar: String)परिवर्तन न हो " का मामला हो । यह कैसे इस जगह में फिट बैठता है का एक और अधिक पूर्ण उदाहरण देखने के लिए अच्छा होगा। धन्यवाद
२२:५० पर thecoshman

5
इस ब्लॉग पोस्ट है कि शुरू की है kotlin-noargऔर kotlin-jpaअपने उद्देश्य का ब्यौरा लिंक के साथ blog.jetbrains.com/kotlin/2016/12/kotlin-1-0-6-is-here
डैलिबर Filus

1
और CustomerEntityPK जैसे एक प्राथमिक कुंजी वर्ग के बारे में क्या है, जो एक इकाई नहीं है, लेकिन एक डिफ़ॉल्ट निर्माता की आवश्यकता है?
जन्ननिक

3
मेरे लिए काम नहीं कर रहा है। यह केवल तभी काम करता है जब मैं कंस्ट्रक्टर फ़ील्ड को वैकल्पिक बनाता हूं। जिसका मतलब है कि प्लगइन काम नहीं कर रहा है।
Ixx

3
@jannnik आप प्राथमिक कुंजी वर्ग को @Embeddableविशेषता के साथ चिह्नित कर सकते हैं भले ही आपको इसकी आवश्यकता न हो। इस तरह, यह द्वारा उठाया जाएगा kotlin-jpa
svick

33

सभी तर्कों के लिए बस डिफ़ॉल्ट मान प्रदान करें, कोटलिन आपके लिए डिफ़ॉल्ट निर्माता बना देगा।

@Entity
data class Person(val name: String="", val age: Int=0)

देखने के NOTEनिम्न अनुभाग नीचे दिए गए बॉक्स:

https://kotlinlang.org/docs/reference/classes.html#secondary-constructors


18
आपने स्पष्ट रूप से उसके प्रश्न को नहीं पढ़ा, अन्यथा आपने वह हिस्सा देखा होगा जहाँ वह बताता है कि डिफ़ॉल्ट तर्क खराब दिख रहे हैं, विशेष रूप से अधिक जटिल वस्तुओं के लिए। उल्लेख नहीं करने के लिए, कुछ के लिए डिफ़ॉल्ट मान जोड़ने से अन्य मुद्दों को छुपाता है।
स्नो डे

1
डिफ़ॉल्ट मान प्रदान करना बुरा क्यों है? यहां तक ​​कि जब जावा के नो आर्ग्स कांस्टेक्टर का उपयोग किया जाता है, तो डिफ़ॉल्ट मान फ़ील्ड को सौंपे जाते हैं (उदाहरण के लिए संदर्भ प्रकार शून्य)।
उमेश राजभंदरी

1
ऐसे समय होते हैं जिनके लिए आप एक समझदार चूक प्रदान नहीं कर सकते हैं। किसी व्यक्ति का दिया गया उदाहरण लें, आपको वास्तव में जन्मतिथि के साथ इसका मॉडल तैयार करना चाहिए क्योंकि इसमें बदलाव नहीं होता (बेशक, अपवाद कहीं न कहीं लागू होते हैं) लेकिन उसको देने के लिए कोई समझदार डिफ़ॉल्ट नहीं है। इसलिए एक शुद्ध कोड बिंदु देखें, आपको व्यक्ति निर्माता में एक DoB पास करना होगा, इस प्रकार यह सुनिश्चित करना कि आपके पास कभी ऐसा व्यक्ति नहीं हो सकता जिसकी वैध आयु नहीं है। समस्या यह है कि, जिस तरह से जेपीए काम करना पसंद करता है, वह एक नो-आर्ग कंस्ट्रक्टर के साथ एक ऑब्जेक्ट बनाना पसंद करता है, फिर सब कुछ सेट करें।
22

1
मुझे लगता है कि ऐसा करने का यह सही तरीका है, यह जवाब अन्य मामलों में काम करता है जो आप जेपीए या हाइबरनेट का उपयोग नहीं करते हैं। यह भी दस्तावेजों के अनुसार सुझाया गया तरीका है जैसा कि उत्तर में वर्णित है।
मोहम्मद रफी जूल

1
इसके अलावा, आपको जेपीए के साथ डेटा क्लास का उपयोग नहीं करना चाहिए: "वैल संपत्तियों के साथ डेटा वर्गों का उपयोग न करें क्योंकि जेपीए को अपरिवर्तनीय वर्गों या डेटा कक्षाओं द्वारा स्वचालित रूप से उत्पन्न विधियों के साथ काम करने के लिए डिज़ाइन नहीं किया गया है।" spring.io/guides/tutorials/spring-boot-kotlin/…
टेफेंस

11

@ D3xter के पास एक मॉडल के लिए एक अच्छा जवाब है, दूसरा कोटलिन में एक नई सुविधा है lateinit:

class Entity() {
    constructor(name: String, age: Date): this() {
        this.name = name
        this.birthdate = age
    }

    lateinit var name: String
    lateinit var birthdate: Date
}

आप इसका उपयोग तब करेंगे जब आपको यकीन होगा कि निर्माण के समय या कुछ समय बाद (मूल्यों के पहले उपयोग से पहले) मूल्यों में कुछ भरा जाएगा।

आप ध्यान दें कि मैं बदल ageगया birthdateक्योंकि आप के साथ आदिम मूल्यों का उपयोग नहीं कर सकते हैं lateinitऔर वे भी पल के लिए होना चाहिए var(प्रतिबंध भविष्य में जारी किया जा सकता है)।

तो अपरिवर्तनीयता के लिए एक सही जवाब नहीं, उस संबंध में अन्य उत्तर के समान समस्या। इसके लिए समाधान प्लग-इन पुस्तकालयों के लिए है जो कोटलिन कंस्ट्रक्टर को समझने और एक डिफ़ॉल्ट कंस्ट्रक्टर की आवश्यकता के बजाय, कंस्ट्रक्टर मापदंडों के लिए मैपिंग गुणों को समझ सकता है। जैक्सन के लिए Kotlin मॉड्यूल करता है, तो यह स्पष्ट रूप से संभव है।

यह भी देखें: समान विकल्पों की खोज के लिए https://stackoverflow.com/a/34624907/3679676


ध्यान दें कि लेटनीट और डेलिगेट्स। नॉटनुल () समान हैं।
Fasth

4
समान लेकिन समान नहीं। यदि डेलिगेट का उपयोग किया जाता है, तो यह जावा द्वारा वास्तविक क्षेत्र के क्रमांकन के लिए जो देखा जाता है उसे बदलता है (यह प्रतिनिधि वर्ग को देखता है)। इसके अलावा, lateinitजब आप निर्माण के तुरंत बाद प्रारंभ करने की गारंटी देने वाले एक अच्छी तरह से परिभाषित जीवनचक्र रखते हैं, तो इसका उपयोग करना बेहतर होता है, यह उन मामलों के लिए है। जबकि प्रतिनिधि "पहले उपयोग के कुछ समय पहले" के लिए अधिक अभिप्रेत है। यद्यपि तकनीकी रूप से उनके पास समान व्यवहार और सुरक्षा है, वे समान नहीं हैं।
जैसन मिनार्ड

यदि आपको आदिम मूल्यों का उपयोग करने की आवश्यकता है, तो केवल एक चीज जिसके बारे में मैं सोच सकता था, वह यह है कि किसी ऑब्जेक्ट को इंस्टेंट करते समय "डिफॉल्ट वैल्यू" का उपयोग किया जा सके, और इसका मतलब है कि मैं falseक्रमशः 0 और इन्टस और बुलियन का उपयोग कर रहा हूं । यकीन नहीं है कि फ्रेमवर्क कोड को कैसे प्रभावित करेगा, हालांकि
OzzyTheGiant

6
@Entity data class Person(/*@Id @GeneratedValue var id: Long? = null,*/
                          var name: String? = null,
                          var age: Int? = null)

प्रारंभिक मानों की आवश्यकता होती है यदि आप विभिन्न क्षेत्रों के लिए पुनः निर्माण निर्माता चाहते हैं, तो कोटलिन नल की अनुमति नहीं देता है। इसलिए जब भी आप ओइट फील्ड की योजना बना रहे हों, तो इस फॉर्म को कंस्ट्रक्टर में उपयोग करें:var field: Type? = defaultValue

jpa को कोई तर्क निर्माता की आवश्यकता नहीं है:

val entity = Person() // Person(name=null, age=null)

कोई कोड दोहराव नहीं है। यदि आपको निर्माण इकाई और केवल सेटअप आयु की आवश्यकता है, तो इस फ़ॉर्म का उपयोग करें:

val entity = Person(age = 33) // Person(name=null, age=33)

कोई जादू नहीं है (सिर्फ प्रलेखन पढ़ें)


1
हालांकि यह कोड स्निपेट प्रश्न को हल कर सकता है, जिसमें स्पष्टीकरण सहित वास्तव में आपकी पोस्ट की गुणवत्ता में सुधार करने में मदद करता है। याद रखें कि आप भविष्य में पाठकों के लिए प्रश्न का उत्तर दे रहे हैं, और उन लोगों को आपके कोड सुझाव के कारणों का पता नहीं चल सकता है।
दिमासन

@DimaSan, तुम ठीक कह रहे हैं, लेकिन वह धागा पहले से ही कुछ पदों में स्पष्टीकरण है ...
Maksim Kostromin

लेकिन आपका स्निपेट अलग है और हालांकि इसका एक अलग विवरण हो सकता है, वैसे भी अब यह बहुत स्पष्ट है।
दिमासन

4

इस तरह अपरिवर्तनीयता रखने का कोई तरीका नहीं है। उदाहरण का निर्माण करते समय Vals को आरंभीकृत किया जाना चाहिए।

अपरिवर्तनीयता के बिना इसे करने का एक तरीका है:

class Entity() {
    public constructor(name: String, age: Int): this() {        
        this.name = name
        this.age = age
    }

    public var name: String by Delegates.notNull()

    public var age: Int by Delegates.notNull()
}

तो यहां तक ​​कि हाइबरनेट को निर्माण स्तंभों पर स्तंभों को बताने का कोई तरीका नहीं है? खैर, हो सकता है, कोई ORM फ्रेमवर्क / लाइब्रेरी हो जिसमें नॉन-आर्ग कंस्ट्रक्टर की आवश्यकता न हो? :)
हॉटकी

उस बारे में निश्चित नहीं, लंबे समय तक हाइबरनेट के साथ काम नहीं किया। लेकिन किसी भी तरह नामांकित मापदंडों के साथ लागू करना संभव होना चाहिए।
D3xter

मुझे लगता है कि हाइबरनेट काम के एक बिट (बहुत अधिक) के साथ ऐसा कर सकता है। जावा 8 में, आपके पास वास्तव में आपके पास कंस्ट्रक्टर में नामित पैरामीटर हो सकते हैं और उन्हें मैप किया जा सकता है जैसे वे अभी खेतों में हैं।
क्रिश्चियन बोंजियोर्नो

3

मैं काफी समय से कोटलिन + जेपीए के साथ काम कर रहा हूं और मैंने अपना खुद का विचार बनाया है कि एंटिटी कक्षाएं कैसे लिखनी चाहिए।

मैं अभी आपके प्रारंभिक विचार को थोड़ा बढ़ाता हूं। जैसा कि आपने कहा हम निजी argumentless निर्माता बना सकते हैं और के लिए डिफ़ॉल्ट मान प्रदान करते हैं पुरातन है, लेकिन जब हम एक और कक्षाओं उपयोग करने की आवश्यकता की कोशिश यह थोड़ा जटिल हो जाता है। मेरा विचार यह है कि आप वर्तमान में लिखने वाले निकाय वर्ग के लिए स्टैटिक स्टब ऑब्जेक्ट बनाएँ:

@Entity
data class TestEntity(
    val name: String,
    @Id @GeneratedValue val id: Int? = null
) {
    private constructor() : this("")

    companion object {
        val STUB = TestEntity()
    }
}

और जब मेरे पास एंटिटी क्लास है जो TestEntity से संबंधित है तो मैं आसानी से स्टब का उपयोग कर सकता हूं जो मैंने अभी बनाया है। उदाहरण के लिए:

@Entity
data class RelatedEntity(
        val testEntity: TestEntity,
        @Id @GeneratedValue val id: Long? = null
) {
    private constructor() : this(TestEntity.STUB)

    companion object {
        val STUB = RelatedEntity()
    }
}

बेशक यह समाधान सही नहीं है। आपको अभी भी कुछ बॉयलरप्लेट कोड बनाने की आवश्यकता है जो आवश्यक नहीं होने चाहिए। इसके अलावा एक मामला यह है कि ठूंठ के साथ अच्छी तरह से हल नहीं किया जा सकता है - एक इकाई वर्ग के भीतर माता-पिता का बच्चा संबंध - इस तरह:

@Entity
data class TestEntity(
        val testEntity: TestEntity,
        @Id @GeneratedValue val id: Long? = null
) {
    private constructor() : this(STUB)

    companion object {
        val STUB = TestEntity()
    }
}

यह कोड चिकन-अंडे के मुद्दे के कारण NullPointerException का उत्पादन करेगा - हमें STUB बनाने के लिए STUB की आवश्यकता है। दुर्भाग्य से हमें कोड काम करने के लिए इस क्षेत्र को अशक्त (या कुछ इसी तरह के समाधान) बनाने की आवश्यकता है।

इसके अलावा मेरी राय में अंतिम क्षेत्र के रूप में आईडी (और अशक्त) काफी इष्टतम है। हमें इसे हाथ से नहीं बताना चाहिए और डेटाबेस को हमारे लिए करने देना चाहिए।

मैं यह नहीं कह रहा हूं कि यह सही समाधान है, लेकिन मुझे लगता है कि यह इकाई कोड पठनीयता और कोटलिन सुविधाओं (जैसे शून्य सुरक्षा) का लाभ उठाता है। मुझे उम्मीद है कि जेपीए और / या कोटलिन की भविष्य की रिलीज़ हमारे कोड को और भी सरल और अच्छे बनाएगी।


3

जैसा कि ऊपर कहा गया है कि आपको no-argJetbrains द्वारा प्रदान किए गए नो प्लगइन का उपयोग करना है ।

यदि आप Eclispe का उपयोग कर रहे हैं तो आपको Kotlin Compiler Settings को एडिट करना पड़ सकता है।

विंडो> प्राथमिकताएं> कोटलिन> कंपाइलर

no-argकंपाइलर प्लगइन्स अनुभाग में प्लगिन को सक्रिय करें ।

देखें: https://discuss.kotlinlang.org/t/kotlin-allopen-plugin-doesnt-work-with-sts/13277/10


2

मैं खुद एक नब हूं लेकिन लगता है कि आपको इस तरह के मूल्य को कम करने के लिए इनिशियलाइज़र और कमबैक करना होगा

@Entity
class Person(val name: String? = null, val age: Int? = null)

1

@Pawelbial के समान, मैंने डिफ़ॉल्ट ऑब्जेक्ट बनाने के लिए साथी ऑब्जेक्ट का उपयोग किया है, हालांकि एक द्वितीयक कंस्ट्रक्टर को परिभाषित करने के बजाय, बस डिफ़ॉल्ट निर्माता का उपयोग करता है जैसे @iolo। यह आपको कई कंस्ट्रक्टर को परिभाषित करने से बचाता है और कोड को सरल रखता है (हालांकि "STUB" को परिभाषित करना साथी वस्तुओं को बिल्कुल सरल नहीं बनाए रखता है)

@Entity
data class TestEntity(
    val name: String = "",
    @Id @GeneratedValue val id: Int? = null
) {

    companion object {
        val STUB = TestEntity()
    }
}

और फिर उन वर्गों के लिए जो संबंधित हैं TestEntity

@Entity
data class RelatedEntity(
    val testEntity: TestEntity = TestEntity:STUB,
    @Id @GeneratedValue val id: Int? = null
)

जैसा कि @pawelbial ने उल्लेख किया है, यह काम नहीं करेगा जहां TestEntityक्लास "में एक" TestEntityक्लास है क्योंकि STUB को कंस्ट्रक्टर के चलने पर शुरू नहीं किया जाएगा।


1

इन ग्रेडेल बिल्ड लाइनों ने मेरी मदद की:
https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.jpa/1.1.50
कम से कम, यह इंटेलीज में बनाता है। यह फिलहाल कमांड लाइन पर फेल है।

और मुझे ए

class LtreeType : UserType

तथा

    @Column(name = "path", nullable = false, columnDefinition = "ltree")
    @Type(type = "com.tgt.unitplanning.data.LtreeType")
    var path: String

var पथ: LtreeType काम नहीं किया।


1

यदि आपने ग्रेडिंग प्लगइन https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.jpa जोड़ा है, लेकिन काम नहीं किया है, तो संभावना है कि संस्करण दिनांकित है। मैं 1.3.30 पर था और यह मेरे लिए काम नहीं करता था। मैंने 1.3.41 (लेखन के समय नवीनतम) में अपग्रेड करने के बाद काम किया।

नोट: कोटलिन संस्करण इस प्लगइन के समान होना चाहिए, जैसे: यह है कि मैंने दोनों को कैसे जोड़ा:

buildscript {
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version"
    }
}

मैं अंतरिक्ष यात्री के साथ काम कर रहा हूं, और मुझे 1.3.41 संस्करण के साथ काम करने के लिए मिला। ग्रैडल का कहना है कि मेरा कोटलिन संस्करण 1.3.21 है और मैंने कोई भी समस्या नहीं देखी, अन्य सभी प्लगइन्स ('kapt / jvm / allopen') 1.3.21 पर हैं। मैं प्लगइन्स का उपयोग भी कर रहा हूँ DSL प्रारूप
Gavin
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.