티스토리 뷰

DB/MyBatis

트랜잭션(Transaction) 처리

xoo | 수진 2023. 8. 3. 14:18

📌 트랜잭션(Transaction)

 

 

✅ 두 쿼리가 실행되어야 하는 상황이 있다.

첫 번째 쿼리는 문제없이 실행됐지만 두 번째 쿼리에서 문제가 발생하였다.

그렇다면 데이터가 잘못된 상태로 저장된다.

두 쿼리가 모두 정상적으로 실행되어야 데이터의 무결성이 유지되기 때문이다.

그래서 한 개 이상의 쿼리가 모두 성공적으로 실행되어야 데이터가 정상적으로 처리되는 경우

DBMS 트랜잭션(transaction)을 이용해서 한 개 이상의 쿼리를 마치 한 개의 쿼리 처럼 처리할 수 있다.

 

 

✅ JDBC에서는 모든 DML 작업이 자동으로 auto commit으로 처리된다.

하지만 트랜잭션으로 처리해야만 하는 작업에서는 명시적으로 auto commit을 비활성 시킨 후에 작업해야 된다.

 

 

💡 둘 다 성공해서 실제 DB에 반영 => COMMIT
     또는
     둘 중에 하나라도 실패하면 모두 취소 => ROLLBACK

 

 

 


 

 

 

 

 

 

✅ 다음은 JDBC에서 트랜잭션 처리를 위한 가장 기본적인 코드로서,

      Service클래스에서 Connection API의 setAutoCommit(false) 메서드를 사용하여 비활성화 시키고

      commit() 또는 rollback() 메서드를 사용하여 트랜잭션 처리를 할 수 있다.

 

 

✅ dao.insert()와 dao.delete() 메서드가 모두 성공하면 commit()하고

      만약 하나라도 예외가 발생되면 rollback()으로 처리한다.

 

 

 

 


 

 

 

DeptMain.java
import java.util.List;
import java.util.Scanner;

import com.dto.DeptDTO;
import com.exception.DuplicatedDeptnoException;
import com.service.DeptService;
import com.service.DeptServiceImpl;

public class DeptMain {

	public static void main(String[] args) {

		// 화면처리 추가
		while(true) {
			Scanner scan = new Scanner(System.in);
			System.out.println("5. 삭제 및 수정하기");  // 여러개의 작업이 하나처럼 : 트랜잭션
			
			// 문자열로 읽기
			String num = scan.nextLine();     // 한 줄 읽기

			if("5".equals(num)) {
				// 5. 삭제 및 수정
				// 수정할 데이터
				System.out.println("수정할 부서번호를 입력하시오");
				String deptno = scan.next();
				System.out.println("수정할 부서명을 입력하시오");
				String dname = scan.next();
				System.out.println("수정할 부서위치를 입력하시오");
				String loc = scan.next();
				DeptDTO dto =        // 위에 세가지를 dto에 담기
						new DeptDTO(Integer.parseInt(deptno), dname, loc);
				
				// 삭제할 데이터
				System.out.println("삭제할 부서번호를 입력하시오");
				String deptno2 = scan.next();
				
				// Service 연동
				DeptService service = new DeptServiceImpl();
				int n = service.updateAndDelete(dto, Integer.parseInt(deptno2));
				
			}else if("0".equals(num)) {
				System.out.println("프로그램 종료");
				System.exit(0);    // exit : 프로그램 정상종료 
			}
			
		}//end while
		
	}//end main

}//end class

DeptService.java

package com.service;

import java.util.List;

import com.dto.DeptDTO;
import com.exception.DuplicatedDeptnoException;

// dept 테이블의 데이터를 가공하는 역할 ==> 비즈니스 로직 처리 및 트랜잭션 처리 담당.
public interface DeptService {

	public List<DeptDTO> findAll();

	// 5. 수정 및 삭제 처리 하는 메서드
	// DTO와 deptno2를 받아주는 메서드
	public int updateAndDelete(DeptDTO dto, int deptno);
}

DeptServiceImpl.java

package com.service;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import com.dao.DeptDAO;
import com.dto.DeptDTO;
import com.exception.DuplicatedDeptnoException;

// 오라클 데이터베이스와 연결하기 위해 Connection을 얻는 작업을 하기 위한 클래스
// Connection 얻는 이유? 트랜잭션을 처리하려고
// 얻은 Connection을 실제 데이터베이스와 연동하는 DeptDAO 클래스에 인자로 전달하여 사용된다.
public class DeptServiceImpl implements DeptService {

