关于JDBC访问存储过程的问题

最近开发一个应用,需要调用一个入参为List的存储过程。

存储过程为: proc_test(p1 OUT Number, p2 IN Number, p3 IN TAB_CUSTOMER);

这个List入参是一个在oracle中自定义的类型的表,如下:

CREATE OR REPLACE TYPE TAB_CUSTOMER AS TABLE OF TYP_CUSTOMER;

CREATE OR REPLACE TYPE TYP_CUSTOMER AS OBJECT
(
  ID            VARCHAR2(20),
  NAME            VARCHAR2(20),
  GENDER      NUMBER,
  AGE      NUMBER,

BIRTHDAY DATE
);

问题一:如何传List对象给oracle存储过程

一 开始我使用jpa的@Procedure,结果一直报错:参数个数或参数类型错误。我在项目中

定义了一个实体类Customer,和Oracle中的类型TYP_CUSTOMER字段相同。传了一个

List<Customer>给存储过程。java.util.List无法转换成存储过程需要的oracle.sql.ARRAY对象。

后来咨询了又相关经验的同事,问题解决,代码如下:

【以下代码解决如何传oracle.sql.ARRAY给存储过程的问题】

import java.util.List;
import java.sql.Connection;
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
import oracle.sql.STRUCT;
import oracle.sql.StructDescriptor;
import oracle.sql.DATE;

/*
    以下方法返回一个oracle.sql.ARRAY对象,该对象与oracle中自定
    的对象的表映射。
    @param oracleType oracle中自定义的类
    @param oracleTable oracle中自定义的类的表
    @list 本地封装的数据列表
    @return ARRAY 一个oracle.sql.ARRAY对象,该对象可与oracle中自定
    的对象的表映射。
*/
private ARRAY getOracleArray(Connection con, String oracleType, String oracleTable, List<Customer> list)
            throws Exception {
        ARRAY array = null;
        ArrayDescriptor desc = ArrayDescriptor.createDescriptor(oracleTable, con);
        STRUCT[] structs = new STRUCT[list.size()];

if (list != null && list.size() > 0) {
            StructDescriptor structdesc = new StructDescriptor(oracleType, con);
            for (int i = 0, len = list.size(); i < len; i++) {
                Object[] result = {
                        list.get(i).getId(),
                        list.get(i).getName(),
                        list.get(i).getGender(),
                        list.get(i).getAge(),
                        new DATE(new java.sql.Date(list.get(i).getBirthday().getTime()))};
                structs[i] = new STRUCT(structdesc, con, result);
            }
            array = new ARRAY(desc, con, structs);
        } else {
            array = new ARRAY(desc, con, structs);
        }
        return array;
    }

import java.util.Date;
class Customer {
    int id;
    String name;
    String gender;
    int age;
    Date birthday;
    /* 省略getter/setter */
}

问题二:只能发起连接池的最大连接数量的请求

问题代码如下:

@Autowired

private HikariDataSource hikariDataSource;

public void save(List<Customer> customers, int p2) {
        Connection conn = null;
        try {
            conn = getConnection();

PreparedStatement pstmt = conn.prepareStatement("call PKG_TEST.PROC_TEST(?,?,?)");
            ARRAY p3 = getOracleArray(conn, TYP_CUSTOMER, TAB_CUSTOMER, customers);

int p1 = 0;
            pstmt.setInt(1, p1);
            pstmt.setInt(2, p2);
            pstmt.setArray(3, p3);

pstmt.execute();
            pstmt.close();
        } catch (SQLException e) {
            throw new PersistException(e);
        } catch (Exception e) {
            throw new PersistException(e);
        } finally {
            if(conn != null)
                try{
                    conn.close();
                }catch(Exception e){
                    throw new PersistException(e);
                }
        }
    }
    
    private Connection getConnection() {
        Connection conn = null;
        try {
            conn = hikariDataSource.getConnection();
            DatabaseMetaData metaData = conn.getMetaData();
            conn = metaData.getConnection();
        } catch (SQLException e) {
            throw new PersistException(e);
        }

return conn;
    }

class PersistException extends RuntimeException{ /*省略*/}

我这里注入了HikariDataSource,因为Hikari的Connection无法直接cast为

Oracle的Connection,所以做了以上转换。

看起来,一次访问数据库结束后,数据库连接没有释放。

【表查询也有同样的问题】

解决问题的代码如下:

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.hibernate.engine.spi.SessionImplementor;

@PersistenceContext

private EntityManager entityManager;

