<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های mahdi shirinabadi</title>
        <link>https://virgool.io/feed/@mahdishirinabadi</link>
        <description>کسی که خودش رو سنیور نمیدونه ولی بقیه میدونن!</description>
        <language>fa</language>
        <pubDate>2026-04-14 18:37:35</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/147522/avatar/avatar.png?height=120&amp;width=120</url>
            <title>mahdi shirinabadi</title>
            <link>https://virgool.io/@mahdishirinabadi</link>
        </image>

                    <item>
                <title>تفاوت مقیاس‌پذیری افقی و عمودی</title>
                <link>https://virgool.io/@mahdishirinabadi/%D8%AA%D9%81%D8%A7%D9%88%D8%AA-%D9%85%D9%82%DB%8C%D8%A7%D8%B3-%D9%BE%D8%B0%DB%8C%D8%B1%DB%8C-%D8%A7%D9%81%D9%82%DB%8C-%D9%88-%D8%B9%D9%85%D9%88%D8%AF%DB%8C-cvtaqnjm0ztc</link>
                <description>سلام به همه‌ی برنامه‌نویس‌ها، دولوپرها و دوستان عزیز DevOps!یکی از پر تکرارترین مفاهیمی که تقریباً در تمام مصاحبه‌های فنی، طراحی سیستم‌ها و معماری میکروسرویس‌ها با آن روبه‌رو می‌شویم، بحث مقیاس‌پذیری (Scalability) است.اما سؤال اصلی اینجاست:مقیاس‌پذیری افقی و عمودی دقیقاً یعنی چه؟و چرا اغلب این دو را با هم قاطی می‌کنیم؟🧠 قانون طلاییافقی = تعداد بیشترعمودی = قدرت بیشترفقط همین دو خط را در ذهنت نگه دار، چون کلید درک همه‌ی ماجراست!🚗 مثال روزمره برای درک بهترفرض کن می‌خواهی ۱۰۰ نفر را از تهران به شمال ببری.دو انتخاب داری:راه اول (افقی):۱۰ تا پیکان قدیمی می‌خری و هر کدام ۱۰ نفر را سوار می‌کنی.این یعنی تعداد وسیله‌ها را زیاد کردی → مقیاس‌پذیری افقیراه دوم (عمودی):یک اتوبوس VIP می‌خری که همان ۱۰۰ نفر را با راحتی کامل و وای‌فای می‌برد.یعنی قدرت همان وسیله را بالا بردی → مقیاس‌پذیری عمودیدیدی چقدر ساده است؟💻 حالا بیایید وارد دنیای نرم‌افزار شویمفرض کن یک اپلیکیشن Spring Boot داری که ۱۰۰۰ کاربر هم‌زمان دارد.مقیاس‌پذیری افقی:۱۰ تا سرور جدید راه می‌اندازی و جلوی آن‌ها یک Load Balancer قرار می‌دهی.هر سرور بخشی از درخواست‌ها را پاسخ می‌دهد.👉 تعداد سرورها زیاد شد → افقیمقیاس‌پذیری عمودی:به جای چند سرور، همان سرور فعلی را ارتقا می‌دهی:مثلاً از ۸ گیگ رم به ۶۴ گیگ، یا از ۴ هسته CPU به ۳۲ هسته.👉 قدرت همان سرور افزایش یافت → عمودی⚙️ مثال از Kafkaفرض کن از Kafka برای پردازش لاگ‌های کاربران استفاده می‌کنی.افقی: تعداد پارتیشن‌ها را از ۴ به ۱۶ افزایش می‌دهی و ۳ تا broker جدید اضافه می‌کنی.حالا سیستم می‌تواند میلیون‌ها پیام در ثانیه را مدیریت کند.→ مقیاس‌پذیری افقیعمودی: همان broker را با یک سرور جدید جایگزین می‌کنی که SSD NVMe و ۱۲۸ گیگ رم دارد.→ مقیاس‌پذیری عمودی🗄️ مثال از دیتابیسفرض کن یک دیتابیس MySQL داری که کم‌کم کند شده است.افقی:یک Read Replica اضافه می‌کنی تا کوئری‌های SELECT به آن بروند.یا دیتابیس را Sharding می‌کنی (هر گروه از کاربران روی سرور جدا).→ مقیاس‌پذیری افقیعمودی:سرور دیتابیس را ارتقا می‌دهی (CPU، RAM، SSD سریع‌تر).→ مقیاس‌پذیری عمودی🧩 ترفند حافظه (Mnemonic)افقی (Horizontal) → با حرف H شروع می‌شود → «هزار تا سرور»عمودی (Vertical) → با حرف V شروع می‌شود → «یکی ولی قوی»⚡ تست ۲ ثانیه‌ای — ببین درست بلدی یا نه!۱. ۵ تا سرور جدید راه انداختم → ✅ افقی۲. رم سرور را از ۱۶ به ۶۴ گیگ افزایش دادم → ✅ عمودی۳. پارتیشن‌های Kafka را زیاد کردم → ✅ افقی۴. CPU سرور RabbitMQ را ارتقا دادم → ✅ عمودیاگه همه را درست گفتی، تبریک! 🎉 تو کاملاً مفاهیم رو درک کردی.🏁 قانون طلایی نهاییاگر تعداد چیزی را زیاد کردی → افقیاگر قدرت همان چیز را بالا بردی → عمودی🎯 چرا دانستن این موضوع مهم است؟در طراحی سیستم، مقیاس‌پذیری اساس پایداری و رشد اپلیکیشن است.در مصاحبه‌های شغلی هم معمولاً ازت می‌پرسند:«اگر ترافیک بالا بره، چطور سیستم رو scale می‌کنی؟»اگر بگویی: «سرور را قوی‌تر می‌کنم» → یعنی عمودیاگر بگویی: «چند تا instance دیگر راه می‌اندازم» → یعنی افقیاشتباه در این مفاهیم ممکن است باعث شود مصاحبه‌کننده فکر کند درک دقیقی از معماری سیستم نداری.✍️ جمع‌بندی در ۲ خطافقی = اضافه کردن سرور / پارتیشن / ماشینعمودی = قوی‌تر کردن همان سرور / ماشین</description>
                <category>mahdi shirinabadi</category>
                <author>mahdi shirinabadi</author>
                <pubDate>Sun, 09 Nov 2025 11:37:49 +0330</pubDate>
            </item>
                    <item>
                <title>افزایش امنیت در احراز هویت</title>
                <link>https://virgool.io/@mahdishirinabadi/%D8%A7%D9%81%D8%B2%D8%A7%DB%8C%D8%B4-%D8%A7%D9%85%D9%86%DB%8C%D8%AA-%D8%AF%D8%B1-%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-izypwoyzjjr2</link>
                <description>کلمه PKCE مخفف Proof Key for Code Exchange(&quot;پیکسی&quot;) یک افزونه امنیتی برای پروتکل OAuth 2.0 است که به‌طور خاص برای افزایش امنیت در سناریوهای کلاینت‌های عمومی (Public Clients) مانند برنامه‌های موبایل، برنامه‌های تک‌صفحه‌ای (Single Page Applications) یا برنامه‌های دسکتاپ طراحی شده است. این مکانیزم برای جلوگیری از حملات تزریق کد احراز هویت (Authorization Code Interception Attack) استفاده می‌شود.چالش موجود چی هستش؟جریان کد احراز هویت (Authorization Code Flow)جریان Authorization Code Grant یکی از رایج‌ترین جریان‌های OAuth 2.0 است که برای برنامه‌هایی که می‌توانند راز کلاینت (Client Secret) را به‌صورت امن ذخیره کنند (مانند برنامه‌های سمت سرور) طراحی شده است.مراحل کلی این جریان:کلاینت (برنامه که درخواست دسترسی به اطلاعات کاربر رو داره) کاربر را به سرور احراز هویت (Authorization Server) هدایت می‌کند تا اجازه دسترسی بدهد.سرور احراز هویت یک کد احراز هویت (Authorization Code) تولید می‌کند و آن را از طریق پارامتر redirect_uri به کلاینت بازمی‌گرداند.کلاینت این کد را به همراه شناسه کلاینت (Client ID) و راز کلاینت (Client Secret) به سرور احراز هویت ارسال می‌کند تا توکن دسترسی (Access Token) دریافت کند.مشکلات کلاینت‌های عمومیکلاینت‌های عمومی (مانند اپلیکیشن‌های موبایل یا SPAها) نمی‌توانند راز کلاینت را به‌صورت امن ذخیره کنند، زیرا کد منبع آن‌ها (مانند جاوااسکریپت در مرورگر) در دسترس است یا می‌توان آن را از دستگاه استخراج کرد.بدون راز کلاینت، هر برنامه مخربی که بتواند کد احراز هویت  (code) را رهگیری کند (مثلاً از طریق redirect_uri جعلی یا شنود)، می‌تواند آن را به سرور احراز هویت ارسال کرده و توکن دسترسی دریافت کند.این آسیب‌پذیری به حمله تزریق کد احراز هویت معروف است.چگونه PKCE این مشکل را حل می‌کند؟یک لایه امنیتی اضافی اضافه می‌کند تا اطمینان حاصل شود که تنها کلاینتی که درخواست اولیه را شروع کرده است، می‌تواند کد احراز هویت را برای دریافت توکن دسترسی استفاده کند. این کار با استفاده از یک جفت کلید موقت (Code Verifier و Code Challenge) انجام می‌شود که در ادامه توضیح میدهیم.اجزای کلیدی PKCEشماره یک (Code Verifier)یک رشته تصادفی با امنیت بالا (High-Entropy) است که توسط کلاینت تولید می‌شود.طول آن باید بین 43 تا 128 کاراکتر باشد و می‌تواند شامل حروف، اعداد و برخی کاراکترهای خاص (مانند -, ., _, ~) باشد.مثال: E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cMشماره دو (Code Challenge)یک تبدیل رمزنگاری‌شده از Code Verifier است که به سرور احراز هویت ارسال می‌شود.دو روش برای تولید Code Challenge وجود دارد:Plain: Code Challenge = Code Verifier (بدون تغییر).S256: Code Challenge = Base64URL(SHA256(Code Verifier)). شماره سه (Code Challenge Method)مشخص می‌کند که Code Challenge چگونه تولید شده است (plain یا S256).سرور احراز هویت از این اطلاعات برای اعتبارسنجی استفاده می‌کند.روش S256 به دلیل امنیت بالاتر ترجیح داده می‌شود.مقدارCode Verifier فقط در کلاینت ذخیره می‌شود و هرگز به سرور ارسال نمی‌شود (جلوگیری از افشا).مقدار Code Challenge به سرور ارسال می‌شود و به‌عنوان یک &quot;چک‌پوینت&quot; عمل می‌کند تا سرور بتواند تأیید کند که کلاینتی که کد احراز هویت را ارائه می‌دهد، همان کلاینتی است که درخواست اولیه را شروع کرده است.نحوه کار PKCEمرحله 1: تولید Code Verifier و Code Challengeکلاینت یک Code Verifier تصادفی تولید می‌کند.کلاینت از Code Verifier یک Code Challenge تولید می‌کند:اگر روش plain باشد: Code Challenge = Code Verifier.اگر روش S256 باشد: Code Challenge = Base64URL(SHA256(Code Verifier)).کلاینت Code Verifier را به‌صورت محلی ذخیره می‌کند.مرحله 2: ارسال درخواست احراز هویتکلاینت کاربر را به سرور احراز هویت هدایت می‌کند و پارامترهای زیر را در URL درخواست اضافه می‌کند:response_type=code (مشخص‌کننده جریان کد احراز هویت).client_id (شناسه کلاینت).redirect_uri (آدرس بازگشت).scope (دامنه دسترسی‌های موردنظر).state (برای جلوگیری از حملات CSRF).code_challenge (Code Challenge تولیدشده).code_challenge_method (معمولاً S256 یا plain).GET https://authorization-server.com/auth?  response_type=code&amp;client_id=your_client_id&amp;redirect_uri=https://yourapp.com/callback&amp;scope=user_profile&amp;state=xyz123&amp;code_challenge=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk&amp;code_challenge_method=S256مرحله 3: دریافت کد احراز هویتکاربر در سرور احراز هویت لاگین می‌کند و دسترسی را تأیید می‌کند.سرور احراز هویت یک کد احراز هویت تولید می‌کند و آن را همراه با state به redirect_uri کلاینت بازمی‌گرداندhttps://yourapp.com/callback?code=abc123&amp;state=xyz123سرور احراز هویت Code Challenge و Code Challenge Method را برای استفاده در مرحله بعدی ذخیره می‌کند.مرحله 4: تبادل کد احراز هویت با توکن دسترسیکلاینت کد احراز هویت را به همراه Code Verifier به سرور احراز هویت ارسال می‌کند:POST https://authorization-server.com/tokenContent-Type: application/x-www-form-urlencodedgrant_type=authorization_code&amp;code=abc123&amp;redirect_uri=https://yourapp.com/callback&amp;client_id=your_client_id&amp;code_verifier=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
