Object Type(객체 타입) - 1

    오라클의 객체 지향 프로그래밍 기술의 핵심은 객체 타입(Object Type)이다. 객체 타입을 이해하기 위해서는 객체 지향 프로그래밍에 대한 조금의 사전 지식이 필요하다. 하지만 그에 대한 설명은 이 post에서 하기엔 이 포스트의 목적에 어긋나므로 googling으로 조금 알아보기를 권한다.

    객체 타입 개념

    오라클에서의 객체는 간단히 말해 속성과 메소드가 결합된 데이터베이스 객체이다. 객체 지향 언어의 클래스를 데이터베이스에 적용시킨 것이 객체 타입이라고 보면 거의 틀리지 않다. 객체를 이해하기 위해서는 객체타입과 인스턴스에 대한 구분이 필요하다.

    객체 타입 인스턴스
    객체의 속성과 행위에 대한 정의. 이는 객체 지향 언어의 클래스와 유사한 개념이다. 객체 타입은 생성될 인스턴스에 대한 탬플릿이며, 객체 타입 변수를 위한 데이터 타입 또는 칼럼의 데이터 타입으로 사용된다. 객체 타입을 탬플릿으로 하여 실제로 생성되는 PL/SQL변수나 테이블/칼럼의 특정 값, 특정 객체 타입을 가지는 인스턴스는 여러 개가 존재할 수 있다.

    오라클의 기본 데이터 타입은 CHAR/VARCHAR2, NUMBER, DATE와 마찬가지로 객체 타입을 사용하여 테이블, 칼럼, PL/SQL 변수나 상수 등을 선언하거나 정의할 수 있다. 패키지는 상속될 수 없으나 객체 타입은 상속에 의해 기능을 확장할 수 있다. 상속을 사용하면 객체의 일반화와 구체화를 구현할 수 있다. 패키지는 생성자를 지원하지 않지만, 객체 타입은 자동적으로 묵시적인 생성자를 제공하고, 사용자가 추가적인 생성자를 정의할 수 있도록 지원한다. 패키지에 기반한 테이블은 정의할 수 없지만, 객체 타입은 이에 기반한 테이블의 정의가 가능하다. 또한 패키지는 테이블에 저장될 수 없지만, 객체는 해당 타입으로 정의된 테이블의 로우나 칼럼에 영속성을 가지는 인스턴스로 저장될 수 있다. 객체타입의 테이블을 정의하면 테이블의 각 로우에 대해 객체 타입에 정의된 메소드를 사용할 수 있다. 패키지는 자체가 데이터 타입이 될 수 없지만, 객체 타입은 데이터 타입이 될 수 있다(이를 ADT라고 함) 

    객체 타입의 속성

    객체를 구성하는 속성과 메소드 중에서 속성은 테이블의 칼럼 또는 레코드의 필드와 비슷한데 데이터를 저장하기 위해 사용된다. 객체 타입은 여러 개의 속성을 가질 수 있고, 속성은 테이블에서 사용할 수 있는 모든 데이터 타입을 사용할 수 있으며 필요시에는 길이와 정밀도를 명시할 수 있다. 객체 타입에서 속성의 순서는 중요한데, 속성이 선언된 순서대로 시스템 정의 생성자의 매개변수 순서가 결정되며, 또한 객체 타입을 사용하여 객체 테이블을 생성하면 칼럼의 순서가 객체 타입의 속성 순서와 동일해지기 때문이다. 기본적으로 객체 타입의 속성과 메소드는 모두 Public인데 이것은 속성과 메소드의 접근에 제약이 없다는 의미이다.

    CREATE OR REPLACE TYPE person_type AS OBJECT(
    	family_name	VARCHAR2(30),	-- 성
        given_name	VARCHAR2(30),	-- 이름
        address_1	VARCHAR2(30),	-- 기본 주소
        address_2	VARCHAR2(30)	-- 상세 주소
    );

    객체 타입의 메소드

    객체 타입의 메소드(Method)는 객체의 행위를 구현하기 위해 객체 타입 내에 선언된 함수나 프로시저와 같은 서브프로그램을 말한다. 메소드는 보통 PL/SQL로 작성하지만, 자바나 C와 같은 언어로 작성할 수도 있다.

    객체 메소드는 몇 가지 유형으로 나눌 수 있다,

    • 멤버(Member) 메소드: 응용 프로그램이 객체 타입의 특정 인스턴스 데이터에 접근할 수 있도록 하는 메소드.

    멤버 메소드는 다시 다음의 세 가지 유형으로 나뉜다.

    - 일반 멤버 메소드: 변수나 객체 타입의 비교 목적으로 사용되지 않는 멤버 메소드

    - MAP 멤버 메소드: 비교와 정렬을 위해 사용될 수 있는 값을 반환하는 멤버 메소드

    - ORDER 멤버 메소드: 직접 일대일 비교를 수행하여 비교 결과를 반환하는 멤버 메소드

    MAP과 ORDER는 둘 다 비교를 위해 사용되는데, 객체 타입은 한 개의 MAP 메소드 또는 한 개의 ORDER 메소드를 가질 수 있으나 둘 다를 동시에 가질 수는 없다.

    • 정적(Static) 메소드: 개별 인스턴스가 아닌 객체 타입 자체에 대해 수행하기 위한 메소드
    • 생성자(Constructor) 메소드: 새로운 객체 타입 인스턴스를 생성하고 속성의 값을 초기화하기 위해 사용되는 멤버 메소드

    멤버 메소드

    멤버 메소드 세 가지에서 MAP 멤버 메소드와 ORDER 멤버 메소드는 일반 멤버 메소드의 특별한 경우이다.

    멤버 메소드는 포괄적인 의미에서의 멤버 메소드이며, 이는 곧 일반 멤버 메소드를 의미한다.

    또한 일반 멤버 메소드에 대한 설명은 기본적으로 세 가지 유형의 멤버 메소드에 동일하게 적용된다.

    일반 멤버 메소드

    일반 멤버 메소드는 함수와 프로시저 두 가지로 사용 가능한데, 키워드 MEMBER FUNCTION과 MEMBER PROCEDURE를 사용하여 선언한다.

    CREATE OR REPLACE TYPE person_type AS OBJECT(
    	family_name	VARCHAR2(30),	-- 성
        given_name	VARCHAR2(30),	-- 이름
        address_1	VARCHAR2(30),	-- 기본 주소
        address_2	VARCHAR2(30)	-- 상세 주소
    	
        MEMBER		FUNCTION	full_name		RETURN	VARCHAR2,	-- 풀 네임 반환 메소드
        MEMBER		FUNCTION	mailing_address RETURN	VARCHAR2,	-- 전체 주소 반환 메소드
        MEMBER		PROCEDURE	set_name(family_name VARCHAR2, given_name VARCHAR2)	-- 이름 설정
    );

    멤버 메소드가 응용 프로그램이 특정 인스턴스에 접근할 수 있도록 하는 방법을 제공한다고 했는데, 이를 위해서는 멤버 메소드와 관련되는 인스턴스를 식별할 수 있는 수단이 필요하다. 이 역할을 하는 것이 바로 멤버 메소드에 사용되는 SELF다. SELF는 실질적으로는 멤버 메소드의 묵시적 매개변수다. 모든 멤버 메소드와 생성자 메소드는 첫 번째 매개변수로 SELF를 가지며, 매개변수의 데이터 타입은 메소드가 속한 객체 타입이다. SELF는 명시적 매개변수로 선언될 수도 있지만 보통은 생략되는데, 생략된 경우에는 시스템이 묵시적인 첫 번째 매개변수로 SELF를 선언해 준다.

    위 예제의 멤버 함수 선언은 실질적으로 다음 선언과 완전히 동일하다.

    MEMBER	FUNCTION	full_name(SELF IN OUT person_type)	RETURN VARCHAR2,
    MEMBER  FUNCTION	mailing_address(SELE IN OUT person_type)  RETURN VARCHAR2,
    MEMBER  PROCEDURE	set_name(SELF IN OUT person_type, family_name VARCHAR2, given_name VARCHAR2)
    CREATE OR REPLACE TYPE BODY person_type
    IS
    	-- 풀 네임(성 + 이름) 반환
        MEMBER FUNCTION full_name RETURN VARCHAR2
        IS
        BEGIN
        	RETURN family_name || ' ' || given_name;
        END;
        
        -- 전체 주소(기본 주소 + 상세 주소)를 반환
        MEMBER FUNCTION mailing_address RETURN VARCHAR2
        IS
        BEGIN
        	RETURN address_1 || ' ' || address_2;
        END;
        
        -- 이름 설정
        MEMBER PROCEDURE set_name(family_name VARCHAR2, given_name VARCHAR2)
        IS
        BEGIN
        	-- 객체 타입의 속성과 매개변수의 이름이 동일하므로
            -- 둘 간의 구별을 위해 "SELF."을 객체 타입 속성의 접두어로 사용해야 함
            SELF.family_name := family_name;
            SELF.given_name := given_name;
        END;
    END;

    위에서 주의해서 볼 것은 set_name 메소드다. set_name 메소드는 두 개의 매개변수를 가지는데, 매개변수의 이름 family_name, given_name이 객체 타입의 속성 이름과 동일하다. 매개변수가 높은 우선순위를 가지므로 이름만 사용하면 매개변수를 가리키게 되어 객체 타입 인스턴스의 속성에는 접근할 방법이 없다. 이때 인스턴스의 속성에 접근하기 위해서 묵시적 매개변수 SELF를 사용하여 'SELF.'을 접두어로 사용하면 된다. 메소드 full_name에서는 이름 충돌이 없으므로 속성에 접두어 'SELF.'을 사용하지 않아도 정상적으로 속성명으로 인식된다.

    다음은 일반 멤버 메소드를 사용하는 예제다.

    DECLARE
    	v_emp person_type := person_type('홍', '길동', '한양 종로', '32번지');
    BEGIN
    	DBMS_OUTPUT.PUT_LINE(v_emp.full_name()||'의 우편 주소: '||
        					 v_emp.mailing_address());
    END;
    /
    
    홍 길동의 우편 주소: 한양 종로 32번지

    MAP 멤버 메소드

    MAP 멤버 메소드는 두 개의 객체 타입 간의 비교를 위한 값을 반환하는 함수이다. MAP 멤버 메소드는 키워드 MAP MEMBER FUNCTION을 사용하여 선언된다, 반환되는 타입은 오라클 PL/SQL이 반환값 간에 비교를 수행할 수 있는 내장 스칼라 타입이어야 한다. MAP 메소드는 v_obj1 > v_obj2와 같이 두 값을 비교할 때 또는 DISTINCT, GROUP BY, UNION, ORDER BY와 같이 정렬이 필요한 오퍼레이션에 사용된다. 값의 비교에는 오라클에서 지원하는 비교 연산자를 모두 사용할 수 있다. MAP 멤버 메소드가 선언되면 두 값의 비교가 필요할 때에는 언제라도 오라클이 자동으로 인식하여 사용한다.

    -- MAP 멤버 메소드 명세
    CREATE OR REPLACE TYPE person_type AS OBJECT(
    	family_name	VARCHAR2(30),	-- 성
        given_name	VARCHAR2(30),	-- 이름
        address_1	VARCHAR2(30),	-- 기본 주소
        address_2	VARCHAR2(30)	-- 상세 주소
    
    	MEMBER		FUNCTION	full_name		RETURN	VARCHAR2,	-- 풀 네임 반환 메소드
        MAP MEMBER  FUNCTION	person_type_map RETURN  VARCHAR2	-- 비교를 위한 메소드
    );
    --MAP 멤버 메소드 본체
    CREATE OR REPLACE TYPE BODY person_type
    IS
    	-- 풀 네임(성 + 이름) 반환
        MEMBER FUNCTION full_name	RETURN VARCHAR2
        IS
        BEGIN
        	RETURN family_name|| ' ' || given_name;
        END;
        
       -- 비교를 위한 메소드. full_name끼리 비교한다.
       MAP MEMBER FUNCTION person_type_map RETURN VARCHAR2
       IS
       BEGIN
       	RETURN full_name;
       END;
    END;
    DECLARE
    	-- MAP 또는 ORDER 메소드를 사용한 객체 타입의 비교 예제
        v1 person_type := person_type('홍', '길동', NULL, NULL);
        v2 person_type := person_type('임', '꺽정', NULL, NULL);
        v3 person_type := person_type('홍', '길동', NULL, NULL);
    BEGIN
    	DBMS_OUTPUT.PUT_LINE(CASE WHEN v1 = v2 THEN
        							   v1.full_name || ' == ' || v2.full_name
                                  ELSE v1.full_name || ' <> ' || v2.full_name
                             END);
        DBMS_OUTPUT.PUT_LINE(CASE WHEN v1 = v3 THEN
        							   v1.full_name || ' == ' || v3.full_name
                                  ELSE v1.full_name || ' <> ' || v3.full_name
                             END);
    END;
    /
    
    홍 길동 <> 임 꺽정
    홍 길동 == 홍 길동

    ORDER 멤버 메소드

    ORDER 멤버 메소드는 키워드 ORDER MEMBER FUNCTION을 사용하여 선언된다.

    ORDER 멤버 메소드는 MAP 멤버 메소드와 마찬가지로 두 값의 비교를 위해 사용된다. 

    둘의 차이점은 MAP 멤버 메소드가  두 값의 비교를 위한 값을 반환하고 비교는 오라클이 수행하는 반면, ORDER 멤버 메소드는 값을 직접 비교하고 그 결과를 반환한다는 것이다. 비교 결과는 음수, 0, 양소 세 가지 중 하나를 반환해야 한다.(따라서 ORDER 멤버 메소드의 반환값은 INTEGER나 NUMBER와 같은 수치형이어야 한다.)

     

    MAP 멤버 메소드와 마찬가지로 ORDER 멤버 메소드가 선언되면 두 값의 비교가 필요할 때에는 오라클이 언제라도 자동으로 인식하여 사용한다. 기능적으로 볼 때 MAP 멤버 메소드와 ORDER 멤버 메소드는 완전히 동일한 기능을 수행한다. 구현 측면에서 MAP 멤버 메소드는 구현이 간단하다는 장점이 있고, ORDER 멤버 메소드는 복잡한 로직 구조를 구현하기에 적합하다는 장점이 있다.

    -- ORDER 멤버 메소드 명세
    CREATE OR REPLACE TYPE person_type AS OBJECT(
    	family_name	VARCHAR2(30),	-- 성
        given_name	VARCHAR2(30),	-- 이름
        address_1	VARCHAR2(30),	-- 기본 주소
        address_2	VARCHAR2(30)	-- 상세 주소
    
    	MEMBER		FUNCTION full_name	RETURN VARCHAR2,	-- 풀 네임 반환 메소드
        ORDER MEMBER FUNCTION person_type_order(a_comp person_type)
        									RETURN INTEGER	-- 비교를 위한 메소드
    );
    -- ORDER 멤버 메소드 본체
    CREATE OR REPLACE TYPE BODY person_type
    IS
    	-- 풀 네임(성+ 이름) 반환
        MEMBER FUNCTION full_name	RETURN VARCHAR2
        IS
        BEGIN
        	RETURN family_name || ' ' || given_name;
        END;
        
        -- 비교를 위한 메서드
        ORDER MEMBER FUNCTION person_type_order(a_comp person_type) RETURN INTEGER
        IS
        BEGIN
        	IF SELF.full_name < a_comp.full_name THEN
            	RETURN -1;
            ELSIF SELF.full_name > a_comp.full_name THEN
            	RETURN 1;
            ELSE
            	RETURN 0;
            END IF;
        END;
    END;

     

    -- ORDER 멤버 메소드 사용
    DECLARE
    	-- MAP 또는 ORDER 메소드를 사용한 객체 타입의 비교 예제
        v1 person_type := person_type('홍', '길동', NULL, NULL);
        v2 person_type := person_type('임', '꺽정', NULL, NULL);
        v3 person_type := person_type('홍', '길동', NULL, NULL);
    BEGIN
    	DBMS_OUTPUT.PUT_LINE(CASE WHEN v1 = v2 THEN
        							   v1.full_name || ' == ' || v2.full_name
                                  ELSE v1.full_name || ' <> ' || v2.full_name
                             END);
        DBMS_OUTPUT.PUT_LINE(CASE WHEN v1 = v3 THEN
        							   v1.full_name || ' == ' || v3.full_name
                                  ELSE v1.full_name || ' <> ' || v3.full_name
                             END);
    END;
    /
       
    홍 길동 <> 임 꺽정
    홍 길동 == 홍 길동

     

    MAP과 ORDER가 기능적으로 동일하기는 하지만 성능 측면에서는 MAP 멤버 메소드가 유리하다.

    이유는 SQL에 사용 시 ORDER 멤버 메소드는 비교 대상인 두 값의 비교를 위해 반복적으로 호출되어야 하기 때문이다.

    MAP은 비교를 위한 반환값을 일괄적으로 계산한 후에 비교는 오라클이 직접 수행하므로 ORDER에 비해 속도가 훨씬 빠르다.(PL/SQL프로그램 자체의 실행 속도는 SQL에 비해 느리다는 점을 잊지 말아야 한다.)

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    'DATABASE > SQL, PL-SQL' 카테고리의 다른 글

    객체 타입- 3  (0) 2021.05.06
    Object Type(객체 타입) - 2  (0) 2021.04.29
    Trigger(트리거)  (0) 2021.04.14
    서브프로그램의 다양한 기능들 - 매개변수  (0) 2021.04.01
    Package(패키지) - 2  (0) 2021.03.25

    댓글