이 포스팅에서는 트랜잭션의 개념과 실습예시, 추가로 트랜잭션과 MVC를 분리하는 내용의 예제를 풀이하였다.
1. 트랜잭션
1) 트랜잭션이란?
: 트랜잭션에 대한 개념을 잡기에 가장 도움이 된 블로그의 링크를 첨부하였다.
개념과 필요성, 특징, commit과 rollback의 내용 정도를 파악하는게 좋을 것 같다.
[SQL] Transaction(트랜잭션) (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 |