Chinaunix首页 | 论坛 | 博客
  • 博客访问: 103678926
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类: Oracle

2008-05-23 13:13:59

  来源:

在设计系统时经常需要存储很多配置参数,如果配置参数使用文件存储,那么当系统需要做集群或者双机热备等需要将系统部署到多个地方时,将对参数的更改带来非常复杂以及烦琐的:不得不修改每个系统拷贝的配置文件.
因此将参数使用数据库存储不失为一个好办法,但是关系数据库是二维数据,数据的展现能力不足,如果能够类似于windows注册表一样提供树型参数配置,那么参数配置将变得非常灵活。

数据库使用oracle9i:
建表脚本:
存储树的上下级关系
create table PROPERTY_NODE
(
  ID             NUMBER not null,
  NAME           VARCHAR2(32),
  PARENT_NODE_ID NUMBER
)
存储某个节点上的属性值
create table PROPERTY_NODE_VALUE
(
  ID          NUMBER not null,
  NODE_ID     NUMBER,
  NAME        VARCHAR2(32),
  VALUE       VARCHAR2(1024),
  DESCRIPTION VARCHAR2(1024)
)
创建属性与节点的约束关系,定义为级联删除
alter table PROPERTY_NODE_VALUE
  add constraint PROPERTY_NODE_VALUE_FK foreign key (NODE_ID)
  references PROPERTY_NODE (ID) on delete cascade;
 
下面是对树的操作:
设置属性值:
create or replace procedure set_property(property_Node varchar2,property_name varchar2,property_value varchar2,DESCRIPTION varchar2)  is
  property_Node_TEMP varchar(1024);
  nodeId number;
  parentNodeId number;
  currentNodeName varchar(32);
  preSlashIndex number;
  currentSlashIndex number;
  i number;
  nodeCount number;