سرور احراز هویت:مقدارCode Verifier  را با استفاده ازCode Challenge Method بررسی می‌کند:برایS256: محاسبه می‌کند Base64URL(SHA256(code_verifier)) و آن را باCode Challenge ذخیره‌شده مقایسه می‌کند. برایplain: مستقیماً Code Verifier را با Code Challenge مقایسه می‌کند.اگر تطابق داشته باشد، سرور توکن دسترسی (و احتمالاً توکن تازه‌سازی) را صادر می‌کندچرا PKCE امن است؟از حملات تزریق کد احراز هویت جلوگیری می‌کند، زیرا:رهگیری کد احراز هویت بی‌فایده است: حتی اگر یک مهاجم کد احراز هویت را از redirect_uri بدزدد، بدون دانستن Code Verifier نمی‌تواند توکن دسترسی دریافت کند.مقدار Code Verifier مخفی است: این مقدار فقط در کلاینت ذخیره می‌شود و هرگز در شبکه ارسال نمی‌شود (به‌جز در مرحله تبادل توکن با اتصال امن HTTPS).یک‌بارمصرف بودن: Code Verifier و Code Challenge برای هر درخواست احراز هویت جدید تولید می‌شوند، بنابراین نمی‌توان آن‌ها را دوباره استفاده کرد.روش S256: استفاده از هش SHA256 و رمزگذاری Base64URL باعث می‌شود که استخراج Code Verifier از Code Challenge عملاً غیرممکن باشد.</description>
                <category>mahdi shirinabadi</category>
                <author>mahdi shirinabadi</author>
                <pubDate>Sun, 09 Nov 2025 07:26:46 +0330</pubDate>
            </item>
                    <item>
                <title>مشکل هم زمانی Jobها در سیستم های توزیع شده (Distributed) قسمت اول</title>
                <link>https://virgool.io/@mahdishirinabadi/%D9%85%D8%B4%DA%A9%D9%84-%D9%87%D9%85-%D8%B2%D9%85%D8%A7%D9%86%DB%8C-job%D9%87%D8%A7-%D8%AF%D8%B1-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85-%D9%87%D8%A7%DB%8C-%D8%AA%D9%88%D8%B2%DB%8C%D8%B9-%D8%B4%D8%AF%D9%87-distributed-%D9%82%D8%B3%D9%85%D8%AA-%D8%A7%D9%88%D9%84-bwa5dsxqaiak</link>
                <description>سلام، خیلی وقتها ما نیاز داریم تا کارهایی رو به صورت خودکار انجام بدیم و برای همین نیاز داریم تا یک job برای ان تعریف کنیم، مثلا ما سیستم خرید انتقال وجه مینویسیم و بعضی از تراکنشهای ما امکان داره که از سرور ارایه دهنده TimeOut بشه (مثلا شبکه دچار اشکال بشه یا مشکل فایروال داشته باشه و ....) در این حالت تراکنش ما به حالت suspend میره و ما باید یک سیستم خودکار تهیه کنیم تا به صورت مرتبط تا زمان تعیین تکلیف تراکنش وضعیت تراکنش رو استعلام کنه. برای همین یک مثال واقعی در دنیای برنامه نویسی رو با هم مینویسیم.1- سناریو: ما یک سامانه انتقال وجه داریم که اطلاعات کارت مبدا (شماره کارت - CVV2-  تاریخ انقضا - رمز یکبار مصرف)، مبلغ مورد نظر و شماره کارت مقصد رو میگریم و سرویس انتقال وجه رو از شرکت ارایه دهنده سرویس (بانک) فراخوانی میکنیم، در صورتی که همه چیزی خوب باشد ما سرویس رو فراخوانی میکنیم و پول از حساب مبدا کم و به حساب مقصد واریز میشه و ما نتیجه رو در جواب سرویس میگیریم (خیلیم خوب) حالا فرض کنیدما سرویس رو فراخوانی کردیم ولی هیچ جوابی نگرفتیم حالا باید چیکار کنیم و از کجا مطمین بشیم که الان وضعیت تراکنش چیه؟؟؟؟راه حل: در این حالت ما باید تراکنش رو به وضعیت suspend بروزرسانی کنیم و یک سرویس خودکار بنویسیم که هر چند وقت یکبار برود و این تراکنشها رو استعلام کند و بر اساس نتیجه استعلام وضعیت تراکنش رو نهایی کنیم (یا انجام شده یا نشده)بریم برای کد نویسی:تو قسمت دوم میریم برای کد نویسی</description>
                <category>mahdi shirinabadi</category>
                <author>mahdi shirinabadi</author>
                <pubDate>Thu, 31 Oct 2024 14:24:32 +0330</pubDate>
            </item>
                    <item>
                <title>کتاب برای استفاده از گیت</title>
                <link>https://virgool.io/@mahdishirinabadi/%DA%A9%D8%AA%D8%A7%D8%A8-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%DA%AF%DB%8C%D8%AA-zyafevfnuxtd</link>
                <description>سلام تو هر سازمانی که بخواهند یک کد رو مدیریت کنند معمولا از یک سیستم کنترل نسخه (Version Control System) استفاده میکنند که یکی از معروف ترین و محبوب ترین آنها گیت (Git) هستش (Git یک پروژه منبع باز (open source) بالغ و فعال است که در سال 2005 توسط لینوس توروالدز خالق مشهور سیستم عامل لینوکس ساخته و توسعه یافت.) که ما با چند تا دستور اون میتونیم کلی از کارهای روزمره رو انجام دهیم (مثلا Clone - Pull-Push-Commit-...) ولی برای حرفه ای تر شدن و استفاده از همه امکانات بهتره که کتاب زیر رو بخونیم پشنهاد میکنم تو هر تیم که هستیم این کتاب رو بخونیم.BookName: Git Essentials developer&#x27;s Guide to Gitاگه کتاب رو پیدا نکردین یه ایمیل تو کامنت بزارید براتون بفرستم</description>
                <category>mahdi shirinabadi</category>
                <author>mahdi shirinabadi</author>
                <pubDate>Sat, 20 Jul 2024 12:26:57 +0330</pubDate>
            </item>
                    <item>
                <title>ذخیره تغییرات رکورد جدول در پایگاه داده در Spring boot and Hibernate</title>
                <link>https://virgool.io/@mahdishirinabadi/%D8%B0%D8%AE%DB%8C%D8%B1%D9%87-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1%D8%A7%D8%AA-%D8%B1%DA%A9%D9%88%D8%B1%D8%AF-%D8%AC%D8%AF%D9%88%D9%84-%D8%AF%D8%B1-%D9%BE%D8%A7%DB%8C%DA%AF%D8%A7%D9%87-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AF%D8%B1-spring-boot-and-hibernate-scte88qmyjwd</link>
                <description>سلام دوبارهچند روز پیش در حین انجام پروژه نیاز داشتم تا تغییرات بعضی از جدول ها رو بتونم نگه دارم تا بعدا بتونم بهشون مراجعه کنم. مثال خیلی واقعی این طوری میشه:من تو برنامم به جدول به اسم setting دارم که از طریق کنسول مدیریتی قابل تغییر هستش، و برای اینکه بفهمم چه کسی در چه زمانی و چه اطلاعاتی رو عوض کرده چند راه بیشتر ندارم:1- نوشتن تغییرات در فایل ( که جستجو کردن تو اون سخت هستش و باید بدونید کدوم فایل رو باید بگردید)۲- ایجاد یک جدول در دیتابیس و نوشتن تغیییرات در اون جدول به صورت دستی۳- استفاده از JPAدر برنامه۴- استفاده از hibernate enversما در اینجا میخوایم در مورد hibernate envers و نحوه استفاده از اون تو spring boot توضیح بدهیمخب خیلی کار سختی اصلا نیست و به جای توضیحات اضافه بریم کار رو شروع کنیم:من تو مقاله قبلی نحوه ایجاد یک پروژه رو توضیح داده بودم که میتونید ببینید بعد از اینکه پروژه رو ایجاد کردید library زیر را اضافه کنید:&lt;dependency&gt;
    &lt;groupId&gt;org.hibernate&lt;/groupId&gt;
    &lt;artifactId&gt;hibernate-envers&lt;/artifactId&gt;
    &lt;version&gt;6.1.4.Final&lt;/version&gt;