public void save(SaleRetrainingReport report) {
        try {
            Connection conn = entityManager.unwrap(SessionImplementor.class).connection();
            
            CallableStatement stmt = conn.prepareCall("call PKG_TEST.PROC_TEST(?,?,?)");
            ARRAY p3 = getOracleArray(toOracleConnection(conn), TYP_CUSTOMER, TAB_CUSTOMER, customers);

int p1 = 0;
            stmt.setInt(1, p1);
            stmt.setInt(2, p2);
            stmt.setArray(3, p3);

stmt.execute();
            stmt.close();
        } catch (SQLException e) {
            throw new PersistException(e);
        } catch (Exception e) {
            throw new PersistException(e);
        }
    }

private Connection toOracleConnection(Connection connection) {
        Connection conn = null;
        try {
            DatabaseMetaData metaData = connection.getMetaData();
            conn = metaData.getConnection();
        } catch (SQLException e) {
            throw new PersistException(e);
        }

return conn;
    }

---------------------

原文地址:https://www.cnblogs.com/ly570/p/10977623.html

时间: 2024-08-08 05:00:47

关于JDBC访问存储过程的问题的相关文章

oracle存储过程--在应用程序中访问存储过程程序完整举例

认识存储过程和函数 存储过程和函数也是一种PL/SQL块,是存入数据库的PL/SQL块.但存储过程和函数不同于已经介绍过的PL/SQL程序,我们通常把PL/SQL程序称为无名块,而存储过程和函数是以命名的方式存储于数据库中的.和PL/SQL程序相比,存储过程有很多优点,具体归纳如下: * 存储过程和函数以命名的数据库对象形式存储于数据库当中.存储在数据库中的优点是很明显的,因为代码不保存在本地,用户可以在任何客户机上登录到数据库,并调用或修改代码. * 存储过程和函数可由数据库提供安全保证,要想

Java数据库连接——JDBC调用存储过程,事务管理和高级应用

阅读目录 一.JDBC常用的API深入详解及存储过程的调用1.存储过程(Stored Procedure)的介绍2.JDBC调用无参存储过程3.JDBC调用含输入参数存储过程4.JDBC调用含输出参数存储过程二.JDBC的事务管理1.JDBC实现事务管理2.通过代码实现事物的管理三.数据库连接池(dbcp.c3p0)1.dbcp使用步骤2.c3p0使用步骤3.连接池总结四.JDBC的替代产品(Hibernate.Mybatis)1.Commons-dbutils 2.Hibernate简介3.M

Java数据库连接--JDBC调用存储过程,事务管理和高级应用

相关链接:Jdbc调用存储过程 一.JDBC常用的API深入详解及存储过程的调用 1.存储过程的介绍 我们常用的操作数据库语言SQL语句在执行的时候要先进行编译,然后执行,而存储过程是在大型数据库系统中,一组为了完成特定功能的SQL语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过制定存储过程的名字并给出参数(如果该存储过程带有参数) 来执行它.存储过程是数据库中 的一个重要对象,任何一个设计良好的数据库应用程序都应该用到存储过程. 一个存储过程是一个可编程的函数,它在数据

项目小结-JDBC访问数据库的基本步骤

JDBC访问数据库的基本步骤: (1)将数据库的JDBC驱动加载到classpath中,在基于javaEE的web应用实际开发过程中, 通常把目标产品的JDBC驱动复制到WEB-INF/lib中 (2)加载JDBC驱动,将其注册到DriverManager中 //Oracle8/8i/9i(thin模式)数据库 Class.forName("oracle.jdbc.driver.OracleDriver").newInstance(); //Sql server2005数据库 Clas

用jdbc访问二进制类型的数据

1 package it.cast.jdbc; 2 3 import java.io.BufferedInputStream; 4 import java.io.BufferedOutputStream; 5 import java.io.File; 6 import java.io.FileInputStream; 7 import java.io.FileOutputStream; 8 import java.io.InputStream; 9 import java.io.OutputSt

Java jdbc访问sqlserver,oracle数据库

1.JDBC访问Oracle数据库 1 public class Jdbc_Oracle { 2 3 // 静态代码块,只会执行一次,类似C#静态构造方法 4 static { 5 try { 6 // 加载数据库驱动一次 7 Class.forName("oracle.jdbc.driver.OracleDriver"); 8 } catch (ClassNotFoundException e) { 9 e.printStackTrace(); 10 } 11 } 12 13 //m

使用JDBC访问MySQL

JSP中使用JDBC访问MySQL,首先应将mysql-connector-java-5.1.31.jar拷贝到tomcat安装目录下的lib文件夹里,然后再配置classpath. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.sql.*"

c#访问存储过程

// 2015/07/04 // 访问存储过程 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Data; using System.Data.SqlClient; namespace Ado.netstart_1 { class Program { static void Main(st

用jdbc访问大段文本数据

1 package it.cast.jdbc; 2 3 import java.io.BufferedReader; 4 import java.io.BufferedWriter; 5 import java.io.File; 6 import java.io.FileNotFoundException; 7 import java.io.FileReader; 8 import java.io.FileWriter; 9 import java.io.IOException; 10 impo