需求:有一个数据量比较大的表tableA,大概有几十万数据。里面存放用户手机号码,现在要求批量保存至少几百的的手机号码,保存之前需要分别验证这些手机号码是否在数据库中已存在。
2)、在批量保存之前,把所有的手机号查出来,放到服务器内存;然后在内存中分别对需 要保存的手机号进行校验,过滤出需要保存号码,然后在进行批量保存;
缺点:在数据量少的情况下可以使用该方法,但是几十万条的数据都要放到内存中 个,一是浪费系统内存空间,导致程序运行缓慢。也不可取。
3)、在保存之前,利用in来查询库中存在的手机号码,然后在把查询出来手机号分别与需要保存的的手 机号做对比,在查询出的手机号列表中存在的,则过滤到,然后在批量保存;
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包中即可。
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);