본문 바로가기
  • 실행력이 모든걸 결정한다
Spring Series/Spring Framework

[Spring] JDBC 트랜잭션

by 김코더 김주역 2021. 1. 14.
반응형

1. 개념

- 트랜잭션의 개념을 이해하기 전에 COMMIT과 ROLLBACK이 무엇인지 알 필요가 있다.

  • COMMIT : 변경된 내용을 모두 영구적으로 저장하는 명령어
  • ROLLBACK : 문제가 발생 했을 때, 이전에 COMMIT 했던 시점으로 되돌리는 명령어

 

웹과 데이터베이스를 연동하는 테스트를 할 때, 명령프롬프트로 Oracle 데이터베이스에 접속하여 INSERT문으로 레코드들을 직접 추가하고 웹에서 SELECT문으로 전체 테이블을 불러오는 간단한 시스템을 만들어 사용했다.

그런데 직접 INSERT문으로 추가한 레코드들이 조회가 안되어서 무엇이 문제였나 고민해본적이 있었다.

결론은 데이터베이스에서 COMMIT; 명령으로 최종 저장을 하지 않은 것이 문제였던 것이다.

이렇게 변경된 내용을 모두 영구적으로 저장하는 명령어가 COMMIT이다.

 

트랜잭션은 두 COMMIT 지점 사이를 의미한다.

게임을 예로 들면, 세이브 포인트가 이 개념과 비슷할 것 같다.

ROLLBACK 명령어를 호출하면 최근 COMMIT 시점으로 되돌아가는데, 오류 및 제약 조건 부합으로 변경된 내용을 불가피하게 되돌려야 할 때 유용하게 쓰인다. 

 

 

2. 자동으로 COMMIT 되는 경우

다음과 같은 경우에는 자동으로 COMMIT이 되기 때문에 유의하도록 한다.

  • SQLPLUS가 정상적으로 종료된 경우
  • DDL, DCL 명령문이 수행된 경우 (예 - CREATE, DROP, ALTER, GRANT, REVOKE 등) 

 

 

3. Connection을 이용한 트랜잭션

1) 트랜잭션의 기본 동작

- JDBC의 트랜잭션은 하나의 Connection 객체를 가져오고 닫는 사이에 진행된다.

Connection c = dataSource.getConnection();
c.setAutoCommit(false); // JDBC는 기본적으로 자동 커밋이 설정되어 있기 때문에 이를 해제한다.
try {
    // DB 작업들
    c.commit();
} catch (Exception e) {
    c.rollback();
}
c.close();

- 하나의 DB Connection 안에서 만들어지는 트랜잭션을 로컬 트랜잭션이라고 한다.

- 2개 이상의 DB로의 작업을 하나의 트랜잭션으로 만들기 위해서는 JDBC의 Connection을 이용할 수 없고, 별도의 트랜잭션 관리자를 통해 트랜잭션을 관리하는 방식인 글로벌 트랜잭션을 지원하는 JTA(Java Transaction API)를 이용해야 한다.

 

2) Connection 객체의 생성과 데이터 액세스 작업이 다른 곳에서 수행되는 경우

(1) Connection 객체를 파라미터로 전달하는 방법 (비추천)

- DB 관련 메소드마다 파라미터가 추가되는 꼴이니 코드가 복잡해지고, Connection을 사용하지 않는 데이터 액세스 기술에 독립적일 수 없게 된다.

(2) Connection 객체를 트랜잭션 동기화 저장소에 보관하는 방법 (추천)★

- 작업 스레드마다 독립적으로 Connection 오브젝트를 저장하고 관리하기 때문에 다중 사용자 환경에서도 충돌이 날 염려가 없다.

