< 이전 | 다음 >

학습 7: 예외 처리 및 롤백

올바르지 않은 데이터 검색, 올바르지 않은 데이터 삽입 또는 응용프로그램과 데이터 소스 사이 불일치를 예방하기 위해 데이터 소스에 액세스하는 응용프로그램에서 오류를 적절히 처리하는 것은 매우 중요합니다. 이 학습에서는 관계형 데이터베이스에 액세스할 때 오류에 응답하는 데 사용할 수 있는 몇 가지 도구에 대해 설명합니다.

SQL 데이터 액세스의 예외 처리

이 학습서의 예제에서는 종종 try 블록에 데이터 액세스 명령문을 둡니다.

function execDropTable(status StatusRec inOut)
  try  
    execute #sql{
      DROP TABLE EGL.CUSTOMERTEMP
      } ;
  onException (exception SQLException)
    ConditionHandlingLib.HandleException(status, exception);
  end
end
try 블록에 코드를 넣으면 EGL이 해당 코드를 일반적으로 실행하려 합니다. EGL에 오류가 발생하면 try 블록에서 코드 실행을 중지하고 try 블록에 남아 있는 모든 명령문을 건너뛴 다음 onException 블록으로 직접 이동합니다. onException 블록에 코드를 넣어 오류에서 복구하거나 문제점을 정정하거나 로그 파일에 오류 정보를 쓸 수 있습니다.

try 블록에서 EGL에 오류가 발생하면 예외 레코드도 작성하여 오류에 대한 정보를 제공합니다. 해당 레코드를 onException 블록에 전달합니다. 오류의 원인을 판별하고 오류에서 복구하는 데 도움이 되도록 onException 블록에서 해당 레코드의 정보를 사용할 수 있습니다. 위의 예제에서 onException 블록은 오류를 처리하는 함수에 예외 레코드를 전달합니다.

오류 유형에 따라 예외 레코드에는 여러 스테레오타입 중 하나가 있을 수 있습니다. 위의 예제에서 onException 블록은 SQL 오류 및 기타 관계형 데이터베이스 오류용인 SQLException 스테레오타입의 예외 레코드를 요구합니다. EGL에 널값 참조 등과 같은 다른 유형의 오류가 발생하면 다른 유형의 예외 레코드가 작성됩니다(널값 참조의 경우, NullValueException 스테레오타입이 있는 예외 레코드).

다음 예제에서와 같이 다른 유형의 오류를 각각 다르게 처리하도록 단일 try 블록 다음에 여러 onException 블록을 둘 수 있습니다.
function execDropTable(status StatusRec inOut)
  try  
    execute #sql{
      DROP TABLE EGL.CUSTOMERTEMP
      } ;
  onException (exception SQLException)
    ConditionHandlingLib.HandleException(status, exception);
  onException (exception AnyException)
		HandleOtherError(exception);
  end
end
이 예제에서 첫 번째 onException 블록은 SQLException 레코드를 요구하며 두 번째 블록은 AnyException 스테레오타입을 요구합니다. 이 경우, EGL에 SQL 오류가 발생하면 첫 번째 onException 블록이 실행됩니다. EGL에 다른 유형의 오류가 발생하면 두 번째 블록이 실행됩니다. AnyException 스테레오타입은 스테레오타입에 관계없이 모든 오류에 응답하는 데 사용하는 일반 사용자 예외 레코드 스테레오타입입니다.

모든 예외 레코드에는 최소 두 개의 필드(오류의 오류 코드가 있는 messageID 필드 및 문제점의 간략한 설명이 있는 message 필드)가 있습니다. 스테레오타입에 따라 예외 레코드에는 다른 필드가 있을 수 있습니다. 예를 들어, SQLRecord 스테레오타입에는 명령문의 상태가 있는 CHAR(5) 값을 보유하는 sqlState 필드와 DBMS에서의 INT 리턴 코드를 보유하는 sqlCode 필드가 있습니다.

