oracle入门(8)——实战:支持可变长参数、多种条件、多个参数排序、分页的存储过程查询组件

时间:2021-11-09 16:33:34

【本文介绍】

  学了好几天,由于项目需要,忙活了两天,写出了个小组件,不过现在还只能支持单表操作。也没考虑算法上的优化,查询速度要比hibernate只快了一点点,可能是不涉及多表查询的缘故吧,多表的情况下才更快。

  经非专业的测试,在有分页的情况下,在300万条数据里面查询的时间保持在0.1秒内。相同查询条件+分页的情况下,hibernate 用时0.3秒内。

  不分页的条件下,查出来的数据越多,时间越长,时间长的话,跟hibernate相相比就没什么优势了。

【思路】

  我的思路是从java传来”字段名,值,排序字段,升降序,分页“等 几个参数,都是字符串。然后在存储过程中 根据 标识符 切割字符串,最后拼接成一个SQL语句。

但也有不少值得改进的地方:

(1)PL/SQL语法的字符串最多只能传4000个字符,所以多于4000个字符的字符串可能会导致查询失败。

(2)日期的排序只能靠的是字符串的排序,所以数据库的日期 要 varchar类型。这样会引起不通用的问题。

(3)比较的符号要约定好,比如查询条件为包含什么什么,即contains,就要发送instr到数据库去拼接SQL语句,因为PL/SQL语言的instr 就相当于contians。这个问题有待改成常量的形式。具体约定如下:

大于:>

小于:<

大于等于:>=

小于等于:<=

不等于:!=

包含:instr

以什么开始:startWith

以什么结尾:endWith

是否为空:isNull

是否不为空:isNotNull

【第一步:在数据库中建立分割函数】

  oracle没有自带的”根据某标识“切割字符串的函数,所以我们要自己建立。

 /**
用pipe函数实现字符串分割
**/
CREATE OR REPLACE TYPE ty_str_split IS TABLE OF VARCHAR2 (4000);
/
CREATE OR REPLACE FUNCTION fn_split (p_str IN VARCHAR2, p_delimiter IN VARCHAR2)
RETURN ty_str_split PIPELINED
IS
j INT := 0;
i INT := 1;
len INT := 0;
len1 INT := 0;
str VARCHAR2 (4000);
BEGIN
len := LENGTH (p_str);
len1 := LENGTH (p_delimiter); WHILE j < len LOOP
j := INSTR (p_str, p_delimiter, i); IF j = 0 THEN
j := len;
str := SUBSTR (p_str, i);
PIPE ROW (str);
IF i >= len THEN
EXIT;
END IF;
ELSE
str := SUBSTR (p_str, i, j - i);
i := j + len1;
PIPE ROW (str);
END IF;
END LOOP; RETURN;
END fn_split;
/

【第二步:建立游标】

 create or replace package testpackage as
type Test_CURSOR is ref cursor;
end testpackage;

【第三步:建立存储过程】

 CREATE OR REPLACE
