<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های محمد عباسی</title>
        <link>https://virgool.io/feed/@mujan</link>
        <description>علاقه‌مند به یادگیری. 
آن کس که &quot;چرایی&quot; را یافته است، &quot;چگونگی&quot; را نیز خواهد توانست. • فردریش نیچه •</description>
        <language>fa</language>
        <pubDate>2026-07-08 03:29:16</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/12111/avatar/PB3OHX.png?height=120&amp;width=120</url>
            <title>محمد عباسی</title>
            <link>https://virgool.io/@mujan</link>
        </image>

                    <item>
                <title>رویکرد «حل مسئله» در مقابلِ «حذف مسئله»</title>
                <link>https://virgool.io/dotin/%D8%B3%D8%A7%D8%B2%D9%85%D8%A7%D9%86%D8%AA%D9%88-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1-%D8%A8%D8%AF%D9%87-jawprf4cbyyj</link>
                <description>اگر بخواهم خیلی رُک و بی‏‌تعارف صحبت کنم، به گَمانم یکی از غُرغُروترین اقشارِ جامعه هستیم. منظورم برنامه‌‏نویسان، متخصصین عملیات، دیتاساینتیست‏‌ها و به طور کلی جامعه مهندسینِ کامپیوتر است.ما از همه چیز گِله داریم. از زیرساخت‌‏های ناکافی، شرایطِ کار، جنسِ کار، حقوق، بی‏سوادی همکاران، فرهنگِ نامناسب سازمان و به قول آن شاعرِ مردمی «ما حتی به خودمان هم نقد داریم!»آیا تا به حال برای شما پیش آمده که پیش یک وکیلی بروید و بابتِ یک بندِ قراردادِ کاری از ایشان سوال کنید که «چرا شما اعتقاد دارید این بند برای من تعهد قانونی می‌آورد ولی وکیل «ایکس» که در اینستاگرام پیدایش کردم، ادعا کرده هیچ تعهدی برای من نمی‌آورد؟»، بعد وکیل مذکور، یک‌باره بزند زیر میز و بگوید من وکالت کسی که الفبای قانون را نمی‌داند نمی‌‏پذیرم؟بله ما مهندسینِ کامپیوتر حتی از مشتریانِ خودمان هم گله داریم. اگر کسی از ما بپرسد «چرا بابت یک فروشگاه اینترنتی مبلغی چند ده میلیونی پیشنهاد دادیم؟ درحالی‌که یک برنامه‏‌نویسِ دیگر ادعا کرده‏ با مبلغی زیرِ یک میلیون فروشگاهی قدرتمند با استفاده وردپرس می‎‌سازد.» خشم وجودمان را می‏گیرد که چرا مشتری نمی‎فهمد! و با نگاهی از بالا به پایین، توام با تمسخر و خودبرتربینی، می‏گوییم «برو به همان بگو برات بزنه!» (که در این مورد خاص امیدوارم مشتری حرف ما رو بپذیره.)خب، مقدمه را خیلی تاریک بیان کردم. واقعیت این است همه‏ ما این‌طور نیستیم. همه‏ گله و شکایاتمان هم بی‏راه نیست و همیشه گله و شکایت هم بد نیست. یادمان نرود جهان به واسطه افرادی بهتر شده که نسبت به وضع فعلی جهان نقد داشتند.ایده‏ اصلی این نوشتار از قسمت اول کتاب برنامه‏‌نویس عمل‏گرا آمده است. در کتاب، آقایان دیوید توماس و اندریو هانت، اعتقاد دارند خیلی اوقات با توسعه‌‎دهندگان که حرف می‏زنند، آنها را ناامید و ناراحت می‏بینند. به عقیده‏ آنها نگرانی‏ها و گرفتاری‏‌های توسعه‏‌دهندگان هم متفاوت است. برخی از آنها احساس راکدبودن در کارشان دارند، برخی دیگر تصور می‏کنند تکنولوژی از آنها گذشته است. یا از احترام کم مدیران، حقوق پایین یا از تیم‌‏های فسیل خودشان گله‌‏مند هستند. برخی به فکر مهاجرت هستند و برخی به فکر دورکاری!و البته پاسخی که این دو عزیز به این گله‌‏مندی‏ها هم می‏دهند همیشه یک چیز سر راست است: «چرا تغییرش نمی‏دهی؟»واقعیت این است که توسعه نرم‌‏افزار، کارهای زیرساختی و عملیاتی، مشاغلی مثل تحلیل داده و امثال آن، در صدر لیست مشاغلی است که ما بر آن کنترل داریم. مهارت‎های ما در بازار، تقاضای بالایی دارد، محل استفاده دانش ما محدود به مرزبندی‌‏های جغرافیایی نیست و امکان دورکاری مهیاست، دست‏مزد بالاتری نسبت به میانگین مشاغل دیگر دریافت می‏کنیم، منابع یادگیری تقریبا رایگان و در دسترس است و کلی دلیل دیگر که نشان می‏دهد ما واقعا تسلط و کنترل بالایی در آنچه انجام می‏دهیم، داریم.با وجود این مقدار از شرایطِ ایده‏‌آل، که در بسترِ این شغل مهیاست، خیلی از افراد در برابر تغییر مقاومت می‏کنند. افرادی که از تکنولوژی جامانده‌‏اند و شکایت از شرکت‌‏هایشان دارند که چرا به آموزش نیروها بها نمی‏دهند! چرا به خودمان آدرسِ غلط بدهیم؟از اینکه تکنولوژی از شما گذر کرده ناراحت هستید؟ زمانی ایجاد کنید که در آن بتوانید به مطالعه‏ مسائلی که از نظرتان جذاب و کاربردی است، بپردازید. اگر شرکتی که در آن مشغول به کار هستید این فرصت را به شما نمی‏دهد، از زمان شخصیِ خود استفاده کنید. این کار سرمایه‌‏گذاری روی خود است.اگر شرکت هم این فرصت را به شما نمی‏دهد الزاما غیرحرفه‌‏ای یا غیراخلاقی نیست. این موضوعی است که می‌‎بایست در زمان عقد قرارداد مشخص می‎شد و اگر نگاه شرکتی به شما کوتاه‌مدت است، چه دلیلی دارد که به شما فرصت رشد بدهد؟ در چنین شرایطی استفاده از زمان شرکت برای رشد شخصی، غیراخلاقی است.ترجیح می‎دهید دورکار باشید و به صورت ریموت کار کنید؟ تابه حال چنین درخواستی را مطرح کردید؟ اگر پاسخ «نه» بوده است، کسی/ جایی را پیدا کنید که در پاسخ به چنین خواسته‌‎ای «بله» بگوید!این صنعت به شما مجموعه‌‎ای قابل توجه از فرصت‌‎ها ارائه می‎دهد. پس فعال باشید و از این فرصت‎‌ها به نحو احسن استفاده کنید.کارتان، کارِ گِل (تکراری) است؟ خسته‌کننده است؟ محیطِ کارتان کِسل‌کننده است؟ سعی کنید اصلاحش کنید. اما این تلاش را برای همیشه ادامه ندهید. همان‌طور که مارتین فالِر گفت: «شما می‌توانید سازمان خودتان رو تغییر بدهید یا سازمان خودتان رو تغییر بدهید.» قطعا متوجه بازی با کلمات شدید. یکی از تغییر دادن‏ها مربوط به تغییر درون سازمان است، یعنی مجموعه اقداماتی را انجام دهیم که منجر به بهبود سازمان شود و اگر در این زمینه موفق نشدید، خود سازمان را تغییر دهید؛ استعفا دهید و در جایی دیگر مشغول به کار شوید.پیش از اینکه این مورد را به دلیل اهمیت بالای آن بسط دهیم، با خود فکر کنید وقتی جمله‎ مارتین فالِر را خواندید، اولین «تغییر» شما را یاد کدام تغییر انداخت؟ اینکه فرهنگ، رفتار و روال سازمان را تغییر دهید؟ یا محل را عوض کنید؟ پاسخ به این سوال «شاید» بخشی از شخصیت شما را برای خود شما فاش کند.اگر نظر شخصی من را می‎‌خواهید، به عنوان یک کسی که می‎خواهد حرفه‎‌ای رفتار کند، شما در ابتدا «باید» سعی کنید محیط و شرایط را اصلاح کنید و اگر به این نتیجه رسیدید که به هر دلیلی (اعم از هسته‏ دگم مدیریتی، همکاران سخت در ارتباط، انگیزه‏‌های سازمان و هر دلیلِ دیگر) موفق نمی‌‎شوید، پیش از آنکه فرهنگ غالب به شما غلبه کند و شما را هم بخشی از خودش کند، جابه‌جا شوید.به دو دلیل گفتم «باید»: یک آنکه ما حقوق دریافت می‎یکنیم تا به بهترین نحو کار را انجام دهیم. اصلاح امورِ مشکل‎دار، بخشی از این وظیفه‎ حرفه‌‎ای ماست. شانه‌خالی‌کردن و بی‎تفاوت‌بودن، عملی بزدلانه و غیراخلاقی است و خارج از تعهدات حرفه‌‎ای ماست. دلیل دوم این است که هم جابه‌جایی پرهزینه است، هم احتمال آنکه جایی باشد که بدون مشکل و به بهترین شکل کار کند، بسیار کم است. صنعت نرم‎افزار، فعالیتی غالبا گروهی است و اگر ما توانایی حل مشکلات سازمانی را نداشته باشیم، کٌمیتمان می‎لنگد!اما چطور؟ چطور باید تغییر ایجاد کرد؟ در ادامه به بخشی از توصیه‌‎های خردمندانه در این خصوص اشاره می‎کنم:1. کار خودتان را انجام بدهید. شما استخدام نشدید که سازمان را تغییر بدهید. کاری را که برای آن استخدام شده‌‎اید را به بهترین نحو انجام دهید. اول از همه مصمم برای تغییر بخشی از روندها باشید که مانع از انجام کارتان به بهترین نحو می‎شود. شما اگر کار خودتان را به درستی انجام نمی‎دهید، اصلا نمی‎توانید مدعی تغییر در سازمان باشید.2. چرایی را بفهمید. حتما دلایلی وجود دارند که شرایط فعلی به وجود آمده است. این توهم را هم نداشته باشید که همکارانی بدخواه و بی‎کفایت دارید. غالب اوقات در بدترین حالت همکارانی «نادان» خواهید داشت.3. محترمانه رفتار کنید. هیچ‎وقت نگاهی از بالا به پایین به هیچ شخصی نداشته باشید. مهم نیست چه کسی یا به چه دلیلی. حتی در حریم عقایدتان. تفکرات و اظهارنظرهای متفاوت، به معنای نادانی نیست. حتی نادانی به معنای حماقت نیست.4. به ادبیاتتان دقت کنید. اصطلاحاتی هستند که ممکن است برای شما مثبت و برای اشخاص دیگری منفی تلقی شود. حتی‌‎الامکان از این اصطلاحاتی که ممکن است برداشت دوگانه شود بپرهیزید و خواسته‎ خودتان را با صراحت بیان کنید.5. راهِ فرار را مشخص کنید. پیش از آنکه وارد فاز تغییرات شوید، راهِ برگشتی برای خود بگذارید. در رابطه با این راهِ فرار فکر کنید. راه فرار، می‎تواند استراتژی پیداکردن یک کار جدید باشد، یا اینکه به‌نحوی با کمترین هزینه، برگشتن بر سر وظایف سابق!6. آرام صحبت کنید. ایده‎‌هایتان را در آرامش مطرح کنید و تُن صدای مشخصی را حفظ کنید. گاهی افراد فنی به سرعت و هیجان صحبت می‎کنند، خصوصا زمان‎‌هایی که احساس کنند به «چیز» مهیجی برخوردند. اما شما باید تُناژ صدایتان بویی از خردمندی بدهد تا یک هیجان فنی!7.  انگیزه‌‎ها را پیدا کنید. هنگامی که می‎‌خواهید ایده‎‌های خودتان را برای کسی مطرح کنید، بهتر است از انگیزه‌‎های فردی آن آگاه باشید. به دنبال کمتر کارکردن است؟ چالش دوست است؟ به دنبال پیشرفت است؟ در صحبت‌‎هایتان به این انگیزه‎‌ها آدرس دهید.8. منعطف باشید. اصول مهندسی وحی منزل‌شده نیستند. گاهی شاید بهتر است به جای تغییرِ سازمان، روال‎ها را تغییر داد تا منطبق با سازمان شوند. در این خصوص منعطف باشید.9. آرام، شاد و با اعتمادبه‌نفس باشید. تغییر، هزینه‌‎بر است و ممکن است آن‌طور که پیشبینی می‎شد هم اتفاق نیفتد. در این مواقع به جای دست‎پاچگی، آرامش خودتان را حفظ کنید تا دیگران هم از اعتمادبه‌نفس شما آرامش بگیرند. سعی کنید از تغییرات کوچک شروع کنید تا با موفقیت در آنها و در نتیجه احساس خوبِ حاصل‌شده انگیزه شما و اطرافیان را بیشتر کند.10. الگو باشید. مردم اساسا بیش از آنکه حرف روی آنها اثر بگذارد، الگوبرداری می‎کنند. پس سعی کنید پیشنهادتان را ابتدا خودتان انجام دهید و رهبری را با تکنیکِ «الگوشدن» جلو ببرید.موارد مطرح‌شده در لیستِ بالا، کلیت و اصلش از اینترنت جمع‌‎آوری شده و البته روی هرکدام دستی کشیده‌‎ام تا مطابق میل خودم شوند. به همین جهت امکان لینک‌دادن وجود ندارد. اما یک مورد را شخصا می‎خواهم با تاکید بیشتری بیان کنم:صبور باشید. تغییرات یک‌دفعه و سریع نیستند. کافی است در مسیر باشید. آماده باشید که بارها و بارها شکست بخورید. واقعیت این است که آدم‎‌ها دوست ندارند تصور کنند که شما تلاش می‎کنید آنها را تغییر دهید. واقعیت دیگر هم این است که اگر چنین اتفاقی رخ دهد، یعنی مجموعه اقدامات شما منجربه تغییر چیزی شود، هیچ لطف و افتخاری در آن نیست چراکه نه تنها اگر آنها نمی‎‌پذیرفتند تغییری صورت نمی‎گرفت، بلکه هدف شما هم کسبِ افتخار نبوده است. شما باید تنها انگیزه‌‎ی درونی خودتان حرفه‌‎ای کار کردن در محیطی مناسب باشد. با عجله، شما این حس را القا خواهید کرد که موضوع موردِ نظرتان به درد نمی‌خورد و باید تغییر کند به آنچه شما می‌‎خواهید!صبر، کلید موفقیت است. اما فراموش نکنید در این راه از دیگران هم کمک بگیرید. فردی خردمند از همکارانتان را شناسایی کنید و دغدغه‌‎هایتان را عنوان کنید. احتمال آنکه از رویکردی دیگر به قضایا نگاه کند و نصیحت‎‌های درستی بدهد بسیار بالاست.من به شخصه، تغییر را برای خود تغییر دوست ندارم. تغییر را برای رویکرد آن می‎پسندم. آیا تا به حال شده از جانب همکارانتان مورد بی‌‎مهری قرار بگیرید؟ رویکرد تغییر می‎گوید اینجا چیزی مشکل دارد و باید درست شود! ولی رویکرد غیرِ این، شما را در تقابل با آن فرد قرار می‎دهد. از مدیرتان بخواهید چرا نمی‎توانید با آن همکار، رابطه دوستانه برقرار کنید. شاید مدیر شما این فضا را مهیا کند. اگر حتی می‎خواهید با کسی تقابل کنید، این تقابل باید در راستای بهبود باشد، نه انتقام‎جویی.در انتها باید بگویم که مایلم بحث در خصوص غرولندهای دوستان فنی را بیشتر بسط دهم، پس مقدمه را ثابت می‎گذارم، و هربار به یک موضوع که بتوانم در آن خصوص چیزی بگویم می‌پردازم. این بار «تغییر» موضوع مورد بحث من بود.علاوه بر منابعی که در حین نوشتن به آن‌ها اشاره کردم، برخی از حرف‌های بالا آموخته‌های من از دوست و مدیرم علیرضا ارومند بوده که البته ممکن است به‌خوبی حق مطلب سخنان ایشان را ادا نکرده باشم.</description>
                <category>محمد عباسی</category>
                <author>محمد عباسی</author>
                <pubDate>Sat, 21 Aug 2021 15:06:16 +0430</pubDate>
            </item>
                    <item>
                <title>سیر تا پیاز انتشار و نصب اپلیکیشن در دات نت (قسمت دوم)</title>
                <link>https://virgool.io/@mujan/%D8%B3%DB%8C%D8%B1-%D8%AA%D8%A7-%D9%BE%DB%8C%D8%A7%D8%B2-%D8%A7%D9%86%D8%AA%D8%B4%D8%A7%D8%B1-%D9%88-%D9%86%D8%B5%D8%A8-%D8%A7%D9%BE%D9%84%DB%8C%DA%A9%DB%8C%D8%B4%D9%86-%D8%AF%D8%B1-%D8%AF%D8%A7%D8%AA-%D9%86%D8%AA-%D9%82%D8%B3%D9%85%D8%AA-%D8%AF%D9%88%D9%85-xjti3qwicuvh</link>
                <description>در قسمت قبلی در مورد ماهیت هاست کردن صحبت کردم، و در اونجا گفتم که به دو مدل میتوانیم وب اپلیکیشن را هاست کنیم. مواردی که مطرح شد خاص اپلیکیشن های دات نتی نبود، مختص به تمام وب اپلیکیشن ها بودند. اما دات نت چه امکاناتی برای ما فراهم کرده است که بتوانیم به این دو مدل هاستینگ داشته باشیم؟ این قسمت به این موضوع پاسخ میدهیم و در انتها جمع بندی مینماییم که کدام مدل در چه شرایطی مناسب تر است؟قسمت های قبلیقسمت اولسلام، من محمد عباسی هستم. در این مقاله با ذره بین به تمام وجه های publish و deploy اپلیکیشن های دات نتی میپردازم و در این راه از مقالات، کتب و تجربیاتم استفاده میکنم.دامنه این مقاله در حوزه برنامه نویسی میماند، و به هیچ عنوان وارد مباحثی که مختص به دوستان عملیاتی و مدیرسیستمی هستند، نمیشود.فهرست مطالبمدل OutOfProcess Hostingمدل InProcess Hostingتنظیمات مربوط به هاستینگ مدلبررسی وضعیت هاستینگ در اپلیکیشن در حال اجرامدل In Process یا Out Of Process ؟در ادامهاجازه دهید شیرجه بزنیم و برویم سر اصل مطلب. در ASP.Net Core ما هر دو مدل هاستینگ که در بخش اول توضیح داده شد را میتوانیم عملی بسازیم. ولی تمام آنچه را که تاکنون گفته شد یک گوشه از ذهن خود نگهدارید و ادامه ماجرا را با ذهنی نو دنبال کنید، بوقتش آنها را پیوند میدهم. در ASP.Net Core دو نوع هاستینگ را پشتیبانی میکند بنام های In-process Hosting و Out-of-process Hosting. اما این ها چی هستند؟!مدل OutOfProcess Hostingدر این مدل از هاستینگ ما میتوانیم به دو شکل وب اپلیکیشن خودمان را هاست کنیم، هم میتوانیم اپلیکیشن را با کسترل بالا بیاوریم، و کسترل مستقیما درخواست ها را پردازش کند، هم اینکه ما بین کسترل و کلاینت، از IIS به عنوان ریورس پراکسی استفاده کنیم.پرسش گر: تاجاییکه من میدونم IIS فقط در ویندوز ساپورت میشود. پس کاربران لینوکس نمیتوانند از مدل ریورس پراکسی استفاده کنند؟پاسخ: بله، بخش اول حرف شما درست است. یعنی IIS فقط در ویندوز ساپورت میشود. اما کاربران لینوکسی میتوانند از آلترناتیوهای دیگر استفاده کنند مثل Nginx یا Apache. حتا کاربران ویندوز هم میتوانند از Nginx یا Apache استفاده کنند. اگر راحتی کار با IIS را در نظر نگیریم، عملکرد Nginx بعنوان وب سرور و ریورس پراکسی به مراتب بهتر از گزینه های دیگر هم هست. و البته Nginx در لینوکس هم به مراتب از Nginx در ویندوز بهتر است که بنظرم اینجا جای صحبت آن نیست.همانطور که در عکس زیر به وضوح نمایان است، وقتی مدل هاستینگ را با OutOfProcess تنظیم میکنیم، هم میتوانیم کسترل را بعنوان وب سرور بطور مستقیم در معرض کلاینت قرار دهیم، هم میتوانیم بین آنها از واسطِ ریورس پراکسی استفاده کنیم.در کنار این مدل از هاستینگ، مدل InProcess را نیز داریم. که در ادامه به آن میپردازیم و در نهایت به تفاوت های بین آنها اشاره خواهیم کرد.پرسش گر: مهندس چقدر حرف میزنی! این ها رو چطور عملی پیاده کنیم پس؟پاسخ: صبر کنید، به آنجا هم میرسیم! فعلا مفاهیم را خوب در ذهنتان بسپارید.مدل InProcess Hostingبعد از ریلیز نسخه net core 2.2 مایکروسافت نوع دیگری از هاستینگ بنام InProcess را معرفی کرد. در این مدل، فقط یک سرور مثل IIS یا Nginx برای هاستیگ مورد استفاده قرار میگیرد. به این معنا که اپلیکیشن بطور مستقیم درون IIS هاست میشود و دیگر سرور کسترلی در کار نخواهد بود. در اصل بجای سرور کسترل، سرور http ی IIS (IISHttpServer) استفاده میشود تا اپلیکیشن ها مستقیما روی IIS هاست شوند. و از نسخه ASP.NET Core 3.1 به بعد نیز اگر برای ایجاد اپلیکیشن از قالب های موجود استفاده کنید، مدل in-process بعنوان تنظیمات پیشفرض لحاظ میشود.اگر بخواهم مجدد تکرار کنم، در مدل In Process از Kestrel استفاده نمیشود، در عوض از یک پیاده سازی جدید وب سرور بنام IISHttpServer که مستقیما درون IIS Application Pool قرار گرفته است استفاده میشود. این پیاده سازی جدید هم خودش از هر ریکوئست آبجکت HttpContext را میسازد (که قبلا کسترل میساخت) و آن را تحویل پایپلاین Middlewareها میدهد. خب اگرچه بنظر میرسد این تغییرات از منظر سازگاری اپلیکیشن با نسخه های قدیمی بسیار جدی است، ولی هیچ مسئله ای از این منظر گزارش نشده است. و تغییرات در عمل فقط در بحث پرفورمنس بوده است.به اعتقاد خیلی ها، مایکروسافت اقدام بزرگی را بصورت موفق انجام داده است که با کمترین تغییر پرفورمنس را بهبود داده است.پرسش گر: منظورتون از کمترین تغییر چیه؟پاسخ: تغییر در اپلیکیشن. همانطور که قبلا گفتم ASP.NET Core بسیار ماژولار است و همانطور که دیدیم مقوله هاستینگ آن بطور کلی از بحث کد (خود اپلیکیشن) جدا است. اما دقیقا حرفم اینجا به این معنا است که سوییچ کردن بین In Process و Out Of Process بسیار ساده است، نه تنها خود اپلیکیشن تغییری نمیکند، بلکه شما صرفا با تغییر دادن یک فایل کانفیگ، میتوانید رفتار هاستینگ اپلیکیشن دیپلوی شده را هم تغییر دهید. جلوتر عملی میبینیم.پرسش گر: صبرکنید ببینم. گفته شد که کسترل برای کراس پلتفرم بودن عرضه شد، یعنی بتوانیم اپلیکیشن را در لینوکس هم براحتی ویندوز، اجرا کنیم. با این تفاسیر که در مدل InProcess مطرح شد که از ماژول های داخلی IIS برای ایجاد HttpContext استفاده میشود و عملا دیگر کسترلی وجود ندارد، برای اجرای InProcess در لینوکس چه کار باید کرد؟پاسخ: سوال بسیار خوبی بود. مشخص است که مطالب را با دقت دنبال میکنید. بله همانطور که عرض شد کسترل بود که امکان اجرای اپلیکیشن در لینوکس را نیز محیا میکرد، و خب مشخصا با نبود کسترل، و وابستگی فقط به IIS که آن هم فقط برای ویندوز عرضه میشود، امکان استفاده از InProcess Hosting Model در غیر از ویندوز وجود ندارد!پرسش گر: ولی خب این مدل بعدها اضافه شد، اینجوری یک شکلی از عقب گرد برای مایکروسافت نیست؟پاسخ: من در جایگاه پاسخ به این پرسش نیستم، ولی اگر نظر شخصی خودم را بخواهید باید بگویم خیر، عقب گرد نیست. همچنان خیلی از اپلیکیشن های دات نتی در زیرساخت های مایکروسافتی اجرا میشوند. مایکروسافت با این امکان پرفورمنس این اپلیکیشن ها را بالا برد. جلوتر با جزئیات بررسی خواهیم کرد که از نظر سرعت، مدل InProcess تقریبا تا دوبرابر سریع تر از OutOfProcess است.اما حالا چند سوال مطرح میشود. از کدام مدل استفاده کنیم؟ چگونه تشخیص دهیم از کدام مدل استفاده میکنیم؟ و چطور این تنظیمات را تغییر دهیم. جدا از این موارد که باید به آنها پاسخ داد، هنوز مطالبی هست که میتوان در خصوص هاستینگ مدل های مختلف مطرح کرد که من در ادامه، در خلل پاسخ به سوالات بالا آنها را مطرح میکنم.تنظیمات مربوط به هاستینگ مدلهمانطور که عرض شد تنظیم کردن و تغییر دادن تنظیمات برای مدل هاست کردن اپلیکیشن بسیار ساده است و همچنین سوییچ کردن بین آنها. تنظیمات اساسا در دو محل انجام میشود. یکی فایل کانفیگی web.config و دیگری در فایل .csproj یا همان فایل پروژه. باتوجه به ماهیت کانفیگی بودن فایل web.config، اعمال تغییرات در این فایل ساده تر است و میتوانید اپلیکیشن دیپلوی شده را نیز دستخوش تغییر دهید.تغییر در سطح پروژهخب مشخصا برای تغییر در سطح پروژه میبایست فایل csproj پروژه endpoint خودتون را کنید و تغییرات در آن اعمال کنید. در ویژوال استدیو این فایل پنهان است و زمانی نمایان میشود که در پنجره Solution Explorer روی پروژه دوبار کلیک کنید.اما در محیط های دیگر این فایل نمایان است و براحتی میتوانید آن را باز کنید.برای مشخص کردن تنظیم در فایل xml مدنظرتون باید مقدار InProcess یا مقدار OutOfProcess را درون کلید (یا تگ) AspNetCoreHostingModel قرار دهید. تکه کد زیر را مشاهده کنید:&lt;PropertyGroup&gt;
    &lt;AspNetCoreHostingModel&gt;InProcess&lt;/AspNetCoreHostingModel&gt;
