تراکنش های پایگاه داده یا Database Transaction یک مفهوم اساسی در سیستم های مدیریت پایگاه داده (DBMS) است که یکپارچگی و سازگاری داده ها را در یک محیط چند کاربره تضمین می کند. در این مقاله میخواهیم درباره ترنزکشن در لاراول صحبت کنیم.
همینطور به شما توصیه می کنیم برای یادگیری صفر تا صد لاراول این دوره را مشاهده بفرمایید.
Transaction یا تراکنش یا ترنزکشن در MySQL یک گروه متوالی از عبارات، کوئری ها یا عملیاتی مانند SELECT، INSERT، UPDATE یا DELETE است تا به عنوان یک واحد کاری انجام شود.
تراکنش ها یا Transaction ها به عنوان ساختار ACID شرح داده می شوند. در ادامه مخفف عبارت ACID را توضیح می دهیم:
اکنون اگر می خواهید از تراکنش ها در MySQL استفاده کنید، به ذخیره سازی موتور InnoDB نیاز دارید.
InnoDB موتور پیش فرض و پرکاربردترین موتور ذخیره سازی در MySQL است. این موتور تراکنش های سازگار با ACID، پشتیبانی از کلید خارجی و قفل در سطح ردیف را فراهم می کند و آن را برای طیف گسترده ای از برنامه ها مناسب می کند.
MySQL از تراکنش های تو در تو پشتیبانی نمی کند، اما موتور InnoDB از نقاط ذخیره مختلف پشتیبانی می کند.
با استفاده از DB Clouser ها می توانید ترنزکشن در لاراول را به صورت زیر پیاده سازی کنید.
use Illuminate\Support\Facades\DB; DB::transaction(function () { DB::insert('insert on orders'); DB::update('update users set votes = 1'); DB::delete('delete from posts'); }, 5); // <= This is used to handle Deadlocks, and is the number of tries.
با استفاده از این روش، نیازی به نگرانی در مورد شروع تراکنش، commit کردن و بازگشت در صورت بروز مشکل ندارید، همه این کارها را به صورت خودکار انجام می دهد. شما همچنین می توانید به عنوان آرگومان دوم تعداد دفعات تکرار را که تعداد دفعاتی که یک تراکنش باید در زمان شکست در اجرا، انجام شود را مشخص کنید.
برای شروع یک ترنزکشن در لاراول، از متد ()beginTransaction
ارائه شده توسط ویوی DB استفاده کنید. هنگامی که یک تراکنش شروع شد، تمام عملیات های بعدی پایگاه داده که با استفاده از ویوی DB یا Eloquent ORM اجرا می شوند، در آن تراکنش گنجانده می شوند تا زمانی که commit شود یا برگشت داده شود.
پس از اجرای موفقیت آمیز تمامی عملیات پایگاه داده در یک تراکنش، می توانید با استفاده از متد ()commit
تغییرات را در پایگاه داده انجام دهید.
اگر در طول هر عملیاتی استثنا رخ دهد، می توانید از متد ()rollback
برای بی تاثیر کردن تغییرات ایجاد شده در تراکنش استفاده کنید.
use Illuminate\Support\Facades\DB; try { DB::beginTransaction(); // <= Starting the transaction // Update user's balance DB::table('users')->where('id', 1)->decrement('balance', 100); // Insert a new order $orderID = DB::table('orders')->insertGetId([ 'user_id' => 1, 'amount' => 100, ]); // Insert a new order history DB::table('order_history')->insert([ 'order_id' => $orderID, 'status' => 'pending', ]); DB::commit(); // <= Commit the changes } catch (\Exception $e) { report($e); DB::rollBack(); // <= Rollback in case of an exception }
همانطور که در بالا ذکر کردیم، لاراول از نقاط ذخیره یا save point ها (در صورت پشتیبانی توسط موتور دیتابیس) استفاده می کند. ویژگی ای که تراکنش ها را مدیریت می کند در آدرس زیر قرار دارد: Illuminate/Database/Concerns/ManagesTransactions.php
. در این مقاله، ما فقط به ()startTransaction()، commit
و ()rollback
می پردازیم.
/** * Start a new database transaction. * * @return void * * @throws \Throwable */ public function beginTransaction() { $this->createTransaction(); // <== create transaction $this->transactions++; $this->transactionsManager?->begin( $this->getName(), $this->transactions ); $this->fireConnectionEvent('beganTransaction'); } /** * Create a transaction within the database. * * @return void * * @throws \Throwable */ protected function createTransaction() { if ($this->transactions == 0) { $this->reconnectIfMissingConnection(); try { $this->getPdo()->beginTransaction(); } catch (Throwable $e) { $this->handleBeginTransactionException($e); } } elseif ($this->transactions >= 1 && $this->queryGrammar->supportsSavepoints()) { $this->createSavepoint(); // <= create savepoint } } /** * Commit the active database transaction. * * @return void * * @throws \Throwable */ public function commit() { if ($this->transactionLevel() == 1) { $this->fireConnectionEvent('committing'); $this->getPdo()->commit(); // <= if it's last transaction commit it } $this->transactions = max(0, $this->transactions - 1); // <= decrement transaction if ($this->afterCommitCallbacksShouldBeExecuted()) { $this->transactionsManager?->commit($this->getName()); } $this->fireConnectionEvent('committed'); } /** * Rollback the active database transaction. * * @param int|null $toLevel * @return void * * @throws \Throwable */ public function rollBack($toLevel = null) { // We allow developers to rollback to a certain transaction level. We will verify // that this given transaction level is valid before attempting to rollback to // that level. If it's not we will just return out and not attempt anything. $toLevel = is_null($toLevel) ? $this->transactions - 1 : $toLevel; if ($toLevel < 0 || $toLevel >= $this->transactions) { return; } // Next, we will actually perform this rollback within this database and fire the // rollback event. We will also set the current transaction level to the given // level that was passed into this method so it will be right from here out. try { $this->performRollBack($toLevel); } catch (Throwable $e) { $this->handleRollBackException($e); } $this->transactions = $toLevel; $this->transactionsManager?->rollback( $this->getName(), $this->transactions ); $this->fireConnectionEvent('rollingBack'); }
اگر دقت کنید ()DB::beginTransaction
یک تراکنش جدید ایجاد می کند، اگر تراکنش وجود نداشته باشد، یا اگر در حال حاضر یک تراکنش موجود باشد، آن را افزایش می دهد و یک ذخیره (در صورت پشتیبانی) ایجاد می کند.
از طرف دیگر ()commit
و ()rollback
تعداد تراکنش ها (savepoint) را کاهش می دهند و اگر آخرین مورد باشد، تمام کوئری ها را انجام می دهند.
تراکنش های تو در تو در لاراول اینگونه عمل می کنند
استفاده از تراکنش های درون یک حلقه می تواند کمی پیچیده تر از استفاده از تراکنش ها برای عملیات های تکی باشد. هنگامی که با حلقه ها و تراکنش ها سر و کار دارید، باید مرزهای تراکنش را به دقت مدیریت کنید تا از ثبات و عملکرد داده ها اطمینان حاصل کنید.
بیایید حالتی را در نظر بگیریم که در آن شما یک حلقه دارید که مجموعهای از آیتمها را پردازش میکند و باید یک عملیات پایگاه داده برای هر آیتم در یک تراکنش انجام دهید.
کاری که ما انجام می دهیم این است که بررسی کنیم که آیا مقدار یک آیتم صفر است یا خیر، و اگر این طور است، از پردازش آن صرف نظر می کنیم و به آیتم بعدی در آرایه می رویم. اگر صفر نباشد، stock را تغییر می دهیم و تراکنش را انجام می دهیم.
کد بالا یک مشکل را نشان می دهد. :(
فرض کنید یک آرایه با 3 عنصر داریم.
$item = [ [ 'id' => 1, 'quantity' => 8, ], [ 'id' => 2, 'quantity' => 0, ], [ 'id' => 3, 'quantity' => 2, ], ];
DB::beginTransaction();
بنابراین این تعداد تراکنش را به 1 افزایش می دهد، زیرا مقدار آن صفر نیست (آیتم با id => 1)، $item[‘quantity’] === 0
. ما به به تغییر stock ادامه می دهیم و سپس این تراکنش را با استفاده از آن انجام می دهیم. DB::commit();
. این تعداد تراکنش را به 0 کاهش می دهد.id => 2
). ما تراکنش را شروع می کنیم و این تعداد تراکنش را به 1 افزایش می دهد. حالا چون مقدار این آیتم صفر است، $item[‘quantity’] === 0
از این مرحله می گذریم و به تکرار بعدی می رویم. به یاد داشته باشید، چون ما ()commit
یا ()rollback
را انجام ندادیم، تعداد تراکنش ها هنوز 1 است.id => 3
). تراکنش را شروع می کنیم و این تعداد تراکنش را به 2 افزایش می دهد (یکی برای آیتم قبلی و یکی برای آیتم فعلی). این حالت به این دلیل اتفاق میافتد که در مرحله قبل، تراکنش را با فراخوانی ()DB::commit
یا ()DB::rollBack
تکمیل نکردیم. پس از بررسی مقداری که 0 نیست، تغییر stock و سپس انجام تراکنش ادامه می دهیم، اما commit تحت عنوان یک commit واقعی برای پایگاه داده انجام نمی شود، فقط counter تراکنش را از 2 به 1 کاهش می دهد.هنگامی که تعداد تراکنش و تعداد commit/rollback برابر نشدند، هیچ تغییری در DB ایجاد نخواهد شد.
به خاطر داشته باشید که هر تراکنش باز شده باید با یک ()commit
یا ()rollback
، به خصوص در حلقه ها انجام شود.
با توجه به مطالب ارائه شده در این مقاله، نکات کلیدی در مورد مدیریت ترنزکشن در لاراول به شرح زیر است:
DB::beginTransaction()
استفاده میشود. تمام عملیاتهای بعدی پایگاه داده تا زمان commit یا rollback شدن، بخشی از آن تراکنش خواهند بود.DB::commit()
و برای باطل کردن تغییرات از DB::rollback()
استفاده میشود.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.