首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

4.2  理解体系结构

本节将描述、比较和对照两种模式的体系结构。通过比较,我们将演示定义者权限模式的优点。

介绍两种体系结构的原因:

在这里我们需要知道两种模式各自的优点和不足,以便在实际中能很好地选择其中一种体系结构来满足应用程序的要求。就像前面描述的那样,当单个用户仅能看到数据的概念视图时,定义者权限非常适合集中式的代码仓库和数据。另一方面,调用者权限对于分布式应用程序和数据来说又是非常有效的。然而,我们需要说明为什么会这样认为,本节将解释其原因。

4.2.1  理解定义者权限体系结构

在定义者权限体系结构中数据像萨拉米香肠一样被切成了一片一片的,每个用户只能看见整个数据的一小片,这是最常见的方法。下面的插图4-1通过演示4个用户各自所能看到的信息片说明了定义者权限的概念。

图4-1  各用户看到的信息片

定义者权限模式要求将数据集中在一个概念文件中。如果有一些用户,它们只能看到数据的一个子集,那我们就需要建立一些关于用户安全级别的表格,以划分终端用户可以看到的内容。一个通用的方法是根据会话信息来创建用户视图。这就要求(a)设置用于数据库连接的CLIENT_INFO值,(b)创建表的条状视图。

下一节我们将介绍定义者权限体系结构实现的方法。

1. 设置CLIENT_INFO值

在设置之前,假定您会在PLSQL模式下对CLIENT_INFO值进行测试。

DBMS_APPLICATION_INFO包用于为连接或会话设置CLIENT_INFO值。这个包中有两个过程,它们的签名稍有不同,SET_CLIENT_INFO过程的签名是一个IN模式的VARCHAR2变量,而READ_CLIENT_INFO过程的签名则是一个OUT模式的VARCHAR2变量。如下所示:

过程名和签名

SET_CLIENT_INFO(client_info IN VARCHAR2)

READ_CLIENT_INFO(client_info OUT VARCHAR2)

SET_CLIENT_INFO过程的作用是存储V$SESSION.CLIENT_INFO列的信息,这列的长度是64个字符。当使用这种方法时,应该使用编码模式以获得最大的分配空间。下面的脚本说明了怎样在PL/SQL中设置和检索CLIENT_INFO值,设置完后,在PLSQL和MYAPP两种模式下测试CLIENT_INFO值。

-- Available online as part of demo_client_info1.sql file.

-- Anonymous block program to demonstrate DBMS_APPLICATION_INFO package.

DECLARE

-- Declare a test variable.

my_env VARCHAR2(1);

BEGIN

-- Read CLIENT_INFO into a variable.

DBMS_APPLICATION_INFO.READ_CLIENT_INFO(my_env);

-- Print the current value of CLIENT_INFO.

DBMS_OUTPUT.PUT_LINE('Read CLIENT_INFO value ['||my_env||'].');

-- Display message.

DBMS_OUTPUT.PUT_LINE('Set CLIENT_INFO to [1].');

-- Set the value to one.

DBMS_APPLICATION_INFO.SET_CLIENT_INFO('1');

-- Read CLIENT_INFO into a variable.

DBMS_APPLICATION_INFO.READ_CLIENT_INFO(my_env);

-- Print the current value of CLIENT_INFO.

DBMS_OUTPUT.PUT_LINE('Read CLIENT_INFO value ['||my_env||'].');

END;

/

脚本执行了如下3个操作:

●       读取V$SESSION.CLIENT_INFO的默认值或当前值,并打印在控制台上。

●       设置V$SESSION.CLIENT_INFO的值,并打印在控制台上。

●       读取V$SESSION.CLIENT_INFO修改后的值或当前值,并打印在控制台上。

只要我们还没有设置CLIENT_INFO的值,那么LSQL和MYAPP模式中这个脚本都可以显示下面的输出:

Read CLIENT_INFO value [].

Set CLIENT_INFO to [1].

Read CLIENT_INFO value [1].

注意:

如果我们没有看到V$SESSION.CLIENT_INFO是空值,断开并重新连接SQL会话。

除非用户得到允许,否则任何试图验证V$SESSION.CLIENT_INFO内容的操作都会失败,并产生下面的错误:

FROM     V$SESSION

*

ERROR at line 2:

ORA-00942: table or view does not exist

如果断开连接并以SYSTEM用户或拥有DBA特权的授权用户重新连接,那么您通常不能看见V$SESSION视图中CLIENT_INFO列的值。如果您拥有DBA权限,那么通过创建一个新的会话并查询V$SESSION视图,您可以看见自己的CLIENT_INFO信息。