올바르지 않은 정보를 전달하고, 존재하지 않는 레코드를 삭제하거나 갱신 또는 동일한 1차 키가 있는 레코드를 추가하려고 시도하여 예외 처리를 시도할 수 있습니다. 다음 예제에서는 데이터베이스의 기존 행과 같은 ID 번호가 있는 새 고객 레코드를 추가하려 합니다. 따라서, 1차 키와 충돌을 일으키며 EGL이 SQLException 예외를 처리하도록 프롬프트됩니다.

  1. programs 패키지에 이름이 exceptionHandlingTest인 새 프로그램을 작성하십시오.
  2. 다음과 같이 새 프로그램의 main 함수에서 고객 레코드 변수를 작성하여 정보(데이터베이스에 이미 있는 ID 번호 포함)를 지정하십시오.
    package programs;
    
    import eglderbyr7.data.Customer;
    
    program exceptionHandlingTest type BasicProgram {}
      
      function main()
        customer Customer;
        customer.FirstName = "John";
        customer.LastName = "Doe";
        customer.CustomerId = 1;
      end
    
    end
  3. try 블록에서 다음과 같이 데이터베이스에 레코드를 추가하십시오.
    try
      add customer;
      SysLib.writeStdout("Customer added successfully.");
    end
    오류에 응답하는 일치하는 onException 블록이 없으므로 이 try 블록은 그리 유용하지 않습니다.
  4. try 블록을 닫는 end 앞에 onException 블록을 추가하여 EGL이 try 블록의 코드를 실행하려 할 때 발생하는 SQLException을 처리하십시오.
    try
      add customer;
      SysLib.writeStdout("Customer added successfully.");
    onException(ex SQLException)
      SysLib.writeStdout("SQL exception "::ex.messageID);
      SysLib.writeStdout("message = "::ex.message);
      SysLib.writeStdout("SQLCode = "::ex.sqlCode);
      SysLib.writeStdout("SQLState = "::ex.sqlState);
    end
    전체 프로그램은 다음과 같습니다.
    package programs;
    
    import eglderbyr7.data.Customer;
    
    program exceptionHandlingTest type BasicProgram {}
      
      function main()
        customer Customer;
        customer.FirstName = "John";
        customer.LastName = "Doe";
        customer.CustomerId = 1;
      
        try
          add customer;
          SysLib.writeStdout("Customer and order added successfully.");
        onException(ex SQLException)
          SysLib.writeStdout("SQL exception "::ex.messageID);
          SysLib.writeStdout("message = "::ex.message);
          SysLib.writeStdout("SQLCode = "::ex.sqlCode);
          SysLib.writeStdout("SQLState = "::ex.sqlState);
        end
        
            
      end
      
    end
  5. 프로그램을 저장하고 생성하여 실행하십시오. 콘솔이 onException 블록에서 출력을 표시합니다.
    SQL exception EGL0504E
    message = EGL0504E ADD: The statement was aborted 
      because it would have caused a duplicate key value 
      in a unique or primary key constraint or unique 
      index identified by 'SQL070307095259210' defined 
      on 'CUSTOMER'.[sqlstate:23505][sqlcode:20000]
    EGL0002I The error occurred in the 
      exceptionHandlingTest program processing the main function.
    SQLCode = 20000
    SQLState = 23505

데이터베이스에 변경사항 롤백

Derby를 포함하여, 대부분의 관계형 데이터베이스는 복구 가능 자원입니다. 복구 가능한 자원을 변경할 때 변경사항을 저장하는 확약을 실행해야 변경사항이 영구적으로 됩니다. 이 방식으로, 변경사항을 확약하기 전에 예외가 발생하는 경우 데이터베이스의 변경사항을 되돌릴 수 있습니다.

sqlCommitControl 빌드 설명자 옵션을 NOAUTOCOMMIT로 설정하여 EGL이 자동으로 각 변경사항을 확약하지 않도록 할 수 있습니다. 자동 확약을 꺼져 있는 상태라면 수동으로 sysLib.commit()을 호출하여 변경사항을 확약하고 sysLib.rollback()을 호출하여 마지막 확약 이후로 변경된 사항을 되돌릴 수 있습니다. sysLib.commit()으로 변경사항을 확약하지 않으면 EGL이 항상 실행 단계의 끝(예: main 프로그램의 끝)에서 변경사항을 확약합니다.