TransactionSynchronizationManager.initSynchronization(); // 트랜잭션 동기화 작업 초기화
Connection c = DataSourceUtils.getConnection(dataSource); // Connection 오브젝트 생성 + 저장소에 바인딩
c.setAutoCommit(false); // JDBC는 기본적으로 자동 커밋이 설정되어 있기 때문에 이를 해제한다.
try {
    // DB 작업들
    c.commit();
} catch (Exception e) {
    c.rollback();
    throw e;
} finally {
    DataSourceUtils.releaseConnection(c, dataSource); // Connection 닫기
    TransactionSynchronizationManager.unbindResource(this.datasource); // 저장소 바인딩 해제
    TransactionSynchronizationManager.clearSynchronization(); // 동기화 작업 종료
}

 

 

4. TransactionTemplate을 이용한 트랜잭션

- TransactionTemplate은 Spring에서 쉽게 트랜잭션을 처리할 수 있도록 제공한다. try/catch 블록을 작성해야 하는 번거로움도 사라진다.

- TransactionTemplate은 JDBC 뿐만 아니라 iBatis, JPA, Hibernate 환경에서도 사용 가능하다.

이제 사용법을 설명할 것인데, JdbcTemplate으로 데이터베이스를 관리한다고 가정할 것이다.

JdbcTemplate 포스팅 참고

kimcoder.tistory.com/241

 

[Spring] JdbcTemplate

JdbcTemplate는 DriverManager에 드라이버를 로드하고, Connection, Statement, ResultSet 관련 작업, 자원 해제 작업을모두 담당 해주기 때문에 상당한 소스코드를 줄일 수 있게 된다. 이제 Spring JdbcTemplate..

kimcoder.tistory.com

 

 

<Dao.java>

처음에 JdbcTemplate, TransactionTemplate 객체를 생성해주었다. 이 객체들은 servlet-context.xml 설정 파일에서 bean 객체로 만들어올 것이므로 setter 메소드를 생성했다.

트랜잭션을 적용하고 싶은 메소드안에 TransctionTemplate객체의 execute 메소드를 호출하면 된다.
이 execute 메소드의 인자로 TransactionCallbackWithoutResult 클래스를 익명 클래스로 넣었으며, 이 클래스는 doInTransactionWithoutResult 메소드를 필수로 Override 해야하는 추상클래스이다.

doInTransactionWithoutResult 메소드의 parameter인 status로 트랜잭션 관리가 이루어지게 된다.

JdbcTemplate의 query, update 메소드는 위에 링크한 포스팅에서 설명했으므로 생략한다.

package com.example.demo;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

public class Dao {
	
	JdbcTemplate template;
	TransactionTemplate transactionTemplate;

	public void setTemplate(JdbcTemplate template) {
		this.template = template;
	}
	
	public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
		this.transactionTemplate = transactionTemplate;
	}
	
	public ArrayList<User> list(){
		String query = "select * from member";
		return (ArrayList<User>)template.query(query,new BeanPropertyRowMapper<User>(User.class));
	}
	
	public void insertEx(final String id, final String pw){
		this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {

			@Override
			protected void doInTransactionWithoutResult(TransactionStatus status) {
				String query = "insert into member (ID, PW) values (?,?)";
				template.update(query,new PreparedStatementSetter() {

					@Override
					public void setValues(PreparedStatement ps) throws SQLException {
						ps.setString(1, id);
						ps.setString(2, pw);
					}			
				});
			}
			
		});
	}
}

 

 

<servlet-context.xml>

총 5개의 bean이 정의되었다.
Spring bean은 다른 bean에서 참고 객체로 받거나, Controller에서 @Autowired 어노테이션으로 받는다.

"dataSource", "template" bean은 위에 링크한 포스팅에서 설명했으므로 생략한다.

- "transactionManager" bean : Spring에서 제공하는 DataSourceTransactionManager 클래스로부터 bean 객체를 생성하고, name property에서 참조 객체로 "dataSource"를 사용하겠다고 지정한다. 바로 위에서 만든 "dataSource" bean을 참조하는 것이다.

- "transactionTemplate" bean : Spring에서 제공하는 TransactionTemplate 클래스로부터 bean 객체를 생성하고, name property에서 참조 객체로 "transactionManager"를 사용하겠다고 지정한다. 바로 위에서 만든 "transactionManager" bean을 참조하는 것이다.