另一个方法是使用SQL的USERENV 函数和dual 表,就像demo_client_info2.sql脚本中给出的那样。这个脚本通过执行DBMS_APPLICATION_INFO.SET_CLIENT_INFO过程设置CLIENT_INFO,同时使用了select语句对其进行查询。

-- Available online as part of demo_client_info2.sql file.

-- Select the default value from V$SESSION.CLIENT_INFO.

SELECT   USERENV('CLIENT_INFO')

FROM     dual;

-- Execute the DBMS_APPLICATION_INFO package.

EXECUTE DBMS_APPLICATION_INFO.SET_CLIENT_INFO('1');

-- Select the overridden value from V$SESSION.CLIENT_INFO.

SELECT   USERENV('CLIENT_INFO')

FROM      dual;

demo_client_info2.sql脚本的执行不受权限限制,执行脚本后输出如下:

USERENV('CLIENT_INFO')

----------------------------------------------------------------

<Null>

USERENV('CLIENT_INFO')

----------------------------------------------------------------

1

USERENV('CLIENT_INFO')

----------------------------------------------------------------

1

小提示:

<Null>的显示是通过SQL*Plus命令SET NULL"<Null>"产生的,这样的设置在空值返回调试时,对我们是很有帮助的。

通过上面的内容,我们已经了解了在PL/SQL和SQL中设置和检索CLIENT_INFO值的方法。下面介绍实现数据条状视图的方法。

2. 创建表的条状视图

如果您知道几个窍门并理解了发布-订阅方法(在面向对象的程序设计中称为观察者模式),那么条状表对您来说是相当简单的。

发布-订阅模式的意思是一个程序发布某个事件,其他程序会订阅这个事件以便在事件发布时读取它。对于条状视图来说,视图就是发布事件的那个程序,而能够读取视图内容的用户就是订阅者。

这些视图有两个关键窍门:它们必须在运行时验证会话信息,以及它们之间存在两个动态的相关性。一个相关性是订阅者必须在他们的会话中设置CLIENT_INFO值,另一个相关性是视图中必须存在这样一列,其值是每个数据条的一个非惟一的标识符。

在本节中我们将使用MYAPP模式以及PLSQL模式,也就是说,我们应该在MYAPP和PLSQL两种模式下连接会话。

create_striped_view1.sql脚本创建了具有一个非惟一的标识符列striping_id的视图。您应该注意到了这一列的值是非数字的。但如果在PLSQL模式下执行脚本,则会返回数字的列值和CLIENT_INFO值。

-- Available online as part of create_striped_view1.sql file.

-- Drop shared table.

DROP TABLE shared_all;

-- Create shared table.

CREATE TABLE shared_all

( shared_id NUMBER

, shared_text VARCHAR2(20 CHAR)

, striping_id VARCHAR2(10 CHAR));

-- Select the default value from V$SESSION.CLIENT_INFO.

CREATE OR REPLACE VIEW shared

SELECT     shared_id

,          shared_text

FROM       shared_all

WHERE      NVL(TO_NUMBER(striping_id),0) =

NVL(TO_NUMBER(SUBSTR(USERENV('CLIENT_INFO'),1,10)),0)

-- Insert a non-striped row.

INSERT

INTO shared_all

VALUES

( 1,'One','');

-- Insert a striped row.

INSERT

INTO shared_all

VALUES

( 2,'Two','1');

-- Insert a striped row.

INSERT

INTO shared_all

VALUES

( 3,'Three','2');

脚本执行以下任务:

●       删除并创建共享数据仓库shared_all表,并将shared_all. striping_id作为非惟一的标识符。

●       创建了一个共享视图,视图用于发布基于条纹标识符返回的动态内容。这可通过表中数据的空值函数来计算shared_all. striping_id列的内容,同时将返回值与USERE-NV函数的封闭函数集合合并。后面由里至外做了如下事情:

  ●    为当前会话捕获V$SESSION.CLIENT_INFO值,这个值以64位字符长的VARCHAR2类型返回。

  ●    使用SUBSTR函数截取V$SESSION.CLIENT_INFO的前10个字符,并转换为字符串。

  ●    使用TO_NUMBER函数将被解析的字符串转换为数字。

  ●    使用NVL函数检查数字是否为空。

  ●    将条状视图的select、insert、update和delete权限授予MYAPP模式。

●       在shared_all表插入3行值以适应不同的条状标准。

●       使用下面select语句返回表中的所有值。

COL shared_text FORMAT A11

COL shared_id FORMAT A11

SELECT *

FROM shared_all;

在PLSQL模式下运行脚本,返回结果如下:

