Spring JDBC调用存储过程的几种情况

阅读更多
注入spring jdbc,参见:http://godandghost.iteye.com/admin/blogs/1724763

1. 存储过程中各个出入参都是基本类型时:
PROCEDURE p_get_province
(
    iv_code                 VARCHAR2,  --字符串类型
    in_staff_role           NUMBER,    --数字类型
    in_provincial_id        NUMBER,    
    iv_m_id                 VARCHAR2,
    ocur_exec_status        OUT pkg_ref_cursor.gcur_record_set, --游标类型
    ocur_province           OUT pkg_ref_cursor.gcur_record_set  --游标类型
);


public static List getProvice(final Map paramMap) throws Exception
{
	try {
		List list = template.execute(new ConnectionCallback()
		{
			@Override
			public List doInConnection(Connection conn)
					throws SQLException, DataAccessException
			{
				CallableStatement call = conn.prepareCall("call p_get_province(?,?,?,?,?,?)");
				call.setString("iv_code", paramMap.get("Report_Id"));
				if (StringUtils.isNotBlank(paramMap.get("Role"))) {
					call.setInt("in_staff_role", Integer.parseInt(paramMap.get("Role")));
				} else {
					call.setString("in_staff_role", null);
				}
				if (StringUtils.isNotBlank(paramMap.get("ProvinceId"))) {
					call.setInt("in_provincial_id", Integer.parseInt(paramMap.get("ProvinceId")));
				} else {
					call.setString("in_provincial_id", null);
				}
				call.setString("iv_m_id", paramMap.get("mId"));
				call.registerOutParameter("ocur_exec_status", OracleTypes.CURSOR);
				call.registerOutParameter("ocur_province", OracleTypes.CURSOR);
				DataSourceUtils.applyTimeout(call, template.getDataSource(), 30);// 30秒超时
				call.execute();

				ResultSet rs = (ResultSet) call.getObject("ocur_exec_status");
				int status = 0;
				String info = "";
				while (rs.next()) {
					status = rs.getInt("Exec_Status");
					info = rs.getString("Exec_info");
				}
				rs.close();
				if (status == 0) {
					List provinceList = new ArrayList();
					ResultSet rs1 = (ResultSet) call.getObject("ocur_province");
					while (rs1.next()) {
						MyObject mo = new MyObject();
						mo.setValue(rs1.getString("province_value"));
						mo.setText(rs1.getString("province"));
						provinceList.add(mo);
					}
					rs1.close();
					return provinceList;
				} else {
					return null;
				}
			}
		});
		return list;
	}
	catch (DataAccessException e) {
		throw e;
	}
	catch (Exception e) {
		throw e;
	}
}


2.存储过程中有自定义struct时
Struct类型声明:
CREATE OR REPLACE TYPE header_struct as object
(
	w_id          varchar2(8),
	a_time        date,
	b_time        date,
	token         varchar2(20)
)

create or replace type Report_List as varray(2000) of REPORT_STRUCT

create or replace type REPORT_STRUCT as object(
	REQUEST_ID	NUMBER(10),
	STATUS		NUMBER(3),
	FILENAME		VARCHAR2(200),
	DESCRIPTION	VARCHAR2(201),
	REQUESTDATETIME	DATE
)

CREATE OR REPLACE TYPE ERROR_MESSAGE_STRUCT as object
(
    error_type         number(3),
    error_code         number(10),
    message      	   varchar2(255)
)

存储过程声明:
CREATE OR REPLACE PROCEDURE get_report_list
(
	iv_header               IN header_struct,
	iv_function_name        IN VARCHAR2,
	iv_report_date 			IN DATE,
	ov_report_list   		OUT report_list,
	ov_error_message        OUT error_message_struct
)