	String driver = "oracle.jdbc.driver.OracleDriver";
	String url = "jdbc:oracle:thin:@localhost:1521:xe";
	String userid = "SCOTT";
	String passwd = "TIGER";

	public DeptServiceImpl() {
		try {
			Class.forName(driver);    // 드라이버 로딩
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	
	
	// ▣ select 기능하는 메서드
	// Connection 까지만 얻는다. 나머지 작업은 DAO에서 처리
	@Override
	public List<DeptDTO> findAll(){
		
		List<DeptDTO> list = null;   // ★
		Connection con = null;

		// Connection 맺기
	    try{
	       con = DriverManager.getConnection(url, userid, passwd);
	       // DAO 접근
	       DeptDAO dao = new DeptDAO();    // ★
	       list = dao.findAll(con);   // ★
	    }catch(SQLException e){
	    	e.printStackTrace();
	    }finally {                  // close()
			try {
				if(con != null)con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return list;   // ★ 리턴
	}

	
	
	// ▣ insert 기능하는 메서드 
	// Connection 까지만 얻는다. 나머지 작업은 DAO에서 처리
	@Override
	public int insert(DeptDTO dto) throws DuplicatedDeptnoException {
		int n = 0;
		Connection con = null;
		try {
			con = DriverManager.getConnection(url, userid, passwd);
	        // DAO 연동
			DeptDAO dao = new DeptDAO();
			n = dao.insert(con, dto);
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (con != null)
					con.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return n;
	}

	
	
	
	// ▣ update 기능하는 메서드 
	// Connection 까지만 얻는다. 나머지 작업은 DAO에서 처리
	@Override
	public int update(DeptDTO dto) {
		int n = 0;
		Connection con = null;
		try {
			con = DriverManager.getConnection(url, userid, passwd);
			// DAO 연동
			DeptDAO dao = new DeptDAO();
			n = dao.update(con, dto);
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (con != null)
					con.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return n;
	}

	
	
	
	// ▣ DELETE 작업 
	@Override
	public int delete(int deptno) {
		// Connection때문에 try~catch 필요
		// close() 해야되는 것들은 finally에 쓰기 위해 다 바깥에 
		int n = 0;
		Connection con = null;
		try {
			con = DriverManager.getConnection(url, userid, passwd);
			// DAO 연동
			DeptDAO dao = new DeptDAO();
			n = dao.delete(con, deptno);
		}catch(SQLException e) {
			e.printStackTrace();
		}finally {
			try {
				if(con != null)con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return n;
	}

	// ▣ updateAndDelete 작업 (트랜잭션 처리)
	@Override
	public int updateAndDelete(DeptDTO dto, int deptno) {
		
		int n = 0;
		Connection con = null;
		try {
			con = DriverManager.getConnection(url, userid, passwd);
			// DAO 연동
			DeptDAO dao = new DeptDAO();
			
			// ----------------트랜잭션-------------------
			// 여러 개의 작업을 하나의 작업으로 처리할 때 씀
			// 둘 다 성공해서 실제 DB에 반영 => COMMIT
			// 또는
			// 둘 중에 하나라도 실패하면 모두 취소 => ROLLBACK
			
			// AutoCommit을 비활성화
			con.setAutoCommit(false);
			
			// 수정
			n = dao.update(con, dto);
			// 삭제
			n = dao.delete(con, deptno);
			// -------------------------------------------
			
			con.commit();   // 아무 문제가 없어서 커밋한다.
		}catch(SQLException e) {
			//catch에 잡혔다면 둘 중에 하나는 에러가 있다는 거니까 rollback
			try {
				if(con != null)con.rollback();
			} catch (SQLException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			
			e.printStackTrace();
		}finally {
			try {
				if(con != null)con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return n;
	}

	private void rollback() {
		// TODO Auto-generated method stub
		
	}

}

DeptDAO.java

package com.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.dto.DeptDTO;
import com.exception.DuplicatedDeptnoException;

// 실제 데이터베이스 (Oracle의 dept테이블)와 연동하는 클래스
// dept 테이블의 하나의 레코드는 DeptDTO에 저장
// 누적하기 위하여 ArrayList<DeptDTO>를 사용
// 주의할 점 : Connection을 DAO 클래스에서 close()하면 안되고 반드시 Service 클래스에서 close() 시켜야한다.
public class DeptDAO {

	// ▣ select 작업
	public List<DeptDTO> findAll(Connection con){    // DAO는 service에서 Connection을 꼭 전달받는다!
		
		// 다형성 통해 DeptDTO 누적용
		List<DeptDTO> list = new ArrayList<DeptDTO>();
		// 블록 밖에서 쓰기 위해 바깥에서 선언을 해준다.
		PreparedStatement pstmt=null;
	    ResultSet rs = null;
	    try{
	       String sql = "select deptno as no, dname, loc from dept";
	       pstmt = con.prepareStatement(sql); //에러발생시
	       rs = pstmt.executeQuery();
	       // 결과값인 ResultSet에서 데이터를 얻기
	       while(rs.next()) {
	    	  int deptno = rs.getInt("no"); // getInt(1) 가능
	    	  String dname = rs.getString("dname");  // getString(2)
	    	  String loc = rs.getString("loc");  // // getString(3)
	    	  // 생성자 통해 DTO에 저장. 한 번 돌때마다 DTO 생성.
	    	  DeptDTO dto = new DeptDTO(deptno, dname, loc);
	    	  // 누적
	    	  list.add(dto);
	       }
	    }catch(SQLException e){
	    	e.printStackTrace();
	    }finally {
	    	//finally
			try {
				//역순
				// null 오류는 조건문(if)으로 처리해야함
				// rs나 pstmt가 null일 수도 있기 때문에 확인하고 close() 하는 작업
				if(rs != null)rs.close();
				if(pstmt != null)pstmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
//				System.out.println(e.getMessage());     이것도 가능
			}
		}
		
		 return list;
	}//

	
	
	
// ▣ insert 작업 
	public int insert(Connection con, DeptDTO dto ) throws DuplicatedDeptnoException {
		int num = 0;
		PreparedStatement pstmt=null;
	    try{
	    	String sql = "insert into dept ( deptno, dname, loc) "
       		        + " values( ?, ?, ? )";
	       pstmt = con.prepareStatement(sql); //에러발생시
	       pstmt.setInt(1, dto.getDeptno());  // deptno 값은 중복되지 않도록 확인할 것.
           pstmt.setString(2, dto.getDname());
           pstmt.setString(3, dto.getLoc());
           num = pstmt.executeUpdate();
	    }catch(SQLException e){
//	    	e.printStackTrace();
	    	throw new DuplicatedDeptnoException(dto.getDeptno()+" 값이 중복됨");
	    }finally {
			try {
				if(pstmt != null)pstmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return num;
}

	
	
// ▣ UPDATE 작업 	
public int update(Connection con, DeptDTO dto) {
	int num = 0;
	PreparedStatement pstmt=null;
    try{
    	String sql = "update dept set dname=?, loc=? where deptno=?"; 
    	pstmt = con.prepareStatement(sql); //에러발생시
    	pstmt.setInt(3, dto.getDeptno());    // deptno 값은 중복되지 않도록 확인할것.
		pstmt.setString(1, dto.getDname());
		pstmt.setString(2, dto.getLoc());
		num = pstmt.executeUpdate(); 
       
    }catch(SQLException e){
    	e.printStackTrace();
    }finally {
		try {
			if(pstmt != null)pstmt.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	return num;
}

// ▣ DELETE 작업 
	public int delete(Connection con, int deptno) {
		
		// return 위해 밖에서 선언해주기
		int n = 0;
		// finally에 쓰기위해 밖에서 선언해주기
		PreparedStatement pstmt = null;
		try {
			String sql = "delete from dept where deptno=?";
			pstmt = con.prepareStatement(sql);
			pstmt.setInt(1, deptno);    // ?에 값 넣어주기
			n = pstmt.executeUpdate();
		}catch(SQLException e) {
			e.printStackTrace();
		}finally {    // close()작업
			try {
				if(pstmt != null) pstmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return 0;
	}
}

'DB > MyBatis' 카테고리의 다른 글

Mybatis - 조건문  (0) 2023.08.07
Mybatis - Dynamic SQL  (0) 2023.08.07
Mybatis - 환경설정 및 SELECT 예제  (0) 2023.08.04
DAO(Data Access Object) Pattern  (0) 2023.08.02
JDBC  (0) 2023.08.01
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함