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

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

【本文介绍】

  学了好几天,由于项目需要,忙活了两天,写出了个小组件,不过现在还只能支持单表操作。也没考虑算法上的优化,查询速度要比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 PIPELINEDIS    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 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 还是orparaOrderKey in VARCHAR2,                         --排序的keyparaOrderSort   in VARCHAR2,                       --排序的顺序paraPagesize in NUMBER,                               --页数paraPageNow in NUMBER                                --第几页                          )issqlStr VARCHAR2(1000) := test; --拼接的sql语句paraFiledCount NUMBER := 0;              --记录字段数paraCount NUMBER := 1;                       --循环计数paraOrderCount NUMBER := 0;              --排序字段计数paraKeySplit ty_str_split;             --切割后的 keyparaConditionSplit ty_str_split;   --切割后的 conditionparaValueSplit ty_str_split;           --切割后的valuepareAndOrSplit ty_str_split;           --切割后的连接符paraOrderKeySplit ty_str_split;        --切割后的排序KEYparaOrderSortSplit 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");    }