CallableStatement ב-Java משמש לקריאת הפעלת הלקוח המאוחסן מתוך תוכנית Java. ההלקוחות המאוחסנים הם קבוצה של הצהרות שאנו מכניסים לבסיס הנתונים לצורך משימה מסוימת. הלקוחות המאוחסנים הם מועילים כאשר אנו עוסקים עם כמה טבלאות בסצנריו מורכב ובמקום לשלוח שאילתות מרובות לבסיס הנתונים, אנו יכולים לשלוח נתונים נדרשים להלקוח המאוחסן ולהפעיל את הלוגיקה בשרת בסיס הנתונים עצמו.
CallableStatement
ה-JDBC API מספק תמיכה לביצוע של לקוחות מאוחסנים דרך ממשק ה-
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
בואו נכתוב תהליך אחסון פשוט כדי להכניס נתונים לטבלת Employee. 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
שימו לב שהביצוע השני נכשל כיוון שהשם שהועבר גדול מגודל העמודה. אנו צורפים את החריגה בנפלא ומחזירים שקר במקרה זה.
דוגמה על CallableStatement – פרמטרים OUT של הנפלא המאוחסן
עכשיו בואו נכתוב הנפלא לקבלת נתוני העובד לפי ה־ID. המשתמש יזין את מספר תעודת הזהות של העובד והתוכנית תציג את פרטי העובד. 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;
התוכנית לדוגמה של CallableStatement ב-Java המשתמשת בפרוצדורת האחזור 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);
//רישום הפרמטר OUT לפני קריאת הפרוצדורה
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();
//קריאת הפרמטר OUT כעת
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
מכיוון שאנו קוראים את מידע העובד על ידי המזהה, אנו מקבלים תוצאה יחידה ופרמטרי OUT עובדים כהלכה לקריאת הנתונים. אך אם נחפש לפי תפקיד או מדינה, יתכן שנקבל מספר שורות ובמקרה כזה נוכל להשתמש ב־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);
//רישום הפרמטר OUT לפני קריאת הפרוצדורה
stmt.registerOutParameter(2, OracleTypes.CURSOR);
stmt.execute();
//קריאת הפרמטר OUT כעת
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 – אובייקט DB של Oracle ו-STRUCT
אם תסתכל בפרוצדורות המאוחסנות insertEmployee
ו-getEmployee
, יש לי את כל פרמטרי טבלת העובדים בפרוצדורה. כאשר מספר העמודות גדל, זה יכול להוביל לבלבול וליותר טעויות. מסד הנתונים של Oracle מספק אפשרות ליצירת אובייקטי DB וניתן להשתמש ב-Oracle STRUCT כדי לעבוד איתם. בוא נגדיר תחילה אובייקט DB של 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 ומפעילים אותה. קוד זה מחובר באופן צמוד ל-API של 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