`

Spring访问传入数组参数的Oracle存储过程

阅读更多

在JAVA程序中访问具有数组入参的Oracle存储过程,必须通过java.sql.Array来实现。通过查看java.sql.Array的源代码

 

package java.sql;
 
/**
 * The mapping in the Java programming language for the SQL type
 * <code>ARRAY</code>.
 * By default, an <code>Array</code> value is a transaction-duration 
 * reference to an SQL <code>ARRAY</code> value.  By default, an <code>Array</code>
 * object is implemented using an SQL LOCATOR(array) internally, which
 * means that an <code>Array</code> object contains a logical pointer
 * to the data in the SQL <code>ARRAY</code> value rather
 * than containing the <code>ARRAY</code> value's data.
 * <p>
 * The <code>Array</code> interface provides methods for bringing an SQL
 * <code>ARRAY</code> value's data to the client as either an array or a
 * <code>ResultSet</code> object.
 * If the elements of the SQL <code>ARRAY</code>
 * are a UDT, they may be custom mapped.  To create a custom mapping,
 * a programmer must do two things:
 * <ul>
 * <li>create a class that implements the {@link SQLData}
 * interface for the UDT to be custom mapped. 
 * <li>make an entry in a type map that contains 
 *   <ul>
 *   <li>the fully-qualified SQL type name of the UDT
 *   <li>the <code>Class</code> object for the class implementing
 *       <code>SQLData</code>
 *   </ul>
 * </ul>
 * <p>
 * When a type map with an entry for
 * the base type is supplied to the methods <code>getArray</code>
 * and <code>getResultSet</code>, the mapping
 * it contains will be used to map the elements of the <code>ARRAY</code> value.
 * If no type map is supplied, which would typically be the case,
 * the connection's type map is used by default.
 * If the connection's type map or a type map supplied to a method has no entry
 * for the base type, the elements are mapped according to the standard mapping.
 * <p>
 * @since 1.2 
 */

public interface Array {
  ...
}

 

可以明显看到,Array的实现时通过SQL LOCATOR。通过JDBC调用时,是使用指向数组的指针来实现,而不是复制一份拷贝。类似的类型还有BLOB,CLOB等大对象处理,在调用时,仅维护一个引用。维护指向数据库实际类型的指针,比较类似的用法是java.sql.ResultSet。为了获取到实际的指针引用,应用程序需要维护一个数据库的物理连接。

 

在实际应用中,尤其是基于Spring的应用中,优先采用数据库连接池来操作数据库,而不是JDBC只连。Spring的参考配置:

   <bean id="devDataSource" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName">
			<value>oracle.jdbc.driver.OracleDriver</value>
		</property>
		<property name="url">
			<value>jdbc:oracle:thin:@10.240.14.10:1521:p09tr</value>
		</property>
		<property name="username">
			<value>test</value>
		</property>
		<property name="password">
			<value>test</value>
		</property>
		<property name="defaultAutoCommit">
			<value>false</value>
		</property>
		<property name="maxActive">
			<value>5</value>
		</property>
		<property name="accessToUnderlyingConnectionAllowed">
			<value>true</value>
		</property>
	</bean>	
	<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
		<property name="jndiName">
			<value>mm_datasource</value>
		</property>
		<property name="jndiEnvironment">
			<props>
				<prop key="java.naming.provider.url">
					t3://10.240.14.1:7100 
				</prop>
				<prop key="java.naming.factory.initial">
					weblogic.jndi.WLInitialContextFactory 
				</prop>
			</props>
		</property>
	    <property name="defaultObject" ref="devDataSource" />
	</bean>	
	<bean id="sessionFactory"
		class="com.fenet.insurance.core.spring.hibernate3.annotation.ScanAnnotationSessionFactoryBean">
		<property name="dataSource">
			<ref bean="dataSource" />
		</property>
		<property name="annotatedScanPackages">
			<list>
				<value>com.fenet.insurance.mm.study.hibernate.id.entity</value>
			</list>
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">
					org.hibernate.dialect.Oracle9Dialect
				</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.jdbc.batch_size">5</prop>
			</props>
		</property>
	</bean>
	<bean id="oracleTypeHandler"
		class="ccom.fenet.insurance.mm.common.oracle.OracleTypeHandlerImpl">
		<property name="dataSource">
			<ref local="dataSource" />
		</property>
	</bean>

 

优先通过JNDI获取连接池,如果没有取到则,默认用dbcp的连接池。通过DataSource访问数据库,用到的连接是逻辑连接,而非实际连接数据库的物理连接,仅仅是物理的连接的一个Wrapper。在本文中讨论,在Spring应用中调用具有Array参数的Oracle存储过程,在创建Array时需要用到实际的物理连接。

 

假设,我们使用的Oracle表和数组类型为

create table study_array_nick_tab
(
  name varchar2(200)
);

create or replace type study_array_nick_list is VARRAY(1000) of varchar2(200);

 

被调用的Oracle存储过程为

create or replace procedure study_array_nick(in_array in study_array_nick_list) is
  v_i number;
begin
  for v_i in 1 .. in_array.count loop
    insert into study_array_nick_tab(name) values(in_array(v_i));
  end loop;

  commit;
exception when others then
 rollback;
 raise_application_error('20999','测试错误');
end study_array_nick;

 

 在实际生成Array时,

	public static <D> ARRAY createARRAY(DataSource dataSource,
			String arrayname, D[] objects) {
		Assert.notNull(dataSource, "创建Array失败,传入参数dataSource为空.");
		Assert.hasText(arrayname, "创建Array失败,传入参数arrayname为空.");
		Assert.notEmpty(objects, "创建Array失败,传入参数objects为空.");
		
		Connection poolConn = null;
		Connection vendorConn = null;
		try {
			poolConn = dataSource.getConnection(); 
			if(poolConn instanceof DelegatingConnection) {
				vendorConn = (OracleConnection) ((PoolableConnection) ((DelegatingConnection) poolConn).getDelegate()).getDelegate();
			} else {
				vendorConn = ((WLConnection) poolConn).getVendorConnection();			}
			
			ArrayDescriptor arrayDescriptor = ArrayDescriptor.createDescriptor(
					arrayname, vendorConn);
			return new ARRAY(arrayDescriptor, vendorConn, objects);
		} catch (SQLException e) {
			throw new BusinessException("创建Array失败.", e);
		} finally {
			vendorConn = null;
			try {
				if (poolConn != null && !poolConn.isClosed()) {
					poolConn.close();
				}
			} catch (SQLException e) {
				throw new BusinessException("创建Array,关闭连接失败.", e);
			}
		}
	}

 使用完连接后,尽量只关闭池逻辑连接,对于物理连接不应该关闭,而是交给池去管理。得到Array后,就可以调用包含此Array参数的存储过程。

                                DataSource ds = bean.getDataSource();
		JdbcTemplate jdbc = new JdbcTemplate(ds);
//		PlatformTransactionManager tm = new DataSourceTransactionManager(ds); 
//		TransactionStatus status = tm.getTransaction(null); 
		jdbc.execute(new CallableStatementCreator() {
			public CallableStatement createCallableStatement(Connection con)
					throws SQLException {
				return con.prepareCall("{call study_array_nick(?)}");
			}
		}, new CallableStatementCallback() {
			public Object doInCallableStatement(CallableStatement cs)
					throws SQLException, DataAccessException {
				ARRAY oa = bean.createARRAY("STUDY_ARRAY_NICK_LIST",
						new String[] { "666", "7777", "7775" });
				Assert.notNull(oa);
				cs.setArray(1, oa);
				cs.execute();
				return null;
			}
		});
//		tm.commit(status);

 

在实际使用中发生了很多问题,首先对于不同连接池,获取物理连接的方式不一致。另外,在非Weblogic环境下使用Weblogic连接池来生成Oracle的ARRAY时,调用getVendorConnection时会出现序列化错误。经查实后,发现Weblogic中的JDBC操作的序列化,会由容器通过特别的类来中转(比如说SerailConnection)。

 

分享到:
评论
1 楼 fantasycs 2009-01-04  
向Oralce存储过程传递数组的确问题很多啊,比较头疼,有比较折中的办法不。
我目前也在研究ing

相关推荐

    使用Spring的JdbcTemplate调用Oracle的存储过程

    使用Spring的JdbcTemplate调用Oracle的存储过程

    Spring JdbcTemplate调用Oracle存储过程实现CRUD

    使用 Spring JdbcTemplate 调用 Oracle 存储过程实现 CRUD 在本文中,我们将讨论如何使用 Spring JdbcTemplate 调用 Oracle 存储过程来实现 CRUD(Create、Read、Update、Delete)操作。我们将首先编写 Oracle 存储...

    Mybatis传list参数调用oracle存储过程的解决方法

    "Mybatis调用Oracle存储过程传List参数解决方法" Mybatis是当前最流行的持久层框架之一,它提供了强大的数据访问和操作功能。但是,如何将List类型参数传递给Oracle存储过程,实现批量插入数据却是一个棘手的问题,...

    springboot整合mybatis调用oracle存储过程

    本文将深入探讨如何在Spring Boot项目中整合MyBatis,实现调用Oracle存储过程并处理游标返回的数据。 首先,我们需要在Spring Boot项目中引入相关的依赖。在`pom.xml`文件中添加Oracle JDBC驱动(ojdbc66-oracle...

    spring mybatis 调用oracle存储过程

    本文将深入探讨如何在Spring和MyBatis集成环境中调用Oracle数据库的存储过程,包括无返回值、返回结果集以及返回多个结果的情况。 首先,让我们理解基础概念。Spring是一个全面的后端开发框架,它提供了依赖注入、...

    springboot mybatis 动态调用oracle存储过程,通过存储过程名称,就能动态调用存储过程、java动态调用or

    能不能写个动态的业务,只输入存储过程名称,自动获取存储过程参数,并且参数的数据从前台传递过来,这个就通用了。只写一个通用方法,就可以调用所有的存储过程。只根据输入不同的存储过程名称、参数内容,自动调用...

    jquery ajax 向后台传递数组参数示例

    以上就是使用jQuery AJAX向后台传递数组参数的完整过程,包括JSON序列化和反序列化的处理。请注意,这只是一个基础示例,实际项目中可能需要考虑更多的错误处理和数据验证。同时,不同的后端框架可能有内置的JSON...

    Springboot调用Oracle存储过程的几种方式.docx

    在Spring Boot应用中,调用Oracle数据库的存储过程有多种方式。本文主要介绍三种方法,包括使用`entityManagerFactory.unwrap(SessionFactory.class).openSession()`、直接使用`EntityManager`的`...

    springmvc 传递和接收数组参数的实例

    SpringMVC传递和接收数组参数的实例 在本文中,我们将详细介绍如何在SpringMVC中传递和接收数组参数。从 URL 中传递数组参数到 Controller 类中接收数组参数,我们将一步步地学习如何实现。 URL 中传递数组参数 ...

    Spring boot调用Oracle存储过程的两种方式及完整代码

    在解决问题的过程中,我们发现了 Spring Boot 项目中调用 Oracle 存储过程时遇到的问题,即连接池数量默认为 10,导致每次访问数据库后连接未释放。我们可以通过手动关闭 Session 来释放连接,或者使用第一种方式来...

    初学SpringCloud连接Oracle数据库

    在初学SpringCloud的过程中,与Oracle数据库的连接是构建分布式系统的一个重要环节。SpringCloud作为一个微服务框架,提供了丰富的服务治理功能,而数据库作为数据存储的核心,与SpringBoot的集成使得我们可以轻松...

    jdbc连接oracle,执行存储过程,带数据库存储过程

    本篇将详细介绍如何使用JDBC连接Oracle数据库并执行存储过程。 首先,我们需要理解JDBC的基本概念。JDBC是Java平台的标准API,它允许Java程序与各种数据库进行通信。通过JDBC,我们可以创建数据库连接、发送SQL语句...

    Spring boot连接oracle数据库JDBC配置步骤

    Spring Boot 连接 Oracle 数据库 JDBC 配置步骤 在本文中,我们将详细介绍如何使用 Spring Boot 连接 Oracle 数据库,配置 JDBC 驱动程序,并实现数据库的基本操作。 Step 1: 添加依赖项 在 Spring Boot 项目中,...

    springcloud 可运行的demo 集成了oracle mysql

    SpringBoot 是由 Pivotal 团队提供的全新框架,其设计目标是简化新 Spring 应用的初始搭建以及开发过程。通过内置的 Tomcat 容器和默认配置,SpringBoot 可以让开发者快速创建独立的、生产级别的基于 Spring 的应用...

    java调用oracle存储过程

    在Java编程中,调用Oracle数据库的存储过程是一项常见的任务,尤其在处理复杂业务逻辑或数据操作时。本文将深入探讨如何使用Java与Oracle数据库进行交互,并调用存储过程。 首先,我们需要理解Oracle存储过程的概念...

    asp.net_spring_nhibernate_oracle

    asp.net_spring_nhibernate_oracle 网上关于在.net开发的例子不多,这个例子能跑,遗憾的是,nhibernate在存储过程不完美,在oracle 传回多结果集,只能接受到第一个,官网上也是这样说的。 提示:开发前,先将...

    spring mvc + mybatis 调用mysql 存储过程

    本文将详细讲解如何在Spring MVC和MyBatis集成的项目中调用MySQL存储过程。 首先,让我们理解存储过程的概念。存储过程是在数据库中预编译的一组SQL语句,可以包含输入、输出和内部参数,能够执行复杂的业务逻辑。...

Global site tag (gtag.js) - Google Analytics