&lt;/PropertyGroup&gt;که خب همانطور که توضیح دادم بسته به نسخه asp.net core که استفاده میکنید یک مقدار پیشفرض هم دارد که اگر مقدار صراحتا اینجا مشخص نشود، از مقدار پیشفرض استفاده میشود.این مقدار به عملکرد دستور dotnet publish را دستخوش تغییر میدهد و در نهایت منجر به تغییر فایل web.config تولید شده توسط این دستور منتهی میشود.پرسش گر: والا به ما گفتی میخوای سیر تا پیاز پابلیش و دیپلوی رو بگی! ولی الان اولین باره که از یک دستور پابلیش صحبت کردی! چند چندی با خودت؟پاسخ: مسئله همین صحبت از سیر تا پیاز است. برای بررسی تمام جنبه های پابلیش و دیپلوی نیاز به تمام این مفاهیم است. در غیر این صورت مطرح کردن چند دستور dotnet cli چیز خاصی نیست که یک برنامه نویس دات نت نداند. کمی صبر کنید.تغییر فایل web.configهمانطور که عرض شد تنظیم مقدار برای پراپرتی &lt;AspnetCoreHostingModel&gt; منجر به فایل تولید شده توسط دستور پابلیش میشود. یعنی هر مقداری که در فایل پروژه تنظیم شود، در نهایت بعنوان یک خصیصه (hostingModel) در المان &lt;aspNetCore&gt; میشود.&lt;aspNetCore processPath=&amp;quotdotnet&amp;quot arguments=&amp;quot.\WebApplication1.dll&amp;quot stdoutLogEnabled=&amp;quotfalse&amp;quot stdoutLogFile=&amp;quot.\logs\stdout&amp;quot hostingModel=&amp;quotInProcess&amp;quot /&gt; نکته مهم اینجاست که محتویات فایل web.config رو بعد از پابلیش گرفتن هم میتوانید تغییر دهید و بین InProcess و OutOfProcess سوییچ کنید. جالب هم هست نه؟ با تغییر یک مقدار در این فایل میتوانید عملکرد برنامه خودتون رو تغییر دهید.یک نکته: منطقا شما هربار که پابلیش میگیرید، باید محتویات فولدر پابلیش جدید شود. اما شواهدی هست که بعد از یک پابلیش جدید ممکن است فایل web.config تغییری نکند. یعنی اگر بطور مثال در فایل پروژه مقدار هاستینگ را قبلا برابر با InProcess گذاشته بودید و پابلیش گرفته بودید، و حالا تغییر دادید و مقدار آن را برابر با OutOfProcess گذاشتید و مجدد پابلیش گرفتید، ممکن است فایل web.config مقدار هاستینگش همچنان InProcess باشد! برای حل چنین مشکلی پیشنهاد میشود یا فایل web.config را پاک کنید و مجدد پابلیش تهیه کنید، یا بطور کلی تمام فولدر پابلیش را حذف کنید و مجدد پابلیش تهیه کنید.بررسی وضعیت هاستینگ در اپلیکیشن در حال اجراخب اگر خودمان مشخص کرده باشیم که مدل هاستینگ چه باشد که میدانیم به چه مدل هاستینگ کردیم :)یا اگر ندانیم میتوانیم مقادیر را در فایل csproj و یا web.config را چک کنیم. اما اگر به هرجهت بخواهیم مطمئن شویم که تغییراتمان به درستی اعمال شده است یا نه، باید چکار کرد؟ در این بخش به پاسخ به این پرسش میپردازیم.چک کردن پروسه dotnetشما میتوانید در برگه پروسه های Task Manager دنبال پروسه dotnet بگردید. اگر اپلیکیشن با dotnet run اجرا شده باشد، به ازای آن یک پروسه دات نت در سیستم بوجود خواهد آمد که مشخص خواهد کرد که حتا چه dllای اجرا شده است. این مورد برای ما این را هم مشخص میکند که اپلیکیشن با تنظیم OutOfProcess اجرا شده است.چک کردن مقدار سرور در هدر Responseشاید بهتر از مورد قبل، اکتفا کردن و قبول کردن مقداری است که در پکت ریسپانس از سرور برمیگردد باشد. برای مشاهده هدر response میتوان از ابزارهای مختلفی مثل Postman استفاده کرد، اما احتمالا دم دستی تر از همه همان ابزار Developer Tools در مرورگرهایی مثل chrome است.اپلیکیشن اجرا شده را با مرورگر باز کنید. البته فراموش نکنید که F12 را بزنید تا Developer Tools هم باز باشد. سپس به برگه Network برید و یک ریکوئست به سمت سرور بفرستید تا این ابزار Response بازگشتی را capture کند.به عکس زیر توجه کنید. همانطور که مشاهده میفرمایید در بخش سرور مقدار Microsoft IIS است.این نمایش دهنده مدل هاستینگ بصورت InProcess است. به چه دلیل؟ به همان دلیلی که از قسمت قبل تا کنون راجع به آن صحبت کردیم :)ولی اگر مقدار این بخش Kestrel بود، مشخصا نشان دهنده OutOfProcess Hosting Model است.به عکس زیر توجه کنید:اجازه دهید این بخش را بیش از ادامه ندهیم. به گمان من تا همین جا کاملا کافی است. و برویم بپردازیم به مسئله مقایسه.مدل In Process یا Out Of Process ؟کدام را انتخاب کنیم؟ اساسا چه فاکتورهایی برای انتخاب دارید؟ مشخصا یکی از آنها Performance یا به نوعی سرعت پاسخ به ریکوئست هایی که به سمت سرور داده میشود. اما همواره مسئله سرعت نیست. گاهی محدودیت ها، یا امنیت یا شرایط معماری زیرساختی نیز مطرح است. میخواهم پیش از آنکه یک مدل ساده برای تست کردن مطرح کنم، با شما توافق کنم اینکه شرایط تست حاظر یک شرایط ساده است، و نتیجه این تست دلیل نمیشود که همیشه یک گزینه برای انتخاب داشته باشیم. شاید در حالت معمول سرعت پاسخ InProcess تا دو برابر از حالت OutOfProcess باشد، اما کامل این امکان پذیر است که در شرایط دیگری مدل OutOfProcess عملکرد بهتری داشته باشد نسبت به گزینه مقابل خودش. چه بسا این را هم در نظر بگیرید محدودیت بزرگ حالت InProcess این است که فقط قابل پشتیبانی در ویندوز است. و اگر شما از شیفتگان لینوکس هستید، انتخاب زیادی ندارید.برای تست من از یک اپلیکیشن سمپل API استفاده کردم، در کلاس کنترلر دو اکشن do-nothing داریم. یعنی عملا کاری نمیکند که خب با شرایط واقعی متفاوت است.public class TestController : Controller
{

    [Route(&amp;quotapi/helloworld&amp;quot)]
    public string HelloWorld()
    {
        return &amp;quotHello World. Time is: &amp;quot + DateTime.Now.ToString();
    }
    
    [Route(&amp;quotapi/helloworldjson&amp;quot)]
    public object HelloWorldJson()
    {
        return new
        {
            Message = &amp;quotHello World. Time is: &amp;quot + DateTime.Now.ToString(),
            Time = DateTime.Now
        };
    }    

    [HttpPost]        
    [Route(&amp;quotapi/helloworldpost&amp;quot)]
    public object HelloWorldPost(string name)
    {
        return $&amp;quotHello {name}. Time is: &amp;quot + DateTime.Now.ToString();
    }
        ... informational requests removed
}
در OutOfProcessنتیجه گزارش تست در هاستینگ OutOfProcess به شرح زیر است که در عکس آماده است:OutOfProcess with Reverse Proxyتست روی لپتاپ من با core i7 صورت گرفته است. همانطور که میبینید نتیجه نشان میدهد که در هر ثانیه چیزی بیشتر از 8.2 هزار است. اما نکته مهم این است که در این مدل  از Kestrel به عنوان وب سرور استفاده شده و از IIS به عنوان Reverse Proxy استفاده شده است. اما ما یک مدل دیگر در OutOfProcess داریم که حائز اهمیت است. در عکس زیر نتیجه تست را در زمانی اندازه گرفتم که اپلیکیشن بطور مستقیم با کسترل و بدون واسطه گری IIS استفاده شده است.همانطور که مشاهده میکنید کسترل خام قدرت بیشتری در پاسخگویی دارد. یعنی پاسخ به بیش از 14 هزار ریکوئست در ثانیه.ناگفته نماند که وب سرورها اصولا در پاسخ دادن به static requestها و dynamic requestها گاها متفاوت رفتار میکنند. بعنوان مثال ممکن است یک وب سرور درخواست های ثابت را بهتر پاسخ دهند. برویم مدل InProcess را ببینیم.در InProcessخب من فقط مقدار hostingmodel را در فایل web.config به مقدار زیر تغییر میدهم و مجدد تست را انجام میدهم:hostingModel=&quot;InProcess&quot;با همین تغییر ساده، نتیجه در عکس به شرح زیر است:نتیجه شگفت آور است. این مدل به بیش از 19 هزار ریکوئست در ثانیه پاسخ می دهد. چیزی نزدیک 2 برابر!تصور نکنید که فقط چون در InProcess یک لایه اضافه ندارد، سریع تر است. تست قبلی را بیاد بیاورید که مستقیما کسترل را اجرا کردیم و reverse proxy را حذف کردیم. نتیجه حذف این لایه به این مقدار نبود (نهایتا تا 6 هزارتا ریکوئست بهبود حاصل شد). مشخصا تفاوت در نحوه پردازش است.البته این تصور هم ایجاد نشود که اپلیکیشن شما دوبرابر سریع تر شده است! این ربطی به اپلیکیشن شما ندارد، صرفا سرعت ارتباط با اپلیکیشن بهبود پیدا کرده است.نکته نهایی اینکه اگر صرفا static page ایجاد میکردیم، این مقدار تا بیش از 50 هزار ریکوئست در ثانیه افزایش پیدا میکرد!پرسش گر: خوب بود، ولی واقعا آخر کدومش؟پاسخ: قسمت اول مقاله را بخاطر دارید؟ چندین دلیل آوردم که چرا از reverse proxy استفاده کنید خوب است. در اینجا هم نشان دادم که اگر استفاده نکنید در حالت معمول سرعت بیشتری خواهید داشت! پس مولفه های مختلفی مطرح شده است. شما بنا به شرایط خود باید انتخاب کنید!در ادامه...با وجود آنکه بسیار خوب پیش آمدیم، اما مباحث زیادی مانده است. با من همراه باشید تا قسمت بعدی! </description>
                <category>محمد عباسی</category>
                <author>محمد عباسی</author>
                <pubDate>Sat, 05 Jun 2021 19:26:37 +0430</pubDate>
            </item>
                    <item>
                <title>سیر تا پیاز انتشار و نصب اپلیکیشن در دات نت (قسمت اول)</title>
                <link>https://virgool.io/@mujan/%D8%B3%DB%8C%D8%B1-%D8%AA%D8%A7-%D9%BE%DB%8C%D8%A7%D8%B2-%D8%A7%D9%86%D8%AA%D8%B4%D8%A7%D8%B1-%D9%88-%D9%86%D8%B5%D8%A8-%D8%A7%D9%BE%D9%84%DB%8C%DA%A9%DB%8C%D8%B4%D9%86-%D8%AF%D8%B1-%D8%AF%D8%A7%D8%AA-%D9%86%D8%AA-oqhik7ctre52</link>
                <description>سیر تا پیاز انتشار و نصب اپلیکیشن دات نتی مقالات و آموزشهای بسیاری در حوزه توسعه اپلیکیشن دات نتی تالیف شده اند، از بایدها و نبایدهای معماری، تا چگونگی کد نویسی و شناخت فریمورک ها و امثالهم. اما در این بین کمتر به موضوع publish کردن اپلیکیشن و همچنین باید و نبایدهای deploy اپلیکیشن پرداخته شده است.اگر تاکنون دراین خصوص مطالعه مستقلی نداشته اید، و یا صرفا به روال های معمول اکتفا کرده اید و کارتان هم راه افتاده است، یا خیلی خوش شانس هستید و در جایی مشغول به کار هستید که افراد دیگری جور ندانستن شما را در تیم کشیده اند، و یا کار جدی انجام نداده اید!سلام، من محمد عباسی هستم. در این مقاله با ذره بین به تمام وجه های publish و deploy اپلیکیشن های دات نتی میپردازم و در این راه از مقالات، کتب و تجربیاتم استفاده میکنم.دامنه این مقاله در حوزه برنامه نویسی میماند، و به هیچ عنوان وارد مباحثی که مختص به دوستان عملیاتی و مدیرسیستمی هستند، نمیشود.فهرست مطالبدرک Hosting اپلیکیشندرک Hosting اپلیکیشنبعد از اینکه یک وب اپلیکیشنی را توسعه دادیم و آن را آماده استفاده کردیم، چه کاری باید انجام دهیم؟ جواب هاست کردن است. ما باید اپلیکیشنی را که توسعه دادیم را روی یک سرور هاست کنیم تا در دسترس دیگران قرار بگیرد. اساسا به فرایند نصب و دیپلوی اپلیکیشن روی یک سرور را هاست کردن (hosting) میگویند.هر زمان که یک وب اپلیکیشن ایجاد میکنید، بطور پیشفرض یک وب سرور بواسطه دات نت درون خودش قرار میگیره بنام کسترل (Kestrel) که تمام آنچه که برای سرویس دهی وب نیاز هست رو پوشش میدهد.کسترل این امکان رو برای اپلیکیشن های دات نتی ایجاد میکنه که بصورت cross platform روی سیستم عامل های مختلف نظیر ویندوز، مک و لینوکس اجرا بشند.کسترل با استفاده از کتابخانه libuv ساخته شده، همون کتابخانه ای که node هم ازش استفاده میکنه.توی کلاس Program.cs و در متد ConfigureWebHostDefaults یک اکستنشن متد بنام UseKestrel صدا زده میشه که امکان استفاده از کسترل رو برای ما مهیا میکنه.کلاس Program.csبا این تعاریف اشتباه نیست که وب سرور کسترل رو بخشی از اپلیکیشن دات نت بدونیم، نه یک تیکه جدا از آن. البته این یکپارچگی به معنای در هم تنیدگی نیست. معماری فریمورک ASP.NET بسیار ماژولار است و امکان تغییر هر جز آن وجود دارد که در ادامه هم به آن اشاره میکنم.در اینجا ASP.NET Core Web Server همان Kestrel استبرای ختم کلام در رابطه با خود کسترل، فقط این نکته را اضافه کنم که این وب سرور، همانی است که آبجکت HttpContext را برای ما ایجاد میکند و تحویل Middlewareهای پایینی میدهد، و Respone را از آنها تحویل میگیرد و به کاربر برمیگرداند.همانطور که عرض شد، کسترل درخواست را دریافت میکند و به بدنه اپلیکیشن برای تولید پاسخ میفرستد. (که در تصویر زیر شاهد آن هستید). این مدل از هاست کردن، فرایندهای وب سرور و پراکسی را از خود اپلیکیشن جدا میکند، این جدا سازی هم این امکان را میدهد که اپلیکیشن بدون آنکه خودش تغییری داشته باشد، بتواند در محیط های مختلف هاست شود. از آنجاییکه که در عکس تصویر یک Reverse Proxy (قدم های 1 و 7) مشاهده شد، و در تعریف بالا یک جدا سازی بین وب سرور و پراکسی لحاظ کردیم، لازمه که در این خصوص صحبت کنیم.ما میتوانیم از کسترل برای ارتباط مستقیم با کلاینت استفاده کنیم، و همچنین میتوانیم بین کسترل و کلاینت یک سرور واسط قرار دهیم که این ارتباط را برقرار کند. به این سرور واسط Reverse Proxy میگویند. اما چرا باید این کار رو انجام داد؟ یا سوال دقیق تر اینکه چه میشود که چنین نیازمندی ای بوجود میاید؟ چرا که بایدی در کار نیست، همانطور که عرض شد کسترل میتواند بصورت مستقیم با کلاینت ارتباط برقرار کند.پرسش گر: ببخشید مهندس، یکم یواش تر! من اصلا نفهمیدم که این Reverse Proxy چی بود!پاسخ: ببینید در اصل Reverse Proxy برنامه ای است که ریکوئست را از اینترنت میگیرد، و بعد خودش به وب سرور اپلیکیشن مدنظر ما ریکوئست میزنه. اینکار در مقابل با عملی است که اپلیکیشن ما مستقیما از اینترنت ریکوئست رو دریافت کنه.پرسش گر: فقط اینترنت؟پاسخ: نه دیگه، منظور بیرون از شبکه داخلی هست. ریورس پراکسی اساسا در لبه یک شبکه (یا حتا یک سرور) قرار میگیرد، و تنها پل ارتباطی اون شبکه (یا سرور) با بیرون میشه. هر ریکوئستی که از بیرون (حالا چه اینترنت، و چه شبکه داخلی بزرگتر) بخواهد به این شبکه ما (یا سرور ما) برسد، به دست ریورس پراکسی میرسد، و اون تصمیم میگیرد که با این ریکوئست چکار بکند. که در حالت معمول اون روکوئست رو میفرسته (forward) به وب سرور اپلیکیشن مدنظر، و پاسخ تولید شده توسط اپلیکیشن رو هم میگیره و پس میده به کلاینت.پرسش گر: خب چرا به چنین چیزی نیاز هست؟پاسخ: یکم زبان به کام بگیرید، داشتم همین را توضیح میدادم که سوال مطرح کردید!همانطور که توضیح داده شد، ما میتوانیم در یک سرور (یا یک شبکه ای از سرورها) چند اپلیکیشن داشته باشیم، و در لبه یا پل ارتباطی آن سرور (یا شبکه) یک Reverse Proxy ایجاد کنیم که از بیرون (حال چه اینترنت چه شبکه ای دیگر) از طریق آن ریورس پراکسی با اپلیکیشن های ما در ارتباط باشند. از آنجاییکه اپلیکیشن های ما هم در پلتفرم وب هستند، پس قضیه وب سرور منتقی نیست! صرفا یک لایه (ریورس پراکسی) اضافه شده است (تصویر زیر). و به عبارت دقیق تر ریورس پراکسی رابط وب سرور و کلاینت است.این مدل از هاست کردن اپلیکیشن (که یک ریورس پراکسی مابین قرار بگیرد) مزایایی دارد:امنیت: اساسا ریورس پراکسی ها برای خوب هندل کردن ترافیک های مخرب طراحی شدند، و خب برای اینکار خیلی آزموده شدند و مطمئن هستند.عملکرد: ریورس پراکسی ها میتونند یکسری از responseها را برای بهبود عملکرد کش کنند و بعضی از ریکوئست ها رو بدون فرستادن سمت اپلیکیشن هندل کنند.مدیریت فرایند: خب واقعیت این است که بعضی وقت ها اپلیکیشن های کرش میکنند. بعضی از ریورس پراکسی ها امکان رصد کردن دارند، و میتونند ترافیک را به سمت instanceهای سالم بفرستند، یا اپلیکیشن را بصورت اتوماتیک restart کنند.پشتیبانی از چندین اپلیکیشن: همانطور که در عکس قبل هم نشان دادیم، ممکن است در یک سرور چندین اپلیکیشن درحال اجرا باشد، ریورس پراکسی این سناریو رو به خوبی پشتیبانی میکند.با همه این مزایا، استفاده از ریورس پراکسی همیشه گل و بلبل نیست! گاهی تنظیمات خود ریورس پراکسی میتواند پیچیده باشد و هم اینکه اضافه شدن یک لایه اضافه میتونه سرعت رو تحت تاثیر قرار دهد.پرسش گر: تا اینجا که من فهمیدم، ریورس پراکسی یکسری مزایا دارد، اما به هر جهت یک لایه ریکوئیست را دریافت میکند، میفرستد سمت وب سرور درون اپلیکیشن، و آن ریسپانس را آماده میکند. در صورتیکه خود وب سرور اپلیکیشن میتوانست مستقیما همین کار را بکند تا سرعت عملکرد آن بخاطر حذف این لایه بهتر باشد.پاسخ: بله، همینطور است. مزایایی که از آن صحبت شد، در گرو وجود این لایه اضافه است، و خب مشخصا اگر این لایه اضافه نباشد، شاید ما کمی بهبود سرعت داشته باشیم، اما از آن مزایا خبری نیست. ولی باز هم برای نتیجه گیری زود است، در مقاله بعدی من این بحث را مجدد باز میکنم، و در آنجا سعی میکنم به موراد بیشتری اشاره کنم تا قدرتمان برای تصمیم گیری افزایش یابد.برای هاست کردن اپلیکیشن های ASP.NET Core ما به هر دو روش که در بالا به آن اشاره شد، میتوانیم عمل کنیم. در قسمت بعدی به این موضوع میپردازم و مفاهیم InProcess و OutOfProcess را شرح میدهم.</description>
                <category>محمد عباسی</category>
                <author>محمد عباسی</author>
                <pubDate>Thu, 03 Jun 2021 22:53:42 +0430</pubDate>
            </item>
                    <item>
                <title>جاوا، جاوا و بازهم جاوا (قسمت اول و البته آخر!!)</title>
                <link>https://virgool.io/@mujan/%D8%AC%D8%A7%D9%88%D8%A7-%D8%AC%D8%A7%D9%88%D8%A7-%D9%88-%D8%A8%D8%A7%D8%B2%D9%87%D9%85-%D8%AC%D8%A7%D9%88%D8%A7-%D9%82%D8%B3%D9%85%D8%AA-%D8%A7%D9%88%D9%84-%D9%88-%D8%A7%D9%84%D8%A8%D8%AA%D9%87-%D8%A2%D8%AE%D8%B1-eulvx1cdojfr</link>
                <description>سلام!خوشحالم که در اینجا هستید، من قرار است یک سلسله مقاله درمورد جاوا بنویسم و مفتخرم از اینکه می‌تونم در اینجا نتایج را با شما به اشتراک بگذارم. اما دقیقا قرار است از چه صحبت کنم؟همه چیز از یک مصاحبه کاری شروع شد، همه چیز عالی پیش‌ می‌رفت تا اینکه مصاحبه‌کننده پرسید با امکانات جاوا ۸ کار کردی؟! و من گفتم از تمام امکاناتی که ارائه شده، فقط با Streamهایی که در جاوا ۸ ارائه شد آشنا نیستم. از همانجا سوال برای خودم پیش آمد که چرا آشنا نیستم؟! آیا بعنوان یک برنامه‌نویس که روی تکنولوژی‌های جاوا کار می‌کند نباید با تمام امکانات آن آشنا باشم؟ من سال‌هاست که می‌دانم جاوا ۸ امکانی اضافه کرده بنام Stream، ولی واقعا چقدر از آن می‌دانم؟ مدت‌ها خودم را با این بهانه‌ها توجیح می‌کردم که امکانات جاوا ۸ بیشتر مختص به برنامه‌نویسی فانکشنال است و فعلا ارتباطی بکار من ندارد و این تصور را هم (به غلط) داشتم که Streamها نیز مربوط به مبحث I/O می‌شود، هر زمان که در کار به این مسائل برخوردم به سراغ ‌آنها خواهم رفت. اما من حتا نگاهی درست و درخور به این مباحث نداشتم و خودم نیز میدانستم که از این بهانه‌ها نیز نمی‌توانم با قاطعیت دفاع کنم. به همین خاطر پس از مصاحبه به سراغ فراگیری امکانات جاوا ۸ رفتم!اما یک لحظه صبر کنید! الان که این مقاله نگارش می‌شود جاوا ۱۲ هم منتشر شده است. من تقریبا ۵ نسخه از جاوایی که اکنون ارائه شده است عقب هستم، کمی برای من فاجعه‌بار است! البته از سال ۲۰۱۷ که جاوا ۹ ارائه شده است سرعت انتشار نسخه‌های مختلف بسیار شدت گرفته است، یعنی توسعه‌دهندگان زبان جاوا برای زنده نگهداشتن این زبان تمام تلاش و توان خودشان را بکار بسته‌اند، اما من استفاده کننده همچنان اندرخم جاوا ۸ هستم! اگر بخواهیم بازار زبان جاوا در ایران از بین نرود (یا کم‌رنگ نشود) باید از تمام توان این زبان که برای نیازهای روز در حال توسعه است، به درستی استفاده کنیم. فلذا تصمیم گرفتم تمام امکانات زبان جاوا را پس از نسخه‌ی ۷ یادبگیرم و ارائه دهم.JDK 1.0 (January 23, 1996)JDK 1.1 (February 19, 1997)J2SE 1.2 (December 8, 1998)J2SE 1.3 (May 8, 2000)J2SE 1.4 (February 6, 2002)J2SE 5.0 (September 30, 2004)Java SE 6 (December 11, 2006)Java SE 7 (July 28, 2011)Java SE 8 (March 18, 2014)Java SE 9 (September 21, 2017)Java SE 10 (March 20, 2018)Java SE 11 (September 25, 2018)Java SE 12 (March 19, 2019)دوستان، این مسیر، یک ماجراجویی فوق‌العاده و هیجان‌انگیز برای من است، دوست دارید در این راه با من همراه باشید؟ من از به اشتراک گذاشتن این مطالب نیز هیجان‌زده هستم.مطالب قرار است چگونه باشند؟ قطعا مثل تمام مقالاتی که من می‌پسندم و تلاش می‌کنم مقالاتم آنگونه باشد، قرار است ریز و با بیان جزئیات به شکلی صریح و واضح و دقیق باشد. این مقالات سری هستند، یعنی یک سیر زمانی دارند، ولی هر موضوع مستقل گفته خواهد شد، اما این را هم درنظر داشته باشید، که خیلی از مباحث بطور ذاتی به مباحثی دیگر مرتبط هستند. بطور مثال در تشریح جاوا ۸ من تمام امکانات را به استقلال بیان خواهم کرد، ولی چطور باید Method Referenceها را فهمید وقتی فرد از Functional Method و Lambda بی‌اطلاع است؟ پس پیشنهاد من این است تا جاییکه امکانش هست از ابتدا شروع کنید.آیا احتمال خطا وجود دارد؟ قطعا! لطفا اگر اشتباه نگارشی دیدید لطف کنید و اطلاع دهید و از آن مهم‌تر اگر اشکال فنی دیدید خواهش می‌کنم حتما زمان بگذارید و اطلاع دهید! چرا که هم من دچار اشتباه هستم و هم شاید خیلی‌های دیگر را نیز به اشتباه بندازم (اینجای کار یک وظیفه‌ی صنفی است).توجه داشته باشید که ما از جاوا نسخه‌ی ۷ شروع خواهیم کرد و کار را با آخرین نسخه خاتمه خواهیم داد، امیدوارم در طول این مسیر همراه من باشید.چرا؟چرا باید فیچرهای جدید زبان را آموخت؟ چه سوال پرواضح‌ای! اگر شما هم مثل من از آن دست آدم‌هایی هستید که تقریبا از جاوا ۷ استفاده می‌کنید، حتما میدونید که دائما در حال استفاده از امکاناتی هستید که در جاوا ۶ وجود نداشته است.در واقع امکانات بسیار گسترده‌ای با کمک مفاهیم جدید ارائه شده است که عدم آشنایی با آنها یک ضعف محسوب می‌شود. دانستن امکانات جدید بقدری بدیهی بنظر می‌رسد که گمان می‌کنم همه می‌دانیم که فراگیری آن‌ها یک ضرورت است.از طرفی، هرچه ما در استفاده از امکانات جدید تنبل باشیم، دنیا و برنامه‌نویسان حرفه‌ای اینگونه نخواهند بود، فلذا به مرور دست‌خط برنامه‌نویسی با زبان جاوا متحول خواهد شد. بعبارتی شما سینتکسی را در کدهای دیگران مشاهده خواهید کرد که الزامش را درک نخواهید کرد. تصور کنید، نتوانید چند خط کد را به زبانی که سال‌ها با آن سر و کله زده‌اید بخوانید! فاجعه‌ است!برنامه‌نویسی فانکشنال هم به جاوا ۸ اضافه شده است، یعنی یک پارادایم برنامه‌نویسی به آن اضافه شده است، چطور می‌توان از کنار آن به راحتی گذشت و بی‌اهمیت بود؟کدها در نسخه‌های جدید، ساده‌تر و گویا‌تر هم شده‌اند! می‌خواهید از کنار آنها ساده‌ بگذرید؟ مطمئنم که چنین نخواهید کرد، در غیر این‌صورت اینجا حضور نداشتید!هرچه بیشتر این دلایل را ذکر می‌کنم، بیشتر عذاب وجدان آزارم می‌دهد که چرا تا مدت‌ها نسبت به آن بی‌اهمیت بوده‌ام! بگذارید این عذاب را به این نحو (نگارش این سلسله مقالات) به پایان برسانم!آنچه در ادامه خواهید دید!بنظرم بد نیست نگاهی به ویژگی‌ها و امکاناتی که به نسخه‌ی ۷ اضافه شد بیاندازیم، بعبارتی بهتر از نسخه‌ی ۷ شروع کنیم. نسخه‌ای که در سال ۲۰۱۱ منتشر شد و باعث خوشحالی برنامه‌نویسان جاوا ۶ بود.پس پیش‌به‌سوی تشریح امکاناتی که در جاوا ۷ ارائه شد!فهرستپروژه‌ی Coinویژگی Diamond Operatorاستفاده از رشته در عبارات switchمدیریت خودکار منابعفرم‌دهی زیبای اعدادذخیره‌ی باینریبهبود مدیریت خطاکتابخانه‌ی جدید برای کار با فایل‌سیستم (NIO 2.0)پروژه‌ی Coinجاوا ۷ چندین ویژگی در قالب پروژه‌ی Coin به زبان اضافه کرد. این ویژگی‌ها برای برنامه‌نویسان بسیار کاربردی بود. اما پروژه‌ی Coin دقیقا چیست؟ از فوریه‌ی سال ۲۰۰۹ تا مارس ۲۰۰۹ فراخوانی داده شد تا افراد پروپزال‌های خودشان را برای اضافه کردن به زبان جاوا ارسال کنند، نزدیک به ۷۰ پروپزال ارسال شد که از این بین ۵ پروپزال نهایی شد تا در قالب پروژه‌ی Coin در جاوا ایجاد شوند. در نهایت هم تصمیم گرفته شد این ۵ پروپزال نهایی به JDK 7 اضافه شود.هدف این پروژه شناسایی مجموعه تغییرات کوچک در زبان بود. در آخر لیست زیر نهایی شد:Strings in switchBinary integral literals and underscores in numeric literalsMulti-catch and more precise rethrowImproved type inference for generic instance creation (diamond operator)try-with-resources statementSimplified varargs method invocationدر ادامه تک تک این ویژگی‌ها را به تفصیل بحث خواهیم کرد. صرفا می‌خواستم بگویم بخشی از تغییرات در قالب پروژه‌ی Coin به جاوا ۷ اضافه شدش.ویژگی Diamond Operatorمی‌دانیم وقتی که با Genericها کار می‌کنیم باید Type آنها را مشخص کنیم. بطور مثال وقتی یک HashMap تعریف می‌کنیم که کلید آن String و مقدار آن Integer است باید به شکل زیر بنویسیم:Map&lt;String, Integer&gt; myMap = new HashMap&lt;String, Integer&gt;();خب همانطور که مشاهده می‌کنید Type در هر دو طرف عملگر انتساب (=) مشخص شده است. که خب البته کمی پرگویی بی‌فایده بنظر می‌رسه. بنظرتون کامپایلر نمی‌تونه از سمت چپ تشخیص بده Type آبجکت چی هست تا مجبور نباشیم در سمت راست آن را تکرار کنیم؟ جواب این است که تا جاوای ۷ خیر، کامپایلر نمی‌توانست! اما از جاوای ۷ به بعد کافی است که فقط سمت چپ را بنویسید. پس در جاوای ۷ چنین می‌نویسیم:Map&lt;String, Integer&gt; myMap = new HashMap&lt;&gt;();بخاطر اینکه صرفا یک &lt;&gt; خالی می‌گذاریم، به آن عملگر لوزی می‌گویند (شبیه به لوزی است). حتا می‌توانیم از گذاشتن عملگر لوزی هم صرف نظر کنیم، در این صورت کامپایلر صرفا اخطارهای type-safety تولید خواهد کرد:Map&lt;String, Integer&gt; myMap = new HashMap ();استفاده از رشته در عبارات switchعبارت‌های switch یا انواع primitive را پشتیبانی می‌کردند یا انواع enumerated. اما از جاوا ۷ نوع دیگری به آنها اضافه شد، یعنی نوع String!پیش از جاوا ۷ برنامه‌نویس مجبور بود برای مقایسه‌ی چندین رشته از if-else استفاده کند، اما از جاوا ۷ به بعد می‌تواند رشته‌ها را در عبارت سوییچ بکار گیرد. به مثال زیر توجه کنید:switch (token)
{
    case (&amp;quotone&amp;quot):
        return &amp;quotToken one identified&quot;
    case (&amp;quottwo&amp;quot):
        return &amp;quotToken one identified&quot;
    case (&amp;quotthree&amp;quot):
        return &amp;quotToken one identified&quot;
    case (&amp;quotfour&amp;quot):
        return &amp;quotToken one identified&quot;
    default:
        return &amp;quotNo token was identified&quot;
}مدیریت خودکار منابعیکی از بهترین ویژگی‌هایی که در جاوا ۷ اضافه شد و منجر شد خیلی از سهل‌انگاری‌های برنامه‌نویسان نادیده گرفته شد همین ویژگی try-with-resources می‌باشد. پیش‌تر (قبل از جاوا ۷) برنامه‌نویس منابعی را که در بلاک try-catch گرفته بود، به کمک بلاک finally رها می‌کرد. البته آزادسازی در بلاک finally اجباری نبود، ولی رهاسازی منابع اخذ شده اجباری بود (حال به هر شکلی). اما از نسخه‌ی ۷ به بعد اگر برنامه‌نویس منابع را به کمک try-with-resources اخذ کند، خود کامپایلر در انتهای کار منابع را به درستی آزاد می‌کند و چنانچه برنامه‌نویس هم فراموش کند، مشکلی پیش نخواهد آمد.اما چطور این اتفاق می‌افتد؟ پاکسازی به کمک اینترفیس جدیدی (در نسخه‌ی جاوا ۷) بنام AutoCloseable رخ می‌هد. متد close این اینترفیس توسط خود JVM فراخوانی می‌شود، درست در پایان بلاک try.توجه کنید، وقتی منبعی را به کمک try-with-resources اخذ می‌کنید، دیگر خودتان نباید بصورت دستی متد close() آن منبع را فراخوانی کنید. خود JVM این کار را انجام خواهد داد. دستی فراخوانی کردن آن منجر به رفتارهای غیرقابل انتظار خواهد شد.public class ResourceManagementInJava7
 {
  public static void main(String[] args)
  {
   try (BufferedReader br = new
  BufferedReader(newFileReader(&amp;quot/tmp/test.txt&amp;quot)))
 {
  String sCurrentLine;
  while ((sCurrentLine = br.readLine()) != null)
 {
 System.out.println(sCurrentLine);
 }
 }
 catch (IOException e)
 {
 e.printStackTrace();
 }
 }
}اگر کد بالا کمی شلوغ و گیج کننده است به سینتکس زیر دقت کنید. تنها کافی است که منبع را در پرانتزی مقابل try اخذ کنیم:try(resources_to_be_cleant) {
   // your code
}نکته‌ی حائز اهمیت این است که اگر قرار باشد چندین منبع را اخذ کنیم، آنها را با سیمی‌کالن (;) جدا خواهیم کرد.تکرار کنم که هر منبعی که اینترفیس AutoCloseble را implement کرده باشد، می‌تواند درون try-with-resources قرار بگیرد. این اینترفیس سوپرکلاسِ اینترفیس java.io.Closeable است که تنها یک متد close() دارد که توسط خود JVM فراخوانی می‌شود.فرم‌دهی زیبای اعداداعداد با ارقام بالا، مخصوصا صفرهای زیاد چشم را آزار می‌دهند. مثلا 100000000 چند صفر دارد؟ چه عددی است؟ ما معمولا ارقام را به شکل 100,000,000 مشاهده می‌کنیم و به کمک جدا کننده‌ای (,) براحتی تشخیص می‌دهیم که چه عددی است.یکی از ویژگی‌های جالبی که در جاوا ۷ اضافه شد، و من کمتر در کدهای دیگران میبینم که استفاده بشود، امکان استفاده از خط زیرین (_) در اعداد است. بطور کلی خط زیرین (یا همان Underscore) در اعداد نادیده گرفته می‌شود (یعنی عدد را عوض نمی‌کنند) و صرفا برای فرم‌دهی و خواناتر شدن برای انسان است. بطور مثال شما می‌توانید در کد جاوا عدد 100000000 را با خط‌زیرین خوانا تر کنید./**
* Supported in int
* */
int improvedInt = 10_00_000;

