j2ee调用Oracle带数组参数和游标的存储过程方法

环境:Oracle 10g; jboss4.2.2;jdk1.6;hibernate 3.2 java

需求:有一个数据量比较大的表tableA,大概有几十万数据。里面存放用户手机号码,如今要求批量保存至少几百的的手机号码,保存以前须要分别验证这些手机号码是否在数据库中已存在。 spring


思路:1)、在每一个号码保存以前,查询数据库,是否是已经有了。
           缺点:每次保存以前都要查询数据库,与数据库交互过于频繁,效率很是差;

      2)、在批量保存以前,把全部的手机号查出来,放到服务器内存;而后在内存中分别对需                         要保存的手机号进行校验,过滤出须要保存号码,而后在进行批量保存;
            优势:减小了与数据库交互的次数;节省了系统资源
            缺点:在数据量少的状况下可使用该方法,可是几十万条的数据都要放到内存中                                 个,一是浪费系统内存空间,致使程序运行缓慢。也不可取。

      3)、在保存以前,利用in来查询库中存在的手机号码,而后在把查询出来手机号分别与须要保存的的手            机号作对比,在查询出的手机号列表中存在的,则过滤到,而后在批量保存;
           优势:相比一、2点来讲这个方法比较好一些,适合保存数据很少的状况下;
           肯定:若是一次性须要保存号码不少的好比几百条,几千条的时候,效率依然不好。

      4)、利用临时表:在数据库中建立一个临时表(事务级);而后在创建一个存储过程;在进行数据保存            时将须要保存的手机号码一次性传入存储过程当中,在存储过程当中先将须要保存的号码插入到临时表            中,而后在利用临时表关联实际表,查出表中已存在的号码,而后返回给程序,在程序中根据返回            的手机号码列表进行重复性过滤;
           优势:速度快,能够应对大数据量的数据操做。节省资源。
           缺点:若是业务发生变化,须要同时维护程序和存储过程代码。程序在进行存储过程调用时,不方                  便调试。

综上所述:对于大数据量而言,利用第四点比较可取。现列出操做方法:

