设为首页 收藏本站
查看: 680|回复: 0

[经验分享] Spring访问传入数组参数的Oracle存储过程

[复制链接]
YunVN网友  发表于 2016-8-14 07:09:02 |阅读模式
  在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、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-257432-1-1.html 上篇帖子: oracle的一些需要注意的问题(持续更新) 下篇帖子: Hibernate 中oracle 主键的自动生成办法
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表