/**
* Supported in float
* */
float improvedFloat = 10_00_000f;

/**
* Supported in long
* */
float improvedLong = 10_00_000l;

/**
* Supported in double
* */
float improvedDouble = 10_00_000;از این قابلیت می‌توانید در نمایش اعداد باینری هم استفاده کنید. البته خود باینری‌ها خودشان یک موضوع قابل بحث جدا در جاوا ۷ هستند، به آن می‌پردازیم!ذخیره‌ی باینریدر جاوا ۷ شما این امکان را دارید که مقادیر باینری (دودویی) را با پیشوند &#x27;0b&#x27; یا &#x27;0B&#x27; در متغییرهای عددی نظیر byte، short، int و long ذخیره کنید. پیش از JDK 7 فقط امکان استفاده از مقادیر اُکتال (با پیشوند &#x27;0&#x27;) یا هگزادسیمال (با پیشوند &#x27;0x&#x27; یا &#x27;0X&#x27;) وجود داشت.با استفاده از این ویژگی دیگر برنامه‌نویس مجبور به convert این مقادیر نخواهد بود.int sameVarOne = 0b01010000101;
//or if use the number formatting feature as well.
int sameVarTwo = 0B01_010_000_101;همانطور که ملاحظه می‌کنید امکان فُرم‌دهی با خطِ‌زیرین هم محیا می‌باشد.بهبود مدیریت خطاچند اتفاق خوب تو بخش مدیریت‌ خطاها رخ دادش. جاوا ۷ امکان multi-catch رو برای catch گرفتن چندین نوع از exceptionها تنها با یک خط بلاک catch را اضافه کرد.اجازه دهید با یک مثال آن را شرح دهم، فرض کنید متدی دارید که ۳ اکسپشن پرتاب می‌کند. در این وضعیت بطور معمول (تا نسخه‌ی ۶) باید چنین کرد:public voidoldMultiCatch() {
            try{
                  methodThatThrowsThreeExceptions();
            } catch(ExceptionOne e) 
                  // log and deal with ExceptionOne
            } catch(ExceptionTwo e) {
                  // log and deal with ExceptionTwo
            } catch(ExceptionThree e) {
                  // log and deal with ExceptionThree
            }
      }همانطور که مشاهده می‌کنید میتوانید بی‌شمار اکسپشن‌ را یکی پس از دیگری catch کنید. البته حتما به خاطر دارید که ترتیب اکسپشن‌ها نیز اهمیت دارد. این رفتار (تعداد زیادی بلاک catch) بشدت خطا پرور است! جاوا ۷ امکانی اضافه کرد در زبان تا این شکل زشت تغییر یابد، نگاهی به کد تغییر کرده در جاوا ۷ بیاندازید:public voidnewMultiCatch() {
            try{
                  methodThatThrowsThreeExceptions();
            } catch(ExceptionOne | ExceptionTwo | ExceptionThree e) { // به این خط دقت کنید
                  // log and deal with all Exceptions
            }
      }خب، همانطور که مشاهده می‌کنید چندین اکسپشن تنها در یک بلاک catch با استفاده از عملگر &#x27;|&#x27; مشخص شده است. شاید برای شما یک سوال پیش بیاید، اگر بخواهیم برای یکی از اکسپشن‌ها رفتاری جدا درنظر بگیریم چکار باید کرد؟ جواب ساده است، باید از multi multi-catch استفاده کرد. یعنی آن دسته از اکسپشن‌هایی را که قرار است رفتار مشابهی با آنها داشته باشیم را با عملگر | در یک بلاک قرار می‌دهیم و آن‌هایی را که می‌خواهیم جدا کنیم را نیز همچون سابق یک بلاک catch جدا برای آن درنظر می‌گیریم. کد زیر را نگاه کنید:public voidnewMultiMultiCatch() {
            try{
                  methodThatThrowsThreeExceptions();
            } catch(ExceptionOne e) {
                  // log and deal with ExceptionOne
            } catch(ExceptionTwo | ExceptionThree e) {
                  // log and deal with ExceptionTwo and ExceptionThree
            }
      }خب همانطور که واضح است در مثال بالا ExceptionTwo و ExceptionThree در یک بلاک قرار داده شده است و به یک شکل با‌ آنها رفتار می‌شود اما ExceptionOne جدا در نظر گرفته شده است و منطقی منحصربه‌فرد برای مدیریت اکسپشن دارد.کتابخانه‌ی جدید برای کار با فایل‌سیستم (NIO 2.0)برای کار با فایل‌ها از خیلی قدیم‌ترها پکیجی وجود داشت بنام java.io که احتمالا با آن آشنا هستید، شاید هم نباشید! اما بعدتر، در جاوا ۴ (سال ۲۰۰۲) پکیج جدیدی عرضه شد بنام java.nio که منظورشان از nio همان new io بوده است. که خب امکانات بیشتری نسبت به پکیج io به آن اضافه شده بود.اما در نهایت در جاوای ۷، پکیجی بنام java.io.file ارائه شد که سعی می‌کرد محدودیت پکیج‌های io نظیر کپی کردن فایل‌ها را برطرف کند. به این پکیج جدید (یعنی java.io.file) به اختصار nio 2.0 هم می‌گویند. این پکیج شامل کلاس‌ها و اینترفیس‌های مهمی نظیر Path، Paths، FileSystem، FileSystems و Files است. که در ادامه به تفصیل آن‌ها را بررسی خواهیم کرد.کلاس Paths و اینترفیس Pathکلاس Paths یک کلاس کمکی است که تنها یک متد استاتیک بنام get دارد. متد get() یک آدرس را می‌گیرد و یک شی از نوع Path بر می‌گرداند. هر شی از نوع Path هم اطلاعاتی در مورد فایل یا دایرکتوری مدنظر دارد.Path path = Paths.get(&amp;quot/home/mujan/Desktop/file1&amp;quot);این کلاس و اینترفیس در بسته‌ی java.nio.file هستند، یعنی در جاوای ۷ اضافه شدند، فلذا توصیه می‌شود از این پس بجای استفاده از کلاس File، از امکانات جدید جاوا ۷ استفاده کنید. البته متدهایی نیز برای تبدیل آبجکت‌های Path به File و بلعکس نیز وجود دارد که برای ایجاد سازگاری با نسخه‌های جدید یا قدیم می‌توانید استفاده کنید. متدهای toPath و toFile برای این منظور است.Path path = Paths.get(&amp;quot/home/mujan/Desktop/file1&amp;quot);
File file = Path.toFile&#40;path&#41;;کلاس Filesکلاس Files هم در بسته‌ی java.nio.file قرار دارد و مملو از متدهای استاتیک کاربردی و متنوع است. با کمک متدهای این کلاس می‌توانید فایل کپی کنید، جابجا کنید، بنویسید، حذف کنید، ویژگی‌های آن را دریافت کنید یا دایرکتوری ایجاد کنید و خیلی از کارهای دیگر! باز هم توصیه می‌شود بجای استفاده از کلاس File از کلاس Files استفاده کنید.خب، به‌گمانم هیچ چیز مثل مثال نمی‌تواند ویژگی‌های این کلاس پرکاربرد را نمایش دهد. البته دقت داشته باشید که برای کار با کتابخانه‌های io حتما باید exception را هندل کنید (حال یا با try-catch یا throws کردن). ولی من در اینجا فقط خط به خط کاربردهای کلاس را لیست می‌کنم و کاری به این مسائل نخواهم داشت.Files.createDirectory(Paths.get(&amp;quot/home/mujan/new_dir&amp;quot);کد بالا همانطور که مشخص است منجر به ایجاد یک دایرکتوری (new_dir) در آدرس /home/mujan می‌شود.میدونید Symbolic Link چیه؟ اگر کاربر ویندوز هستید یک چیز مشابه به shortcutها هستش. بچه‌های لینوکسی احتمالا با symbolic linkها کار کردند. با کلاس Files امکان ایجاد سیمبولیک لینک هم وجود داره:File.createSymbolicLink(Paths.get(&amp;quot/home/dest&amp;quot), Paths.get(&amp;quot/home/src&amp;quot));اما برای خواندن و نوشتن چکار باید کرد؟ به قطعه کد زیر دقت کنید، ابتدا یک شی از کلاس Path بنام src ایجاد می‌کنم که یک فایل text می‌باشد، سپس یکبار آن را بصورت باینری می‌خوانم و بعد بصورت text و همچین قابل write بودن و نبودن آن را نیز تست می‌کنم. سپس سعی می‌کنم که فایلی مشابه آن در جای دیگری بنویسم، و در نهایت یک کپی از فایل ایجاد می‌کنم:Path src = Paths.get(&amp;quot/home/mujan/text&amp;quot);
byte[] bytes = Files.readAllBytes(src);
List&lt;String&gt; strings = Files.readAllLines(src);
boolean check = Files.isWritable(src);
Files.write(byte, Paths.get(&amp;quot/home/mujan/anotherBytesFile&amp;quot)); // save Bytes
Files.write(string, Paths.get(&amp;quot/home/mujan/anotherStringFile&amp;quot)); // save String  
Files.write(string, Paths.get(&amp;quot/home/mujan/otherStrinFile&amp;quot), StandardOpenOption.APPEND)); *
Files.copy(src, Paths.get(&amp;quot/home/mujan/anothorFile&amp;quot));خب به کدهای بالا نگاه کنید، تقریبا واضح و شفاف هستند. دقت کردید که عملیات خواندن از فایل با کمک متد readAllLines و readAllBytes چقدر راحت است؟ یا همچنین عملیات نوشتن. اگر پیاده‌سازی‌های آن را هم نگاه کنید خیلی بهینه نوشته شدند و از buffered reader و writer هم استفاده کردند.اما اجازه دهید توضیحی کوتاه راجع به خطی بدهم که با * مشخص کرده‌ام. در این خط با یک رشته را می‌نویسیم ولی یک Open Option هم بعنوان پارامتر پاس می‌دهیم. با کمک Open Optionها مشخص می‌کنیم که میخواهیم چه رفتاری با فایل شود. آیا اطلاعات جدید در فایل append شوند (اضافه شوند به انتهای فایل) یا بر روی فایل overwrite شوند؟ این قبیل رفتارها را میتوانید با Open Optionها مشخص کنید.اعلان تغییرات فایلکار ما با بسته‌ی NIO 2 تمام نشده است. یکی از امکانات جالب و کمتر شناخته‌شده‌ی جاوا ۷، امکان اعلان تغییرات فایل است. از آن دست فیچرهایی هم هست که مدت زیادی منتظر ماند تا در نهایت در کتابخانه‌ی NIO 2 عرضه شد. من در ادامه در مورد اینترفیس WatchService صحبت خواهم کرد.دقت کردید وقتی تو IDE کد می‌زنید تغییرات فایل‌ها رو بهتون نشون میده؟ یعنی مثلا فایلی که تغییر میدید رو با ی رنگ دیگه نشون میده. چطور این کار رو انجام میده؟ اون‌ها دارند از امکانی بنام file change notification استفاده می‌کنند که تقریبا در تمام فایل‌سیستم‌ها وجود دارد. بطور کلی می‌توان کدی نوشت که فایل‌سیستم را مانیتور کند که آیا دایرکتوری یا فایل خاصی تغییر می‌کند یا خیر. اما این راه‌حل برای زمانیکه تعداد فایل‌ها و دایرکتوری‌هایی که می‌خواهیم رصد کنیم به صد یا هزار می‌رسد، مناسب نیست. بعبارتی این سولوشن scale پذیر نیست.در کتاب‌خانه‌ی NIO 2 در جاوا ۷، APIای بنام WatchService عرضه شد که یک راه‌حل قابل توسعه برای رصد کردن تغییرات فایل‌ها و دایرکتوری‌ها ‌می‌باشد. خب یک API ساده و شفاف است، برای این کار بهینه نوشته شده و دیگه نیازی نیست ما خودمون چیزی را پیاده سازی کنیم. اما چطور کار می‌کند؟برای استفاده از امکان WatchService، اولین گام ایجاد یک نمونه‌ (instance) از WatchService با استفاده از کلاس java.nio.file.FileSystems می‌باشد.WatchService watchService = FileSystems.getDefault().newWatchService();سپس باید شی‌ای از جنس Path ساخت، که آدرس دایرکتوری مدنظر ما برای مانیتور کردن را در خود دارد:Path path = Paths.get(&amp;quot/home/mujan/myDirForMonitor&amp;quot);بعد از این گام، وقت آن است که این path را در watch service ثبت (register) کنیم. توی این مرحله، ۲ مفهوم مهم وجود دارد که باید درک کنید، یک کلاس StandardWatchEventKinds و کلاس WatchKey. قطعه کد زیر برای ثبت path را نگاه کنید تا ببینید که چطور از آن‌ها استفاده شده است، تا در ادامه در مورد آنها به تفصیل صحبت کنم:WatchKey watchKey = path.register(watchService, StandardWatchEventKinds...);خب دو موضوع مهم و قابل توجه این‌جا مطرح است: اول فراخوانیِ متدِ ثبت آدرس (path registration) که بعنوان اولین پارامتر نمونه‌ی (instance) کلاس watch service را میگیرد و سپس یک VarArgs از StandardWatchEventKindsها. موضوع مهم دوم retrun type فرایند ثبت است که یک نمونه (instance) از WatchKey می‌باشد. خب بریم همانطور که قول دادم این ۲ موضوع را به تفصیل باز کنیم.کلاس StandardWatchEventKinds، کلاسی است که نمونه‌های آن به watch service می‌گوید که چه نوع از رخدادها (events) را برای دایرکتوری مدنظر درنظر بگیرد (یعنی حواسش به چه اتفاق‌هایی باشد، یا به بعبارتی نسبت به چه اتفاق‌هایی واکنش نشان دهد). چهار رخداد برای نظاره کردن (watch) وجود دارد.رخداد StandardWatchEventKinds.ENTRY_CREATE: زمانی رخ می‌دهد که یک موجودیت جدید در دایرکتوری تحت نظر ایجاد شود. ممکنه مربوط به ساخت یک فایل جدید باشد یا تغییر نام یک فایل موجود.رخداد StandardWatchEventKinds.ENTRY_MODIFY: زمانی رخ میدهد که یک موجودیت موجود در دایرکتوری مدنظر تغییر کند. تمام انواع تغییرات یک فایل این رخداد را اجرا می‌کند. توی بعضی از پلتفرم‌ها حتا تغییر ویژگی‌های فایل هم این رخداد را اجرا می‌کند.رخداد StandardWatchEventKinds.ENTRY_DELETE: زمانی رخ می‌دهد که یک موجودیت در دایرکتوی تحت نظر پاک شود، تغییرنام دهد و یا جابه‌جا شود.رخداد StandardWatchEventKinds.OVERFLOW: برای رخدادهای گم‌شده و درنظرگرفته نشده اتفاق‌ می‌افتد. ما کاری به آن نداریم :)اما کلاس WatchKey. این کلاس وظیفه ی ثبت دایرکتوری با watch service  را دارد. نمونه یا شی آن رخدادهایی که اتفاق افتاده‌اند را هم نمایش می‌دهد. وقتی ی رخدادی پیش میاد watch service به ما متد callback به ما نمی‌دهد. ما فقط می‌تونیم به چند روش poll کنیم.استفاده از poll API:WatchKey watchKey = watchService.poll();این API بهمون میگه که چه رخدادی اتفاق افتاده است، و اگر هم چیزی رخ نداده باشد null برمی‌گرداند.WatchKey watchKey = watchService.poll(long timeout, TimeUnit units);این API هم همینطور، با این تفاوت که اگر رخدادی نباشد مدت زمانی را منتظر می‌ماند و درجا null برنمی‌گرداند. و در نهایت آخرین متد:WatchKey watchKey = watchService.take();این API متفاوت رفتار می‌کند: تا زمانی که رخدادی اتفاق بیافتد صبر می‌کند (بلاک می‌کند).نکته‌ی بسیار مهم در رابطه با شی WatchKey این است که چه با poll یا چه با take ریترن (return) شود، دیگر رخدادی را ثبت نمی‌کند مگر این که متد reset آن فراخوانی شود:watchKey.reset();به این معنا که هربار پس از poll شدن، شی watch key از صف سرویس watch خارج می‌شود. فراخوانی متد reset دوباره برای ثبت رخدادهای دیگر شی watch key را درون صف قرار می‌دهد.خب اجازه دهید یک مثال کامل با استفاده از این سرویس نشان دهیم تا تمام ابهامات احتمالی مرتفع شود. کد زیر را خوب نگاه کنید:import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

public class Main {
    public static void main(String[] args) throws IOException, InterruptedException {
            WatchService watchService = FileSystems.getDefault().newWatchService();
                Path path = Paths.get(&amp;quot/home/mujan/Desktop&amp;quot);
                path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
                WatchKey key;
                        while ((key = watchService.take()) != null) {
                            for (WatchEvent&lt;?&gt; event : key.pollEvents()) {
                                            System.out.println(&amp;quotEvent kind:&amp;quot + event.kind() + &amp;quot. File affected: &amp;quot + event.context() + &amp;quot.&amp;quot);
                                            }
                                                        key.reset();
                                                                }
                                                                
                                                                                watchService.close();
                                                                                    }
                                                                                    }حرف نهایی و چرا این قسمت، قسمت نهایی شد؟مجدد سلام عرض میکنم. علت آنکه سلامی دوباره دارم، فاصله زمانی نوشتن تمام مطالب بالا، با این پاراگراف نهایی است که تصور میکنم 3 سال باشد. در این مدت به دلایلی بنده از زبان جاوا و تکنولوژی جاوا فاصله گرفتم، و به سمت C# و .Net Core رفتم :دیصرفا برای اینکه شاااید این مطالب به درد کسی بخورد، آنها را منتشر کردم، ولی برای من ادامه آن بی معنا است!از این پس نیز مقاله جاوایی نخواهیم داشت. مگر بر علیه آن :دی</description>
                <category>محمد عباسی</category>
                <author>محمد عباسی</author>
                <pubDate>Thu, 03 Jun 2021 14:01:03 +0430</pubDate>
            </item>
                    <item>
                <title>فیصله دادن به مبحث Logging در جاوا (قسمت دوم)</title>
                <link>https://virgool.io/@mujan/%D9%81%DB%8C%D8%B5%D9%84%D9%87-%D8%AF%D8%A7%D8%AF%D9%86-%D8%A8%D9%87-%D9%85%D8%A8%D8%AD%D8%AB-logging-%D8%AF%D8%B1-%D8%AC%D8%A7%D9%88%D8%A7-%D9%82%D8%B3%D9%85%D8%AA-%D8%AF%D9%88%D9%85-b367sotnkjie</link>
                <description>... در قسمت اول در رابطه با انتخاب کتابخانه مناسب مفصلا صحبت کردیم و سعی کردیم کلاف سردرگم در بحث لاگ را تا حدودی باز کنیم. امیدوارم که به این مهم دست یافته باشم.در قسمت اول ما در نهایت به این نتیجه رسیدیم که از کتابخانه‌ی sla4j بعنوان facade library استفاده کنیم و از logback بعنوان کتابخانه‌ی اصلی. اگر منظور من رو متوجه نمیشید الان، مطمئن بشید که قسمت اول را مطالعه فرمودید. خب بریم آموزش کار با logback را  شروع کنیم!فهرستمقدمهمعماری Logbackبرپا کردنتنظیمات و یک مثال سادهسلسله‌مراتب و ارث‌بریپیام‌های پارامتریکتنظیماتضمائم و یا همان Appendersو اما Layoutsنتیجه‌گیریگام بعدیمقدمهفریمورک Logback یکی از پراستفاده‌ترین فریمورک‌های لاگ در کامیونیتی جاواست. یجورایی جایگزین فریمورک محبوب Log4j است. Logback پیاده‌سازی سریع‌ترین نسبت به Log4j دارد، گزینه‌های بیشتری برای تنظیمات محیا می‌کند و همچنین انعظاف‌پذیری بیشتری برای ذخیره سازی در فایل دارد.در این مقدمه من معماری Logback رو شرح میدم و نشان میدهم که چگونه از آن برای ساخت اپلیکیشن‌های بهتر استفاده کنید.معماری Logback۳ کلاس اصلی معماری Logback را تشکیل می‌دهد: Logger، Appender، و Layout.شی logger یک context برای پیام‌های لاگ می‌باشد. این همان کلاسی است که اپلیکیشن برای ایجاد پیام‌های لاگ ازش استفاده می‌کند.و appenderها. آنها پیام‌های لاگ را در مکان(های) نهایی قرار می‌دهد. یک logger میتواند چندین appender داشته باشد. بطور معمول ما پیام‌های لاگ را در فایل ذخیره می‌کنیم، اما logback امکانات بیشتری هم به ما می‌دهد (در قسمت اول اشاره شده است).و اما Layout. لیئوت پیام‌ها را برای خروجی آماده می‌کند. Logback این امکان را می‌دهد که شما کلاس‌های خاص خودتان را برای فرمت‌دهی به پیام‌ها استفاده کنید، همانطور که این امکان را می‌دهد که کلاس‌های موجود را تنظیم کنید.برپا کردنفریمورک Logback از slf4j بعنوان اینترفیس اصلی خودش استفاده می‌کنه. قبل از اینکه شروع کنیم به لاگ انداختن پیام‌ها، نیاز است که Logback و Slf4j را به فایل pom.xml اضافه کنیم.نیاز هست که بگم من از maven برای مدیریت وابستگی‌ها استفاده می‌کنم؟! فکر نمی‌کنم!&lt;dependency&gt;
    &lt;groupId&gt;ch.qos.logback&lt;/groupId&gt;
    &lt;artifactId&gt;logback-core&lt;/artifactId&gt;
    &lt;version&gt;1.2.3&lt;/version&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
    &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
    &lt;artifactId&gt;slf4j-api&lt;/artifactId&gt;
    &lt;version&gt;1.7.25&lt;/version&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
    &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
    &lt;artifactId&gt;slf4j-simple&lt;/artifactId&gt;
    &lt;version&gt;1.6.4&lt;/version&gt;
&lt;/dependency&gt;فریمورک Logback همچنین به logback-classic.jar در classpath نیاز داره در حالت runtime. به همین خاطر آن را هم به فایل pom.xml اضافه می‌کنیم بعنوان یک  dependency:&lt;dependency&gt;
    &lt;groupId&gt;ch.qos.logback&lt;/groupId&gt;
    &lt;artifactId&gt;logback-classic&lt;/artifactId&gt;
    &lt;version&gt;1.2.3&lt;/version&gt;
&lt;/dependency&gt;تنظیمات و یک مثال سادهخب بیاید با یک مثال سریع استفاده‌ی Logback تو یک اپلیکیشن را بررسی کنیم.اول از همه، ما به یک فایل تنظیمات نیاز داریم (configuration file). من یک فایل بنام logback.xml ایجاد می‌کنم و یجایی در classpath قرار می‌دهم:&lt;configuration&gt;
      &lt;appender name=&quot;STDOUT&quot; class=&quot;ch.qos.logback.core.ConsoleAppender&quot;&gt;
          &lt;encoder&gt;
          &lt;pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&lt;/pattern&gt;
          &lt;/encoder&gt;
     &lt;/appender&gt;
    &lt;root level=&quot;debug&quot;&gt;
                &lt;appender-ref ref=&quot;STDOUT&quot; /&gt;
    &lt;/root&gt;
&lt;/configuration&gt;و سپس یک کلاس Example ایجاد می‌کنیم و در متد main کد ساده‌ی زیر را قرار می‌دهیم:public class Example {
    private static final Logger logger  = LoggerFactory.getLogger(Example.class);
    
            public static void main(String[] args) {
                    logger.info(&quot;Example log from {}&quot;, Example.class.getSimpleName());
                        }
}خب وقتی اجرا می‌کنیم چه اتفاقی میافتد؟ من انتظار دارم چنین خطی را مشاهده کنم:09:08:41.774 [main] INFO me.mujan.Example - Example log from Exampleخروجی را مشاهده کنید:خب، همه چیز همانطور به نظر می‌رسد که انتظار می‌رفت!الان می‌فهمیم چرا Logback انقدر محبوب است، تو چند دقیقه برنامه رو بالا آوردیم. بریم یک نگاهی بندازیم به تنظیمات و کدی که نوشتیم تا کمی برامون روشن بشه چطور داره کار می‌کنه. ما یک appender بنام STDOUT ایجاد کردیم که به کلاس ConsoleAppender اشاره می‌کنه.با الگویی فرمت پیام‌های لاگ رو مشخص کردیم.تو کد هم کلاس Logger ایجاد کردیم و پیاممون رو به متد info() آن پاس دادیم.تا اینجای کار مفاهیم پایه‌ای رو فهمیدیم. حالا وقت این است که عمیق‌تر به موضوع بنگریم.برای ثبت پیام تو Logback، ما Logger رو مقداردهی اولیه (initialize) می‌کنیم:private static final Logger logger  = LoggerFactory.getLogger(Example.class);و بعد از آن استفاده می‌کنیم:logger.info(&quot;Example log from {}&quot;, Example.class.getSimpleName());اینی که الان ایجاد شده loggin context ماست. نمیدونم چطور باید ترجمه‌اش کنم. کلاس LoggerFactory برای ما ایجادش می‌کند و یک اسمی هم به Logger می‌دهد. متد getLogger یک نسخه‌ی overload دیگه هم داره که میتوانید به آن String پاس دهید.یک Logger چیزی بنام Level دارد که هم امکان تنظیم شدن در فایل تنظیمات را دارد هم می‌توانیم به کمک Logger.setLevel() آن را تنظیم کنیم. توجه داشته باشید که سطحی که در کد نوشته می‌شود روی فایل‌های تنظیمات بازنویسی (override) می‌شود. یعنی اولویت دارد.سطوحی که وجود دارد به ترتیب اولویت بدین شرح است: TRACE، DEBUG، INFO، WARN و ERROR که هر سطح متد متناظر خودش را دارد که ما برای لاگ کردن پیام در آن سطح از آن استفاده می‌کنیم.چنانچه Logger بطور شفاف سطح (level) را مشخص نکرده باشد، آن را از نزدیک‌ترین نیا (ancestor) به ارث می‌برد. root logger در حالت پیشفرض روی DEBUG ست شده است که با هم خواهیم دید چگونه می‌توان آن را بازنویسی کرد.سلسله‌مراتب و ارث‌بریخب، تمام حواستان را جمع کنید، میخواهم در مثال زیر چند مفهوم مهم را بررسی کنیم. یکی از آن مفاهیم سلسله‌مراتب logger هاست. چطور یک logger میتواند نیای یک logger دیگر باشد و طبعا چطور یک logger فرزند یک logger دیگر می‌شود. همچنین اشاره‌ای به مفهوم level ها خواهم کرد. این‌ها را در قالب مثال توضیح خواهم داد.به کد زیر نگاه کنید، من این تکه کد رو درون متد main نوشته‌ام:Logger parentLogger = (Logger) LoggerFactory.getLogger(&quot;me.mujan.mylogger&quot;);

parentLogger.setLevel(Level.INFO);

parentLogger.warn(&quot;This message is logged because WARN &gt; INFO.&quot;);
parentLogger.debug(&quot;This message is not logged because DEBUG &lt; INFO.&quot;);فکرمی‌کنید خروجی چه می‌باشد؟ کدام پیام(ها) در کنسول پرینت می‌شود؟ خروجی به شکل زیر است:10:47:03.532 [main] WARN me.mujan.mylogger - This message is logged because WARN &gt; INFO.متن پیام‌ها خودشان گویا هستند، ولی من توضیح بیشتری می‌دهم. پیام &quot;This message is not logged because DEBUG &lt; INFO.&quot; که با استفاده از متد debug() لاگ شده بود در کنسول چاپ نشده است. چرا؟ چون سطح logger با استفاده متد setLevel() روی INFO سِت شده است و از آنجایی اولویت INFO کمتر از DEBUG است، پیامی که با متد debug() می‌خواهد لاگ شود در اصل اجرا نمی‌شود. اگر من level را با Level.TRACE ست کنم چه رخ خواهد داد؟parentLogger.setLevel(Level.TRACE); قطعا هر دو پیام اجرا می‌شود چرا که اولویت TRACE از تمام سطوح بیشتر است.بیاید به تکه کد اول دقت کنیم:Logger parentLogger = (Logger) LoggerFactory.getLogger(&quot;me.mujan.mylogger&quot;);بعنوان نام logger من اسم &quot;me.mujan.mylogger&quot; به متد getLogger پاس داده‌ام. ما می‌توانیم به کمک همین نام‌گذاری در اصل یک سلسله‌مراتب درست کنیم. فرض کنیم می‌خواهیم یک context دیگر ایجاد کنیم، و می‌خواهیم این context از parentLogger تنظیمات را ارث ببرد. باید چکار کنم؟یک logger دیگر ایجاد می‌کنم و نام آبجکتش را childLogger می‌گذارم. ولی هنگام نام‌گذاری خود لاگر باید از الگوی خاصی پیروی کنم تا سلسله‌مراتب را بدرستی ایجاد کنم. برای اینکه childLogger تنظیمات را از parentLogger به ارث ببرد باید هنگام نام‌گذاری childLogger یک نقطه (dot) به انتهای &quot;me.mujan.mylogger&quot; اضافه کنم و در ادامه یک نام برای childLogger بگذارم. به این شکل می‌شود:Logger childLogger = (Logger) LoggerFactory.getLogger(&quot;me.mujan.mylogger.child&quot;);با این نام‌گذاری من ارث‌بری را ایجاد کرده‌ام. حالا childLogger تنظیمات را از parentLogger به ارث می‌برد. یعنی چی؟ یعنی از این پس هم childLogger  سطح‌اش (level) می‌شود INFO. اجازه دهید کد را کامل کنیم:Logger parentLogger = (Logger) LoggerFactory.getLogger(&quot;me.mujan.mylogger&quot;);

parentLogger.setLevel(Level.INFO); // دقت شود

Logger childLogger = (Logger) LoggerFactory.getLogger(&quot;me.mujan.mylogger.child&quot;);

parentLogger.warn(&quot;This message is logged because WARN &gt; INFO.&quot;);
parentLogger.debug(&quot;This message is not logged because DEBUG &lt; INFO.&quot;);

