java,调用,存储过程

存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中。Java应用程序可以通过JDBC调用这些存储过程,实现复杂的业务逻辑。本文将详细介绍如何在Java中调用数据库的存储过程,包括传递参数和获取返回结果,并提供丰富的示例代码。

一、存储过程概述
存储过程具有以下优点:

提高性能:存储过程只需要编译一次,以后每次执行都不需要重新编译,提高了执行效率。
减少网络流量:存储过程在数据库服务器上执行,只需要将结果返回给客户端,减少了网络传输的数据量。
增强安全性:可以通过存储过程控制对数据库的访问,只允许用户执行特定的存储过程,而不是直接访问表。
复用性强:存储过程可以被多个应用程序共享和复用。
存储过程的参数类 型有三种:

IN参数:输入参数,用于向存储过程传递值。
OUT参数:输出参数,用于从存储过程返回值。
INOUT参数:输入输出参数,既可以传递值给存储过程,也可以从存储过程返回值。
二、Java调用存储过程的基本步骤
在Java中调用存储过程主要通过CallableStatement接口实现,基本步骤如下:

获取数据库连接:通过DriverManager.getConnection()方法获取数据库连接。
准备调用存储过程的SQL语句:使用{call 存储过程名(参数1, 参数2, ...)}格式。
创建CallableStatement对象:通过Connection.prepareCall()方法创建。
设置输入参数:如果存储过程有输入参数,使用setXxx()方法设置。
注册输出参数:如果存储过程有输出参数,使用registerOutParameter()方法注册。
执行存储过程:使用execute()或executeQuery()或executeUpdate()方法执行。
获取输出参数的值:如果存储过程有输出参数,使用getXxx()方法获取。
处理结果集:如果存储过程返回结果集,遍历结果集获取数据。
关闭资源:关闭CallableStatement和Connection。
三、Java调用存储过程示例
下面通过具体示例演示如何在Java中调用不同类型的存储过程。

1. 无参数存储过程
存储过程定义(MySQL):

DELIMITER $$
CREATE PROCEDURE GetAllEmployees()
BEGIN
    SELECT * FROM employees;
END$$
DELIMITER ;
运行项目并下载源码
sql

Java调用代码:

import java.sql.*;

public class CallStoredProcedureNoParams {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/company";
        String username = "root";
        String password = "password";
        