&lt;/dependency&gt;حالا باید entity رو که میخوایم روش تغییرات رو اعمال کنیم باید انتخاب کنیم که ما در این مثال میخوایم روی جدول setting این کار رو انجام بدیم. برای این کار اصلا کار سختی نداریم فقط ما @Audited رو بالای کلاس باید اضافه کنیم و اگه از flyway تو پروژه داریم استفاده میکنیم باید جدول ها رو طبق روال flyway بسازیم:CREATE TABLE revinfo (
                         rev INTEGER AUTO_INCREMENT,
                         revtstmp BIGINT,
                         PRIMARY KEY (rev)
);خب این جدول اصلی هستش برای نگه داشتن تغییرات، و به نوعی مهمترین جدول برای ذخیره اطلاعات.CREATE TABLE setting
(
    id         BIGINT            NOT NULL AUTO_INCREMENT,
    name       VARCHAR(100)      NOT NULL,
    value   VARCHAR(100)      NOT NULL,
    primary key (id)
);
ALTER TABLE setting ADD CONSTRAINT uc_setting_name UNIQUE (name);حالا جدول برای ذخیره تغییرات جدول settingCREATE TABLE setting_aud (
                             id bigint NOT NULL,
                             name VARCHAR(50),
                             value VARCHAR(50),
                             rev INTEGER NOT NULL,
                             revtype tinyint NOT NULL,
                             PRIMARY KEY (id, rev)
);خب کار ما تا ایحا تموم شد و وقتی تغییراتی رو اعمال کنید مشاهده میکنید که همه تغییرات داره تو جدول ثبت میشه، اطلاعات مربوط له REVTYPE به شرح زیر هستش:0-Add
1-Update
2-Deleteخب حالا باید entity مربوط به setting رو درست کنیم.import org.hibernate.envers.AuditTable;
import org.hibernate.envers.Audited;