childLogger.info(&quot;INFO == INFO&quot;);
childLogger.debug(&quot;DEBUG &lt; INFO&quot;);خب به کد دقت کنید، الان انتظار داریم که چه چیزهایی در خروجی چاپ شود؟ دقت کردید که ما سطح را فقط برای parentLogger قرار دادیم. خروجی به شکل زیر است:11:26:10.971 [main] WARN me.mujan.mylogger - This message is logged because WARN &gt; INFO.
11:26:10.978 [main] INFO me.mujan.mylogger.child - INFO == INFOفکرمیکنم به میزان لازم توضیح داده‌ام. پیام‌های پارامتریکبرخلاف پیام‌های ساده‌ای که در بالا دیدیم، عمده‌ی پیام‌های کاربردی لاگ نیازمند چسباندن چندین رشته بهم است (جلوتر منظورم رو بهتر می‌فهمید). که خب این کار هم مستلزم اخذ حافظه، سریال کردن آبجکت، الحاق رشته‌ها‌ و احتمالا تمیزکاری های گاربیج کالکتر (garbage collector) است. پیام زیر را نگاه کنید:log.debug(&quot;Current count is &quot; + count);در این حالت ما خودمان باید متحمل هزینه‌های ساخت پیام شویم. اما Logback یک راه بهتر به ما معرفی می‌کند. پیام‌های پارامتریک. همان پیام قبلی را می‌توانیم به شکل زیر ارسال کنیم:log.debug(&quot;Current count is {}&quot;, count);نکته‌ی مهم کجاست؟ کروشه‌ها {} (بعضی ها بهش کِرلی‌براکت می‌گن و حدادعادلیش هم میشه کمانک) هر آبجکتی را قبول می‌کنند. بعبارتی شما وقتی {} می‌گذارید، میتوانید هر آبجکتی را به آن پاس دهید، تا بطور خودکار متد toString آن آبجکت فراخوانی شود و پیام را ایجاد کند. باید مثال‌های بیشتری ببینیم تا خوب جا بیافتد!Logger logger = (Logger) LoggerFactory.getLogger(&quot;me.mujan.mylogger&quot;);
String msg = &quot;@Mujan&quot;;
logger.info(&quot;My weblog address in Virgool is:{} &quot;, msg);خروجی کد بالا می‌شود:11:41:56.240 [main] INFO me.mujan.mylogger - My weblog address in Virgool is: @Mujan خب همانطور که ملاحظه می‌کنید می‌توان رشته را پارمتریک پاس داد. آیا فقط رشته؟ خیر هر آبجکتی را می‌توانید پاس دهید. برویم کمی عمیق‌تر شویم!تنظیماتتوی مثال‌های قبلی ما از یک فایل تنظیمات کوتاه (تقریبا ۱۱ خطی) استفاده کردیم. رفتار پیش‌فرض Logback به این شکل است که اگر نتواند فایل configuration  را پیدا کند خودش یک ConsoleAppender ایجاد می‌کند و وصل می‌کند به root logger.فایل configuration درون classpath قرار می‌گیره و نامش میتونه logback.xml یا logback-test.xml باشه. بطور کلی logback برای پیدا کردن فایل تنظیمات چنین رفتاری دارد: بدنبال فایل‌هایی با نام logback-text.xml، logback.groovy و یا logback.xml در classpath می‌گردد.اگر کتابخانه نتواند این فایل‌ها را پیدا کند، بکمک ServiceLoader جاوا سعی می‌کند یک پیاده‌سازی از com.qos.logback.classic.spi.Configurator را پیدا کند.خودش سعی می‌کند بطور مستقیم با کنسول خودش را تنظیم کند.نکته: نسخه‌ی فعلی Logback از تنظیمات Groovy پشتیبانی نمی‌کند چرا که Groovy با جاوا ۹ سازگار نیست.بریم نگاهی دقیق‌تر بخود تنظیمات بیاندازیم!برید و فایل تنظیماتی رو که قبلا نوشته بودیم رو مجددا مشاهده کنید، تمام این فایل درون تگ‌های &lt;configuration&gt; قرار دارد.همچنین ما تگی مشاهده می‌کنیم بنام Appender که از نوع ConsoleAppender است و نامش را هم STDOUT قرار دادیم. درون این تگ‌ هم تگی بنام encoder وجود دارد. تگ encoder الگویی مشاهده printf-style دارد.در آخر هم تگ root را داریم. این تگ root logger را روی حالت DEBUG تنظیم کرده است و با استفاده از تگ appender-ref آنرا به STDOUT ارجاع داده‌ایم.فایل تنظیمات logback میتونه خیلی پیچیده باشد، به همین خاطر چندین مکانیزم درونی برای مشکل‌یابی برایش ایجاد کرده‌اند. برای مشاهده اطلاعات debug در فرایند تنظیمات Logback، میتوانید امکان دیباگ رو برای تنظیمات روشن کنید! &lt;configuration debug=&quot;true&quot;&gt;
...
  &lt;/configuration&gt;توی عکس زیر هم تنظیمات و هم خروجی را مشاهده می‌کنید:مکانیزم دیگری که وجود داره StatusListener نام دارد که می‌تونید در این خصوص خودتان تحقیق کنید.امکان reload کردن تنظیمات در زمانیکه برنامه در حال اجرا است یکی از امکانات پرقدرت و بسیار خوب logback برای troubleshooting است. این امکان رو میتوانید با پارامتر scan برای logback محیا کنید.رفتار پیش‌فرض برای scan کردن فایل تنظیمات روی بازه‌های ۶۰ ثانیه‌ای میباشد که آن را هم می‌تواند با اضافه کردن scanPeriod  تغییر دهید. همچنین می‌توانید مقادیر را به میلی‌ثانیه، ثانیه، دقیقه و ساعت وارد کنید:&lt;configuration scan=&quot;true&quot; scanPeriod=&quot;15 seconds&quot;&gt;
...
&lt;/configuration&gt;توی مثال‌های ساده‌ی بالا ما سطح root logger رو تنظیم کردیم و سپس مرتبطش کردیم با Console appender. ولی در واقع می‌توانیم سطح تمام loggerهای خود را بطور مجزا تعریف کنیم. تماشا کنید:&lt;configuration&gt;
&lt;appender name=&quot;STDOUT&quot; class=&quot;ch.qos.logback.core.ConsoleAppender&quot;&gt;
    &lt;encoder&gt;
        &lt;pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&lt;/pattern
   &lt;/encoder&gt;
 &lt;/appender&gt;
    &lt;logger name=&quot;me.mujan.logback&quot; level=&quot;INFO&quot; /&gt; 
    &lt;logger name=&quot;me.mujan.logback.child&quot; level=&quot;WARN&quot; /&gt; 
    &lt;root level=&quot;debug&quot;&gt;
    &lt;appender-ref ref=&quot;STDOUT&quot; /&gt;
        &lt;/root&gt;
&lt;/configuration&gt;فایل‌های تنظیمات logback از متغیرها نیز پشتیبانی می‌کند. ما متغیرها را هم میتوانیم درون اسکریپت تنظیمات تعریف کنیم، هم بصورت خارجی. یک متغیر میتواند توی هرنقطه از اسکریپت تنظیمات تعریف شود.برای مثال، اینجا یک تنظیمات برای FileAppender داریم:&lt;property name=&quot;LOG_DIR&quot; value=&quot;/var/log/application&quot; /&gt;
&lt;appender name=&quot;FILE&quot; class=&quot;ch.qos.logback.core.FileAppender&quot;&gt;
    &lt;file&gt;${LOG_DIR}/tests.log&lt;/file&gt;
        &lt;append&gt;true&lt;/append&gt;
            &lt;encoder&gt;
                    &lt;pattern&gt;%-4relative [%thread] %-5level %logger{35} - %msg%n&lt;/pattern&gt;
            &lt;/encoder&gt;
&lt;/appender&gt;در بالای تنظیمات، یک متغیر با کمک تگ property بنام LOG_DIR ایجاد کردیم. سپس از آن بعنوان بخشی از آدرس تعریف appender استفاده کردیم.خصیصه‌ها (متغیرها) درون تگ property تغریف می‌شوند. اما آنها از منابع خارجی هم قابل دسترس هستند، نظیر system properties. میتوانیم تعریف property رو در این مثال حذف کنیم و مقدار LOG_DIR رو از طریق کامندلاین بدیم:$ java -DLOG_DIR=/var/log/application me.mujan.logback.LogbackTestsضمائم و یا همان Appendersدر اصل Loggerها LoggingEventsها رو به Appenderها ارسال می‌کند. این Appenderها هستند که کار اصلی لاگ انداختن را انجام می‌دهند. بطور معمول هم ما لاگ را چیزی درنظر می‌گیریم که در کنسول یا فایل ذخیره می‌شود ولی همانطور که در قسمت اول اشاره کردیم Logback امکانات بیشتری هم دارد. کتابخانه Logback-core چندین appender محیا می‌کند.تشریح ConsoleAppenderهمانطور که تا الان مشاهده کردید، ما از ConsoleAppender استفاده کردیم. در واقع ConsoleAppender پیام‌ها را به System.out و System.err ارسال نمی‌کند. در عوض از OutputStreamWriter استفاده می‌کند.تشریح FileAppenderکلاس FileAppender پیام‌ها در فایل می‌ریزد. این کلاس از رنج پارامترهای زیادی در تنظیمات استفاده می‌کند. بریم و کانفیگ پایه‌ای آن را ببینیم:&lt;configuration debug=&quot;true&quot;&gt;
    &lt;appender name=&quot;STDOUT&quot; class=&quot;ch.qos.logback.core.ConsoleAppender&quot;&gt;
            &lt;!-- encoders are assigned the type
                         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --&gt;
                                 &lt;encoder&gt;
                                             &lt;pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&lt;/pattern&gt;
                                                     &lt;/encoder&gt;
                                                         &lt;/appender&gt;
                                                             &lt;appender name=&quot;FILE&quot; class=&quot;ch.qos.logback.core.FileAppender&quot;&gt;
                                                                     &lt;file&gt;tests.log&lt;/file&gt;
                                                                             &lt;append&gt;true&lt;/append&gt;
                                                                                     &lt;encoder&gt;
                                                                                                 &lt;pattern&gt;%-4relative [%thread] %-5level %logger{35} - %msg%n&lt;/pattern&gt;
                                                                                                         &lt;/encoder&gt;
                                                                                                             &lt;/appender&gt;
                                                                                                                 &lt;logger name=&quot;me.mujan.logback&quot; level=&quot;INFO&quot; /&gt; 
                                                                                                                     &lt;logger name=&quot;me.mujan.logback.tests&quot; level=&quot;WARN&quot;&gt; 
                                                                                                                             &lt;appender-ref ref=&quot;FILE&quot; /&gt; 
                                                                                                                                 &lt;/logger&gt; 
                                                                                                                                     &lt;root level=&quot;debug&quot;&gt;
                                                                                                                                             &lt;appender-ref ref=&quot;STDOUT&quot; /&gt;
                                                                                                                                                 &lt;/root&gt;
                                                                                                                                              &lt;/configuration&gt;در اینجا FileAppender با تگ file تنظیم شده است. تگ append نیز مشخص کرده است که اگر فایل موجود بود، دیگر آن را ایجاد نکند، و به ادامه‌ی فایل اضافه کند، که اگر امتحانی هم چندین بار اجرا کنید میبینید که هر بار به ادامه‌ی فایل لاگ‌ها اضافه می‌شود.این رو هم دقت می‌کنید که مابقی loggerها از root logger ارث بری می‌کند. میتونیم این تنظیمات رو هم بازنویسی کنیم:&lt;logger name=&quot;me.mujan.logback.tests&quot; level=&quot;WARN&quot; additivity=&quot;false&quot; &gt; 
    &lt;appender-ref ref=&quot;FILE&quot; /&gt; 
&lt;/logger&gt; 
&lt;root level=&quot;debug&quot;&gt;
    &lt;appender-ref ref=&quot;STDOUT&quot; /&gt;
&lt;/root&gt;در اینجا additivity رو هم false قرار دادیم تا رفتار پیشفرض غیرفعال شود. الان tests نه در کنسول لاگ می‌کند و نه دیگر لاگری از نسل آن چنین می‌کند.طبق معمول مقالاتی که نگارش کرده‌ام، هرچه جلوتر می‌رویم توضیحات کمتری درمورد کانفیگ‌ها و کدها می‌دهم (برخلاف ابتدای کار)، چرا که اعتقاد دارم الان میتونید بدرستی فایل کانفیگ را بخوانید. فلذا از این پس توجه بیشتری را باید داشته باشید.بررسی RollingFileAppenderاغلب اوقات ما نمی‌خواهیم که پیام‌های لاگ به یک فایل مشخص اضافه شود (بعد از مدتی حجم فایل وحشتناک می‌شود)، به همین خاطر ما فایل‌ها یا براساس زمان، یا براساس حجم (یا بر اساس هر دو) ریست شوند یا رها شوند و فایل دیگری ایجاد شوند.&lt;property name=&quot;LOG_FILE&quot; value=&quot;LogFile&quot; /&gt;&lt;appender name=&quot;FILE&quot; class=&quot;ch.qos.logback.core.rolling.RollingFileAppender&quot;&gt;&lt;file&gt;${LOG_FILE}.log&lt;/file&gt;&lt;rollingPolicy class=&quot;ch.qos.logback.core.rolling.TimeBasedRollingPolicy&quot;&gt;    &lt;!-- daily rollover --&gt;    &lt;fileNamePattern&gt;${LOG_FILE}.%d{yyyy-MM-dd}.gz&lt;/fileNamePattern&gt;    &lt;!-- keep 30 days&#039; worth of history capped at 3GB total size --&gt;    &lt;maxHistory&gt;30&lt;/maxHistory&gt;    &lt;totalSizeCap&gt;3GB&lt;/totalSizeCap&gt;&lt;/rollingPolicy&gt;&lt;encoder&gt;    &lt;pattern&gt;%-4relative [%thread] %-5level %logger{35} - %msg%n&lt;/pattern&gt;&lt;/encoder&gt;&lt;/appender&gt; در RollingFileAppender یک RollingPolicy هست. که توی این مثال ما TimeBasedRollingPolicy را کانفیگ کرده‌ایم.مشابه FileAppender، ما appender را هم با یک نام فایل تنظیم کردیم، که البته در اینجا از متغیرها استفاده کردیم.ما همچنین fileNamePattern را درون RollingPolicy قرار دادیدم. این الگو اسم فایل‌ها را مشخص می‌کند. دقت کنید TimeBasedPolicy تنها گزینه‌ی ما برای rolling کردن فایل‌ها نیست. Logback گزینه‌های دیگری نظیر SizeAndTimeBasedRollingPolicy را دارد که براساس حجم فایل لاگ کنونی و زمان است و همچنین FixedWindowRollingPolicy که هربار logger اجرا می‌شود roll می‌کند. برای اطلاعات بیشتر در اینباره اینجا را مطالعه کنید.آخرین نکته‌ی مهم درباره‌ی RollingFileAppender اینکه، یک فشرده‌ساز داخلی هم دارد. وقتی ما نام فایل را LogFile.gz قرار دادیم، خودش شروع به فشرده سازی هم می‌کند.ایجاد Custom Appendersما همچنین می‌تونیم بر اساس کلاس‌های پایه‌ی Logback شروع کنیم و appenderهای خودمان را بنویسیم، چه کار جالب‌انگیزی!در این مورد خودتان جست‌وجو کنید.و اما Layoutsو خب رسیدیم به Layoutها. Layoutها پیام‌های لاگ را فرمت می‌دهند. مشابه اجزای دیگر Logback ما میتونیم layoutها را هم توسعه بدیم و نسخه‌ی بومی و خودمانی آن را ایجاد کنیم. ولی همان PatternLayout پیش‌فرض عمده‌ی نیازهای ما را پوشش می‌دهد.تا اینجای کار ما در تمام مثال‌هایی که دیدیم از PatternLayout استفاده شده بود:&lt;encoder&gt;
    &lt;pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&lt;/pattern&gt;
