परिचय: Kotlin + JPA में "डिफ़ॉल्ट कंस्ट्रक्टर की समस्या"
\n\nअगर आपने कभी Kotlin को JPA (Java Persistence API) के साथ उपयोग करने की कोशिश की है, तो आपने शायद एप्लिकेशन चलाते समय Hibernate से कुछ इस तरह का एरर देखा होगा:
\n\norg.hibernate.InstantiationException: No default constructor for entity: com.example.User\n\n\nपरिचित लग रहा है? हाँ, यह वही समस्या है जिसने Stack Overflow पर हज़ारों व्यूज़ बटोरे हैं और इसे "Kotlin with JPA: default constructor hell" का नाम दिया गया है।
\n\nइस लेख में हम शांति से, कदम दर कदम, समझेंगे:
\n- \n
- यह एरर क्यों आता है \n
- Kotlin खाली कंस्ट्रक्टर क्यों नहीं बनाता \n
- समस्या को हल करने के तीन सिद्ध तरीके (सरल से पेशेवर तक) \n
- आपके प्रोजेक्ट के लिए कौन सा तरीका सबसे अच्छा है \n
चलिए शुरू करते हैं!
\n\nHibernate को बिना पैरामीटर वाले कंस्ट्रक्टर की आवश्यकता क्यों है?
\n\nपहले Java को समझते हैं। क्लासिक Java में हम आमतौर पर ऐसे लिखते हैं:
\n\npublic class User {\n private Long id;\n private String name;\n\n // अनिवार्य खाली कंस्ट्रक्टर\n public User() {}\n\n // गेटर्स, सेटर्स...\n}\n\n\nHibernate (और कोई भी JPA इम्प्लीमेंटेशन) ऑब्जेक्ट बनाने के लिए रिफ्लेक्शन का उपयोग करता है। जब वह डेटाबेस से एक रो पढ़ता है, तो उसे User क्लास का एक इंस्टेंस बनाना होता है, और फिर रिफ्लेक्शन के ज़रिए फ़ील्ड्स को भरना होता है। इसके लिए उसे बिना पैरामीटर वाले कंस्ट्रक्टर (default constructor) की ज़रूरत होती है।
Java में, यदि आपने कोई अन्य कंस्ट्रक्टर डिफ़ाइन नहीं किया है, तो कंपाइलर अपने आप एक खाली कंस्ट्रक्टर बना देता है। इसलिए आमतौर पर यह समस्या नहीं आती।
\n\nKotlin में क्या खास है?
\n\nKotlin एक ऐसी भाषा है जो अपरिवर्तनीयता (immutability) और सुरक्षा पर ज़ोर देती है। एक सामान्य Kotlin क्लास देखें:
\n\ndata class User(\n val id: Long = 0,\n val name: String = ""\n)\n\n\nयहाँ दो महत्वपूर्ण बातें हो रही हैं:
\n- \n
- सभी प्रॉपर्टीज़ प्राथमिक कंस्ट्रक्टर (primary constructor) में घोषित की गई हैं \n
- प्रॉपर्टीज़ को
val(अपरिवर्तनीय) के रूप में घोषित किया गया है और उनके डिफ़ॉल्ट मान हैं \n
Kotlin अलग से कोई खाली कंस्ट्रक्टर नहीं बनाता अगर आपके पास पैरामीटर वाला प्राथमिक कंस्ट्रक्टर है। भले ही सभी पैरामीटर के डिफ़ॉल्ट मान हों, JVM बाइटकोड स्तर पर इसे "असली" बिना पैरामीटर वाला कंस्ट्रक्टर नहीं माना जाता।
\n\nजब Hibernate रिफ्लेक्शन के ज़रिए User.class.newInstance() को कॉल करने की कोशिश करता है, तो उसे खाली कंस्ट्रक्टर नहीं मिलता — और वह एरर देता है।
तरीका 1: सबसे आसान — स्पष्ट रूप से द्वितीयक कंस्ट्रक्टर जोड़ें
\n\nसबसे सीधा तरीका है मैन्युअल रूप से दूसरा, खाली कंस्ट्रक्टर जोड़ना:
\n\ndata class User(\n val id: Long = 0,\n val name: String = ""\n) {\n // बिना पैरामीटर वाला द्वितीयक कंस्ट्रक्टर\n constructor() : this()\n}\n\n\nयहाँ constructor() : this() का मतलब है: "डिफ़ॉल्ट मानों के साथ प्राथमिक कंस्ट्रक्टर को कॉल करो"।
फायदे:
\n- \n
- बहुत सरल और समझने में आसान \n
- किसी प्लगइन या अतिरिक्त सेटिंग की आवश्यकता नहीं \n
नुकसान:
\n- \n
- हर एंटिटी के लिए यह कंस्ट्रक्टर लिखना पड़ता है \n
- अगर आपके पास 10-15 एंटिटीज़ हैं, तो यह उबाऊ हो जाता है \n
- इसे जोड़ना भूलना आसान है \n
तरीका 2: open class का उपयोग करें और सभी प्रॉपर्टीज़ को var बनाएं
\n\nइंटरनेट पर कई उदाहरण यह समाधान सुझाते हैं:
\n\nopen class User {\n var id: Long = 0\n var name: String = ""\n}\n\n\nयहाँ हम क्लास को open बनाते हैं (ताकि Hibernate प्रॉक्सी बना सके), और प्रॉपर्टीज़ को var (परिवर्तनीय) बनाते हैं। और कोई प्राथमिक कंस्ट्रक्टर नहीं — इसका मतलब है कि Kotlin डिफ़ॉल्ट रूप से खाली कंस्ट्रक्टर जनरेट करेगा।
फायदे:
\n- \n
- बहुत सरल \n
- कोई अतिरिक्त कोड नहीं \n
नुकसान:
\n- \n
- सभी प्रॉपर्टीज़
varहोनी चाहिए, जो Kotlin की अपरिवर्तनीयता की भावना के खिलाफ है \n - डेटा क्लास की सुविधाएँ (जैसे
copy(),equals()) खो जाती हैं \n - कोड अधिक Java जैसा हो जाता है \n