import javax.persistence.*;

@Table(name = &amp;quotsetting&amp;quot)
@Entity
@Audited
@AuditTable(&amp;quotsetting_aud&amp;quot)
public class Setting {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = &amp;quotid&amp;quot, nullable = false)
    private Long id;

    @Column(unique = true, length = 100, nullable = false)
    private String name;

    @Column(unique = false, length = 100, nullable = false)
    private String value;

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}همانطور که مشخص هستش ما فقط دو تا annotation  به نام های (@Audited و @AuditTable ) رو اضافه کردیم , و هیج تغییر دیگه تو برنامه ندادیم!Git address: https://github.com/mahdiShirinabadi/envers_testخب تو پروژه میام که controller برای تست تعریف میکنیم و متد ها رو تست میکنیم.import com.mahdi.evenrs.example.model.Setting;import com.mahdi.evenrs.example.model.SettingRepository;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import javax.servlet.http.HttpServletRequest;import java.util.Optional;@RestController@RequestMapping(&quot;/v1/&quot;)public class EndPoint {    @Autowired    SettingRepository settingRepository;    @PostMapping(path = &quot;/new&quot;)    public @ResponseBody    String create(HttpServletRequest httpRequest,@RequestParam(value = &quot;name&quot;, required = false) String name, // resid digital                      @RequestParam(&quot;value&quot;) String value) {        Setting setting = new Setting();        setting.setName(name);        setting.setValue(value);        settingRepository.save(setting);        return &quot;ok&quot;;    }    @PostMapping(path = &quot;/update&quot;)    public @ResponseBody    String update(HttpServletRequest httpRequest,@RequestParam(value = &quot;id&quot;, required = false) String id,@RequestParam(value = &quot;name&quot;, required = false) String name, // resid digital                      @RequestParam(&quot;value&quot;) String value) {        Optional&lt;Setting&gt; setting = settingRepository.findById(Long.parseLong(id));        setting.get().setName(name);        setting.get().setValue(value);        settingRepository.save(setting.get());        return &quot;update&quot;;    }    @PostMapping(path = &quot;/delete&quot;)    public @ResponseBody    String delete(HttpServletRequest httpRequest,@RequestParam(value = &quot;id&quot;, required = false) String id, // resid digital                  @RequestParam(&quot;value&quot;) String value) {        Optional&lt;Setting&gt; setting = settingRepository.findById(Long.parseLong(id));        settingRepository.delete(setting.get());        return &quot;delete&quot;;    }}فایل SettingRepository هم میسازیم که بتونیم تو متد controller ازش استفاده کنیمimport org.springframework.data.repository.CrudRepository;import org.springframework.stereotype.Repository;@Repositorypublic interface SettingRepository extends CrudRepository&lt;Setting, Long&gt; {}پروژه رو اجرا میکنیم و با postman اون رو اجرا میکنیم.متد ها رو اجرا کردیم و حالا جدول setting_aud رو میبینیم.همانظور که دیدن مشخص هستش که هر رمورد در چه زمانی اجرا شده است.خب تو قسمت بعدی مبحث پیشرفته رو میگم خدمتتون.موفق باشید، فعلا</description>
                <category>mahdi shirinabadi</category>
                <author>mahdi shirinabadi</author>
                <pubDate>Fri, 28 Oct 2022 10:59:34 +0330</pubDate>
            </item>
                    <item>
                <title>حل مشکل نبودن زیر نویس انگلیسی در فیلم های به زبان انگلیسی</title>
                <link>https://virgool.io/@mahdishirinabadi/%D8%AD%D9%84-%D9%85%D8%B4%DA%A9%D9%84-%D9%86%D8%A8%D9%88%D8%AF%D9%86-%D8%B2%DB%8C%D8%B1-%D9%86%D9%88%DB%8C%D8%B3-%D8%A7%D8%AA%DA%AF%D9%84%DB%8C%D8%B3%DB%8C-%D8%AF%D8%B1-%D9%81%DB%8C%D9%84%D9%85-%D9%87%D8%A7%DB%8C-%D8%A8%D9%87-%D8%B2%D8%A8%D8%A7%D9%86-%D8%A7%D9%86%DA%AF%D9%84%DB%8C%D8%B3%DB%8C-i2oyk83kibws</link>
                <description>خب خیلی وقتها ما وقتی فیلم اموزشی به زبان انگلیسی رو دانلود میکنیم این فیلم ها زیر نویس ندارن و تو بعضی وقتها نیاز داریم که با زیر نویس بفهمیم چی میگن، یه راه اضافه کردن این هستش که اون فیلم رو بریم توی یوتیوب آپلود کنیم و زیر نویس رو دانلود کنیم که با سرعت اینترنت ما یه هفت هشت ساعتی طول میکشه، حالا یه راه حل نسبتا خوب دیگه هم هست (حدود 90 درصد رو درست تشخیص میده)روی chrome یه قابلیت به نام Live Caption وجود داره که باید اون رو فعال کنید،حال فیلم رو با chrome اجرا کنید و میبینید که زیر نویس اضافه شده.البته نسخه کروم باید از 85 به بالا باشه.موفق باشید </description>
                <category>mahdi shirinabadi</category>
                <author>mahdi shirinabadi</author>
                <pubDate>Fri, 22 Jul 2022 11:09:26 +0430</pubDate>
            </item>
                    <item>
                <title>مهاجرت های پایگاه داده در SpringBoot با flyway</title>
                <link>https://virgool.io/@mahdishirinabadi/%D9%85%D9%87%D8%A7%D8%AC%D8%B1%D8%AA-%D9%87%D8%A7%DB%8C-%D9%BE%D8%A7%DB%8C%DA%AF%D8%A7%D9%87-%D8%AF%D8%A7%D8%AF%D9%87-%D8%AF%D8%B1-springboot-%D8%A8%D8%A7-flyway-v3vqniwo4fk1</link>
                <description>به نام خداتا حالا شده بعد از اینکه دیتا بیس و جدول ها رو ایجاد کردین بخواهین به تغییری تو یه فیلد ازجدول بدین؟مثلا یه ستون اضافه کنین یا یکی حذف کنید؟ یا بخواید نوع (Data Type) رو عوض کنین؟برای اینکه این کارها رو به صورت دستی ایجاد نکنیم  و استرس اینکه تعییر در محیط Development و production یکسان باشند  بیاید با هم این مقاله رو بخونیم :)تو سیستمهایی کوچیک و بزرگ که برای سازمان ها مختلف مینویسیم تغییرات در جداول پایگاه داده یه چیزه خیلی روال هستش چون بر اساس نیاز کارفرما ما باید تغییرات رو اعمال کنیم و برای اینکه کارمون راحت تر باشه و استرس اینکه تعییر در محیط Development و production یکسان باشند نیاز داریم تا از یک کتابخانه هایی استفاده کنیم که این کار رو برای ما راحت کنند. تو زبان جاوا کتابخانه های زیادی وجود دارن که بعضی از مهمترین هاش اینا هستن: flyway, liquibaseخب ما تو این مقاله میخوایم از flyway تو زبان جاوا و فریمورک spring boot استفاده کنیم.خب دیگه تئوری کافیه و بریم به صورت عملی این کار رو با هم انجام بدیم :)یه چیزم بگم اولش که من خیلی خوب نمینویسم :) اگه جایی رو خوب متوجه نشدین بهم بگین که توضیح بدم یا متن رو اصلاح کنم.برای شروع کافیه موارد زیر رو داشته باشیم1- یک دیتابیس mysql یا mariadb 2-یک IDE که انتخاب من Intellij هستش و شما میتونین با هر IDE دیگه خواستین کار کنین3- یک پروژه spring bootبرای اینکه یه پروژه spring boot ایجاد میکنیم، به این آدرس  میریم و یه پروژه میسازیم ( البته از راههای دیگه هم میشه ساخت ولی من با این روش راحتترم) طبق عکس زیر Group, Artiface,Name packageName رو مشخص میکنیم (هر اسمی که دوست داشتین بزارین فعلن مهم نیست) و همچنین از maven برای Build پروژه استفاده مکنیم.بر روی دکمه سمت چپ ADD DEPENDENCIES کلیک میکنیم تا  کتابحانه های Spring Data JPA, Flyway Migration,Spring Web,MySQL Driverرو به پروژه اضافه کنیم و نسخه جاوا رو هم مشخص میکنیم من در حال حاضر از جاوا 11 دارم استفاده میکنم.یجاد پروژه در سایت start.spring.ioحالا باید رو دکمه GENERATE که پایین عکس مشخص هستش کلیک کنیم و پروژه دانلود میشه. ( به همین راحتی)وقتی که دانلود پروژه تموم شد اون رو تو مسیری که میخواید کپی کنید و بعدش اون رو از حالت فشرده خارج کنید، داخل پروژه برید و فایلهای زیر رو باید ببینید.محتویات فایل pom.xml به صورت زیر هستش:&lt;?xml version=&amp;quot1.0&amp;quot encoding=&amp;quotUTF-8&amp;quot?&gt;&lt;project xmlns=&amp;quothttp://maven.apache.org/POM/4.0.0&amp;quot xmlns:xsi=&amp;quothttp://www.w3.org/2001/XMLSchema-instance&amp;quot	xsi:schemaLocation=&amp;quothttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd&amp;quot&gt;	&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;	&lt;parent&gt;		&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;		&lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;		&lt;version&gt;2.6.5&lt;/version&gt;		&lt;relativePath/&gt; &lt;!-- lookup parent from repository --&gt;	&lt;/parent&gt;	&lt;groupId&gt;com.mahdi&lt;/groupId&gt;	&lt;artifactId&gt;flyway&lt;/artifactId&gt;	&lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;	&lt;name&gt;flyway&lt;/name&gt;	&lt;description&gt;Demo project for use flyway in Spring Boot&lt;/description&gt;	&lt;dependencies&gt;		&lt;dependency&gt;			&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;			&lt;artifactId&gt;spring-boot-starter-data-jpa&lt;/artifactId&gt;		&lt;/dependency&gt;		&lt;dependency&gt;			&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;			&lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;		&lt;/dependency&gt;		&lt;dependency&gt;			&lt;groupId&gt;org.flywaydb&lt;/groupId&gt;			&lt;artifactId&gt;flyway-core&lt;/artifactId&gt;		&lt;/dependency&gt;		&lt;dependency&gt;			&lt;groupId&gt;mysql&lt;/groupId&gt;			&lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;			&lt;scope&gt;runtime&lt;/scope&gt;		&lt;/dependency&gt;		&lt;dependency&gt;			&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;			&lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;			&lt;scope&gt;test&lt;/scope&gt;		&lt;/dependency&gt;	&lt;/dependencies&gt;	&lt;build&gt;		&lt;plugins&gt;			&lt;plugin&gt;				&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;				&lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;			&lt;/plugin&gt;		&lt;/plugins&gt;	&lt;/build&gt;&lt;/project&gt;
