Oracle定义包实现动态查询数据库表

摘要:

项目中有使用xml传递大数据量的写入IT模型,因为之前的测试场景都是单一表的写入测试,有的表数据在初期使用量少,所以有可能导致没有使用xml进行数据的传递与使用模型进行写入的测试。如果这些隐藏的问题发现越晚,对之前历史数据的清理以及重发消息的同步数据工作量就更大。所以有必要填充一份完整的传递数据的xml,然后验证IT模型中所有表数据写入的正确性。
这个需求的前提是,这个IT模式中有维护一份数据库字段映射xml字段的映射表,这里面最重要的其实就是所有表的数据动态查询与取值,为了模型的简化,我们直接取 Oracle数据库自带的 表 User_Tab_Columns作为我们字段的映射表。
GitHub地址:点我

该方案优缺点:
优点:

简单查询效果明显,可以不要写任何sql,java层面使用Map承接数据,可以直接转入VO中。该项目启发性大于实用性

缺点:

只能用于简单的单表查询,复杂查询有点鸡肋,所以整体上属于启发性Demo,如果各位有类似简单的查询可以使用,可扩展性差

启发点:

可以根据这种方式实现插入,更新,删除操作,更新操作会比这个略显复杂,同时也是另外一种启发方式解放无聊的增删改查把

User_Tab_Columns表简介:

只要在数据库中有新建表和字段,改表中就会存储对应字段名称和字段数据类型信息,注意我们建立的emp表的hireDate字段类型为Date,在后面的包中,我们会对日期进行统一格式化处理

SELECT column_name ,lower(column_name),data_type FROM User_Tab_Columns WHERE table_name = upper('emp' )

在这里插入图片描述

1.效果展示:

访问表emp数据:
http://localhost:10001/curDynamicSql/findByDynamicSqlGetReturn/emp
在这里插入图片描述
访问表operator数据:
http://localhost:10001/curDynamicSql/findByDynamicSqlGetReturn/operator
在这里插入图片描述

2.Oracle包代码建立与解析

2.1 创建包头
create or replace 
package pkg_querySql
as
type cursorRef is ref cursor;
procedure querySqls(p_table VARCHAR2,cursor_ref out cursorRef);
end pkg_querySql;
代码解析:

包头中定义了一个过程,入参是 要查询的表名 p_table , cursor_ref是定义的一个游标,存储查询表的数据,可以是多条既存储 List

2.2创建包体
create or replace 
package body pkg_querySql
is
procedure querySqls(p_table VARCHAR2,cursor_ref out cursorRef)
AS
  v_column VARCHAR2(50);   --存储待拼接的数据库字段名
  v_mappName VARCHAR2 (50); --存储映射xml字段的名称
  v_dataType VARCHAR2 (50);  --存储该字段数据库字段类型
  v_sqlStmt VARCHAR2(5000); --存储拼接的临时查询sql