procedure testc
(
p_cursor out testpackage.Test_CURSOR, --游标,返回列表
paraReturn out VARCHAR2, --返回的结果
paraTableName in VARCHAR2, --数据库名称
paraKey in VARCHAR2, --key,字段名
paraCondition in VARCHAR2, --condition,条件
paraValue in VARCHAR2, --value,值
paraAndOr in VARCHAR2, --where连接附,and 还是or
paraOrderKey in VARCHAR2, --排序的key
paraOrderSort in VARCHAR2, --排序的顺序
paraPagesize in NUMBER, --页数
paraPageNow in NUMBER --第几页
)
is
sqlStr VARCHAR2(1000) := 'test'; --拼接的sql语句
paraFiledCount NUMBER := 0; --记录字段数
paraCount NUMBER := 1; --循环计数
paraOrderCount NUMBER := 0; --排序字段计数
paraKeySplit ty_str_split; --切割后的 key
paraConditionSplit ty_str_split; --切割后的 condition
paraValueSplit ty_str_split; --切割后的value
pareAndOrSplit ty_str_split; --切割后的连接符
paraOrderKeySplit ty_str_split; --切割后的排序KEY
paraOrderSortSplit ty_str_split; --切割后的排序顺序
paraBegin NUMBER:= (paraPageNow-1)*paraPagesize;
paraEnd NUMBER:= paraPageNow*paraPagesize; begin
-- 查询的基本结构
--sqlStr := 'select * from (select tt.*,ROWNUM rowno from (select t.* from ' || paraTableName || ' t ';
--sqlStr := 'select * from (select t1.*, ROWNUM rn from (select * from ' || paraTableName ;
sqlStr := 'select * from ' || paraTableName ; -- 分割
select fn_split (paraKey,'^') into paraKeySplit from dual;
select fn_split (paraCondition,'^') into paraConditionSplit from dual;
select fn_split (paraValue,'^') into paraValueSplit from dual;
select fn_split (paraAndOr,'^') into pareAndOrSplit from dual;
select fn_split (paraOrderKey,'^') into paraOrderKeySplit from dual;
select fn_split (paraOrderSort,'^') into paraOrderSortSplit from dual; IF paraKey != 'null' THEN
sqlStr := sqlStr || ' where ';
--key 长度
for I in paraKeySplit.first()..paraKeySplit.last() loop
paraFiledCount := paraFiledCount + 1;
end loop;
-- 循环
LOOP
-- 退出循环的条件
EXIT WHEN paraCount > paraFiledCount; -- 循环拼接
-- 拼接 = ,< ,> , >= , <= ,!=
if
paraConditionSplit(paraCount) = '=' OR
paraConditionSplit(paraCount) = '<' OR
paraConditionSplit(paraCount) = '>' OR
paraConditionSplit(paraCount) = '>=' OR
paraConditionSplit(paraCount) = '<=' OR
paraConditionSplit(paraCount) = '!='
THEN
sqlStr := sqlStr || paraTableName || '."' || paraKeySplit(paraCount) || '"' || paraConditionSplit(paraCount) || CHR(39) || paraValueSplit(paraCount) || CHR(39);
end if;
-- 拼接contians
if
paraConditionSplit(paraCount) = 'instr' THEN
sqlStr := sqlStr || 'instr(' || paraTableName || '."' || paraKeySplit(paraCount) || '",' || CHR(39) || paraValueSplit(paraCount) || CHR(39) || ')>0';
end if;
-- 拼接 startWith
if
paraConditionSplit(paraCount) = 'startWith' THEN
sqlStr := sqlStr || 'REGEXP_LIKE(' || paraTableName || '."' || paraKeySplit(paraCount) || '",' || CHR(39) || '^' || paraValueSplit(paraCount) || CHR(39) || ')';
end if;
-- 拼接 endWith
if
paraConditionSplit(paraCount) = 'endWith' THEN
sqlStr := sqlStr || 'REGEXP_LIKE(' || paraTableName || '."' || paraKeySplit(paraCount) || '",' || CHR(39) || paraValueSplit(paraCount) || '$' || CHR(39) || ')';
end if;
-- 拼接 is null
if
paraConditionSplit(paraCount) = 'isNull' THEN
sqlStr := sqlStr || paraTableName || '."' || paraKeySplit(paraCount) || '"' || ' is null';
end if;
-- 拼接is not NULL
if
paraConditionSplit(paraCount) = 'isNotNull' THEN
sqlStr := sqlStr || paraTableName || '."' || paraKeySplit(paraCount) || '"' || ' is not null';
end if;
-- 拼接and 或者 or
IF paraCount != paraFiledCount THEN
sqlStr := sqlStr || ' ' || pareAndOrSplit(paraCount+1) || ' ';
end IF;
-- 计数增长
paraCount := paraCount + 1; end LOOP;
end if; --排序
IF paraOrderKey != 'null' THEN
-- 排序字段 长度
for I in paraOrderKeySplit.first()..paraOrderKeySplit.last() loop
paraOrderCount := paraOrderCount + 1;
end loop;
paraCount := 1;
sqlStr := sqlStr || ' order by ';
--循环
LOOP
-- 退出循环的条件
EXIT WHEN paraCount > paraOrderCount;
sqlStr := sqlStr || ' ' || paraOrderKeySplit(paraCount) || ' ' || paraOrderSortSplit(paraCount);
IF paraCount != paraOrderCount THEN
sqlStr := sqlStr || ' , ';
END IF;
paraCount := paraCount + 1;
END LOOP;
END IF; -- 分页
--sqlStr := sqlStr || ')t1 where ROWNUM <=' || paraEnd || ') table_alias where table_alias.rowno >=' || paraBegin;
--sqlStr := sqlStr || ')t1 where ROWNUM <=' || paraEnd || ') where rn >=' || paraBegin;
sqlStr := 'SELECT * FROM (SELECT a.*, ROWNUM rn FROM ('||sqlStr||') a WHERE ROWNUM <= ' || paraEnd || ') WHERE rn >= ' || paraBegin; -- 记录下sql语句,返回去,以便调试
paraReturn := sqlStr; -- 查询
open p_cursor for sqlStr; -- 异常
EXCEPTION
WHEN no_data_found THEN
DBMS_OUTPUT.PUT_LINE('找不到数据');
paraReturn := '找不到数据';
end testc;