- "dao" bean : com.example.demo 패키지의 Dao 클래스로부터 bean 객체를 생성하고, property 속성으로 "template", "transactionTemplate" bean을 참조하겠다고 지정한다. 바로 위에서 만든 "template", "transactionManager" bean을 참조하는 것이다.

<beans:beans ...>
  <beans:bean name="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <beans:property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
    <beans:property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
    <beans:property name="username" value="DB아이디"/>
    <beans:property name="password" value="DB비밀번호"/>
  </beans:bean>

  <beans:bean name="template" class="org.springframework.jdbc.core.JdbcTemplate">
    <beans:property name="dataSource" ref="dataSource"/>
  </beans:bean>

  <beans:bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <beans:property name="dataSource" ref="dataSource"/>
  </beans:bean>

  <beans:bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <beans:property name="transactionManager" ref="transactionManager"/>
  </beans:bean>

  <beans:bean name="dao" class="com.example.demo.Dao">
    <beans:property name="template" ref="template"/>
    <beans:property name="transactionTemplate" ref="transactionTemplate"/>
  </beans:bean>
</beans:beans>

 

 

 

5. 트랜잭션의 종류

- 트랜잭션도 여러 개를 만들 수 있으며, 트랜잭션끼리도 의존 관계를 줄 수 있다. 서로의 트랜잭션이 어떻게 영향을 미치게 하는가를 정의하는 것을 전파(Propagation)라고 한다.

 

1) 종류

- PROPAGATION_REQUIRED(value="0") : 기본값. 진행 중인 트랜잭션이 없으면 새로 시작하고, 이미 시작된 트랜잭션이 있으면 이에 참여한다. 하나의 트랜잭션이라도 문제가 발생하면 전체를 롤백함.
- PROPAGATION_SUPPORTS(value="1") : 부모 트랜잭션에 의존하며, 부모 트랜잭션이 없다면 트랜잭션 없이 독립적으로 실행함. 
- PROPAGATION_MANDATORY(value="2") : 어느 트랜잭션에 반드시 포함 되어야 하며, 트랜잭션이 있는 곳에서 호출해야 함.
- PROPAGATION_REQUIRES_NEW(value="3") : 항상 새로운 트랜잭션을 시작하며, 진행 중인 트랜잭션이 있으면 보류해 둠. 독립적인 트랜잭션이 보장되어야 하는 코드에 사용함
- PROPAGATION_NOT_SUPPORTED(value="4") : 트랜잭션을 적용하지 않으며, 진행 중인 트랜잭션이 있으면 보류해 둠.
- PROPAGATION_NEVER(value="5") : 트랜잭션을 적용하지 않으며, 진행 중인 트랜잭션이 있으면 예외를 발생시킴.

 

 

2) 적용 방법

TransactionTemplate bean의 propagationBehavior에 위에 나열한 종류에 맞는 value값을 지정해주면 된다.

<beans:bean name="transactionTemplate1" class="org.springframework.transaction.support.TransactionTemplate">
  <beans:property name="transactionManager" ref="transactionManager"/>
  <beans:property name="propagationBehavior" value="0"/>
</beans:bean>

<beans:bean name="transactionTemplate2" class="org.springframework.transaction.support.TransactionTemplate">
  <beans:property name="transactionManager" ref="transactionManager"/>
  <beans:property name="propagationBehavior" value="0"/>
</beans:bean>

 

 

+) 참고 Mybatis를 사용할 경우 트랜잭션 처리(타 블로그 포스팅)

codevang.tistory.com/264

 

스프링, Mybatis, MySQL_트랜잭션 처리 [4/5]

- Develop OS : Windows10 Ent, 64bit - WEB/WAS Server : Tomcat v9.0 - DBMS : MySQL 5.7.29 for Linux (Docker) - Language : JAVA 1.8 (JDK 1.8) - Framwork : Spring 3.1.1 Release - Build Tool : Maven 3.6..

codevang.tistory.com

 

 

반응형

댓글