Fork me on GitHub

oracle-pl/sql之二

java

触发器

 

 

 

你可以编写用户定义的函数(用pl/sql,java,c)来提供在sql中或sql内置函数中不可用的功能

有时,我们会发现有些功能通过PL/SQL完成会很麻烦,而通过C/C++语言编程则会容易很多。因此,oracle提供了在PL/SQL程序里直接调用外部函数(包括C函数或Java方法)的功能,从而扩展了PL/SQL的程序功能。

 

 

在Oracle的存储过程和函数中,其实IS和AS是同义词,没有什么区别。
还有在自定义类型(TPYE)和包(PACKAGE)时,使用IS和AS也并没有什么区别。
但是在创建视图(VIEW)时,只能使用AS而不能使用IS。
在声明游标(CURSOR)时,只能使用IS而不能使用AS。

 

 

用java的方法

例子:一个简单的需求,用Java代码实现求圆的面积,圆周率PI为3.14,输入的参数为圆的半径R,输出圆的面积S,要求可以在oracle中用PL/SQL代码调用该Java类实现求圆的面积的功能。

Step 1:编写Java代码,PL/SQL Developer本身虽然支持编写Java代码,但是毕竟不是专业的,对Java的工具提供的不是特别好,我喜欢用MyEclipse开发好以后复制过来,下面是我在MyEclipse中开发好的代码。

// 圆工具类,计算圆的面积
public class CircleTools {
  // 定义常量PI
  public static final double PI = 3.14;
 
  // 计算面积
  public static double calcSquare(double r) {
    return PI * r * r;
  }
}

注意在写Java代码的时候,为了能够直接能够在oracle中被调用,所以这里在需要调用的方法前要加上public和static。


Step 2:写好Java代码,下面就是要将Java导入到oracle数据库中,
导入的方法在前文提到过可以用loadJava或者直接写。如果要用loadJava导入,先把上面的代码文件保存为CircleTools.java,然后在cmd命令行中进入该目录下,然后执行如下命令:

loadjava是安装数据库自带的工具
[oracle@oracle1 ~]$ which loadjava
/u01/app/oracle/product/11.2.0/dbhome_1/bin/loadjava

loadjava –u username/userpassword –v -resolve CircleTools.java

执行上面的命令就OK了,下面提供另一种方法,就是直接在PL/SQL中写,写法如下:

CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED circletools AS
// 圆工具类,计算圆的面积
public class CircleTools {
  // 定义常量PI
  public static final double PI = 3.14;
  // 计算面积
  public static double calcSquare(double r) {
    return PI * r * r;
  }
}


查看这个数据字典发现已经进入数据库中了
select * from user_objects
where length(object_type) >5;


Step 3:编写存储过程(procedure)或者函数(function)来封装,以实现以后的调用,一般来说,如果一个Java方法没有返回值,那么就封装成存储过程,如果有返回值,就封装成函数,这里我只给出一个封装成函数的例子:

CREATE OR REPLACE FUNCTION calc_square(r IN NUMBER) RETURN NUMBER AS
  LANGUAGE JAVA NAME 'CircleTools.calcSquare(double) return double';

Step 4:测试函数,2是半径
select calc_square(2) from dual;

Step 5:如果不想用那个Java类了,可以用dropjava命令删除掉系统中的Java,这个跟loadjava很相似,
dropjava -user username/userpassword@db -v javasourcename

注意上面删除的是Java的source name,不是class name,即对象的OBJECT_TYPE是JAVA SOURCE,不是JAVA CLASS。

 

触发器

===============================

创建触发器

create or replace trigger test.login_log  
  after create or drop on schema
 BEGIN
 -- 以下使用的是事件属性
 DBMS_OUTPUT.PUT_LINE('I believe you have created a ' ||
 ORA_DICT_OBJ_TYPE || ' called ' ||
 ORA_DICT_OBJ_NAME);
 END;

测试触发器

set serveroutput on
CREATE TABLE "TEST"."T3"
  (
    "PRODUCT_ID"   NUMBER,
    "PRODUCT_NAME" VARCHAR2(80 BYTE),
    "MONTH"        NUMBER
  );
