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

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

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


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

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

      3)、在保存之前,利用in来查询库中存在的手机号码,然后在把查询出来手机号分别与需要保存的的手            机号做对比,在查询出的手机号列表中存在的,则过滤到,然后在批量保存;
           优点:相比1、2点来说这个方法比较好一些,适合保存数据不多的情况下;
           确定:如果一次性需要保存号码很多的比如几百条,几千条的时候,效率依然很差。

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

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

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

   2、创建临时表: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级的临时表;  
   3、建立存储过程:
      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;

  4、建立测试代码:测试存储过程的重要性
     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;

二、程序中调用存储过程:
    1、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;

2、纯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;
}

3、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的游标。 

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

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

6、以上都准备好以后,分别运行时发现,只有第二种利用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);

你可能感兴趣的:(j2ee存储过程)