تو intellij برای اینکه یه پروژه رو باز کنید تنها کافیه رو فایل Pom.xml کلیک راست کنید و بزنید RunWith و اسم برنامه Intellij IDEA رو انتخاب کنید بعدش به شما به پیام میده مینی بر اینکه این پروژه از نظر شما trust هست که شما گزینه trust رو انتخاب کنید.احتمالا بعدش یه مدت طول میکشه که برنامه کتابخانه ها رو دانلود کنه و بعد اینکه دانلود تموم بشه این صفحه رو باید ببینید.نمایش پروژهخب الان دیگه مراحل ایجاد پروژه تموم شد و کار ما تازه شروع شد :)ایتدا یه DataBase با اسم دلخواه تو mysql میسازیم. (چون یکم نصب انواع DataBase  تو ویندوز وقت گیره من اصولا یه VMWARE رو ویندوز نصب میکنم و داخل اون انواع و اقسام سیستم عامل ها رو دارم - شما هم هرجور که راحتین انجام بدین و به نظر من اصلا هم تعصب تکنولوژی هم نداشته باشین) در حال حاصر من یه Rocky linux دارم که رو اون mysql رو نصب کردم.اگه هنوز رو سیستمتون Mariadb or mysql نصب نکردین از اینجا  اون رو روی سیستم عاملهای از نوع redhat نصب کنید و از اینجا برای debian نصب کنید.با دستور زیر به پایگاه داده وصل میشیمmysql -uroot -pو دستور ایجاد database رو میزنیم و اسم flyway_test رو انتخاب کردیم ( شما هر اسمی دوست داشتس انتخاب کن)CREATE DATABASE flyway_test DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;حالا باید یک کاربر بسازیم که برنامه بتونه به پابگاه داده وصل بشه. اسم کاربر رو میزاریم app و با رمز App@123GRANT ALL PRIVILEGES ON &#x60;flyway_test&#x60;.* TO &#x27;app&#x27;@&#x27;%&#x27;  IDENTIFIED BY &#x27;App@123&#x27;;حالا باید دستور FLUSH PRIVILEGES رو بزنیم تا تغییرات بر روی پایگاه داده ایجاد بشه.حالا بر اطمینان از اینکه همه چیز درست انجام شده باشه از mariadb با دستور quit میام بیرون و دوباره با کاربر app به mariadb لاگین میکنیم.خب الان باید بریم سر وقت کدمون تا بهش بگیم به کدوم پایگاه داده وصل بشه.برای اینکار در فایل  application.properties  اطلاعات زیر را وارد میکنیمserver.port=8080spring.datasource.url=jdbc:mysql://192.168.231.128:3306/flyway_test?serverTimezone=UTC&amp;useSSL=falsespring.datasource.username=appspring.datasource.password=App@123## Hibernate propertiesspring.jpa.hibernate.use-new-id-generator-mappings=falsespring.jpa.hibernate.ddl-auto=validatespring.jpa.show-sql=true خب الان باید اطلاعات مربوط به کانفیگ flyway هم تو فایل  application.properties برازیم#flywayConfigspring.flyway.enabled=truespring.flyway.url=jdbc:mysql://192.168.231.128:3306/flyway_test?serverTimezone=UTC&amp;useSSL=falsespring.flyway.user=appspring.flyway.password=App@123spring.flyway.group=trueنمای کلی فایل application.properties در عکی زیر مشخص هستشapplication.propertiesآدرس 192.168.231.128 مربوط به سرور پایگاه داده من هستش که برای شما مسلما با این فرق داره، اگر روی کامپیوتر خودتون هستش باید localhost باشه و اصولا پورت پیش فرض پایگاه داده 3306 هستش.خب حالا برای اینکه به پروژه بگیم وقتی داری میای بالا باید چه تعییراتی تو پایگاه داده بدی تو مسیر زیر باید فایل با مشخصات زیر بسازیم.src/main/resources/db/migrationخب اسم فایلی که میسازیم باید از الگو زیر پیروی کنه:قسمت اول: باید حرف V انگلیسی باشد (V بزرگ)قسمت دوم:شماره نسخه تعییرات باشد. این شماره میتونه 1- 001-1.2.3-2029.67.786.4 و .... باشد.قسمت سوم: دو عدد underscores  به صورت پیوستهقسمت چهارم: توضیح مختصر در مورد تغییراتقسمت پنجم: پسوند .sqlقسمت های یک و سه و پنج میتونن از طریق تنظمات عوض بشنspring.flyway.sql-migration-prefix=T
spring.flyway.sql-migration-separator=--
spring.flyway.sql-migration-suffixes=.blog 1.0--create_users_table.blog  اسم فایل با استفاده از تنظمات جدیدکه ما از این تنظمات چشم پوشی میکنیم.خب حالا میایم یه  folder  به اسم  model میسازیم و داخل اون یه کلاس به اسم Author میسازیم.package com.mahdi.flyway.model;