Java代码写法:
public static Object[] getReportResultList(final Header header,
			final String functionName, final Date dateTime)
{
	try {
		return template.execute(new ConnectionCallback()
		{
			@Override
			public Object[] doInConnection(Connection conn) throws SQLException, DataAccessException
			{
				Connection con = conn.getMetaData().getConnection();// 此处需要将connection类型转换一下
				OracleCallableStatement call = (OracleCallableStatement) con
						.prepareCall("call get_report_list(?,?,?,?,?)");
				
				// 存储过程第一个参数
				oracle.sql.STRUCT headerStruct = getHeaderStruct(header, con);
				// 声明存储过程的入参
				call.setSTRUCT(1, headerStruct);// 参数类型是Struct
				call.setString(2, functionName);// 参数类型是String
				call.setDate(3, dateTime);
				// 声明存储过程的出参
				call.registerOutParameter(4, OracleTypes.ARRAY, "REPORT_LIST");// REPORT_LIST必须大写
				call.registerOutParameter(5, OracleTypes.STRUCT, "ERROR_MESSAGE_STRUCT");// ERROR_MESSAGE_STRUCT必须大写

				DataSourceUtils.applyTimeout(call, sbcTemplate.getDataSource(), 30);
				call.execute();
				
				// 出参第一个结构体是一个Struct的Array
				oracle.sql.ARRAY array = call.getARRAY(4);
				List resultList = new ArrayList();
				if (array != null) {
					Datum[] data = array.getOracleArray();
					for (Datum datum : data) {
						STRUCT struct = (STRUCT) datum;
						Datum[] datas = struct.getOracleAttributes();
						
						int requestId = 0;
						int status = 0;
						String fileName = "";
						String description = "";
						NUMBER requestIdNum = (NUMBER) datas[0];
						if (requestIdNum != null) {
							requestId = requestIdNum.bigIntegerValue().intValue();
						}
						NUMBER statusNum = (NUMBER) datas[1];
						if (statusNum != null) {
							status = statusNum.bigIntegerValue().intValue();
						}
						
						try {
							if (datas[2] != null) {
								fileName = new String(datas[2].getBytes(), "GBK");
							}
							if (datas[3] != null) {
								description = new String(datas[3].getBytes(), "GBK");
							}
						}
						catch (UnsupportedEncodingException e) {
							logger.warn("不支持的字符编码", e);
						}
						oracle.sql.DATE requestDate = (oracle.sql.DATE) datas[4];
						java.sql.Timestamp date = null;
						if (requestDate != null) {
							date = requestDate.timestampValue();
						}
						
						ReportResult rr = new ReportResult(requestId,
								status, fileName, description, date);
						resultList.add(rr);
					}
				} else {
					System.out.println("空");
				}
				// 第二个结构体errormessage
				STRUCT errorMsgStruct = call.getSTRUCT(5);
				ErrorMessage em = new ErrorMessage();
				if (errorMsgStruct != null) {
					em = initErrorMessage(errorMsgStruct);
				}
con.close();
				return new Object[] { resultList, em };
			}
		});
	}
	catch (Exception e) {
		throw e;
	}
}

private static STRUCT getHeaderStruct(Header header, Connection con)
			throws SQLException
{
	java.sql.Timestamp aTime = null;
	java.sql.Timestamp bTime = null;
	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	try {
		java.util.Date aTimeUtil = sdf.parse(header.getaTime());
		aTime = new java.sql.Timestamp(aTimeUtil.getTime());
	}
	catch (ParseException e) {
		logger.note("aTime时间转换发生错误", e);
	}
	try {
		java.util.Date bTimeUtil = sdf.parse(header.getbTime());
		bTime = new java.sql.Timestamp(bTimeUtil.getTime());
	}
	catch (ParseException e) {
		logger.note("bTime时间转换发生错误", e);
	}

	Object[] head = { header.getWitnessID(), bTime, aTime,
			header.getToken() };
	oracle.sql.StructDescriptor headerDesc = oracle.sql.StructDescriptor.createDescriptor("HEADER_STRUCT", con);// HEADER_STRUCT必须大写
	oracle.sql.STRUCT headerStruct = new oracle.sql.STRUCT(headerDesc, con, head);
	return headerStruct;
}

private static ErrorMessage initErrorMessage(STRUCT errorMsgStruct)
{
	try {
		Datum[] datas = errorMsgStruct.getOracleAttributes();
		
		int errorType = 0;
		int errorCode = 0;
		String errorMsg = "";
		
		NUMBER errorTypeNum = (NUMBER) datas[0];
		if (errorTypeNum != null) {
			errorType = errorTypeNum.bigIntegerValue().intValue();
		}
		NUMBER errorCodeNum = (NUMBER) datas[1];
		if (errorCodeNum != null) {
			errorCode = errorCodeNum.bigIntegerValue().intValue();
		}
		try {
			if (datas[2] != null) {
				errorMsg = new String(datas[2].getBytes(), "GBK");
			}
		}
		catch (UnsupportedEncodingException e) {
			logger.warn("不支持的字符编码", e);
		}
		ErrorMessage em = new ErrorMessage(errorCode, errorType, errorMsg);
		return em;
	}
	catch (SQLException e) {
		return null;
	}
}


需要注意的是,oracle的驱动包与字符集包必须对应,否则一旦Struct结构体中有字符串(无论是不是中文)都是乱码,导致Struct结构体初始化失败。我用的oracle驱动包是ojdbc6.jar,就必须用与其对应的orai18n.jar。关于乱码问题可以参见:http://blog.csdn.net/hzw2312/article/details/8444462

另注:
当使用Connection con = conn.getMetaData().getConnection();转换数据库连接时,此连接相当于新建了一个连接,不受Spring JDBC控制,不会自动回收。所以在程序最后需要手动关闭:con.close();

你可能感兴趣的:(spring,jdbc,oracle,java)