SHARED_ID SHARED_TEXT STRIPING_ID

---------- ----------- ---------------

1 One          <Null>

2 Two          1

3 Three        2

现在我们已经建立发布-订阅模式的发布方。现在转换到MYAPP模式的会话,运行下面的subscribing.sql脚本:

-- Available online as part of subscribing.sql file.

-- Drop synonym to the striped view.

DROP SYNONYM shared;

-- Create synonym to the striped view.

CREATE SYNONYM shared FOR plsql.shared;

-- Select the default value from V$SESSION.CLIENT_INFO.

SELECT USERENV('CLIENT_INFO')

FROM dual;

-- Format column value returns.

COL shared_id FORMAT 990

COL shared_text FORMAT A20

COL userenv_col FORMAT A10

-- Select the non-striped values from the view.

SELECT shared_id

,       shared_text

,       USERENV('CLIENT_INFO') userenv_col

FROM   shared;

-- Execute the DBMS_APPLICATION_INFO package.

EXECUTE DBMS_APPLICATION_INFO.SET_CLIENT_INFO('1');

COL shared_id FORMAT 990

COL shared_text FORMAT A20

COL userenv_col FORMAT A10

-- Select the overridden value from V$SESSION.CLIENT_INFO.

SELECT  shared_id

,         shared_text

,         USERENV('CLIENT_INFO') userenv_col

FROM     shared;

脚本执行以下任务:

●       删除并创建条状shared视图的同义词。

●       验证会话的CLIENT_INFO值是空。

●       将CLIENT_INFO值设为1。

●       设置输出的格式,避免在SQL*Plus中输出超过一行。

●       查询条状shared视图和CLIENT_INFO值。

您能看到它映射插入表中的第二行数据。

注意:

如果在早些时候已经设置了CLIENT_INFO值,那我们应该清除这些设置。使用EXECUTEdbms_application_info.set_client_info(NULL)语句重新设置CLIENT_INFO值的默认空值。

使用条状化的原因:

我们需要理解的是为什么条状化是非常重要的,这对正确使用Oracle应用程序至关重要。它的重要性在于统一的定义者权限模式中,使用条状化建立的概念视图可以限制用户访问。正如您所看到的那样,这项技术有助于在集中式的数据格式中建立功能强大的应用程序。我们得感谢Oracle通过应用程序的划分来实现这项技术,从而解决了在同一数据库中存在多个组织的问题。

下面的输出没有设置CLIENT_INFO值,因为表中的第一行不是条状化的:

SHARED_ID SHARED_TEXT           USERENV_CO

--------- -------------------- ----------

1 One                   <Null>

因为shared_all表的第2行记录的非惟一的条状标示符的值等于您的CLIENT_INFO值,所以在设置CLIENT_INFO值后应该返回下列输出:

SHARED_ID SHARED_TEXT           USERENV_CO

--------- -------------------- ----------

2 Two                   1

注意:

Oracle应用程序必须使用定义者权限体系结构才能生效,在会话连接的过程中会保存几个数值,这些数值用于为多组织、多币种报表(multiple reporting currencies)以及应用程序体系结构的其他方面保留条状化。

此种实现的一个替换方法是定义条状表中的数据类型为数字。这避免了在共享视图中使用TO_NUMBER函数处理非惟一的条状化列。在create_striping_view2.sql脚本中应用了PLSQL模式创建视图,但创建视图的方法有所改变,就像下面这样:

-- Available online as part of create_striped_view2.sql.

-- Build a striped view.

CREATE OR REPLACE VIEW shared AS

SELECT  shared_id

,        shared_text

FROM     shared_all

WHERE    NVL(striping_id,0) =

NVL(TO_NUMBER(SUBSTR(USERENV('CLIENT_INFO'),1,10)),0);

小提示:

我们可以通过连接同一模式清除CLIENT_INFO。例如,如果当前是MYAPP用户,那么键入CONNECTION MYAPP/MYAPP命令,我们就可以清除CLIENT_INFO值以便重新测试示例的代码。

注意:

Oracle应用程序使用数据表中的ORG_ID列,并加上后缀_ALL来条状化子系统数据。Oracle实现条状化的方法就像旧的车轮,像应付账或者总账这样的子系统在辐条的末端。子系统授予了中心代码仓库select、insert、update和delete权限。子系统之间的所有接口都要经过中心代码仓库。

现在我们已经学习了创建和实现定义者权限的方法,下面将介绍调用者权限方法的创建和实现。

4.2.2  理解调用者权限体系结构