&lt;/encoder&gt;این اسکریپت تنظیمات شامل تنظیمات برای PatternLayoutEncoder است. ما این Encoder را به Appenderی که ایجاد کردیم پاس می‌دهیم و خود این Encoder از PatternLayout برای فرمت‌دهی پیام‌ها استفاده می‌کند.متنی که در تگ pattern قرار دارد چگونگی فرمت‌دهی پیام‌های لاگ را مشخص می‌کند. PatternLayout تنوع بالایی از کلمات مشخص را پیاده‌سازی کرده که به کمک آنها بتوانیم الگوهای لاگ را مشخص کنیم و تغییر دهیم.اجازه دهید کمی این قسمت را باز کنیم. PatternLayout کلمات از پیش‌تعریف‌شده را با استفاده از % تشخیص می‌دهد، بنابراین داریم:%d{HH:mm:ss.SSS} – a timestamp with hours, minutes, seconds and milliseconds[%thread] – the thread name generating the log message, surrounded by square brackets%-5level – the level of the logging event, padded to 5 characters%logger{36} – the name of the logger, truncated to 35 characters%msg%n – the log messages followed by the platform dependent line separator characterخودتان بخوانید، واضح و مبرهن است. چنین الگویی، چنین پیامی را تولید می‌کند (تو مثال‌ها هم دیدیم):21:32:10.311 [main] DEBUG me.mujan.logback.LogbackTests - Logging message: This is a Stringبه اطلاعات بیشتری در این خصوص نیاز دارید؟ بسیار عالی، اینجا را بخوانید.نتیجه‌گیریعجب مقاله‌ی سنگین و مفصلی در رابطه با Logback شد. عاشق نگارش مقالاتی هستم که نمونه‌ی فارسی‌ آنها موجود نیست، تا شاید بتوانم به مقدار ناچیزی در پیشبرد کار شما همکاران عزیزم، مفید واقع شوم.در این مقاله ما پایه‌ و اساس Logback در اپلیکیشن را پوشش دادیم. به سه جز کلی آن پرداختیم، یعنی loggerها، appenderها، و layout. دیدیم که Logback تنظیمات پرتوانی دارد و ما می‌توانیم با کمک آن تمام اجزای آن را customize کنیم. همچنین FileAppender و RollingFileAppender را بعنوان پراستفاده‌ترین ها به تشریح بحث کردیم و گفتیم چطور آن را مدیریت کنید یا لاگ فایل‌هاتون را فشرده کنید.گام بعدیبرنامه‌ی من اینجا با Logback خاتمه یافت، ولی اگر شرایط محیا شود، شاید استفاده از Logback در Spring Boot را هم بعنوان قسمت سوم اضافه خواهم کرد.در آخر بابت ایراد احتمالی نگارشی و فنی پیشاپیش عذرخواهی می‌کنم و خواهشمندم که گوشزد کنید. براتون آرزوی موفقیت دارم :)</description>
                <category>محمد عباسی</category>
                <author>محمد عباسی</author>
                <pubDate>Wed, 14 Aug 2019 16:52:47 +0430</pubDate>
            </item>
                    <item>
                <title>فیصله دادن به مبحث Logging در جاوا (قسمت اول)</title>
                <link>https://virgool.io/dayche-group-Blog/java-logging-tutorial-fchaqkhhzhsi</link>
                <description>با عرض سلام و ادب خدمت همکاران عزیز. خوشحالم که در اینجا هستید.احتمالا مجبور شدید که از فریمورک‌های لاگ انداختن استفاده کنید. توی یک محصول واقعی، لاگ جز لاینفک محصول است، و بعنوان برنامه‌نویس حتما باید از آن استفاده کنید. شاید اصلا برای رسیدن به این فریضه خودتان دست بکار شده باشید و چنین امکانی را ایجاد کرده باشید (بدون استفاده از فریمورک). شاید هم تازه گرایش پیدا کردید که از فریمورک لاگ انداختن استفاده کنید، و حسابی بین کتابخانه‌هایی که هستند سردرگم هستید! اگر به گزاره آخر بله گفتید، یعنی تازه می‌خواهید شروع کنید و بین انواع کتابخانه‌ها سردرگم هستید، این مقاله برای شماست! واقعا چه تفاوتی بین slf4j و log4j هست؟ logback چیه و کی از log4j2 استفاده کنیم؟ یا اصلا مگه خود JDK یک پکیج بنام java.util.logging ارائه نکرده؟ پس این بازی‌ها چیه؟! در این مقاله بحث لاگ را تمام خواهیم کرد و به این سردرگمی فیصله خواهیم داد.فهرستهدفتاریخچهکدام کتابخانهبعدش!هدفچرا باید از لاگ کردن استفاده کنیم؟ طبیعتا برای دیباگ کردن و پی‌بردن به وضعیت برنامه در هنگام اجرا و کشف مشکل و مانیتور کردن رفتار برنامه و کاربران. شاید دلایل دیگری هم داشته باشید، ولی من تقریبا برای محقق شدن چنین مسائلی به سراغ لاگ گرفتن از سیستم می‌روم. دلایل دیگری هم می‌تواند داشته باشد؟ صد البته! شما احتمالا هدفتون از این کار مشخص شده است که می‌خواهید این کار را انجام دهید، پس مسئله‌ی من در اینجا پرداختن به اهمیت لاگ نیست.مسئله اینجا است که شما می‌خواهید &quot;چیزی&quot; را لاگ کنید (حال به هر هدفی!). عکس را نگاه کنید. پیغامی را میخواهید به یک آبجکتی که وظیفه‌ی لاگ کردن دارد بفرستید و انتظار دارید آن آبجکت‌ هم برای شما اون پیغام را &quot;جایی&quot; ذخیره کند که بعدها شما بتوانید به آن مراجعه کنید. این کلیت فرایندی است که می‌خواهیم انجام دهیم. حالا باید به تمام اجزای آن بپردازیم و آن را باز کنیم.تاریخچههمانطور که در مقدمه اشاره شد، در JDK 1.4 یک پکیج built-in برای لاگ ارائه شد. اما واقعیت این است که این پکیج فریمورک مناسبی برای استفاده نبود، یا اجازه دهید بهتر بگویم، امکانات زیادی برای استفاده نداشت. به مرور زمان فریمورک‌های دیگری وارد گود شدند که به مراتب بهتر و بهتر بودند. این پکیج مشکلات پرفورمنسی داشت، زیاد قابل انعطاف نبود و همانطور که اشاره کردم مردم به امکانات بیشتری نیاز داشتند.این پکیج کلاسی بنام java.util.logger.Logger داشت که نقش همان آبجکت LOGGER در عکس را بازی می‌کند. در اصل این کلاس متدی بنام getLogger() دارد که بعنوان پارامتر اسم کلاس را می‌گیرد و initialize می‌شود. و خب حالا جزء بعدی، لاگ را چکار کند؟ نمایش بدهد یا در یک فایل بنویسد؟ پکیج logger اینجا مفهومی را مطرح می‌کند بنام handlers. بعبارتی در handler ما مشخص می‌کنیم که توی کنسول لاگ را نمایش دهد یا در فایل ذخیره کند. میتونید توی این لینک بیشتر درموردش بخونید. ما توی این مقاله کاری به آن نداریم.خب وقتی چنین چیزی رخ میده توی جامعه نرم‌افزار آزاد (یعنی برای یک هدف مهم، ابزار مناسبی نباشد) چه می‌شود؟ مشخص است! برنامه‌نویسان شروع می‌کنند به توسعه کتابخانه‌های بهتر. در همین حین کتابخانه‌ای بنام log4j توسعه داده شد که برای سال‌ها متمادی عالی بود و مردم به کرات از آن استفاده می‌کردند.در این کتابخانه هم مشابه همان پکیج JDK کلاسی بنام Logger هست که یک متد getLogger() دارد و دقیقا مشابه قبلی، به عنوان پارامتر اسم کلاس را می‌گیرد. در log4j بجای handlers ما appenders داریم. همان نقش را دارد کاملا. انتخاب‌های ما در کتابخانه log4j خیلی بیشتر است. در زیر آن‌ها را لیست می‌کنیم:Log4j Appenders:FileConsoleRolling file &#40;daily&#41;JDBCSMTPJMSهمانطور که مشاهده می‌کنید امکان انتقال به دیتابیس یا حتا ایمیل کردن (SMTP) هم موجود می‌باشد. جزء دیگه‌ای که از log4j می‌ماند بحث Formatting است. اینکه لاگ به چه شکل و شمایلی باشد. که بعدا در مورد اینکه لاگ به چه فرمت‌هایی می‌تواند باشد صحبت خواهیم کرد. علی‌الحساب بگذریم!خب، بنظر با log4j همه چیز عالی میاد، آیا قصه‌ی فریمورک‌های لاگ انداختن در اینجا به پایان می‌رسد؟ نه متاسفانه. اجازه دهید در مورد چالشی که پیش رو بود صحبت کنیم و بگوییم چطور آن را حل کردند.فرض کنید در کدهایتان از java.util.logging استفاده کردید، حالا می‌خواهید آن را با log4j جابجا کنید؟ چکار باید کنید؟ باید تمام کدها را تغییر دهید،‌ مصیبت بار است،‌ چرا که اصلا ماهیت لاگ هم این است که تقریبا در هر متدی وجود دارد. برای حل چنین چالشی آمدند و Facade Library تعریف کردند. ۲ کتابخانه بعنوان facade library عرضه شد:APACHE COMMONSslf4jکتابخانه sla4j به مراتب مشهورتر و پراستفاده‌تر است. اما منظور ما از کتابخانه‌های facade چیست؟ کتابخانه‌های facade فقط اینترفیس هستند و فاقد implementation هستند. بعبارتی استانداری هستند که کتابخانه‌هایی نظیر log4j آنها را پیاده‌سازی می‌کنند. چه مزیتی دارند؟ چالشی که مطرح شد را برطرف می‌کنند. بعبارتی ما در کدها طبق استاندارد sla4j کد می‌زنیم، و بواسطه‌ی log4j پیاده‌سازی می‌کنیم. به این شکل هر زمان بخواهیم log4j را با java.util.logger جابجا کنیم، بدون تغییر می‌توانیم این کار را انجام دهیم. چون شکل کد ما بر اساس sla4j می‌باشد.پس تفاوت رو تونستید قائل بشید؟ apache commons و sla4j کتابخانه‌هایی نیستند که در واقع عملیاتی انجام دهند. آنها صرفا اینترفیس هستند. ولی log4j و JDK logging کتابخانه‌هایی هستند که این استانداردها رو پیاده‌سازی کردند (بعبارتی عملیاتی هستند). خب، تمام شد قصه‌ی کتابخانه‌ها؟ نه!کتابخانه‌ی log4j دیگه توسعه داده نمی‌شود. الان کتاب‌خانه‌ی جدیدتری هست بنام log4j2 که از هر نظر عملکرد بهتری نسبت به log4j دارد. کتابخانه‌ی دیگری هم هست بنام logback. بطور کلی در این روزها ۲ کتابخانه‌ی مطرح برای لاگ وجود دارد که هر دوی آنها بر پایه‌ی sla4j api می‌باشند:log4j2logbackاما کدام؟!کدام کتابخانهاول از هر چیز، من برای کتابخانه facade از sla4j استفاده می‌کنم، خیلی محبوبیت و مقبولیت بیشتری دارد. و همه کتابخانه‌های مرسوم و محبوب از آن استفاده می‌کنند. ولی برای پیاده‌سازی چه؟پیشنهاد می‌کنم که قید استفاده از log4j را بزنید. چرا که هم توسعه‌ آن متوقف شده است و قدیمی است و هم گزینه‌های بهتر موجود است. بحث java.util.logging را هم که نمی‌کنم، با توجه به توضیحاتی که دادم این کتابخانه در انتخاب‌های من گزینه‌ی آخر است. انتخاب‌های من می‌ماند بین log4j2 و logback.کتابخانه‌ی log4j2 یک مقدار امکانات بیشتری نسبت به logback دارد، بعنوان مثال:Lazy loading messageAsync loggingاما کتابخانه‌ی logback از محبوبیت بیشتری برخوردار است و توسط همان تیمی توسعه داده می‌شود که sla4j را توسعه دادند. انتخاب من logback است. خاطرتون هست دیگه؟ شما بواسطه‌ی یک api واسط (یعنی همان sla4j) می‌توانید هر زمانی که خواستید براحتی logback را با log4j2 تعویض کنید.بعدش!و اما بعد؟ خب حال چطور استفاده کنیم؟ با من همراه باشید تا در قسمت دوم می‌خواهم کتابخانه‌ی logback را بشکافم و از آن در عمل استفاده کنم. در آن بین به مفاهیم زیادی از مبحث لاگ خواهم پرداخت.</description>
                <category>محمد عباسی</category>
                <author>محمد عباسی</author>
                <pubDate>Wed, 07 Aug 2019 15:38:30 +0430</pubDate>
            </item>
                    <item>
                <title>مروری اجمالی به آنچه که هایبرنیت می‌کند - قسمت چهارم</title>
                <link>https://virgool.io/dayche-group-Blog/hibernate-tutorial-part-4-gaov3z6wmvd9</link>
                <description>سلام.خوشحالم که به اینجا رسیدیم. اگر کار را پس از همین‌ پست به پایان ببرم نیز از خودم راضی خواهم بود. چرا که بخش اعظمی از مبحث نگاشت را پایان داده‌ام. دوستان زیادی تلاش کرده‌اند به فارسی hibernate را توضیح دهند، اما از دید من (تاکید می‌کنم برای من) مقاله‌ای نبوده است که یکپارچه و یک تیکه به تمام ماجرا بنگرد. به همین خاطر ارزش زیادی برای این سری از مقالات قائل هستم. چون من از ابتدای کار، به کمک چندین منبع و چکیده کردن آن‌ها و ساعت‌ها مطالعه در بحث هایبرنیت توانستم برای خودم مباحث را یکپارچه کنم.اولین باری که سراغ هایبرنیت رفتم مهرماه سال ۹۶ بود، متاسفانه با حجم عظیمی از ندانم‌ها طرف بودم، و سخت‌ترین بخش برای من بحث نگاشت‌ روابط بود (Relational Mapping). امروز دقیقا به همین بحث می‌پردازیم. مطمئن باشید که در کار به شدت به این بخش وابسته خواهید بود. و در هر مصاحبه کاری که قرار باشد مهارت شما نسبت به هایبرنیت سنجیده شود دست‌کم ۲ پرسش از این بخش خواهد بود.مسئله‌ی دیگری که شاید بد نباشد با شما درمیان بگذارم،‌ علت استفاده از متن به جای ویدیو است. ویدیو یک مزیت بزرگ دارد، شما دقیقا با چند و چون پیاده‌سازی آشنا می‌شوید. گرچه من تمام کدهایی را که برای پیاده‌سازی نیاز بوده را اینجا به اشتراک گذاشته‌ام، اما عمل کردن و فهمیدن آن تلاش مضاعفی می‌خواهد که باید به گردن بگیرید. مزیت دیگر ویدیو راحتی ایجاد آن است. نوشتن به مراتب سخت‌تر است و انرژی بیشتری می‌طلبد. ویدیو را راحت‌تر می‌توانستم تهیه کنم چرا که هم به ابزار تدوین، و هم ابزار با کیفیت ضبط دسترسی داشتم. اما چرا به سراغ مقاله‌ی متنی آمدم؟ قطعا دلیلم جست‌وجوی اینترنتی و دیده شدن نبوده است. این دست آموزش‌ها در یوتیوب با اقبال بهتری روبه‌رو می‌شود. من تلاش می‌کنم تا بتوانم دفتری را بعنوان رفرنس تهیه کنم، صد البته که رفرنس کامل نخواهد بود، منظور من این است که هر گاه مطلبی را فراموش کردید بتوانید به راحتی پیدا کنید، و به سراغش بیایید، نه این که مجبور باشید چندین ویدیو را بالا پایین بکنید تا ببینید دقیقا در کدام قسمت فلان مطلب ذکر شده بود!مباحثی مثل هایبرنیت مخاطب عام ندارد،‌ تقریبا فیدبک جدی‌ای که تا کنون دریافت کرده‌ام،‌ ولی درک می‌کنم که شاید شمایی که اکنون در حال مطالعه هستید در زمانی باشید که ماها از نگارش این مقاله گذشته باشد. پس ۲ نکته را با شما درمیان می‌گذارم. من تمام تلاشم را برای نگارش دقیق مقالات بکار می‌برم، سعی می‌کنم سمبل‌‌کاری نشود، پس نگران این موضوع نباشید. نکته‌ی دوم اینکه هایبرنیت در حال پیشرفت است، همین حالا که صحبت می‌کنیم نسخه‌ی ۶ آن در شرف انتشار است. شاید بعضی از APIهایی که از آن‌ها استفاده می‌کنیم، تغییر کند، اما دوست گرانقدر، بخش اعظمی از مباحثی که تاکنون گفته شده است کاملا تئوریک هستند، دقیقا مشابه این قسمت و ۲ قسمت قبلی و ابتدای قسمت اول. این‌ها تقریبا هیچ زمانی نقض نمی‌شوند، شاید به آنها اضافه شود، ولی احتمال کاسته شدن از آن کم است، این اتفاق هم بیافتد چیزی از دست نخواهید داد.لینک قسمت‌های مرتبطقسمت اولقسمت دومقسمت سوم&lt;br/&gt;فهرستنگاشت روابط - Relational Mappingدنیای برنامه‌نویسی شی‌گرا احاطه شده از کلاس‌ها و روابط بین آنها. این روابط ساختارهایی هستند که آبجکت‌ها را بنوعی دیگر از آبجکت‌ها مرتبط می‌کند (لینک می‌کند). نکته. من در اینجا (بخشی که مربوط به جاوا است) وقتی از روابط صحبت می‌کنم منظورم چیست؟ منظورم لغت Associations است. چون relation رو هم رابطه می‌گویم (در بخشی که مربوط به دیتابیس است)، الزامی دیدم که بگویم من هر دوی این‌ها را یک چیز می‌گویم.پرسش. گیجمان نکنید، اصلا association چه چیزی بود توی دنیای برنامه‌نویسی که می‌گویید آن احاطه شده از کلاس‌ها و associationهای آنها؟!پاسخ. خواهش می‌کنم سخت نگیرید، مطمئن هستم بلد هستید. association به روابط بین چندین آبجکت اشاره می‌کنه (refers to the relationship between multiple objects). بعبارت دیگر به چگونگی ارتباط آبجکت‌ها با هم و همچنین چگونگی استفاده از قابلیت‌های یکدیگر اشاره می‌کند(refers to how objects are related to each other and how they are using each other&#x27;s functionality). در دنیای برنامه‌نویسی شی‌گرا (نه آنطور که شما میگید فقط دنیای برنامه‌نویسی) ۲ نوع رابطه‌ی مهم دارید. یک Composition و یکی Aggregation. این‌ها چه هستند را سرچ کنید!پیش از هرچیز بگویم، هر ارتباط جهت (direction) دارد. می‌تواند یک طرفه (unidirectional) یا دو طرفه (bidirectional) باشد. ارتباط یک طرفه بطور مشخص با فلش جهت آن نشان داده می‌شود:Unidirectional Associationو ارتباط دو طرفه در UML با خط ممتد مشخص می‌شود:Bidirectional Associationحتا می‌توان تعداد آبجکت‌های اشاره شده در ارتباط را نیز مشخص کرد. بعبارتی در UML چندی ارتباط یا همان کاردینالیتی آن را مشخص کرد. به تصویر زیر دقت کنید، هر ارتباطی که بین کلاس‌ها برقرار شده است (که با فلش یا خط نشان داده‌شده است) شامل ۲ عدد نیز می‌شود. بعنوان مثال ارتباط بین Platform و Decive ID را مشاهده کنید. اول از همه با توجه به فلش متوجه می‌شویم که ارتباط یک‌طرفه است، و مالک ارتباط Device ID است. و همچنین به کمک * و 1 که در ارتباط نشان داده شده، متوجه می‌شویم که یک ارتباط چندبه‌یک است. در جاوا وقتی بیشتر از یک آبجکت را می‌خواهیم از انواع مجموعه‌ای Collections، List، Set و حتا Map که همه‌ی آنها در پکیج java.util هستند، استفاده می‌کنیم.هر ارتباط نیز یک مالکیت دارد (ownership). بعبارتی کسی صاحب (owner) رابطه است. در روابط یک طرفه مالک رابطه بطور ضمنی مشخص است (در عکس اول (Unidirectional Association) مشخص است که کلاس A مالک رابطه است)، اما در ارتباط‌های دوطرفه باید بطور واضح مالکیت رابطه را مشخص کنیم. روابط در دیتابیس‌های رابطه‌ای - Relationships in Relational Databasesدر دنیای رابطه‌ای مسائل متفاوت است چرا که همه چیز این‌جا رابطه (relation) است. که به آن‌ها جدول هم می‌گوییم. به این معنا که همه چیز اینجا با جدول مدل می‌شود. برای نگاشت یک association دیگر List، Set و یا Map نداریم، در عوض جدول داریم. فلذا برای نگاشت رابطه‌ی بین یک کلاس با کلاس دیگر باید از ارجاع جدولی (table reference) استفاده کنیم. که این ارجاع به ۲ روش مختلف مدل می‌شود:استفاده از کلید خارجی (join column)استفاده از جدول میانی (join table)اجازه بدید این ۲ روش را با تصویر به شما نمایش بدم. از کتاب Beginning Java EE این تصاویر را برمی‌دارم. فرض کنید موجودیتی بنام Customer داریم که با موجودیت Address یک ارتباط یک به یک دارد. حال با استفاده از متد Join Column به این شکل می‌شود:ارتباط یک به یک با استفاده از Join Columnهمانطور که ملاحظه می‌کنید در روش اول با استفاده از کلیدخارجی (Join Column) این ارتباط نمایش داده شده است. حال همین ارتباط یک به یک را می‌توان با استفاده از جدول میانی نمایش داد. توجه داشته باشید که ارتباط یک به یک را معمولا با استفاده از کلیدخارجی مدل می‌کنند، اما در اینجا که هدف ما بحث آموزش است روش جدول میانی (Join Table) را هم نمایش می‌دهیم:ارتباط یک به یک با استفاده از Join Tableهمانطور که خدمتتون عرض کردم، از جدول میانی عموما برای مدل کردن ارتباط‌های یک‌به‌چند و یا چند‌به‌چند استفاده می‌شود.خب، نگاهی به مطالب بالا بیاندازید، چه چیزهایی را گفتیم؟ گفتیم در جاوا ارتباط‌ها جهت دارند، یا به عبارتی مالکیت در آنها مطرح است. و نشان دادیم که چندی ارتباط را در جاوا چطور پیاده سازی می‌کنیم‌ (در صورت نیاز از انواع کالکشن‌ها استفاده می‌کنیم) و نشان دادیم که در دیتابیس چگونه است. گفتیم ارتباطات در دیتابیس به ۲ شکل (یکی با کلیدخارجی و دیگری با جدول میانی) مدل می‌شود. حالا وقت آن است که برویم سراغ JPA و امکاناتی که آن برای نگاشت آبجکت‌های جاوا به جداول دیتابیس فراهم کرده نگاهی بیاندازیم. البته مشخصا منظور ما نگاشت ارتباطات است. ارتباطات بعضا پیچیده، چرا که پیش از این انواع دیگر نگاشت (بغیر از وارثت) را بررسی کردیم.ارتباطات موجودیت - Entity Relationshipsهایبرنیت یا دقیق‌تر JPA روابط را خودش آنطور که می‌فهمد بصورت پیش‌فرض برای ما نگاشت می‌کند (بدون آنکه چیزی به آن بگوییم) اما احتمالا این روش همان چیزی نباشد که مناسب کار ما باشد. به همین خاطر امکاناتی برای سفارشی‌سازی (customize) کردن عملیات نگاشت فراهم کرده است.چندی ارتباطات ممکن بین ۲ موجودیت شامل یک‌به‌یک، یک‌به‌چند، چندبه‌یک و چندبه‌چند می‌شود. به همین ترتیب برای نگاشت آن انوتیشن‌های @OneToOne، @OneToMany، @ManyToOne و @ManyToMany را فراهم کرده است.تمام این ارتباطات می‌تواند در حالت یک‌طرفه یا دوطرفه برقرار شود. (خاطرتان هست که قسمت اول در مورد مالکیت و جهت ارتباط صحبت کردیم). فلذا تمام حالاتی را که می‌تواند رخ دهد را در عکس زیر آورده‌ام:تمام ترکیبات ممکن!من به تمام این حالات می‌پردازم، اما خواهید دید که ارائه‌ی چند مثال بقیه مباحث رو تکراری می‌کنه. پس بهتر است بگویم توضیحاتی در ادامه خواهم داد که تمام این حالات را پوشش بدهد.متوجه هستید که وارد چه مباحث جذابی شده‌ایم؟ در عین سادگی جذابیت زیادی دارند. همراه باشید!یک‌طرفه (Unidirectional) و دوطرفه (Bidirectional)!همانطور که تو بخش اول هم متذکر شدیم، جهت در بحث مدل‌کردن آبجکت امری طبیعی است. در یک ارتباط یک‌طرفه، آبجکت A به آبجکت B اشاره می‌کند، در یک ارتباط دوطرفه، هر دو آبجکت بهم اشاره می‌کنند. اجازه بدهید دیگر وارد مثال شویم تا بهتر درک کنیم.در یک ارتباط یک‌طرفه، موجودیت Customer یک فیلد از نوع Address دارد (در عکس زیرین نیز مشخص است). این ارتباط یک طرفه است، از یک سمت به سمت دیگر اشاره می‌شود. بعبارت دیگر Customer مالک این ارتباط است.خب، اجازه دهید در ادبیات دیتابیس هم این موضوع را بررسی کنیم. در ادبیات دیتابیس جدول CUSTOMER کلیدخارجی‌ای (Join Column) دارد که به ADDRESS اشاره می‌کند. هر زمان هم که شما مالک ارتباط هستید، می‌توانید ارتباط را تغییر دهید. یعنی چه؟ یعنی اگر بخواهیم اسم کلیدخارجی (که به ADDRESS اشاره می‌کند) را تغییر دهیم باید موجودیت Customer را تغییر دهیم (مالک رابطه).ارتباط یک طرفههمانطور که اشاره کردیم یک ارتباط می‌تواند دوطرفه نیز باشد. در همین مثال خودمان اگر یک فیلد از نوع Customer در موجودیت Address قرار دهیم، این ارتباط را به صورت دوطرفه درآورده‌ایم.ارتباط دوطرفه بین Customer و Addressپرسش. من آشناییِ خیلی کمی با UML دارم. در ارتباط یک‌طرفه وقتی گفتید یک فیلد از Address در کلاس Customer قرار دهیم، من در دیاگرام Customer فیلد آدرس را مشاهده نکردم. و به همین ترتیب در ارتباط دوطرفه هم که فیلدی از جنس Customer در Address قرار دادیم، دوباره چیزی مشاهده نکردم! در صورتیکه مابقی فیلدها نوشته شده است (بعنوان مثال در Customer فیلدهای firstName، lastName و...)پاسخ. در دیاگرام‌های کلاس UML وقتی فیلدی با رابطه مشخص شده باشد، دیگر نوشته نمی‌شود. بعنوان مثال در ارتباط یک‌طرفه وقتی فلشی جهت‌دار از Customer به Address رفته است بیانگر فیلدی از جنس Address در کلاس Customer است، به همین خاطر دیگر آن را نمی‌نویسند.در یک ارتباط دوطرفه، چه کسی مالک رابطه است؟ چه کسی اطلاعات ستون پیوندی (کلیدخارجی یا همان join column) و یا اطلاعات جدول پیوندی (جدول میانی یا همان join table) را در خودش دارد؟درحالیکه ارتباطات یک‌طرفه تنها سمت مالک دارد، ارتباطات دوطرفه هم سمت مالک دارد هم سمت مقابل (inverse side) که با المان mappedBy در انوتیشن‌های @OneToOne، @OneToMany و ManyToMany قابل مشخص کردن است. در اصل mappedBy مشخص می‌کند که کدام خصیصه مالک رابطه است و وجودش در ارتباط‌های دوطرفه الزامی است.کمی گیج کننده است! می‌دانم. اجازه دهید یک توضیح کوچک دیگر هم بدهم. فرض کنید دو entity بنام‌های Customer و Address داریم. بین این دو انتیتی،‌ یک ارتباط یک‌به‌یک برقرار است (هر مشتری تنها یک آدرس دارد، و هر آدرس تنها متعلق به یک مشتری است). مالک رابطه کدام انتیتی است؟ Customer!ارتباط یک‌به‌یکِ دوطرفهاجازه دهید خیلی خودمانی توضیح دهم که چه رخ داده است، با انوتیشن @OneToOne مشخص کردیم که یک ارتباط یک‌به‌یک داریم، این انوتیشن‌را در دوطرف استفاده کردیم، پس یک ارتباط دوطرفه داریم. در سمت Address از المان mappedBy استفاده کردیم و با این مشخص کردیم که Address سمت مقابلِ (inverse side) مالک قرار دارد و همچنین مشخص کردیم دقیقا به چه ارتباطی، مرتبط است. با @JoinColumn هم مشخص کردیم که یک کلید خارجی می‌خواهیم به Address و نام ستون‌اش را نیز مشخص کردیم. توجه داشته باشید، مقداری که در mappedBy قرار می‌گیرد باید با نام propertyای که در سمت مالک تعریف شده است یکی باشد (چون می‌خواهد مشخص کند که بین این دو ارتباطی برقرار است). حالا شفاف شد؟ بنظرم باید شده باشد، ولی چنانچه نشده است نگران نباشد،‌ تازه می‌خواهیم وارد آب شویم و تنی به آب بزنیم!ارتباطِ یک‌طرفه‌ی یک‌به‌یک (OneToOne Unidirectional)چنین ارتباطی به چندی ۱ از یک طرف به سمت دیگر است. بیایید همان مثال مشتری و آدرس را توسعه دهیم. همچنان می‌خواهیم هر مشتری تنها یک آدرس داشته باشد اما (بنا به هر دلیلی) نمی‌خواهیم آدرس چیزی از مشتری بداند. یعنی ما از مشتری ‌می‌خواهیم به آدرس برسیم، اما از آدرس به مشتری نه! عکس زیر گویای مطلب هست، نمونه‌هایش را هم در مثال‌های قبل مشاهده کردید.اینبار چون ارتباط یک طرفه است و مالک ارتباط هم Customer می‌باشد، اطلاعات مروبط به نگاشت را فقط در انتیتی Customer قرار می‌دهیم و نیازی نیست که انتیتی آدرس چیزی از آن بداند.@Entity
public class Customer {
        @Id @GeneratedValue
        private Long id;
        private String firstName;
        private String lastName;
        private String email;
        private String phoneNumber;
        @OneToOne (fetch = FetchType.LAZY)
        @JoinColumn(name = &quot;add_fk&quot;, nullable = false)
        private Address address;
       // Constructors, getters, setters
       }به گمانم کاملا واضح است، اما باز هم برای رفع هر سوتفهامی، کد مربوط به انتیتی Address را هم قرار می‌دهم تا ملاحظه کنید که هیچ چیز آن تغییر نکرده است:@Entitypublic class Address {
        @Id @GeneratedValue
        private Long id;
        private String street1;
        private String street2;
        private String city;
        private String state;
        private String zipcode;
        private String country;
        // Constructors, getters, setters
        }همانطور که ملاحظه می‌کنید آدرس هیچ اطلاعاتی مربوط به نگاشت را ندارد.پرسش. در انتیتی Customer المانی بنام fetch اضافه کردید و مقدار آن را برابر با LAZY قرار دادید، آن چیست؟پاسخ. بله، متاسفانه هنوز به سراغ fetch کردن نرفته‌ایم، به زودی آن را توضیح خواهیم داد. اما اگر بطور خلاصه بخواهم بگویم هدف من در اینجا این بوده است که اگر من اطلاعات مشتری را خواستم نیازی نیست اطلاعات مربوط به آدرس آن هم برای من بازیابی شود مگر اینکه صراحتا بگویم! فعلا به همین توضیح مختصر بسنده کنید.ارتباط یک‌طرفه‌ی یک‌به‌چند (ManyToMany Unidirectional)خب، حال فرض کنید که ۲ انتیتی بنام‌های Order و OrderLine داریم که یک ارتباط یک‌به‌چند یک‌طرفه بین آنها برقرار است. مالک ارتباط نیز Order می‌باشد. خب، تا همینجای کار باید حدس زده باشید که چطور باید نگاشت صورت بگیرد. اگر نه، عیبی ندارد، اجازه دهید بیشتر توضیح دهم.هر Order چندین OrderLine دارد. ارتباط از سمت Order به OrderLine است (یک طرفه). فلذا OrderLine هیچ اطلاعاتی در مورد نگاشت این ارتباط ندارد. پس ما فقط باید یک آبجکت از OrderLine درون انتیتی Order قرار دهیم، درست است؟ نه کاملا! ما باید لیستی از آبجکت‌های OrderLine را درون انتیتی Order قرار دهیم (چرا که هر Order چندین OrderLine دارد و بعبارتی ارتباط یک به چند است).ارتباط یک‌به‌چندخب، کد انتیتی‌های مربوطه به شکل زیر است:Order@Entity
public class Order {
        @Id @GeneratedValue
        private Long id;
        @Temporal(TemporalType.TIMESTAMP)
        private Date creationDate;
        private List&lt;OrderLine&gt; orderLines;
        // Constructors, getters, setters
}OrderLine@Entity
@Table(name = &quot;order_line&quot;)
        public class OrderLine {
        @Id @GeneratedValue
        private Long id;
        private String item;
        private Double unitPrice;
        private Integer quantity;
        // Constructors, getters, setters
 }خوب نگاه کنید! بنظر هیچ اطلاعاتی مربوط به نگاشت یک‌به‌چند در انتیتی Order نیست! ما گفتیم هیچ انوتیشنی‌(اطلاعاتی) نباید در انتیتی OrderLine قرار داد چرا که ارتباط یک‌طرفه است. اما اینجا انوتیشنی هم در Order قرار نداده‌ایم و فقط لیستی از آبجکت‌های OrderLine را تعریف کرده‌ایم. چرا؟هایبرنیت زمانیکه ببینید یک انتیتی لیستی از یک انتیتی دیگر را در خودش دارد، بصورت خودکار متوجه می‌شود که یک ارتباط یک‌به‌چند وجود دارد. و انگار که خودش انوتیشن @OneToMany را برای فیلد مربوطه قرار می‌دهد. اما سوال دیگر، چطور در دیتابیس نگاشت می‌کند؟ بصورت پیشفرض هایبرنیت ارتباط یک‌به‌چند را با جدول میانی (@JoinTable) ایجاد می‌کند. مدل جدول‌ها با استفاده از جدول میانیمیبینید؟ جدولی میانی بنام ORDER_ORDER_LINE ایجاد کرده است و ترکیب آیدی جدول ORDER و آیدی جدول ORDER_LINE را بعنوان کلید اصلی آن قرار داده است. اسامی چطور انتخاب شده است؟ منظورم اسامی جدول ایجاد شده با اسم ۲ ستون آن است. این اسامی بصورت پیش‌فرض گذاشته شده است. چنانچه بخواهید آن‌ها را تغییر دهید، دیگر باید خودتان صراحتا وارد عمل شوید و اجازه ندهید هایبرنیت تنظیمات پیش‌فرضش را اعمال کند. پس من انتیتی Order را به شکل زیر تغییر می‌دهم:@Entity
public class Order {
        @Id @GeneratedValue
        private Long id;
        @Temporal(TemporalType.TIMESTAMP)
        private Date creationDate;
        @OneToMany
        @JoinTable(name = &quot;jnd_ord_line&quot;,
                joinColumns = @JoinColumn(name = &quot;order_fk&quot;),
                inverseJoinColumns = @JoinColumn(name = &quot;order_line_fk&quot;) )
        private List&lt;OrderLine&gt; orderLines;
        // Constructors, getters, setters
        }
کد را خوب بنگرید! قسمتی را که بولد کردم را خوب نگاه کنید، در اینجا اسم جدول میانی و همچنین اسم ستون‌های آن را اضافه کرده‌ام. با این تنظیمات همان مدل قبلی ایجاد می‌شود ولی با اسامی جدید و دلخواه من.همه چیز شفاف است؟ امیدوارم باشد. ببینید دوستان پیش‌فرض برای مدل کردن ارتباط یک‌به‌چند استفاده از جدول میانی است، اما فرض کنید بنابه‌هردلیلی من تصمیم بگیرم که از کلیدخارجی (Join Column) استفاده کنم. چکار باید کرد؟ حتما میدا‌نید که به راحتی فقط باید بجای @JoinTable که جدول میانی ایجاد می‌کند از @JoinColumn استفاده کنم. نگاه کنید که چطور دوباره کد مربوط به انتیتی Order را ریفکتور می‌کنم:@Entity
public class Order {
        @Id @GeneratedValue
        private Long id;
        @Temporal(TemporalType.TIMESTAMP)
        private Date creationDate;
        @OneToMany(fetch = FetchType.EAGER)
        @JoinColumn(name = &quot;order_fk&quot;)
        private List&lt;OrderLine&gt; orderLines;
        // Constructors, getters, setters
  }به همین راحتی. حالا مدل من به چه شکل می‌شود در دیتابیس؟ عکس زیر را نگاه کنید:مدل جدول‌ها با استفاده از کلیدخارجیخب، یک مدل نگاشت دیگر باقی می‌ماند تا بحث مربوط به آن تمام شود. متاسفانه ویرایش‌گر ویرگول بسیار سنگین شده است و نوشتن در آن سخت شده است. بعضی از عکس‌هایی هم که آپلود می‌کنم بی‌خودی و بی‌دلیل می‌پَرَد! بهتر است مطلب جدیدی را شروع کنم.ممنونم که تا اینجا همراهی کردید، و امیدوارم که این مقالات به درد شما خورده باشد!</description>
                <category>محمد عباسی</category>
                <author>محمد عباسی</author>
                <pubDate>Wed, 29 May 2019 12:25:11 +0430</pubDate>
            </item>
                    <item>
                <title>کار با MongoDB در جاوا (اتصال +‌ CRUD) - قسمت اول</title>
                <link>https://virgool.io/dayche-group-Blog/mongodb-with-java-driver-d5jtaldyaerb</link>
                <description>کار با mongodb در جاواسلام و عرض ادب خدمت جاوا کاران عزیز.با کمترین مقدمه به سراغ نحوه‌ی اتصال به MongoDB از طریق درایور جاوا می‌روم. امیدوارم این پست مقدمه‌ای شود تا بتوانم یک‌سری از نوشته‌ها درمورد چگونگی کار با MongoDB و همچنین مباحث تئوری آن بنویسم. در حال حاضر Mongo تکنولوژی‌است که در مورد آن مطالعه می‌کنم و  به آن علاقه‌مند شدم.پیش از شروع مبحث، باید اشاره کنم که در این نوشته مفاهیم ابتدایی MongoDB را شرح نمی‌دهم. پیش‌فرض من بر این است که شما آن را نصب کرده‌اید، معانی کلماتی نظیر database، collection و document را در MongoDB می‌دانید. خیلی خوب است اگر با پوسته‌ی جاوااسکریپت آن کار هم کرده باشید.خب، شروع کنیم!پیش‌نیازهاوجود سرویس فعال MongoDB روی لوکال‌هاست با پورت پیش‌فرض ۲۷۰۱۷ (غیر از این هم بود مهم نیست، مهم وجود سرویس فعال است)درایور MongoDBنصب MongoDBنصب MongoDB به‌عهده‌ی خود شماست. بسته به نوع پلتفرمی که استفاده می‌کنید بسته‌ی موردنظر را از سایت خود Mongo دانلود کنید و نصب کنید. البته توجه داشته باشید که سایت Mongo برای ما ایرانیان عزیز بسته است و می‌بایست آی‌پی خودتان را با کمک ابزارهایی مثل VPN تغییر دهید. پس از نصب از فعال بودن سرویس آن مطمئن شوید.در ابونتو با دستور mongod می‌توانید پوسته‌ی جاوااسکریپت آن را اجرا کنید.نصب درایور MongoDB برای اتصال به آن از طریق جاواراه توصیه شده برای اضافه کردن پکیج درایور به پروژه استفاده از سیستم‌های مدیریت وابستگی نظیر Maven و Gradle است. نکته‌ی حائز اهمیت در اینجا وجود ۲ آرتیفکت متفاوت است. کدام را باید استفاده کرد؟یکی از آرتیفکت‌ها mongodb-driver است و دیگری mongo-java-driver. آرتیفکت mongodb-driver جدیدتر است و ما از آن استفاده می‌کنیم (یک اینترفیس بیشتر دارد!).&lt;dependencies&gt;  
   &lt;dependency&gt;   
         &lt;groupId&gt;org.mongodb&lt;/groupId&gt; 
         &lt;artifactId&gt;mongodb-driver&lt;/artifactId&gt;         
         &lt;version&gt;3.4.3&lt;/version&gt;
     &lt;/dependency&gt; 
&lt;/dependencies&gt; بلاک بالا وابستگی مربوط به درایور mongo را به پروژه maven اضافه می‌کند.ایجاد کانکشناز MongoClient() برای ایجاد کانکشن از یک instance فعال مونگو استفاده‌ می‌کنیم. شی MongoClinet نماینده‌ی استخری از کانکشن‌ها به دیتابیس است. به عبارت دیگر فقط به یک شی از کلاس MongoClient نیاز دارید، حتا اگر در چندین ترد مختلف می‌خواهید استفاده کنید.اتصال به یک Single MongoDB instanceخب من در اینجا چندین راه مختلف برای اتصال به دیتابیس &quot;virgool&quot; روی ماشین لوکال را نشان می‌دهم. توجه داشته باشید در همه‌ی این روش‌ها اگر دیتابیس وجود نداشته باشد، MongoDB آن را ایجاد خواهد کرد.روش ۱. مقداردهی شی MongoClient بدون هیچ پارامتری برای اتصال به اینستنس MongoDB که بصورت پیشفرض روی localhost و پورت ۲۷۰۱۷ قرار گرفته است:MongoClient mongoClient = new MongoClient();روش ۲. می‌توان hostname را مشخص کرد. بطور مثال زمانی که اینستنس MongoDB روی ماشین محلی قرار ندارد. اما همچنان پورت بصورت پیش‌فرض ۲۷۰۱۷ می‌باشد:MongoClient mongoClient = new MongoClient( &quot;localhost&quot; );روش ۳. مشخص کردن hostname و همچنین port بصورت شفاف:MongoClient mongoClient = new MongoClient( &quot;localhost&quot; , 27017 );روش ۴. شما همچنین می‌توانید از MongoClinetURI برای ایجاد connection string استفاده کنید: MongoClientURI connectionString = new MongoClientURI(&quot;mongodb://localhost:27017&quot;);
 MongoClient mongoClient = new MongoClient(connectionString);اجازه دهید کمی این بخش را بیشتر باز کنیم.شمای استاندارد برای کانکشن‌استرینگ به فرم زیر است:mongodb://[username:password@]host1[:port1][,...hostN[:portN]]][/[database][?options]]که در آن mongodb:// مشخص می‌کند که این رشته از نوع استاندارد اتصال به دیتابیس است.و username:password@ بیانگر نام‌کاربری و رمزعبور است (اگر نیاز بود) و توجه داشته باشید چنانچه در نام‌کاربری و رمزعبور کارکترهایی نظیر @، : و امثالهم دارید، باید‌ آن‌ها را به کمک  percent encoding بنویسید. سپس host[:port] را مشخص می‌‌کنید که در مثال ما localhost (یا همان 127.0.0.1) و پورت 27017 می‌باشد. و در نهایت دیتابیس را باید مشخص کرد. مشخص کردن دیتابیس اختیاری است، چنانچه نام‌کاربری‌ای که با آن لاگین می‌کنید مختص به یک دیتابیس خاص است، می‌بایست حتما آن دیتابیس را مشخص کرد. و اما بخش options! می‌توان خصوصیاتی را به کانکشن نسبت داد، چه خصوصیاتی؟  Connection String Options را ببینید!یک مثال از کانکشن‌استرینگ:mongodb://myDBReader:D1fficultP%40ssw0rd@mongodb0.example.com:27017/adminاتصال به دیتابیسهنگامی که شی MongoClinet به دیتابیس MongoDB متصل شد، می‌توان به کمک متد MongoClinet.getDatabase() به یک دیتابیس مشخص متصل شد. باید نام آن دیتابیس را در متد getDatabase مشخص کرد، چنانچه وجود هم نداشته باشد خود Mongo آن را ایجاد خواهد کرد (در زمان ذخیره اولین دیتا).مثال زیر نحوه اتصال به دیتابیس &quot;virgool&quot; را نشان ‌می‌دهد:MongoDatabase database = mongoClient.getDatabase(&quot;virgool&quot;);نکته: شی MongoDatabase بصورت immutable است!دسترسی به کالکشنخب، حالا که شی‌ای از MongoDatabase داریم، برای دسترسی به یک کالکشن خاص از متد getCollection() استفاده می‌کنیم. باید نام آن کالکشن را در متد مشخص کرد، اگر وجود نداشته باشد نیز Mongo آن را ایجاد خواهد کرد (در زمان ذخیره اولین دیتا).برای مثال، من می‌خواهم به کمک شی database به کالکشن posts در دیتابیس virgool دسترسی داشته باشم:MongoCollection&lt;Document&gt; collection = database.getCollection(&quot;posts&quot;);شی MongoCollection نیز immutable است.ایجاد یک Documentبرای ایجاد یک داکیومنت از کلاس Document استفاده می‌کنیم،‌ برای مثال JSON زیر را در نظر بگیرید:{    
    &quot;name&quot; : &quot;MongoDB&quot;,   
    &quot;type&quot; : &quot;database&quot;,    
    &quot;count&quot; : 1,    
    &quot;versions&quot;: [ &quot;v3.2&quot;, &quot;v3.0&quot;, &quot;v2.6&quot; ],    
    &quot;info&quot; : 
        { 
            x : 203, 
            y : 102 
        }   
}برای ایجاد یک داکیومنت با استفاده از Java driver باید یک شی از کلاس Doucument را با field و value مقداردهی کرد. همچنین می‌توان به کمک متد append() فیلدها و مقادیر بیشتری را به یک شی داکیومنت نسبت داد. حتا مقدار (value) می‌تواند یک شی Document دیگر باشد (برای اسنادی تو در تو). به مثال زیر توجه کنید که چگونه سند JSON بالا را تبدیل به یک شی از کلاس Document می‌کنیم:Document doc = new Document(&quot;name&quot;, &quot;MongoDB&quot;)                 
                .append(&quot;type&quot;, &quot;database&quot;)                 
                .append(&quot;count&quot;, 1)                 
                .append(&quot;versions&quot;, Arrays.asList(&quot;v3.2&quot;, &quot;v3.0&quot;, &quot;v2.6&quot;))                 
                .append(&quot;info&quot;, new Document(&quot;x&quot;, 203).append(&quot;y&quot;, 102)
                );درج یک داکیومنتحال که یک شی از کلاس MongoCollection داریم، می‌توانیم به کمک آن داکیومنت‌هایی که ایجاد کردیم را در یک کالکشن مشخص insert کنیم. برای این‌کار از متد insertOne() استفاده خواهیم کرد. توجه کنید:collection.insertOne(doc);نکته: چنانچه فیلد _id را در سند مشخص نکرده باشیم، MongoDB بصورت خودکار آن را در سند ما قرار می‌دهد!حال چگونه لیستی از داکیومنت‌ها را درج کنیم؟ ابتدا بیاید آن را ایجاد کنیم سپس با کمک متد insertMany() از کلاس MongoCollection آن‌ها را درج کنیم.برای ایجاد لیستی داکیومنت‌ها یک حلقه‌ی for ایجاد می‌کنیم تا ۱۰۰ شی امتحانی ایجاد کنیم:List&lt;Document&gt; documents = new ArrayList&lt;Document&gt;(); 
        for (int i = 0; i &lt; 100; i++) {     
                documents.add(new Document(&quot;i&quot;, i)); 
}سپس با کمک متد insertMany() شی documents را وارد کالکشن مربوطه می‌کنیم:collection.insertMany(documents);شمارش تعداد اسناد در یک کالکشنبرای شمارش تعداد اسناد در یک کالکشن از متد count استفاده می‌کنیم. انتظار داریم خط زیرین عدد ۱۰۱ را چاپ کند (۱۰۰ شی را با متد insertMany و یک شی را با insertOne درج کردیم):System.out.println(collection.count());بازیابی اسنادبرای کوئری زدن به یک کالکشن، می‌توانیم از متد find() استفاده کنیم. اگر بدون هیچ آرگومانی فراخوانی شود، تمام اسناد یک کالکشن را درخواست می‌کند و اگر آرگومانی به آن بدهیم می‌توانیم اسناد را بر اساس آن فیلتر کنیم. اگر گیج کننده شده است عذرخواهی می‌کنم، به مثال‌ها توجه کنید، گویای مطلب است.متد find() یک شی از نوع FindIterable ایجاد می‌کند که می‌توان متدهای دیگری را بصورت آبشاری (زنجیری) در آن فراخوانی کرد.پیدا کردن یک داکیومنت در کالکشنبرای بازیابی اولین سند در کالکشن از متد find بدون هیچ آرگومانی استفاده می‌کنیم که بعد از آن بصورت آبشاری متد first() را فراخوانی می‌کنیم.نکته: اگر کالکشن خالی باشد، null برخواهد گشت!Document myDoc = collection.find().first();
System.out.println(myDoc.toJson());بازیابی تمامی داکیومنت‌های یک کالکشنبرای بازیابی تمام داکیومنت‌های یک کالکشن‌ هم از متد find() بدون هیچ پارامتری استفاده می‌کنیم. بمنظور اینکه بتوانیم نتیجه را پیمایش کنیم باید متد iterator() را به متد find() زنجیر کنیم.مثال زیر تمام داکیومنت‌های یک کالکشن را برمی‌گرداند و آن‌ها را در کنسول چاپ می‌کند (۱۰۱ داکیومنت):MongoCursor&lt;Document&gt; cursor = collection.find().iterator(); 
    try {     
        while (cursor.hasNext()) {         
            System.out.println(cursor.next().toJson());     
        } 
    } finally {     
        cursor.close(); 
    }فیلتر کردن خروجیبرای درخواست داکیومنت‌هایی که با شروط مشخصی هماهنگ هستند (بعبارتی match هستند)، آبجکت فیلتر را به متد find() پاس می‌دهیم. برای راحتی ایجاد آبجکت‌های فیلتر، Java driver کلاس کمکمی‌ای بنام Filters ایجاد کرده است.برای مثال، برای یافتن اولین سندی که فیلد i آن مقدار 71 را داشته باشد، فیلتر &quot;eq&quot; را برای مشخص کردن شرط برابری به متد find() پاس می‌دهیم:myDoc = collection.find(eq(&quot;i&quot;, 71)).first();
System.out.println(myDoc.toJson());مثال زیر، تمام داکیومنت‌هایی که مقدار i آنها بزرگتر از ۵۰ هست (&quot;i&quot; &gt; 50) را برمی‌گرداند:Block&lt;Document&gt; printBlock = new Block&lt;Document&gt;() {
     @Override
     public voidapply(final Document document) {
         System.out.println(document.toJson());
     }
};

collection.find(gt(&quot;i&quot;, 50)).forEach(printBlock);برای مشخص کردن یک بازه نظیر i بزرگتر از ۵۰ و کوچک‌تر و مساوی ۱۰۰ باشد (50 &lt; i &lt;= 100) می‌توانید از کمکی and استفاده کرد:collection.find(and(gt(&quot;i&quot;, 50), lte(&quot;i&quot;, 100))).forEach(printBlock);آپدیت کردن داکیومنتبرای بروز کردن (آپدیت) اسنادِ کالکشن از متدهای updateOne و updateMany استفاده می‌کنیم.چیزهایی که به متدها ارسال می‌کنیم:(اول) آبجکت فیلتر، داکیومنت یا داکیومنت‌ها را برای آپدیت مشخص می‌کند. برای راحتی ایجاد آبجکت فیلتر هم Java driver یک کلاس هلپر (کمکی) بنام Filters ایجاد کرده است. برای مشخص کردن یک فیلتر خالی نیز (بعبارتی تمام داکیومنت‌های یک کالکشن را می‌خواهید)، از آبجکت خالی Document استفاده کنید.(دوم) سند آپدیتی که تغییرات را مشخص می‌کند، برای اینکه لیست عملیات‌های ممکن رو ببینید رو این کلیک کنید. این لیست را من در زیر هم خلاصه میاورم:$currentDateSets the value of a field to current date, either as a Date or a Timestamp.$incIncrements the value of the field by the specified amount.$minOnly updates the field if the specified value is less than the existing field value.$maxOnly updates the field if the specified value is greater than the existing field value.$mulMultiplies the value of the field by the specified amount.$renameRenames a field.$setSets the value of a field in a document.$setOnInsertSets the value of a field if an update results in an insert of a document. Has no effect on update operations that modify existing documents.$unsetRemoves the specified field from a document.متدهای update یک UpdateResult برمی‌گردانند که شامل اطلاعاتی درمورد تعداد داکیومنت‌های تغییر یافته است. چنانچه می‌خواهید تنها یک داکیومنت را تغییر دهید از updateOne استفاده کنید.مثال زیر، اولین داکیومنتی که شرط فیلتر را ارضا کند‌ (i مساوی با ۱۰) تغییر می‌دهد (مقدار i را برابر با ۱۱۰ قرار می‌دهد): collection.updateOne(eq(&quot;i&quot;, 10), new Document(&quot;$set&quot;, new Document(&quot;i&quot;, 110)));اما برای آپدیت کردن تمام اسنادی که با فیلتر همخوانی دارند (match) از متد updateMany استفاده خواهیم کرد.در مثال زیر مقادیر i تمام اسنادی که مقدار i آنها کمتر از ۱۰۰ می‌باشد را ۱۰۰تا افزایش می‌دهیم:UpdateResult updateResult = collection.updateMany(lt(&quot;i&quot;, 100), inc(&quot;i&quot;, 100));
System.out.println(updateResult.getModifiedCount());حذف کردن داکیومنتبرای حذف کردن اسناد از یک کالکشن می‌توانید از متدهای deleteOne و deleteMany استفاده کنید.مشابه آپدیت، برای این متدها ابتدا باید آبجکت فیلتر را پاس داد تا مشخص شود کدام سند یا سندها باید حذف شوند. که می‌توان از کلاس کمکی درایور جاوا (Filters) کمک گرفت. مشابه آپدیت، برای حذف تمام داکیومنت‌ها یک آبجکت خالی Document ارسال کنید تا بعنوان بدون فیلتر درنظر گرفته شود.متدهای حذف هم یک DeleteResult برمی‌گردانند که شامل اطلاعاتی در مورد تعداد اسناد حذف شده است.برای حذف اولین سندی که مطابق فیلتر است از متد deleteOne استفاده می‌کنیم. مثال زیر سندی را که مقدار i آن برابر با ۱۱۰ می‌باشد را حذف می‌کند:collection.deleteOne(eq(&quot;i&quot;, 110));برای حذف چندین سند هم از deleteMany استفاده می‌کنیم. مثال زیر تمام اسنادی را که مقدار آن‌ها بزرگ‌تر و مساوی ۱۰۰ می‌باشد را حذف می‌کند:DeleteResult deleteResult = collection.deleteMany(gte(&quot;i&quot;, 100));
System.out.println(deleteResult.getDeletedCount());
ساخت ایندکسبرای ایجاد ایندکس روی یک فیلد یا چندین فیلد، سند مشخص ایندکس را باید به متد createIndex() پاس داد. سند مشخص ایندکس شامل فیلدهایی که باید ایندکس شوند و همچنین نوع ایندکس هر فیلد می‌باشد، فرمت زیر را نگاه کنید:new Document(&lt;field1&gt;, &lt;type1&gt;).append(&lt;field2&gt;, &lt;type2&gt;) ...برای نوع ایندکس افزایشی، مقدار 1 را برای &lt;type&gt; قرار دهید.برای نوع ایندکس کاهشی، مقدار -1 را برای &lt;type&gt; قرار دهید.بعنوان مثال یک ایندکس افزایشی برای فیلد i ایجاد می‌کنیم:collection.createIndex(new Document(&quot;i&quot;, 1));برای دیدن تمام انواع ایندکس‌ها صفحه ایجاد ایندکس‌ها را مشاهده کنید.خب، به پایان رسید این قصه، اما حکایت همچنان باقیست. بحث مربوط به درایور جاوا برای MongoDB ادامه دارد. می‌توان خیلی بیشتر آن را بسط داد، شما بگویید، قسمت دوم آن را اضافه کنم؟</description>
                <category>محمد عباسی</category>
                <author>محمد عباسی</author>
                <pubDate>Sat, 25 May 2019 14:20:04 +0430</pubDate>
            </item>
                    <item>
                <title>مروری اجمالی به آنچه که هایبرنیت می‌کند - قسمت سوم</title>
                <link>https://virgool.io/dayche-group-Blog/hibernate-tutorial-part-3-lu8lttggmewa</link>
                <description>سلام!به قسمت سوم مقاله‌ی &quot;مروری اجمالی بر هایبرنیت&quot; خوش‌آمدید. در این مقاله بدون مقدمه به ادامه‌ی مبحث قبل برخواهیم گشت. همراه باشید!خب، در مقاله‌ی قسمت دوم توضیح Annotationهای پایه‌ای را شروع کرده بودیم. ادامه می‌دهیم. البته ذکر یک نکته بنظرم با اهمیت است. هرچه جلوتر برویم، توضیح من در مورد انوتیشن‌های مشخص مختصرتر خواهد شد. انوتیشن‌های مشخص چه انوتیشنی است؟ آنهایی که وظیفه‌اشان مشخص است، پیاده‌سازی آنها توسط خود شما براحتی صورت می‌پذیرد، فلذا لزومی نمی‌بینم که بابت هرکدام از آنها یک پیاده‌سازی ارائه دهم.پرسش. منظور شما از انوتیشن‌های پایه‌ای چیست؟پاسخ. این فقط یک اصطلاح من است، دقت شما ستودنی است. من بطور قراردادی با خودم، انوتیشن‌هایی که مربوط به نگاشت Entity می‌شود را پایه‌ای نامیدم. چیزهایی که حتما بکار می‌آیند.لینک قسمت‌های مرتبطقسمت اولقسمت دومفهرستتوضیح Annotationهای پایه‌ای بخش ۲عملیات CRUD در هایبرنیتتوضیح Annotationهای پایه‌ای بخش ۲خب، پس از توضیح بخشی از انونیشن‌ها، حال نوبت به ادامه‌ی روند است.@Basicاین انوتیشن خصیصه‌ای بسیار مهم دارد. این انوتیشن خصیصه‌ای بنام fetch دارد که مشخص می‌کند که فیلد مدنظر به چه شکل باید بازیابی شود. این خصیصه (fetch) دو مقدار مشخص می‌تواند بگیرد. یک مقدار Lazy و دیگری Eager. بسته به نوع استفاده می‌توانید مشخص کنید که از کدام fetch type باید استفاده شود. به مثال زیر توجه کنید:@Basic(fetch = FetchType.LAZY)
@Lob
private byte[] wav;انوتیشن @Lob چیست؟! انوتیشن @Lob دقیقا برابر با نوع داده blob در دیتابیس است. وقتی فایل‌های حجیم باینری در دیتابیس داشته باشیم، از این نوع داده استفاده می‌کنیم. در اینجا هم مشخص شده است که فیلد wav از نوع داده‌ی باینری است (blob).نکته‌ی مهم این است که اگر خصیصه‌ی fetch صراحتا مشخص نشود به صورت پیش‌فرض روی Eager خواهد بود.پرسش. کاملا گیج شده‌ام! نه چیزی در مورد fetch type فهمیدم! نه چیزی در مورد @Lob!اصلا LAZY و EAGER چه فرقی باهم دارند؟ یا blob چیست؟پاسخ. حق دارید، متوجه بودم که احتمالا چنین حسی پیدا می‌کنید. اجازه دهید به ۲ سوال شما جداگانه پاسخ دهم.در رابطه با fetch type باید بگویم که این مبحثی جداگانه است و در برنامه‌ی پیش رو داریم. مهم برای من در اینجا این بود که بگویم در انوتیشن Basic می‌توانید نوع fetch type را مشخص کنید. اما اینکه دقیقا fetch type چیست و اینکه Lazy چیست یا Eager چیست، مدنظرم نبوده است. در این مورد در بزودی صحبت خواهیم کرد. کمی صبر کنید.اما در رابطه با نوع داده‌ی blob. این را اگر نمی‌دانید گوگل کنید، خارج از بحث ماست، و کاملا مربوط به دیتابیس است. ولی بطور کلی ما وقتی می‌خواهیم داده‌های باینری را ذخیره کنیم از این نوع داده استفاده می‌کنیم‌ (مثل عکس، فیلم، موزیک و...).@Columnاین انوتیشن را دیده‌اید. جدید نیست. از آن استفاده هم کرده‌اید. بیاد دارید که استفاده از خصیصه‌ی name در انوتیشن Column، اسم ستون را مشخص کردید؟ قسمت اول و دوم را نگاه کنید. خب اکنون می‌خواهیم این انوتیشن را باز کنیم و نشان دهیم چه اطلاعات دقیق‌تری میتوانیم برای ستون‌هایمان درنظر بگیریم.اجازه دهید نگاهی به کد خود این انوتیشن بیاندازیم:@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface Column {
    String name() default &quot;&quot;;
    boolean unique() default false;  // خط چهارم
    boolean nullable() default true;
    boolean insertable() default true;
    boolean updatable() default true;
    String columnDefinition() default &quot;&quot;;
    String table() default &quot;&quot;;
    int length() default 255;
    int precision() default 0; // decimal precision
    int scale() default 0; // decimal scale
}این کد خود انوتیشن @Column است، همانطور که ملاحظه می‌کنید تمام خصیصه‌ها بعلاوه‌ی مقدار پیش‌فرض آنها لیست شده است. بعنوان مثال در خط چهارم، به خصیصه‌ی unique اشاره شده است که مقدار پیش‌فرض آن هم false است. این خصیصه مشخص می‌کند که یک فیلد (یا همان ستون در دیتابیس) باید یکتا باشد یا می‌تواند مقدار تکراری بگیرد.همانطور که متوجه شده‌اید، به کمک این انوتیشن و خصیصه‌هایش میتوانید محدودیت‌های بسیاری را برای یک فیلد درنظر بگیرید. از مهم‌ترین خصیصه‌هایی که می‌توان به آن اشاره کرد مقادیر unique، nullable، length و name می‌باشد. اگر خاطرتان باشد در قسمت دوم از خصیصه‌ی table هم استفاده کردیم (جایی که می‌خواستیم یک Entity را به چندین جدول تقسیم کنیم).به مثال زیر دقت کنید:@Entity
public class Book {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column(name = &quot;book_title&quot;, nullable = false, updatable = false)
    private String title;
    private Float price;
    @Column(length = 2000)
    private String description;
    private String isbn;
    @Column(name = &quot;nb_of_page&quot;, nullable = false)
    private Integer nbOfPage;
    // متد های گتر و ستر و سازنده
}@Temporalاین انوتیشن برای مشخص کردن تاریخ استفاده می‌شود. مثال زیر گویای مطلب است:@Temporal(TemporalType.DATE)
private Date dateOfBirth;@Transientکلیدواژه‌ی transient در جاوا را بیاد دارید؟ که اجازه نمی‌داد یک فیلد مشخص serial شود؟ خاطرتان نیست؟ عیبی ندارد، بطور کلی فراموش کنید این جمله را. این انوتیشین وقتی برای فیلدی استفاده شود، مانع از آن می‌شود که آن فیلد مشخص نگاشت شود.بطور کلی ما به ازای هر فیلد در جاوا، یک ستون در جدول خواهیم داشت، ولی اگر این انوتیشن را برای فیلدی استفاده کنیم، مانع از آن می‌شویم که ستونی برای آن در دیتابیس لحاظ شود. شاید ما در جاوا به فیلدهایی نیاز داشته باشیم، که وجود ستونی برای آنها الزامی نباشد، با استفاده از @Transient به این هدف دست می‌یابیم!@Enumeratedبرای نگاشت کلاس‌های enum استفاده می‌شود.توضیح بیشتر از این خط را در گوگل جست‌وجو کنید!کالشکنی (مجموعه‌ای) از انواع ساده خب تقریبا داریم به جاهای قشنگ هایبرنیت می‌رسیم. مطمئن هستم تا اینجای کار کلی برایتان سوال پیش آمده که چطور ارتباطات بین Entityها را برقرار کنیم؟ چطور لیستی (مثلا از تلفن) را نگاشت کنیم؟ همه و همه را خواهم گفت. حوصله کنید و همراه باشید!در اینجا می‌خواهیم به این موضوع اشاره کنیم که اگر بخواهیم لیستی از از انواع ساده را مدیریت کنیم چه باید بکنیم. اول توضیح دهیم که منظورمان از انواع ساده چیست. مقصود از انواع ساده، کلاس‌هایی هستند که Entity نیستند. مثلا لیستی از Stringها، لیستی از Integerها و امثالهم. این‌ها را اگر بخواهیم مدیریت کنیم از ۲ انوتیشن استفاده می‌کنیم.اونتیشن @ElementCollection مشخص می‌کند که این فیلد، شامل لیستی از انواع ساده است (یعنی Stringها و یا هر چیز دیگری که Entity نیست). در کنار این انوتیشن باید از انوتیشن دیگری نیز استفاده کنیم. یعنی انوتیشن @CollectionTable که با استفاده از آن می‌توانیم اطلاعات جدول مربوطه‌اش را مدیریت کنیم. ببینید، وقتی ما مثلا لیستی از Stringها را داشته باشیم، منطقا هایبرنیت باید جدولی مجزا برای آن درنظر بگیرید که به کمک کلیدخارجی با Entity ما ارتباط دارد. حال این جدول چه مشخصاتی باید داشته باشد؟ بطور مثال چه نامی باید داشته باشد؟ این‌ها را با استفاده از @CollectionTable مشخص می‌کنیم.پرسش. منظور شما از لیست چیست؟ منظورتان کلاس List یا پیاده‌سازی آن مثل ArrayList است؟پاسخ. منظور من مجموعه‌ای از داده‌ها است. یعنی هم می‌تواند ArrayList باشد هم HashSet و یا HashMap و یا هر Collection دیگری. لیست منظورم دقیقا یعنی مجموعه‌ای از انواع ساده.پرسش. کلیدخارجی چیست؟ منظورم آنجایی است که گفتید هایبرنیت یک جدول جدا در نظر می‌گیرد و به کمک کلیدخارجی به جدول اصلی ارتباط می‌دهد.پاسخ. کلیدخارجی هم مشابه مبحث blob کاملا مربوط به بحث دیتابیس است. یادگیری آن هم به عهده‌ی خود شماست. انتظار من بر این بود که کلیدخارجی را بشناسید، حال که اینطور نیست عیبی ندارد. همین‌جا صبر کنید، و اول کلیدخارجی را جست‌وجو کنید و یادبگیرید، بعد ادامه دهید.خب بیایید فرض کنیم می‌خواهیم در کلاس دانشجو (Student) یک لیستی از موضوعات مورد علاقه‌ی دانشجو داشته باشیم! فرضی است کاملا! بر نوع مثال خرده نگیرید. باید چکار کنیم؟ با توجه به توضیحاتی که دادیم باید ساده باشد. یک ArrayList به موجودیت (Entity) دانشجو اضافه می‌کنیم.نکته‌ی بسیارمهم: وقت آن است که با دقت تمام خط به خط کدها را نگاه کنید و ملاحظه کنید که دقیقا چه اتفاقی افتاده است. من تمام خروجی‌ها بعلاوه‌ی کدها را می‌گذارم. فقط با تفسیر خط به خط و نوشتن خودتان می‌توانید مطمئن شوید که فرا گرفتید. غیر از این احتمالا چیزی دستگیرتان نشود.کلاس Student:package entities;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
public class Student {
    @EmbeddedId
    private StudentKey studentKey;
    @Column(name = &quot;sname&quot;)
    private String name;
    @Column(name = &quot;stell&quot;)
    private String tell;
    private String address;

    @ElementCollection(fetch = FetchType.EAGER)  /// از این جا به بعد خوب دقت کنید
    @CollectionTable(name = &quot;alaghemandiha&quot;)
    @Column(name = &quot;value&quot;)
    private List&lt;String&gt; favorites = new ArrayList&lt;String&gt;();

    public Student(StudentKey studentKey, String name, String tell, String address) {
        this.studentKey = studentKey;
        this.name = name;
        this.tell = tell;
        this.address = address;
    }
    public List&lt;String&gt; getFavorites() {
        return favorites;
    }
    public void setFavorites(List&lt;String&gt; favorites) {
        this.favorites = favorites;
    }
    public StudentKey getStudentKey() {
        return studentKey;
    }
    public void setStudentKey(StudentKey studentKey) {
        this.studentKey = studentKey;
    }
    public Student() {} // non-arg constructor
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getTell() {
        return tell;
    }
    public void setTell(String tell) {
        this.tell = tell;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}کلاس Test:import entities.Student;
import entities.StudentKey;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class Test {
    public static void main(String[] args) {
        StudentKey studentKey = new StudentKey(12345,67890);
        Student student = new Student(studentKey,&quot;Mohamad&quot;, &quot;09121112233&quot;, &quot;Tehran&quot;);
        student.getFavorites().add(&quot;Book&quot;);
        student.getFavorites().add(&quot;Programming&quot;);
        student.getFavorites().add(&quot;Dancing&quot;);

        SessionFactory sessionFactory = new Configuration().configure(&quot;hibernate.cfg.xml&quot;)
                .addAnnotatedClass(Student.class)
                .buildSessionFactory();

        Session session = sessionFactory.getCurrentSession();

        session.beginTransaction();

        session.save(student);

        session.getTransaction().commit();

        session.close();
        sessionFactory.close();
    }
}خروجی مربوط به جداول:خروجی مربوط به پایگاه داده پرسش. ببینید تا اینجای کار متوجه شدم که چطور ذخیره سازی میکنید، اما اینکه همین‌ها رو چطور باید بازیابی کنم برایم سوال است! چطور باید بعنوان مثال اطلاعات یک دانشجو، بهمراه تمام علاقه‌مندی‌هایش را داشته باشم؟پاسخ. متوجه نگرانی شما هستم، اجازه دهید این موضوع را موکول کنیم به زمانی که می‌خواهیم راجع به بحث CRUD توضیح دهیم. آنجا بازیابی اطلاعات بحث خواهد شد، دوباره در بخش HQL به آن خواهیم پرداخت، باز هم در مبحث Criteria به آن برخواهیم گشت. پس نگران نباشید. توصیه‌ی قبلی من را بیاد دارید؟ به آن چیزی که نمیدانید تمرکز نکنید، به آنهایی که میدانید تمرکز کنید. همراه باشید!پرسش. متوجه شدم که چطور یک ArrayList را باید به عنوان یک جدول در نظر گرفت و یک نام هم حتا برای ستون آن گذاشتیم ()، اما چطور یک HashMap را نگاشت کنیم؟ مگر به ۲ ستون نیاز نداریم؟پاسخ. صحیح است. برای این منظور باید چنین کرد:@ElementCollection
@CollectionTable(name=&quot;track&quot;)
@MapKeyColumn (name = &quot;position&quot;)
@Column(name = &quot;title&quot;)
private Map&lt;Integer, String&gt; tracks = new HashMap&lt;&gt;();چطور یک آبجکت را Embedd کنیم؟بیاد دارید چطور کلید مرکب ساختیم؟ از همان تکنیک می‌توانیم برای Embed کردن یک آبجکت درون یک کلاس استفاده کنیم. اگر خوب بیاد ندارید ابتدا قسمت کلید مرکب را مجددا مطالعه کنید، و بعد به کد‌های پایین نگاه کنید.دقت کنید که تمام فیلدهای آبجکت Embed شده تبدیل به ستون در موجودیت مربوطه می‌شود.کلاس Address:package entities;
import javax.persistence.Embeddable;
@Embeddable
public class Address {
    private String country;
    private String city;
    private String street;
    public Address() {}
    public Address(String country, String city, String street) {
        this.country = country;
        this.city = city;
        this.street = street;
    }
    public String getCountry() {
        return country;
    }
    public void setCountry(String country) {
        this.country = country;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    public String getStreet() {
        return street;
    }
    public void setStreet(String street) {
        this.street = street;
    }
}تکه کد مربوطه به Address در کلاس Student:@Embedded
private Address address;خب دوستان عزیز، حرف بیشتری راجع به نگاشت یک Entity ندارم، اگر حالتی هست که پوشش داده نشده شما متذکر شوید. چیز دیگری که مانده بحث نگاشت روابط است. روابط یک‌به‌یک، یک‌به‌چند، چندبه‌یک و چندبه‌چند و بحث مربوط به وراثت.می‌خواهم ابتدا در مورد عملیات CRUD با هایبرنیت صحبت کنم، بعد به Relation Mapping بپردازیم.عملیات CRUD در هایبرنیتهمانطور که احتمالا می‌دانید، CRUD مخفف Create، Read، Update و Delete است. این چهارگانه، عمده‌ی عملیاتی است که ما با دیتابیس انجام می‌دهیم. فلذا در ادامه می‌خواهیم به این چهار عملیات اصلی بپردازیم. حدس من بر این است که شما این چهار عمل را با خود SQL انجام داده‌اید، و حتا از طریق jdbc تجربه‌ی آن را دارید. حالا فرصت مناسبی است که این چهار عملیات را با هایبرنیت بررسی نماییم.نحوه‌ی درج (insert)خب تا اینجای کار نحوه‌ی Insert کردن به دیتابیس را مشاهده کرده‌اید. ما برای insert از متد save در کلاس Session استفاده کردیم. بیایید مرور کنیم...قبلا نیز اشاره کردم، برای انجام عملیات با دیتابیس ما نیاز به یک تراکنش داریم (اگر مفصل می‌خواهید بدانید، انتهای قسمت اول، و ابتدای قسمت دوم را مطالعه کنید). در داخل یک تراکنش است که ما با دیتابیس صحبت می‌کنیم. در تمام مثال‌ها نیز در داخل یک تراکنش متد save را فراخوانی کردیم.تاکید می‌کنم، برای عملیات insert بکمک هایبرنیت، از متد save در کلاس Session استفاده می‌کنیم:session.beginTransaction();          
session.save(object);          // آبجکتی را که میخواهیم پرسیست کنیم 
session.getTransaction().commit();نحوه‌ی بازیابی (retrieve)خاطرتان هست که گفتیم هر Entity باید یک کلیداصلی داشته باشد که با انوتیشن @Id نشانش می‌دادیم؟ فلسفه‌ی کلید اصلی چیست؟ اینکه با کمک آن به رکوردی یکتا برسیم. خب، منطقا در هایبرنیت هم برای بازیابی به کلیداصلی احتیاج داریم.برای بازیابی اطلاعات، ما از متد get در کلاس Session استفاده می‌نماییم. اما پارامترهای این متد چیست؟این متد دریکی از شکل‌های خود که ما استفاده می‌کنیم، ۲  پارامتر بعنوان آرگومان ورودی دریافت می‌کند. آرگومان اول نام کلاسی (entity) است که ما می‌خواهیم یک رکورد از آن را بازیابی کنیم. آرگومان دوم کلید آن رکورد است.فرض کنید در جدول Student، دانشجویی داریم که کلید آن 2231 است. برای بازیابی تمام رکورد آن (در قالب یک آبجکت) چنین رفتار میکنیم:ابتدا یک آبجکت از نوع دانشجو می‌سازیم، و سپس خروجی متد get را به آن پاس می‌دهیم.Student student = new Student();
session.beginTransaction();
student = session.get(Student.class, 2231);
session.getTransaction().commit();حال تمام اطلاعات مربوط به دانشجو درون آن آبجکت قرار دارد. به همین راحتی می‌توانیم از جدول اطلاعات را بارکشی کنیم.پرسش. تا اینجا بسیار ساده بود، اما صبر کنید، در آخرین مثالی که برای ما زدید برای کلاس دانشجو یک کلید مرکب ساختید، بعبارتی یک کلاس دیگر بنام StudentKey ایجاد کردید، و ترکیب شماره‌ملی و شماره‌شناسنامه آن را بعنوان کلیداصلی دانشجو قرار دادید، در این حالت چطور باید کلید را مشخص کرد؟پاسخ. خوشحالم که چنین هوشیارانه در حال دنبال کردن مقالات هستید، بله کاملا بیاد دارم، و تصمیم داشتم که اکنون بعنوان مثال دوم از بازیابی مطرح کنم. گرچه تفاوت خاصی ندارد، تنها اینبار بعنوان کلید (که در مثال اول یک عدد integer بود) باید یک آبجکت کلید (در این مثالی که شما ذکر کردید StudentKey) بعنوان آرگومان دوم پاس بدهید. ادامه مطلب را ملاحظه کنید.خب، در مثالی که تا به اینجای کار توسعه دادیم، کلید اصلی مرکب بود، من عینا کدهایی که باید برای بازیابی چنین رکوردی استفاده شود را برای شما کپی/پیست می‌کنم. لطفا خوب به کد‌ها بنگرید:کلاس Test:public class Test {
    public static void main(String[] args) {
        Student student;
        SessionFactory sessionFactory = new Configuration().configure(&quot;hibernate.cfg.xml&quot;)
                .addAnnotatedClass(Student.class)
                .buildSessionFactory();

        Session session = sessionFactory.getCurrentSession();

        session.beginTransaction();

        student = session.get(Student.class, new StudentKey(12345, 67890)); // به این خط توجه شود

        session.getTransaction().commit();

        System.out.println(student);

        session.close();
        sessionFactory.close();
    }
}خروجی را ملاحظه کنید:Student{studentKey=entities.StudentKey@6e019, name=&#039;Mohamad&#039;, tell=&#039;09121112233&#039;, favorites=[Book, Programming, Dancing], address=entities.Address@1255b1d1}همانطور که ملاحظه می‌کنید، براحتی رکورد مربوطه بازیابی شد.نحوه‌ی حذف (delete)اجازه دهید دیگر بدون پرگویی بگویم، برای حذف یک رکورد دو مرحله کار باید انجام داد: خواندن آن رکورد به کمک متد getحذف همان آبجکتی که بازیابی شده است به کمک متد deleteهمانطور که ملاحظه کردید، برای حذف از متد delete در کلاس Session استفاده می‌کنیم. به کد زیر توجه کنید، می‌خواهم تنها رکورد موجودی که در دیتابیس دارم را حذف نمایم!کلاس Test:public class Test {     
public static void main(String[] args) {         
Student student;         
SessionFactory sessionFactory = new Configuration().configure(&quot;hibernate.cfg.xml&quot;)                     .addAnnotatedClass(Student.class)                 
.buildSessionFactory();          
Session session = sessionFactory.getCurrentSession();         
session.beginTransaction();          

 student = session.get(Student.class, new StudentKey(12345, 67890));            student.setTell(&quot;0935936912&quot;);         
 session.delete(student);          
 
 session.getTransaction().commit();         
 session.close();         
 sessionFactory.close();    
 } }و خروجی لاگ:لاگ حذف (دو خط آخر)نکته‌ی جالب را توجه کردید؟ رکورد نه تنها از جدول student حذف شد، بلکه رکوردهای مرتبط آن نیز در جدول alaghemandiha نیز حذف شد.پرسش. هایبرنیت این کار را انجام می‌دهد؟ می‌توانیم این رفتار را کنترل کنیم؟پاسخ. بله، هایبرنیت اینکار را انجام می‌دهد. قطعا در این مثال بخصوص، ما رفتاری غیر از این را انتظار نداشتیم، اما بطور کلی بحثی بنام cascading وجود دارد که مربوط به مدیریت این بخش‌هاست. در قسمت‌های بعدی به آن می‌پردازیم.نحوه‌ی آپدیت (update)انجام عملیات بروز، مشابه عملیات delete است، یعنی ابتدا آبجکت مربوطه را از دیتابیس بازیابی می‌کنیم، سپس آن را به کمک متدهای setterهمان آبجکت تغییر می‌دهیم، و در نهایت از متد update استفاده می‌کنیم.فلذا برای آپدیت کردن از متد update در کلاس Session استفاده می‌کنیم. به مثال زیر توجه کنید، و دقیقا ببینید که چه رخ داده است. من خروجی + لاگ هایبرنیت را برای شما می‌آورم.کلاس Test:public class Test {
    public static void main(String[] args) {
        Student student;
        SessionFactory sessionFactory = new Configuration().configure(&quot;hibernate.cfg.xml&quot;)
                .addAnnotatedClass(Student.class)
                .buildSessionFactory();

        Session session = sessionFactory.getCurrentSession();
        session.beginTransaction();

        student = session.get(Student.class, new StudentKey(12345, 67890));
        student.setTell(&quot;0935936912&quot;);
        session.update(student);

        session.getTransaction().commit();
        session.close();
        sessionFactory.close();
    }
}به لاگ هایبرنیت مربوط به update توجه کنید:Hibernate: update Student set city=?, country=?, street=?, sname=?, stell=? where shomare_meli=? and shomare_shenasname=?و همچنین ببینید که در دیتابیس هم مقدار شماره‌تلفن تغییر کرده است:آپدیت شدن مقدار تلفن!پرسش. شما مدام میگید که متد فلان از کلاس Session! در صورتیکه اصلا Session کلاس نیست، یک رابط است. اشتباه خودتان را اصلاح کنید.پاسخ. خوشحالم که به گفته‌های من بسنده نکردید و خودتان جست‌وجو کردید. ابتدا بگویم که احتمال خطا در گفته‌های من زیاد است، و به این خاطر ازتون پوزش می‌خواهم.بله Sesssion یک interface است که با آبجکتی از کلاس SessionFactory مقداردهی می‌شود. اما از دید من خود رابط نیز نوعی کلاس است. ولی خوشحالم که متذکر شدید تا دوستان دیگر هم متوجه اصطلاحی که من بکار می‌برم شده باشند.سخن پایانیخب، حالا با عملیات CRUD هم آشنا شدید. وقت آن است که به یکی از جدی‌ترین بخش‌های هایبرنیت برویم. یعنی Relation Mapping. به من بگویید، آیا این مقاله توانسته نیاز شما رو برطرف سازد؟ توجه داشته باشید که برای نگارش این مقالات وقت خیلی زیادی صرف شده است، فلذا اهمیت زیادی برای من دارد تا بدانم آنطور که باید و شاید بکار می‌آید یا خیر.پایان بخش سوم.</description>
                <category>محمد عباسی</category>
                <author>محمد عباسی</author>
                <pubDate>Thu, 09 May 2019 13:27:48 +0430</pubDate>
            </item>
                    <item>
                <title>مروری اجمالی به آنچه که هایبرنیت می‌کند - قسمت دوم</title>
                <link>https://virgool.io/dayche-group-Blog/hibernate-tutorial-part2-l8bslr8dt0zv</link>
                <description>در قسمت گذشته خلاصه‌ای از هایبرنیت خدمتتان ارائه دادم، و در نهایت یک آبجکت را در پایگاه‌داده به واسطه‌ی هایبرنیت Persist کردیم. به گمانم قسمت اول بسیار جذاب بود. امیدوارم آنطور که من انتظار داشتم مقاله‌ای کاربردی بوده باشه، اگر پیشنهادی یا انتقادی داشتید با من درمیان بگذارید. این اولین باری است که مطلبی فنی می‌نویسم و قطعا بی‌ایراد نخواهد بود.متاسفانه وقتی قسمت اول نزدیک به ۴۰۰۰ هزار واژه شد (که بیشتر بخاطر وجود سورس کدها هم بود) ویرایشگر متن بشدت کند شده بود، به همین خاطر بالاجبار مطالب را شکستم و در چند قسمت ارائه خواهم داد.در این قسمت به مباحث جدیدی خواهیم پرداخت.لینک قسمت‌های مرتبطقسمت اولفهرست مطالبویژگی‌های هایبرنیتراه‌اندازی هایبرنیت (بخش ۲)توضیح Annotationهای پایه‌ایویژگی‌های هایبرنیتقسمت پیش به خیلی از ویژگی‌های هایبرنیت اشاره کردیم. به گمانم پس از ملاحظه‌ی یک مثال عملی وقت آن است که کمی مفاهیم و واقعیت‌ها رو مرور کنیم.با هم مرور کردیم که هایبرنیت اصلی‌ترین امکانش، ORM یا همان نگاشت (Mapping) است. و با هم دیدیم چطور یک آبجکت Student به رکوردی در جدول student تبدیل شد. در آینده این نگاشت را بسیار مفصل‌تر بررسی خواهیم کرد. از قابلیت‌های مهم دیگر هایبرنیت مستقل از نوع دیتابیس بودن است. ما در هنگام کار با هایبرنیت به این توجه نمی‌کنیم که دیتابیس‌ MySQL است یا Oracle! این امکان فوق‌العاده‌ای است که هایبرنیت به ما می‌دهد. اما غیر از این‌ها دیگر چه؟هایبرنیت امکان Auto DDL دارد. زبان SQL را بیاد میاورید؟ زبان SQL از سه بخش عمده تشکیل شده است: DDL، DML و DCL. دستورات DDL مربوط به ساخت و حذف Schema می‌شوند. بیان ساده‌تر دستورات DDL مربوط به ساخت جداول و اعمال مشابه مربوط می‌شود. قسمت قبل را بیاد بیاورید، آیا ما برای اینکه جدول student را بسازیم کاری کردیم؟ جز این که مشخص کردیم این کلاس (Student) یک Entity است. با همین مشخصه، هایبرنیت عملیات مربوط به ساخت و تغییر یا بروز کردن آن را انجام می‌دهد. پس ساخت جداول به عهده‌ی هایبرنیت است.سوال. چه زمانی هایبرنیت جدول را می‌سازد و چه زمانی آپدیت می‌کند؟پاسخ. امیدوارم بودم تا رسیدن به بخش تنظیمات این سوال را نمی‌پرسیدید، چرا که در آنجا مشخص می‌کنیم که جداول create بشوند یا update یا حتا validate و... . اجازه دهید در جای مناسب خودش مفصل توضیح خواهم داد.هایبرنیت امکان Cache کردن را دارد. هایبرنیت کش کردن را در سه سطح انجام می‌دهد. در این خصوص صحبت خواهیم کرد.هایبرنیت امکانات بسیار خوب دیگری نظیر primary key generation، exception free و... را می‌دهد. جدا از آن‌ها یک زبان مستقل بنام HQL (Hibernate Query Language) را عرضه کرده و همچنین API های مناسبی برای Dynamic Queries. در خلل مثال‌ها سعی‌ خواهم کرد به آن‌ها بپردازم. راه‌اندازی هایبرنیت (بخش ۲)از اینجا به بعد مثال قسمت پیشین را بسط خواهیم داد و نکات مبهم آن را برطرف خواهیم کرد. در قسمت گذشته بارها توضیح موردی را به آینده موکول کردیم. امروز دقیقا وقت همان است که پاسخ آن‌ها را بدهیم.فایل کانفیگ را بیاد بیاورید (در گام۲ قسمت اول)، یکی از Propertyهای آن بنام hbm2ddl.auto بود. مقدار این خصوصیت را ما برابر با create گذاشتیم. می‌خواهیم اکنون به مقدارهایی بپردازیم که می‌توانیم به آن خصیصه اختصاص دهیم.مقدار create: وقتی برنامه اجرا می‌شود، و فایل کانفیگ خوانده می‌شود، اگر مقدار hbm2ddl.auto برابر با create باشد، فریمورک hibernate تمام جداول موجود در دیتابیس را (درصورت وجود) پاک می‌کند و دوباره ایجاد می‌کند. توجه داشته باشید که تمام اطلاعات موجود در دیتابیس ازبین خواهد رفت (drop می‌شود)، و دوباره جداول ساخته خواهد شد.بنابراین چنانچه دیتایی در جداول خودتان دارید باید نسبت به استفاده از این مقدار هوشیار باشید. چون سطرهای جداول دوباره برنخواهند گشت، صرفا جدول را دوباره خواهد ساخت.پس در واقع مقدار create دو کار را انجام می‌دهد: جداول موجود را پاک می‌کند.جداول جدیدی را ایجاد می‌کند.حالا می‌دانید که اگر قصد آموزش و آزمایش ندارید، استفاده از این مقدار خطرناک است (خطر از دست دادن دیتا). معمولا تنها زمانی از این مقدار استفاده می‌کنند که می‌خواهند schemaی جداول را ایجاد کنند. ولی هنگامی که دیتایی درون دیتابیس وارد شد دیگر این مقدار را استفاده نخواهند کرد. چه مقداری را استفاده می‌کنند؟ همراه باشید!مقدار update: شما می‌توانید بجای استفاده از create مقدار update را قرار دهید. این مقدار در صورت اجرای مجدد دیگر جداول موجود را پاک نخواهد کرد. رفتاری روشنفکرانه دارد. بطور دقیق، هایبرنیت وقتی با این مقدار تنظیم شده باشد ملاحظه می‌کند، اگر جدولی در دیتابیس وجود ندارد، ولی در جاوا Entity برای آن ساخته شده است، آن جدول را ایجاد می‌کند. ما برنامه را همواره توسعه می‌دهیم و ممکن است Entityهای زیادی را اضافه کنیم که بخواهیم درهنگام اجرا، جداول مربوطه‌ی آنها ایجاد شود و مشکلی هم برای جداول از پیش تعیین شده (برای Entityهای قدیمی) پیش نیاید. مقدار update برای ما چنین می‌کند.یکی دیگر از کارهایی که مقدار update انجام می‌دهد، ملاحظه می‌کند کدام Entity قدیمی تغییر کرده است (بعنوان مثال فیلدی به آن اضافه شده است که در نتیجه باید ستونی به جدول مربوطه‌اش اضافه شود). مقدار update تمام جداولی را که باید تغییر کنند را نیز alter می‌کند. بعبارتی جداول را بروز می‌کند.پس درواقع مقدار update دو کار انجام می‌دهد: اگر جدول مربوطه به Entityای وجود نداشته باشد، آن را ایجاد می‌کند.۲.   اگر جدول مربوط به Entityای تغییر کرده باشد، آن را تغییر می‌دهد.یک کار سومی نیز انجام می‌دهد: به entityهایی که جداول متناظرشان وجود دارد و نیازی به تغییر ندارند، دست نمی‌زند!مقدار create-drop: این مقدار رفتار جالبی دارد. ابتدا تمام جداول را پاک می‌کند (درصورت وجود) بعد همچون مقدار create، آنها را ایجاد می‌کند، و پس از آن که کار به اتمام رسید دوباره آن‌ها را پاک میکند.مقدار validate: این مقدار در واقع فقط چک می‌کند که آیا mapping schema (همان کلاس‌های ما با Annotationهای مربوطه‌اش) با table schema که در دیتابیس قرار دارد هماهنگ هستند یا خیر. و هیچ کار اضافه‌ای انجام نخواهد داد.وقتی که برنامه از حالت development به حالت production می‌رود، معمولا این مقدار (validate) را برای تنظیمات هایبرنیت درنظر می‌گیرند.خب، تا اینجای کار مقدار hbm2ddl.auto را برابر با update قرار دهید تا با ری‌استارت‌ کردن‌های مجدد برنامه اطلاعات دیتابیس پاک نشود و به ادامه‌ی توضیح مثال قبل بپردازیم.من تصویر مثال قبل را دوباره در این قسمت نیز آپلود می‌کنم:آزمودن هایبرنیت، به خروجی توجه کنید!اول اجازه دهید که تاکید کنم هنگام استفاده از هایبرنیت توجه ویژه‌ای به لاگ‌های خود فریمورک داشته باشید. هایبرنیت بطور کامل توضیح می‌دهد که دقیقا چه دستوراتی را اجرا می‌کند، و عملا چه رفتاری دارد.متاسفانه من حوصله‌ی تحلیل لاگ را ندارم، ولی چیزی نیست که از عهده‌ی آن برنیایید.پرسش. من وقتی هایبرنیت را اجرا می‌کنم هیچ چیز راجع به دستورات SQL نمایش نمی‌دهد!پاسخ. احتمالا مقدار show_sql در فایل کانفیگ را برابر با false قرار دادید. همانطور که در فایل کانفیگ مشاهده می‌کنید ما این مقدار را برابر با true قرار دادیم.پرسش. من Entityای بنام hibernate_sequence نساخته‌ام، با این وجود یک جدول به این نام در دیتابیس من وجود دارد، و هربار هم عملیات insert را انجام می‌دهم ابتدا یک مقداری در این جدول آپدیت می‌شود. این چه جدولی است؟پاسخ. خوشحالم که به لاگ‌های هایبرنیت هوشیارانه نگرسته‌اید. بله، این جدول را هایبرنیت خودش برای مدیریت یکتایی مقدار در فیلد id درست کرده است. زمانی که ما از Annotation تولید مقادیر (یعنی @GeneratedValue) استفاده می‌کنیم این اتفاق میافتد. ولی می‌توانستیم استراتژی مشخصی برای GeneratedValue در نظر بگیریم. بطور مثال وقتی در MySQL از مقدار @GeneratedValue(strategy = GenerationType.IDENTITY) استفاده می‌کنیم، دیگر این جدول کمکی ایجاد نمی‌شود. چند و چون و استراتژی‌های مختلف را شرح خواهم داد. در هر حال توجه ویژه شما به لاگ‌ها شایسته‌ی تقدیر است.هر عملیاتی که با هایبرنیت انجام می‌دهیم، می‌بایست در یک تراکنش مشخص باشد. نگران نباشید، این جمله را کاملا توضیح خواهم داد.به تصویر توجه بکنید، ما یک Session از کلاس SessionFactory میگیریم (Session session = sessionFactory.getCurrentSession). سپس با استفاده از‌ آن آبجکت یک تراکنش را شروع می‌کنیم (session.beginTransaction). بطور محاوره، از اینجا تا زمانی که تراکنش شده را کامیت می‌کنیم (session.getTransaction().commit)، میتوانیم عملیات خود را با دیتابیس انجام دهیم (درج کنیم، حذف کنیم، تغییر دهیم و یا بازیابی کنیم). در این مثال یک درج ساده انجام داده‌ایم (از متد save استفاده کردیم).از زمان شروع تراکنش تا زمان کامیت آن، آبجکت در وضعیت persistence قرار می‌گیرد. این وضعیت را بعدا توضیح می‌دهم.پرسش. شما با گفتن بعدا توضیح می‌دهم، حس بدی را به ما منتقل می‌کنید. حس نفهمیدن، و حس اینکه کلی مباحث دیگر مانده است که ما نمی‌دانیم.پاسخ. متاسفانه این حس را نیز من دارم (کلی مباحث مانده که من نمی‌دانم)، ولی از اینکه حس بدی به شما منتقل شده است متاسفم، هدف من این است که دست‌کم اشاره به تمام موضوعات (تا جایی که خودم می‌دانم) بکنم، تا بتوانید جست‌و‌جو کنید. برای اینکه باور کنید حالت‌های آبجکت در هایبرنیت هم چیز عجیب و غریبی نیست، توضیح مختصری می‌دهم.در هایبرنیت آبجکت ۳ وضعیت کلی دارد:۱. وضعیت transient: زمانی که آبجکت با عملگر new ساخته می‌شود و هیچ ارتباطی با هایبرنیت ندارد!۲. وضعیت persistant: زمانی که آبجکت دقیقا درگیر با Session هایبرنیت است.۳. وضعیت detached: زمانی که آبجکت از از session حذف می‌شود.شفاف نبود؟ عیبی ندارد. کارایی اصلی دانستن این موضوع بیشتر در رابطه با بحث Caching است، پس مفصل خواهیم گفت.دوست دارید به کدام مبحث بپردازیم؟ بحث CRUD در هایبرنیت؟ یا کمی Annotationهای پایه‌ای را معرفی کنیم؟ هر دو مبحث جذاب هستند، اما اصلا قابل قیاس با جذابیت بحث Association Mapping نیست!توضیح Annotationهای پایه‌ایخب، تصمیم گرفته شد! البته حتما پیش‌تر توی فهرست مطالب خوانده بودید و من آنگونه که باید نتوانستم شما را سوپرایز کنم!@Tableاین Annotation اطلاعاتی در رابطه با اینکه جدول ساخته شده از یک Entity مشخص چگونه باید باشد را می‌دهد! البته صرفا در مورد نام آن تصمیم می‌گیرد. با تغییر پارامتر name در آن می‌توانید نام جدول را نامی دلخواه بگذارید:@Table(name=&quot;my_table_name&quot;)@Entityاین Annotation را باز هم خواهیم دید، اما اینجا یک اشاره کوتاه به آن کنیم. این Annotation نیز پارامتری بنام name دارد که نام Entity را تغییر می‌دهد. توجه کنید که نام Entity با نام جدول متفاوت است. نام جدول چیزی است که دیتابیس می‌شناسد و با آن کار می‌کند. اما نام Entity چیزی است که جاوا می‌شناسد. اگر این نام را تغییر دهید، دیگر همیشه باید با نام تغییر شده در کدهای جاوا آن را صدا کنید. گیج کننده شد؟ عیبی ندارد، مثالش را خواهید دید.پرسش. درست است که گفتید اگر متوجه نشدید عیبی ندارد، اما دقیقا یعنی چی؟ یعنی نام کلاس جاوا تغییر می‌کند؟پاسخ. خیر، نام کلاس جاوا تغییر نمی‌کند. بطور پیش‌فرض نام Entity همان نام کلاس جاوا است. بطور مثال کلاس Student نام Entityاش Student است. ولی وقتی تغییر دهیم، نام Entity دیگر نام پیش‌فرض نخواهد بود.بخاطر دارید که در قسمت فلسفه‌ی هایبرنیت گفتم که مزیت اصلی هایبرنیت آن است که رویکرد شی‌گرا را حفظ کنیم؟ و بدون آنکه دیتابیس را مدنظر قرار دهیم به برنامه‌نویسی بپردازیم؟ مسئله همین جاست. بطور مثال ما در زبان HQL از نام Entity استفاده می‌کنیم که بطور پیش‌فرض نام کلاس است (بدون توجه به نام جدول)، وقتی نام Entity را تغییر می‌دهیم باید حواسمان همیشه جمع باشد که دیگر با نام تغییر کرده از آن در کد های جاوا استفاده کنیم (هرجا که نیاز به نام Entity هست، مثلا در استفاده از HQL)@SecondaryTable(name=&quot;second_table&quot;)@SecondaryTables({@SecondaryTable(name=&quot;second_table&quot;),                     @SecondaryTable(name=&quot;another_second_table&quot;)})خیلی وقت‌ها جداول دیتابیس آماده هستند و ما مجبور هستیم کلاس‌هایمان را با آن منطبق کنید. بطور مثال ممکن است یک کلاس جاوا، شامل ۲ جدول در دیتابیس باشد. چطور باید آن Entity را به ۲ جدول تقسیم کنیم؟ یا حتا بیشتر. اگر صرفا یک جدول اضافه هست (در مجموع ۲ جدول) کافی است از @SecondaryTable استفاده کنیم و نام جدول دوم را بدهیم. اگر بیش از ۲ جدول بود میباست از @SecondaryTables استفاده کنیم که یک &quot;s&quot; جمع دارد. و نوع استفاده از آن را در عنوان آورده‌ام. حال چگونه بگوییم کدام فیلد برای کدام جدول است؟ از @Column استفاده می‌کنیم. در @Column خصیصه‌ای هست بنام table که مقدار نام جدول دوم را برای آن در نظر می‌گیریم. اگر هیچ مقداری لحاظ نکنیم، بطور پیش‌فرض آن فیلد برای جدول اصلی درنظر گرفته می‌شود:@Entity@SecondaryTables({        @SecondaryTable(name = &quot;city&quot;),        @SecondaryTable(name = &quot;country&quot;)})public class Address {    @Id    private Long id;    private String street1;    private String street2;    @Column(table = &quot;city&quot;)    private String city;    @Column(table = &quot;city&quot;)    private String state;    @Column(table = &quot;city&quot;)    private String zipcode;    @Column(table = &quot;country&quot;)    private String country;    // Constructors, getters, setters}این تکه کد را از کتاب Beginning Java EE 7 برداشته‌ام. خوب به آن توجه کنید.سوال، وقتی نگاشت مثال بالا صورت بگیرد، چه اتفاقی میافتد؟ لطفا در کامنت‌ها برای من بگویید.کلیداصلی مرکبخب، بنظور داشتن یک کلید مرکب، میتوان از قابلیت @Embeddable در JPA استفاده کرد. کلاسی را که Embeddable در نظر می‌گیریم می‌بایست حتما از قوانین JavaBeans پیروی کند. بعبارتی باید حتما یک سازنده‌ی بدون آرگومان (no-args constructor) داشته باشد، بهمراه متدهای getter و setter و همچنین متدهای equals و hashCode. نکته‌ی مهم دیگر اینکه کلاس Embeddable نباید هیچ کلیدی داشته باشد. بعبارتی فیلدی از آن نباید انوتیشن @Id داشته باشد. و اما چطور کار می‌کند؟ اجازه دهید ابتدا یک کلاس را بعنوان Embeddable ایجاد کنیم.پس از ایجاد این کلاس، یک فیلد از جنس همین کلاس در کلاس مقصد (جایی که می‌خواهیم کلیدمرکب داشته باشیم) ایجاد می‌کنیم.اگر گیج شده‌اید نگران نباشید، به مثالی که میزنم خوب دقت کنید. ما می‌خواهیم کلاس Student کلیدی مرکب داشته باشد، به این معنا که ترکیب کدملی و شماره‌شناسنامه‌ی داشنجو را بعنوان کلید درنظر بگیریم. تمام اطلاعات دیگر دانشجو هم سرجای خودش باقی بماند. حال چکار باید بکنیم؟ اولین کار ایجاد کلاس کلید! و دومین کار، تزریق آن به کلاس Student.توجه کنید که کلاس Embeddable باید اینترفیس Serializable را impelement کند!من تک تک کلاس‌ها را برای شما می‌گذارم، با دقت نگاه کنید، چرا که نسبت به مثال‌های پیشین اندکی تغییر داشته است.کلاس StudentKey:package entities;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import java.io.Serializable;

@Embeddable
public class StudentKey implements Serializable {

 @Column(name = &quot;shomare_meli&quot;)
    private int nationalId;
 @Column(name = &quot;shomare_shenasname&quot;)
    private int sid;

    public StudentKey() { }

    public StudentKey(int nationalId, int sid) {
        this.nationalId = nationalId;
        this.sid = sid;
    }

    public int getNationalId() {
        return nationalId;
    }

    public void setNationalId(int nationalId) {
        this.nationalId = nationalId;
    }

    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

 @Override
 public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        StudentKey that = (StudentKey) o;

        if (nationalId != that.nationalId) return false;
        return sid == that.sid;
    }

 @Override
 public int hashCode() {
        int result = nationalId;
        result = 31 * result + sid;
        return result;
    }
}کلاس Student:package entities;
import com.sun.istack.internal.NotNull;
import javax.persistence.*;

@Entity
public class Student {
    @EmbeddedId
    private StudentKey studentKey;
    @Column(name = &quot;sname&quot;)
    private String name;
    @Column(name = &quot;stell&quot;)
    private String tell;
    private String address;

    public Student(StudentKey studentKey, String name, String tell, String address) {
        this.studentKey = studentKey;
        this.name = name;
        this.tell = tell;
        this.address = address;
    }
    public StudentKey getStudentKey() {
        return studentKey;
    }
    public void setStudentKey(StudentKey studentKey) {
        this.studentKey = studentKey;
    }
    public Student() {} // non-arg constructor
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getTell() {
        return tell;
    }
    public void setTell(String tell) {
        this.tell = tell;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}کلاس Test:import entities.Student;
import entities.StudentKey;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class Test {
    public static void main(String[] args) {
        StudentKey studentKey = new StudentKey(12345,67890);
        Student student = new Student(studentKey,&quot;Mohamad&quot;, &quot;09121112233&quot;, &quot;Tehran&quot;);

        SessionFactory sessionFactory = new Configuration().configure(&quot;hibernate.cfg.xml&quot;)
                .addAnnotatedClass(Student.class)
                .buildSessionFactory();

        Session session = sessionFactory.getCurrentSession();
        session.beginTransaction();

        session.save(student);

        session.getTransaction().commit();
        session.close();
        sessionFactory.close();
    }
}تصویر ساختار پروژهساختار پروژهمی‌توانید عینا کدها را کپی/پیست کنید، گرچه توصیه می‌شود که بنویسید تا بهتر درک کنید. خروجی کد بالا را میتوانید در دیتابیس من ببینید:خروجی کد بالابجای استفاده از انوتیشن‌های @Embeddable و @EmbeddedId می‌توانستیم از @IdClass استفاده کنیم. شما به من بگویید استفاده از آن چگونه است!خب، با توجه به اینکه دوباره میزان کلمات استفاده شده در این پست زیاد شده است، ادیتور متن ویرگول سنگین شده است و سرعت تایپ من را پایین آورده است، باید این مطلب را ببندم و برای ادامه پست دیگری را باز کنم.دوستان عزیزم به من بگویید که نظر شما در رابطه با این مطالب چیست. آیا چیزی دستگیرتان می‌شود یا مباحث همچنان برایتان گنگ است؟ نظرات شما کمک شایانی به من می‌کند. موفق باشید.پایان قسمت دوم.</description>
                <category>محمد عباسی</category>
                <author>محمد عباسی</author>
                <pubDate>Wed, 08 May 2019 19:46:16 +0430</pubDate>
            </item>
                    <item>
                <title>مروری اجمالی به آنچه که هایبرنیت می‌کند - قسمت اول</title>
                <link>https://virgool.io/dayche-group-Blog/%D9%85%D8%B1%D9%88%D8%B1%DB%8C-%D8%A7%D8%AC%D9%85%D8%A7%D9%84%DB%8C-%D8%A8%D9%87-%D8%A2%D9%86%DA%86%D9%87-%DA%A9%D9%87-%D9%87%D8%A7%DB%8C%D8%A8%D8%B1%D9%86%DB%8C%D8%AA-%D9%85%DB%8C%DA%A9%D9%86%D8%AF-%D9%82%D8%B3%D9%85%D8%AA-%D8%A7%D9%88%D9%84-pkqvy2sqhtfn</link>
                <description>در این مقاله قصد دارم از ارتفاع ۱۰هزارپایی به کلیت هایبرنیت نگاهی بیاندازم تا شاید بتوان حدودی مشخص برای آن تعیین کرد. گرچه بمنظور عملی بودن مقاله، در خیلی مواقع فرودی اضطراری خواهیم داشت. اگر بتوانم به این هدف دست یابم، احتمالا کار خود و دیگر دوستانی که بدنبال مباحث Hibernate و بطور کلی ORMها هستند را راحت‌تر کرده‌ام.فهرست مطالبفلسفه‌ی هایبرنیتراه‌اندازی هایبرنیتفلسفه‌ی هایبرنیتهایبرنیت چیست و چکار می‌کند؟ این اولین پرسشی است که باید به آن پاسخ داد. در مورد فلسفه‌ی هایبرنیت مطالب فارسی و انگلیسی بسیاری وجود دارد، به این خاطر اشاره من به آن کوتاه، مختصر و خلاصه خواهد بود.هایبرنیت یک فریمورک برای ذخیره‌سازی و بازیابیِ آبجکت‌های جاوا درون پایگاه‌داده است. تکرار می‌کنم، هایبرنیت ابزاری است که Java Objectها را درون پایگاه‌داده Persist / Save می‌کند و طبعا در زمان مورد نیاز Retrieve می‌کند.تاکید من بر Java Objectها از این منظر است که تفاوتی جدی بین JDBC و فریمورک هایبرنیت قائل شویم. گرچه خود این فریمورک در نهایت از درایور JDBC برای ارتباط با پایگاه‌داده استفاده می‌کند، اما این امکان را به ما می‌دهد که با رابطی متفاوت، رویکرد شی‌گرای خودمان را حفظ کنیم.اگر هیچ اطلاعی از پیش راجع به هایبرنیت نداشته‌اید، و مطالب بالا را گنگ و بی‌معنی می‌بینید، تنها یک توصیه به شما دارم: باید تحمل و صبر بیشتری بخرج دهید. من فقط می‌توانم قول دهم که به محض دست به کد شدن تمام گرفتاری‌های ذهنی راجع به فلسفه‌ی هایبرنیت برایتان برطرف خواهد شد، و کافی است بعد از آن یکبار دیگر این مقدمه‌ی کوتاه را مطالعه کنید تا بنظرتان شفاف و گویا بیاید. همراه باشید!وقتی صحبت از هایبرنیت می‌کنم،‌ منظورم دقیقا به فریمورک ORM آن است. احتمالا شما نیز چنین برداشتی را کرده بودید که صحبت از چیست. اما در سایت هایبرنیت تاکید کرده است که هایبرنیت بیشتر از یک فریمورک ORM (یا همان Object Relational Mapping) است. فلذا مجددا تاکید می‌کنم که هدف این مقاله شرح Hibernate ORM می‌باشد.هایبرنیت فریمورک‌‌های دیگری نظیر Validator ، Search ، OGM و... را دارد. فریمورک ORM مخصوص عملیات نگاشت بین آبجکت‌های جاوا و پایگاه‌داده‌ی رابطه‌ای است. بعبارت  دیگر هایبرنیت مابین پایگاه‌داده‌ی رابطه‌ای و آبجکت‌های جاوا قرار می‌گیرد و آن‌ها را بهم تبدیل می‌کند. پرسش. آیا هایبرنیت فقط برای پایگاه‌داده‌های رابطه‌ای نظیر MySQL, SQL Server و Oracle است؟ پس برای پایگاه‌داده‌های غیر رابطه‌ای نظیر MongoDB نمی‌توان از هایبرنیت استفاده کرد؟پاسخ. اگر منظور شما از هایبرنیت، Hibernate ORM است، باید به پرسش اول بگویم بله، Hibernate ORM فقط برای پایگاه‌داده‌های رابطه‌ای است نظیر همان‌هایی که گفتید و ده‌ها سیستم پایگاه‌داده‌ایِ رابطه‌ای دیگر! و به پرسش دوم باید بگویم خیر، نمیتوان از آن برای MongoDB استفاده کرد. ولی می‌توان از Hibernate OGM که برای نگاشت گراف‌ها طراحی شده است برای این منظور استفاده کرد.پرسش. آیا Hibernate OGM در این مقاله شرح داده‌ خواهد شد؟جواب. خیر، فقط فریمورک ORM را بررسی خواهیم کرد. به همین خاطر در هر کجای مقاله زمانی که صحبت از هایبرنیت است دقیقا منظورم Hibernate ORM می‌باشد. و به همین ترتیب، هرجا صحبت از پایگاه‌داده می‌کنم منظورم پایگاه‌داده‌های رابطه‌ای است.همانطور که عرض کردم، هایبرنیت مابین پایگاه‌داده و آبجکت‌های جاوا ایستاده است و عملیات Mapping را انجام می‌دهد. از آنجایی که دیگر ما نگران این نیستیم که چطور قرار است در پایگاه‌داده ذخیره‌سازی انجام دهیم، کاملا با رویکرد شی‌گرای خودمان کار را پیش می‌بریم. بدون هایبرنیت برنامه‌نویس خودش مجبور است که نگاشت بین آبجکت‌ها و موجودیت‌های سمت پایگاه‌داده را انجام دهد. و اینکار جدا از اینکه پیچیدگی‌های خود را می‌داشت، از نظر مفهومی ممکن بود اهرم فشاری باشد تا برنامه‌نویس در طول کار در بعضی موارد رویکرد شی‌گرا را از دست بدهد.https://www.javaguides.net/2018/12/what-is-difference-between-hibernate-and-spring-data-jpa.htmlپرسش. چرا نیاز به Mapping است؟پاسخ. ما باید اطلاعات برنامه‌ی خودمان را در پایگاه‌داده ذخیره کنیم. در سمت کد‌های جاوا، ما با مفاهیمی بنام property، کلاس و آبجکت طرف هستیم و در سمت پایگاه‌داده با مفاهیمی نظیر سطر، ستون، صفت، جدول و تاپل (tuple) طرف هستیم. به عبارت دیگر ما باید یک کلاس را به یک جدول تبدیل کنیم، و هر آبجکت را به تاپلی (سطر) از جدول نگاشت کنیم.جدا از این‌ها، ما در سمت جاوا، مفاهیم دیگری نظیر وراثت و ارتباط Composition را داریم، در سمت پایگاه‌داده مفاهیم ارتباط بین موجودیت‌ها و چندی رابطه (Cardinality) را داریم. برگردیم به سوال، چه نیازی به نگاشت است؟ ما باید اطلاعاتی که در سمت جاوا بدست می‌آید را درون پایگاه‌داده ذخیره کنیم و اطلاعاتی را که درون پایگاه‌داده ذخیره شده است را بتوانیم به کد‌های جاوا بیاوریم. چطور این‌ها را باید بهم نگاشت کرد؟ هایبرنیت این کار را برای ما انجام می‌دهد.از قابلیت‌های خوب دیگر که هایبرنیت به ما می‌دهد (بغیر از ORM که توضیح داده شد)، این است که میزان کدهای نوشته شده توسط برنامه‌نویس نسبت به jdbc به شدت کاهش پیدا می‌کند. اما از آن مهمتر اینکه هایبرنیت برنامه‌نویسی را کاملا از مدل دیتابیس مستقل می‌کند. این امکان فوق‌العاده به شما اجازه میده که بدون اندیشیدن به آن که قرار است با چه پایگاه‌داده‌ای کار کنید (Mysql باشد یا Postgresql؟ فرقی نمیکند) کد بزنید و نگران نوع نگاشت آن نباشید (بر عهده‌ی هایبرنیت است). بعبارتی شما می‌توانید بدون هیچ هزینه‌ای و فقط با تغییری کوچک در یک فایل تنظیمات، دیتابیس خود را بطور مثال از Oracle به MySQL تغییر دهید.و اما آخرین نکته‌ای که باید در قسمت فلسفه‌ی هایبرنیت گفت؛ در اصل هایبرنیت یک پیاده‌سازی (implementation) از یک استاندارد دیگر است. بعبارتی JPA  (یا همان Java Persistence API) یک استاندارد است که چگونگی نگاشت و تمام آنچه که مربوط به مبحث ORM می‌شود را در خودش مشخص کرده است، و هایبرنیت یک فریمورک است که این استاندارد را اجرا کرده است. فلذا ما هنگام استفاده از هایبرنیت ناگزیر هستیم JPA را نیز تاحدودی بشناسیم، و از آن استفاده کنیم. البته کاملا مشخص است که این دو، جدا از هم نیستند. JPA استاندارد ( یا به عبارتی interface) و هایبرنیت پیاده‌سازی (implementation) است.اطلاعات بیشتر راجع به JPA را در اینترنت جست‌وجو کنید. هرآنچه که این مقاله به آن نیاز داشته باشد را در خلل مثال‌ها توضیح خواهد داد، و دیگر توضیحی مستقل راجع به JPA نخواهیم داشت.می‌توان برای اطلاعات بیشتر به فصل ۴، فصل ۵ و فصل ۶ کتاب Beginning Java EE 7 نوشته‌ی آقای Goncalves مراجع کنید.راه‌اندازی هایبرنیتخب، تا اینجای کار توضیح دادم که هایبرنیت چیست و قرار است برای ما چکاری انجام دهد. حال باید راجع به چگونگی آن صحبت کنم.می‌خواهم خیلی زود و با کم‌ترین مقدمه دست‌بکار شویم، و خودمان را کمی درگیر کدنویسی کنیم. سپس اطلاعات بیشتر را بعد از آن ارائه دهم.فرایند راه‌اندازی هایبرنیت بصورت گام به گام!گام۰. آماده‌سازی محیطگام۱. اضافه کردن کتابخانه‌های موردنیاز به پروژه.گام۲. اضافه کردن فایل تنظیماتِ هایبرنیت به پروژه.گام۳. آماده کردن کلاس‌های جاوا برای نگاشت به دیتابیس.گام۴. اجرای عملیاتهای پایگاه‌داده‌ای.گام۰. آماده‌سازی محیطگام صفر را ساخت پروژه و آماده‌سازی دیتابیس درنظر بگیرید. برای من اهمیت ندارد که شما در چه محیطی کد می‌نویسید، یا روی چه پروژه‌ای کار می‌کنید. آیا وب‌اپلیکیشن می‌نویسید؟ یا یک برنامه‌ی کنسول؟ این‌ها در نوع استفاده از هایبرنیت اهمیت ندارد. گرچه بطور مثال اگر از Spring Boot استفاده می‌کنید می‌توانید از Starter های مروبط به JPA استفاده کنید و عملا گام ۱ را حذف کنید. ولی در هر حال پیشنهاد می‌کنم نوع کانفیگ را بیاموزید، تا بتوانید در هر نوع پروژه‌ای آن را بکار بگیرید. چنانچه از عهده‌ی این کار برنیامدید پس از خواندن این بخش (راه‌اندازی هایبرنیت) مشخصا نوع نیاز خودتان را سرچ کنید. بعنوان مثال هایبرنیت با Spring Boot.من برای مدیریت پروژه از Maven استفاده می‌کنم، پیشنهاد می‌کنم شما هم چنین کنید، یا از Gradle استفاده کنید. استفاده از Maven جدا از مزیت‌هایش، پروژه را مستقل از ide نیز می‌کند (بعبارتی IDE Agnostic). اگر نمی‌دانید Maven چیست در یوتیوب سرچ کنید و آن را یادبگیرید.پس پروژه‌ی خود را هر طور که صلاح می‌دانید بسازید. حال به سراغ پایگاه‌داده برویم.خب، از چه پایگاه‌داده‌ای استفاده کنیم؟ این بار هم هر طور صلاح می‌دانید تصمیم بگیرید. اگر از Oracle استفاده می‌کردید، الان هم می‌توانید استفاده کنید. تنها الزام این بخش آن است پایگاه‌داده باید رابطه‌ای باشد. تکرار می‌کنم هایبرنیت تقریبا تمام RDBMSها را پشتیبانی می‌کند. چنانچه از دیتابیس متداولی استفاده نمی‌کنید و نگران هستید که آیا هایبرنیت از آن پشتیبانی می‌کند یا خیر، می‌توانید در این باره نیز سرچ کنید. من از MySQL استفاده می‌کنم. حال باید یک پایگاه‌داده درون MySQL بمنظور استفاده در این پروژه ایجاد کنم. اینکه چطور این کار را می‌کنید هم باز بخودتان مربوط است، از محیط‌های گرافیکی استفاده می‌کنید یا دستوری، فرقی ندارد. عملا این بخش اصلا ارتباطی با هایبرنیت ندارد از لحاظ مفهومی. من دستور زیر را در کنسول MySQL می‌نویسم:CREATE DATABASE hibernate;خب تقریبا مطمئن هستم که تمام RDBMSها این دستور را پشتیبانی می‌کنند. بعد از آن که مطمئن شدم پایگاه‌داده به درستی ایجاد شده است به سراغ گام بعدی می‌رویم. برای مطمئن شدن نیز می‌توانید دستور زیر را بزنید تا لیستی از تمام دیتابیس‌ها داشته باشید. همراه باشید!SHOW DATABASES;گام۱. اضافه کردن کتابخانه‌های موردنیاز به پروژهطبعا نیاز است تا JAR فایل‌ها یا همان کتابخانه‌های هایبرنیت را به پروژه اضافه کنیم. برای اینکار می‌توان به صفحه‌ی هایبرنیت رفت و آن را دانلود کرد. توجه کنید که بهتر است نسخه‌ی stable را دانلود کنید. در زمان نگارش این مقاله نسخه‌ی ۵ پایدار است و نسخه‌ی ۶ در حال توسعه می‌باشد. زمانی که فایل zip را دانلود کردید، به پوشه‌ی lib روید. در آن پوشه یک پوشه‌ی دیگر بنام required وجود دارد. تمام آن JAR فایل‌هایی که ما برای پروژه‌ی خود نیاز داریم در آن وجود دارد. بعبارتی می‌توان از تمام آنچه دانلود کردیم فقط این پوشه را به class path خود اضافه کنیم. از چه محیطی برای کد زدن استفاده می‌کنید؟ eclipse یا IntelliJ IDEA؟ یا چه ابزاری برای مدیریت وابستگی‌ها؟ maven؟ gradle؟ هیچ‌کدام؟ قطعا بیاد دارید که گفتم این‌ها به این مقاله مرتبط نیست، بسته به نوع سلیقه و ذائقه‌ی خودتان رفتار کنید. من از IntelliJ و Maven استفاده می‌کند. بر اساس نوع سلیقه‌ی خودتان کتابخانه‌ای که به آن اشاره کردیم را به پروژه‌ی خودتان اضافه کنید. من از Maven استفاده می‌کنم درنتیجه به فایل POM در پروژه وابستگی زیر را اضافه می‌کنم:&lt;dependency&gt;
    &lt;groupId&gt;org.hibernate&lt;/groupId&gt;
    &lt;artifactId&gt;hibernate-core&lt;/artifactId&gt;
    &lt;version&gt;5.2.17.Final&lt;/version&gt;
&lt;/dependency&gt;خب یک چیز دیگر باقی مانده است. قطعا از بخش فلسفه‌ی هایبرنیت بخاطر دارید که اشاره کردم هایبرنیت در نهایت و دور از چشم ما، از JDBC استفاده می‌کند. فحوای کلامم این است که باید درایور مربوط به دیتابیس را هم اضافه کنیم (در این جا MySQL). ما نیاز به JAR فایلِ MySQL Connector داریم. آن را به هر شکل که می‌دانید به پروژه‌ی خودتان اضافه کنید. من هم باید فایل POM را با وابستگی زیر بروز کنم:&lt;dependency&gt;
    &lt;groupId&gt;mysql&lt;/groupId&gt;
    &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
    &lt;version&gt;8.0.11&lt;/version&gt;
&lt;/dependency&gt;خب بنظر دیگر کار تمام شد. ساده‌تر از آن چیزی بود که شاید فکرش را می‌کردید. اجازه دهید خلاصه کنم تا برای همیشه بیادتان بماند و سپس به سراغ گام بعدی برویم. در این بخش ۲ چیز نیاز داشتیم: کتاب‌خانه‌های مربوط به هایبرنیتکتاب‌خانه‌ی مربوط به اتصال jdbc به پایگاه‌دادهگام۲. اضافه کردن فایل تنظیماتِ هایبرنیت به پروژهو اما شروع کار با خود هایبرنیت. هایبرنیت برای این که بداند به کدام دیتابیس متصل شود و چه رفتاری داشته باشد، به یک فایل کانفیگ استناد می‌کند.ساختار پروژه تاکنونساختار پروژه‌ی من تاکنون چنین است. یک فایل xml بنام hibernate.cfg.xml در دایرکتوری resources ایجاد می‌کنم. این فایل hibernate.cfg.xml دقیقا همان فایل کانفیگی است که هایبرنیت به آن استناد می‌کند. شما اگر از maven استفاده نمی‌کنید در همان دایرکتوری src این فایل را بسازید.پرسش. آیا حتما نام این فایل باید hibernate.cfg.xml باشد؟ یا جایش حتما باید مشخص باشد؟پاسخ. خیر. میتوان نام دلخواهی برای آن گذاشت، یا در هر جایی از پروژه قرار داد. نام و مکان پیش‌فرض به ما کمک می‌کند تا بدون گفتن مکان و نام فایل، فریمورک هایبرنیت آن را پیدا کند. در غیراین صورت باید بطور شفاف به هایبرنیت بگوییم که فایل تنظیمات نامش چیست و در کجا قرار گرفته. در پروژه‌های معمولی مسیر پیش‌فرض src می‌باشد و نام hibernate.cfg.xml و در پروژه‌های maven نیز همچنین، با این تفاوت که مسیر پیش‌فرض دایرکتوری resources می‌باشد.و اما محتویات این فایل. همانطور که گفتم یک فایل تنظیمات xml است، که dtd مشخص دارد. من فایل را برای شما می‌گذارم و سپس اجزای آن را شرح می‌دهم.
&lt;!DOCTYPE hibernate-configuration PUBLIC
        &quot;-//Hibernate/Hibernate Configuration DTD 3.0//EN&quot;
        &quot;http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd&quot;&gt;
        
        
&lt;!--محتویات فایل hibernate.cfg.xml--&gt;


&lt;hibernate-configuration&gt;
    &lt;session-factory&gt;

        &lt;!-- JDBC Connection Settings --&gt;
        &lt;property name=&quot;connection.driver_class&quot;&gt;com.mysql.jdbc.Driver&lt;/property&gt;
        &lt;property name=&quot;connection.url&quot;&gt;jdbc:mysql://localhost:3306/hibernate?useSSL=false&lt;/property&gt;
        &lt;property name=&quot;connection.username&quot;&gt;newuser&lt;/property&gt;
        &lt;property name=&quot;connection.password&quot;&gt;password&lt;/property&gt;

        &lt;!-- Select SQL Dialect--&gt;
        &lt;property name=&quot;dialect&quot;&gt;org.hibernate.dialect.MySQL5Dialect&lt;/property&gt;
        

        &lt;!-- Hibernate Settings--&gt;
        &lt;property name=&quot;hbm2ddl.auto&quot;&gt;create&lt;/property&gt;
        &lt;property name=&quot;connection.pool_size&quot;&gt;20&lt;/property&gt;
        &lt;property name=&quot;hibernate.current_session_context_class&quot;&gt;thread&lt;/property&gt;

        &lt;property name=&quot;show_sql&quot;&gt;true&lt;/property&gt;


    &lt;/session-factory&gt;
&lt;/hibernate-configuration&gt;من دیگر اشاره‌ای به جزئیات نمی‌کنم که بطور مثال تنظیمات درون تگ hibernate-configuration قرار می‌گیرد و بعد از آن تنظیمات مورد نظر ما درون تگ session-factory است. یا نام propertyها را بیان کنم. لطفا تیزبینانه نگاه کنید و این فایل را کپی نکنید، بنویسید تا بهتر متوجه اتفاقات شوید.بخش اول، مربوط به تنظیمات اتصال به دیتابیس است. کلاس Driver و url و همچنین نام کاربری و پسورد را وارد می‌کنیم. توجه کنید که اطلاعات درایور و url برای اتصال به MySQL است، و اطلاعات نام‌کاربری و پسورد مربوط به سیستم پایگاهی من است (کپی نکنید!). قطعا متوجه شدید که اطلاعاتی را وارد می‌کنید در این بخش که همیشه هنگام کار با jdbc هم آن‌ها را استفاده می‌کردید.محتویات فایل تنظیمات بهمراه ساختار پروژه تاکنونبخش دوم، Dialect هایبرنیت است. اگر بخواهم خودمانی بگویم که اینجا چه چیز را تنظیم می‌کنیم اینطور می‌شود که به هایبرنیت می‌گوییم با چه لهجه‌ای با دیتابیس SQL ما صحبت کن. درست است که SQL استانداردی مشخص است، اما پیاده‌سازی آن در بین RDBMSها با کمی تفاوت سینتکسی همراه است. در این بخش این ابهامات را برطرف می‌کنیم (با مشخص کردن اینکه با لهجه‌ی MySQL صحبت کن).پرسش. من از اوراکل استفاده می‌کنم، چطور Dialect مربوط به آن را پیدا کنم؟پاسخ. جست‌وجو در اینترنت! لیست آنها در اینترنت هست.بخش‌های بعدی هم در رابطه با این است که هایبرنیت چطور رفتار کند. اجازه دهید از توضیح بیشتر بپرهیزیم، و به گام بعدی برویم. و بعد از آن وارد جزییات بیشتری می‌شویم.گام۳. آماده کردن کلاس‌های جاوا برای نگاشت به دیتابیسخب، عالی پیش آمدید. دیگر راه زیادی نمانده است. فقط مانده است که کلاس‌های جاوا را بنویسیم و آنها را برای نگاشت به دیتابیس آماده کنیم.اجازه دهید اینطور تصور کنیم که می‌خواهیم اطلاعات دانشجویان را در دیتابیس ذخیره کنیم. ما در دیتابیس جدول دانشجو خواهیم داشت که ستون‌های آن باید id، sname، stell و saddress باشد که به ترتیب شماره‌ی دانشجو، نام دانشجو، تلفن دانشجو و آدرس دانشجو هستند. در جاوا نیز قطعا کلاسی خواهیم داشت که این اطلاعات را میگیرد.دوستان عزیز، توجه خودتان را به آنچه از هایبرنیت و اتفاقات پیرامونش نمی‌دانید متمرکز نکنید. با من پیش بیاید و فقط به آنچه با خواندن این متن میفهمید تمرکز کنید! همه را جز به جز باز خواهیم کرد.پس در اینجا، من شروع می‌کنم کلاس Student را می‌سازم. اما این کلاس باید ویژگی‌های خاصی داشته باشد. دقیق‌تر، این کلاس باید یک POJO ساده باشد. برای نوشتن یک Plain Old Java Object چه چیزهایی الزامی است؟ تمام propertyها متدهای getter و setter داشته باشند.کلاس یک سازنده‌ی بدون آرگومان (no-args constructor) داشته باشد.پرسش. آیا باید این constructor را ساخت؟ تا جاییکه من میدانم خود کامپایلر چنین سازنده‌ای را ایجاد می‌کند.پاسخ. اگر هیچ سازنده‌ی دیگری در کلاس وجود نداشته باشد، کامپایلر چنین سازنده‌ی پیش‌فرضی را ایجاد می‌کند. اما چنان‌چه شما یک (یا چند) سازنده‌ی دیگر با آرگومان لحاظ کرده باشید، باید صراحتا خودتان یک سازنده‌ی بدون آرگومان ایجاد کنید.کلاس داشنجو باید مشابه زیر شده باشد:package entities;

public class Student {
    
    private int id;
    private String name;
    private String tell;
    private String address;

    
    public Student() {} // non-arg constructor
    
    // Setters &amp; Getters
    
}من مشخصه‌های تلفن و آدرس را نیز از نوع String در نظر گرفتم (برای سهولت کار). و کاملا مشخص است که از آوردن متدهای setter &amp; getter پرهیز شده است. شما برای خودتان باید بنویسید. کمی بعد عکسی از این کلاس خواهم گذاشت تا ابهامات احتمالی برطرف شود.خب، سوالی اساسی اینجا مطرح می‌شود. چطور باید به هایبرنیت بفهمانم که این کلاس باید به جدولی بنام student در دیتابیس تبدیل شود، و چطور اسم مشخصه‌ی name را sname بگذارد و چطور به دیتابیس بگوید که فیلد id را کلیداصلی (primary key) در نظر بگیرد؟! باید راهی باشد تا نقشه‌ی نگاشت را به هایبرنیت گفت، تا به کمک آن نقشه هایبرنیت عملیات نگاشت را انجام دهد.پیش‌تر ها چنین نقشه‌ای را در یک فایل دیگر xml قرار می‌دادند و هایبرنیت به کمک آن فایل میفهمید که با کلاس Student چطور باید رفتار کند. اینکه آیا اصلا این کلاس را باید به جدولی تبدیل کند؟ اگر بله، به چه نامی؟ اینکه چطور با فیلدهای آن رفتار کند، کلید اصلی کدام فیلد است، اسم فیلدها در دیتابیس چگونه باشد، و چه محدودیت‌هایی برای آن بگذارد و... را در آن فایل xml به هایبرنیت معرفی می‌کردیم.امروزه چکار می‌کنند؟ امروزه عمدتا از راه آسان‌تری استفاده می‌شود. از Annotationها استفاده می‌کنیم. با استفاده از Annotationها می‌توانیم تمام آن اطلاعات را خیلی آسان‌تر و شفاف‌تر به هایبرنیت بدهیم. پس وقت آن است که شروع به معرفی Annotationهای نگاشت کنیم.پرسش. Annotation چی هست؟پاسخ. Annotation همانطور که از ترجمه‌ی لغوی آن پیداست یکجور حاشیه نویسی است. می‌توان آن را به چشم متادیتا دید (اطلاعاتی اضافه). فریمورکی همچون هایبرنیت از این اطلاعات اضافه برای چگونگی نگاشت استفاده می‌کنند. استفاده از Annotationها بسیار رایج است، بخصوص در استفاده از فریمورک‌هایی نظیر Spring یا هایبرنیت. حتما در Java Core هم از Annotationهایی نظیر @Override استفاده کرده‌اید.اولین Annotationی که باید استفاده کنیم تا به هایبرنیت بگوید این کلاس باید تبدیل به یک موجودیت در دیتابیس شود، @Entity است. این Annotation به هایبرنیت می‌گوید که این کلاس را تبدیل به یک جدول کن (به محاوره‌ترین شکل ممکن گفتم!).تصویر زیر حالت کامل شده است، مشاهده کنید و به تمام Annotationهای آن توجه کنید.کلاس Studentخیلی خلاصه ادامه می‌دهم و بعد از گام۴ به آن برخواهم گشت و جز به جز توضیح خواهم داد.در خط ۶ به کمک @Table و شناسه‌ی name به هایبرنیت گفتم که می‌خواهم چه نامی برای این کلاس در دیتابیس درنظر بگیرد. @Table اختیاری است، چنانچه استفاده نکنید هایبرنیت نام کلاس را بعنوان اسم جدول قرار می‌دهد (که در این مثال هیچ فرقی ندارد!).در خط ۹ به هایبرنیت گفتم که فیلد id کلید اصلی من در جدول می‌باشد (با کمک @Id) و با @GeneratedValue هم وظیفه‌ی یکتا بودن کلید را به هایبرنیت سپردم. به این معنا که دیگر خود هایبرنیت وظیفه‌ی تولید id را به گردن دارد. (مشابه Auto Increment در دیتابیس).در خط‌های بعد از @Column استفاده کردم. استفاده از این Annotation اختیاری است. به این معنا که در نبودن آن هایبرنیت اطلاعات پیش‌فرض را قرار می‌دهد. بعنوان مثال در مورد فیلد address هیچ Annotationی لحاظ نشده است، به این خاطر هایبرنیت نام ستون را دقیقا با نام فیلد قرار می‌دهد. اما در مورد فیلد name به هایبرنیت گفته شده است که نام این ستون را sname قرار بده.نکته‌ی بسیار مهم در رابطه با این Annotationها این است که همه‌ی آنها از پکیج javax.persistence می‌باشد. بعبارتی این‌ها Annotationهای JPA هستند و نه Hibernate. البته هایبرنیت هم مشابه آنها را دارد، اما خود هایبرنیت توصیه می‌کند که از Annotationهای پکیج JPA استفاده بشود.شاید برایتان جالب باشد که هایبرنیت قدمت بیشتری از JPA دارد، بعبارتی ابتدا هایبرنیت متولد شد، با بخت و اقبال مواجه شد، به همین خاطر استانداردی (JPA) برای آن در نظر گرفتند.پرسش. من همچنان متوجه @Id نشدم. این Annotation صرفا کلیداصلی را مشخص می‌کند؟پاسخ. بله، همین است. چیز پیچیده‌ای نیست.پرسش. چطور بفهمم @GeneratedValue چه چیز تولید می‌کند؟ میتوانم بر آن مدیریت داشته باشم؟پاسخ. بله، میتوان مدیریت کرد. ولی بطور پیشفرض چند استراتژی از پیش تعیین شده هم برای استفاده گذاشته‌اند. پاسخ دقیق به این سوال را در این قسمت صلاح نمی‌دانم، فقط بدانید که بله می‌شود مدیریت کرد.پرسش. آیا می‌توانم فیلدی را کلیداصلی در نظر بگیرم ولی @GeneratedValue را استفاده نکنم؟پاسخ. بله می‌شود. آنگاه خودتان باید یکتایی مقدار را تضمین کنید.پرسش. اگر بخواهم چند فیلد را باهم بعنوان کلید نظر بگیرم، باید برای هرکدام از آن فیلد از @Id استفاده کنم؟پاسخ. خیر، ابدا. این موضوع را در قسمت دوم بخش کلیداصلی مرکب توضیح خواهم داد، تا آن زمان صبور باشید.خب اجازه دهید تمام کد را هم به اشتراک بگذارم و به سراغ گام بعد برویم.package entities;

import javax.persistence.*;

@Entity
@Table(name = &quot;student&quot;)
public class Student {

    @Id @GeneratedValue
    private int id;
    @Column(name = &quot;sname&quot;)
    private String name;
    @Column(name = &quot;stell&quot;)
    private String tell;
    private String address;


    public Student() {} // non-arg constructor

    public Student(String name, String tell, String address) {
        this.name = name;
        this.tell = tell;
        this.address = address;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        return id == student.id;
    }

    @Override
    public int hashCode() {
        return id;
    }

    @Override
    public String toString() {
        return &quot;Student{&quot; +
                &quot;id=&quot; + id +
                &quot;, name=&#039;&quot; + name + &#039;\&#039;&#039; +
                &quot;, tell=&#039;&quot; + tell + &#039;\&#039;&#039; +
                &quot;, address=&#039;&quot; + address + &#039;\&#039;&#039; +
                &#039;}&#039;;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getTell() {
        return tell;
    }

    public void setTell(String tell) {
        this.tell = tell;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}پرسش. چرا متدهای hashCode و equals هم ایجاد شده است؟پاسخ. این متدها به هایبرنیت در انجام تضمین یگانگی فیلد id کمک می‌کند. همانطور که ملاحظه می‌کنید متد equals فقط برای فیلد id ایجاد شده است. البته این کار الزامی نیست. یکجور سفت کاری است!پرسش. اجازه دهید! من دارم گیج می‌شوم. حجم مطالب زیاد شده است و شما پرحرفی زیادی میکنید. باعث شده است من گم کنم!پاسخ. حق دارید، ولی نگران نباشید. تنها یک گام دیگر تا معجزه باقی مانده است. کار زیادی تا الان انجام ندادیم، فقط مجبور به پرحرفی بودیم که موجب نگرانی شما شده است.تاکنون ما کتابخانه‌های موردنیاز را اضافه کردیم، یک فایل کانفیگ ساختیم و اطلاعات ارتباط با دیتابیس را وارد آن کردیم، و در نهایت یک کلاس جاوای خود را به کمک Annotationها به Entity تبدیل کردیم. و حالا هم میرویم که تست انجام دهیم. به همین سادگی. همراه باشید!گام۴. اجرای عملیاتهای پایگاه‌داده‌ایتست کردن و گام آخرهرآنچه که برای آزمودن هایبرنیت می‌بایست انجام دهیم در تصویر بالا مشخص است. در این تصویر من ابتدا یک شی از کلاس Student ایجاد کرده‌ام و سپس آن را درون دیتابیس ذخیره (save) کرده‌ام. خروجی را میخواهید در دیتابیس مشاهده کنید؟ عکس زیر را ملاحظه کنید.خروجی جدول دانشجو دقیقا همان است که انتظار داشتیم. دوستان من خسته نباشید. نفسی تازه کنید، اکنون دریافتید که هایبرنیت چطور کار می‌کند. اما هیچ توضیحی راجع به این کلاس‌ها و آبجکت‌های عجیب و غریب که در فایل Test است ندادیم. محتویات کلاس Test را مشاهده کنید:package entities;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class Test {

    public static void main(String[] args) {

        SessionFactory sessionFactory = new Configuration().configure()
                .addAnnotatedClass(Student.class)
                .buildSessionFactory();

        Session session = sessionFactory.getCurrentSession();

        Student student = new Student(&quot;Mohamad&quot;, &quot;09121112233&quot;, &quot;Tehran&quot;);

        session.beginTransaction();

        session.save(student);

        session.getTransaction().commit();

        session.close();
        sessionFactory.close();
    }
}کلاس SessionFactory فایل تنظیمات را می‌خواند (به کمک کلاس Configuration) و یک آبجکت سنگین (به این خاطر که تنها یکبار ساخته می‌شود) ایجاد می‌کند. در خط ۱۴ من به تنظیمات این را اضافه کردم که به کلاس Student نگاه کند. این کار باید صورت بگیرد، در غیر این صورت هایبرنیت این کلاس را برای نگاشت درنظر نمی‌گیرد.کلاس Session ارتباط jdbc را Wrap کرده است. کلاس اصلی برای ذخیره و بازیابی اطلاعات از پایگاه داده است. شی این کلاس از کلاس SessionFactory گرفته می‌شود.سپس یک شی از کلاس Student ایجاد کردیم (بنام student)، و در نهایت تراکنشی را به کمک متد   beginTransaction از کلاس Session شروع کردیم و پس از آن به کمک متد save از کلاس Session مقدار student را درون دیتابیس ذخیره کردیم. و در آخر تراکنش را commit کردیم.پرسش. متوجه آنچه شما گفتید شدم! اما سوالات فراوانی دارم.پاسخ. کاملا طبیعی است، بخشی از آن سوالات را می‌توانم حدس بزنم چه‌ها هستند. توضیح خواهم داد. اما در قسمت بعد!باید همین اتفاقی که اینجا رخ داد را خیلی بیشتر بسط داد.لطفا نظر خودتون رو کامنت کنید، آیا توانستید از این مطلب استفاده کنید؟حمایت معنوی از گروه داده‌کاوی دایکه</description>
                <category>محمد عباسی</category>
                <author>محمد عباسی</author>
                <pubDate>Wed, 24 Apr 2019 16:04:38 +0430</pubDate>
            </item>
            </channel>
</rss>