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