drop table t3 purge;

===========================

 

记录登录数据库历史记录,使用触发器

建表可以在普通用户之下,但创建触发器需要在sys用户下

create table login_history  
(  
  username   varchar2(60),  --用户名  
  machine    varchar2(60),  --机器名  
  login_time date,          --登录时间  
  ip         varchar2(50)   --ip地址  
);


create or replace trigger login_log  
  after logon on database  
begin  
  insert into test.login_history
     select username, machine, sysdate, sys_context('userenv', 'ip_address')  
      from v$session  
     where audsid = userenv('sessionid');  
  commit;  
end;  

CREATE OR REPLACE TRIGGER TRIGGER1
BEFORE DELETE OR INSERT OR UPDATE OF IP,LOGIN_TIME,MACHINE,USERNAME ON LOGIN_HISTORY
BEGIN
  NULL;
END;

可用的DDL事件
DDL事件 			触发时机
ALTER 				对数据库中的任何一个对象使用SQL的ALTER命令时触发
ANALYZE 			对数据库中的任何一个对象使用SQL的ANALYZE命令时触发
ASSOCIATE STATISTICS 统计数据关联到数据库对象时触发
AUDIT 				通过SQL的AUDIT命令打开审计时触发
COMMENT 			对数据库对象做注释时触发
CREATE 				通过SQL的CREATE命令创建数据库对象时触发
DDL 				列表中所用的事件都会触发
DISASSOCIATE STATISTICS 去掉统计数据和数据库对象的关联时触发
DROP 				通过SQL的DROP命令删除数据库对象时触发
GRANT 				通过SQL的GRANT命令赋权时触发
NOAUDIT 			通过SQL的NOAUDIT关闭审计时触发
RENAME 				通过SQL的RENAME命令对对象重命名时触发
REVOKE 				通过SQL的REVOKE语句撤销授权时触发
TRUNCATE 			通过SQL的TRUNCATE语句截断表时触发 

 

可用属性
Oracle 提供了一系列的函数用来提供关于什么触发了DDL触发器以及触发器的状态灯信息。上面那个触发器的例子就使用了属性。
DDL触发器事件以及属性函数
函数名 							返回值
ORA_CLIENT_IP_ADDRESS 			客户端IP地址
ORA_DATABASE_NAME 				数据库名称
ORA_DES_ENCRYPTED_PASSWORD 		当前用户的DES算法加密后的密码
ORA_DICT_OBJ_NAME 				触发DDL的数据库对象名称
ORA_DICT_OBJ_NAME_LIST 			受影响的对象数量和名称列表
ORA_DICT_OBJ_OWNER 				触发DDL的数据库对象属主
ORA_DICT_OBJ_OWNER_LIST 		受影响的对象数量和名称列表
ORA_DICT_OBJ_TYPE 				触发DDL的数据库对象类型
ORA_GRANTEE 					被授权人数量
ORA_INSTANCE_NUM 				数据库实例数量
ORA_IS_ALTER_COLUMN 			如果操作的参数column_name指定的列,返回true,否则false
ORA_IS_CREATING_NESTED_TABLE 	如果正在创建一个嵌套表则返回true,否则false
ORA_IS_DROP_COLUMN 				如果删除的参数column_name指定的列,返回true,否则false
ORA_LOGIN_USER 					触发器所在的用户名
ORA_PARTITION_POS 				SQL命令中可以正确添加分区子句位置
ORA_PRIVILEGE_LIST 				授予或者回收的权限的数量。
ORA_REVOKEE 					被回收者的数量
ORA_SQL_TXT 					触发了触发器的SQL语句的行数。
ORA_SYSEVENT 					导致DDL触发器被触发的时间
ORA_WITH_GRANT_OPTION 			如果授权带有grant选项,返回true。否则false
更多属性函数请参考官方文档PL/SQL Language Reference -> Triggers and Oracle Database Data Transfer Utilities

 

创建触发器

