본문 바로가기
DBMS

[Oracle] 트랜잭션

by amoomar 2022. 1. 22.
반응형

 

 

이 포스팅에서는 트랜잭션의 개념과 실습예시, 추가로 트랜잭션과 MVC를 분리하는 내용의 예제를 풀이하였다.

 


 

 

1. 트랜잭션

 

1) 트랜잭션이란?

: 트랜잭션에 대한 개념을 잡기에 가장 도움이 된 블로그의 링크를 첨부하였다.

개념과 필요성, 특징, commit과 rollback의 내용 정도를 파악하는게 좋을 것 같다.

 

 

[SQL] Transaction(트랜잭션) (tistory.com)

 

[SQL] Transaction(트랜잭션)

* 트랜잭션(Transaction) - 트랜잭션이란 '거래'라는 뜻으로 데이터베이스 내에서 하나의 그룹으로 처리되어야 하는 명령문들을 모아 놓은 논리적인 작업 단위이다. - 데이터베이스 응용 프로그램은

jerryjerryjerry.tistory.com

 

 

 


 

2) 실습예제

 

: 각각 수행 허가와 수행 취소의 기능을 하는 메서드이다. 실습예제를 통해 메서드의 사용예시를 확인할 수 있다.

 

// 포인트 사용을 위한 변수
final String update2="update member set point=point-? where id=?"; // -> "포인트사용"도 추가할예정!
final String check ="selec point from member where id=?";

// [포인트 사용 : 트랜잭션]
public boolean update2(MemberVO vo) {
	conn=JDBCUtill.connect();

	try {
    
		//기본적으로 모든 작업을 각각 하나의 단위로 취급한다
		//"내가 처리할게"라고 이야기 해야하는데, 아래와 같이 명령한다.
		//아래에서 true처리하여 다시 니가 해 라고 명령한다.
		conn.setAutoCommit(false);

		// 일단 point-사용한 포인트 진행
		pstmt = conn.prepareStatement(update2);
		pstmt.setInt(1, vo.getPoint());
		pstmt.setInt(2, vo.getId());
        
		//이제 DB접근해서 진짜로 바꿔!
		int result = pstmt.executeUpdate();

		if(result==0) { // 해당 id가 0개라면,
			return false; // 포인트 사용대상 없음
		}
        
		// 포인트의 사용대상이 있다면! 이쪽으로 진입	
        pstmt=conn.prepareStatement(check); //근데 포인트가 있나?
		pstmt.setInt(1, vo.getId());//내 포인트 얼마지?
		ResultSet rs=pstmt.executeQuery(); //DB에 직접 접근해서 조회

		//다음rs의 객체 참조한다
		//boolean을 반환하므로값이 있으면 T / 없으면 F
		//즉, DB에 point 있으면  T / 없으면 F
		rs.next(); 
		if(rs.getInt(1) < 0) {
			// point-사용포인트가 0보다 작다면
			conn.rollback();//수행취소
			System.out.println("DAO로그 : update2 실행취소");
		} else {//그게 아니라면//수행허가
			conn.commit(); 
			System.out.println("DAO로그 : update2 실행완료");
		}
        
		rs.close();
		conn.setAutoCommit(true); //"이제 니가해"
        
	} catch (SQLException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
		return false;
	}finally {
		JDBCUtill.disconnect(pstmt, conn);
	}
	return true; //포인트 사용 성공
}

 

 

주석을 통해 해당 코드를 해석할 수 있으며, 중점적으로 확인해야 하는 내용은 conn.setAutoCommit( )의 사용과 conn.commit( ) , conn.rollback( )의 사용구간이다!

 

 

* main *

System.out.print("번호입력 : ");
int id=sc.nextInt();
System.out.println("포인트입력 : ");
int point = sc.nextInt();

MemberVO vo = new MemberVO();
vo.setId(id);
vo.setPoint(point);

if(!dao.update(vo)) {//리턴 없으면!
	System.out.println("app로그 : 사용실패");
	continue; //main화면으로 복귀
}

// 많은 경우 수정이 완료되기에 하단배치
System.out.println("app로그 :사용완료");

 

 

 


 

 

2. 예제

 

1) 설계

: 실습을 위한 대략적 설계

 

 


 

2) tableDB

--내가 만든 데이터 확인--
select * from user_tables;

--DB생성--
create table product(
   pid int primary key,
   pname varchar(20) not null,
   price int,
   cnt int default 0
);