예를 들어, 데이터베이스의 여러 사항을 변경한다고 가정하십시오(예: 새 고객 추가 및 해당 고객의 첫 번째 주문에 대한 주문 정보 입력). 일부 해당 오퍼레이션이 다른 오퍼레이션이 실패하기 전에 성공하면 주문이 없는 고객이 남거나 주문을 한 고객이 없는 주문이 남을 수 있습니다. 이런 경우, 다음과 같이 onException 블록에 롤백을 넣어 변경사항을 되돌리고 데이터베이스 외부에 불완전한 정보를 보존하십시오.

  1. sqlCommitControl 빌드 설명자 옵션을 NOAUTOCOMMIT로 설정하십시오.
    1. 프로젝트 탐색기 보기에서 EGLSQL 프로젝트의 빌드 설명자를 두 번 클릭하여 EGL 빌드 파트 편집기로 여십시오. 이 파일은 EGLSQL/EGLSource/EGLSQL.eglbld에 있습니다.
    2. 빌드 파트 편집기에서 지정된 옵션만 표시 선택란을 지우십시오.
    3. 옵션에서 sqlCommitControl을 찾으십시오.
    4. sqlCommitControl의 값을 NOAUTOCOMMIT로 설정하십시오.
      주: 편집을 위해 sqlCommitControl 옵션을 열려면 해당 옵션 옆의 열을 천천히 두 번 클릭하십시오. 열을 빠르게 세 번 클릭할 수도 있습니다.
    5. 빌드 설명자를 저장하고 닫으십시오.
  2. 이전 절에서 작성한 exceptionHandlingTest 프로그램을 여십시오.
  3. 이미 프로그램에 있는 고객 레코드에 주문 레코드를 추가하십시오.
    program exceptionHandlingTest type BasicProgram {}
      
      function main()
        customer Customer;
        customer.FirstName = "John";
        customer.LastName = "Doe";
        customer.CustomerId = 1;
        
        customerOrder Orders;
        customerOrder.CustomerId = 1;
        customerOrder.OrderId = 50;
        customerOrder.OrderAmount = 0;
        customerOrder.OrderDetails = 
          "This is my first order, so get it right!";
        
        ...
        
      end
  4. try 블록에서 먼저 주문을 추가한 다음 고객을 추가하십시오.
    try
      add customerOrder;
      SysLib.writeStdout("Order added successfully.");
      add customer;
      SysLib.writeStdout("Customer added successfully.");
    end
    이 경우, 중복된 1차 키로 인해 주문은 추가되지만 고객은 추가되지 않습니다. 따라서, onException 블록에서 롤백을 호출하여 ORDERS 테이블의 변경사항을 되돌릴 수 있습니다.
  5. 다음과 같이 try 블록 다음에 롤백을 포함하여 onException 블록을 추가하십시오.
    try
      add customerOrder;
      SysLib.writeStdout("Order added successfully.");
      add customer;
      SysLib.writeStdout("Customer added successfully.");
    onException(ex SQLException)
      SysLib.writeStdout("SQL exception "::ex.messageID);
      SysLib.writeStdout("message = "::ex.message);
      SysLib.writeStdout("Performing rollback.");
      SysLib.rollback();
    end
    이제, add 문 중 하나가 실패하면 onException 블록이 실행되어 데이터베이스의 변경사항을 되돌리려고 시도합니다.
  6. 예외가 발생하기 전에 추가된 주문을 롤백하여 성공적으로 제거했는지 여부를 테스트하는 코드를 onException 블록 다음에 추가하십시오. 주문 레코드가 여전히 데이터베이스에 있는지 확인하려면 다음과 같이 noRecordFound와 비교해 보십시오.
    tempOrder Orders;
    tempOrder.OrderId = 50;
    get tempOrder;
    
    if (tempOrder is noRecordFound)
      SysLib.writeStdout("The order was rolled back successfully.");
    else
      SysLib.writeStdout("The order is still in the database");
    end
    데이터베이스 호출 상태를 확인하는 또 다른 방법으로서 sqlLib.sqlData 시스템 변수의 값을 테스트합니다. 이 시스템 변수는 레코드이며 해당 필드는 마지막 SQL 데이터베이스 오퍼레이션의 성공 또는 실패에 대한 정보를 저장합니다. 특히, sqlLib.sqlData.sqlcode 필드는 오퍼레이션이 성공적으로 완료되어 결과를 리턴하면 0을 포함하며 오퍼레이션이 성공적으로 완료되었지만 SELECT 문이 결과를 리턴하지 않으면 100을 포함합니다. 따라서, 다음 코드를 사용하여 행이 발견되었는지 여부를 확인할 수도 있습니다.
    if (sqlLib.sqlData.sqlcode == 0)
    	SysLib.writeStdout("The order is still in the database");
    else
    	if (sqlLib.sqlData.sqlcode == 100)
    		SysLib.writeStdout("The order was rolled back successfully.");
    	end
    end
  7. 프로그램을 생성하고 실행하십시오.
다음 프로그램 출력은 주문이 데이터베이스에 추가되고 예외가 발생한 다음 롤백하여 주문이 성공적으로 제거되었음을 나타냅니다.
Order added successfully.
SQL exception EGL0504E
message = EGL0504E ADD: The statement was aborted 
  because it would have caused a duplicate 
  key value in a unique or primary key constraint 
  or unique index identified by 'SQL070307095259210' 
  defined on 'CUSTOMER'.[sqlstate:23505][sqlcode:20000]
EGL0002I The error occurred in the exceptionHandlingTest 
  program processing the main function.
Performing rollback.
The order was rolled back successfully.
exceptionHandlingTest.egl 파일의 코드가 완성되었습니다. 파일에 빨간색 X 기호로 표시된 오류가 나타나면 코드가 학습 7에서 완성된 exceptionHandlingTest.egl 파일의 코드와 일치하는지 확인하십시오.

학습 체크포인트

이 학습에서는 SQL 오퍼레이션으로 오류를 처리하는 기초 작업에 대해 배웠습니다. 문제점을 예상하고 응용프로그램이 복구할 수 있도록 예외 처리를 제공하는 것은 매우 중요합니다.

예외를 초래할 수 있는 런타임 오류에 대한 자세한 정보는 EGL Java™ 런타임 오류 코드를 확인하십시오. EGL이 작성할 수 있는 예외 레코드 목록은 EGL 코어 예외 레코드를 확인하십시오.