JDBC Scripting به چه معناست؟
JudoScript یك زبان نوشتاری جاوا است كه ساده و قدرتمند بوده و پشتیبان تابعی مختص دامنه 4GL-Link (domain-specific) را دارد و به كاربران اجازه میدهد، مشخص كنند چه كاری میخواهند انجام دهند و چه زمانی برنامههای الگوریتمی لازم میباشند.
این پشتیبان تابعی، جودواسکریپت را در یك دسته جدید از زبانهای نوشتاری تابعی قرار میدهد، مانند هر زبان نسل چهارم. جودواسکریپت روی مشخص كردن اهداف و آمادهسازی راهحلهای خودكار (الگوریتمها)، برای عملی كردن اهداف، تاكید دارد. جودواسکریپت از نحوی شبیه نحو جاوااسکریپت و مدل برنامهنویسی در آن استفاده میكند، ولی قدرت محاسبه بیشتری در ساختار دادههای كامل دارد. این زبان همچنین در الگوهای جاوا بسیار قوی است، بنابراین به همه منابع قابل خواندن جاوا، دسترسی دارد. هماهنگی تمام این خصیصهها باعث ایجاد قدرت، بهرهوری و ظرافت زیادی در برآوردن نیازها شده است.
پردازش داده یكی از مهمترین كاربردهای جودواسکریپت است. امروز برنامههای كاربردی با فرمتهای دادهای خاص و كامل، مثل پایگاه دادههای رابطهای XML، (Standard Generalized Markup Language) SGML، انواع دادههای انتزاعی مثل EJBها (Enterprise JavaBeans)، سرویسهای وب و فایلهای مسطح كار میكنند.
جودواسکریپت برای پردازش دادهای چند فرمتی و چند منبعی كاملا ایدهآل است. این دو بخش روی (Java Database Connectivity) JDBC جودواسکریپت تمركز میكنند، زبان جودواسکریپت را معرفی مینمایند و پردازش داده با J2EE را شرح میدهد. بخش اول پشتیبانی نوشتاری JDBC جودواسكریپت را شرح میدهد.
JDBC Scripting به چه معناست؟
JDBC یك استاندارد APIجاوا برای دستیابی به پایگاههای داده SQL است. از JDBC برای دستیابی به پایگاه داده در جهت برطرف كردن نیازهای مشتریان پایگاه، استفاده میشود. نكته قابل توجه اینجاست كه شركتهای اصلی (Relational Database Management System) RDBMS دستورالعملهای JDBC خالص را برای تولیدات خودشان تهیه میكنند، بنابراین یك ابزار مفید اسكریپتنویسی JDBC میتواند به سادگی امتیازی از این قابلیت را بگیرد. تمام چیزی كه نیاز دارید، JDBC URL و فایل JDBC درایور میباشد كه در classpath وجود دارد. شما میتوانید همزمان به چند پایگاه داده برای پردازش داده ناهمگن وصل شوید.
از نظر فلسفی، اسكریپتنویسی JDBC بخشی از یك ایده بزرگ است: استفاده از پایه جاوا به عنوان یك زبان شیگرا و برای اجرای منظم محاسبه كارها. زبان جودواسکریپت به این منظور طراحی گردید.
امروزه شركتهای بزرگ نرمافزاری فقط از پایگاه دادههای رابطهای استفاده نمیكنند و قالب دادههای قویتر مثل XML وSGML و انواع دادههای انتزاعی مثل Enterprise EJB و سرویسهای وب به خوبی همان فرمتهای قدیمی مثل فایلهای هموار و صفحات گسترده عمل میكنند.
اكنون ابزارهای مختلفی را برای پردازش، گزارش گیری و پرسوجوی دادهها به كار میبرند كه نمونههایی از مزیتهای بدیهی آنها به صورت ذیل میباشد:
1. شما میتوانید هر پردازشی را با هر منبع دادهای با هر فرمتی به صورت همزمان انجام دهید.
2. دسترسی به راهحلها و نتایج درست، سریعتر است
3. ابزارهای كمی برای یادگیری، نصب، پیكربندی و راهاندازی مورد نیاز است
4. دارا بودن یك ابزار رایگان و منفرد ارزانتر تمام میشود
5. فرآیند حل مشكل آسانتر میباشد، زیرا میتوانید بدون نگرانی در مورد مسائل محیطی، تعویض زمینه (context switching) و یكپارچگی كامپوننتهای نامتجانس مشكل را بررسی كنید.
جاوا در حد یك ابزار عملی تنزل كرده و این بدان علت است كه به عنوان یك زبان سیستمی، برای ایجاد سیستمهای نرمافزاری شیگرا طراحی شده است.
یك وسیله نوشتاری خوب باید خصیصههای تابعی انتزاعی برای كاربران و برای بهتر انجام شدن كارها داشته باشد و همچنین قدرت برنامهنویسی آن كم نشود.
از طرفی SQL (محض) خیلی محدود شده، چرا كه ابزارهای پرسوجو ساده خیلی ضعیف عمل میكنند. جودواسکریپت ساختارهای دادهای زیادی دارد و برای كامپوننتهای جاوا خیلی قوی است. شما میتوانید از جودواسکریپت برای دریافت داده از پایگاه داده A، محاسبه برخی نتایج میانی در حافظه و پایان كار با پایگاه داده B، استفاده كنید. اگر از اوراكل استفاده میكنید، حتما نیاز بر استفاده از PL/SQL، پیوندهای پایگاه داده و جداول موقت خواهید داشت. جودواسکریپت به راحتی میتواند با فایلهای ساده، صفحات گسترده، XML، SGML، EJBS و غیره كار كند.
پشتیبانی JDBC در جودواسكریپت:
جودواسکریپت مجموعهای از پشتیبانهای نحوی را برای اجرای SQL دارد. دستورات زبان تعریف داده (DDL) و زبان دستكاری داده (DML) میتوانند به صورت منفرد یا به صورت گروهی یا دستهای اجرا شوند. شما میتوانید پروسیجرها را (روال) با پارامترهای -in، -out و in-out فراخوانی كنید. مثال ساده زیر را ببینید:
connect to "jdbc:oracle:thin:@dbsvr:1521:dbname", "user", "pass";
// Create table and insert a few rows.
executeSQL {
CREATE TABLE emp(emp_no INTEGER PRIMARY KEY,
first_name VARCHAR(100),
last_name VARCHAR(100),
birth_date DATE,
salary NUMBER);
CREATE INDEX emp_fname ON emp(first_name);
CREATE INDEX emp_lname ON emp(last_name);
INSERT INTO emp(emp_no,first_name,last_name,birth_date,salary)
VALUES(100, "Jim", "Billups", to_date("1954-1-3","yyyy-mm-dd"), 86500.0);
INSERT INTO emp(emp_no,first_name,last_name,birth_date,salary)
VALUES(101, "Linda", "Jordan", to_date("1980-7-24","yyyy-mm-dd"),45250.0);
}
// Query and print out rows.
executeQuery qry:
SELECT emp_no, first_name, last_name, salary
FROM emp
WHERE salary < 50000
ORDER BY salary ASC
;
while qry.next() {
println "#", qry[1], " ", qry.last_name, ", ", qry.first_name, ": ",
qry.salary;
}
disconnect(); // From database
این برنامه به پایگاه داده وصل شده، تعدادی از دستورات تعریف و دستكاری داده SQL را اجرا كرده و نهایتا یك پرسوجو را برای چاپ نتایج در جدول اجرا میكند. در بخش executeQuery، متغیر qry بخشی ازJava.Sql.ResultSet است، پس شما میتوانید متد next() را فراخوانی كنید، ستونها در یك سطر میتوانند با اسامی یا شاخصهایشان مورد دستیابی قرار گیرند. مثال بعدی نشان میدهد كه چطور متغیرها میتوانند محدود و مقید (bound) شوند: در این مثال بخشهای مربوط به اتصال به پایگاه داده حذف شده است:
// Prepare a SQL
prepare qry:
SELECT emp_no, first_name, last_name, salary
FROM emp
WHERE salary < ?
ORDER BY salary ASC
;
// Run the query
executeQuery qry with @1:number = 5000.0;
while qry.next() {
println "#", qry[1], " ", qry.last_name, ", ", qry.first_name, ": ",
qry.salary;
}
در كد بالا، ما از متد toCsv()آرایهها استفاده كرده و یك تابع بینام را برای متغیرها به كار میبریم. نتیجه عبارت SQL به این صورت است:
SELESCT * FROM emp WHERE Last_ name IN (‘Olajuwan’, ‘Yao’).
مثالهایی را دیدیم كه عبارتهای SQL را به طور خودكار ساخته و SQL را مستقیما اجرا میكند. حالت دیگر از اجرای SQL به صورت دستهای است.
اتصالات پایگاههای داده
شما به صورت زیر به یك پایگاه داده وصل میشوید:
cannect mycon to ‘jdbc:oracle:thin:@dbsvr:1521:dbname’, ‘user’, ‘pass’;
اتصال ایجاد شده در متغیر mycon ذخیره شده است. اگر نام متغیر اتصال حذف شود، جودواسكریپت از متغیر سراسری از پیش تعریف شده $$con استفاده میكند. میتوانید صفات اتصال را مثل زیر مشخص كنید:
connect mycon ( autoCommit=false ) to
"jdbc:oracle:thin:@dbsvr:1521:dbname", "user", "pass";
چطور جودواسكریپت، درایور JDBC را بارگذاری میكند؟ جودواسکریپت لیستی از اسامی كلاسهای درایور JDBC و پیشوندهای URL آنها را دارد. مثلا وقتی جودواسكریپت، اوراكل را در Jdbc:oracle:… ببیند، كلاس درایور JDBC آن یعنی oracle.jdbc.driver.JdbcDriver را بارگذاری میكند. اگر یكی از درایورها در لیست نباشد، به عنوان یك صفت درایور مشخص میشود یا به شیوه قدیمی جاوا بارگذاری میگردد:
// JudoScript style
connect (driver=‘my.db.jdbc.driver’) to "jdbc:….’, “/”;
// Java style
(java::class). forName (‘my.db.jdbc.driver’);
یك شیء اتصال یك شیء java.Sql.Connection است. شرط use در تمام دستورات اجرایی Sql مشخص میكند كه كدام اتصال باید استفاده شود:
executeSQLuse mydb {…}
هنگامی كه عملیات پایگاه داده كامل میشود، شما باید متد disconnect() را فراخوانی كنید. تابع سیستمی disconnect() متد name-sake از شیء سراسری $$con را فراخوانی میكند، همچنین شیء اتصال، متدهای زیادی مثل Commit() و rollback() دارد.
آماده سازی و اجرا
نحوه اجرای پرسوجوها و پردازش نتایج آنها .
اجرای پرسوجوها:
نحو عمومی در BNF (Backus Naur Form) برای executeQuery به صورت زیر است:
executeQuery variable [ ( attributes ) ] [ use variable ] :
sql_statement ; [ with bind_list ; ] | executeQuery variable with bind_list ;
نحو اصلی شامل دو شكل از عبارت executeQuery است. شكل دوم یك عبارت ذخیره شده آماده در متغیر را اجرا میكند كه بعد توضیح داده میشود. هر دو شكل مجموعهای از نتایج را برمیگردانند. شرط use variable مشخص میكند كه كدام اتصال پایگاه داده باید استفاده شود، جایی كه variable یك شیء اتصال را نگه میدارد. SELECT یكی از بیشترین دستورات مورد استفاده در SQL است، اما فقط این نیست. درایورهایJDBC زیادی از دستورات RDBMS خود پشتیبانی میكنند كه همگی مجموعهای از نتایج را باز میگردانند مثل دستورات Show در MySQL. اشیایی كه برای پرسوجو به كار میروند نیز مجموعهای از نتایج را باز میگردانند. جودواسکریپت مقادیر بازگشتی را به انواع دادهای در جودواسکریپت تبدیل میكند: یكی از پر استفادهترین متدها، متد Next() است كه در میان مجموعه نتایج، پیمایش میكند.
executeQuery qry:
SELECT emp_no, first_name, last_name, birthday, salary
FROM emp
WHERE salary < 50000
ORDER BY salary ASC;
while qry.next() {
println "#", qry[1]," ",qry[2],", ",qry[3],
"(", qry[4].fmtDate("yyyy-MM-dd"), "): ", qry.salary;
}
در مثال بالا ستون اول یك عدد صحیح، ستون دوم و سوم رشتهای و ستون چهارم data و بعدی number است. چون آخرین ستون تاریخ است، میتوانیم متد fmtData() را فراخوانی كنیم.
فراتر از SQL
قدرت اسكریپتنویسی JDBC فقط در اجرای دستورات SQL نیست، بلكه انجام محاسبات و SQL را نیز هماهنگ میكند. فرض كنید كه ثبت وقایع یك وب در جدولی شبیه زیر ذخیره شده است:
CREATE TABLE web_log (
uri VARCHAR(1500),
referer VARCHAR(1500),
time TIMESTAMP
);
ما به گزارشی از تعداد وقایع در روز، در هفته و در ماه نیاز داریم. گزارش روزانه و ماهانه با SQL امكان پذیر است، اگر RDBMS توابع را در قسمت شرط GROUP BY پشتیبانی كند، نظریه این است كه زمان را به رشته تبدیل كرده و در قسمت GROUP BY بكار ببرید. راه حل جودواسکریپت برای این مساله به صورت زیر است.
1: executeQuery qry: SELECT time from web_log;
2:
3: // Step 1. Collect weekly counts
4: counts = new Object;
5: while qry.next() {
6: time = qry.time;
7: token = time.year + "-week-";
8: if time.weekOfYear < 10 {
9: token += "0"; // Fill in a 0 for single digit numbers.
10: }
11: token += time.weekOfYear; // The week-token for the day.
12: ++ counts.(token);
13: }
14:
15: // Step 2. Print out weekly counts
16: for wk in counts.keysSorted() {
17: println wk, ":", counts.(wk) :>8; // Right-aligned, 8-digits
18: }
كلیدهای راهحل ساده این مساله ساختار داده درونی Object و جنبههای مقادیر Data میباشند. Object ارسالی یك نگاشت است كه جفت نام و مقدار را ذخیره میكند. در خط 12 و 16 gets و sets مقداری را به كلید نسبت میدهند یا از آن برمیگردانند. در خطهای 6 تا 11 یك نشانه هفتگی برای زمان میسازیم و مقدار آن را در Object اضافه میكنیم. اگر كلیدی وجود نداشت، مقدار Null یا0 قرار داده میشود. در خط 15 كلیدهای ذخیره شده را با همان ترتیب طبیعی بدست میآوریم. نتایج شبیه كد زیر میباشند:
2004-week-06: 8438
2004-week-07: 21409
2004-week-08: 34940
2004-week-09: 128343
2004-week-10: 99827
2004-week-11: 78343
2004-week-12: 30968
2004-week-13: 44021
متدهای نتایج پرس و جو
شیء پرسوجو متدهای مفیدی دارد كه در جدول 2 نشان داده شده:
توضیح متد
ویژگیهای ستونهای پرس و جو را به عنوان یك شیءTableData برمیگرداند
getColumnAttributes ()
به اندازه limit سطر از نتیجه پرس و جو را برمیگرداند.. اگر تنها یك ستون در پرس و جو وجود داشته باشد، نتیجه یك آرایه است؛ در غیر اینصورت یك شیء TableData میباشد.
getResult (limit)
عبارت SQL را برمیگرداند.
getSQL ()
عبارت آماده شده (كه یك نمونه java.sgl.PeparedStatement است) را اگر پرس و جو آماده شده باشد برمیگرداند.
getPreparedStatement ()
مجموعه نتیجه را در یك نمونه از Java.sql.Resultset را هنگامی كه پرس و جو اجرا شود، برمیگرداند.
getReultSet
هنگامی كه پرس و جو اجرا گردد یك نمونه از Java.sql.ResultsetMetaData را برمیگرداند.
getReultSetMetaData ()
getcolumnAttributes()، TableData را برمیگرداند كه یك ساختار داده دو بعدی (2D ) است. مثال زیر از این متد برای تشریح جدول استفاده میكند:
function tableDesc tableName, dbcon {
if dbcon == null { dbcon = $$con; }
executeQuery qry use dbcon:
SELECT * FROM (* tableName *) WHERE 0 > 1
;
println [[*
----------------------------------------------------------------------
Name Type Display Precision Scale Nullable Class
Name Size Name
----------------------------------------------------------------------
*]];
printTable qry.getColumnAttributes()
for column("name") :<16,
column("type") :<10,
column("displaySize") :>8,
column("precision") :>11,
column("scale") :>8,
column("nullable").fmtBool() :>9,
" ", column("className"), nl; // Newline
}
// Try it out
connect to dbUrl, dbUser, dbPassword;
tableDesc "emp";
disconnect();
چهار متد آخر در جدول 2 اشیاء جاوا را برمیگرداند كه میتوانند متدهای جاوا را دستكاری كنند یا به آنها ارسال شوند. در یك فایل كه مقادیرش با كاما از هم جدا شدهاند، مثال زیر از getResultSetMetaData() برای نسخه برداری از نتایج استفاده میكند:
function printResultsetAsCSV outfile, rs, sep, closeOnExit {
if outfile == null {
outfile = getSysOut();
closeOnExit = false;
}
rsmd = rs.getResultSetMetaData();
cnt = rsmd.getColumnCount();
// Print headers
for i from 1 to cnt {
if i>1 { print sep; }
print rsmd.getColumnName(i);
}
println ;
// Print results
while rs.next() {
for i from 1 to cnt {
if i>1 { print SEP; }
print rs[i];
}
println ;
}
if closeOnExit { outfile.close(); }
}
// Try it out
connect to dbUrl, dbUser, dbPass;
executeQuery qry: SELECT * FROM emp;
printResultsetAsCSV openTextFile("result.csv", "w"), qry, ",", true;
متدهای فراخوانی مجموعه نتایج:
همانطور كه میدانید در JDBC مقادیر ستونها (جداول) با متدهای get$$$() قابل دستیابی هستند.
جودواسکریپت به شما اجازه دستیابی به ستونها (و ویژگیها) را از طریق اسامی و شاخصهایشان میدهد، ولی هنوز هم میتوانید هر متدی را فراخوانی كنید. برای مثال یك جدول در اوراكل را كه یك ستون LONG دارد در نظر بگیرید:
CREATE TABLE error_log(
log_id INTEGER PRIMARY KEY,
note LONG,
encoding VARCHAR(30)
);
شما میتوانید با استفاده از متد gry.getBytes() بایتها را گرفته و آنها را به متن تبدیل نمایید.
executeQuery qry:
SELECT * FROM error_log;
;
while qry.next() {
bytes = qry.getBytes("note");
println "========== ", qry.log_id, " ==========", nl,
encode(bytes, neverEmpty(qry.encoding, "UTF8"));
}
اگر با qry.note به ستون دستیابی داشته باشید، نتیجه متفاوت خواهد بود با فراخوانی qry.getBytes("note") دقیقا میدانیم چه كاری انجام میدهیم، همچنین فراخوانی متدهای نتیجه تنها راه دستیابی به خصیصههای مختص درایور JDBC غیر استاندارد است.
اجرای مستقیم updateها در SQL:
Update های SQL آسانتر از پرسوجوها هستند، چون مقدار تنها بازگشتی یك تعدادupdate است. دستورات به روز درآوردن در SQL شامل UPDATE، INSERT وDELETE میباشد. مثال زیر نشان میدهد كه چطور یك عمل به روزرسانی انجام میشود:
executeUpdate upd:
UPDATE SET salary = 55000 WHERE salary < 50000
;
println unit(upd.getResult(), "person has", "people have"), " got raise.";
در این كد، تابع unit() یك تابع مفید است. اگر پارامتر اول 1 باشد، پارامتر دوم برگردانده میشود و در غیر اینصورت یك شكل جمعی برمیگرداند كه پارامتر سوم است، اگر مشخص شده باشد، پارامتر دوم بعلاوه یك s میباشد.
آماده نمودن و اجرا SQL:
از ویژگیهای JDBC این است كه از دستورات SQL كه در زمان اجرا پارامتری شدهاند، پشتیبانی میكند. SQL به جای پارامترها علامت سوال (؟) را قرار میدهد. دستورات SQL را بهطور پیوسته میتوان چندین بار اجرا كرد (چه با پارامتر چه بدون پارامتر). در جودواسكریپت، دستورات آمادهسازی هم برای جستجو و هم برای به روزرسانی در SQL استفاده میشوند. جستجوها روی executeQuery و به روزرسانیها روی executUpdate اجرا میشوند مانند این نمونه:
prepare qry: SELECT emp_no, salary FROM emp WHERE salary;
prepare upd: UPDATE SET salary=? WHERE emp_no=?;
// Give a 10% raise for those earning less than 50,000
executeQuery qry with @1:number = 50000;
while qry.next() {
executeUpdate upd with @1:number = qry.salary * 1.10,
@2:int = qry.emp_no;
}
commit();
// Assuming auto-commit turned off.
نحوی كه برای اتصال به یك پارامتر به كار رفته شرط with @n: type میباشد كه در آن n ایندكس پارامتر متصل كننده است و با 1 آغاز میشود و type میتواند یكی از انواع boolean، byte، date، double، float،int ، long، number، ref، bit،longvarchar، other، Java-Object، oracle-rowid، oracle-cursor and oracle-bfile باشد. بهطور پیشفرض اگر نوع آن را مشخص نكنیم، String در نظر گرفته میشود.
اجرای اسكریپتهای پایگاه داده as-is:
قبلا دیدیم كه executeSQL{…} میتواند چندین دستور SQL را اجرا كند. دستورات داخل بلوك با ( جدا میشوند. تمام متنها (دستورات) به سرور پایگاه داده as-is فرستاده میشوند. مثال زیر یك روال ذخیره شده اوراكل را ایجاد میكند كه ما در بخش بعد استفاده میكنیم:
executeAny [[*
CREATE PROCEDURE test_proc(
param_io IN OUT NUMBER,
param_i IN VARCHAR,
param_o OUT VARCHAR)
AS BEGIN
param_o := param_i;
IF param_io IS NOT NULL THEN
param_io := param_io + 1;
ELSE
param_io := -1000;
END IF;
END;
*]];
نحو [ [* *] ] برای نقل كردن قطعهای از متن كه ممكن است شامل خطهای جدیدی باشد، به كار میرود.
فراخوانی روالهای ذخیره شده
RDBMSهای اصلی روالهای ذخیره شده را پشتیبانی میكنند. JDBC یك نحو استاندارد را برای فراخوانی این روالها تعریف میكند. مثل این:
{ ? = call foo(?,?,?) }
پارامترها میتوانندOUT ، IN و IN OUT باشند. روال مثال قبلی یعنی executeAny را در اینجا فراخوانی میكنیم. با این عمل یك مقدار در پارامتر param-io برگشت داده میشود و یك مقدار از param-i به param-o فرستاده میشود، كد زیر را ببینید:
prepareCall: { call test_proc(?,?,?) };
x = null;
y = "abcd";
executeSQL with @1:int <=> x,
@2:varchar = y,
@3:varchar => z;
// z will be the same as y
println "x = ", x; // Prints: x = -1000
println "z = ", z; // Prints: z = abcd