高等資料庫 物件導向資料庫(二)
高等資料庫報告
物件導向資料庫(二)
The Object Database Standard:
ODMG-93
指導老師:陳彥良教授
學 生:吳岳樺 87423011
麥孟生 87423013
88.4.12
目 錄
目 錄………………………………………………………………………… | 1 |
第一章 物件導向資料庫基本概念………………………………………… | 2 |
1.1 起源………….……………………………………………………….. | 2 |
1.2 ODMG93-物件導向資料庫新標準………….………………………. | 4 |
第二章 ODMG物件模式…………………………………………………… | 7 |
2.1 概要………………….……………………………………………….. | 7 |
2.2 型態及實例……….………………………………………………….. | 9 |
2.3 物件……………….………………………………………………….. | 10 |
2.4 模式狀態-性質………………………………………………………. | 13 |
2.5 模式行為-操作………………………………………………………. | 15 |
2.6 結構化物件…………………………………………………………... | 15 |
2.7 結構化實字…………………………………………………………… | 21 |
2.8 完整的內定型態階層………………………………………………… | 21 |
2.9 交易…………………………………………………………………… | 24 |
2.10 資料庫型態的操作…………………………………………………. | 26 |
第三章 物件定義語言………………………………………………………. | 27 |
3.1 引言…………………………………………………………………… | 27 |
3.2 規格…………………………………………………………………… | 28 |
3.3 ODL的範例…………………………………………………………… | 31 |
3.4 ODL文法……………………………………………………………… | 34 |
第四章 物件查詢語言………………………………………………………. | 39 |
4.1 基本原則……………………………………………………………… | 39 |
4.2 查詢的輸入和結果…………………………………………………… | 39 |
4.3 物件識別……………………………………………………………… | 40 |
4.4 語言描述……………………………………………………………… | 42 |
第五章 C++鏈結…………………………………………………………. | 48 |
5.1 引言…………………………………………………………………… | 48 |
5.2 C++物件定義語言…………………………………………………….. | 51 |
5.3 C++物件處理語言…………………………………………………….. | 55 |
參考文獻………………………………………………………………………. | 64 |
第一章 物件導向資料庫基本概念
起源
物件導向資料庫的起源,是由於物件導向程式語言的興盛而產生的。程式設計師需要一個儲存庫來儲存永久存在的資料。物件導向資料庫對於某些應用軟體特別重要,例如CAD及CAE,因為CAD及CAE會產生大量的二進位格式的物件例如設計圖,使用者在存取這些資料的時候,不單單只是希望存取資料而已,並且希望能夠得到關於存取這些資料的一些程序,這些程序是為了用來控制資料的整合性及安全性。
在電腦程式經過了好幾代的發展後,程式設計師漸漸瞭解物件導向技術對於程式的開發會有很大的幫助,物件導向的技術用在資料庫上,就成為了物件導向資料庫。另一方面,傳統關連式資料庫所不能達到的各種要求,漸漸的促使新一代資料庫管理系統的產生。並且由於資料庫漸漸的變成框架導向(frame-oriented),其中每一個框架是一種物件,包括與其相關的法則所組成。也有越來越多種的抽象型別資料需要支援,例如音效、影像及Video。複雜的資料需要特別的存取技術,使得其效能高於關連式資料庫,以上的需求促使了物件導向資料庫的誕生。在此以條列方式說明物件導向資料庫誕生的原因:
關連式資料庫的效能不能使大家滿意
由於大型二進位資料物件型態(如聲音、影像、Video)的大量盛行,傳統關連式資料庫無法處理這些資料。
有些系統需要永久性存在的資料,如專家系統
使用者自訂資料型別及抽象資料型別的大量使用
由於物件導向語言的興盛,物件導向資料庫因此漸漸被重視,物件導向資料庫被設計可與物件導向程式語言整合得很好,資料庫語言沒有必要與程式設計語言不同。例如有些物件導向資料庫使用C++來作為所有資料庫的定義與處理。相同的物件模式亦可用於分析與設計,分析師決定高階物件及高階的行為,設計師將其實作到較低階的物件,低階的物件繼承了高階物件的特質及行為。有了物件導向軟體工程工具,物件一被做出,程式碼就可立即的被翻譯出來。
在1980年代的末期,第一代的物件導向資料庫系統出現了,早期的系統被設計成物件導向程式語言的延伸。其資料處理語言(DML)及資料定義語言(DDL)是一種共同的物件導向語言,現代的物件導向資料庫設計,應該要利用電腦輔助軟體工程所創造出來方法,包括宣告性敘述、程式碼產生器及法則式的推理。物件導向式資料庫管理系統(OO DBMS)標準的訂定最初由ANSI的資料庫系統研究群(ANSI Database Systems Study Group)首先在1991年發表了關於物件導向式資料庫管理與未來標準訂定的相關報告。接著有兩個機構相繼為OO DBMS的標準訂定投注人力,分別是:
1.Object Database Management Group(ODMG)-Object Database Management Group是一個物件導向資料庫廠商所組成的聯盟體制,該聯盟在1992年成立,目標在訂定物件導向式資料庫管理系統上的各項相關標準。該聯盟在歷經一年半的努力後,於1993年10月公佈了第一版的標準-ODMG-93,其中定義了資料模式、查詢語言等。
2.ANSI Object SQL委員會(簡稱ANSI SQL3委員會)-此委員會的標準制定涵蓋面較廣,包含了各式各樣的資料庫規則與觸發條件等。
Alltalk-由Eastman Kodak公司發展。
GemStone-由Servio Corp. Oragon Graduate Center所發展。
GENESIS-由德州大學奧斯汀分校所發展。
IRIS- 由Hewlett-Packard Lab.所發展。
Itasca-由Itasca Systems所發展。
O2-由法國的O2 Technology公司所發展。
Object Database-由Object Database,Inc. 所發展。
Objectivity/DB-由Objectivity,Inc.公司所發展。
ObjectStore-由Object Design Inc.所發展。
ONTOS-ONTOS公司所發展。
Versant-由Won Kim博士所成立的UniSQL Corp.所發展。
ORION-由MCC公司所發展。
表1-1 著名的物件導向資料庫管理系統
系統名稱 | 發展單位 | 關聯式( R )或語言的擴充(L) | 應用範圍與發展標的 |
ORION | MCC | WiSS( R ) | CAD/CAM,AI,OIS |
IRIS | HP | HP-SQL( R ) | OIS,AI,CAD,CASE |
GemStone | Servio Logic | Smalltalk(L) | CAD,AI,OIS,超媒體,太空 |
O2 | Altair | O2C,C,C++(L) | CAD/CAM,CASE,OIS |
Ontos | Ontologic | C++(L) | CAD/CAM,CASE,OIS,多媒體,文件處件 |
Versant | Versant Object | C++(L) | CAD/CAM,OA,CASE,電子出版系統 |
ObjectStore | Object Design | C++(L) | CAD/CASE,CAP,OIS |
表1-2 物件導向式資料庫管理系統的語言擴充與應用標的
ODMG93-物件導向資料庫新標準
ODMG在1994年又修訂ODMG-93為1.1版,主要是將1.0版的內容中不一致的部份加以修正。1995年底又進一步發表1.2版,在這版中加入一些重要的特性如:訂定了與SQL92相容的部份,以及與ANSI C++相關的一些特性。
在1996年IEEE的「國際資料工程研討會」(IEEE International Conference on Data Engineering)中,POET軟體公司的D.Bartels針對ODMG93發表論文,公佈新增的部份:「ODMG物件模式」(ODMG Object Model)、「ODMG物件定義語言」(ODMG Object Definition Language-ODL) 、「ODMG物件查詢語言」(ODMG Object Query Language-OQL)與「ODMG C++和Smalltalk的聯繫」。
ODMG架構
ODMG希望在使用介面上是與現存的物件導向程式語言,如:C++、Smalltalk等,直接以緊密結合(Tightly -Binding)的方式溝通。這與關聯式資料庫中所使用的SQL不一樣,因為在SQL中,DDL與DML在與程式語言的聯繫上,都是以較鬆散的嵌入式SQL來做。因此,對於使用者以ODL(或依某特定程式語言所訂定的ODL)所描述的宣告,需要經過一個「物件宣告前置處理器」(Declaration Preprocessor)來加以剖析,以建立資料庫的綱要(Object Database Scheme)。應用程式則可以使用標準的物件導向式程式語言(C++或Smalltalk)來處理或查詢該資料庫,因此在觀念上會有一個OODBMS Runtime(Object-Oriented Database Management Systems Runtime)要與應用程式的目的碼(Object Code)互相連結(Link)。其架構按D.Bartels(1996)的說明可描繪如圖所示。
圖1-1 ODBMS的使用
ODMG物件模式
ODMG的物件模式中有許多是根據OMG(Object Management Group)的物件模式(Object Model)而得來的。它支援了「型態」(Types)、「類別」(Classes)、「封包」(Encapsulation)、「繼承」(Inheritance),與「同體多型態」(Polymorphism)。另外還有Object Identifier(OID)、Collection Types-聚集型態、Transaction Model交易模式、Relationships、Extents、Keys。(詳細內容將於第二章介紹)
Relationship-Relationship是物件的特性之一,可以定義在一個以上的物件上。
Extent-一個型態的Extent代表了某個Database中所有屬於此型態的實例(Instance)。
Key-可以用來唯一決定某個型態中的實例,Key可以是簡單的(Simple Key)或是複合的(Compound Key),是物件的一個性質(Property)。
Collection Types-聚集型態,可以用來建立複雜的物件。有許多事先定義好Collection Types可以使用,如:Set<Type>﹑Bag<Type>﹑List<Type>﹑Array<Type>。
ODMG物件定義語言(ODL)
ODL(Object Definition Language)是一個規格語言(Specification Language),與任何程式語無關,也不是被用來描述物件內部實作的細節(Implementation Detail)。它是以OMG的IDL(Interface Definition Language)為基礎,再增加了Relationships、Collection、Extents與Keys等特性,並且可以將這些特性對應到現存的ODMG物件模式(ODMG Object Model)上。(詳細內容將於第三章介紹)
ODMG物件查詢語言OQL
ODMG的標準化制定過程中,OQL曾歷經數度的修改,期望能與標準SQL儘可能地相容。目前的OQL已經是標準SQL的Superset了,將來可以預期的是:在關聯式資料庫上所下的任何標準SQL指令,若移到物件導向式資料庫上做查詢,則所表達的查詢意義與所得到的結果應該是一樣的。不過要謹記在心的是:OQL所讀取出來的是物件,而非如關聯式資料庫的表格資料。(詳細內容將於第四章介紹)
ODMG物件模式
概要
本章所要描述的是由ODMG所支援的物件模式(Object Model)。物件模式簡單地摘要如下:
物件是最基本的原始模型。
物件可以被分類為各種型態(types)。所有被定義成某一型態的物件表示有相同的行為(behavior)以及相同的狀態(states)。
物件的行為由一組操作(operations)所定義,可在物件型態上執行。
物件的狀態由一組性質(properties)的值所定義。這些性質可以是物件本身的屬性(attributes),或是物件與物件之間的關連(relationships)。
圖2-1是一個物件型態介面(interface)定義的範例,它說明了這些基本特質。
interface Course
//type properties:
( extent courses
keys name,number)
//instance properties:
{
attribute String name;
attribute String number;
relationship List<Section> has_sections
inverse Section::is_section_of
{order_by Section::number};
relationship Set<Course> has_prerequisites
inverse Course::is_prerequisitie_for;
relationship Set<Course> is_prerequisite_for
inverse Course::has_prerequisites;
//instance operations:
Boolean offer (in Integer semester) raises(already_offered);
Boolean drop (in Integer semester) raises(not_offered);
};
圖2-1 物件型態定義範例
一個型態有一個介面以及一個或更多的實作(implementations)。介面定義了由型態的實例所支援的外部介面(external interface)。實作則定義了資料結構及方法(methods),這裡的資料結構是指型態實例的實際表現方式﹔方法是指操作這些資料結構使能夠支援定義在介面中的可見的狀態和行為。
型態自己本身就是物件,因此有自己的性質。型態的介面定義說明了這些型態性質的值。在這個範例中舉出了三個型態性質-物件型態的超型態(supertypes),物件型態的extent,以及物件型態的鍵(keys)。
超型態:物件型態的上下關係是子型態(subtype)/超型態,例如,副教授是教授的子型態;教授是職員的子型態。所有在超型態所定義的屬性、關連、和操作會被其子型態繼承(inherited)。子型態可以加入額外的性質和操作以區別其他的子型態。此外,子型態也可以將他們所繼承的性質和操作精化以切合他們的需要。
extent:一個型態的所有實例的集合稱之為extent。
鍵:一個型態的實例若可以被一些性質或一組性質所認定是唯一的,那麼這些性質或性質的集合就可稱之為鍵。單一鍵(simple key)是由單一的性質所組成。複合鍵(compound keys)則由一組性質所組成。在這範例中,可以識別出course的鍵是name或number。
我們再看看這個範例,你可以發現物件型態的介面定義也包括了實例性質(instance property)和實例操作(instance operation)的宣告。實例性質是指能提供型態紀錄其值的性質。實例操作是指型態所支援的操作。在這些宣告中所能看出的細節如下:
物件型態所支援的的操作是由一組操作特徵(operation signatures)所定義。每一個特徵定義了操作的名稱、每一個參數(argument)的名稱和型態、傳回值的名稱和型態、以及例外狀況(exceptions)的名稱。
提供物件型態紀錄其值的屬性(attributes)是由一組屬性特徵(attribute signatures)所定義。每一個特徵定義了屬性的名稱以及它合法值的型態。屬性是以實字(literals)為值,如strings,numbers等等。
物件型態所參與的關連(relationships)是由一組關連特徵(relationship signatures)所定義。每一個特徵定義了使用這關連的其他物件的型態以及用來指向相關物件的關連函數(traversal function)的命名。關連是二元的而且定義在物件之間。關連可以是一對一、一對多、以及多對多。
物件模式包含了一組內定的collection型態-sets、bags、lists、arrays。這些型態的具名實例(named instances)可以被用來分類物件,例如,honors_candidates可以將grade_point_average超過90的學生分類出來。
物件模式的主要型態列示在圖2-2。在圖中,縮排是用來說明子型態/超型態。舉例來說,Attribute是Property的子型態。
Denotable_Object
Object
Literal
Characteristic
Operation
Property
Attribute
Relationship
圖2-2 物件模式的基本型態階層
型態及實例
一個型態定義了它的實例的狀態和行為。狀態被定義成一組性質;行為被定義成一組操作。當我們不需要區分出型態的狀態和行為時,我們就參考它的特性(characteristics)。
一個型態所有實例的集合我們稱之為型態的extent。
繼承
型態也許會被組織成子型態或超型態。子型態繼承它超型態的所有特性。它也可以為它的實例定義額外的特性。這樣的目的是為了要讓子型態實例的處理就像其所有的超型態的實例一樣。子型態的實例支援其超型態的所有狀態和行為就如同新的狀態和行為一樣,能分辨它更獨特的本質。
有一些型態是直接立即可用(instantiable)的,有一些則稱為抽象型態(abstract types)。抽象型態只定義自其超型態繼承而來的特性(characteristics)。它們並不定義實作,因此無法直接立即使用。抽象型態的可立即使用的子型態必須定義實作,這實作支援每一個繼承自其抽象超型態的特性。
雖然物件模式的內定型態被組織成樹狀,但是在資料庫綱要中的使用者自訂型態的圖形也許是一有向的非循環圖形。如此便提高了一個型態能夠繼承來自兩個不同超型態但相同名稱的特性的可能性。這種名稱衝突的狀況可以經由重新定義其中之一特性的名稱來獲得控制。例如,如果型態D被定義成型態B和C的子型態,而型態B和C都定義了一個操作expand,那麼型態D就必須要包含如become_larger的描述去重新定義型態B的expand。
請注意,在定義ODMG物件模式內定型態的型態圖(圖2-2)中,使用在Characteristics之下子樹的繼承,就像使用在Denotable_Object之下一樣。在ODMG模式中,應用系統建構者只在Object型態之下定義子型態。特性(Characteristic)或性質(Property)抽象型態以及屬性(Attribute)或關連(Relationship)型態都不可被定義成子型態。這是我們期待在ODMG物件模式的未來版本中能夠放寬的限制。
Extent
所謂型態的extent是指型態所有實例的集合。型態定義者利用在物件型態定義中的extent宣告,去指示ODBMS自動地維護型態的extent,就如同型態實例的建立及刪除。
如果一個物件是型態A的實例,那麼它必然是A的extent中的一個成員。同樣的,如果型態A是型態B的子型態,那麼A的extent則是B的extent的一個子集。
實行和類別
一個型態有一或多個實行(implementations)。在基本模式中,只有物件型態(不是性質或操作型態)可以有使用者自訂的實行。物件型態的實行由說明(representation)和一組方法(methods)所組成。說明是一組資料結構(data structures),方法則是程序主體(procedure bodies)。實行需要被命名,在型態定義的範圍內,實行的命名必須是唯一的。
型態介面描述與型態定義的一個實作的組合稱之為類別(class)。在ODMG模式中,類別這個字的使用與在C++模式中大略上一致。C++的類別有公用部分(public part)和私用部分(private part)。私用部分相當於在ODL中的實作。在允許一個特定介面擁有多重實作方面,ODMG模式較C++更豐富。
物件
物件是由特性(characteristics)所描述出來的。在形式上,物件有狀態和行為。
指示物件型態
物件型態的階層是以指示物件(Denotable_Object)型態為起源。指示物件可以分解成兩種1.可變(mutable)與不可變(immutable)2.基元的(atomic)與結構的(structured)。在型態階層圖中,可變與不可變是以物件(Object)與實字(Literal)來區分。物件是可變的;實字是不可變的。基本的階層如下:
Denotable_Object
Object
Atomic_Object
Structured_Object
Literal
Atomic_Literal
Structured_Literal
物件型態
物件型態的實例是可變的。它們的屬性的值可能會改變、它們所參與的關連也可能會改變。但是經過了這些改變,物件的識別(identity)仍然保持不變。
物件識別
在物件建立的定義域(domain)內,可以以物件的識別(identity)來唯一地區分物件之間的差別。這裡的識別是指object identifier(通常縮寫成OID),OID產生的唯一目的就是用來分辨各個不同的物件。在基本模式中,OID的使用範圍是物件所存在的資料庫。改變物件屬性的值或是其所參與的關連並不會改變物件的識別,它仍然是原來的物件。
物件命名
除了OID外,個別的物件需要有對程式設計師或使用者有意義的名稱。任何物件都有單一的OID,不過卻可能有一個以上的名稱。然而,在命名的範圍內,一個名稱則只能指向單一物件。如果一個物件有多個名稱,在程式執行階段才決定使用哪一個是被允許的。
物件描述
物件也可以由定義在特性(characteristics)中的述詞(predicates)來認明。述詞是提供給collection去挑選滿足述詞條件的成員。
為了加速這類選擇查詢的處裡,型態的定義者可能會宣告某些性質的值能夠在相關型態的extent內唯一分辨出某一物件來。
物件型態中子型態的繼承
一個物件型態可能會被定義成一個或更多其他物件型態的子型態,例如,
interface Associate_Professor:Professor{…};or
interface TA: Employee:Student{…}; //Teaching Assistant
如果物件型態B被宣告為物件型態A的型態,這代表A定義的任何操作、屬性、關連,B也可利用。
物件的性質
下列是定義在物件型態內的內定性質(properties):
˙has_name?:Boolean
˙names:Set<String>
˙type:Type
物件的操作
下列是定義在物件型態內的內定操作(operations):
˙delete()
˙same_as?(oid:Object_id)->b:Boolean
操作特徵(operation signature)是以以下這種形式表現:
operation_name([argument{,argument}])[->result]
其中:
{符號}是指0或多個符號
[符號]是指選擇性符號
其中:
argument::=argument_name:argument_type
result::=result_name:Result_type
最簡單的操作特徵形式就是operation_name()。但是一個操作可能有一或更多的參數(arguments),而且可能以一個物件為傳回值。傳回物件的型態稱為Result_type。
create操作為物件的表示(representation)分配儲存空間,指定一個Object_id,並且傳回此id作為操作的值。程式設計師可能會選擇性地提供一組(property_name,property_value)作為create操作的參數。這些值是用在物件建立時,初始化相對應的物件性質。如果有操作要取出性質內的值,而這性質尚未被指定值時,就會以nil為傳回值。
delete操作是將物件從資料庫中移除並將儲存空間釋放,同時也將它從其所參與的所有關連中移除。刪除一個物件並不會遞迴地將其他與其有關聯的物件一併刪除。已被刪除的物件的Object_id不可再使用。
在same_as操作中的參數若都指向同一物件便傳回布林值true,若否則傳回false。
物件生命週期
在基本的模型中有三種生命週期:
˙coterminus_with_procedure
˙coterminus_with_process
˙coterminus_with_database
在基本模型中,物件的生命週期是在物件建立時被指定的,而以後都不可被改變。較長生命週期的物件若要參照到較短生命週期的物件時,只有在較短生命週期的物件存在的期間才算有效。
實字(不可變物件)
實字(literals)是指實例(instances)是不可變的物件。模式定義了兩個實字的子類別─基元實字(atomic literals)和結構化實字(structured literals)。數字(numbers)和字元(characters)是基元實字的例子。基元實字型態有唯一的識別但是沒有OID。模式支援下列Atomic_Literal型態的子型態:
Integer
Float
Boolean
Character
Structured_Literal型態有兩個子型態─Immutable_Collection及Immutable_Structure。這些型態除了是不可變之外,和Structured_Object的Structurer及Collection相類似。如果一屬性(attribute)以一結構化實字為其值,它可以以新的結構化實字取代其值,但不可以修改原始的結構化實字。內定的Immutable_Collection及Immutable_Structure子類別如下:
Immutable_Collection
Bit_String
Character_String
Enumeration
Immutable_Structure
Date
Time
Timestamp
Interval
型態設計師可以定義額外的實字的子型態,但是卻不能重新定義內定實字型態的操作(operations)。這是實行上的強制限制,因為許多實字定義的操作是直接實行在ODBMS執行的機器上的硬體及韌體中。
模式狀態─性質
物件型態定義了一組性質(properties)以便使用者能透過這些性質去詢問及操控型態實例的狀態。模式中定義了兩種性質─屬性(attribute)及關連(relationship)。屬性是定義在物件型態上並且以實字為其值。關連則定義在兩個可變的(mutable)物件型態之間。
屬性
屬性是定義在單一的物件型態上。舉例來說,物件型態Person可能包含以下屬性定義:
attribute Integer age;
attribute Enumeration sex{male,female};
attribute Integer height;
型態Person的一個實例,John,可能有以下相對應的屬性實例:John.age=32,John.sex=male,John.height=72。屬性以一個或一組實字為其值。
下列是定義在屬性內的內定操作:
set_value(new_value:Literal)
get_value()->existing_value:Literal
set_value操作給予屬性一個新值,以取代現存的值(包括nil)。
get_value操作則傳回屬性的現存值。
模式允許型態定義者指定起始值,並提供nil值。clear_value操作的意義同於set_value(nil)。
關連
關連型態是定義在(可變)物件型態之間。基本的物件模式只支援二元的關連,不支援多元的關連。一對一、一對多、多對多的關連都有支援。
關連本身並沒有名稱。取代的是,利用具名關連路徑(named traversal paths)來定義關連的方向,例如,a course has_sections﹔反過來說,a section is_section_of a course。這些名稱是宣告在參與關連的物件型態的介面定義(interface definitions)中。以上例來說,has_sections會在Course物件型態的介面中宣告﹔is_section_of則在Section物件型態的介面中宣告。事實上,可以利用inverse子句去指明這兩個關連路徑適用的關連型態,例如:
interface Course
{…
relationship List<Section>has_sections
inverse Section::is_section_of
{order_by Section::number};
…
};
以及
interface Section
{…
relationship Course is_section_of inverse Course::has_sections;
…
};
關連實例不具有OID。他們透過參與關連的物件實例來唯一地辨明。
多對多關連所定義的操作如下:
•delete()
•add_one_to_one(o1:Denotable_Object,o2:Denotable_Object)
•remove_one_to_one(o1:Denotable_Object,o2:Denotable_Object)
•add_one_to_many(o1:Denotable_Object,s:Set< Denotable_Object >)
•remove_one_to_many(o1:Denotable_Object,s:Set< Denotable_Object >)
•remove_all_from(o1:Denotable_Object)
一對多關連所定義的操作如下:
•create(o1:Denotable_Object,s:Set< Denotable_Object >)
•delete()
•add_one_to_one(o1:Denotable_Object,o2:Denotable_Object)
•remove_one_to_one(o1:Denotable_Object,o2:Denotable_Object)
•traverse(from:Denotable_Object)-> s:Set< Denotable_Object>
一對一關連所定義的操作如下:
•create(o1:Denotable_Object,o2:Denotable_Object)
•delete()
•traverse(from:Denotable_Object)-> to:Denotable_Object
模式行為─操作
物件型態實例的可能行為被指定為一組操作(operations)。
對於每一個操作,操作特徵(operation signature)被包含在物件型態定義中。特徵包括參數的名稱和型態、例外狀況的產生、傳回值的型態。操作通常定義在單一物件型態上。沒有與物件型態無關的操作,也沒有定義在兩個或更多物件型態上的操作。
在單一型態定義中,操作的名稱必須是唯一的。如此開啟了不同物件卻有相同名稱的操作的可能性,這就叫重載操作(overload operations)。當一個操作要求使用重載的名稱時,指定的操作便會被挑出來執行。這種挑選的動作稱為操作名稱解析(operation name resolution)或操作分派(operation dispatching)。
操作的參數可以是任何指示物件─可變的(mutable)或實字(literal),結構化的(structured)或基元的(atomic)。操作可能會以一個或一組指示物件為其傳回值。
操作型態定義的內定操作如下:
•invoke()
•return()
•return_abnormally(e:Exception)
例外模式
物件模式使用例外處理終結模式(termination model)動態地支援巢狀例外處理器(nested exception handler)。例外狀況本身就是物件,並且可組織成子型態/超型態的階層。例外狀況型態的根基(root)是由ODBMS所提供。這個型態包括可列印出例外狀況訊息及終結程序的操作。例外狀況發生的原因或情況會被當成例外物件的性質(properties)傳回例外處理器。
結構化物件
Structured_Object型態有兩個子型態─Structure及Collection。
Structure有固定數目的具名插槽(named slots),每一插槽包含一個物件或一個實字。填滿插槽的元素型態可能而且通常是不同的。
相對的,collection包含任意數目的元素,沒有具名插槽,而且所包含的元素都是同一型態的實例。元素的插入位置可能是絕對位置(在開始或結束點)或是由游標所建立的點(在目前元素之前或之後)。元素的擷取可能是根據絕對位置、目前游標所在相關的位置或是根據物件性質的值去唯一地擷取出來。
模式支援有序(ordered)及無序(unordered)的collection。所謂有序的定義是根據物件插入的次序或根據物件性質的值。
Structured_Object之下的子結構如下:
Structured_Object
Collection
Set
Bag
List
Array
Structure
結構化物件可以隨意組合。模式支援sets of structures,structures of sets,arrays of structures等等。如此允許型態(如Dictionary)定義為有序集(ordered set),有序集的元素是包含兩個欄位─鍵(key)及OID的structure。由鍵欄位來完成索引。
物件模式定義了可變(mutable)及不可變的(immutable)collection。可變的collection表達內涵性的語意(intensional semantics);不可變的collection表達外在的語意(extensional semantics)。可變的collection定義為Structured_Object的子型態,而不可變的collection定義為Structured_Literal的子型態。在Structured_Object之下的結構化型態包括Colleciton,Set,Bag,List,Array,以及Structure。這些型態的實例都是可變的,也就是說,如果collection或structure的成員改變了,它的識別(identity)仍會被保留下來。舉例來說,如果my_favorite_books集合加了一本新書進去,這集合仍然是同一集合。這與集合的外在處置(extensional treatment)成對比。在外在處置中,只有在兩個集合擁有相同的成員時才可說這兩集合是相同的。意思是,你無法將一成員加入或移出一集合﹔除非建立一個新的集合。關於這點,在數學邏輯的文獻中,有一個典型的例子:unicorns集合以及living_kings_of_France集合。在外在處置中,如果這兩個集合有同樣的成員,那麼這兩個集合就是同一集合。在每個人的現實生活中,這似乎不是大部分人處置集合的方式。因此,ODMS物件模式將內涵性的模式看成‘unmarked’的情況。Set型態是可變的;它的不可變類似物則定義成Immutable_Set。
Collections
Collection是一個聚集其他物件的物件。例如:a set of students,a list of classes。Collection中的所有成員必須是同一型態。
每一collection都有一不可變的識別,就像任何其他的物件一樣。兩個collection可以擁有同樣的元素而不需成為同一collection。同樣地,插入、移除、或修改collection中的元素並不會產生一個新的collection,這collection仍然是同一個collection。Collection有兩個子型態:predicate_defined及insertion_defined。建立一個滿足collection前提的物件並不會自動地將其加入collection中。修改一個物件以致不再滿足collection的前提也不會自動地將其從collection移除。
內定的Collection子型態
物件模式定義了一組內定的collection型態產生器(type generators):
Set<T>
Bag<T>
List<T>
Array<T>
這些都是型態產生器Collection<T>的子型態。
Collection<T>
Collection<T>型態不可直接地立即使用。它只是一個抽象型態。它定義了下列這些性質及操作:
properties:
.cardinality:Integer
.empty?:Boolean
.ordered?:Boolean
.allows_duplicates?:Boolean
operations:
.create([pragma{,pragma}])->c:Collection<T>
where pragma ::= clustering | expected_cardinality | representation
.delete()
.copy(c2:Collection<T>[pragma{,pragma}])
where pragma ::== clustering | representation
.insert_element(o:Denotable_Object)
.remove_element(o:Denotable_Object)
.remove_element_at(current_position:Iterator)
.replace_element_at(o:Denotable_Object ,current_position:Iterator)
.retrieve_element_at(current_position:Iterator)->element:T
.select_element(predicate:String)->element:T
.select(predicate:String)->element:T
.exist?(predicate:String)->b:Boolean
.contains_element?(o:Denotable_Object)->b:Boolean
.create_iterator(stable:Boolean)->i:Iterator
實作時可能也會定義一些與collection型態性質、操作相關的儲存(storage)和執行(performance)。
properties:
.index?:Boolean
opreations:
.create_index(on:property of element[,t:Index_type])
.create_index(on:component of element[,t:Index_type])
.create_index(on:function[,t:Index_type])
.create_index(on:value[,t:Index_type])
.drop_index()
create_index操作允許程式設計師在collection的元素中建立索引。如果這個collection是基元物件(atomic objects)的集合,那麼索引就會被建立在任何由物件型態定義的性質上。如果collection是結構化物件(structured objects)的集合,索引則會被建立在任何結構化物件的元件上。如果collection是基元實字(atomic literals)的集合,索引則被定義在實字的值上。如果collection是結構化實字(structured literals)的集合,索引會被建立在結構化實字的一些組成要素上。第二個參數讓程式設計師定義所建立的索引的型態。代表性的索引型態包括B-trees,L-trees,hash-tables。
Set<T>
Sets是無序(unordered)且不允許重複(duplicates)的collections。
Set<T>重新定義了下列從Collection<T>繼承而來的操作:
.create()->s:Set<T>
.insert_element(o:T)
Set<T>定義了以下的操作:
.union(s2:Set<T>)->s3:Set<T>
.intersection(s2:Set<T>)->s3:Set<T>
.difference(s2:Set<T>)->s3:Set<T>
.copy(s2:Set<T>)->s2:Set<T>
.is_subset?(s2:Set<T>)->b:Boolean
. is_proper_subset?(s2:Set<T>)->b:Boolean
.is_superset?(s2:Set<T>)->b:Boolean
.is_proper_superset?(s2:Set<T>)->b:Boolean
create操作建立一個新的set,這個set的元素被限制在必須是型態T的物件或是型態T的子型態的物件。
insert_element操作根據它的參數將一物件插入一現存的set(existing set)中。插入物件若已存在於現存的set中,那麼這操作便不做任何事,這並不會產生例外狀況。
union操作傳回一新的set,這set的元素是由兩個來源set(original set)的聯集得來。
intersection操作傳回一新的set,這set的元素是由兩個來源set的交集得來。
difference操作傳回一新的set,這set的元素是由來源set與參數內set的插集得來。
copy操作傳回一新的set,這set的元素與來源set的元素相同。
Bag<T>
Bags是無序(unordered)但允許重複的collections。
Bag<T>重新定義了下列從Collection<T>繼承而來的操作:
.create()->b:Bag<T>
.insert_element(o:T)
.remove_element(element:T)
.select(predicate:String)->b:Bag<T>
Bag<T>定義了以下的操作:
.union(b2:Bag<T>)->b3:Bag<T>
.intersection(b2:Bag<T>)->b3:Bag<T>
.difference(b2:Bag<T>)->b3:Bag<T>
create操作傳回一個Bag<T>型態的集合。
Insert_element操作會將參數中的指示物件插入集合中。如果這物件已經是Bag的成員,它會嘗試再插入一次。
remove_element操作會將集合中的物件減去一個。
List<T>
Lists是有序(ordered)且允許重複的collections。List的元素根據它們插入的次序來排序。
List<T>定義以下性質:
.current_position:Integer
List<T>重新定義了下列從Collection型態繼承而來的操作:
.create()->l:List<T>
.insert_element(o:T)
.select(predicate:String)-> l:List<T>
List<T>定義了以下的操作:
.insert_element_after(o:T,position:Integer)
.insert_element_before(o:T,position:Integer)
.insert_first_element(o:T)
.insert_last_element(o:T)
.remove_element_at(position:Integer)
.remove_first_element()
.remove_last_element()
.replace_element_at(o:T,position:Integer)
.retrieve_element_at(position:Integer)->element:T
.retrieve_first_element()->elememt:T
.retrieve_last_element()->elememt:T
List的current_position性質被設定為insert、remove、replace、retrieve操作的副作用(side effect)。List的元素是從數字1開始排下去。insert操作會將current_position設定在最新插入的元素之後。remove操作會將current_posiiton設定在移除元素的前一個元素的號碼上,若這移除元素是位在第一個,那麼current_position會被設定為1。replace和retrieve操作都會將current_position設定在取代或擷取元素的號碼上。
insert_element操作會將元素插到List的游標所在,意思同於”insert_element_at”操作。
Array<T>
Arrays是有不定長度的一維陣列。它的起始大小可在Array建立時給定。Array的長度可以在其建立之後改變。
Arrayt<T>重新定義了下列從Collection<T>繼承而來的操作:
.create(length:Integer)->a:Array<T>
.insert_element_at(o:T,position:Integer)
Array<T>定義了以下的操作:
.insert_element_at(o:T,position:Integer)
.remove_element_at(position:Integer)
.replace_element_at(o:T,position:Integer)
.retrieve_element_at(position:Integer)->element:T
resize(new_size:Integer)
remove_element_at操作是以nil取代Array中細胞(cell)的現值,它並不移除這個細胞,這是與List的remove_element_at操作不同的地方。
內定的Structure型態
模式定義了一個內定的型態產生器Structure。Structure是一個不具名的群組,群組中的元素是以(name,value)成對形式出現,其中value可以是Denotable_Object型態的任何子型態。由於structure和collection都是Denotable_Object的子型態,因此structure可以將其他的structure或collection當成它元素的value。
Structure<e1:T1,…en:Tn>定義的操作如下:
.create([<initializer_list>])->s:Structure
.delete()
.get_element_value(element)->value:Denotable_Object
.set_element_value(element,value:Denotable_Object)
.clear_element_value(element)
.clear_all_values()
.copy()->s:Structure
create操作傳回一Structure,其元素由<initializer_list>選擇性地提供。
copy操作傳回一新的Structure,其元素與原來的Structure相同,而且有同樣的value。如果原來的Structure的元素e1,其value為一實字,那麼新的Structure會複製一新的實字。若原來的Structure的元素e2,其value是一物件的參照(reference),那麼此物件的參照便會置於新Structure的相對應元素中。如此,這兩個Structure的相對應元素就指向了單一物件的實例。
結構化實字
模式定義了基元的實字(atomic literals)及結構化實字(structured literals)。結構化實字有兩個子型態─Immutable_Collection及Immutable_Structure。內定的Immutable_Collection子型態對應了內定的Collection子型態:
Immutable_Set
Immutable_Bag
Immutable_List
Immutable_Array
Enumeration
結構化實字一旦被建立起來就不可以再修改。
不可變的Collections
不可變的collections行為除了它們不可被修改外,就如可變的一樣。在不可變的collection中插入或移除一個成員是不被允許的。
內定的型態─String和Bit_String是List的子型態。
不可變的Structures
不可變的Structure被用來獲取在性質的值中最新的限制。它們往往也被當成查詢的結果傳回。提出查詢的人或程式也許只對滿足查詢的一些物件性質感興趣,而不是所有滿足條件的物件性質。因此根據效率的考量,查詢回傳結果─”不可變的Structure”最好是只包含感興趣的性質在內,而不是全部。如此便不需那提出要求的程式去走覽整個物件,以得到其感興趣的性質了。
內定的型態─Date、Time、Timestamp是Immutable_Structure的子型態。
完整的內定型態階層
圖2-3是完整的內定型態階層。
我們察覺到一個問題,型態和型態產生器被混雜圖中。形式上,型態產生器,如Collection<T>,不可以是一個型態的子型態。一個型態也不可以是一個型態產生器的子型態,例如,String顯示是List<T>的子型態。為了清楚明瞭,我們將其混入同一圖中。
Denotable_Object
Object
Atomic_Object
Type
Exception
Iterator
Structured_Object
Collection<T>
Set<T>
Bag<T>
List<T>
String
Bit_String
Array<T>
Structure<e1:T1…en:Tn>
Literal
Atomic_Literal
Integer
Float
Character
Boolean
Structured_Literal
Immutable_Collection<T>
Immutable_Set<T>
Immutable_Bag<T>
Immutable_List<T>
Immutable_String
Immutable_Bit_String
Immutable_Array<T>
Enumeration
Immutable_Structure<e1:T1…en:Tn>
Date
Time
Timestamp
Interval
Characteristic
Property
Attribute
Relationship
Operation
圖2-3 完整內定型態階層
存取Metadata
展示在圖2-3的所有型態都是Type型態的實例。Type型態本身是Atomic_Object的子型態及實例。使用適用於使用者自訂型態的方法,可以輕鬆地取得定義資料庫綱要的metadata。Metadata被揭露成預先定義的子綱要(predefined subschema)。標準的DML可以用來訊問metadata,這可以出現在編譯、鏈結、載入或執行的時候。
Type型態有下列性質實例:
.has_operations:Set<Operation>
.has_properties:Set<Property>
.has_subtypes:Set<Type>
.has_supertypes:Set<Type>
.name:String
.extent:Set<Atomic_Object>
.name_of_extent:String
Type型態定義了以下操作:
.create_instance([property=property_value{,property=property_value}])
->o:Denotable_Object
.create_extent(name:String)->c:Collection
型態相容性
物件模式被清楚地定義著。每一個指示物件都有一個型態。每一個操作都要有型態的運算元。
物件間的型態相容性
兩個物件只有在它們被宣告成同一具名型態的實例時才可說是具有同樣的型態﹔若被宣告成兩個不同物件的實例,那麼就不是同一型態,即使這兩個型態都定義了同樣的性質和操作。物件的型態相容性遵循著由型態階層所定義的子型態關係。如果TS是T的子型態,那麼TS型態的物件可以被指定給T型態的物件,但是反過來就不可行了。
實字間的型態相容性
若兩個基元實字(atomic literals)同屬於一組實字如Integer、Float…,那麼這兩個基元實字就擁有同一型態。如果結構化實字(structured literals)有相同的結構並且每一基元部分有同樣的型態,這些結構化實字便可說是具有同一型態。如果型態T和TS有同樣的結構,那麼型態T的結構化實字就可以指定給型態TS的變數。
交易
交易(transactions)是由使用持續性資料的程式所組成。交易是基元性(atomicity)、一致性(consistency)、完整性(integrity)的單位。所謂基元性,我們是指交易要就全部發生,不然就都不發生。如果一交易被委任,那麼所有此交易所造成的改變也都會反映在資料庫中並且能讓其他使用資料庫的使用者看到。若交易被取消,則資料庫不會有任何因該交易而造成的改變。
所謂的一致性,我們是指交易負起把資料庫從一內部一致狀態移到另一內部一致狀態的責任。當交易執行時,可能會有一時間點是資料庫不一致的時候,但是沒有使用者會看到這些交易所造成的改變,直到交易完全委任為止。也就是說,共享資料庫的並行使用者看到的總是呈內部一致狀態的資料庫。
所謂完整性是指,一旦交易被委任,DBMS要保證由交易造成的改變不能有任何遺失─儘管程序被取消、作業系統故障、或是硬體設備故障。
物件模式支援巢狀的交易模式,圖2-4說明了一連續的巢狀交易:
Transaction::begin()->t:Transaction
…
Transaction::begin()->x:Transaction
…
Transaction::begin()->y:Transaction
…
if minor_error y.abort()
if major_erroe y.abort_top_level()
…
y.commit()
…
x.commit()
…
t.commit()
圖2-4 巢狀交易
此圖顯示了一個最外層的交易t,其包含兩個巢狀交易x和y。t在x開始之前所造成的改變是x和y可看見的。一旦x被委任,x所造成的改變可以被其父交易t所看見。x和y所造成的任何改變不會被t以外的交易看見,除非t被委任。同樣地,如果t被取消,由x和y所造成的改變也會被取消,不管它們是否已被委任。巢狀交易的委任只與包含它的父交易的委任有關。如果巢狀交易取消,這並不會造成其父交易被取消。
交易型態定義了下列操作:
.begin([optimistic])->t:Transaction
.commit()
.abort()
.checkpoint()
.abort_to_top_level()
在現行的標準中,暫時的物件(transient objects)並不受交易語意的支配。取消一項交易並不會回復已遭修改的暫時物件的狀態。不過,模式的確包含了巢狀交易﹔abort_to_top_level操作會造成巢狀交易被取消至最高層。
資料庫型態的操作
資料庫提供物件型態儲存的空間。每一資料庫都有一綱要,這些綱要由一組型態定義所組成。資料庫包含定義在其綱要中的型態實例。一個單一的(邏輯)資料庫可能儲存在一或更多的實體資料庫中。每一個資料庫都是Database型態的實例。Database型態支援以下操作:
.open()
.close()
.contain_object?(oid:Object)->b:Boolean
.lookup_object(oid:Object)->b:Boolean
它也支援設計給資料庫管理的操作,例如,move、copy、reorganize、verify、backup、restore。
物件定義語言
引言
物件定義語言(Object Definition Language)是一種規格描述語言,用來定義物件型態的介面使其符合ODMG的物件模式。ODL的主要目標是促進資料庫綱要的可攜性以配合各種ODBMS。
ODL的發展有以下幾點原則:
.ODL應該支援ODMG物件模式的語意架構。
.ODL不應該是一個完整的程式語言,而是一個見面特徵的規格描述語言。
.ODL應該與程式語言無關。
.ODL應該要與OMG的介面定義語言(Interface Definition Language)相容。
.ODL應該有擴充性,不只是對未來的功能性,而且也要能擴充實體的最佳化。
.ODL應該要有實用性,能對應用系統發展者提供價值。
ODL並不是企圖要成為一完整的程式語言,它只是介面特徵的規格描述語言。傳統上,DBMS提供支援資料定義(使用資料定義語言─DDL)和資料處理(使用資料處理語言─DML)的介面。DDL供給使用者定義他們的資料型態和介面,DML則供給程式去建立、刪除、讀取及改變那些資料型態的實例。在這個章節所描述的ODL是提供給物件型態的DDL。它定義型態的的特性,包括它們的性質和操作。ODL只定義操作的特徵,而不定義實行這些操作的方法。
ODL企圖定義能夠在各種程式語言上實行的物件型態。因此ODL不會拘泥在一特定程式語言的文法結構上﹔使用者可以在與程式語言無關的情況下利用ODL定義語意綱要。只要是ODL定義出來的綱要,任何順從ODMG的ODBMS以及混合語言的實作皆可支援。這種可攜性對應用系統來說是必須的,只有如此才能夠以最少的修改而能在各種ODBMS上執行。事實上,有些應用系統需要同時由多個不同的ODBMS來支援,有一些則需要使用不同的程式語言來達成物件的建立和儲存。ODL提供應用系統一種獨立的程度,使其能夠對抗程式語言及ODBMS的變動。
ODL的語法結構是延伸自IDL─Interface Definition Language,IDL是由CORBA(Common Object Request Broker Architecture)的OMG(Object Management Group)所發展出來。IDL本身受到C++的影響,因此ODL也帶有C++的味道。
ODL提供整合自多重來源和應用系統的整合綱要一個前後關聯的關係。這些來源綱要可能與各種物件模式和資料定義語言一起定義﹔ODL是一種整合性的國際語言。例如STEP/PDES(EXPRESS)、ANSI X3H2(SQL)、ANSI X3H7(Object Information Management)、CFI(CAD Framework Initiative)及其他曾經發展各種不同的物件模式和資料定義語言的標準組織,這些模式的任何一種皆可轉譯成ODL規格(圖3-1)。這個共通的原則容許將各種模式與標準與意義一起整合。ODL規格可以在物件導向程式語言如C++中具體實現。
圖3-1 ODL與其他語言的對應關係
規格
型態的定義是經由在ODL中描述其介面來完成。針對ODL的最高階BNF如下:
<interface_dcl>
::=
interface <identifier>[<inheritance_spec>]
<type_property_list>
[:<persistence_dcl>]
{[<interface_body>]};
<persistence_dcl>
::=
persistence | transient
型態的特性(characteristics)出現在一開始,跟隨在定義其介面性質(properties)和操作(operations)的list之後。任何不適合介面的list都會被忽略。
型態特性
超型態(supertype)資訊、extent命名、以及鍵(keys)的描述都是型態的特性,但不能直接適用於型態的實例。針對型態性質的BNF如下:
<inheritance_spec>
::=
<scoped_name>|<scoped_name>,<inheritance_spec>
<type_property_list>
::=
([<extent_spec>][<key_spec>])
<extent_spec>
::=
extent<identifier>
<key_spec>
::=
key[s]<key_list>
<key_list>
::=
<key>|<key>,<key_list>
<key>
::=
<property_name>|(<property_list>)
<property_list>
::=
<property_name>
|<property_name>,<property_list>
<property_name>
::=
<scoped_name>
<scoped_name>
::=
<identifier>
| ::<identifier>
| <scoped_name> :: <identifier>
每一個超型態都要在它自己的型態定義中描述。每一個命名為型態的鍵的屬性或關連路徑都必須在key_spec中說明。不適用在已定義型態中的界線和鍵的定義會被忽略。型態定義包含的界線及鍵的定義不應超過一個。
下面是一個簡單的Professor型態的介面定義範例:
interface Professor:Person
( extent professors
keys faculty_id,soc_sec_no):persistent
{
properties
operations
};
實例性質
所謂型態的實例性質是指其實例的屬性和關連。這些性質會在屬性和關連的描述中說明。以下為其BNF:
<interface_body>
::=
<export> | <export><interface_body>
<export>
::=
<type_dcl>;
|<const_dcl>;
|<except_dcl>;
|<attr_dcl>;
|<rel_dcl>;
|<op_dcl>;
屬性
描述屬性的BNF如下:
<attrib_dcl >
::=
[readonly] attribute
<domain type> <identifier>
[[<positive_int_const>]]
<domain_type>
::=
<simple_type_spec>
|<struct_type>
|<enum type>
|<attr_collection_specifier><literal>
|<attr_collection_specifier><identifier>
<attrib_collection_specifier>
::=
Set | List | Bag | Array
舉例來說,在Professor型態的ODL描述中加入屬性定義:
interface Professor: Person
( extent professors
keys faculty_id,soc_sec_no): persistent
{
attribute String name;
attribute Unsigned Short faculty_id[6];
attribute Long soc_sec_no[10];
attribute Address address;
attribute Set<string> degrees;
relationships
operations
};
關連
關連的描述是對其關連路徑做命名及定義。描述關連的BNF如下:
<rel_dcl>
::=
relationship
<target_of_path><identifier>
[inverse<inverse_traversal_path>]
[{order_by<attribute_list>}]
<target_of_path>
::=
<identifier>
|<rel_collection_type><<identifier>>
<inverse_ traversal_path>
::=
<identifier>::<identifier>
<attribute_list>
::=
<scoped_name>
|<scoped_name>,<attribute_list>
關連路徑的基數資訊(cardinality information)包含在關連路徑目標的描述中。目標型態必須與它自己的型態定義一起描述,除非這個關連是遞迴的。使用了BNF的collection_type選項暗示著在目標那邊,基數大於一。一般常用到的collection型態是Set─關連路徑的目標方成員是無序的(unordered),以及List─目標方的成員是有序的(ordered)。Bag和Array也同樣被支援。排序的基準是定義在order_by子句。接下來我們將關連加到Professor型態介面描述中:
interface Professor: Person
( extent professors
keys faculty_id, soc_sec_no): persistent
{
attributeString name;
attribute Unsigned Short faculty_id[6];
attribute Long soc_sec_no[10];
attribute Address address;
attribute Set<string> degrees;
relationship Set<Student> advises inverse Student::advisor;
relationship Set<TA> teaching_assistants inverse TA::works_for;
relationship Department department
inverse Department::faculty;
operations
};
操作
ODL的操作描述與IDL是相容的。操作的高階BNF如下:
<op_dcl>
::=
[oneway]<op_type_spec><identifier>
<parameter_dcls>[<raises_expr>][<context_expr>]
<op_type_spec>
::=
<simple_type_spec> | void
<parameter_dcls>
::=
([<param_dcl_list>])
<param_dcl_list>
::=
<param_dcl>
| <param_dcl>, <param_dcl_list>
<param_dcl>
::=
<param_attribute><simple_type_spec><declarator>
<param_attribute>
::=
in | out | inout
<raises_expr>
::=
raises(<scoped_name_list>)
<context_expr>
::=
context(<string_literal_list>)
<scoped_name_list>
::=
<scoped_name>
| <scoped_name>,<scoped_name_list>
<string_literal_list>
::=
<string_literal>
|<string_literal_list>,<string_literal_list>
ODL的範例
在
這一節,我們要利用ODL去宣告一個有關於大學資料庫的綱要。在圖3-2中,矩形是代表物件型態。關連型態是以線條表示,其圖示如下:
在範例中,Professor型態是Employee型態的一個子型態,且TA型態同時是Employee及Student型態的子型態。圖中的大箭頭是由子型態指向超型態。
圖
3-2 綱要的圖形表示
以下是ODL的定義:
interface Course
( extent courses
keys name, number)
{
attribute String name;
attribute String number;
relationship List<Section> has_sections
inverse Section::is_section_of
{order_by Section::number};
relationship Set<Course> has_prerequisites
inverse Course::is_prerequisite_for;
relationship Set<Course> is_prerequisite_for
inverse Course::has_prerequisites;
Boolean offer(in Unsigned Short semester) raises (already offered);
Boolean drop(in Unsigned Short semester) raises (not_offered);
};
interface Section
( extent sections
key (is_section_of, number))
{
attribute String number;
relationship Professor is_taught_by inverse Professor::teaches;
relationship TA has_TA inverse TA::assists;
relationship Course is_section_of inverse Course::has_sections;
relationship Set<Student> is_taken_by inverse Student::takes;
};
interface Employee
( extent employees
key (name, id))
{
attribute String name;
attribute Short id;
attribute Unsigned Short annual_salary;
void hire();
void fire() raises(no_such_employee);
};
interface Professor:Employee
( extent professors)
{
attribute Enum Rank{full, associate, assistant} rank;
relationship Set<Section> teaches inverseSection::is_taught_by;
Short grant_tenure() raises(ineligible_for_tenure);
};
interface TA: Employee, Student
()
{
relationship Section assists inverse Section::has_TA;
};
interface Student
( extent students
keys name, student_id)
{
attribute String name;
attribute String student_id;
attribute Struct Address {String college, String room_number}
dorm_address;
relationship Set<Section> takes inverse Section::is_taken_by;
Boolean register_for_course (in Unsigned Short course,
in Unsigned Short Section)
raises(unsatisfied_prerequisites, section_full, course_full);
void drop_course(in Unsigned Short Course)
raises (not registered_for that course);
void assign major (in Unsigned Short Department);
Short transfer(in Unsigned Short old_section,
in Unsigned Short new_section)
raises(section_full, not_registered_in);
};
ODL文法
以下是完整包括IDL的ODL的BNF。製作規則上數字與OMG CORBA規格的數字相配。修改過的規則會在數字後方加上星號,如(5*)。新的規則則在後面加上字母,如(5a)。
(1) (2) (3)(4) (5*) (5a) (6) (7*) (7a) (7b) (7c) (7d) (7e) (7f) (7g) (8) (9*) (10) (11) (12) (13) (14) (15) (16)(17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) (29) (30) (31)(32*) (33) (34) (35) (36) (37) (38) (39) (40) (41) (42) (43) (44) (45) (46) (47) (48) (49) (50) (51) (52) (53) (54) (55) (56) (56a) (57)(58) (59) (59a) (60) (61*) (61a*) (62) (63) (63a) (64) (65*) (65a) (65b) (65c) (65d) (65e) (65f) (65g) (66) (67) (68) (69) (70)(70a) (71) (72) (73) (73a) (74) (74a) | <specification>::= <definition> | <definition> <specification> <definition>::= <type_dcl>; | <const_dcl>; | <except_dcl>; | <interface>; | <module>; <module>::= module <identifier> { <specification> }<interface>::= <interface_dcl> | <forward dcl> <interface_dcl>::= <interface_header> [:<persistence_dcl>]{[<interface_body>]} <persistence_dcl>::= persistent | transient <forward_dcl>::= interface <identifier> <interface_ header>::= interface <identifier> [<inheritance_ spec>] [<type_ property_list>] <type_property_list> ::= ([<extent_spec>][<key_spec>]) <extent_spec>::= extent <string> <key_spec>::= key[s]<key_list> <key_list>::=<key>|<key>,<key_list> <key>::= <property_name> | (<property_list>) <property_list>::= <property_name> | <property_name>,<property_list> <property_name>::= <identifier> <interface_body>::= <export> | <export> <interface_body> <export>::= <type_dcl>; | <const_dcl>; | <except_dcl>; | <attr_dcl>; | <rel_dcl>; | <op_dcl>; <inheritance_spec>::= : <scoped_name>[,<inheritance_spec>] <scoped_name>::= <identifier> |::<identifier> | <scoped_name>::<identifier> <const_dcl>::= const<const_type><identifier> = <const_exp> <const_type>::= <integer_type> | <char_type> | <boolean_type> | <floating_pt_type> | <string_type> | <scoped_name> <const_exp>::= <or_expr> <or_expr>::= <xor_expr> | <or_expr>|<xor_expr> <xor_expr>::=<and_expr> | <xor_expr>^<and_expr><and_expr>::=<shift_expr> |<and_expr> & <shift_expr> <shift_expr>::= <add_expr> |<shift_expr>>><add_exp> |<shift_expr><<<add_expr> <add_expr>::=<mult_expr> |<add_expr>+<mult_expr> |<add_expr>-<mult_expr> <mult_expr>::=<unary_expr> |<muit_expr> * <unary_expr> |<muit_expr> / <unary_expr> |<mult_expr> % <unary_expr> <unary_expr>::= <unary_operator> <primary_expr> |<primary_expr> <unary_operator>::= - |+ |- <primary_expr>::= <scoped_name> |<literal> |(<const_exp>) <literal>::= <integer_literal> |<string_literal> |<character_literal> |<floating_pt_literal> |<boolean_literal> <boolean_literal>::= TRUE | FALSE <positive_int_const>::= <const_exp> <type_dcl>::= typedef <type_declarator> |<struct_type> |<union_type> |<enum_type> <type_declarator>::= <type_spec><declarators> <type_spec>::= <simple_type_spec> |<constr_type_spec> <simple_type_spec>::= <base_type_spec> |<template_type_spec> |<scoped_name> <base_type_spec>::= <floating_pt_type> |<integer_type> |<char_type> |<boolean_type> |<octet_type> |<any_type> <template_type_spec>::= <array_type> | <string_type> <constr_type_spec>::= <struct_type> | <union_type> | <enum_type> <declarators>::= <declarator> | <declarator>, <declarators> <declarator>::= <simple_declarator> | <complex_declarator> <simple_declarator>::= <identifier> <complex_declarator>::= <array_declarator> <floating_pt_type>::= Float | Double <integer_type>::= <signed_int> | <unsigned_int> <signed_int>::= <signed_long_int> | <signed_short_int> <signed_long_int>::= Long <signed_short_int>::= Short <unsigned_int>::= <unsigned_long_int> | <unsigned_short_int> <unsigned_long_int>::= Unsigned Long <unsigned_short_int>::= Unsigned Short <char_type>::= Char <boolean_type>::= Boolean <octet_type>::= Octet <any_type>::= Any <struct_type>::= Struct <identifier>(<member_list>} <member_list>::= <member> | <member> <member_list> <member>::= <type_spec><declarators>; <union_type>::= union <identifier>switch (<switch_type_spec>){<switch_body>} <switch_type_spec>::= <integer_type> | <char_type> | <boolean_type> | <enum_type> | <scoped_name> <switch_body>::= <case> | <case> <switch_body> <case>::= <case_label_list> <element_spec>; <case_label_list>::= <case_label> | <case_label><case_label_list> <case_label>::= case <const_exp>: | default: <element_spec>::= <type_spec> <declarator> <enum_type>::= Enum <identifier>{<enumerator_list>} <enumerator_list>::= <enumerator> | <enumerator>,<enumerator_list> <enumerator>::= <identifier> <array_type>::= <array_spec><<simple_type_spec>, <positive_int_const>> | <array_spec><<simple_type_spec>> <array_spec>::= Array | Sequence <string_type>::=String<<positive_int_const>> | String <array_declarator>::= <identifier> <array_size_list> <array_size_list>::= <fixed_array_size> | <fixed_array_size> <array_size_list> <fixed_array_size>::= [<postive_int_const>] <attr_dcl>::= [readonly]attribute <domain_type><attribute_name> [<fixed_array_size>] <domain_type>::= <simple_type_spec> | <struct_type> | <enum_type> | <attr_collection_specifier> < literal> | <attr_collection_specifier> <identifier> <attr_collection_specifier>::= Set | List | Bag | Array <rel_dc)>::= relationship <targe_ of_path> <identifier> inverse<inverse_traversal_path> [{order_by<attribute_list>}] <target_of_path>::= <identifier> | <rel_collection_type><<identifier>> <inverse_ traversal_path>::= <identifier>:: <identifier> <attribute_ list>::= <scoped_ name> | <scoped_name>, <attribute_list> <rel_collection_type>::= Set | List | Bag | Array <except_dcl>::= exception <identifier> {[<member_list>]} <op_dcl>::=[<op_attribute>]<op_type_spec> <identifier> <parameter_dcls> [<raises_expr>][<context_expr>] <op_attribute>::= oneway <op_type_spec>::= <simple_type_spec> | void <parameter_dcls>::=([<param_dcl_list>]) <param_dcl_list>::= <param_dcl> | <param_dcl>,<param_dcl_list> <param_dcl>::= <param_attribute><simple_type_spec> <declarator> <param_attribute>::= in | out | inout <raises_expr>::= raises(<scoped_name_list>) <scoped_name_list>::= <scoped_name> | <scoped_name>, <scoped_name_list> <context_expr>::= context(<string_literal_list>) <string_literal_list>::= <string_literal> | <string_literal>,<string_literal_list> |
第四章物件查詢語言(Object Query Language)
在本章當中,我們將介紹物件查詢語言稱為OQL,它支援ODMG的物件模式,我們將詳細介紹其如何查詢物件,及其語法。
在4.1當中,我們將介紹其設計之基本原則,4.2當中則舉個例子來說明。之後的幾節我們將介紹其語言的語法、語意等。
4.1基本原則(principles)
我們的設計是依據下列的原則與假設:
OQL本身並不是完整的,他必須由任何可以支援方法(method)的語言來包含其查詢(queries)
OQL提供存取(access)物件的能力
OQL建構於ODMG的物件模式上
OQL的語意可以容易地被定義
OQL有一抽象的句法(abstract syntax)
OQL有一個像SQL的句法,而其他的句法則定義如何將查詢語言併入程式語言當中(如:C++、Smalltalk…)
OQL並沒有提供更新(update)的操作,但可以利用定義於物件中的操作(operations)來達到這個目的
4.2查詢的輸入和結果(Query Input and Result)
於本節當中,我們將舉一個實際的查詢的例子來讓您有個初步的認識。
OQL允許你查詢物件根據其物件的名字,這個名字扮演著進入資料庫的點。這個名字可以是任何形式的物件,如:collection、literal….。OQL允許你查詢由正確運算式所表達的物件。以下我們舉兩個簡短的例子。
假設有兩個定義的形態(type)Person 和Employee,這兩個形態有兩個extents,也就是從這兩個形態所衍生出的所有實例集合:Persons、Employees。其中有一個person是Chairman。這個形態Person定義有下列屬性name、birthdate、salary和age。而Employee是Person的子形態(subtype),除了繼承Person的屬性之外,還有下屬(subordinates)這個屬性,所表示的是有哪些員工是下屬。另一個屬性為工作年資(seniority)。執行下列敘述︰
Select distinct x.age
From x in Persons
Where x.name = “Pat”
他會找出一年齡的集合(set)為所有名字為Pat的人之年齡,這個查詢傳回的形態為set<integer>。
Select distinct struct(a: x.age, s: x.sex)
From x in Persons
Where x.name = “Pat”
這個例子和上一個差不多,只是他建構一結構(structure)來包含年齡和性別。這個查詢傳回的形態為set<struct>。
Select distinct struct(name: x.name, hps: (select y
from y in x.subordinates
where y.salary > 100000))
from x in Employess
這個查詢的目的在於對於每個員工x ,得到他的hps(表示高薪資的下屬),hps就是一個對於這個x員工的部下薪資高於$100,000的集合。這個查詢所傳回的形態為set<struct(name: string, hps: bag<Employee>)>
當然,也可以不使用select-from-where 來傳回結果,有以下的例子:
Chairman
傳回Chairman 這個object。
Chairman.subordinates
得到Chairman下屬的集合。
Persons
得到所有人的集合。
4.3 物件識別(object identity)
4.3.1 建立物件(Creating Objects)
假設要建立一個實例(instance)於之前所描述的Person中,可以這樣寫:
Person(name: “Pat”, birthdate: “3/28/56”, salary: 100,000)
這些參數允許你去初始化物件的屬性,沒有初始化的屬性則給定一default value。
我們回到4.2節的例子中,假設我們定義三個如下的物件形態:
type vectint: bag<integer>;
type stat
attributes
a: integer
s: char
end_type;
type stats: bag<stat>;
這時我們可以使用下面的查詢:
vectint(select distinct x.age
from x in Persons
where x.name = “Pat”)
他會傳回一物件形態vectint。
Stats(select stat (a: x.age, s: x.sex)
from x in Persons
where x.name = “Pat”)
會傳回物件形態stats。
4.3.2 取出存在的物件(Selecting Existing Objects)
擷取運算式的傳回可以分為如下的種類:
包含許多物件的聚集(collection),如:select x from x in Persons where x.name = “Pat”,傳回名字是Pat的人的聚集。
單一物件,如:element(select x from x in Persons where x.passport_number = 1234567),傳回護照號碼是1234567的人。
literals的聚集(collection) ,如:select x.passport_number from x in Persons where x.name = “Pat” ,傳回名字是Pat的人的護照號碼的collection為integeer形態。
單一literal,如:Chairman.salary。
4.4 語言描述(Language Description)
在本節當中所使用的範例架構來描述於第三章。
4.4.1 查詢(Queries)
一個查詢由定義的查詢運算式集合所組成,此查詢運算式必須為合法之運算式,並且不可為遞迴(一個合法的運算式有可能包含其他運算式而形成遞迴)。
Example:
Define jones as select distinct x from x in Students where x.name=”Jones”;
Select distinct x.student_id from x in jones
這裡定義一個名字為Jones的學生的集合,並取得他們的student_id。
4.4.2 查詢定義運算式(Query Definition Expressions)
假設q 是一個查詢的名字,而e是查詢運算式,則define q as e是一個查詢定義運算式。
Example:
Define Does as select x from x in Student where x.name =”Doe”
這個敘述定義一個名為Does的查詢,他會傳回bag形態包含名字是Doe的所有學生。
4.4.3 基本運算式(Elementary Expressions)
假如x是一個變數,那麼x 是一個運算式。
假如a是一個atom(如數字….),那麼a是一個運算式。
Example:
27
此查詢傳回27。
Example:
nil
此查詢傳回nil。
假如define q as e 是查詢定義運算式,那麼q是一個運算式。
Example:
Does
此查詢傳回名字是Doe的所有學生。
4.4.4 Construction Expressions
4.4.4.1 Constructing Objects
假如t是一個type name,P1,P2,…….,Pn是t的屬性,且e1,……en是運算式,那麼t(P1: e1 ……,Pn: en) 是一個運算式。ei的type必須合適於Pi的type。
假如t 是一個collection 的type name ,e是literal,那麼t(e)是一個collection object。
Example:
Employee(name: “Peter”, boss:Chairman)
此處建立一新的員工叫Peter,而他的上司是Chairman。
4.4.4.2 Constructing Structures
假設P1,P2,…….,Pn是屬性名,且e1,……en是運算式,那麼
struct(P1: e1 ……,Pn: en)
是一個運算式。他定義了一個structure來裝e1,e2……,en的值。
Example:
Struct(name: “Peter”, age: 25);
此敘述傳回一structure包含兩個屬性-name、age,其值分別為Peter 和25。
4.4.4.3 Constructing Sets
假設e1,……en 運算式,那麼set(e1,……en)是一運算式。它定義了一個集合(set)包含元素e1,……en 。它建立一個set的實例(instance)。
Example:
Set(1, 2, 3)
此敘述傳回一集合包含三個元素1, 2, 3。
4.4.4.4 Constructing Lists
假設e1,……en 運算式,那麼list(e1,……en)是一運算式。它定義了一個串列(list)包含元素e1,……en 。
Example:
list(1, 2, 2, 3)
此敘述傳回一list包含四個元素。
4.4.4.5 Constructing Bags
假設e1,……en 運算式,那麼bag(e1,……en)是一運算式。它定義了一個袋子(bag)包含元素e1,……en 。它建立一bag instance。
Example:
bag(1, 1, 2, 2, 3)
此敘述傳回一bag包含五個元素。
4.4.4.5 Constructing Arrays
假設e1,……en 運算式,那麼array(e1,……en)是一運算式。它定義了一個陣列(array)包含元素e1,……en 。它建立一array的instance。
Example:
array(3, 4, 2, 2, 3)
此敘述傳回一array包含五個元素。
4.4.5 算術運算式(Arithmetic Expressions)
假設e是一個運算式,<op>是一個合法的operation 對e來說,那麼<op> e是一個運算式。
Example:
not (true)
傳回false。
假設e1和e2為運算式,<op>是一operation,那麼e1<op>e2為一運算式。
Example:
Count(Stdents) – Count(TA)
傳回學生數量和TA數量的差。
4.4.6 聚集運算式(Collection Expressions)
4.4.6.1 Universal Quantifications
假設x是一個變數名,e1和e2是運算式,那麼
for all x in e1:e2
是運算式。假如對於collection e1所有的元素均滿足e2,則此敘述會傳回true。
Example:
For all x in Students: x.student_id >0
假如所有student_id為正值,則此敘述會傳回true。
4.4.6.2 Existential Quantification
假設x是一個變數名,e1和e2是運算式,那麼
exists x in e1:e2
是運算式。假如對於collection e1所有的元素中至少有一個元素滿足e2條件式,則此敘述會傳回true。
Example:
Exists x in Doe.takes: x.taught_by.name = “Turing”
假如Doe至少有一門課是被Turing教的話會傳回true。
4.4.6.3 Membership Testing
假設e1和e2為運算式,e2是一個collection,e1的type和e2的元素的type相同,那麼e1 in e2是一個運算式。假如e1屬於e2那麼會傳回true。
Example:
Doe in TA
假如Doe是TA,則傳回true。
4.4.6.4 Select From Where
假設e, e’, e1, e2, ….., en 是運算式,且X1, X2,……,Xn是變數名,那麼
select e from X1 in e1, X2 in e2,…..Xn in en where e’
select distinct e from X1 in e1, X2 in e2,…..Xn in en where e’
是運算式。
Example:
Select couple(student: x.name, professor: z.name)
From x in Students,
y in x.takes,
z in y.taught_by
where z.rank = “full professor”
4.4.6.5 Sort-by 操作
假設e, e1, e2, ….., en是運算式,那麼sort x in e by e1, e2, ….., en是運算式。
Example:
Sort x in Persons by x.age, x.name
將Persons用年齡排序之後,再用姓名來排序,傳回list。
4.4.6.6 Unary Set Operators
假設e是一個運算式,它代表了一個聚集(collection),且<op>代表{min,max,count,sum,avg}其中之一,那麼<op>(e)為運算式。
Example:
Max(select x.salary from x in Professors)
這個敘述會傳回Professors中最大的薪資。
4.4.6.7 Group-by
假設e是一個運算式,它代表了一個聚集(collection),P1, P2,…..,Pn是屬性名,e1, e2, ….., en 是x的運算式,P’1, P’2,…..P’m 是屬性名,e’1, e’2,…e’m是運算式對每個分割區(partition)而言,那麼
group x in e by(P1:e1, P2: e2, …, Pn:en)
group x in e by(P1:e1, P2: e2, …, Pn:en) with (P’1:e’1, ……, P’m: e’m)
均是運算式。
對於第一個式子而言,就是將e依照各個條件式,將之分成數個不同的分割區,每個分割區代表一物件的集合。
Example:
group x in Employees
by( low: x.salary < 1000,
medium: x.salary >= 1000 and x.salary < 10000,
high: x.salary >= 10000)
這個傳回一個集合包含三個元素,每一個元素會有一個叫partition的屬性,其表示屬於這個元素分類的employees集合。所以傳回的形態如下:
set<struct(low: boolean, medium: boolean, high: boolean, partition: set<Employee> ) >
第二個運算式而言,主要是加強第一個運算式,能夠對分類後的分割區的結果做運算。
Example:
group e in Employees
by (department: e.deptno)
with (avg_salary: avg(sellect x.salary from x in partition))
這個敘述傳回一個集合:包含部門的編號和這個部門員工薪資的平均。其集合的形態如下:
set<struct(department: integer, avg_salary:float)>
4.4.7 聚集索引運算式(Indexed Collection Expressions)
4.4.7.1 取得第i個元素
假如e1和e2 是運算式,e1是list或array,e2是integer,那麼e1[e2]是運算式。它會取得e1中第e2個元素,注意e1中的元素由0開始算起。
Example:
list(a, b, c, d)[1]
傳回b。
4.4.7.2 取得子集合
假如e1, e2和e3 是運算式,e1是list或array,e2和e3是integer,那麼e1[e2:e3]是運算式。它會取得e1的子集合,從e2位置開始到e3。
Example:
list(a, b, c, d)[1:3]
傳回list(b, c,d)。
第五章C++鏈結
5.1 引言
在本章當中我們將定義ODL/OML於C++中如何使用。
在ODL的C++ binding用一類別庫(class library)和標準的C++ class定義文法來表達。這個類別庫提供許多類別(classes)和函式(functions)來實作定義於ODMG 物件模式的概念。利用額外增加的一些關鍵字(keyword)和語法來結合C++ class。OML代表物件處理語言(Object Manipulation Language),它是用來取得和修正資料庫中的物件們(objects)。
ODL/OML 說明在邏輯的(logical)物件及其用來處理物件的操作(operations),並不涉及物件實體(physical)上的儲存。它不能使用叢集或記憶體管理來控制存取物件。但在真實世界實用上卻不然,一個額外的建構稱為physical pragmas,它提供程式設計師可以直接控制實體上的儲存。或至少可以讓程式設計師提供一些提示(hints)給ODBMS執行時(runtime)的儲存管理子系統,讓程式設計師可控制到實體儲存。
5.1.1 語言設計基本原則(Language Design Principles)
ODL/OML的設計有一個基本的原則:讓程式設計師覺得這裡只有一個程式語言,而不是兩種。意思是說程式設計師在使用基本程式語言(如:C++、Smalltalk)撰寫應用程式而要用到ODL/OML時,不會覺得是在使用兩種不同的語言。對於ODL/OML崁入基本程式語言時,必須遵守其基本程式語言的語法和語意,而不是架構一子程式語言(sublanguage) 依附於原來的語言當中。
5.1.2 語言鏈結(Language Binding)
C++和ODBMS語言鏈結的方法,是用一種聰明指標(smart pointer)的方式或稱為Ref-based的方式。
在這個方式當中,C++和物件模式的鏈結是利用許多的類別來加以實現,這些類別可衍生出暫時性(transient)或持續性(persistent)的實例。此種類別稱為“具有持續性能力的類別“。這種類別跟一般C++中的類別是有所不同的,C++中的類別所衍生出的實例(instance)都是暫時性的實例,所謂暫時性的實例就是說它只能存活於建立它的處理程序當中,一當此程序執行完畢,此實例將不存在。而經由具有持續能力的類別所衍生出來的實例,可以是暫時性或持續性,所謂持續性即是就算處理程序結束後,它依然存在(可存入資料庫中)。對於每個具有持續能力的類別T,可以用Ref 這個class 來產生實例,看如下之例子:
Ref<Professor> profP;
Ref<Department> deptRef;
ProfP ->grant_tenure( );
DeptRef = proP ->dept;
在(1)中,宣告一個物件profP,其為Ref<Professor>這個形態的實例。在(2)中宣告deptRef物件是形態Ref<Department>的實例。在(3)中,profP執行定義於Professor類別中的操作。於(4)中,將profP中屬性dept的值,指派給deptRef。
於下圖中,我們將各語言的關係畫成一階層圖,經過編譯器、連結器等處理,最後形成可執行的應用程式:
5.2 C++ 物件定義語言(ODL)
C++ ODL 描述一資料庫的架構(schema)__物件包含其屬性、物件和物件之間的關連、操作等。接下來我們舉一個例子,宣告一稱為Professor的形態。新的關鍵字(沒有定義於C++中的字)將以斜體表示。在此先看個例子,稍後會詳細說明:
class Professor: public Persistent_Object {
public:
//properties:
int age;
int id_number;
String office_number;
String name;
Set<Ref<Student>> advisees inverse Student::advisor;
//operations:
void grant_tenure( );
void assign_course(Course &);
private:
……………
};
這個宣告包括屬性的宣告和關連路徑(relationship traveersal paths),而關連路徑使用inverse這個關鍵字。
5.2.1 屬性宣告
屬性的宣告和C++中的資料的宣告是相同的,屬性的形態可包括C++本身提供之形態、使用者定義之形態、基本形態(literal types)其包括:
String
Interval
Date
Time
Timestamp
Example:
class Student : public Persistent_Object {
public:
String name;
Date birth_date;
Phone_Number dorm_phone;
struct{
int PO_box;
String university;
String city;
}university_address;
List<string> favorite_friends;
};
5.2.1.1 String
這個類別定義了包括字串的初始、複製、比較等操作。
Class String{
public:
string();
string( const string&) ;
String(const char*);
~String();
String& operator=(const String&);
String& operator=(const char*);
operator=(const char*)const;
char& operator[](unsigned long index) ;
unsigned long length()const;
friend int operator==( const String &sL,const String &sR);
friend int operator==( const String&sL,const char *PR) ;
friend int operator==( const char* PL,const String &sR) ;
friend int operator!=( const String &sL,const String &sR) ;
friend int operator!= (const String &sL,const char *pR) ;
friend int operator!=(const char *pL,const String &sR) ;
friend int operator< (const String &sL,const String &sR) ;
friend int operator< (const String &sL,const char *pR) ;
friend int operator< (const char *PL,const String &sR) ;
friend int operator<=(const String &sL,const String &sR) ;
friend int operator<=(const String &sL,const char *pR );
friend int operator<=(const char *PL,const String &sR );
friend int operator> (const String &sL,const String &sR );
friend int operator> (const String &sL,const char *PR);
friend int operator>=(const String &sL,const String &sR) ;
friend int operator>=(const string &sL,const char *PR) ;
friend int operator>=(const char *pL,const String &sR) ;
5.2.1.2 lnterval
這個類別用來表示時間的間隔,也用來用在Date、Time、Timestamp的數學運算上,例如:兩個Date形態的時間相減會變成一個lnterval的形態的時間間隔。
class lnterval{
Public:
lnterval(int day=O,int hour=O,int min=O,float sec=O.O);
lnterval(const lnterval &) ;
lnterval & operator=(const lnterval&) ;
int day()const;
int hour()const;
int minute()const;
float second()const;
int is_zero()const;
lnterval & operator+=(const lnterval & );
lnterval & operator-=(const lnterval &) ;
lnterval & operator*=(int);
lnterval & operator/=(int);
lnterval operator- ()const;
friend lnterval operator+(const lnterval &L,const lnterval &R );
friend lnterval operator-(const lnterval &L,const lnterval &R );
friend lnterval operator*(int L, const lnterval &R);
friend lnterval operator/(const lnterval &L,int R);
friend int operator==(const lnterval &L,const lnterval &R);
friend int operator!=(const lnterval &L,const lnterval &R );
friend int operator< (const lnterval &L,consst lnterval &R );
};
5.2.1.3 Date
這個類別所儲存的日期由年、月、日所組成,提供關於日期的計算。
class Date{
public:
enum weekday {
Sunday = O, Monday = l,Tuesday = 2,Wednesday = 3,
Thursday=4, Friday = 5,Saturday = 6
};
enum Month {
January= l, February = 2, March= 3,April= 4,May= 5,June=6,
July=7,August= 8,September= 9,October= 10, November=11,
DeCember= 12
};
Date();
Date(unsigned short year, unsigned short julian_day);
Date(unsigned short year,unsigned short month= l,
unsigned short day= l);
…………………….
};
5.2.1.4 Time
這個類別提供關於時間的操作,而此處的時間是用格林威治時間為標準。
class Time{
public:
enum Time_Zone{
GMT =O, GMT12= 12, GMT_12=-12,
GMTl= 1, GMT_1=-1, GMT2=2, GMT_2=-2,
GMT3=3, GMT_3= -3, GMT4=4, GMT_4=-4,
GMT5=5, GMT_5=-5, GMT6=6, GMT_6=-6,
GMT7 =7, GMT_7=-7, GMT8=8, GMT_8=-8,
GMT9= 9, GMT_9=-9, GMT10=10, GMT_10= -10,
GMT11 = 11, GMT_11=-11
};
.................
};
5.2.1.5 Timestamp
這個類別包含日期和時間.
5.2.2 關連路徑宣告(Relationship Traversal Path Declarations)
關連路徑(traversal path)用來定義兩個物件之間的關連,這個路徑的兩個端點就是這兩個物件,因此在這兩個物件中均要表現這個路徑。例如,老師和學生有1對多的指導關連,一個老師可以指導很多學生。那麼在老師和學生的物件形態定義時都必須定義這個關連路徑。
關連路徑的宣告就像是屬性的宣告,不同處在於一個關連的兩端都必須宣告路徑,另外在宣告之後,必須再接inverse關鍵字,之後放另一端路徑名,就像:type 路徑名inverse另一端路徑名
Example:
假設有下列關連存在
將
之關連宣告成:
class Department: public Persistent_object{
public:
set<Ref<Professor>> professors inverse Professor::dept;
};
class Professor: public Persistent_object{
public:
Ref<department> dept inverse Department::professors;
Set<Ref<student>> advisees inverse Student::advisor;
};
class Student: public Persistent_object{
public:
Ref<Professor> advisor inverse Professor::advisees;
Set<Ref<Course>> classes inverse Course::students_enrolled;
};
class Course: public Persistent_object{
public:
set<Ref<department>> students_enrolled inverse student::classes;
};
5.3 C++物件處理語言(OML)
5.3.1 物件的建立、刪除
5.3.1.1物件建立
C++ OML建立物件是利用new這個操作,它包括建立暫時性和持續性的物件。所謂暫時性的物件是說當建立此物件的程序結束時,此物件也就結束。而持續性的物件即是建立後會存入資料庫中的物件。
正式的ODMG對於C++的new操作有以下格式:
void * operator new(size_t size, const char* typename=0);
void * operator new(size_t size, const Ref<Persistent_Object> &clustering, const char* typename=0);
void * operator new(size_t size, Database *database, const char* typename=0);
(1)是用來建立一暫時性的物件,(2)和(3)都是建立一持續性的物件。在(2)中,這個新建立的物件可以將之存放於資料庫中某一已存在物件的附近,(3)則是放於資料庫中。
這三種格式的第一個參數是size,它是當程式進行編譯時,由編譯器來決定其大小,並不是由程式設計師自行設定,而typename則是用在某些實作上面。
Example:
Database *yourDB, *myDB;//宣告資料庫
Ref<Schedule> temp_sched1 = new Schedule;
Ref<Professor> prof2 = new(yourDB,”Professor”) Professor;
Ref<Student> student1 = new(myDB)Student;
Ref<Student> student2 = new(student1) Student;
敘述(1)建立一暫時性的物件temp_sched1,敘述(2) -(4)建立持續性的物件。敘述(2)建立一物件Professor的實例並存於資料庫yourDB中,敘述(3)建立Student的實例並存於myDB中,敘述(4)做和(3)同樣的事,但將student2置放於靠近student1的地方。
5.3.1.2 物件刪除
在C++OML中刪除物件可以用Ref::delete_object這個函式,刪除一個物件會將其從記憶體中移除,如果是持續性物件,也會從資料庫中刪除。
Example:
Ref<any Type>obj_ref;
……….
Obj_ref.delete_object();//此時會將obj_ref刪除
5.3.2 性質(Properties)
5.3.2.1 屬性(Attributes)
C++ OML對於屬性的存取是用標準的C++語法,例如現在我們想要修正一個professor他的編號並將之印出,我們可以寫成:
Prof -> id_number = next_id;
Cout << Prof -> id_number;
5.3.2.2 關連(Relationships)
在ODL當中,我們可以定義物件和物件間的關連。而在OML當中,我們要加以處理其物件所衍生出的實例之間關連的建立、修正、刪除等動作。至於關連間的完整性則交由ODBMS來維護管理。
接下來我們將以圖示來加以說明關連的新增、修正、及刪除。每個圖示有其標示,如:1-1、1-m、m-m表示其關連,N(表示目前沒關連)、A(表示新增一關連)、M(修正其關連)。
假設class A和class B之間存在1-1的關連:
class A{
Ref<B> rb inverse B::ra;
};
class B{
Ref<A> ra inverse A::rb;
};
假設A有個實例a,B有個實例b
之後,a和b之間新增一關連使用下列敘述:
a.rb = &b;
結果會成為如下圖:
假如我們接者執行下面敘述:
a.rb.clear();
其執行結果就像1-1N所示,兩者沒有關連。
假設現在仍像1-1A中的關連,接著執行
a.rb = &bb;
那麼我們會得到下面之結果:
a和bb建立關連,a和b變成沒有關連.
接下來,我們來看看1對多的關連,假設class A和class B為1對多的關連:
class A{
Set<Ref<B>> sb inverse B::ra;
};
class B {
Ref<A> ra inverse A::sb;
};
假設現在有class A和B的兩個實例:a、b,尚沒有關連:
a.sb有三個元素,它們是除了b之外的class B的實例。
現在我們要新增一個關連在a和b之間,可以使用下列敘述:
a.sb.insert_element(&b);
之後結果會如下圖:
b.ra的關連路徑會自動指向a的參考位址。當然我們也可以執行下列指令:
b.ra = &a;
也會得到相同之結果,而a.sb也會自動參考到b的位址。以上兩個操作只需執行其中之一即可,ODBMS會自動維護此關連路徑。
接著我們執行下列敘述:
a.sb.remove_element(&b) or b.ra.clear( );
其結果會刪除a和b之間的關連,而成為如同1-mN。
假設現在a和b的關係回到1-mA所示,接著執行下列敘述:
b.ra = &aa;
那麼結果會變成下圖:
b.ra 會參考到aa,而原本和a的關連會被移除,且a.sb的參考到b的元素被移除。而aa.sb將會參考到b。
接下來要介紹第三種的關連-多對多,假設A和B有多對多的關連:
class A{
Set<Ref<B> > sb inverse B::sa;
};
class B {
Set<Ref<A>> sa inverse A::sb;
};
假設我們現在有兩個實例a和b彼此沒有關連,但a和b和其他實例存在關連,參考下圖m-mN:
接著,我們要建立a和b的關連,執行下列敘述:
a.sb.insert_element(& b);
結果會如下圖m-mA:
此時,a.sb會新增一個元素參考到b,而b.sa也會自動地新增一參考到a的元素。
接者,若執行下列敘述:
a.sb.remove_element(&b) or b.sa.remove_element(&a)
此時,會將a和b之間的關連移除,其結果就像m-mN所示。
接著執行下列敘述:
a.sb.replace_element_at( &bb, 3);
其結果會成為:
a.sb對b的參考將會更改為對bb的參考,而bb.sa也將會新增一參考a的元素。
5.3.3 持續性物件類別(Persistent_Object Class)
定義如下:
class Persistent_Object{
public:
Persistent_Object( );
Persistent_Object(const Persistent_Object &);
~Persistent_Object( );
void mark_modified( );
void * operator new(size_t size, const char *typename = 0);
void * operator new(size_t size, Ref<Persistent_object> cluster, const char *typename = 0);
void * operator new(size_t size, Database *database, const char *typename = 0);
};
當您想要讓自己所撰寫的類別具有可持續性的能力時,您可以繼承Persistent_Object這個類別:
class My_Class : public Persistent_Object{ ……};
5.3.4 Ref Class
這個類別的作用在於提供類似C++ 中的pointer和reference的功能。當您宣告一個物件其為形態Ref<T>的實例時,此物件就可以和其他同樣為形態Ref<T>的實例做和C++所提供的指標、參考同樣的運算(如->、=…..)
example:
Ref<T> X;
Ref<T> Y;
X = Y;
也就是說,當使用者自行定義一個類別時,它只要使用這個Ref 來宣告其實例,即可使用其所提供的運算,而不須自行在定義的類別中撰寫其運算(物件指標參考的運算)。
5.3.5 交易(Transactions)
交易可以開始(start)、委任(committ)、放棄(abort)、檢查點(checkpoint)。在C++中,我們建立一Transaction 類別來處理這些動作:
class Transaction{
public:
Transaction();
~Transaction();
void begin();
void commit();
void abort();
void checkpoint();
};
當您呼叫begin後,就會開始一個交易,直到呼叫commit所作的存取資料庫的動作、物件的處理均屬於此交易。
當您呼叫commit後,關於此次交易資料庫中的物件均會被修正(包括刪除和建立),並釋放對資源的鎖定(lock)。
當呼叫checkpoint後,本交易在最近一次檢查點之後的修正動作寫入資料庫。
呼叫abort後,會放棄任何更改修正的動作。
5.3.6 資料庫操作(Database Operations)
建立一Database的類別:
class Database{
public:
enum access_status{ not_open, read_write, read_only, exclusive};
void open( const char* database_name, access_status status = read_write);
void close();
void set_object_name(const Ref_Any &theObject, const char* theName);
const char* get_object_name(const Ref_Any &)const;
void rename_object(const char* oldName, const char* newName);
Ref_Any lookup_object(const char* name)const;
};
在任何必須使用到這個資料庫A物件的交易開始之前,必須先將A開啟(open),於交易完成之後將之關閉(close)。當要開啟資料庫,我們可以用Database::open這個方法,其參數為資料庫的名字,如:
database ->open(“myDB”);
會開啟”myDB”這個資料庫,並建立一連結。
當要關閉一資料庫時,可以用Database::close:
database ->close();