CREATE OR REPLACE TRIGGER test.no_drop BEFORE DDL ON DATABASE BEGIN IF ORA_SYSEVENT = 'CREATE' THEN DBMS_OUTPUT.PUT_LINE('Warning !!! You have created a '|| ORA_DICT_OBJ_TYPE ||' called '|| ORA_DICT_OBJ_NAME|| '; UserName(creater):'|| ORA_DICT_OBJ_OWNER||'; IP:'|| ORA_CLIENT_IP_ADDRESS||'; event:'|| ORA_SYSEVENT); ELSIF ORA_SYSEVENT = 'DROP' THEN RAISE_APPLICATION_ERROR (-20000, 'Cannot create the ' || ORA_DICT_OBJ_TYPE || ' named ' || ORA_DICT_OBJ_NAME || ' as requested by ' || ORA_DICT_OBJ_OWNER); END IF; END;

测试触发器
set serveroutput on
CREATE TABLE "TEST"."T4"
  (
    "PRODUCT_ID"   NUMBER,
    "PRODUCT_NAME" VARCHAR2(80 BYTE),
    "MONTH"        NUMBER
  );
drop table t4 purge;

 

 

package包的使用

SET SERVEROUTPUT ON SIZE 40000
DECLARE
req UTL_HTTP.REQ;
resp UTL_HTTP.RESP;
value VARCHAR2(1024);
BEGIN
--UTL_HTTP.SET_PROXY('proxy.my-company.com', 'corp.my-company.com');
req := UTL_HTTP.BEGIN_REQUEST('http://www.ka1che.com');
UTL_HTTP.SET_HEADER(req, 'User-Agent', 'Mozilla/4.0');
resp := UTL_HTTP.GET_RESPONSE(req);
LOOP
UTL_HTTP.READ_LINE(resp, value, TRUE);
DBMS_OUTPUT.PUT_LINE(value);
END LOOP;
UTL_HTTP.END_RESPONSE(resp);
EXCEPTION
WHEN UTL_HTTP.END_OF_BODY THEN
UTL_HTTP.END_RESPONSE(resp);
END;

 

 

==================================================

http://blog.csdn.net/ls_man/article/details/11981967

创建过程来自动杀死相关session

GRANT create view to PERSONAL;
grant alter system to personal;

create view mysession as  
select SID,SERIAL# from v$session
where schemaname in ('TEST1','TEST2');

create user test1 identified by 123456;
create user test2 identified by 123456;
grant connect,resource to test1,test2;



create or replace procedure kill_session is  
  v_sid    varchar2(30);  
  v_serial varchar2(30);  
  v_sql    varchar2(1000);  
  TYPE DyData IS REF CURSOR;  
  rows DyData;  
begin  
 
  v_sql := 'select * from mysession ';  
  OPEN rows FOR v_sql;  
  LOOP  
    FETCH rows  
      into v_sid, v_serial;  
    
    v_sql := 'alter system kill session ''' || v_sid || ',' || v_serial || '''';  
    execute immediate v_sql;  
    --dbms_output.put_line(v_userid || '__' || v_user || '__' || v_pwd);  
    
    EXIT WHEN rows%NOTFOUND;  
  END LOOP;  
 
end kill_session;  


======================================


Oracle wrap 和 unwrap( 加密与解密) 说明
http://blog.chinaunix.net/uid-7589639-id-3767585.html

wrap 的使用步骤如下:
(1)将我们要加密的sql 语句保存到一个sql文本里。
(2)用wrap 进行处理,指定输入的sql,即我们第一步的问题,然后指定输出的路径和文件名,默认扩展名是plb。
(3)执行我们第二步进过wrap 处理的sql,即plb文件,创建我们的对象.

命令行界面
cat dave.sql
wrap iname=dave.sql
cat dave.plb

sqlplus界面
@dave.plb
select F_DAVE(4) from dual;
select text from dba_source where name='F_DAVE';

wrap的目的是为了加密,所以Oracle并没有提供unwrap 的方法。 itpub上的一些牛人研究了一下这个问题,写了一些unwrap的代码。 具体讨论的过程,参考itpub的2个帖子:

阿里巴巴的张端弄了一个界面的Unwrap软件,下载地址:

 

posted on 2017-05-26 15:30  阳光-源泉  阅读(385)  评论(0编辑  收藏  举报

导航