import javax.persistence.*;
import java.util.Date;

@Table(name = &amp;quotauthor&amp;quot)
@Entity
public class Author{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = &amp;quotid&amp;quot, nullable = false)
    private Long id;

    @Column(unique = true, length = 100, nullable = false)
    private String name;

    @Column(length = 100, nullable = false)
    private String password;

    @Column(nullable = false, columnDefinition = &amp;quottinyint not null default 0&amp;quot)
    private boolean enabled;

    @Column(updatable = false)
    private Date createdAt;

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public Date getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }
}برای اینکه بخوایم همین جدول در پایگاه داده هم ساخته بشه با استفاده از الگو و در مسیر اعلام شده فایلی به اسم زیر میسازیمV1__create_authers_table.sqlو محتویات زیر را داخل فایل میزاریم: CREATE TABLE author
(
    id         BIGINT            NOT NULL AUTO_INCREMENT,
    name       VARCHAR(100)      NOT NULL,
    password   VARCHAR(100)      NOT NULL,
    enabled    TINYINT DEFAULT 0 NOT NULL,
    created_at datetime          NULL,
    updated_at datetime          NULL,
    CONSTRAINT pk_authors PRIMARY KEY (id)
);

ALTER TABLE author ADD CONSTRAINT uc_authors_name UNIQUE (name);خب کار ما فعلن تموم شد و حالا اگه پروژه رو اجرا کنیم باید جدول author را به صورت خودکار بسازه.همانطور که در عکس بالا میبینید متن زیر نشان دهنده این هستش که جدول با موفقیت ساخته شده استSuccessfully applied 1 migration to schema `flyway_test`, now at version v1برای اطمینان میتونبد دستور show tables رو بزنید، دو تا جدول ساخته شده که یکی از اونها flyway_schema_history هستش که برای نگه داشتن تاریخچه migrate هستش.اگه خواستین تعییرات دیگه هم بدین باید اسم فایل به ترتیب بره بالا مثلا فایل بعدی V2__create_user_table.sql باشه.نکته اول: بعد از اینکه فایل با موفقیت ایجتد شد به هیچ عنوان محتویات فایل رو عوض نکنید ،بعد از اجرای فایل یه checksum از فایل گرفته میشه و داخل جدول در فیلد checksum قرار داده میشه.نکته دوم: کلی دستور برای flyway هستش که میتونید از اینجا برید و بخونید و بر اساس نیازتون اون رو اجرا کنید.نکته مهم: همیشه بر یادگیری بهتر همیشه خودتون یه بار پروژه رو بسازین تا بهتر متوجه بشین.آدرس گیت پروژه:https://github.com/mahdiShirinabadi/flyway_testپایدار باشید</description>
                <category>mahdi shirinabadi</category>
                <author>mahdi shirinabadi</author>
                <pubDate>Thu, 31 Mar 2022 18:24:34 +0430</pubDate>
            </item>
            </channel>
</rss>