DB의 데이터변경은 '트랜잭션(Transaction)' 단위로 이루어진다.
트랜잭션이란?
DB 데이터 변경은 단 하나의 쿼리만으로 이루어지지 않는다. 입금을 생각해보자. A가 B에게 5만원을 입금하면 두 가지 쿼리가 실행되어야 한다.
1) A계좌에 5만원 출금
2) B계좌에 5만원 입금
1) 과 2)는 독립되어 있지 않다. 1)이 성공해도 2)가 실패하면 1)은 원복되어야 한다. 그러므로 여러 처리를 하나의 단위로 묶는 논리적 단위가 필요한데, 이가 바로 '트랜잭션'이다.
DB에서 트랜잭션 구현하기
H2 DB에서 트랜잭션을 이해해보자.
계좌 테이블(Account)은 계좌정보가 들어있다. 사쿠라는 20000원 김채원은 30000이 계좌에 들어있다. 그럼 사쿠라가 김채원에게 5000원을 송금하는 과정을 구현해보자.
◎ 자동커밋 막기 : SET AUTOCOMMIT false;
SQL문이 실행되고 실행결과를 DB에 반영하는 과정을 '커밋(Commit)'이라고 한다. 보통 SQL문이 실행되면 자동으로 커밋되도록 설정되어 있으므로 자동커밋을 막아준다.
◎ 사쿠라 5000원 출금 : UPDATE account SET amount = amount - 5000 WHERE owner = '사쿠라';
◎ 김채원 5000원 입금 : UPDATE account SET amount = amount + 5000 WHERE owner = '김채원';
SQL문이 실행되면 DB에 반영되는 것처럼 보이지만 '커밋'이 되지 않으면 DB는 언제든 롤백할 수 있는 매커니즘을 가지고 있다.
- 실행 쿼리
set AUTOCOMMIT false;
UPDATE account SET amount = amount - 5000 WHERE owner = '사쿠라';
UPDATE account SET amount = amount + 5000 WHERE owner = '김채원';
SELECT * FROM account;
- 결과
테이블에는 반영된 것처럼 보이지만 롤백을 하면 다시 원복된다.
- 실행 쿼리
ROLLBACK;
SELECT * FROM account
- 결과
다시 계좌가 원복되었다. 이처럼 여러 쿼리가 하나의 작업단위에서 모두 성공적으로 수행되어야 DB에 실제 반영이 된다. 이것이 트랜잭션의 원리이다.
JDBC에서 트랜잭션 이해하기
앞에서는 직접 쿼리를 실행해보았다. 이번에는 Application에서 쿼리를 날려 테이블을 조작해보겠다. 이를 지원하는 가장 기본적인 JAVA 라이브러리가 JDBC이다.
JDBC는 개발자가 트랜잭션을 보다 쉽게 사용할 수 있도록 Connection이라는 추상화된 개념을 제시한다. Connection은 App-DB를 연결하는 객체로 Connection의 생성은 트랜잭션의 시작을 의미하고 Connection의 종료는 트랜잭션 종료를 의미한다.
DbConnection 클래스
public class DbConnection {
// 커넥션 얻기(트랜잭션 시작)
public static Connection getConnection() {
//DB 커넥션 정보
String url = "jdbc:h2:tcp://localhost/~/test";
String id = "sa";
String pw = "";
Connection conn = null;
try {
Class.forName("org.h2.Driver"); // DBMS 드라이버
conn = DriverManager.getConnection(url, id, pw); //드라이버로 부터 커넥션 객체얻기
conn.setAutoCommit(false); // 자동커밋 풀기
} catch (SQLException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
return conn;
}
// 커넥션 종료 (트랜잭션 종료)
public static void close(Connection conn){
try {
conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
// 트랜잭션 커밋
public static void commit(Connection conn){
try {
conn.commit();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
//트랜잭션 롤백
public static void rollback(Connection conn){
try {
conn.rollback();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
Connection 객체를 얻기, 종료, 커밋, 롤백을 지원하는 클래스이다. 다시말해, 트랜잭션을 생성하고 종료하고 커밋하고 롤백을 할 수 있는 메소드를 지원하는 클래스이다.
public class DbDao {
public static void updateQuery(HashMap transfer) {
Connection conn = DbConnection.getConnection(); // 커넥션 얻기 ( 트랜잭션 시작 )
String sql = "UPDATE account SET amount = amount + ? WHERE owner = ?;"; //SQL문
Set<String> keys = transfer.keySet(); // 거래자 명단
try {
for( String key : keys ){
String owner = key; // 거래자명
int amount = (int)transfer.get(key); // 거래금액
//SQL 실행객체
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1,amount); //파라미터 세팅
ps.setString(2,owner); //파라미터 세팅
ps.executeUpdate(); // SQL 실행
}
DbConnection.commit(conn); //SQL문이 모두 정상 실행 완료되면 커밋
} catch (SQLException e) {
DbConnection.rollback(conn); //SQL문 실행중 오류발생시 롤백
throw new RuntimeException();
}finally {
DbConnection.close(conn); // 커넥션(트랜잭션) 종료
}
}
}
트랜잭션이 시작되면 SQL문이 실행되어야 한다. SQL을 실행하는 객체가 PreparedStatement 객체이다. PreparedStatement객체는 하나의 SQL문만 실행 가능하므로 for문을 사용하여 여러 SQL문을 실행하였다. SQL문 실행에 필요한 데이터는 HashMap에 담았다.
그럼 HashMap에 데이터를 담고 실제 해당 메소드를 실행해보자.
public class JdbcMain {
public static void main(String[] args) {
HashMap<String,Integer> transfer = new HashMap<>(); //거래 HashMap
transfer.put("사쿠라",-5000); // 사쿠라, 5000원 출금
transfer.put("김채원",5000); // 김채원, 5000원 입금
DbDao.updateQuery(transfer); // 거래 시작
}
}
- 결과
사쿠라는 20000 ➠ 15000, 김채원은 30000 ➠ 35000 으로 변경되었다.
이처럼 JDBC가 트랜잭션 개념을 구현하고 있어 JAVA APP에서 DB 테이블을 조작할 수 있다. 트랜잭션이 시작되고 SQL문이 실행되고 트랜잭션이 커밋,롤백,종료 되는 과정을 Connection과 PreparedStatement와 같은 개념으로 구현하였다.
JDBC 같은 기술을 두고 ORM(Object Relational Mapping)이라 부른다. 나중에 JDBC를 기반으로 개발자가 사용하기 쉽도록 Mybatis나 iBatis 같은 조금더 추상화된 방식의 ORM 기술도 나왔다. 하지만 아쉬운 점은 개발자가 SQL문을 직접 작성해야 하므로, SQL 관점 프로그래밍을 할 수 밖에 없다는 점이다.
이런 점을 해결한 ORM기술이 JPA이다. JPA 또한 JDBC를 기반으로 만들어진 기술이고 테이블을 조작하는 ORM 기술이므로 트랜잭션 개념이 상당히 중요하다. 그럼 다음 포스팅에서는 JPA 환경에서 트랜잭션은 어떻게 구현되는지 알아보겠다.
'JPA > JPA Basic' 카테고리의 다른 글
[JPA] 엔티티(Entity)가 기본생성자를 가져야 하는 이유 ( Reflection ) (0) | 2023.05.25 |
---|---|
[JPA] 트랜잭션(Transaction) 이해하기(2) - JPA (0) | 2023.05.24 |
[JPA] JPA 프로젝트 만들기 ( with Maven ) (2) | 2023.05.22 |
[JPA] JPA 동작원리 ( 영속성 컨텍스트 ) (0) | 2023.05.22 |
[JPA] 패러다임 불일치 (0) | 2023.05.18 |