1、创建存储过程:(数据库中操做)
    一、声明type类型,即创建数据数组类型:
        create or replace type mobileArray as table of varchar2(30);
   (补充:进行数据类型建立时有两种方式:
           一、固定长度数组:建立时须要制定长度:create or replace  类型名 AS VARRAY(52) OF VARCHAR2(20);
           二、嵌套表:可变长度数组:create or replace type  类型名 as table of varchar2(30);
 本实例使用嵌套表。)

   二、建立临时表:create global temporary table TEMP_SEARCH_SUBSCRIBE_MOBILE(mobile VARCHAR2(30))
                  on commit delete rows; 
      (补充: 临时表分为SESSION、TRANSACTION两种,SESSION级的临时表数据在整个SESSION都存在,直到结束这次SESSION;而TRANSACTION级的临时表数据在TRANACTION结束后消失,即COMMIT/ROLLBACK或结束SESSION都会清除TRANACTION临时表数据。  
两种临时表的语法: 
    create global temporary table 临时表名 on commit preserve|delete rows  用preserve时就是SESSION级的临时表,用delete就是TRANSACTION级的临时表;  
   三、创建存储过程:
      create or replace procedure proc_query_subsceibe_mobiles(mobileList in mobileArray,cur_mobileList out sys_refcursor)
 as
   --c number;
begin
  for i in 1..mobileList.count loop
      insert into TEMP_SEARCH_SUBSCRIBE_MOBILE(MOBILE) values(mobileList(i));
  end loop;
  open cur_mobileList for 
     select tempSub.MOBILE from TEMP_SEARCH_SUBSCRIBE_MOBILE tempSub
     where exists (Select 1 from mobile_subscribe_info msi 
     where msi.user_mobile = tempSub.Mobile 
     and msi.OPERATION_TYPE='1' and (msi.OPRCODE <> '04' or msi.SUBSCRIBE_STATUS <> 2));
     --select count(*) into c from TEMP_SEARCH_SUBSCRIBE_MOBILE;
     -- dbms_output.put_line('查询到的数据个数: '||c);
     -- commit;
end proc_query_subsceibe_mobiles;

  四、创建测试代码:测试存储过程的重要性
     declare 
  cur_calling sys_refcursor;

  my_tbl mobileArray := mobileArray('13937025312', '13937025313', '13937025314');
  
  rec_next varchar2(30);
begin
   proc_query_subsceibe_mobiles(my_tbl,cur_calling);  --这样这个游标就有值了
    loop
     fetch cur_calling into rec_next;
     exit when cur_calling%notfound;
     dbms_output.put_line(rec_next);
    end loop;
end;

2、程序中调用存储过程:
    一、spring jdbcTemplate调用存储过程实例:
       public  List<String> getSubscribeMobileList(Map<String,Object> map) {
final List<String>  mobileList = (List<String>) map.get("mobiles");
final JdbcTemplate jdbcTemplateMehtod = this.jdbcTemplate;
   
List resultList = (List) jdbcTemplate.execute(new CallableStatementCreator() { 
       public CallableStatement createCallableStatement(Connection conn) throws SQLException { 
          String procSql="{Call proc_query_subsceibe_mobiles(?,?)}";// 调用的sql 
          CallableStatement proc  = conn.prepareCall(procSql);
          Connection oracleConn = conn.getMetaData().getConnection();
       oracle.sql.ArrayDescriptor mobileArrayDes =
               oracle.sql.ArrayDescriptor.createDescriptor("MOBILEARRAY", oracleConn);
       oracle.sql.ARRAY mobileArray = new oracle.sql.ARRAY(mobileArrayDes,  oracleConn, mobileList.toArray());
       String[] array = (String[]) mobileArray.getArray();
       proc.setArray(1, mobileArray);
       proc.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR);//输出参数
          return proc; 
       } 
    }, new CallableStatementCallback() { 
       public Object doInCallableStatement(CallableStatement cs) throws SQLException,DataAccessException { 
          List<String> mobilesList = new ArrayList<String>(); 
          cs.execute(); 
          ResultSet rs = (ResultSet) cs.getObject(2);// 获取游标一行的值 
          while (rs.next()) {// 转换每行的返回值到Map中 
          String mobile = rs.getString("MOBILE");
          if(!StringUtils.isEmpty(mobile)&&!mobilesList.contains(mobile)){
          mobilesList.add(mobile);
          }
         
          } 
          rs.close(); 
          return mobilesList; 
       } 
 });  
 return resultList;

二、纯jdbc调用实例:
   public  List<String> getSubscribeMobileList2(Map<String,Object> map) {
List<String> mobilesList = new ArrayList<String>();
List<String> mobileList = (List<String>) map.get("mobiles");
ResultSet rs=null;
String driver = "oracle.jdbc.driver.OracleDriver";
String strUrl = "jdbc:oracle:thin: @127.0.0.1 :1521:orcl";
Connection conn = null;  
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
conn = DriverManager.getConnection(strUrl, "xy_mms", "xy_mms");
conn.setAutoCommit(false);
String procSql="{Call proc_query_subsceibe_mobiles(?,?)}";    
       CallableStatement proc  = conn.prepareCall(procSql); 
       oracle.sql.ArrayDescriptor mobileArrayDes =
                oracle.sql.ArrayDescriptor.createDescriptor("MOBILEARRAY",conn.getMetaData().getConnection());
       oracle.sql.ARRAY mobileArray = new oracle.sql.ARRAY(mobileArrayDes, conn.getMetaData().getConnection(), mobileList.toArray());
       proc.setArray(1, mobileArray);
       proc.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR);//输出参数
       proc.execute();
       rs = (ResultSet) proc.getObject(2); 
       while(rs.next()){
        mobilesList.add(rs.getString("MOBILE"));
       }
       
       conn.commit();
       proc.close();
       conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
return mobilesList;
}