--초기데이터 등록--
insert into product (pid,pname,price) values(1,'운동화',59000);
insert into product (pid,pname,price,cnt) values(2,'모자',10000,2);

--전체 데이터 조회--
select * from product;

 


 

3) view

package view;

import java.util.ArrayList;
import java.util.Scanner;

import model.ProductVO;

public class ProductView {
	
	Scanner sc = new Scanner(System.in);
	public int act; //사용자의 입력
	String cMainMsg = "1. 목록조회 \n2. 상품선택 \n3. 프로그램종료";
	String aMainMsg = "1. 상품재고추가 \n2. 상품추가\n3. 관리자모드 종료";
	
	// 사용자의 main화면
	public void clientView() {
		System.out.println(cMainMsg);
		act=sc.nextInt();
	}
	
	// 관리자의 main화면
	public void adminView() {
		System.out.println(aMainMsg);
		act=sc.nextInt();
	}
	
	// 목록조회화면
	public void list(ArrayList<ProductVO> datas) {
		System.out.println("===상품목록페이지===");
		for(ProductVO vo:datas) {
			System.out.println(vo);
		}
		System.out.println("================");
	}
	
	// 상품선택 및 구매화면
	public ProductVO choice () {
		ProductVO vo = new ProductVO();
		System.out.println("상품 번호 :");
		int pid=sc.nextInt();
		System.out.println("구매 개수 : ");
		int cnt=sc.nextInt();
		vo.setPid(pid);
		vo.setCnt(cnt);
		return vo;
	}
	public void choice_A () {
		System.out.println("===구매 완료 페이지===");
	}
	public void choice_B () {
		System.out.println("===구매 실패 페이지===");
	}

	// 재고추가 화면
	public ProductVO addCnt() {
		ProductVO vo = new ProductVO();
		System.out.println("상품번호 : ");
		int pid = sc.nextInt();
		System.out.println("추가개수 : ");
		int cnt = sc.nextInt();
		vo.setPid(pid);
		vo.setCnt(cnt);
		return vo;
	}
	public void addCnt_A() {
		System.out.println("===재고추가 완료 페이지===");
	}
	public void addCnt_B() {
		System.out.println("===재고추가 실패 페이지===");
	}
	
	// 상품추가 화면
	public ProductVO addProduct() {
		ProductVO vo = new ProductVO();
		System.out.println("등록할 상품명 : ");
		String pname = sc.next();
		System.out.println("설정할 가격 : ");
		int price = sc.nextInt();
		System.out.println("설정할 재고 : ");
		int cnt = sc.nextInt();
		vo.setPname(pname);
		vo.setPrice(price);
		vo.setCnt(cnt);
		
		return vo;
	}
	public void addProduct_A() {
		System.out.println("===상품등록 완료 페이지===");
	}
	public void addPoduct_B() {
		System.out.println("===상품등록 실패 페이지===");
	}
	
	// 종료시 출력문구
	public void endPrint() {
		System.out.println("프로그램이 종료됩니다.");
	}
}

 


 

4) model

: VO, DAO, Utill Class로 분류하였다. 이전 포스팅들을 확인하고 내용을 파악한다면 주석이 없어도 충분히 해석이 가능하여 생략하였다.

 

① VO

package model;

public class ProductVO {
	private int pid;
	private String pname;
	private int price;
	private int cnt;
	
	public int getCnt() {
		return cnt;
	}
	public void setCnt(int cnt) {
		this.cnt = cnt;
	}
	public int getPid() {
		return pid;
	}
	public void setPid(int pid) {
		this.pid = pid;
	}
	public String getPname() {
		return pname;
	}
	public void setPname(String pname) {
		this.pname = pname;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	@Override
	public String toString() {
		return "[" + pid + "] " + pname + " | " + price + " | " + cnt;
	}

}

 

② DAO

package model;

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

// 비즈니스 메서드를 알아야 작성가능(설계가 우선!)
public class ProductDAO {

	final String insert="insert into product (pid,pname,price,cnt) values((select nvl(max(pid),0)+1 from product),?,?,?)";
	final String selectAll="select * from product";
	final String selectOne="select * from product where pid=?";
	final String updateClient="update product set cnt=cnt-? where pid=?";
	final String updateAdmin="update product set cnt=cnt+? where pid=?";

	Connection conn;
	PreparedStatement pstmt;

