تستخدم CallableStatement في Java لاستدعاء إجراءات تخزينية من برنامج Java. إجراءات التخزين هي مجموعة من البيانات التي نقوم بتجميعها في قاعدة البيانات لأداء مهمة معينة. تكون الإجراءات التخزينية مفيدة عند التعامل مع جداول متعددة في سيناريوهات معقدة، حيث يمكننا إرسال البيانات الضرورية إلى إجراء التخزين بدلاً من إرسال استعلامات متعددة إلى قاعدة البيانات، ويمكن تنفيذ البرمجة اللوجيستية في خادم قاعدة البيانات ذاته.
CallableStatement
يقدم واجهة JDBC دعمًا لتنفيذ إجراءات التخزين من خلال واجهة
CallableStatement
. يجب كتابة إجراءات التخزين بلغة تخزين البيانات الخاصة بقاعدة البيانات، وسأستخدم قاعدة بيانات Oracle في البرنامج التعليمي الخاص بي. سنتناول الميزات القياسية لـ CallableStatement مع معلمات IN و OUT. وبعد ذلك، سنلقي نظرة على أمثلة خاصة بـ Oracle مثل STRUCT و Cursor. لنقم أولاً بإنشاء جدول لبرامج مثالية على CallableStatement باستخدام استعلام SQL التالي. create_employee.sql
-- لقاعدة البيانات Oracle
CREATE TABLE EMPLOYEE
(
"EMPID" NUMBER NOT NULL ENABLE,
"NAME" VARCHAR2(10 BYTE) DEFAULT NULL,
"ROLE" VARCHAR2(10 BYTE) DEFAULT NULL,
"CITY" VARCHAR2(10 BYTE) DEFAULT NULL,
"COUNTRY" VARCHAR2(10 BYTE) DEFAULT NULL,
PRIMARY KEY ("EMPID")
);
لنقم أولاً بإنشاء فئة أداة للحصول على كائن اتصال قاعدة البيانات Oracle. تأكد من أن ملف JAR Oracle OJDBC موجود في مسار البناء للمشروع. DBConnection.java
package com.journaldev.jdbc.storedproc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DBConnection {
private static final String DB_DRIVER_CLASS = "oracle.jdbc.driver.OracleDriver";
private static final String DB_URL = "jdbc:oracle:thin:@localhost:1521:orcl";
private static final String DB_USERNAME = "HR";
private static final String DB_PASSWORD = "oracle";
public static Connection getConnection() {
Connection con = null;
try {
// قم بتحميل فئة السائق
Class.forName(DB_DRIVER_CLASS);
// قم بإنشاء الاتصال الآن
con = DriverManager.getConnection(DB_URL,DB_USERNAME,DB_PASSWORD);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
}
مثال على CallableStatement
لنقم بكتابة إجراء تخزين بسيط لإدراج بيانات في جدول الموظفين. insertEmployee.sql
CREATE OR REPLACE PROCEDURE insertEmployee
(in_id IN EMPLOYEE.EMPID%TYPE,
in_name IN EMPLOYEE.NAME%TYPE,
in_role IN EMPLOYEE.ROLE%TYPE,
in_city IN EMPLOYEE.CITY%TYPE,
in_country IN EMPLOYEE.COUNTRY%TYPE,
out_result OUT VARCHAR2)
AS
BEGIN
INSERT INTO EMPLOYEE (EMPID, NAME, ROLE, CITY, COUNTRY)
values (in_id,in_name,in_role,in_city,in_country);
commit;
out_result := 'TRUE';
EXCEPTION
WHEN OTHERS THEN
out_result := 'FALSE';
ROLLBACK;
END;
كما يمكنك رؤية أن إجراء insertEmployee يتوقع إدخالات من المتصل ستُدرج في جدول الموظفين. إذا عملت عبارة الإدراج بشكل صحيح، فإنها تعيد TRUE وفي حالة حدوث أي استثناء، فإنها تعيد FALSE. لنرى كيف يمكننا استخدام CallableStatement
لتنفيذ إجراء التخزين insertEmployee
لإدراج بيانات الموظف. JDBCStoredProcedureWrite.java
package com.journaldev.jdbc.storedproc;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Scanner;
public class JDBCStoredProcedureWrite {
public static void main(String[] args) {
Connection con = null;
CallableStatement stmt = null;
// قراءة إدخالات المستخدم
Scanner input = new Scanner(System.in);
System.out.println("Enter Employee ID (int):");
int id = Integer.parseInt(input.nextLine());
System.out.println("Enter Employee Name:");
String name = input.nextLine();
System.out.println("Enter Employee Role:");
String role = input.nextLine();
System.out.println("Enter Employee City:");
String city = input.nextLine();
System.out.println("Enter Employee Country:");
String country = input.nextLine();
try{
con = DBConnection.getConnection();
stmt = con.prepareCall("{call insertEmployee(?,?,?,?,?,?)}");
stmt.setInt(1, id);
stmt.setString(2, name);
stmt.setString(3, role);
stmt.setString(4, city);
stmt.setString(5, country);
// سجّل المعامل OUT قبل استدعاء إجراء التخزين
stmt.registerOutParameter(6, java.sql.Types.VARCHAR);
stmt.executeUpdate();
// اقرأ المعامل OUT الآن
String result = stmt.getString(6);
System.out.println("Employee Record Save Success::"+result);
}catch(Exception e){
e.printStackTrace();
}finally{
try {
stmt.close();
con.close();
input.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
نحن نقوم بقراءة إدخال المستخدم ليتم تخزينه في جدول الموظفين. الشيء الوحيد المختلف عن preparedstatement
هو إنشاء callablestatement من خلال “{call insertemployee(?,?,?,?,?,?)}
” وتعيين معلمة out باستخدام طريقة callablestatement registeroutparameter()
. يجب علينا تسجيل المعلمة out قبل تنفيذ الإجراء المخزن. بمجرد تنفيذ الإجراء المخزن ، يمكننا استخدام طريقة callablestatement getxxx()
للحصول على بيانات الكائن out. لاحظ أنه أثناء تسجيل المعلمة out ، نحتاج إلى تحديد نوع المعلمة out من خلال java.sql.types
. الشيفرة ذات طابع عام ، لذا إذا كان لدينا نفس إجراء مخزن في قاعدة بيانات علاقية أخرى مثل mysql ، يمكننا تنفيذها باستخدام هذا البرنامج أيضًا. فيما يلي الناتج عند تنفيذ برنامج مثال callablestatement أعلاه عدة مرات.
Enter Employee ID (int):
1
Enter Employee Name:
Pankaj
Enter Employee Role:
Developer
Enter Employee City:
Bangalore
Enter Employee Country:
India
Employee Record Save Success::TRUE
-----
Enter Employee ID (int):
2
Enter Employee Name:
Pankaj Kumar
Enter Employee Role:
CEO
Enter Employee City:
San Jose
Enter Employee Country:
USA
Employee Record Save Success::FALSE
لاحظ أن التنفيذ الثاني فشل لأن الاسم الذي تم تمريره أكبر من حجم العمود. نحن نستهلك الاستثناء في الإجراء المخزن ونعيد القيمة false في هذه الحالة.
مثال callablestatement – معلمات out لإجراءات التخزين
الآن دعونا نكتب إجراءً مخزنًا للحصول على بيانات الموظف بواسطة الهوية. سيقوم المستخدم بإدخال هوية الموظف وستقوم البرنامج بعرض معلومات الموظف. getemployee.sql
create or replace
PROCEDURE getEmployee
(in_id IN EMPLOYEE.EMPID%TYPE,
out_name OUT EMPLOYEE.NAME%TYPE,
out_role OUT EMPLOYEE.ROLE%TYPE,
out_city OUT EMPLOYEE.CITY%TYPE,
out_country OUT EMPLOYEE.COUNTRY%TYPE
)
AS
BEGIN
SELECT NAME, ROLE, CITY, COUNTRY
INTO out_name, out_role, out_city, out_country
FROM EMPLOYEE
WHERE EMPID = in_id;
END;
برنامج مثال لـ Java CallableStatement باستخدام إجراء getEmployee لقراءة بيانات الموظف هو: JDBCStoredProcedureRead.java
package com.journaldev.jdbc.storedproc;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Scanner;
public class JDBCStoredProcedureRead {
public static void main(String[] args) {
Connection con = null;
CallableStatement stmt = null;
// قراءة إدخالات المستخدم
Scanner input = new Scanner(System.in);
System.out.println("Enter Employee ID (int):");
int id = Integer.parseInt(input.nextLine());
try{
con = DBConnection.getConnection();
stmt = con.prepareCall("{call getEmployee(?,?,?,?,?)}");
stmt.setInt(1, id);
// سجّل المعلمة الخارجية قبل استدعاء الإجراء المخزن
stmt.registerOutParameter(2, java.sql.Types.VARCHAR);
stmt.registerOutParameter(3, java.sql.Types.VARCHAR);
stmt.registerOutParameter(4, java.sql.Types.VARCHAR);
stmt.registerOutParameter(5, java.sql.Types.VARCHAR);
stmt.execute();
// اقرأ المعلمة الخارجية الآن
String name = stmt.getString(2);
String role = stmt.getString(3);
String city = stmt.getString(4);
String country = stmt.getString(5);
if(name !=null){
System.out.println("Employee Name="+name+",Role="+role+",City="+city+",Country="+country);
}else{
System.out.println("Employee Not Found with ID"+id);
}
}catch(Exception e){
e.printStackTrace();
}finally{
try {
stmt.close();
con.close();
input.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
مرة أخرى، البرنامج هو عام ويعمل لأي قاعدة بيانات تحتوي على نفس الإجراء المخزن. دعونا نرى ما هو الإخراج عند تنفيذ برنامج مثال CallableStatement أعلاه.
Enter Employee ID (int):
1
Employee Name=Pankaj,Role=Developer,City=Bangalore,Country=India
مثال على CallableStatement – إجراء تخزين Oracle CURSOR
نظرًا لأننا نقوم بقراءة معلومات الموظف من خلال الهوية، نحصل على نتيجة واحدة وتعمل المعلمات الخارجية بشكل جيد لقراءة البيانات. ولكن إذا قمنا بالبحث حسب الدور أو البلد، قد نحصل على صفوف متعددة وفي هذه الحالة يمكننا استخدام مؤشر Oracle لقراءتها مثل مجموعة النتائج. getEmployeeByRole.sql
create or replace
PROCEDURE getEmployeeByRole
(in_role IN EMPLOYEE.ROLE%TYPE,
out_cursor_emps OUT SYS_REFCURSOR
)
AS
BEGIN
OPEN out_cursor_emps FOR
SELECT EMPID, NAME, CITY, COUNTRY
FROM EMPLOYEE
WHERE ROLE = in_role;
END;
JDBCStoredProcedureCursor.java
package com.journaldev.jdbc.storedproc;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
import oracle.jdbc.OracleTypes;
public class JDBCStoredProcedureCursor {
public static void main(String[] args) {
Connection con = null;
CallableStatement stmt = null;
ResultSet rs = null;
// قراءة إدخالات المستخدم
Scanner input = new Scanner(System.in);
System.out.println("Enter Employee Role:");
String role = input.nextLine();
try{
con = DBConnection.getConnection();
stmt = con.prepareCall("{call getEmployeeByRole(?,?)}");
stmt.setString(1, role);
// سجّل المعلمة الخارجية قبل استدعاء الإجراء المخزن
stmt.registerOutParameter(2, OracleTypes.CURSOR);
stmt.execute();
// اقرأ المعلمة الخارجية الآن
rs = (ResultSet) stmt.getObject(2);
while(rs.next()){
System.out.println("Employee ID="+rs.getInt("empId")+",Name="+rs.getString("name")+
",Role="+role+",City="+rs.getString("city")+
",Country="+rs.getString("country"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
try {
rs.close();
stmt.close();
con.close();
input.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
هذا البرنامج يستخدم فئات Oracle OJDBC الخاصة ولن يعمل مع قواعد بيانات أخرى. نقوم بتعيين نوع معلمة OUT كـ OracleTypes.CURSOR
ثم نقوم بتحويلها إلى كائن ResultSet
. الجزء الآخر من الشيفرة هو برمجة JDBC بسيطة. عند تنفيذ برنامج مثال CallableStatement أعلاه، نحصل على الناتج التالي.
Enter Employee Role:
Developer
Employee ID=5,Name=Kumar,Role=Developer,City=San Jose,Country=USA
Employee ID=1,Name=Pankaj,Role=Developer,City=Bangalore,Country=India
قد تختلف النتيجة حسب البيانات في جدول الموظفين الخاص بك.
مثال على CallableStatement – كائن قاعدة بيانات Oracle وSTRUCT
إذا نظرنا إلى إجراءات الـ insertEmployee
و getEmployee
المخزنة، سنجد أنني قد قمت بتوفير جميع معلمات جدول الموظفين في الإجراء. عندما يزداد عدد الأعمدة، يمكن أن يؤدي ذلك إلى الالتباس وزيادة خطأ البرمجة. توفر قاعدة بيانات Oracle خيار إنشاء كائنات قاعدة بيانات ويمكننا استخدام Oracle STRUCT للعمل معها. دعونا نعرف أولاً كيفية تعريف كائن قاعدة بيانات Oracle لأعمدة جدول الموظفين. EMPLOYEE_OBJ.sql
create or replace TYPE EMPLOYEE_OBJ AS OBJECT
(
EMPID NUMBER,
NAME VARCHAR2(10),
ROLE VARCHAR2(10),
CITY VARCHAR2(10),
COUNTRY VARCHAR2(10)
);
الآن دعونا نعيد كتابة إجراء تخزين insertEmployee
باستخدام EMPLOYEE_OBJ. insertEmployeeObject.sql
CREATE OR REPLACE PROCEDURE insertEmployeeObject
(IN_EMPLOYEE_OBJ IN EMPLOYEE_OBJ,
out_result OUT VARCHAR2)
AS
BEGIN
INSERT INTO EMPLOYEE (EMPID, NAME, ROLE, CITY, COUNTRY) values
(IN_EMPLOYEE_OBJ.EMPID, IN_EMPLOYEE_OBJ.NAME, IN_EMPLOYEE_OBJ.ROLE, IN_EMPLOYEE_OBJ.CITY, IN_EMPLOYEE_OBJ.COUNTRY);
commit;
out_result := 'TRUE';
EXCEPTION
WHEN OTHERS THEN
out_result := 'FALSE';
ROLLBACK;
END;
لنرى كيف يمكننا استدعاء إجراء التخزين insertEmployeeObject
في برنامج Java. JDBCStoredProcedureOracleStruct.java
package com.journaldev.jdbc.storedproc;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Scanner;
import oracle.jdbc.OracleCallableStatement;
import oracle.sql.STRUCT;
import oracle.sql.StructDescriptor;
public class JDBCStoredProcedureOracleStruct {
public static void main(String[] args) {
Connection con = null;
OracleCallableStatement stmt = null;
//إنشاء مصفوفة كائن لاستدعاء إجراء تخزين
Object[] empObjArray = new Object[5];
//قراءة إدخالات المستخدم
Scanner input = new Scanner(System.in);
System.out.println("Enter Employee ID (int):");
empObjArray[0] = Integer.parseInt(input.nextLine());
System.out.println("Enter Employee Name:");
empObjArray[1] = input.nextLine();
System.out.println("Enter Employee Role:");
empObjArray[2] = input.nextLine();
System.out.println("Enter Employee City:");
empObjArray[3] = input.nextLine();
System.out.println("Enter Employee Country:");
empObjArray[4] = input.nextLine();
try{
con = DBConnection.getConnection();
StructDescriptor empStructDesc = StructDescriptor.createDescriptor("EMPLOYEE_OBJ", con);
STRUCT empStruct = new STRUCT(empStructDesc, con, empObjArray);
stmt = (OracleCallableStatement) con.prepareCall("{call insertEmployeeObject(?,?)}");
stmt.setSTRUCT(1, empStruct);
//تسجيل المعلمة OUT قبل استدعاء إجراء التخزين
stmt.registerOutParameter(2, java.sql.Types.VARCHAR);
stmt.executeUpdate();
//قراءة المعلمة OUT الآن
String result = stmt.getString(2);
System.out.println("Employee Record Save Success::"+result);
}catch(Exception e){
e.printStackTrace();
}finally{
try {
stmt.close();
con.close();
input.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
أولاً وقبل كل شيء ، نقوم بإنشاء مصفوفة كائن بنفس طول كائن قاعدة بيانات EMPLOYEE_OBJ. ثم نقوم بتعيين القيم وفقًا لمتغيرات كائن EMPLOYEE_OBJ. هذا أمر مهم للغاية ، وإلا ستتم إدراج البيانات في أعمدة خاطئة. ثم نقوم بإنشاء كائن `oracle.sql.STRUCT` بمساعدة `oracle.sql.StructDescriptor` ومصفوفة الكائنات الخاصة بنا. بمجرد إنشاء كائن STRUCT ، نقوم بتعيينه كمعلمة IN لإجراء التخزين ، ثم تسجيل المعلمة OUT وتنفيذها. يكون هذا الكود مرتبطًا بشكل صارم مع واجهة برمجة التطبيقات OJDBC ولن يعمل على قواعد بيانات أخرى. إليك الإخراج عند تنفيذ هذا البرنامج.
Enter Employee ID (int):
5
Enter Employee Name:
Kumar
Enter Employee Role:
Developer
Enter Employee City:
San Jose
Enter Employee Country:
USA
Employee Record Save Success::TRUE
يمكننا استخدام كائن قاعدة البيانات أيضًا كمعلمة OUT وقراءته للحصول على القيم من قاعدة البيانات. هذا كل شيء بالنسبة لمثال CallableStatement في Java لتنفيذ إجراءات التخزين، آمل أن تعلمت شيئًا من ذلك.
Source:
https://www.digitalocean.com/community/tutorials/callablestatement-in-java-example