דוגמה ל-CallableStatement ב-Java

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