调用者权限体系结构中的数据可能会也可能不会分切成片。调用者权限体系结构提供了两种类型的实现策略。一种是支持处理分布式数据的方法,另一种方法则支持处理集中式数据,它非常类似于前面讨论的使用条状表来实现定义者权限体系结构的方法。

使用调用者权限体系结构的原因:

在运行处理分布式数据的应用程序时,您需要知道使用调用者权限体系结构的方法。我们在利用相同的代码仓库时,总是使用调用者权限体系结构来确保分布式数据的完整性。调用者体系结构为建立功能强大的应用程序提供了保证。

有两个通用原则适用于所有实现策略。一个原则是遵循一个通用命名模式控制数据仓库的集中,另一个原则是只保存应用程序的单一副本来控制代码仓库的集中。我们只需要注意分布式的解决方案,因为定义者权限通常都是使用条状化方法。

如果因为某些原因如距离太远、资源或生产能力不足,不能实现分布式系统,那么调用者权限体系结构能够支持实现完全的分布式方法。对于Oracle的数据库的分布式计算来说,这是一个廉价的工具。

调用者权限体系结构能支持同一数据库或分布式数据库中的多个数据集共享单一的代码仓库。下面的插图4-2说明了调用者权限体系结构:

图4-2  调用者权限体系结构

在上面的图中,4个数据仓库共享一个代码仓库,而示例代码简化可了这种体系结构,只使用一个MYAPP模式的数据仓库,另外,示例中的代码仓库是PLSQL模式的。

create_invoker_package.sql脚本创建了data_store样表,同时允许用户向data_store表中插入数据,在PLSQL模式下运行下面的代码:

-- Available online as part of create_striped_view2.sql.

-- Create local table.

CREATE TABLE data_store

( data_id NUMBER

, data_text VARCHAR2(20 CHAR));

-- Create the data management package.

CREATE OR REPLACE PACKAGE data_management

AUTHID CURRENT_USER AS

-- Define the insert_data procedure.

PROCEDURE insert_data

( data_id NUMBER

, data_text VARCHAR2);

END data_management;

/

-- Create the data management package body.

CREATE OR REPLACE PACKAGE BODY data_management AS

-- Implement the insert_data procedure.

PROCEDURE insert_data

( data_id NUMBER

, data_text VARCHAR2) IS

BEGIN

-- Insert into the table.

INSERT

INTO data_store

VALUES

( data_id

, data_text );

END insert_data;

END data_management;

/

GRANT EXECUTE ON data_management TO MYAPP;

/

脚本执行以下任务:

●       删除并创建本地表data_store。

●       定义data_management包的包规格,包中含有一个过程insert_data。

●       创建包主体,并实现insert_data过程。

●       授予MYAPP模式data_management包的执行特权。

我们已经完成了代码仓库的安装,现在开始安装数据仓库,这需要2到3个步骤,如下所示:

(1) 创建指向PLSQL.DATA_MANAGEMENT包的同义词。

(2) 创建一个表,表的名称应与它的调用者权限模式的名称相同,或者另取一个名称。

(3) 创建一个视图,如果表有另外一个名称,那么视图名称应和调用者权限模式中目标表的名称相同。您可以使用下面的create_invoker_data_store.sql脚本配置MYAPP数据存储模式,在MYAPP模式下运行如下脚本:

-- Available online as part of create_invoker_data_store.sql.

CREATE SYNONYM data_management FOR plsql.data_management;

-- Create local table.

CREATE TABLE myapp_data_store

( data_id       NUMBER

, data_text      VARCHAR2(20 CHAR));

-- Create a local view that mirrors the code repository.

CREATE OR REPLACE VIEW data_store AS

SELECT *

FROM    myapp_data_store;

-- Call the invoker's-right stored package.

EXECUTE data_management.insert_data(1,'One');

-- Use SQL*Plus to format the output.

COL data_text FORMAT A20

-- Query the local MYAPP_DATA_STORE table.

SELECT    data_id

,          data_text

FROM      myapp_data_store;

该脚本执行以下任务:

●       删除并创建plsql.data_management包的本地同义词。

●       定义本地表myapp_data_store,表的名称与它在调用者权限模式中使用的名称不相同。这就要求data_store视图的名称和PLSQL模式中的表名相同。

●       执行plsql.data_management.insert_data过程,通过本地data_store视图向myapp_data_store表中插入CURRENT_USER对象中的一行记录。

●       设置myapp_data_store.data_text列的格式,然后查询本地表。

查询返回下面的结果:

DATA_ID DATA_TEXT

---------- --------------------

1 One

现在我们已经了解了调用者权限的体系结构,它与定义者体系结构在某些地方有明显的区别。下面将对这两种体系结构进行比较和对照。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论