	public boolean insert (ProductVO vo) {
		conn = JDBCUtill.connect();
		
		try {
			pstmt = conn.prepareStatement(insert);
			pstmt.setString(1, vo.getPname());
			pstmt.setInt(2, vo.getPrice());
			pstmt.setInt(3, vo.getCnt());
			pstmt.executeUpdate();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			System.out.println("dao로그 : 등록실패");
			e.printStackTrace();
			return false;
		} finally {
			JDBCUtill.disconnect(pstmt, conn);
		}
		System.out.println("dao로그 : 등록성공");
		return true;
	}
	
	public ArrayList<ProductVO> selectAll(ProductVO vo){
		conn = JDBCUtill.connect();
		ArrayList<ProductVO> datas = new ArrayList<ProductVO>(); 

		try {
			pstmt = conn.prepareStatement(selectAll);
			ResultSet rs = pstmt.executeQuery();
			while(rs.next()) {
				ProductVO data = new ProductVO();
				data.setPid(rs.getInt("pid"));
				data.setPrice(rs.getInt("price"));
				data.setPname(rs.getString("pname"));
				data.setCnt(rs.getInt("cnt"));
				datas.add(data);
			}

			rs.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			JDBCUtill.disconnect(pstmt, conn);
		}
		return datas;
	}

	public ProductVO selectOne(ProductVO vo) {
		conn = JDBCUtill.connect();
		ProductVO data = null;
		try {
			pstmt = conn.prepareStatement(selectOne);
			pstmt.setInt(1, vo.getPid());
			ResultSet rs = pstmt.executeQuery();
			if(rs.next()) {
				data = new ProductVO();
				data.setPname(rs.getString(1));
				data.setPrice(rs.getInt(2));
			}
			rs.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			JDBCUtill.disconnect(pstmt, conn);
		}
		return data;
	}

	public boolean updateClient(ProductVO vo) {

		conn = JDBCUtill.connect();
		try {
			conn.setAutoCommit(false);
			
			pstmt = conn.prepareStatement(updateClient);
			pstmt.setInt(1, vo.getCnt());
			pstmt.setInt(2, vo.getPid());
			int res = pstmt.executeUpdate();

			if(res==0) {
				System.out.println("dao로그 : 사용자수정실패");
				return false;
			}
			
			pstmt = conn.prepareStatement(selectOne);
			pstmt.setInt(1, vo.getPid());
			ResultSet rs = pstmt.executeQuery();
			rs.next();
			
			if(rs.getInt("cnt") < 0) { //cnt-? < 0일때
				System.out.println("dao로그 : 사용자구매실패");
				conn.rollback();
			} else {
				System.out.println("dao로그 : 사용자구매성공");
				conn.commit();
			}
				
			rs.close();
			conn.setAutoCommit(true);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println("dao로그 : 사용자수정실패");
			return false;
		} finally {
			JDBCUtill.disconnect(pstmt, conn);
		}
		System.out.println("dao로그 : 사용자수정완료");
		return true;
	}

	public boolean updateAdmin(ProductVO vo) {
		conn = JDBCUtill.connect();
		try {
			pstmt = conn.prepareStatement(updateAdmin);
			pstmt.setInt(1, vo.getCnt());
			pstmt.setInt(2, vo.getPid());
			int res = pstmt.executeUpdate();

			if(res==0) {
				System.out.println("dao로그 : 관리자수정실패");
				return false;
			}

		} catch (SQLException e) {
			// TODO Auto-generated catch block
			System.out.println("dao로그 : 관리자수정실패");
			e.printStackTrace();
			return false;
		} finally {
			JDBCUtill.disconnect(pstmt, conn);
		}
		System.out.println("dao로그 : 관리자수정완료");
		return true;
	}

}

 

③ Utill Class

package model;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

// 공통되는 로직을 따로 관리하기 위한 클래스
public class JDBCUtill {
	//static변수를 사용하여 자원으로 선언
	static final String driverName="oracle.jdbc.driver.OracleDriver";
	static final String url="jdbc:oracle:thin:@localhost:1521:xe";
	static final String user="ham";
	static final String passwd="1234";
	
