سلام با یکی دیگر از آموزش های برنامه نویسی جاوا در خدمت شما هستیم در این قسمت قصد داریم نحوه ی استفاده از Transaction ها و jdbc در جاوا را بررسی کنیم.
کاربران عزیز لازم است قبل از مطالعه ی درس مدیریت تراکنش های JDBC در جاوا آشنایی لازم با پایگاه داده و اتصال به انواع آن با استفاده از کدهای جاوا را داشته باشند.
Transaction یا تراکنش مجموعه ای از عملیات هایی مانند Insert و Update و Create و... است که بر روی پایگاه داده و در قالب یک واحد منطقی عمل می کنند.
سوال: منظور از انجام عملیات در قالب یک واحد چیست؟
جواب: تراکنش های مالی یک بانک را در نظر بگیرید. شما زمانی که با کارت خود خریدی انجام می دهید مبلغی مشخصی از حساب شما کسر و همان مبلغ عینا به حساب فروشنده واریز می شود. اجازه دهید ساده ترین حالت را در نظر بگیریم: در اینجا ما دو دستور SQL از نوع Update خواهیم داشت که یکی بر روی حساب شما و دیگری بر روی حساب فروشنده اعمال خواهد شد. اگر هر دو عمل به طور همزمان و بدون خطا اجرا شوند اجازه ی اعمال نهایی بر روی پایگاه داده را خواهد داشت.
برای درک بهتر عکس زیر را در نظر بگیرید:
همان طور که مشاهده می کنید در اینجا چهار عملیات SQL وجود دارند که همگی در قالب یک تراکنش مشخص شده اند، یعنی تا زمانی که هر چهار عملیات به درستی انجام نشده باشند اجازه ی اعمال نتیجه بر روی پایگاه داده صادر نخواهد شد. می بینید که در این مورد هر چهار عمل دارای تیک سبز هستند یعنی به درستی و بدون مشکل اجرا شده اندT پس می توان انجام تغییرات در پایگاه داده را نهایی کرد.
در مقابل آن عکس زیر را درنظر بگیرید:
در این مورد یکی از چهار عملیات به درستی انجام نشده است بنابراین سه عملیاتی که به طور موقت انجام شده اند، همگی به حالت قبل از انجام کد SQL بازمی گردند. در ادامه با مفهوم Commit و Rollback آشنا خواهیم شد.
تراکنش ها دارای خواص مشخصی هستند که به شرح زیر می باشند:
Atomicity: یک تراکنش یا باید کامل انجام شود یا اصلا انجام نشود. مثلا از n کوئری در تراکنش ما وجود دارد تمامی n کوئری باید بدون مشکل اجرا شوند، در غیر این صورت تمامی کوئری ها Rollback خواهند شد.
Consistency: تضمین می کند که با انجام هر تراکنش پایگاه داده، از یک حالت سازگار به یک حالت سازگار دیگر برود.
Isolation: تضمین می کند که با اجرای همزمان تراکنش ها، سلامت پایگاه داده حفظ شود این مورد دارای سطح های مختلفی است که در کدنویسی از آن ها استفاده می کنیم و در ادامه با آن ها آشنا خواهیم شد.
Durability: ذخیره ی تغییرات قبل از انجام تراکنش به منظور تغییر وضعیت پایگاه داده به قبل از انجام تراکنش در زمان Crash پایگاه داده.
TRANSACTION_READ_UNCOMMITTED: بسیار ناامن بوده به طوری که تمامی دیتاهای non Commit شده قابل دستیابی و مشاهده هستند.
TRANSACTION_READ_COMMITTED: در این حالت تغییرات انجام شده فقط در داخل تراکنش قابل مشاهده است به عبارتی دیگر هر تراکنش بر روی ردیفی که قرار است تغییرات ایجاد کند یک قفل می گذارد و بقیه ی تراکنش ها در صف باقی می مانند تا تکلیف State مشخص شود و تا زمانی که تراکنش Commit نشده امکان مشاهده ی اطلاعات وجود ندارد.
TRANSACTION_REPEATABLE_READ: در این حالت بر روی تمامی ردیف های موجود در تراکنش قفل گذاشته می شود، حتی ردیف هایی که فقط عمل Select بر روی آن ها انجام می شود.
TRANSACTION_SERIALIZABLE: امن ترین و البته سنگین ترین حالت است که بار زیادی به سیستم وارد می کند به طوری که در زمان انجام هر تراکنش کل Table قفل می شود و تا پایان مشخص شدن State در این وضعیت باقی می ماند.
مهم ترین و بهترین سطحی که عموما استفاده می شود TRANSACTION_READ_COMMITTED است که در قسمت کدنویسی از آن استفاده خواهیم کرد.
قبل از انجام هر کاری لازم است با استفاده از جاوا به پایگاه داده متصل شویم که آموزش آن در این لینک 1 و لینک 2 آورده شده و از هر کدام که مایل بودید می توانید استفاده کنید. بنده در این آموزش قصد دارم به پایگاه داده MySQL در نرم افزار Wampserver متصل شوم.
یک پایگاه داده با نام مناسب و یک جدول با نام Bankservice با ستون های زیر ایجاد کنید و با استفاده از Netbeans یک پروژه ی جاوا بوجود آورده و به پایگاه داده متصل شوید.
تذکر: حتما قبل از ادامه ی کار از اتصال به پایگاه داده مطمئن شوید.
خب اجازه دهید ابتدا یک کلاس کمکی برای ایجاد کانکشن بین پایگاه داده و جاوا ایجاد کنیم که کد آن به صورت زیر خواهد بود. آن را در یک کلاس جاوا پیاده سازی کنید تا در جای مناسب از آن استفاده کنیم:
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class MyConnection { private String databaseDriver="com.mysql.jdbc.Driver"; private String databaseConnectionUrl="jdbc:mysql://localhost:3308/db"; private String databaseUserName="root"; private String databasepassword=""; private MyConnection(){ } private static MyConnection myConnection=null; public Connection GetConnection(){ Connection con=null; try { Class.forName(databaseDriver); con= DriverManager.getConnection(databaseConnectionUrl, databaseUserName, databasepassword); } catch (ClassNotFoundException | SQLException ex) { System.err.println(ex.getMessage()); return null; } return con; } public static synchronized MyConnection getNewInstance(){ if (myConnection == null){ myConnection = new MyConnection(); } return myConnection; } }
تذکر: فراموش نکنید که درایور لازم برای اتصال به پایگاه داده را در قسمت Libraries پروژه اضافه کنید من در اینجا از این درایور استفاده کردم.
در این مثال ساده قصد داریم مبلغی از حساب با ID=1 برداشته و به حساب با ID=2 واریز کنیم. برای این منظور لازم است دو رکورد در جدولی که ایجاد کرده اید اضافه کنید و متناسب با نوع داده ی فیلد جدول، آن را مقدار دهی کنید.
کد تراکنش به شرح زیر می باشد. آن را به پروژه ی خود اضافه کرده و به توضیحات خط به خط آن دقت کنید:
package transaction; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class Transaction { public static void main(String[] args) throws SQLException{ String withDrawQuery="UPDATE bankservice SET Balance = Balance-1000 WHERE ID=1 AND Name='Olivia'"; String depositQuery="UPDATE bankservice SET Balance = Balance+1000 WHERE ID=2 AND Name='Emma'"; PreparedStatement withDrawStatement=null; PreparedStatement depositStatement=null; MyConnection databaseConnection = MyConnection.getNewInstance(); Connection conn = databaseConnection.GetConnection(); try { Statement st = conn.createStatement(); ResultSet rsB = st.executeQuery("SELECT * FROM bankservice"); System.err.println("Before Change"); while (rsB.next()) { System.out.println("Name= "+rsB.getString("Name")+"\t"+"Balance= "+rsB.getInt("Balance")); } System.out.println("*********************************************"); conn.setAutoCommit(false); conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); withDrawStatement = conn.prepareStatement(withDrawQuery); depositStatement = conn.prepareStatement(depositQuery); withDrawStatement.executeUpdate(); depositStatement.executeUpdate(); conn.commit(); ResultSet rsA = st.executeQuery("SELECT * FROM bankservice"); System.err.println("After Change"); while (rsA.next()) { System.out.println("Name= "+rsA.getString("Name")+"\t"+"Balance= "+rsA.getInt("Balance")); } } catch (SQLException ex) { conn.rollback(); System.err.println("Transaction with rollback was encountered"); }finally{ if(depositStatement !=null && withDrawStatement !=null && conn !=null){ depositStatement.close(); withDrawStatement.close(); conn.close(); } } } }
در خطوط 9 و 10 کوئری های لازم برای انجام تراکنش را به صورت String پیاده سازی کرده ایم که دو دستور ساده ی SQL بوده که اولی عملیات برداشت و دومی عملیات واریز را شبیه سازی می کند.
در خطوط 11 و 12 نیز دو شی از نوع PreparedStatement
برای execute کوئری ها و تغییر بر روی جدول تعریف کردیم که در جای مناسب از آن ها استفاده خواهیم کرد. در ادامه با استفاده از کلاس کمکی که قبلا ایجاد کردیم به کانکشن دست پیدا می کنیم. برای این که تغییرات بر روی جدول به راحتی قابل مشاهده باشند قبل از انجام هرگونه تغییر، اطلاعات جاری را از جدول خوانده و نمایش می دهیم که این کار در خطوط 16 تا 22 انجام شده است.
طبق اصل Atomicity با استفاده از متد setAutoCommit
اجازه ی Commit شدن خودکار اطلاعات را False می کنیم و تا زمانی که از انجام شدن همه ی تراکنش ها اطمینان حاصل نکردیم، اجازه ی Commit صادر نخواهد شد.
حال لازم است نوع Isolation را که تراکنش مطابق آن عمل می کند، برای برنامه تعیین کنیم.
به این منظور از متد setTransactionIsolation
استفاده می کنیم. قبلا گفتیم که حالت TRANSACTION_READ_COMMITTED در اکثر موارد نیاز ما را بر طرف می کند بنابراین نوع Isolation را از همین نوع تعیین می کنیم که این کار در خط 24 انجام شده است. از خطوط 25 تا 28 کوئری هایی که قبلا نوشته بودیم را اجرا و به وسیله ی متد executeUpdate
به جدول اعمال می کنیم.
اگر در هنگام اجرای یکی از این دو کوئری مشکلی در execute شدن کدهای SQL پیش آید اجرای برنامه به catch
منتقل شده و به وسیله ی متد rollback
تمامی تغییرات موقتی که انجام شده به حالت قبل از انجام تراکنش باز می گردند، هیچ گونه تغییری در جدول ایجاد نخواهد شد و کاربر با پیغام مناسب از این عمل مطلع می شود (تراکنش Rollback می کند) اما اگر هر دو کوئری بدون مشکل اجرا شوند خط 29 اجرا شده و با استفاده از متد commit
اجازه ی اعمال تغییرات نهایی بر روی جدول صادر می گردد. برای این که تغییرات بعد از انجام تراکنش قابل مشاهده باشند خطوط 30 تا 34 پیاده سازی شده اند. در نهایت در بخش finally
کلیه بخش ها Close خواهند شد.
کل عملیات بالا را می توان در عکس زیر خلاصه کرد:
خب دوستان عزیز این بخش از آموزش هم به پایان رسید هدف از این بخش آشنایی اولیه با تراکنش های jdbc در جاوا و پیاده سازی آن ها بود امیدوارم از این آموزش بهره ی کافی را برده باشید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.