【java通用类的封装】

 package com.topview.util;

 import java.lang.reflect.Method;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List; public class FindByProcedure { private static Connection conn = null;
private static Statement stmt = null;
private static ResultSet rs = null;
private static CallableStatement proc = null; private static int pre;// 查询起始时间
private static int post;// 查询结束时间 private static String sql; // 查询的sql语句 public static String getSql() {
return sql;
} public static void setSql(String sql) {
FindByProcedure.sql = sql;
} public static Connection getConn() {
return conn;
} /**
* 连接由调用者提供。
* @param conn
*/
public static void setConn(Connection conn) {
FindByProcedure.conn = conn;
} public void before() {
try {
stmt = conn.createStatement();
} catch (Exception e) {
e.printStackTrace();
try {
throw new MyException("没有传conn进来。");
} catch (Exception e2) {
e2.printStackTrace();
}
}
} public void after() {
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (stmt != null) {
stmt.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (rs != null) {
rs.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if(proc != null) {
proc.close();
}
} catch (Exception e) {
e.printStackTrace();
}
} /**
*
* @param tableName 要查询的表名,假如数据库有一张myUser表,则 tableName = user
* @param keyList 要查询的字段集合,如["name","address"]
* @param conditionList 要查询的逻辑集合,如[">",">="]
* @param valueList 要查询的值集合,如["小铭","广工"]
* @param andOrList 两个查询中间的连接符,如["and","or"]
* @param orderList 排序的字段集合,如["age","name"]
* @param orderSortList 排序的顺序集合,如["asc","desc"]
* @param pageSize 每页显示的数量,如 10
* @param pageNumber 第几页, 如1
* *@param clazz 实体类的Class
* @return 该实体类的list
*/
@SuppressWarnings("unchecked")
public <T> List<T> findByPropertList(String tableName,List<String> keyList,List<String> conditionList,List<String> valueList,List<String> andOrList,List<String> orderList,List<String> orderSortList,Integer pageSize,Integer pageNumber,Class<T> clazz) { // 表名为空时抛异常。
if(tableName == null || "".equals(tableName)) {
try {
throw new MyException("传进来的tableName为空!");
} catch (Exception e) {
e.printStackTrace();
}
}
// 类型为空时抛异常
if(tableName == null || "".equals(tableName)) {
try {
throw new MyException("传进来的tableName为空!");
} catch (Exception e) {
e.printStackTrace();
}
} before(); pre = (int) System.currentTimeMillis(); StringBuilder keyListBuilder = new StringBuilder();
StringBuilder conditionListBuilder = new StringBuilder();
StringBuilder valueListBuilder = new StringBuilder();
StringBuilder andOrListBuilder = new StringBuilder();
StringBuilder orderListBuilder = new StringBuilder();
StringBuilder orderSortListBuilder = new StringBuilder(); String keyListStr = "";
String conditionListStr = "";
String valueListStr = "";
String andOrListStr = "";
String orderSortListStr = "";
String orderSortSortListStr = ""; List<T> ObjectList = new ArrayList<T>(); // 如果不排序
if(orderList == null || "".equals(orderList) || orderList.isEmpty()) { if(orderList == null) {
orderList = new ArrayList<String>();
}
if(orderSortList == null){
orderSortList = new ArrayList<String>();
}
orderList.add("null");
orderSortList.add("null");
}
else {
for(int i = 0 ; i < orderList.size(); i++) {
orderListBuilder.append(orderList.get(i)).append("^");
orderSortListBuilder.append(orderSortList.get(i)).append("^");
}
orderSortListStr = orderListBuilder.substring(0, orderListBuilder.length()-1);
orderSortSortListStr = orderSortListBuilder.substring(0, orderSortListBuilder.length()-1);
}
// 如果不分页
if(pageSize == null){
pageSize = new Integer(10);
}
// 如果没key
if(keyList == null || "".equals(keyList) || keyList.isEmpty()) {
keyList.add("null");
conditionList.add("null");
valueList.add("null");
andOrList.add("null");
} else {
for(int i = 0 ; i < keyList.size() ; i ++) {
keyListBuilder.append(keyList.get(i)).append("^");
conditionListBuilder.append(conditionList.get(i)).append("^");
valueListBuilder.append(valueList.get(i)).append("^");
andOrListBuilder.append(andOrList.get(i)).append("^"); }
keyListStr = keyListBuilder.substring(0, keyListBuilder.length()-1);
conditionListStr = conditionListBuilder.substring(0, conditionListBuilder.length()-1);
valueListStr = valueListBuilder.substring(0, valueListBuilder.length()-1);
andOrListStr = andOrListBuilder.substring(0, andOrListBuilder.length()-1);
} // 和数据库连接
try {
proc = conn.prepareCall("{ call testc(?,?,?,?,?,?,?,?,?,?,?) }"); proc.registerOutParameter(1, oracle.jdbc.OracleTypes.CURSOR);
proc.registerOutParameter(2, Types.VARCHAR);
proc.setString(3,tableName);
proc.setString(4, keyListStr);
proc.setString(5,conditionListStr);
proc.setString(6,valueListStr);
proc.setString(7,andOrListStr);
proc.setString(8,orderSortListStr);
proc.setString(9,orderSortSortListStr);
proc.setInt(10, pageSize);
proc.setInt(11, pageNumber); proc.execute();
String para1 = (String) proc.getString(2);
sql = para1;
ResultSet rs = (ResultSet) proc.getObject(1); // 反射
Method[] methods = clazz.getMethods();
List<String> fieldNameList = new ArrayList<String>();
List<Method> setMethodList = new ArrayList<Method>(); for(Method m : methods) {
if(m.getName().toString().contains("set")) {
fieldNameList.add((m.getName().toString().substring(3,m.getName().toString().length())).toLowerCase());
setMethodList.add(m);
}
} // 取返回值
while (rs.next()) {
try {
Object o = clazz.newInstance();
for(int i = 0 ; i < setMethodList.size() ; i ++) {
// 通过反射创建对象
setMethodList.get(i).invoke(o, rs.getObject(fieldNameList.get(i)));
}
ObjectList.add((T) o);
} catch (Exception e) {
e.printStackTrace();
}
}
proc.close(); } catch (SQLException e) {
e.printStackTrace();
try {
throw new MyException("连接存储过程错误。");
} catch (MyException e1) {
e1.printStackTrace();
}
}finally {
after();
} post = (int) System.currentTimeMillis(); return ObjectList;
} /**
* 得到查询用时
* @return 查询所用时间
*/
public Float getUseTime(){
return (((float)(post - pre))/1000) ;
} /**
* 异常类
* @author xbw
*
*/
public class MyException extends Exception { private static final long serialVersionUID = 1L; //定义无参构造方法
public MyException(){
super();
} //定义有参数的构造方法
public MyException(String msg){
super("MyExcepyion_By_Zjm:"+msg);
}
}
}

