توسعهدهنده نرمافزار
نگاشتگرهای شیء به رابطه (ORM)
مطلبی که میخوانید ترجمه قسمت ۱۲۱ از رادیو مهندسی نرمافزار است. رادیو مهندسی نرمافزار هر یکی دو هفته یک بار مصاحبهای درباره یکی از موضوعات حوزه مهندسی نرمافزار با افراد خبره و با تجربه در موضوع مورد بحث ترتیب میدهد.
در این قسمت با مایکل پلاد درباره فناوری نگاشت شیء به رابطه مصاحبه میشود. او درباره مفاهیم مشترک نگاشتگرها صحبت میکند و طیف ابزارهای مختلفی که تحت این نام وجود دارند را با هم مقایسه میکند. در ادامه به پیامدهایی که استفاده از نگاشتگرهای شیء به رابطه در معماری و طراحی دارند میپردازد.
مایکل، لطفاً خود را معرفی کنید.
سلام، از اینکه به این مصاحبه رادیو مهندسی نرمافزار گوش میدهید ممنونم. نام من مایکل پلاد است. من در شرکت فناوریهای سناکور در نورمبرگ آلمان کار میکنم. نقش من توسعهدهنده ارشد یا معمار (هر چه اسمش را بگذارید) است. معمولاً بر روی پروژههای بلندمدتتر کار میکنم. چند سال پیش کار با نگاشتگرهای شیء به رابطه را آغار کردم. بیشتر با نگاشتگرهای مبتنی بر جاوا از قبیل Hibernate و JDO و امروزه JPA کار کردهام. خوشحال میشوم که سئوالات شما را بشنوم و امیدوارم بتوانم به آنها جواب دهم.
بیایید با این سئوال شروع کنیم که نگاشتگر شیء به رابطه چیست؟ درباره چیست؟
اساساً نگاشتگر شیء به رابطه چیزی است که به مسأله عدم تطابق بین اشیاء با روابط میپردازد. بین یک نرمافزار شیءگرا و پایگاههای داده یک عدم تطابق طبیعی وجود دارد. بهعنوان مثال اگر به نحوه گروه کردن اشیاء یک حوزه (Domain) در مقابل نحوه تعریف کردن شِمای رابطهای (Schema) نگاه کنید، بهروشنی میبینید که از یک طرف با کلاسها و خواص (Attribute) کار میکنید و نوعهای دادهای دارید که یا مربوط به زبان برنامهنویسی است یا انواع دادهای است که خودتان داخل زبانی که با آن کار میکنید، تعریف میکنید. اما در طرف دیگر با جداول و ستونها کار میکنید و این چیزها را داخل شِماها، گروه میکنید. در یک پایگاه داده رابطهای نیز یک سیستمِ انواع داده دارید. اما در غالب پایگاههای داده این سیستم انواع داده خیلی ایستا است. البته برخی از پایگاههای داده، گونههایی از پشتیبانی انواعهای داده تعریف شده توسط کاربر را دارند. اما غالباً این پشتیبانی خیلی پیشرفته نیست.
مطلب دیگر، جابهجا شدن (Navigation) است. وقتی در گرافی از اشیاء جابهجا میشوید، در یک نرمافزار شیءگرا یا کلاً در نرمافزار میدانید که چطور از یک موجودیت (Entity) مثلاً موجودیت A به سمت موجودیت B جابهجا شوید. اما در طرف دیگر (در پایگاههای رابطهای)، جابهجایی، جهت ندارد. میتوانید از جدول مشتریان به جدول صورتحسابها جابهجا شوید اما در همان حال میتوانید از طریق کلید خارجی (Foreign Key) از جدول صورتحسابها به جدول مشتریان هم جابهجا شوید. بنابراین میبینید که یک مسیر جابهجایی وجود ندارد.
بسیار خوب، اما برای دسترسی به دادهها، زبان برنامهنویسی SQL را دارید. چرا به نگاشتگر شیء به رابطه نیاز دارید؟ مزیت استفاده از یک نگاشتگر شیء به رابطه بر روی SQL نسبت به استفاده از فنونی که برای دسترسی مستقیم به پایگاه داده در زبانهای برنامهنویسی وجود دارد، چیست؟
اساساً از یک نگاشتگر شیء به رابطه برای کپسوله کردن پایگاه داده استفاده میکنید. خیلی از شرکتها میگویند که با استفاده از نگاشتگر شیء به رابطه میتوانید بهسادگی بین پایگاههای داده مختلف تعویض کنید. بله، این درست است. اما من میگویم که در اکثر پروژههای شرکتهای بزرگ، تعویض کردن پایگاه داده، چیز خیلی نادری است. فکر میکنم مورد کاربرد و مزیت اصلی استفاده از یک نگاشتگر OR این است که توسعهدهندگان با اشیاء کار کنند و مقدار زیادی کدهای منطق کسب و کار (Business Logic) ننویسند که اغلب مربوط به پردازش دستورات SQL باشد.
بهعلاوه، کتابخانههایی که تعامل با SQL را پیادهسازی میکنند، خیلی سطح پایین هستند. بهعنوان مثال اگر به JDBC، دقت کنید، یک کتابخانه سطح پایین است که در آن با دستورات و ResultSet ها کار میکنید و باید بر روی نتایج حرکت کنید و خیلی از امور مربوط به مدیریت منابع (Resource Management) را انجام دهید. از این منظر، نگاشتگر شیء به رابطه کاملاً این چیزها را کپسوله میکند. [در آنجا] با فایلهای XML، یا حاشیهگذاریها (Annotation) یا حتی از طریق قراردادهای نامگذاری (Code Convention)، گونههایی از فراداده (Metadata) را دارید که بهوسیله آنها، توضیح میدهید که چطور کلاسهایتان را به جداول نگاشت میکنید و توضیح میدهید که چطور ارثبری و روابط یا چیزهای دیگر را نگاشت میکنید.
بنابراین، آیا کاربرد نگاشتگر شیء به رابطه، بهمنظور شبیه ساختن یک پایگاه داده رابطهای به یک پایگاه داده شیء گرا است؟
خیر، فکر نمیکنم منظور اصلی این بوده باشد. البته از منظر توسعهدهندهها، در عین حال که با اشیاء کار میکنید همچنان به پایگاه داده هم دسترسی دارید. بهعنوان مثال در JPA یا Hibernate، چیزی با عنوان جلسه (Session) یا مدیر موجودیت (Entity Manager) دارید که یک زمینه ثبت کردن (Persistence Context) است که میتواند به مثابه یک پایگاه داده شیء گرا نگریسته شود. در آنجا برخی متدهای مرتبط با ثبت از قبیل ()save و ()list و ()retrieve و چیزهای شبیه به این داریم. اما آنها در واقع، پایگاه داده شیء گرا نیستند. به نظر من اشتباه است که با نگاشتگرهای شیء به رابطه مانند پایگاه دادههای شیءگرا رفتار کنیم. البته آنها برخی ویژگیهای پایگاه دادههای شیء گرا را دارند. مثلاً برخی پرسوجوهایی که بهصورت ضابطه (Criteria) در برخی پیادهسازیهای نگاشتگرهای شیء به رابطه داریم را در پایگاه دادههای شیء گرا هم داریم اما در اینجا در انتها دادهها بر روی یک مخزن داده رابطهای ذخیره میشوند.
بنابراین گرچه یک الگوی برنامهنویسی شیء گرا دارید، همچنان باید آگاه بود که بهعلت جنبههای مربوط به کارایی و دیگر جنبههای غیرعملکردی (Nonfunctional)، دادهها بر روی یک پایگاه رابطهای ذخیره میشوند.
دقیقاً. من میگویم این توهم است که فکر کنید وقتی از یک نگاشتگر شیء به رابطه استفاده میکنید، دیگر نیازی به نگرانی در مورد مفاهیم رابطهای و SQL ندارید. میدانم که تبلیغات زیادی دراینباره وجود دارد اما فکر میکنم این طرز فکر واقعاً خطرناک است زیرا شما بهعنوان توسعهدهندهای که با یک نگاشتگر شیء به رابطه کار میکنید باید بتوانید ببینید که چه جملات SQL ای به پایگاه داده ارسال میشود و آیا آنها جملات SQL خوبی هستند یا جملات بدی هستند؟ و اینکه آیا تعداد خیلی زیادی از آنها ارسال میشود؟ بنابراین بهعقیده من، اگر میخواهید کارتان را با یک نگاشتگر شیء به رابطه آغاز کنید همچنان به پیشزمینه SQL و پایگاه داده رابطهایتان، نیاز دارید. خصوصاً اگر کارایی بالا (Performance) بخواهید این مطلب خیلی اهمیت مییابد.
بله، متوجه میشوم. اما چند لحظه پیش گفتید که نگاشتگر شیء به رابطه، پایگاه داده را از شما کپسوله میکند اما الان میگویید که همچنان باید از جملات SQL ای که بخشهای مابین نگاشتگر و پایگاه داده است نیز آگاه باشید. آیا این مزایایی که میتوانید داشته باشید را کاهش نمیدهد؟
بله و خیر. واقعیتهایی دراینباره وجود دارد. بهعقیده من، نگاشتگر شیء به رابطه یک راه حل صددرصد کامل برای همه کاربردهای موجود نیست. خصوصاً اگر بخواهید بهبودهایی در میران کارایی پایگاه دادههای میراث (Legacy) داشته باشید. من میگویم استفاده از نگاشتگر شیء به رابطه همواره یک راه حل ۹۵ درصدی یا ۹۰ درصدی برای زمانی است که با مسأله ترکیب کردن یک پایگاه داده رابطهای با مدلهای شیء گرا روبرو هستید. در اغلب موارد، کپسولهسازی را انجام میدهد. یعنی دیگر نیاز ندارید جملات SQL را بنویسید یا مدیریت منابع کنید. در غالب موارد فقط توضیح میدهید که یک شیء چگونه به پایگاه داده نگاشت مییابد و بدون اینکه مشکلی داشته باشید با آن کار میکنید. البته شرایط نادری وجود دارد که ممکن است به گزینه برگشت به SQL فکر کنید. من قطعاً این گزینه را منع نمیکنم اما فقط بادقت آن را اختیار کنید. برگشت به SQL، تنها در حالتهای استثنا، ارزش فکر کردن دارد.
بنابراین، [استفاده از نگاشتگرهای شیء به رابطه] مفید است. در اکثر موارد از طریق کپسولهسازیهای خیلی زیاد کمک میکند اما در برخی شرایط خاص باید از اینکه زیر آن چه اتفاقی میافتد آگاه باشید. یعنی در وراء تبلیغاتی که در مورد آن میشود، باید بدانید هر چند مفید است اما به هر حال برای برخی شرایط باید از جزییات آگاه باشید.
قطعاً. من میگویم وقتی که از نگاشتگرهای شیء به رابطه استفاده میکنید، لازم است که افرادی را در تیم داشته باشید که دانش جزییات نگاشتگرهای شیء به رابطه و SQL را داشته باشند. آنگاه آنها، دانش را انتشار خواهند داد. بنابراین توسعهدهندهای که در حال کد زدن منطقهای عادی کسب و کار است نیاز ندارد که به همه ریزهکاریها و اجزاء دقت کند. بنابراین من نمیگویم که باید همه توسعهدهندهها در زمینه نگاشتگرهای شیء به رابطه یا SQL حرفهای باشند. فقط لازم است اطمینان یابید که دانش در دسترستان هست. و این برای توسعهدهندههای جاوا و روبی چیز کاملاً مطلوبی است که بهسراغ مدیرسیستم پایگاه داده (DBA) بروند و به او بگویند: «آیا این کاری که من اینجا دارم میکنم درست است؟». اغلب بین این تیمها (تیم توسعه و تیم پایگاه داده و DBA) مرزهای زیادی وجود دارد اما اشتباه است و این مرزها نباید باشد.
قطعاً درست است. بگذارید بهسراغ جزییات مفاهیم پشت نگاشتگرهای شیء به رابطه برویم. نگاشتگر شیء به رابطه عملاً چه کاری میکند؟
آنچه که نگاشتگر شیء به رابطه انجام میدهد این است که در ابتدای کار یک سری تنظیمات دریافت میکند بهعنوان مثال تعریف میکنید که چگونه به پایگاه داده رابطهای دسترسی پیدا میکنید. میگویید که برای همگامسازی تراکنشها (Transaction) و برای پیدا کردن منابع داده (Data Source) و اتصالات به پایگاه داده، آیا در محیطهای مدیریتشده (Managed) کار میکنید یا محیطهای غیرمدیریت شده. تفاوتهایی بین محیطهایی که بر روی آن اجرا میشوید وجود دارد اما من نمیخواهم اینجا وارد جزییات شوم زیرا اینها بیشتر مرتبط با زیرساختها است. مطلب مهمتر این است که نگاشتگر شیء به رابطه، نوعی اطلاعات فرادادهای را میخواند که بیان میکند چگونه اشیاء را به جداول، نگاشت میدهید و اینکه چگونه روابط بین اشیاء را تعریف میکنید و چطور این روابط در پایگاه داده انعکاس مییابد. مثلاً برای یک رابطه یک به چند به منظور الحاق جداول به هم، گزینههای خیلی زیادی دارید. باید این اطلاعات را فراهم کنید و نگاشتگر شیء به رابطه با استفاده از این اطلاعات قادر خواهد بود دستورات SQL ای را فراهم کند که با اعمالی که میخواهید با نگاشتگر انجام دهید مطابقت داشته باشد.
مفهوم مهم دیگر که در ارتباط با یک نگاشتگر شیء به رابطه داریم، مفهوم «لهجه» (Dialect) است. لازم است که نگاشتگر بتواند با انواع پایگاه دادههای ممکن صحبت کند زیرا قصد دارید انتزاع داشته باشید و محدود به یک پایگاه داده خاص، نباشید. به همین دلیل است که [استفاده از] نگاشتگر شیء به رابطه، برای نرمافزارهایی که فروخته میشوند تا بر روی همه محیطهای ممکن اجرا شوند، خیلی محبوب است.
یعنی اگر تولیدکنندگان مختلف پایگاه داده، یک مسأله را با راههای مختلفی حل کرده باشند، نگاشتگر شیء به رابطه میداند که با هرکدام از آنها چگونه و با چه لهجهای، صحبت کند بدون اینکه توسعهدهنده را مجبور کند که تفاوتهای همه این محصولات را بشناسد.
بله، دقیقاً. و حتی یک نگاشتگر شیء به رابطه خوب، بیشتر از اینها دانش دارد. بهعنوان مثال، برخی دستورات SQL بر روی یک پایگاه داده خاص، بهتر از دستورات دیگر عمل میکنند. بنابراین دستورات SQL بهینه شده برای لهجههای مختلف هم خواهید داشت. وقتی بخواهیم در مورد تأثیرات نگاشتگرهای شیء به رابطه بر روی کارایی صحبت کنیم، به این مبحث باز خواهم گشت.
از مفاهیم اصلی دیگر یک نگاشتگر شیء به رابطه، سیستم انواع (Type System) است. در دنیای برنامهنویسی که شما با آن کار میکنید انواعی از دادهها وجود دارد و پایگاه داده هم، انواع داده خاص خودش را دارد. بنابراین، گاهی مواقع میخواهید که این انواع را به هم تبدیل کنید. نگاشتگر باید تسهیلاتی برای چگونگی نگاشت دادن انواعتان داشته باشد بهعنوان مثال وقتی میخواهید چگونگی ذخیره شدن نوع داده پول (Currency) در پایگاه داده را تعریف کنید. این بخش هم، یک بخش خیلی مهم از نگاشتگر شیء به رابطه است.
بنابراین، نگاشتگر شیء به رابطه، بهنوعی، بر میزان انتزاع میافزاید. وقتی میخواهید دادههایتان را در پایگاه داده مستقر کنید، سطح انتزاع بالاتری خواهید داشت. نیاز ندارید که همه تبدیلات را صریحاً در دستورات خام SQL قرار دهید. درست است؟
بله، دقیقاً. شما از سیستم انواع، انتزاع مییابید. شما به عنوان یک برنامهنویس جاوا یا روبی یا ....، با انواع خودتان کار میکنید و لازم نیست که با هیچ نوع خاصی از SQL یا نوع خاصی از نگاشتگر شیء به رابطه، کار کنید و نگاشتگر است که لازم است این انتزاع از انواع داده خاص پایگاه داده را فراهم کند.
بنابراین شما میگویید که یک نگاشتگر شیء به رابطه، سیستم انواع مربوط به برنامه را به سیستم انواع پایگاه داده تبدیل میکند و شما نحوه نگاشت بین آن دو را در تنها یک مکان تعریف میکنید و میتوانید در همه جا از انواع داده مخصوص به منطق برنامه خودتان استفاده کنید و عملیات نگاشت بهصورت خودکار انجام میشود.
دقیقاً. در حالت ایدهآل، برخی پیشفرضها را دارید مثلاً string را به text یا varchar تبدیل میکنید. اما میتوانید این را تغییر داده و بگویید میخواهم این چیز را به آن چیز نگاشت دهم.
بسیار خوب. اینها در مورد انواع اولیه (Primitive) زبان بودند. در ارتباط با رابطه بین دادهها، ارجاعات، کلید خارجی و این چیزها چطور؟
این چیزها باید در فرادادههای (Metadata) نگاشتگر، مشخص شوند. یک مثال سنتی در مورد تفاوت بین پایگاه دادههای رابطهای و نرمافزارهای شیء گرا، روابط چند به چند هستند. در دنیای رابطهای، برای آنها، با ۳ جدول کار میکنید و همواره یک جدول الحاق (Join Table) یا نگاشت در میان قرار میگیرد، در حالی که در نرمافزار، فقط دو کلاس دارید که در هر کدامشان یک مجموعه (Collection) دارید. بنابراین اگر در کدتان، روابط یکطرفه یا دوطرفهای داشته باشید، باید مشخص کنید که این روابط چگونه در پایگاه داده طراحی شوند. بهعنوان مثال اگر یک رابطه یک به چند داشته باشید و اگر مثلاً این رابطه اختیاری (Optional) باشد، میتوانید مشخص کنید که برایش یک جدول الحاق داشته باشید و ستون کلید خارجی و مواردی از این قبیل را تعیین کنید. این بخشی از فراداده و نیز بخشی از کدتان است. نکته خیلی مهم در اینجا این است که غالب نگاشتگرهای شیء به رابطه، برای شما، روابط دوطرفه را مدیریت نمیکنند. بنابراین این چیزی است که باید در کد شما انجام شود.
در ارتباط با هویت شیء (Object Identity) چطور؟ در دنیای رابطهای، همه چیزها مبتنی بر مقدار (Value Based) هستند و اگر دو سطر باشند که در مقادیر کلیه فیلدهایشان برابر باشند، نمیتوان آنها را از هم تمیز داد اما در زبانهای شیءگرا میتوانید اشیائی داشته باشید که مقادیر و فیلدهای یکسانی داشته باشد اما همچنان اشیاء مجزایی باشند. نگاشتگرهای شیء به رابطه چگونه این فاصله را پر میکنند؟
من با مفهوم موجودیتها (Entity) آغاز میکنم. تعداد زیادی از نگاشتگرهای شیء به رابطه بین مولفهها یا اشیاء مبتنی بر مقدار با موجودیتها تفاوت قائل میشوند. موجودیت همواره یک شناسه ثابت دارد. این مفهومی است که به خوبی توسط اریک ایوانز در کتاب طراحی مبتنی بر دامنه (Domain Driven Design) بیان شده است. من دوست دارم که همین دو کلمه را برای نگاشتگرهای شیء به رابطه هم استفاده کنم. بگذارید با موجودیت آغاز کنیم. موجودیت، یک شناسه ثابت دارد. این، در دنیای رابطهای به کلید اصلی (Primary Key) ارجاع دارد. اگر در یک مجموعه نتایج (Result Set) سطری داشته باشیم که کلید اصلی آن با سطر دیگری در مجموعه نتایج دیگری برابر باشد، آن دو سطر یکسان هستند. به همین خاطر است که همواره برای یک نگاشتگر شیء به رابطه، خصوصیت کلید اصلی یا خصوصیت شناسه را مشخص میکنید. اغلب بهعنوان کلید اصلی با یک ستون یا یک ویژگی در کلاس موجودیتتان کار میکنید اما این میتواند به کلیدهای اصلی ترکیبی نیز گسترش یابد. به این ترتیب که دو یا سه ستون، کلید اصلی را بسازند و دو یا سه خصیصه، شناسه یا کلید اصلی شیء را بسازند. به این ترتیب نگاشتگرهای شیء به رابطه، کلید اصلی را بازتاب میدهند.
در این زمینه، مفهومی با عنوان بررسی هویت (Identity Check) وجود دارد بعنوان مثال در زبان جاوا، برای اینکه یک کلید کسبوکاری (Business Key) داشته باشید، ()equals و ()hashCode را پیادهسازی میکنید. من دیدهام که در بسیاری از پروژهها، کلید اصلی بخشی از این متدها را تشکیل میدهند که امر خطرناکی است زیرا کلید اصلی ممکن است در مراحل بعدی، ساخته شود. اگر یک شیء جدید مثلاً یک شیء مشتری جدید بسازید، کلید اصلی در انتهای تراکنش یا وقتی که شیء را در پایگاه داده ذخیره میکنید، ساخته میشود. بنابراین میتوانید دو شیء مشتری داشته باشید که یکی از آنها کلید اصلیاش بر طبق نگاشت پیشفرض، بهوسیله یک سرویس یا وب سرویس مقداردهی شده و دیگری به تازگی ساخته شده باشد. از دیدگاه منطقی دارید درمورد یک شیء صحبت میکنید اما اگر در متدهای ()equals و ()hashCode از کلید اصلی استفاده کرده باشید، در آن صورت بین این اشیاء تفاوت خواهید دید.
نگاشتگرهای شیء به رابطه با این مسأله چطور برخورد میکنند که میتوانیم چند شیء در زبان شیء گرا، داشته باشیم که به سطر یکسانی در پایگاه داده ارجاع دارند؟ چون این مفهوم هویت شیء (Object Identity)، در پایگاه داده وجود ندارد.
از مفاهیم خیلی مهم برای برخی نگاشتگرهای شیء به رابطه، مفهوم هویت شیء است. این مفهوم در مورد همه نگاشتگرهای شیء به رابطهای که به نوعی با مفهوم زمینه ثبت (Persistence Context) کار میکنند، وجود دارد. این مفهوم، در اغلب نگاشتگرهای شیء به رابطه، با عناوین واحد کاری (Unit of Work) و محتوای نهانگاه سطح اول (First Level Cache) نیز نامیده میشود. بهعنوان مثال در یک چرخه ساده درخواست و پاسخ (Request & Response) در برنامههای تحت وب، یک زمینه ثبت را باز میکنیم و مشتریهایی با شناسههای ۱ و ۲ و ۳ را بارگذاری میکنیم و بعداً این مشتریها در زمینه ثبت، ذخیره میشوند. اگر در یک متد منطقی دیگر، به همین زمینه ثبت مراجعه کنیم و مشتریهایی با همین شناسهها را دوباره بارگذاری کنیم، اشیاء یکسانی خواهیم گرفت. در این حالت هم عملگر مقایسه یعنی == برای دو شیء مورد نظر مقدار true برمیگرداند و هم طبعاً متدهای ()equals و ()hashCode اینطور خواهند بود. اما اگر زمینه ثبت را ببندید یا اینکه اشیاء را از زمینه ثبت حذف کنید، دیگر تضمینی در مورد یکسان بودن اشیاء نخواهید داشت و در این حالت نمیتوانید اطمینان داشته باشید که همچنان با اشیاء یکسانی کار میکنید.
بسیار خوب. اساساً شما این مفهوم «واحد کاری» را دارید به این معنا که متناظر با یک تراکنش پایگاه داده و داخل یک واحد کاری، فقط یک شیء دارید که متناظر با یک سطر باشد. اما اگر چندین تراکنش داشته باشید که بهصورت موازی در حافظه اجرا میشوند و تثبیت (Commit) میشوند، هر نوع ناسازگاری (Conflict) میتواند به وجود آید.
بله، دقیقاً.
به نظر میرسد فقط دو کار است که نگاشتگرهای شیء به رابطه انجام میدهند. یک مورد اساساً تغییر دادن و نوشتن اشیاء است و چیز دیگر پرسوجو گرفتن (Querying) به منظور خواندن دادهها است. بیایید به سراغ خواندن دادهها برویم. نگاشتگرها در این زمینه چه کاری میکنند؟ منظورم این است که قدرت مهم پایگاههای رابطهای در این است که همه پرسوجوها در دنیا، با دستور Select زبان SQL کار میکنند. نگاشتگرها دراینباره چهکار میکنند؟
برای بازیابی دادههای پایگاه داده از طریق یک نگاشتگر شیء به رابطه، راههای فراوانی وجود دارد. اغلب به راههای دسترسی ساده فکر میکنید مثلاً اینکه با کلید اصلی پرسوجو کنید و یک شیء نماینده (Proxy) دریافت کنید که مقدارگیری تنبل (Lazy Initialization) دارد یا مثلاً میتوانید با متد find (:all) در ActiveRecord همه دادههای پایگاه داده را بارگذاری کنید (Active Record یک نوع فریمورک نگاشتگر شیء به رابطه در زبان روبی است - مترجم). در کنار اینها، در اکثر نگاشتگرهای شیء به رابطه، یک زبان پرسوجو دارید. تفاوت بزرگ بین زبان پرسوجوی نگاشتگر و SQL این است که در زبان نگاشتگر شما پرسوجوها را بر روی اشیاء و نه بر روی جداول و ستونها انجام میدهید.
اگر شما با پیشزمینه SQL بیایید و بخواهید کار با زبان پرسوجوی یک نگاشتگر شیء به رابطه را آغاز کنید، اولین چیزی که باید از آن آگاه باشید همین تفاوت است زیرا در اینجا با جداول سروکار ندارید. مطلب دیگر در مورد این زبان پرسوجو، شیء گرایی بیشتر است بهعنوان مثال، ضابطه (Criteria) را داریم که بهوسیله آن، زبان پرسوجوی مبتنی بر متن به یک زبان پرسوجوی مبتنی بر اشیاء انتزاع مییابد.
بنابراین، شما API ای دارید که میتوانید پرسوجوها را بهوسیله آن به هم متصل کنید.
بله. شما با استفاده از API، میتوانید قیود (Restriction)، تصویر کردنها (Projection) و ترتیب (Ordering) را تعریف کنید. همچنین بهوسیله آن، نحوه واکشی دادهها از پایگاه داده را هم مشخص میکنید زیرا بعضی وقتها میدانید که وقتی مثلاً یک مشتری را از پایگاه داده میخوانید، همیشه میخواهید که آدرسهایش را هم داشته باشید بنابراین میتوانید این کار را در یک واکشی انجام دهید. استراتژی واکشی را میتوانید هم به روش شیء گرا و با واسط ضوابط (Criteria) مشخص کنید و هم میتوانید با روش مبتنی بر متن در زبان پرسوجو مشخص کنید.
جنبه دیگر، پرسوجو با مثال (Querying By Example) است. این هم اغلب بخشی از واسط ضوابط مربوط به نگاشتگر است که در آنجا، بهوسیله شیء مثالی، پرسوجو میکنید. مثلاً میگویید: «همه مشتریهای با نام کوچکِ Arno». و همچنین برخی اشارات برای نگاشتگر فراهم میکنید مثلاً میگویید که یک تطابق کامل (Exact Match) میخواهید و یا میخواهید که به بزرگی و کوچکی حروف حساس نباشد و از این قبیل. این تسهیلات دیگری در زمینه پرسوجو است که برای صفحات جستجو قابل استفاده است.
امروزه اغلب نگاشتگرهای شیء به رابطه، استفاده مستقیم از SQL را هم پشتیبانی میکنند. البته وقتی با یک نگاشتگر کار میکنید، این انتخاب اول نیست اما کاربردهایی وجود دارد که بخواهید برای برخی ویژگیهای خاص پایگاه داده از آن استفاده کنید. مثلاً اگر بخواهید برخی تنظیم کردنهای (Tuning) سطح پایین پایگاه داده را انجام دهید میتوانید به استفاده مستقیم از SQL برگردید و در اینحال، نگاشتگر، عملیات تبدیل مجموعه نتایج برگشتی دستور SQL به اشیاء مورد استفادهتان را انجام خواهد داد.
امروزه نگاشتگرهای شیء به رابطه، خیلی رواج دارند. خیلی از محصولات خود را نگاشتگر شیء به رابطه مینامند. Hibernate و NHibernate را داریم، در Rails، آن را داریم. خیلی چیزها هست. آیا میتوانید یک تصویر کلی به ما بدهید که چه انواعی از نگاشتگرهای شیء به رابطه وجود دارد و تفاوت آنها چیست؟ یک مقداری آنها را دستهبندی کنید.
بسیار خوب. من ابتدا با این دستهبندی شروع میکنم که آیا یک نگاشتگر شیء به رابطه، مفهوم «واحد کاری» یا «زمینه ثبت» را پشتیبانی میکند؟ در این دسته، Hibernate قرار دارد. JDO نیز در همین دسته قرار میگیرد. در دنیای جاوا در مشخصات JPA نیز مفهوم «واحد کاری» بیان شده است. NHibernate نیز در همین دسته قرار میگیرد. دسته بعدی، نگاشتگرهای شیء به رابطهای هستند که اطلاعات فراداده را اخذ میکنند اما میتوانم بگویم همچنان خیلی دستورگرا (Statement Oriented) هستند. بهعنوان مثال اگر به پشتیبانی که در Rails میشود مثلاً Active Record نگاه کنید، میبینید که مفهوم «زمینه ثبت» را ندارد؛ یک شیء را میسازید و آن را در پایگاه داده ذخیره میکنید و اگر بعداً همان شیء که در پایگاه داده ذخیره کردهاید را تغییر دهید، باید متد ()update را فراخوانی کنید بنابراین در اینجا، آن همگامسازی پنهانی با پایگاه داده را نخواهید داشت. در اینجا باید همواره بگویید که تغییرات من را با پایگاه داده همگام کن.
از طرف دیگر LINQ را داریم که بیشتر یک زبان پرسوجو است. LINQ محدود به پایگاه داده رابطهای نیست. وقتی از اشیاء پرسوجو میکنید لینک به اشیاء دارید و وقتی از ساختارهای XML پرسوجو میکنید، لینک به XML دارید و زمانی که با پرسوجوهای پایگاه داده کار میکنید، لینک به SQL دارید. اما LINQ، نگاشت اشیاء به جداول پایگاه داده را هم دارد؛ میتوانم بگویم یک نوع نگاشت خیلی مبتنی بر ابزار (Tool Driven) است اما تمرکز اصلی LINQ بر روی پرسوجوها است و البته ابزارهایی برای ذخیره و بروزرسانی بر روی پایگاه داده هم دارد.
همچنین بحثهای زیادی وجود دارد که آیا برخی فریمورکهای سطح پایین مربوط به دستورات هم میتوانند فریمورک نگاشتگر شیء به رابطه تلقی بشوند یا خیر. بهعنوان مثال در زبان جاوا، iBatis را داریم که نگاشتگر SQL است اما خیلی دستورگرا است. من این ابزارها را کموبیش بهعنوان اجزاء فریمورک میبینم اما آنها را نگاشتگر شیء به رابطه نمیدانم. آنها جنبههایی از نگاشتگرهای شیء به رابطه را دارند اما همچنان خیلی به دستورات (Statement) اختصاص دارند. شما پرسوجوهای خودتان را تعریف میکنید. این ابزارها، برای اشیاء شما پرسوجوها را نمیسازند. آنجا شما مجبورید همه اعمال ایجاد، خواندن، حذف و بروزرسانی و ... را خودتان مشخص کنید.
اینها دستههای اصلی هستند که در ارتباط با نگاشتگرهای شیء به رابطه میتوانم تشخیص دهم.
شما در ارتباط با مدلهای مختلف برنامهنویسی در نگاشتگرهای شیء به رابطه مختلف صحبت کردید. فرض کنید من توسعه نرمافزار جدیدی را آغاز میکنم و به استفاده از یک نگاشتگر فکر میکنم. چطور کار را پیش ببرم؟ استفاده از یک نگاشتگر چه تأثیراتی بر روی طراحی و معماری نرمافزارم خواهد داشت؟
من اول بررسی میکنم که آیا واقعاً برای نرمافزارم، به یک نگاشتگر شیء به رابطه نیاز دارم؟ اگر یک نرمافزار وِب خیلی کوچک دارم و فقط دو یا سه کلاس دارم که میخواهم آنها را در پایگاه داده قرار دهم، شاید نگاشتگر شیء به رابطه فقط یک کتابخانهای باشد که تنظیمات بیشتر و فراداده بیشتر را بههمراه داشته باشد و با SQL خالص خیلی سریعتر باشم. اما بیشتر پروژههایی که وجود دارند، پیچیدهتر هستند. در این حالت، باید تشخیص دهم که کدام دسته از نگاشتگرهای شیء به رابطه را میخواهم استفاده کنم. آیا میخواهم یک نگاشتگر شیء به رابطه کاملاً مجهز که خیلی قدرتمند باشد، داشته باشم؟ اغلب، برای موارد کاربرد ساده، استفاده از آنها خیلی راحت است اما همچنان، یک پیچیدگی هم اضافه میکنند زیرا توسعهدهنده تیم باید مثلاً آگاه باشد که تغییراتش بر روی اشیاء، پنهان از دید او، با پایگاه داده همگام میشوند. این ممکن است به نواحی مختلف ناسازگاری (Conflict) رهنمون شود. آیا در تیم توسعه، چنین دانشی از نگاشتگرهای شیء به رابطه وجود دارد؟ اگر اینچنین است، میتوانید به راحتی جلو بروید و یکی از این نگاشتگرها را بکار بگیرید. اگر اینطور نیست، شاید یک متخصص در زمینه نگاشتگر شیء به رابطه را اضافه کنید.
یک توصیه عمومی وجود دارد که تکنولوژیهای جدید از هر نوعش را به خدمت بگیریم. اما فرض کنید که من یک نرمافزاری دارم که به مقدار کافی بزرگ است و محقق ارشد من، به من میگوید که Hibernate و NHibernate گزینههایی از همان نگاشتگرهای شیء به رابطه کاملاً مجهز هستند که انتخابهای خوبی هستند. (استفاده از آنها) چه تأثیراتی بر روی طراحی و معماری نرمافزارم خواهد داشت؟
اثرش این است که اول از همه باید، به نرمافزارتان، فراداده اضافه کنید. شما اغلب، از یک طرف کلاسها و اشیاء و روابط بین آنها را دارید و از طرف دیگر پایگاه داده را دارید که باید آنها را برای نگاشتگر، کنار هم بگذارید. بنابراین نیاز دارید که فرادادهاش را یا از طریق DSL های خارجی یا از طریق تنظیمات یکپارچه شده، بیان کنید. مطلب دیگری که باید با برنامهتان یکپارچه کنید مدیریت زمینه ثبت یا واحد کاری است. اینکه چه زمانی واحد کاریام آغاز میشود؟ و اینکه چگونه آن را پایان میدهم؟
به نوعی طرحریزی مرزهای تراکنشها.
دقیقاً. اینکه چگونه مدیریت تراکنشها را طرحریزی و یکپارچه میکنم؟ آیا در یک محیط مدیریتشده (Managed Environment) یا غیرمدیریتشده کار میکنم؟ و اینکه آیا پشتیبانی از محاورههای طولانی را نیاز دارم یعنی، زمینه ثبت را باز نگه میدارم و هربار، تثبیت (Commit) میکنم و در انتها عمل بیرون فرستادن (Flush) را انجام می دهم؟ و همین طور باید به این فکر کنید که با شیء نگاشتشده چهکار میکنید. در صورتی که یک برنامه تحت وب یا RPC باشم، آیا اشیاء نگاشتشده را مستقیماً به برنامه کلاینت، میدهم؟ آیا برای لایه سرویس با لایه دیگری مثلاً لایه DTO کار میکنم؟ اینها نیز باید درنظر گرفته شوند.
فرض کنید من میخواهم توسعه یک نرمافزار جدید را از صفر شروع کنم که البته بهترین حالت و رؤیای هر توسعهدهندهای است :-) آیا با ساختن مدل شیء خودم کار را آغاز میکنم و سپس آن را به یک روش سررراست به پایگاه داده نگاشت میدهم؟ یا اینکه ابتدا پایگاه دادهام را طراحی میکنم و بعد آنها را بر روی اشیائم تصویر میکنم؟ چطور کار میکنم؟ اول اشیاء یا اول پایگاه داده؟
البته وقتی شما بهعنوان یک برنامه نویس شیء گرا توسعه میدهید معمولاً میگویید که اول اشیاء هستند. من هم فکر میکنم اشیائی که در برنامهتان با آن کار میکنید باید تا حد ممکن گویا باشند. اما من شاید هر هفته، با متخصص پایگاه داده صحبت میکنم و به او میگویم که چنین ساختار اشیائی دارم و میخواهم به این شکل آنها را در پایگاه داده نگاشت دهم. آیا همین خوب است یا اینکه مشکل کارایی در آن میبینید؟ چرا که اگر یک مدل اشیاء خیلی خوب داشته باشید اما سمت پایگاه داده، خرابکاری کرده باشید، ارزش کسب و کار نرمافزارتان به شدت پایین میآید. من همواره با اشیاء آغاز میکنم اما جنبههای مربوط به استقرار در پایگاه داده را هم در ذهن دارم و به آن اهمیت میدهم یا میدهم کسی، نحوه تفکرم درباره پایگاه داده را بازبینی کند.
بنابراین شما میگویید که اشیاء، انتزاع اصلی هستند اما مهم است که مدل پایگاه داده را هم در ذهن داشته باشیم و یک سفر رفت و برگشت داشته باشیم و بگذاریم هر دو موازی با هم جلو بروند.
بله، در واقع من میگویم این کار بهتر است. من همواره با یک متخصص حوزه کسب و کار که دانش کاملی از آن حوزه دارد، مینشینم و مدل اشیائم را طراحی میکنم و سپس با طراح دادهها صحبت میکنم که او چطور در مورد این مدل اشیاء فکر میکند و چطور میتوانیم آنها را در پایگاه داده انعکاس بدهیم. من فکر میکنم این تعامل بین این ۳ دیدگاه، یعنی: طرف کسب و کار، طرف توسعه دهنده شیء گرا و طرف پایگاه داده، باعث میشود کاراترین برنامه کاربردی ساخته شود.
بسیار خوب. مطلب دیگر، کپسوله کردن خروجی نگاشتگر شیء به رابطه است. آیا باید یک لایه DAO برای کپسوله کردن نگاشتگر داشته باشم یا اینکه میتوانم هرجا خواستم از آن استفاده کنم؟
من میدانم که بحثهایی دراینباره وجود دارد. خصوصاً در جاوا برای بیان مشخصات (Specification) برای JPA این بحثها بوده است. اما من قطعاً در مورد الگوی DAO جوابم مثبت است. چند دلیل برای آن وجود دارد. دلیل اول این است که نمیخواهم در منطق کسب و کارم (Business Logic) با تکنولوژیهای مختص ثبت (Persistence Specific) سروکار داشته باشم. این جزیی از منطق کسب و کار نیست و اساساً بخشهای مربوط به منطق کسب و کار نباید از این باخبر باشد که چه زمان و چگونه دادهها ذخیره میشوند. اینها باید انتزاع یابند.
مطلب بعدی این است که به این طریق میتوانید بین تکنولوژیهای ثبت کردن، تعویض کنید. البته معتقدم این ضعیفترین استدلال است زیرا معمولاً یک استراتژی ثبت کردن را ارزیابی میکنید، آن را تست میکنید، تست فشار (Stress Test) میکنید و بعد آن را به محیط توسعه میبرید. کمتر رخ میدهد که روش ثبت کردن خود را عوض کنید، البته رخ میدهد حتی میتوانم بگویم از اینکه بخواهید پایگاه داده را عوض کنید، بیشتر رخ میدهد. اما مزیت اصلی لایه DAO این است که میتوانید یک رفتار استاندارد برای نحوه کار کردن با API نگاشتگر شیء به رابطه تعریف کنید. بهعنوان مثال وقتی من با شناسه، دنبال چیزها میگردم آیا از قبل پایگاه داده را برقرار کردهام یا اینکه با نمایندهها (Proxy) صحبت میکنم؟ یا بهعنوان مثال در Hibernate، آیا از متدهای ()save و ()update استفاده میکنم یا با ()merge کار میکنم؟ چگونه محتویات زمینه ثبت (Persistence Context) را به پایگاه داده میریزم؟
فکر میکنم شما میگویید که جزییات تعاملات با نگاشتگر شیء به رابطه باید بهروشی انتزاع یابد تا به شکل یکنواختی انجام شود و منطق کسب و کار را بههمریخته نکند.
این یک استدلال است و استدلال دیگر این است که بتوانم یک رفتار استانداردشده در یک کلاس انتزاعی که کلاس والد DAO باشد تعریف کنم و در حالت ایدهآل، توسعهگران فقط نیاز باشد که دیگر کارها را پیادهسازی کنند.
بسیار خوب. در ارتباط با بارگذاری تنبل (Lazy Loading) چطور؟ بارگذاری تنبل ویژگی است که خیلی از نگاشتگرهای شیء به رابطه دارند. بارگذاری تنبل به این معناست که وقتی، بر روی نمونههای اشیاء جابهجا میشوید، اشیاء در پسزمینه واکشی میشوند و پرسوجوهای SQL در پسزمینه اجرا میشوند. آیا این گونه رفتار به نوعی پیوند دادن (Coupling) منطق کسب و کار به نحوه ثبت از طریق درِ پشتی نیست؟
بله، بهنوعی درِ پشتی است. به اعتقاد من الگوی بارگذاری تنبل، الگوی خیلی خوبی است زیرا اگر از بارگذاری تنبل استفاده نمیکردیم این خطر وجود داشت که با هر درخواستی، نیمی از پایگاه داده را حتی اگر نیازی به آنها نداشتیم را بارگذاری کنیم. الگوی بارگذاری تنبل میگوید: «تنها وقتی چیزها را بارگذاری میکنیم که واقعاً به آنها نیاز داشته باشیم». بهعنوان مثال اگر مجموعهای (Collection) داشته باشم و برخی از اعضاء آن را نیاز داشته باشم، این مجموعه بصورت تنبل بارگذاری میشود. البته در این حالت، برخی عملیات در منطق کسب و کار، برخی دستورات SQL را راهاندازی میکند اما هیچگاه منطق کسب و کار به زمینه ثبت، نمیگوید که: «لطفاً این را برایم مقداردهی اولیه کنید». اینکار پنهان از دید منطق کسب و کار انجام میشود. من طرفدار این استدلال نیستم که از طریق بارگذاری تنبل، یک پیوستگی سخت (Tight Coupling) بین منطق کسب و کار و [روش] ثبت کردن ایجاد میشود.
درباره سرباری که با در میان قرار دادن نگاشتگر مابین برنامه و پایگاه داده ایجاد میشود چطور؟ آیا تأثیر منفی بر روی کارایی ندارد؟
بله و خیر. بله به این علت که لایه اضافی و یک کتابخانه اضافی دارید که در برنامهتان اجرا میشود و این با مقداری هزینه در کارایی (Performance) همراه خواهد بود. اما برای اغلب نگاشتگرهای شیء به رابطه امروزی، در بیشتر نرمافزارها این امر نمایان نمیشود مگر برنامههای به شدت بلادرنگ با نیازهای کارایی بالا، که چنین ملاحظاتی داشته باشند. خیلی از برنامههای معظم (Enterprise) که من در آنها کار کردهام، سربار نگاشتگر را حتی حس هم نمیکردند زیرا از طرف دیگر، یک نگاشتگر خوب این حس را دارد که با توجه به لهجه پایگاه دادهای که با آن کار میکنید، دستورات SQL خوبی را اجرا کند. در لهجه یک پایگاه داده خاص، این دانش هم وجود دارد که کدام دستور SQL در آن پایگاه داده کارا است و کدام دستور کارا نیست. بنابراین برنامهنویسان عادی که معمولاً مقداری دانش SQL دارند، نیازی ندارند که در مورد جزییات مربوط به کارایی پایگاه داده نگران باشند. این مطلب کپسوله میشود. من با تعدادی از مدیرسیستمهای پایگاه داده (DBA) صحبت کردهام که میگفتهاند که SQL ای که نگاشتگرها تولید میکنند، کاملاً خوب است. اینطور نیست که همه چیز در مورد آنها خوب باشد اما در اکثر موارد میگویند که آخر سر، وقتی یک مورد کاربرد را تست میکنند، کارایی بهتر شده است.
منظورتان این است که کارایی دسترسی به پایگاه داده از طریق یک نگاشتگر از کارایی دستورات دستنوشته SQL معمولی بهتر است؟
بله، خصوصاً نسبت به یک چیز معمولی که یک توسعهدهنده عادی نوشته باشد. البته اگر توسعهدهندههایی داشته باشید که خبره پایگاه داده باشند، آنها همواره میتوانند، بر نگاشتگرها چیره شوند چرا که همه ریزهکاریها را میدانند اما در آن صورت از پایگاه داده انتزاع نمییابید و برنامهتان دیگر، قابل انتقال (Portable) نخواهد بود.
شبیه به بحثهایی است که در مورد اثرات کارایی ماشین مجازی در مقابل کدهای دستنوشته اسمبلی داریم.
بله، شبیه به آن است. همواره افرادی را مییابید که میگویند که نگاشتگرها خیلی کند هستند ولی من فکر میکنم که واقعیت این است که همه توسعهدهندهها نمیتوانند خبره پایگاه داده باشند به این معنی که بتوانند کاراترین دستورات SQL را بنویسند. فکر میکنم نگاشتگرها اگر درست استفاده شوند، حداقل، کارایی را کاهش نمیدهند.
در ارتباط با نهانگاه (Cache) چطور؟ شما از «واحد کاری» گفتید و اینکه معمولاً در حوره مُجزای تراکنشها، نهانگاه داریم. به نوعی نهانگاه داریم اما در حوزه دید یک تراکنش واحد است. در مورد نهانگاههای طولانیتر چطور؟ آیا میارزد که این کار را بکنیم؟ نگاشتگرها چه سطحی از پشتیبانی را در این ارتباط دارند؟ آیا ایده خوبی است که از آن استفاده کنیم و اگر بله چه موقع خوب است که از آن استفاده کنیم و چه وقت باید از آن بپرهیزیم؟
من دیدگاه خیلی محتاطانهای در مورد استفاده از نهانگاه دارم. توصیه من این است که قبل از اینکه بخواهید نهانگاه سطح دوم را به نگاشتگر بیافزایید، ابتدا خود دسترسی به دادهها را تنظیمکاری (Tuning) نمایید. به این معنی که تنها دادههایی که واقعاً نیاز دارید را بارگذاری کنید؛ در پرسوجوهایتان به روابط تجمعی (Aggregation) و تصویر کردنها (Projection) فکر کنید و پرسوجوهایتان را از قبل، تنظیمکاری نمایید. بعد از آنکه این کار انجام شد، میتوانید بررسی کنید که کدام موجودیت یا کدام مورد کاربرد، از نهانگاه سود میبرد زیرا هر نوع کلاسی از نهانگاه سود نمیبرد. بهعنوان مثال یک گزینه خیلی بد برای استفاده از نهانگاه، دادههای مالی هستند زیرا اگر بهخاطر آن به مشکلی بخورید، سازمانتان پول زیادی را از دست میدهد!
من دادهها را دستهبندی میکنم. [گزینه کاندید برای نهانگاه]، برخی دادهها هستند که بهندرت تغییر میکنند و توسط تعداد زیادی کاربران مختلف استفاده میشوند و همچنین دادههای خیلی حساسی هم نیستند. من دادههای خیلی حساس را در نهانگاه نمیگذارم.
به این خاطر که استفاده از نهانگاه خطر غیرمعتبر شدن مقادیر را افزایش میدهد.
بله، در اینجا باید بین سطوح مختلف مجزاسازی (Isolation Level) مرتبط با نهانگاه تفاوت قائل شوید. در اغلب نهانگاهها میتوانید یک سطح مجزاسازی مشخص تعریف کنید که میتواند از یک نهانگاه کاملاً تراکنشی و خوشهبندی شده (Clustered) و تکرارشده (Replicated) تا یک نهانگاه ساده باشد که همچنان سطح مجزاسازی «خواندن تثبیتشدهها» (Read Committed) بر روی تراکنشها را پشتیبانی میکند و بیشتر در زمان بیکاری کار میکند و بین خوشهها به اشتراک گذاشته نشده است و تنها بر روی یک نمونه قرار دارد و برای آن، همگامسازی بین گرهها، وجود ندارد.
از این بحث مشخص میشود که با استفاده از نهانگاه دارید پیچیدگیهایی به زیرساختها اضافه میکنید. ممکن است برای نهانگاههای کوچکتر خیلی پیچیده نباشد اما برای اضافه کردن یک نهانگاه که کاملاً تراکنشی و خوشهبندی شده تکرار شده باشد، همواره پیچیدگیهای خیلی زیادی اضافه میشود. به اعتقاد من این کار نباید توسط متخصص نگاشتگر شیء به رابطه انجام شود و برای این کار، من از یک متخصص نهانگاه درخواست میکنم زیرا چیزهای زیادی باید در آنجا تنظیم شود.
بسیار خوب، بنابراین استفاده از نهانگاه آخرین گزینه است و چیزی نیست که بخواهیم به کرّات از آن استفاده کنیم.
نه، من میگویم از نهانگاه استفاده کنید اما وقتی که قبل از آن همه چیزهای دیگر را تنظیمکاری کرده باشید. نهانگاه برای پرسوجوها هم خیلی استفاده میشود اما به اعتقاد من، در بیشتر کاربردها، خیلی کارا نیست. شما میتوانید موجودیتها، روابط و پرسوجوها را در نهانگاه قرار دهید اما قرار دادن پرسوجوها در نهانگاه خیلی بهندرت توجیه دارد. اگر میدانید که یک پرسوجو با پارامترهای یکسانی بارها فراخوانی میشود، توجیه مییابد.
در ارتباط با مهاجرت دادن دادهها (Data Migration) چطور؟ آیا وقتی برنامهها به روشهای مختلفی از طریق نگاشتگرهای شیء به رابطه به دادهها دسترسی پیدا میکنند، مسأله ایجاد میکند؟
قطعاً. خصوصاً اگر در محیطی که نهانگاه وجود دارد کار میکنید. زیرا هیچگاه نمیدانید که چه زمانی برنامه دیگری در پسزمینه، دادهها را تغییر میدهد. این مورد خصوصاً وقتی رخ میدهد که با یک پایگاه داده موجود کار میکنید که روالهای ذخیرهشده (Stored Procedure) یا برخی نرمافزارهای قدیمی خودش را دارد و آنگاه یک نرمافزار جدید با یک معماری جدید را آغاز میکنید که همه چیزش خوب است و در آن از تکنولوژیهای برتر استفاده شده است. در آن صورت هیچگاه دسترسی انحصاری به پایگاه داده ندارید و باید آگاه باشید که در پسزمینه ممکن است چیزها تغییر کنند.
بنابراین بله، بهعقیده من در محیطهایی که نهانگاه وجود دارد، این کار خطرناک است. در اینگونه موارد من کاملاً نسبت به استفاده از نهانگاه محتاط هستم و برای بیشتر موجودیتها آن را توصیه نمیکنم.
شما به روالهای ذخیرهشده (Stored Procedure) اشاره کردید. خیلیها اینطور تصور میکنند که آنها سریعترین روش برای برنامههای با نیازمندیهای کارایی بالا (High Performance) هستند. اما آنها با نگاشتگرها خیلی خوب کار نمیکنند. یا میکنند؟!
در واقع خیر. شما در نگاشتگرهای شیء به رابطه، پشتیبانی از روالهای ذخیرهشده (Stored Procedure) را دارید اما معمولاً این پشتیبانی خیلی محدود است. من به شخصه هیچگاه آنها را با یک نگاشتگر استفاده نکردهام. البته درست است که آنها خیلی سریع هستند و کارایی کاملاً بالایی دارند اما باید بدانید با استفاده از آنها دادهها را به منطق کسبوکار مقید میسازید. البته در بستههایی کپسوله شدهاند اما بهعقیده من قابلیت نگهداری (Maintainability) یک روال ذخیرهشده از قابلیت نگهداری یک کد شیءگرا که خوب نوشته شده باشد، کمتر است. البته میتوانید کدهای شیءگرایی هم بنویسید که قابلیت نگهداری نداشته باشند اما علیالخصوص با روالهای ذخیرهشده، راحت نیست که بفهمید که چه جاهایی از یک سری دادهها استفاده شده است.
بهعقیده من روالهای ذخیرهشده، برای کارهای دستهای (Batch Job) خیلی عالی هستند. این حوزهای است که اغلب نگاشتگرها، در آن شکست میخورند زیرا یک نگاشتگر یک ابزار برای کارهای دستهای با نیازمندی کارایی بالا نیست. بهعقیده من، این حوزه اصلی روالهای ذخیرهشده است.
فکر میکنم، مطالب را پوشش دادیم. چیزی هست که بخواهید اضافه کنید؟
بله، خیلی ممنونم که گوش دادید. اگر بازخورد یا نظری دریافت کنم خوشحال میشوم که پاسخ دهم. در ارتباط با استفاده از نگاشتگرهای شیء به رابطه، تشویقتان میکنم که آنها را حتماً مدنظر قرار دهید اما فقط زمانی آنها را اضافه کنید که ارزشی را ایجاد کنند. خصوصاً در مورد استفاده از نهانگاه کمی محتاط باشید. من شما را درباره آن دلسرد نمیکنم اما نسبت به آنها حالت احتیاط داشته باشید. و همچنین هر تبلیغی که میگوید: «اگر از نگاشتگر شیء به رابطه استفاده کنید دیگر نیازی به فراگیری SQL ندارید» را باور نکنید. این یک دروغ است! :-)
مطلبی دیگر از این انتشارات
تاریخچه JUnit و آینده تست
مطلبی دیگر از این انتشارات
بدهی فنی
مطلبی دیگر از این انتشارات
رسیدگی به خطاها (قسمت دوم)