begin

  property_Node_TEMP:=TRIM(property_Node); 
  --开始必须为根节点
  if 1!=instr(property_Node_TEMP,'/',1,1)
     then
          return;
  end if;
  if 2=length(property_Node_TEMP)
     then
         return;
  end if;
  --已‘/’结尾
  if instr(property_Node_TEMP,'/',-1,1)!=length(property_Node_TEMP)
     then
         property_Node_TEMP:=property_Node_TEMP||'/';
  end if;
  DBMS_OUTPUT.PUT_LINE('property_Node_TEMP='||property_Node_TEMP);
  preSlashIndex:=1;
  currentSlashIndex:=instr(property_Node_TEMP,'/',1,2);
  i:=2;
  parentNodeId:=-1;
  WHILE currentSlashIndex!=0
  LOOP
     currentNodeName:=substr(property_Node_TEMP,preSlashIndex+1,currentSlashIndex-preSlashIndex-1);
     DBMS_OUTPUT.PUT_LINE('currentNodeName='||currentNodeName);
     preSlashIndex:=currentSlashIndex;
     i:=i+1;
     currentSlashIndex:=instr(property_Node_TEMP,'/',1,i);
     --/*
     --得到该节点的ID
     nodeId:=-1;
     nodeCount:=0;
     DBMS_OUTPUT.PUT_LINE('节点是否存在'||currentNodeName);
     select count(*) into nodeCount from  property_Node p where p.name=currentNodeName and p.parent_node_id=parentNodeId;
     --没有节点
     if 0=nodeCount
        then
            DBMS_OUTPUT.PUT_LINE('创建节点'||currentNodeName);
            insert into property_Node(ID,NAME,Parent_Node_Id) values(PROPERTY_SEQ.nextVal,currentNodeName,parentNodeId);
            commit;           
     end if;
     --
     select ID into nodeId from  property_Node p where p.name=currentNodeName and p.parent_node_id=parentNodeId;
     parentNodeId:=nodeId;
  END LOOP; 
  DBMS_OUTPUT.PUT_LINE('属性节点ID='||parentNodeId);
 
  --删除原来属性
  delete from property_node_value p where p.node_id=parentNodeId and p.name=property_name;
  --插入新值
  insert into property_node_value(ID,node_id,name,value,DESCRIPTION) values(PROPERTY_SEQ.nextVal,parentNodeId,property_name,property_value,DESCRIPTION);
  commit;
 
  EXCEPTION
  WHEN OTHERS
       THEN
            DBMS_OUTPUT.PUT_LINE('异常:'||SQLCODE||SQLERRM);
            return;
end set_property;
使用:call set_property('/root/test','testAttribute','value','描述');


得到属性值:
create or replace function get_property(property_node varchar2, property_name varchar2) return varchar2 is
  Result varchar2(1024);
  property_Node_TEMP varchar(1024);
  nodeId number;
  parentNodeId number;
  propertyValueId number;
  currentNodeName varchar(32);
  preSlashIndex number;
  currentSlashIndex number;
  i number;
  nodeCount number; 
begin
  Result:=null;
  property_Node_TEMP:=TRIM(property_Node); 
  --开始必须为根节点
  if 1!=instr(property_Node_TEMP,'/',1,1)
     then
          return null;
  end if;
  if 2=length(property_Node_TEMP)
     then
         return null;
  end if;
  --已‘/’结尾
  if instr(property_Node_TEMP,'/',-1,1)!=length(property_Node_TEMP)
     then
         property_Node_TEMP:=property_Node_TEMP||'/';
  end if;
  --DBMS_OUTPUT.PUT_LINE('property_Node_TEMP='||property_Node_TEMP);
  preSlashIndex:=1;
  currentSlashIndex:=instr(property_Node_TEMP,'/',1,2);
  i:=2;
  parentNodeId:=-1;
  WHILE currentSlashIndex!=0
  LOOP
     currentNodeName:=substr(property_Node_TEMP,preSlashIndex+1,currentSlashIndex-preSlashIndex-1);
     --DBMS_OUTPUT.PUT_LINE('currentNodeName='||currentNodeName);
     preSlashIndex:=currentSlashIndex;
     i:=i+1;
     currentSlashIndex:=instr(property_Node_TEMP,'/',1,i);
     --/*
     --得到该节点的ID
     nodeId:=-1;
     nodeCount:=0;
     --DBMS_OUTPUT.PUT_LINE('节点是否存在'||currentNodeName);
     select count(*) into nodeCount from  property_Node p where p.name=currentNodeName and p.parent_node_id=parentNodeId;
     --没有节点
     if 0=nodeCount
        then
                return null;           
     end if;
     --
     select ID into nodeId from  property_Node p where p.name=currentNodeName and p.parent_node_id=parentNodeId;
     parentNodeId:=nodeId;
  END LOOP; 
  --DBMS_OUTPUT.PUT_LINE('属性节点ID='||parentNodeId);
 
  select value into RESULT from property_node_value p where p.node_id=parentNodeId and p.name=property_name;
  return Result;
end get_property;
使用:select get_proerty('/root/test','attributeName') from dual;

得到子节点:
create or replace function get_property_child(property_Node varchar2) return varchar2 is
  Result varchar2(1024);
  property_Node_TEMP varchar(1024);
  nodeId number;
  parentNodeId number;
  propertyValueId number;
  currentNodeName varchar(32);
  preSlashIndex number;
  currentSlashIndex number;
  i number;
  nodeCount number; 
  TYPE cv_typ IS REF CURSOR;
  cv cv_typ;
begin
  Result:=null;
  property_Node_TEMP:=TRIM(property_Node); 
  --开始必须为根节点
  if 1!=instr(property_Node_TEMP,'/',1,1)
     then
          return null;
  end if;
  if 2=length(property_Node_TEMP)
     then
         return null;
  end if;
  --已‘/’结尾
  if instr(property_Node_TEMP,'/',-1,1)!=length(property_Node_TEMP)
     then
         property_Node_TEMP:=property_Node_TEMP||'/';
  end if;
  preSlashIndex:=1;
  currentSlashIndex:=instr(property_Node_TEMP,'/',1,2);
  i:=2;
  parentNodeId:=-1;
  WHILE currentSlashIndex!=0
  LOOP
     currentNodeName:=substr(property_Node_TEMP,preSlashIndex+1,currentSlashIndex-preSlashIndex-1);
     preSlashIndex:=currentSlashIndex;
     i:=i+1;
     currentSlashIndex:=instr(property_Node_TEMP,'/',1,i);
     --/*
     --得到该节点的ID
     nodeId:=-1;
     nodeCount:=0;
     select count(*) into nodeCount from  property_Node p where p.name=currentNodeName and p.parent_node_id=parentNodeId;
     --没有节点
     if 0=nodeCount
        then
                return null;           
     end if;
     --
     select ID into nodeId from  property_Node p where p.name=currentNodeName and p.parent_node_id=parentNodeId;
     parentNodeId:=nodeId;
  END LOOP; 
 
  OPEN cv FOR
       'select NAME from property_node where parent_node_id='||parentNodeId;
       LOOP
          FETCH cv INTO currentNodeName;
          EXIT WHEN cv%NOTFOUND;
          if cv%ROWCOUNT!=1
             then
                 Result:=Result||','||currentNodeName;
          else
              Result:=currentNodeName;
          end if;   
       END LOOP;
       CLOSE cv;
 
  return Result;
end get_property_child;
使用:  select get_property_child('/root') from dual;

得某个节点下的所有属性名称:
create or replace function get_property_attribute_names(property_Node varchar2) return varchar2 is
  Result varchar2(1024);
  property_Node_TEMP varchar(1024);
  nodeId number;
  parentNodeId number;
  propertyValueId number;
  currentNodeName varchar(32);
  preSlashIndex number;
  currentSlashIndex number;
  i number;
  nodeCount number; 
  TYPE cv_typ IS REF CURSOR;
  cv cv_typ;
begin
  Result:=null;
  property_Node_TEMP:=TRIM(property_Node); 
  --开始必须为根节点
  if 1!=instr(property_Node_TEMP,'/',1,1)
     then
          return null;
  end if;
  if 2=length(property_Node_TEMP)
     then
         return null;
  end if;
  --已‘/’结尾
  if instr(property_Node_TEMP,'/',-1,1)!=length(property_Node_TEMP)
     then
         property_Node_TEMP:=property_Node_TEMP||'/';
  end if;
  preSlashIndex:=1;
  currentSlashIndex:=instr(property_Node_TEMP,'/',1,2);
  i:=2;
  parentNodeId:=-1;
  WHILE currentSlashIndex!=0
  LOOP
     currentNodeName:=substr(property_Node_TEMP,preSlashIndex+1,currentSlashIndex-preSlashIndex-1);
     preSlashIndex:=currentSlashIndex;
     i:=i+1;
     currentSlashIndex:=instr(property_Node_TEMP,'/',1,i);
     --/*
     --得到该节点的ID
     nodeId:=-1;
     nodeCount:=0;
     select count(*) into nodeCount from  property_Node p where p.name=currentNodeName and p.parent_node_id=parentNodeId;
     --没有节点
     if 0=nodeCount
        then
                return null;           
     end if;
     --
     select ID into nodeId from  property_Node p where p.name=currentNodeName and p.parent_node_id=parentNodeId;
     parentNodeId:=nodeId;
  END LOOP; 
 
  OPEN cv FOR
       'select NAME from property_node_value where node_id='||parentNodeId;
       LOOP
          FETCH cv INTO currentNodeName;
          EXIT WHEN cv%NOTFOUND;
          if cv%ROWCOUNT!=1
             then
                       Result:=Result||','||currentNodeName;
          else
              Result:=currentNodeName;
          end if;
       END LOOP;
       CLOSE cv;
  return Result;
end get_property_nodes;
使用:select get_property_attribute_names('/root/test') from dual; 
还需要扩充的地方是参数配置的多元化:既一个参数存在多个值,这类似于化的支持:一个值对应多种语言版本.这个功能比较好实现,只需要在属性名称中增加命名规范即可,修修改get_property,添加一个参数设置读取哪一个版本,默认读取默认版本.在这儿就不在详述了
可以创建一个view给配置参数一个良好的展示方式
create or replace view properties as
select step,name,attributevalues
    from
(
select step,name,sum_string('select name||''=''||value from property_node_value where node_id='||id,'★') attributevalues
from
(
select step,ID,sum_string_count(step,'      ')||name as name from
(
select level as step,ID,name from property_node start with name='root' connect by prior id=parent_node_id
--and level<=2
)
)
)

其中用到的函数定义:

create or replace function sum_string(sqlString varchar2,splitSeg varchar2) return varchar2 is
  Result varchar2(4000);
  temp varchar(1024);
  arrayTemp dbms_sql.Varchar2_Table;
begin
  EXECUTE IMMEDIATE sqlString BULK COLLECT INTO arrayTemp ;
  if arrayTemp.count=0
     then
         return '';
  end if;
  Result:=arrayTemp(1);
  for i in 2..arrayTemp.count LOOP
      Result:=Result||splitSeg||arrayTemp(i);
  end loop;
  return(Result);
end sum_string;

create or replace function SUM_STRING_COUNT(v_count in number, word in varchar2) return varchar2 is
  Result varchar2(1024);
  i number;
begin
  i:=0;
      LOOP
          exit when i>=v_count;
          Result:=Result||word;
          i:=i+1;
      end loop;
  return(Result);
end SUM_STRING_COUNT;

阅读(336) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~