【一个调用的test】

 public static void main(String[] args) {
FindByProcedure f = new FindByProcedure(); String tableName = "";
List<String> keyList = new ArrayList<String>();
List<String> conditionList =new ArrayList<String>();;
List<String> valueList =new ArrayList<String>();
List<String> andOrList =new ArrayList<String>();
List<String> orderList =new ArrayList<String>();
List<String> orderSortList =new ArrayList<String>();
tableName = "T_AP_ZA_LYT_GNLK"; // key
keyList.add("ZA_LYT_LKBH");
keyList.add("ZA_LYT_TH");
keyList.add("ZA_LYT_XM");
keyList.add("ZA_LYT_MZ");
keyList.add("ZA_LYT_CSRQ");
keyList.add("ZA_LYT_RKSJ");
keyList.add("ZA_LYT_RKSJ"); // 比较符号
conditionList.add("<");
conditionList.add(">=");
conditionList.add("instr");
conditionList.add("<=");
conditionList.add("startWith");
conditionList.add(">=");
conditionList.add("<="); // value
valueList.add("4500000000000000500049");
valueList.add("4600000000000000203771");
valueList.add("VA");
valueList.add("10");
valueList.add("F");
valueList.add("2014-12-24-08-29-38");
valueList.add("2014-12-24-21-37-22"); // 连接符
andOrList.add("and");
andOrList.add("and");
andOrList.add("and");
andOrList.add("and");
andOrList.add("and");
andOrList.add("and");
andOrList.add("and"); // 排序字段
orderList.add("ZA_LYT_XM");
orderList.add("ZA_LYT_XMPY"); // 排序顺序
orderSortList.add("ASC");
orderSortList.add("DESC"); List<T_AP_ZA_LYT_GNLK> list = new ArrayList<T_AP_ZA_LYT_GNLK>(); // 连接conn要从外部传进去
f.setConn(DBManager.getConnection()); // 开始调用
list = f.findByPropertList(tableName, keyList, conditionList, valueList, andOrList,orderList,orderSortList,5,1,T_AP_ZA_LYT_GNLK.class); for(T_AP_ZA_LYT_GNLK o : list) {
System.out.println(o.getZa_lyt_xm());
} System.out.println("总共拿出数据量:"+list.size());
System.out.println("sql语句:"+f.getSql());
System.out.println("查询用时:"+f.getUseTime().toString()+"s");
}