BEGIN
    OPEN cursor_ref FOR --打开动态游标
		'SELECT column_name ,lower(column_name),data_type from User_Tab_Columns WHERE table_name = upper(''' ||p_table || ''' )';
 LOOP 
	FETCH cursor_ref INTO v_column, v_mappName,v_dataType ; 	--循环提取游标数据
	EXIT WHEN cursor_ref%NOTFOUND; 			--没有数据时退出循环
  IF v_dataType = 'DATE' THEN --如果是日期格式
   v_column := 'to_char(' || v_column || ',''YYYY-MM-DD HH24:MI:SS'')';
  END IF;
  IF v_sqlStmt IS NULL THEN  --动态拼接sql
    v_sqlStmt := v_column || ' AS "' || v_mappName || '" ';
 ELSE
    v_sqlStmt := v_sqlStmt || ' ,' || v_column || ' AS "' || v_mappName || '" '; 
  END IF;
END LOOP;
CLOSE cursor_ref;		--关闭游标变量

v_sqlStmt := 'SELECT '|| v_sqlStmt || ' FROM ' || p_table;
DBMS_OUTPUT.PUT_LINE ('查询sql:' ||  v_sqlStmt);
open cursor_ref for --打开动态游标存储查询数据
	v_sqlStmt;  
EXCEPTION 			
	WHEN OTHERS THEN
		IF cursor_ref%FOUND THEN 	--如果出现异常,游标变量未关闭
			CLOSE cursor_ref;		 --关闭游标
		END IF;
		DBMS_OUTPUT.PUT_LINE ('ERROR: ' ||
			SUBSTR(SQLERRM, 1, 200));
end querySqls;
end pkg_querySql;
代码解析:
   1.  'SELECT column_name ,lower(column_name),data_type from User_Tab_Columns WHERE table_name = upper(''' ||p_table || ''' )';

通过p_table 传入需要查询的表查询User_Tab_Columns 中的映射字段

2. FETCH cursor_ref INTO v_column, v_mappName,v_dataType ; 	--循环提取游标数据

一次移动一个游标位置,将一个游标中的三个数据一次性装入后面的三个定义字段中

3.  IF v_dataType = 'DATE' THEN --如果是日期格式
       v_column := 'to_char(' || v_column || ',''YYYY-MM-DD HH24:MI:SS'')';
      END IF;

如果是日期Date格式,进行统一 To_char格式转型

 4. v_sqlStmt := v_sqlStmt || ' ,' || v_column || ' AS "' || v_mappName || '" '; 

动态拼接sql,同时对于映射字段前后加 " 双引号,防止返回Map时字段强制大写

5. open cursor_ref for --打开动态游标存储查询数据
    	v_sqlStmt;  

打开动态游标存储查询数据

2.3 查询表数据准备
CREATE TABLE emp(
	empno NUMBER(4) CONSTRAINT PK_EMP PRIMARY KEY,
    ename VARCHAR2(10),
    job VARCHAR2(15),
    mgr NUMBER(4),
    hiredate DATE,
    sal NUMBER(7,2),
    comm NUMBER(7,2),
    deptno NUMBER(2) CONSTRAINT FK_DEPTNO REFERENCES DEPT
 );

INSERT INTO emp VALUES(7369,'SMITH','CLERK',7902,to_date('17-12-1980','dd-mm-yyyy'),800,NULL,20);
INSERT INTO emp VALUES(7499,'ALLEN','SALESMAN',7698,to_date('20-2-1981','dd-mm-yyyy'),1600,300,30);
INSERT INTO emp VALUES(7521,'WARD','SALESMAN',7698,to_date('22-2-1981','dd-mm-yyyy'),1250,500,30);
INSERT INTO emp VALUES(7566,'JONES','MANAGER',7839,to_date('2-4-1981','dd-mm-yyyy'),2975,NULL,20);
INSERT INTO emp VALUES(7654,'MARTIN','SALESMAN',7698,to_date('28-9-1981','dd-mm-yyyy'),1250,1400,30);
INSERT INTO emp VALUES(7698,'BLAKE','MANAGER',7839,to_date('1-5-1981','dd-mm-yyyy'),2850,NULL,30);
INSERT INTO emp VALUES(7782,'CLARK','MANAGER',7839,to_date('9-6-1981','dd-mm-yyyy'),2450,NULL,10);
INSERT INTO emp VALUES(7788,'SCOTT','ANALYST',7566,to_date('19-4-87','dd-mm-yyyy'),3000,NULL,20);
INSERT INTO emp VALUES(7839,'KING','PRESIDENT',NULL,to_date('17-11-1981','dd-mm-yyyy'),5000,NULL,10);
INSERT INTO emp VALUES(7844,'TURNER','SALESMAN',7698,to_date('8-9-1981','dd-mm-yyyy'),1500,0,30);
INSERT INTO emp VALUES(7876,'ADAMS','CLERK',7788,to_date('23-5-87','dd-mm-yyyy'),1100,NULL,20);
INSERT INTO emp VALUES(7900,'JAMES','CLERK',7698,to_date('3-12-1981','dd-mm-yyyy'),950,NULL,30);
INSERT INTO emp VALUES(7902,'FORD','ANALYST',7566,to_date('3-12-1981','dd-mm-yyyy'),3000,NULL,20);
INSERT INTO emp VALUES(7934,'MILLER','CLERK',7782,to_date('23-1-1982','dd-mm-yyyy'),1300,NULL,10);

----------------operator----------------
  CREATE TABLE operator(
 ID NUMBER(4),
 dates VARCHAR2(10),
 equpment varchar2(10),
 operators varchar2(10)
)
INSERT INTO OPERATOR VALUES(1,'301', 'd',  'jemy');
insert into operator values( 2,'302', 'b', 'tony');
insert into operator values( 3,'303', 'c', 'jemy');
insert into operator values( 4,'304', 'a', 'tony');
insert into operator values( 5,'305', 'c', 'jemy');
INSERT INTO OPERATOR VALUES( 6,'306', 'a', 'jemy');
INSERT INTO OPERATOR VALUES( 7,'307', 'a', 'tom' );
2.4 sql中调用包查询数据
declare
return_cursor pkg_querySql.cursorRef; --定义接收返回数据集的变量
tableName varchar2(20) := 'operator' ;   --定义接收table的变量
id varchar2(20);   --定义接收id的变量
dates varchar2(50);  --定义接收name的变量
equpment varchar2(20);   --定义接收id的变量
operators varchar2(50);  --定义接收name的变量
begin
pkg_querySql.querySqls(tableName,return_cursor);  --调用包中的存储过程返回游标引用
loop fetch return_cursor into id,dates,equpment,operators; --循环获取游标中每一行的数据
exit when return_cursor%notfound;          --游标结束时退出
dbms_output.put_line('id:'||id||' dates:'||dates || ' equpment:'||equpment||' operators:'||operators); --将获取到的值打印出来
end loop;    --循环结束
close return_cursor;  --关闭游标
end;
2.5 Mybatis中的调用
package com.mon.project.model.dynamicSQL.dao;

import org.springframework.stereotype.Repository;

import java.util.Map;

@Repository
public interface DynamicSqlDao {

    public void findByDynamicSql(Map<String, Object> seachMap);

}


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.mon.project.model.dynamicSQL.dao.DynamicSqlDao">

    <resultMap type ="java.util.HashMap" id= "cursorMap">
    	<!--因为过程中已经对返回值前后加 "(双引号)进行了防止大写处理,所以此处resultMap可不写映射-->
    </resultMap >
    <select id="findByDynamicSql" statementType="CALLABLE" parameterType="java.util.HashMap" >
        {call pkg_querySql.querySqls(#{tableName, jdbcType=VARCHAR, mode=IN},
        #{return_cursor, mode=OUT, jdbcType=CURSOR,javaType=ResultSet,resultMap=cursorMap})}
    </select>


</mapper>
2.6 Controller层的获取数据
package com.mon.project.model.dynamicSQL.controller;

import com.mon.project.model.dynamicSQL.dao.DynamicSqlDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;

@RestController
@RequestMapping("/curDynamicSql")
public class CurDynamicSqlContorller {
    private static final Logger logger = LoggerFactory.getLogger(CurDynamicSqlContorller.class);
    @Autowired
    DynamicSqlDao dynamicSqlDao;

    @RequestMapping(value = "/findByDynamicSqlGetReturn/{tableName}",method = RequestMethod.GET)
    public List<Map<String,Object>> findByDynamicSqlGetReturn(@PathVariable("tableName") String tableName){
        logger.info("查询开始");
        Map<String,Object> map=new HashMap<String, Object>();
        List<Map<String,Object>> return_cursor = new ArrayList<>();

        map.put("tableName",tableName);
        map.put("return_cursor",return_cursor);
        dynamicSqlDao.findByDynamicSql(map);
        logger.info("查询结果--------------"+map.get("return_cursor").toString());
        return (List<Map<String, Object>>) map.get("return_cursor");
    }
}
代码解析:

在Map中传入tableName和接受参数return_cursor,从而实现Mybatis对存储过程的调用

3.附加:Oracle包调试技巧

3.1先Oracle中执行包头代码,之后执行包体代码,执行完成后结构如下

在这里插入图片描述

3.2调试权限赋值

CREATE ROLE toad_plsql_debug NOT IDENTIFIED;–新建角色
GRANT DEBUG ANY PROCEDURE TO toad_plsql_debug; --为角色赋值子程序调试权限
GRANT EXECUTE ON SYS.dbms_debug_jdwp TO toad_plsql_debug; --为角色分配调试包权限
GRANT DEBUG CONNECT SESSION TO toad_plsql_debug; --为角色分配调试会话权限
grant toad_plsql_debug to system; --让system具有角色权限

最后一句-让system具有角色权限,有的不是这个账户登录的,改成你对应登录账户即可

3.3 以上执行完成之后,执行 2.3sql查看结果

在这里插入图片描述
如果下面控制台没有打印查询结果可以执行以下语句
set serveroutput on;

3.4 包中的debug方法
3.4.1 双击包体出现Debug和直接正常执行按钮

在这里插入图片描述

3.4.1 点击甲壳虫颜色的debug按钮后弹出如下框,填入要debug查询的表

在这里插入图片描述

3.4.1 步进查看中间变量

在这里插入图片描述

3.5查看存在的包
SELECT object_type,object_name FROM user_objects WHERE object_type IN ('PACKAGE','PAKAGE BODY')
--查看某一个包的代码
select line,text from user_source where name = 'PKG_ALEN' 
--删除无用包
drop  package  pkg_querySql

GitHub地址:点我

本文中的知识学习来自 Oracle PL/SQL 从入门到精通,有兴趣的同学可以买这本书来看看

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值