Chapter 7. 트랜잭션 관리
데이터베이스 작업을 안전하게 수행하려면 트랜잭션 관리가 필수적입니다. 이 장에서는 트랜잭션의 개념, 중요성 그리고 마이바티스에서 트랜잭션을 어떻게 관리하는지에 대해 알아봅니다.
[Chapter 7. 트랜잭션 관리]
7.1. 트랜잭션 개념 설명
7.1.1. 트랜잭션의 중요성
7.1.1.1. 데이터 일관성 유지
7.1.1.2. 작업의 원자성 보장
7.1.2. ACID 속성 소개
7.1.2.1. 원자성(Atomicity)
7.1.2.2. 일관성(Consistency)
7.1.2.3. 고립성(Isolation)
7.1.2.4. 지속성(Durability)
7.2. 트랜잭션 경계 설정
7.2.1. 트랜잭션 시작과 종료
7.2.2. 트랜잭션 경계 설정 예제
7.3. 롤백 및 커밋 처리
7.3.1. 롤백의 필요성과 시점
7.3.2. 커밋의 필요성과 시점
7.3.3. 롤백과 커밋 처리 예제
7.1. 트랜잭션 개념 설명
트랜잭션이란, 데이터베이스의 상태를 변화시키기 위해 수행하는 작업의 단위를 말합니다. 트랜잭션은 여러 개의 쿼리로 이루어질 수 있으며, 이들 쿼리들은 모두 성공해야만 트랜잭션이 성공한 것으로 간주됩니다.
7.1.1. 트랜잭션의 중요성
데이터베이스에서 트랜잭션은 무척 중요한 역할을 담당합니다. 트랜잭션은 여러 데이터베이스 연산을 하나의 작업 단위로 묶는 역할을 하며, 이는 데이터의 일관성 유지와 작업의 원자성 보장에 중요한 요소입니다. 여기서 데이터 일관성 유지란, 모든 사용자가 동일한 정보를 보게 하는 것을 의미하고, 작업의 원자성 보장이란 트랜잭션의 모든 연산이 완료되거나, 아니면 하나도 수행되지 않아야 함을 의미합니다.
7.1.1.1. 데이터 일관성 유지
데이터베이스에서 데이터 일관성이란, 모든 사용자가 동일한 정보를 보게 하는 것을 의미합니다. 이를 위해 데이터베이스는 트랜잭션을 사용하여 여러 작업이 동시에 발생하더라도 데이터의 일관성을 유지합니다.
예를 들어, 한 사용자가 데이터를 수정하는 동안 다른 사용자가 동일한 데이터를 조회하려고 하면, 수정 작업이 완료될 때까지 조회 작업을 대기시킵니다. 이렇게 하면 모든 사용자가 일관된 데이터를 볼 수 있습니다.
[예제]
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User(1, "Alice", "alice@example.com");
mapper.updateUser(user); // 사용자 데이터 수정
session.commit();
User updatedUser = mapper.getUser(1); // 수정된 데이터 조회
System.out.println(updatedUser); // Alice
}
이처럼, 트랜잭션은 데이터의 일관성을 보장하므로 데이터베이스에 데이터가 동시에 액세스 하려는 여러 사용자들 사이에서 데이터 불일치 문제를 방지합니다.
7.1.1.2. 작업의 원자성 보장
원자성은 트랜잭션의 모든 연산이 완료되거나, 아니면 하나도 수행되지 않아야 함을 의미합니다. 만약 트랜잭션 중에 오류가 발생하면, 그동안 수행된 모든 연산이 롤백되어 트랜잭션 전의 상태로 돌아갑니다. 이렇게 함으로써 트랜잭션의 원자성이 보장됩니다.
[예제]
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User(1, "Alice", "alice@example.com");
User newUser = new User(2, "Bob", "bob@example.com");
mapper.insertUser(newUser); // 새로운 사용자 추가
mapper.updateUser(user); // 사용자 데이터 수정
throw new RuntimeException("Error!"); // 오류 발생
session.commit();
} catch (Exception e) {
// 오류 발생 시 롤백
}
위 예제에서 사용자를 추가하고 정보를 업데이트하는 도중 오류가 발생하면, 그동안 수행된 모든 연산(사용자 추가와 정보 업데이트)이 롤백됩니다. 이렇게 함으로써 작업의 원자성이 보장됩니다.
7.1.2. ACID 속성 소개
데이터베이스 트랜잭션에서 가장 중요한 속성은 ACID라고 불리는 네 가지 속성입니다. ACID는 원자성(Atomicity), 일관성(Consistency), 고립성(Isolation), 지속성(Durability)의 첫 글자를 따서 만든 약어입니다. 이 네 가지 속성이 보장되어야 트랜잭션이라고 할 수 있습니다.
7.1.2.1. 원자성(Atomicity)
원자성은 '모두 또는 아무 것도 아님'을 의미합니다. 트랜잭션의 모든 연산이 성공적으로 완료되거나, 아니면 하나도 수행되지 않아야 합니다. 만약 트랜잭션 중에 오류가 발생하면, 그동안 수행된 모든 연산이 롤백되어 트랜잭션 전의 상태로 돌아갑니다.
[예제]
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User(1, "Alice", "alice@example.com");
mapper.updateUser(user); // 사용자 데이터 수정
throw new RuntimeException("Error!"); // 오류 발생
session.commit();
} catch (Exception e) {
// 오류 발생 시 롤백
}
7.1.2.2. 일관성(Consistency)
일관성은 트랜잭션이 데이터를 일관된 상태에서 다른 일관된 상태로 변경해야 함을 의미합니다. 즉, 트랜잭션이 성공적으로 완료되면 데이터베이스는 일관된 상태를 유지해야 합니다.
[예제]
try (SqlSession session = sqlSessionFactory.openSession()) {
AccountMapper mapper = session.getMapper(AccountMapper.class);
mapper.debitAccount("Alice", 100); // Alice 계좌에서 100차감
mapper.creditAccount("Bob", 100); // Bob 계좌에 100추가
session.commit();
}
7.1.2.3. 고립성(Isolation)
고립성은 각 트랜잭션이 독립적으로 실행되어야 함을 의미합니다. 즉, 한 트랜잭션의 중간 결과는 다른 트랜잭션에게 보이지 않아야 합니다.
[예제]
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User(1, "Alice", "alice@example.com");
mapper.updateUser(user); // 사용자 데이터 수정
// 다른 트랜잭션은 수정 중인 데이터를 볼 수 없음
// 다른 트랜잭션은 수정이 완료된 후에만 데이터를 볼 수 있음
session.commit();
}
7.1.2.4. 지속성(Durability)
지속성은 트랜잭션이 성공적으로 완료된 후에는 결과가 영구적으로 반영되어야 함을 의미합니다. 즉, 시스템 오류가 발생하더라도 트랜잭션의 결과는 지속되어야 합니다.
[예제]
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User(1, "Alice", "alice@example.com");
mapper.updateUser(user); // 사용자 데이터 수정
session.commit(); // 트랜잭션 커밋
// 커밋된 후에는 사용자 데이터 수정 결과가 영구적으로 저장됨
}
7.2. 트랜잭션 경계 설정
데이터베이스에서 트랜잭션은 여러 연산이 모여 하나의 논리적인 작업 단위를 형성하는 것을 말합니다. 이 때, 이 논리적인 작업 단위가 언제 시작하고 언제 끝나는지를 알려주는 것이 트랜잭션 경계입니다. 정확한 트랜잭션 경계 설정은 트랜잭션의 원자성, 일관성, 독립성, 지속성(ACID)을 보장하는 중요한 요소입니다.
7.2.1. 트랜잭션 시작과 종료
트랜잭션은 일반적으로 사용자가 요청한 작업의 시작과 끝을 나타냅니다. 트랜잭션의 시작은 대개 데이터베이스 연산의 첫 번째 단계에서 발생하며, 트랜잭션의 종료는 모든 연산이 성공적으로 완료되거나, 문제가 발생하여 롤백이 필요한 시점을 나타냅니다.
7.2.2. 트랜잭션 경계 설정 예제
MyBatis에서는 SqlSession 객체를 통해 트랜잭션을 관리합니다. 이때, SqlSession 객체의 생명 주기가 바로 트랜잭션의 경계를 나타내는 셈입니다. 아래 코드는 이를 보여줍니다.
[예제]
try (SqlSession session = sqlSessionFactory.openSession()) { // 트랜잭션 시작
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User(1, "Alice", "alice@example.com");
mapper.insertUser(user); // 삽입 작업
mapper.updateUser(user); // 수정 작업
session.commit(); // 모든 작업이 성공적으로 수행되면 커밋하여 트랜잭션 종료
} catch (Exception e) {
session.rollback(); // 문제가 발생하면 롤백하여 트랜잭션 종료
}
위 코드에서 SqlSession 객체가 생성되는 순간 트랜잭션이 시작되며, commit() 혹은 rollback()이 호출되는 순간 트랜잭션이 종료됩니다. commit()은 모든 데이터베이스 변경사항을 확정 지으며, rollback()은 트랜잭션 도중 발생한 모든 변경사항을 원래대로 되돌립니다.
다음으로, 트랜잭션을 명시적으로 제어하는 방법을 알아봅시다.
[예제]
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.SIMPLE, false)) { // autoCommit 기능 끄기
UserMapper mapper = session.getMapper(UserMapper.class);
User user1 = new User(1, "Alice", "alice@example.com");
User user2 = new User(2, "Bob", "bob@example.com");
mapper.insertUser(user1);
mapper.insertUser(user2);
session.commit(); // 수동으로 커밋
} catch (Exception e) {
session.rollback(); // 문제가 발생하면 롤백
}
위의 코드에서는 SqlSession을 만들 때 두 번째 인자로 false를 전달함으로써 autoCommit 기능을 끄고, 이후에는 수동으로 commit()을 호출하여 트랜잭션을 제어합니다. 이 방법은 개발자가 트랜잭션을 보다 세밀하게 제어할 수 있게 해 줍니다.
코드의 session.commit()은 모든 변경사항을 데이터베이스에 영구적으로 반영하는 역할을 합니다. 만약 이 과정에서 문제가 발생하면 Exception이 발생하고, 이에 대한 처리를 catch 블록에서 수행할 수 있습니다. 이때 session.rollback()을 호출하면 트랜잭션 도중에 발생한 모든 변경사항이 취소되고, 데이터베이스는 트랜잭션 시작 전의 상태로 돌아갑니다.
따라서, 트랜잭션 경계는 애플리케이션에서 중요한 역할을 하는데, 이는 트랜잭션 내에서 수행되는 모든 연산이 하나의 논리적인 단위로 묶이기 때문입니다.
7.3. 롤백 및 커밋 처리
데이터베이스 작업에서는 두 가지 중요한 개념인 "롤백(Rollback)"과 "커밋(Commit)"에 대해 이해해야 합니다. 이 두 가지 작업은 트랜잭션을 제어하고 데이터 일관성을 유지하는 데 핵심적인 역할을 합니다.
7.3.1. 롤백의 필요성과 시점
롤백은 트랜잭션 도중 오류가 발생했을 때, 그 트랜잭션의 모든 작업을 취소하고 이전 상태로 되돌리는 작업을 의미합니다. 롤백이 필요한 이유는 데이터 일관성을 유지하기 위해서입니다. 예를 들어, 한 트랜잭션 내에서 두 개의 작업이 있고, 첫 번째 작업은 성공했지만 두 번째 작업에서 오류가 발생했다면, 데이터의 일관성을 위해 첫 번째 작업도 취소해야 합니다. 이럴 때 롤백을 사용하게 됩니다.
롤백은 트랜잭션 중에 오류가 발생한 시점에서 수행됩니다. 이 시점은 SqlSession 객체에 대해 rollback() 메소드를 호출하는 시점입니다.
7.3.2. 커밋의 필요성과 시점
커밋은 트랜잭션의 모든 작업이 성공적으로 완료되었을 때, 그 결과를 데이터베이스에 반영하는 작업을 의미합니다. 커밋이 필요한 이유는 데이터의 변화를 영구적으로 만들기 위해서입니다. 커밋을 수행하면, 해당 트랜잭션에서 수행한 모든 작업의 결과가 데이터베이스에 반영되고, 이후에는 이를 취소할 수 없습니다.
커밋은 트랜잭션의 모든 작업이 성공적으로 완료된 시점에서 수행됩니다. 이 시점은 SqlSession 객체에 대해 commit() 메서드를 호출하는 시점입니다.
7.3.3. 롤백과 커밋 처리 예제
[예제]
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
try {
User user = new User(1, "Alice", "alice@example.com");
mapper.insertUser(user);
session.commit(); // 트랜잭션 커밋
} catch (Exception e) {
session.rollback(); // 오류가 발생하면 롤백
}
}
위의 예제에서는 사용자 정보를 데이터베이스에 입력하는 작업을 수행하고 있습니다. 이 작업이 성공적으로 완료되면 session.commit()을 호출하여 트랜잭션을 커밋하고, 오류가 발생하면 session.rollback()을 호출하여 트랜잭션을 롤백합니다.
이렇게 롤백과 커밋을 적절히 사용하면 데이터의 일관성을 유지하고, 트랜잭션을 효과적으로 제어할 수 있습니다.
'GD's IT Lectures : 기초부터 시리즈 > 마이바티스(MyBatis) 기초부터 ~' 카테고리의 다른 글
[마이바티스(MyBatis)] 실습 및 예제 (0) | 2023.05.16 |
---|---|
[마이바티스(MyBatis)] 마이바티스와 스프링 연동 (0) | 2023.05.16 |
[마이바티스(MyBatis)] DAO (Data Access Object) 작성 (0) | 2023.05.16 |
[마이바티스(MyBatis)] 데이터베이스 연결 및 세션 생성 (0) | 2023.05.16 |
[마이바티스(MyBatis)] 동적 SQL (0) | 2023.05.16 |
댓글