	// DB에 연결 == connection 확보하기에 ouput으로 설정
	public static Connection connect() {
		Connection conn = null;
		try {
			Class.forName(driverName);
			conn=DriverManager.getConnection(url, user, passwd);
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return conn;
	}
	
	// conn, stmt 닫는 메서드
	public static void disconnect(PreparedStatement pstmt, Connection conn) {
		try {
			pstmt.close();
			conn.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

 

 


 

5) controller

package controller;

import java.util.ArrayList;

import model.ProductDAO;
import model.ProductVO;
import view.ProductView;

public class ProductController {
	ProductView view;
	ProductDAO dao;

	// 생성자
	public ProductController(){
		view = new ProductView();
		dao = new ProductDAO();
	}

	// 앱 실행 메소드
	public void startApp() {
		
		while(true) {
			
			view.clientView(); // 사용자모드
			// 1. 목록조회
			if(view.act==1) {
				ProductVO vo = new ProductVO();
				ArrayList<ProductVO> datas = dao.selectAll(vo);
				view.list(datas);
			// 2. 상품 선택 및 구매
			}else if(view.act==2) {
				ProductVO vo = view.choice();

				if(dao.updateClient(vo)) {
					view.choice_A();
				}else {
					view.choice_B();
				}
			// 3. 종료
			}else if(view.act==3) {
				view.endPrint();
				break;
			// 1,2,3이 아니라면 관리자모드로 변경	
			}else {
				while(true) {
					view.adminView(); //관리자모드
					// 재고추가
					if(view.act==1) {
						// 초기화는 이미 view에서 완료했으므로!
						ProductVO vo = view.addCnt();
						if(dao.updateAdmin(vo)) {
							view.addCnt_A();
						}else {
							view.addCnt_B();
						}
					// 상품추가
					}else if(view.act==2) {
						ProductVO vo = view.addProduct();
						if(dao.insert(vo)) {
							view.addProduct_A();
						}else {
							view.addPoduct_B();
						}
					// 관리자모드 종료	
					}else if(view.act==3) {
						view.endPrint();
						break;
					}
				}
			}
		}
	}
	
}

 


 

6) 결과

package client;

import controller.ProductController;

public class Client {

	public static void main(String[] args) {
		
		ProductController ctrl = new ProductController();
		ctrl.startApp();
	}
}

 

[출력 화면 : -> 입력]

1. 목록조회 
2. 상품선택 
3. 프로그램종료
1
===상품목록페이지===
[1] 운동화 | 59000 | 5
[2] 모자 | 10000 | 2
[3] 양말 | 3000 | 6
================
1. 목록조회 
2. 상품선택 
3. 프로그램종료
2
상품 번호 :
3
구매 개수 : 
4
dao로그 : 사용자구매성공
dao로그 : 사용자수정완료
===구매 완료 페이지===
1. 목록조회 
2. 상품선택 
3. 프로그램종료
1
===상품목록페이지===
[1] 운동화 | 59000 | 5
[2] 모자 | 10000 | 2
[3] 양말 | 3000 | 2
================
1. 목록조회 
2. 상품선택 
3. 프로그램종료
4
1. 상품재고추가 
2. 상품추가
3. 관리자모드 종료
1
상품번호 : 
1
추가개수 : 
100
dao로그 : 관리자수정완료
===재고추가 완료 페이지===
1. 상품재고추가 
2. 상품추가
3. 관리자모드 종료
2
등록할 상품명 : 
장갑
설정할 가격 : 
5000
설정할 재고 : 
3
dao로그 : 등록성공
===상품등록 완료 페이지===
1. 상품재고추가 
2. 상품추가
3. 관리자모드 종료
3
프로그램이 종료됩니다.
1. 목록조회 
2. 상품선택 
3. 프로그램종료
1
===상품목록페이지===
[1] 운동화 | 59000 | 105
[2] 모자 | 10000 | 2
[3] 양말 | 3000 | 2
[4] 장갑 | 5000 | 3
================
1. 목록조회 
2. 상품선택 
3. 프로그램종료
3
프로그램이 종료됩니다.

 

 

* DB의 값 변화 전 / 후 *

: 구매한 양말의 개수, 추가한 운동화의 재고량, 추가한 상품 장갑에 대한 내용이 잘 반영된 것을 확인할 수 있다.

 

 


 

반응형

'DBMS' 카테고리의 다른 글

[Oracle] 웹크롤링_샘플데이터처리  (0) 2022.01.25
[Oracle] 웹크롤링_기초  (0) 2022.01.24
[Oracle] pstmt  (0) 2022.01.21
[Oracle] DB접근&MVC분리  (0) 2022.01.20
[Oracle] 개요 & SQL문법  (0) 2022.01.19