三、hibernate 调用存储过程:
   public  List<String> getSubscribeMobileList2(Map<String,Object> map) {
List<String> mobilesList = new ArrayList<String>();
List<String> mobileList = (List<String>) map.get("mobiles");
ResultSet rs=null;
Session session = this.getSession();
Connection conn = session.connection();  
try {

conn.setAutoCommit(false);
String procSql="{Call proc_query_subsceibe_mobiles(?,?)}";    
       CallableStatement proc  = conn.prepareCall(procSql); 
       oracle.sql.ArrayDescriptor mobileArrayDes =
                oracle.sql.ArrayDescriptor.createDescriptor("MOBILEARRAY",conn.getMetaData().getConnection());
       oracle.sql.ARRAY mobileArray = new oracle.sql.ARRAY(mobileArrayDes, conn.getMetaData().getConnection(), mobileList.toArray());
       proc.setArray(1, mobileArray);
       proc.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR);//输出参数
       proc.execute();
       rs = (ResultSet) proc.getObject(2); 
       while(rs.next()){
        mobilesList.add(rs.getString("MOBILE"));
       }
       
       conn.commit();
       proc.close();
       conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
return mobilesList;
}

须要注意:
1.关于字符集:11g的jdbc驱动叫orai18n.jar,以前是nls_charset.jar/classes12.jar 
2.ArrayDescriptor:java传入oracle的数组须要处理一下 
3.oracle.jdbc.OracleTypes.CURSOR:java得到oracle的游标。 

四、
oracle.sql.ArrayDescriptor.createDescriptor("MOBILEARRAY",conn.getMetaData().getConnection());该剧中的MOBILEARRAY必定要为大写,不然会报错误;该值就是在第一步建立的的oracle type数组。

五、在运行程序时,可能会发现没有查询到数据,这是任务,在进行建立Oracle array数组是,传递过来的mobiles数组没有正确状态,此时须要查看系统中是否引入了nls_charset12.jar(Oracle 11g之前使用该包,之后的须要使用 orai18n.jar;两个均可以在Oracle安装目录中搜索获得),缺乏该jar包,则没法正常装载字符串数组值。将其进入至库中便可。

六、以上都准备好之后,分别运行时发现,只有第二种利用jdbc的能够正常运行,其余两种会报,OracleConnect类转化异常。
因为本例使用的是jboss,在该例中解决方案是把jboss服务器中deploy中的项目发布包中的ojdbc14.jar和nls_charset12.jar都删掉,将这个两个jar包移动到jboss 的lib包中便可。

PS:在使用hibernate时,因为session.connect方法已被丢弃,因此,能够考虑换一种方法使用doWork方法:
Session session = this.getSession();
Work work = new Work(){
@Override
public void execute(Connection conn) throws SQLException {
ResultSet rs=null;
try {
conn.setAutoCommit(false);//此处须要禁止链接的自动提交,不然会取不到值
String procSql="{Call proc_query_subsceibe_mobiles(?,?,?,?)}";    
       CallableStatement proc  = conn.prepareCall(procSql); 
       oracle.sql.ArrayDescriptor mobileArrayDes =
               oracle.sql.ArrayDescriptor.createDescriptor("MOBILEARRAY",conn.getMetaData().getConnection());
       oracle.sql.ARRAY mobileOralceArray = new oracle.sql.ARRAY(mobileArrayDes, conn.getMetaData().getConnection(), needSaveMobileArray.toArray());
       proc.setArray(1, mobileOralceArray);
       proc.setString(2, finalOperationType);
       proc.setString(3, queryFlag);
       proc.registerOutParameter(4, oracle.jdbc.OracleTypes.CURSOR);//输出参数
       proc.execute();
       rs = (ResultSet) proc.getObject(4); 
       while(rs.next()){
        mobilesList.add(rs.getString("MOBILE"));
       }
       conn.commit();//提交事务,清除临时表数据
       proc.close();
       conn.setAutoCommit(true);//此处须要打开自动提交功能,不然,下面的事务不会提交到数据库中
} catch (SQLException e) {
e.printStackTrace();
}
}
};
session.doWork(work);
相关文章
相关标签/搜索