مدیریت تراکنش های JDBC در جاوا

java-jdbc-transaction

سلام با یکی دیگر از آموزش های برنامه نویسی جاوا در خدمت شما هستیم در این قسمت قصد داریم نحوه ی استفاده از Transaction ها و jdbc در جاوا را بررسی کنیم.

کاربران عزیز لازم است قبل از مطالعه ی درس مدیریت تراکنش های JDBC در جاوا آشنایی لازم با پایگاه داده و اتصال به انواع آن با استفاده از کدهای جاوا را داشته باشند.

Transactions یا تراکنش چیست؟

Transaction یا تراکنش مجموعه ای از عملیات هایی مانند Insert و Update و Create و... است که بر روی پایگاه داده و در قالب یک واحد منطقی عمل می کنند.

سوال: منظور از انجام عملیات در قالب یک واحد چیست؟

جواب: تراکنش های مالی یک بانک را در نظر بگیرید. شما زمانی که با کارت خود خریدی انجام می دهید مبلغی مشخصی از حساب شما کسر و همان مبلغ عینا به حساب فروشنده واریز می شود. اجازه دهید ساده ترین حالت را در نظر بگیریم: در اینجا ما دو دستور SQL از نوع Update خواهیم داشت که یکی بر روی حساب شما و دیگری بر روی حساب فروشنده اعمال خواهد شد. اگر هر دو عمل به طور همزمان و بدون خطا اجرا شوند اجازه ی اعمال نهایی بر روی پایگاه داده را خواهد داشت.

برای درک بهتر عکس زیر را در نظر بگیرید:

مفهوم کلی Transaction-Commit در جاوا
مفهوم کلی Transaction-Commit در جاوا

همان طور که مشاهده می کنید در اینجا چهار عملیات SQL وجود دارند که همگی در قالب یک تراکنش مشخص شده اند، یعنی تا زمانی که هر چهار عملیات به درستی انجام نشده باشند اجازه ی اعمال نتیجه بر روی پایگاه داده صادر نخواهد شد. می بینید که در این مورد هر چهار عمل دارای تیک سبز هستند یعنی به درستی و بدون مشکل اجرا شده اندT پس می توان انجام تغییرات در پایگاه داده را نهایی کرد.

در مقابل آن عکس زیر را درنظر بگیرید:

مفهوم کلی Transaction-Rollback در جاوا
مفهوم کلی Transaction-Rollback در جاوا

در این مورد یکی از چهار عملیات به درستی انجام نشده است بنابراین سه عملیاتی که به طور موقت انجام شده اند، همگی به حالت قبل از انجام کد SQL بازمی گردند. در ادامه با مفهوم Commit و Rollback آشنا خواهیم شد.

تراکنش ها دارای خواص مشخصی هستند که به شرح زیر می باشند:

Atomicity: یک تراکنش یا باید کامل انجام شود یا اصلا انجام نشود. مثلا از n کوئری در تراکنش ما وجود دارد تمامی n کوئری باید بدون مشکل اجرا شوند، در غیر این صورت تمامی کوئری ها Rollback خواهند شد.

Consistency: تضمین می کند که با انجام هر تراکنش پایگاه داده، از یک حالت سازگار به یک حالت سازگار دیگر برود.

Isolation: تضمین می کند که با اجرای همزمان تراکنش ها، سلامت پایگاه داده حفظ شود این مورد دارای سطح های مختلفی است که در کدنویسی از آن ها استفاده می کنیم و در ادامه با آن ها آشنا خواهیم شد.

Durability: ذخیره ی تغییرات قبل از انجام تراکنش به منظور تغییر وضعیت پایگاه داده به قبل از انجام تراکنش در زمان Crash پایگاه داده.

سطح های مختلف Isolation

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 در جاوا
روند اجرای یک تراکنش JDBC در جاوا

خب دوستان عزیز این بخش از آموزش هم به پایان رسید هدف از این بخش آشنایی اولیه با تراکنش های jdbc در جاوا و پیاده سازی آن ها بود امیدوارم از این آموزش بهره ی کافی را برده باشید.

نویسنده شوید
دیدگاه‌های شما (1 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

علیرضا
16 تیر 1398
خیلی جذاب بود از شما ممنونم به خاطر توضیح دادن این مطلب. نتونستم درست درمون یه چیزی پیدا کنم واسه این

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.