        try (Connection conn = DriverManager.getConnection(url, username, password);
             // 创建调用存储过程的语句
             CallableStatement cstmt = conn.prepareCall("{call GetAllEmployees()}")) {
            
            // 执行存储过程
            ResultSet rs = cstmt.executeQuery();
            
            // 处理结果集
            while (rs.next()) {
                System.out.println("ID: " + rs.getInt("id") + 
                                   ", Name: " + rs.getString("name") + 
                                   ", Department: " + rs.getString("department"));
            }
            
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
运行项目并下载源码
java
运行

2. 带IN参数的存储过程
存储过程定义(MySQL):

DELIMITER $$
CREATE PROCEDURE GetEmployeeByDepartment(IN dept VARCHAR(50))
BEGIN
    SELECT * FROM employees WHERE department = dept;
END$$
DELIMITER ;
运行项目并下载源码
sql

Java调用代码:

import java.sql.*;

public class CallStoredProcedureWithINParam {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/company";
        String username = "root";
        String password = "password";
        
        try (Connection conn = DriverManager.getConnection(url, username, password);
             // 创建调用存储过程的语句
             CallableStatement cstmt = conn.prepareCall("{call GetEmployeeByDepartment(?)}")) {
            
            // 设置输入参数
            cstmt.setString(1, "IT");
            
            // 执行存储过程
            ResultSet rs = cstmt.executeQuery();
            
            // 处理结果集
            while (rs.next()) {
                System.out.println("ID: " + rs.getInt("id") + 
                                   ", Name: " + rs.getString("name") + 
                                   ", Department: " + rs.getString("department"));
            }
            
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
运行项目并下载源码
java
运行

3. 带OUT参数的存储过程
存储过程定义(MySQL):

DELIMITER $$
CREATE PROCEDURE GetEmployeeCount(OUT count INT)
BEGIN
    SELECT COUNT(*) INTO count FROM employees;
END$$
DELIMITER ;
运行项目并下载源码
sql

Java调用代码:

import java.sql.*;

public class CallStoredProcedureWithOUTParam {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/company";
        String username = "root";
        String password = "password";
        
        try (Connection conn = DriverManager.getConnection(url, username, password);
             // 创建调用存储过程的语句
             CallableStatement cstmt = conn.prepareCall("{call GetEmployeeCount(?)}")) {
            
            // 注册输出参数
            cstmt.registerOutParameter(1, Types.INTEGER);
            
            // 执行存储过程
            cstmt.execute();
            
            // 获取输出参数的值
            int count = cstmt.getInt(1);
            System.out.println("员工总数: " + count);
            
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
运行项目并下载源码
java
运行

4. 带INOUT参数的存储过程
存储过程定义(MySQL):

DELIMITER $$
CREATE PROCEDURE IncrementSalary(INOUT salary DOUBLE, IN percentage INT)
BEGIN
    SET salary = salary * (1 + percentage/100.0);
END$$
DELIMITER ;
运行项目并下载源码
sql

Java调用代码:

import java.sql.*;

public class CallStoredProcedureWithINOUTParam {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/company";
        String username = "root";
        String password = "password";
        
        try (Connection conn = DriverManager.getConnection(url, username, password);
             // 创建调用存储过程的语句
             CallableStatement cstmt = conn.prepareCall("{call IncrementSalary(?, ?)}")) {
            
            // 设置输入参数
            cstmt.setDouble(1, 5000.0);  // 初始工资
            cstmt.setInt(2, 10);        // 加薪百分比
            
            // 注册输出参数
            cstmt.registerOutParameter(1, Types.DOUBLE);
            
            // 执行存储过程
            cstmt.execute();
            
            // 获取输出参数的值
            double newSalary = cstmt.getDouble(1);
            System.out.println("加薪后的工资: " + newSalary);
            
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
运行项目并下载源码
java
运行

5. 带返回结果集和输出参数的存储过程
存储过程定义(MySQL):

DELIMITER $$
CREATE PROCEDURE GetEmployeesAndCount(OUT count INT)
BEGIN
    SELECT * FROM employees;
    SELECT COUNT(*) INTO count FROM employees;
END$$
DELIMITER ;
运行项目并下载源码
sql

Java调用代码:

import java.sql.*;

public class CallStoredProcedureWithResultSetAndOUTParam {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/company";
        String username = "root";
        String password = "password";
        
        try (Connection conn = DriverManager.getConnection(url, username, password);
             // 创建调用存储过程的语句
             CallableStatement cstmt = conn.prepareCall("{call GetEmployeesAndCount(?)}")) {
            
            // 注册输出参数
            cstmt.registerOutParameter(1, Types.INTEGER);
            
            // 执行存储过程
            boolean hasResultSet = cstmt.execute();
            
            // 处理结果集
            if (hasResultSet) {
                try (ResultSet rs = cstmt.getResultSet()) {
                    System.out.println("员工列表:");
                    while (rs.next()) {
                        System.out.println("ID: " + rs.getInt("id") + 
                                           ", Name: " + rs.getString("name") + 
                                           ", Department: " + rs.getString("department"));
                    }
                }
            }
            
            // 移动到下一个结果(输出参数)
            while (cstmt.getMoreResults()) {
                // 处理可能的其他结果集
            }
            
            // 获取输出参数的值
            int count = cstmt.getInt(1);
            System.out.println("员工总数: " + count);
            
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
运行项目并下载源码
java
运行

四、不同数据库的存储过程调用差异
虽然JDBC提供了统一的API 来调用存储过程,但不同数据库的存储过程语法和调用方式可能存在差异。

1. MySQL
MySQL使用CREATE PROCEDURE语句创建存储过程,调用时使用{call 存储过程名(参数)}语法。

2. Oracle
Oracle使用CREATE OR REPLACE PROCEDURE语句创建存储过程,调用时语法与MySQL类似,但参数类型可能不同。

Oracle存储过程示例:

CREATE OR REPLACE PROCEDURE GetEmployeeCount(emp_count OUT NUMBER)
IS
BEGIN
    SELECT COUNT(*) INTO emp_count FROM employees;
END;
运行项目并下载源码
sql

Java调用Oracle存储过程:

try (Connection conn = DriverManager.getConnection(url, username, password);
     CallableStatement cstmt = conn.prepareCall("{call GetEmployeeCount(?)}")) {
    
    // 注册输出参数(Oracle使用Types.NUMERIC)
    cstmt.registerOutParameter(1, Types.NUMERIC);
    
    // 执行存储过程
    cstmt.execute();
    
    // 获取输出参数的值
    int count = cstmt.getInt(1);
    System.out.println("员工总数: " + count);
    
} catch (SQLException e) {
    e.printStackTrace();
}
运行项目并下载源码
java
运行

3. SQL Server
SQL Server使用CREATE PROCEDURE语句创建存储过程,调用时可以使用EXEC 存储过程名 参数语法,也可以使用标准的JDBC语法。

SQL Server存储过程示例:

CREATE PROCEDURE GetEmployeeByDepartment
    @dept VARCHAR(50)
AS
BEGIN
    SELECT * FROM employees WHERE department = @dept;
END
运行项目并下载源码
sql

Java调用SQL Server存储过程:

try (Connection conn = DriverManager.getConnection(url, username, password);
     CallableStatement cstmt = conn.prepareCall("{call GetEmployeeByDepartment(?)}")) {
    
    // 设置输入参数
    cstmt.setString(1, "IT");
    
    // 执行存储过程
    ResultSet rs = cstmt.executeQuery();
    
    // 处理结果集
    while (rs.next()) {
        System.out.println("ID: " + rs.getInt("id") + 
                           ", Name: " + rs.getString("name") + 
                           ", Department: " + rs.getString("department"));
    }
    
} catch (SQLException e) {
    e.printStackTrace();
}
运行项目并下载源码
java
运行

五、异常处理和资源管理
在调用存储过程时,需要注意异常处理和资源管理,避免资源泄漏。

Connection conn = null;
CallableStatement cstmt = null;
ResultSet rs = null;

try {
    // 获取数据库连接
    conn = DriverManager.getConnection(url, username, password);
    
    // 创建调用存储过程的语句
    cstmt = conn.prepareCall("{call 存储过程名(参数)}");
    
    // 设置参数和执行存储过程
    // ...
    
} catch (SQLException e) {
    e.printStackTrace();
} finally {
    // 关闭资源,注意顺序
    try {
        if (rs != null) rs.close();
        if (cstmt != null) cstmt.close();
        if (conn != null) conn.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
}
运行项目并下载源码
java
运行

或者使用Java 7引入的try-with-resources语句自动关闭资源:

try (Connection conn = DriverManager.getConnection(url, username, password);
     CallableStatement cstmt = conn.prepareCall("{call 存储过程名(参数)}")) {
    
    // 设置参数和执行存储过程
    // ...
    
} catch (SQLException e) {
    e.printStackTrace();
}
运行项目并下载源码
java
运行

六、存储过程调用的性能考虑
缓存存储过程:大多数JDBC驱动会自动缓存存储过程的执行计划,可以通过设置连接参数来控制缓存大小。

批量操作:如果需要多次调用同一个存储过程,可以考虑使用批量操作来提高性能。

避免过度使用存储过程:虽然存储过程有很多优点,但并不是所有情况都适合使用。对于简单的查询,直接使用SQL可能更高效。

优化存储过程:确保存储过程本身已经经过优化,避免在存储过程中执行复杂的逻辑。

七、总结
通过JDBC调用数据库存储过程是Java与数据库交互的重要方式,它允许我们利用数据库的强大功能来实现复杂的业务逻辑。本文介绍了Java调用存储过程的基本步骤和示例,包括无参数、带IN参数、带OUT参数、带INOUT参数以及带结果集和输出参数的存储过程调用方法。

主要关键点:

使用CallableStatement接口调用存储过程
使用setXxx()方法设置输入参数
使用registerOutParameter()方法注册输出参数
使用getXxx()方法获取输出参数的值
处理存储过程返回的结果集
注意不同数据库的存储过程语法差异
做好异常处理和资源管理
在实际项目中,可以根据业务需求选择合适的存储过程调用方式,并结合数据库特性进行优化,以提高应用程序的性能和可维护性。

posted @ 2026-06-13 23:58  涂山树下  阅读(6)  评论(1)    收藏  举报