기록공간

DAO, DTO 그리고 Process 본문

DataBase/JDBC

DAO, DTO 그리고 Process

입코딩 2020. 11. 7. 18:55
반응형

DAO

DAO란 Data Acess Object의 약자로 데이터베이스에 실질적으로 접근하는 객체를 말한다. 

DAO를 사용하는 이유는 효율적인 Connection 관리와 보안성 때문이다.

 

DAO는 저수준의 로직과 고급 비즈니스 로직을 분리하고, 로직 내 CRUD 메커니즘을 숨기기 위해 사용한다.

(CRUD : Create Read Update Delete)

 

즉 쉽게 말해 DAO는 DB를 사용해 데이터를 조작하는 기능을 전담하도록 만든 오브젝트를 뜻한다. 

 

사용자는 자신이 데이터베이스로 부터 수행해야 할 작업을 DAO에게 던지고 DAO는 이를 수행한 후 그에 따른 결과 값을 반환 값으로 제공한다.

 

DTO

DTO는 Data Transfer Object의 약자로 데이터 교환을 위한 오브젝트를 말한다.

 

DTO는 VO(Value Object)로 바꿔 말할 수 있는데 VO와의 차이점은 DTO는 읽고 쓰는것(read and write)이 가능하지만, VO는 읽기 밖에 (read only) 할 수 없다.

 

일반적으로 DTO는 로직을 갖고 있지 않다. 그저 순수한 데이터 객체이며 속성과 그 속성에 접근하기 위한 getter, setter 메소드만 가진 클래스를 말한다.

 

Process

Process는 비즈니스 로직이 들어가는 부분이다. 사용자로 부터 요청을 받으면 적절한 서비스를 전달하고, 전달 받은 서비스는 비즈니스 로직을 처리한다. DAO로 데이터베이스를 접근하고, DTO로 데이터를 전달받은 다음, 적절한 처리를 해 반환한다.

 

예를 들어, 어느 학생 관리 프로그램에 학생 정보를 입력받아 데이터베이스에 추가하는 기능이 있다고 하자. 이 기능은 학생 정보를 사용자의 입력을 통해 DTO 객체로 해당 데이터를 set하고, DAO를 통해 데이터베이스 테이블에 INSERT하는 기능을 담당한다. 이 전반적인 기능은 Process 객체에서 한 메서드가 담당하게 된다. 

 

DAO, DTO, Process 예제

학생 성적 관리 프로그램을 만든다고 가정하자. 프로그램은 다음과 같은 로직으로 돌아간다.

 


○ 성적 처리 -> 데이터베이스 연동(데이터베이스 연결 및 액션 처리)
                ScoreDTO 클래스 활용(속성만 존재하는 클래스. 사용자 정의 자료형. getter/setter 구성)
                ScoreDAO 클래스 활용(데이터베이스 액션 처리 전용 클래스 구성)
                
   여러 명의 이름, 국어점수, 영어점수, 수학점수를 입력받아
   총점, 평균을 연산하여 출력하는 프로그램을 구현한다.
   출력 시 번호(이름, 총점 등) 오름차순 정렬하여 출력한다.
   
   ※ 서브 메뉴 구성 -> Process 클래스 활용
   
실행 예)

====[성적처리]====
1. 성적 입력
2. 성적 전체 출력
3. 이름 검색 출력
4. 성적 수정
5. 성적 삭제
==================
>> 선택(1~5, -1종료) : 1

4번 학생 성적 입력(이름 국어 영어 수학) : 강정우 50 60 70
5번 학생 성적 입력(이름 국어 영어 수학) : 권소윤 80 80 80
6번 학생 성적 입력(이름 국어 영어 수학) : .

====[성적처리]====
1. 성적 입력
2. 성적 전체 출력
3. 이름 검색 출력
4. 성적 수정
5. 성적 삭제
==================
>> 선택(1~5, -1종료) : 2

전체 인원 : 5명
번호      이름  국어  영어  수학  총점  평균  석차
1
2
3                         ...
4


====[성적처리]====
1. 성적 입력
2. 성적 전체 출력
3. 이름 검색 출력
4. 성적 수정
5. 성적 삭제
==================
>> 선택(1~5, -1종료) : -1

프로그램 종료되었습니다.

우선 아래와 같은 테이블을 먼저 생성해야 한다.

 

--○ 테이블 생성
CREATE
 TABLE TBL_SCORE
( SID        NUMBER         
, NAME    VARCHAR2(30)
, KOR       NUMBER(3)
, ENG       NUMBER(3)
, MAT       NUMBER(3)
);
--==>> Table TBL_SCORE이(가) 생성되었습니다.

--○ 제약조건 추가 (기본키) 
ALTER TABLE TBL_SCORE
ADD CONSTRAINT SCORE_SID_PK PRIMARY KEY(SID);
--==>> Table TBL_SCORE이(가) 변경되었습니다. 

--○ 제약조건 추가 (체크)
ALTER TABLE TBL_SCORE
ADD ( CONSTRAINT SCORE_KOR_CK CHECK (KOR BETWEEN 0 AND 100)
       , CONSTRAINT SCORE_ENG_CK CHECK (ENG BETWEEN 0 AND 100)
       , CONSTRAINT SCORE_MAT_CK CHECK (MAT BETWEEN 0 AND 100) );
--==>> Table TBL_SCORE이(가) 변경되었습니다. 

그리고 프로그램에서 DAO가 담당할 기능들을 SQL 쿼리문을 통해 미리 작성해 놓는다.

 

--○ 시퀀스 생성
CREATE SEQUENCE SCORESEQ
NOCACHE;

-- 1. 데이터 입력 쿼리문 구성
INSERT INTO TBL_SCORE(SID, NAME, KOR, ENG, MAT) VALUES(SCORESEQ.NEXTVAL, '이름', 0, 0, 0)


-- 2. 출력 쿼리문 구성
SELECT SID, NAME, KOR, ENG, MAT, (KOR+ENG+MAT) AS TOT, (KOR+ENG+MAT)/3 AS AVG, RANK() OVER(ORDER BY (KOR+ENG+MAT) DESC) AS RANK FROM TBL_SCORE ORDER BY SID ASC
;

-- 3. 인원수 조회 쿼리문 구성
SELECT COUNT(*) AS COUNT FROM TBL_SCORE
;

-- 4. 이름 검색 쿼리문 구성
SELECT SID, NAME, KOR, ENG, TOT, AVG, RANK FROM ( SELECT SID, NAME, KOR, ENG, MAT (KOR+ENG+MAT) AS TOT, (KOR+ENG+MAT)/3 AS AVG, RANK() OVER (ORDER BY (KOR+ENG+MAT) DESC) AS RANK FROM TBL_SCORE) WHERE NAME LIKE '%이름%'
;

-- 5. 번호 검색 쿼리문 구성
SELECT SID, NAME, KOR, ENG, TOT, AVG, RANK FROM ( SELECT SID, NAME, KOR, ENG, MAT (KOR+ENG+MAT) AS TOT, (KOR+ENG+MAT)/3 AS AVG, RANK() OVER (ORDER BY (KOR+ENG+MAT) DESCAS RANK FROM TBL_SCORE) WHERE SID = 1
;-- 6. 데이터 수정 쿼리문 구성
UPDATE TBL_SCORE SET NAME = '수정이름', KOR = 100, ENG = 100, MAT = 100 WHERE SID = 1
;

-- 7. 데이터 삭제 쿼리문 구성
DELETE FROM TBL_SCORE WHERE SID = 1
;

 

JDBC 클래스

package com.util;

import java.sql.Connection;
import java.sql.DriverManager;

public class DBConn
{
	private static Connection dbConn;
	
	public static Connection getConnection()
	{
		if(dbConn == null)
		{
			try
			{
				String url = "jdbc:oracle:thin:@211.238.142.159:1521:xe";
				String user = "scott";
				String pwd = "tiger";
				
				Class.forName("oracle.jdbc.driver.OracleDriver");
				dbConn = DriverManager.getConnection(url, user, pwd);
				
			} catch (Exception e)
			{
				System.out.println(e.toString());
			}
		}
		
		return dbConn;
	}
	
	public static Connection getConnection(String url, String user, String pwd)
	{
		if(dbConn == null)
		{
			try
			{				
				Class.forName("oracle.jdbc.driver.OracleDriver");
				dbConn = DriverManager.getConnection(url, user, pwd);
				
			} catch (Exception e)
			{
				System.out.println(e.toString());
			}
		}
		
		return dbConn;
	}
	
	public static void close()
	{
		if (dbConn != null)
		{
			try
			{
				if (!dbConn.isClosed())
					dbConn.close();

			} catch (Exception e)
			{
				System.out.println(e.toString());
			}
		}
		
		dbConn = null;
	}
}

 

DTO 클래스

package com.test;

public class ScoreDTO
{
	// 주요 속성 구성
	private String sid, name; 		// 식별번호, 이름
	private int kor, eng, mat; 		// 국어점수, 영어점수, 수학점수
	private int tot, rank;			// 총점, 석차
	private double avg;				// 평균
	public String getSid()
	{
		return sid;
	}
	public void setSid(String sid)
	{
		this.sid = sid;
	}
	public String getName()
	{
		return name;
	}
	public void setName(String name)
	{
		this.name = name;
	}
	public int getKor()
	{
		return kor;
	}
	public void setKor(int kor)
	{
		this.kor = kor;
	}
	public int getEng()
	{
		return eng;
	}
	public void setEng(int eng)
	{
		this.eng = eng;
	}
	public int getMat()
	{
		return mat;
	}
	public void setMat(int mat)
	{
		this.mat = mat;
	}
	public int getTot()
	{
		return tot;
	}
	public void setTot(int tot)
	{
		this.tot = tot;
	}
	public int getRank()
	{
		return rank;
	}
	public void setRank(int rank)
	{
		this.rank = rank;
	}
	public double getAvg()
	{
		return avg;
	}
	public void setAvg(double avg)
	{
		this.avg = avg;
	}
}

 

DAO 클래스

package com.test;

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

import com.util.DBConn;

public class ScoreDAO
{
	// 주요 속성 구성
	private Connection conn;
	
	// 주요 기능 구성 
	// - 데이터베이스 연결 담당 메소드
	public Connection connection()
	{
		conn = DBConn.getConnection();
		return conn;
	}
	
	// - 데이터 입력 담당 메소드
	public int add(ScoreDTO dto) throws SQLException
	{
		int result = 0;
		
		Statement stmt = conn.createStatement();
		
		String sql = String.format("INSERT INTO TBL_SCORE(SID, NAME, KOR, ENG, MAT) "
				                + " VALUES(SCORESEQ.NEXTVAL, '%s', %d, %d, %d)"
				                  , dto.getName(), dto.getKor(), dto.getEng(), dto.getMat());
		
		result = stmt.executeUpdate(sql);
		
		stmt.close();
		
		return result;
	}
	
	// - 전체 리스트 출력 담당 메소드
	public ArrayList<ScoreDTO> lists() throws SQLException
	{
		ArrayList<ScoreDTO> result = new ArrayList<ScoreDTO>();
		
		Statement stmt = conn.createStatement();
		
		String sql = "SELECT SID, NAME, KOR, ENG, MAT"
				   + ", (KOR+ENG+MAT) AS TOT"
				   + ", (KOR+ENG+MAT)/3 AS AVG"
				   + ", RANK() OVER(ORDER BY (KOR+ENG+MAT) DESC) AS RANK"
				   + " FROM TBL_SCORE"
				   + " ORDER BY SID ASC";
		
		ResultSet rs = stmt.executeQuery(sql);
		
		while(rs.next())
		{
			ScoreDTO dto = new ScoreDTO();
			
			dto.setSid(rs.getString("SID"));
			dto.setName(rs.getString("NAME")); 
			dto.setKor(rs.getInt("KOR")); 
			dto.setEng(rs.getInt("ENG"));
			dto.setMat(rs.getInt("MAT"));
			dto.setTot(rs.getInt("TOT"));
			dto.setAvg(rs.getDouble("AVG"));
			dto.setRank(rs.getInt("RANK"));
			
			result.add(dto);
		}
		
		rs.close();
		stmt.close();	
		
		return result;
	}
	
	// - 인원수 확인(출력) 담당 메소드
	public int count() throws SQLException
	{
		int result = 0;
		
		Statement stmt = conn.createStatement();
		
		String sql = "SELECT COUNT(*) AS COUNT FROM TBL_SCORE";
		
		ResultSet rs = stmt.executeQuery(sql);
		
		while(rs.next())
		{
			result = rs.getInt("COUNT");
		}
		
		rs.close();
		stmt.close();		
		
		return result;
	}

	// - 이름 검색 담당 메소드
	public ArrayList<ScoreDTO> lists(String name) throws SQLException
	{
		ArrayList<ScoreDTO> result = new ArrayList<ScoreDTO>();

		// 작업 객체 구성
		Statement stmt = conn.createStatement();

		// 쿼리문 준비
		String sql = String.format("SELECT SID, NAME, KOR, ENG, MAT, TOT, AVG, RANK"
				+ " FROM(SELECT SID, NAME, KOR, ENG, MAT" 
				+ ", (KOR+ENG+MAT) AS TOT" 
				+ ", (KOR+ENG+MAT)/3 AS AVG"
				+ ", RANK() OVER(ORDER BY (KOR+ENG+MAT) DESC) AS RANK" 
				+ " FROM TBL_SCORE)"
				+ " WHERE NAME LIKE '%%%s%%'", name);

		// 쿼리문 실행
		ResultSet rs = stmt.executeQuery(sql);

		// ResultSet 처리
		while (rs.next())
		{
			ScoreDTO dto = new ScoreDTO();

			dto.setSid(rs.getString("SID"));
			dto.setName(rs.getString("NAME"));
			dto.setKor(rs.getInt("KOR"));
			dto.setEng(rs.getInt("ENG"));
			dto.setMat(rs.getInt("MAT"));
			dto.setTot(rs.getInt("TOT"));
			dto.setAvg(rs.getDouble("AVG"));
			dto.setRank(rs.getInt("RANK"));

			result.add(dto);
		}
		rs.close();
		stmt.close();

		return result;
	}
	
	// - 번호 검색 담당 메소드
	public ArrayList<ScoreDTO> lists(int sid) throws SQLException
	{
		ArrayList<ScoreDTO> result = new ArrayList<ScoreDTO>();
		
		Statement stmt = conn.createStatement();
		
		String sql = String.format("SELECT SID, NAME, KOR, ENG, MAT, TOT, AVG, RANK"
				   + " FROM"
				   + " ( SELECT SID, NAME, KOR, ENG, MAT"
				   + ", (KOR+ENG+MAT) AS TOT"
				   + ", (KOR+ENG+MAT)/3 AS AVG"
				   + ", RANK() OVER(ORDER BY (KOR+ENG+MAT) DESC) AS RANK"
				   + " FROM TBL_SCORE"
				   + ") WHERE SID = '%d'", sid);
		
		ResultSet rs = stmt.executeQuery(sql);
		
		while(rs.next())
		{
			ScoreDTO dto = new ScoreDTO();
			
			dto.setSid(rs.getString("SID"));
			dto.setName(rs.getString("NAME")); 
			dto.setKor(rs.getInt("KOR")); 
			dto.setEng(rs.getInt("ENG"));
			dto.setMat(rs.getInt("MAT"));
			dto.setTot(rs.getInt("TOT"));
			dto.setAvg(rs.getDouble("AVG"));
			dto.setRank(rs.getInt("RANK"));
			
			result.add(dto);
		}
		
		rs.close();
		stmt.close();
		
		return result;
	}
	
	//- 데이터 수정 담당 메소드
	public int modify(ScoreDTO dto) throws SQLException
	{
		int result = 0;
		
		Statement stmt = conn.createStatement();
		
		String sql = String.format("UPDATE TBL_SCORE SET"
				+ " NAME='%s', KOR=%d, ENG=%d, MAT=%d"
				+ " WHERE SID = %s"
				, dto.getName(), dto.getKor(), dto.getEng(), dto.getMat()
				, dto.getSid());
		
		result = stmt.executeUpdate(sql);
		stmt.close();
		return result;
	}
	
	
	// - 데이터 삭제 담당 메소드
	public int remove(int sid) throws SQLException
	{
		int result = 0;
		
	    Statement stmt = conn.createStatement();
		
		String sql = String.format("DELETE FROM TBL_SCORE"
				+ " WHERE SID = %d"
				, sid);
		
		result = stmt.executeUpdate(sql);
		stmt.close();
		return result;
	}
	
	public void close()
	{
		DBConn.close();
	}
}

 

Process 클래스

package com.test;

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

public class Process
{
	// 주요 속성 구성
	private ScoreDAO dao;
	private Scanner sc;

	// 생성자 정의 -> 사용자 정의 생성자
	public Process()
	{
		dao = new ScoreDAO();
	}

	// 주요 기능 구성
	// - 성적 입력
	public void sungjukInsert()
	{
		try
		{
			dao.connection();

			int count = dao.count();
			sc = new Scanner(System.in);

			do
			{
				System.out.println();
				System.out.printf("%d번 학생 성적 입력(이름 국어 영어 수학) : ", (++count));
				String name = sc.next();
				if (name.equals("."))
					break;
				int kor = sc.nextInt();
				int eng = sc.nextInt();
				int mat = sc.nextInt();

				ScoreDTO dto = new ScoreDTO();
				dto.setName(name);
				dto.setKor(kor);
				dto.setEng(eng);
				dto.setMat(mat);

				int result = dao.add(dto);
				if (result > 0)
					System.out.println(">> 성적 입력이 완료되었습니다.");
			} while (true);

			dao.close();

		} catch (Exception e)
		{
			System.out.println(e.toString());
		}
	}

	// - 성적 전체 출력
	public void sungjukSelectAll()
	{
		try
		{
			dao.connection();

			System.out.printf("전체인원 %d명\n", dao.count());
			System.out.println("번호    이름    국어  영어  수학  총점    평균  석차");
			for (ScoreDTO obj : dao.lists())
			{
				System.out.printf("%s      %3s    %3d   %3d   %3d   %3d   %5.1f   %3d\n", obj.getSid(), obj.getName(),
						obj.getKor(), obj.getEng(), obj.getMat(), obj.getTot(), obj.getAvg(), obj.getRank());
			}

			dao.close();

		} catch (Exception e)
		{
			System.out.println(e.toString());
		}
	}

	// - 이름 검색 출력
	public void sungjukSearchName()
	{
		try
		{
			sc = new Scanner(System.in);

			System.out.print("검색할 이름을 입력하세요 : ");
			String name = sc.next();

			// -- 필요한 경우 이 과정에서 프로그래밍적으로 검증(검사) 수행

			// -- DB 연결
			dao.connection();

			// lists() 메소드 호출 -> 매개변수로 검색할 이름 넘겨주기
			ArrayList<ScoreDTO> arr = dao.lists(name);
			System.out.printf("검색된 인원 %d명\n", arr.size());
			System.out.println("번호    이름    국어  영어  수학  총점    평균  석차");
			for (ScoreDTO obj : arr)
			{
				System.out.printf("%s      %3s    %3d   %3d   %3d   %3d   %5.1f   %3d\n", obj.getSid(), obj.getName(),
						obj.getKor(), obj.getEng(), obj.getMat(), obj.getTot(), obj.getAvg(), obj.getRank());
			}

			dao.close();

		} catch (Exception e)
		{
			System.out.println(e.toString());
		}
	}

	// - 성적 수정
	public void sungjukUpdate()
	{
		// 코드가 많이 달라서 쎔 코드 작성
		try
		{
			// 수정할 학생의 번호 입력
			sc = new Scanner(System.in);
			System.out.print("수정할 학생의 번호를 입력하세요 : ");
			int sid = sc.nextInt();
			
			// -- 입력받은 번호로 체크(검증)해야 할 로직 적용 및 삽입 가능
			
			dao.connection();
			
			ArrayList<ScoreDTO> arrayList = dao.lists(sid);
			if(arrayList.size() > 0)
			{
				System.out.println("번호    이름    국어  영어  수학  총점    평균  석차");
				for (ScoreDTO obj : arrayList)
				{
					System.out.printf("%s      %3s    %3d   %3d   %3d   %3d   %5.1f   %3d\n"
							, obj.getSid(), obj.getName()
							,obj.getKor(), obj.getEng(), obj.getMat()
							, obj.getTot(), obj.getAvg(), obj.getRank());
				}
				
				System.out.print("수정 데이터 입력(이름 국어 영어 수학) : ");
				String name = sc.next();
				int kor = sc.nextInt();
				int eng = sc.nextInt();
				int mat = sc.nextInt();
				
				ScoreDTO dto = new ScoreDTO();
				dto.setName(name);
				dto.setKor(kor);
				dto.setEng(eng);
				dto.setMat(mat);
				dto.setSid(String.valueOf(sid));	// check~!!!
				
				int result = dao.modify(dto);

				if(result > 0)
					System.out.println(">> 수정이 완료되었습니다.");
				
			}
			else
			{
				System.out.println("수정 대상이 존재하지 않습니다.");
			}
			
			dao.close();
			
		} catch (Exception e)
		{
			// TODO: handle exception
			System.out.println(e.toString());
		}
		
	}

	// - 성적 삭제
	public void sungjukDelete()
	{
		try
		{
			
			sc = new Scanner(System.in);

			System.out.println();
			System.out.print(">> 삭제할 번호 입력 : ");
			int sid = sc.nextInt();

			//-- 필요한 유효성 검사 수행
			
			// 데이터베이스 연결
			dao.connection();

			// dao 의 list() 메소드 호출 -> sid 를 매개변수로 넘기기
			ArrayList<ScoreDTO> arrayList = dao.lists(sid);
			if(arrayList.size() > 0)
			{
				System.out.println("번호    이름    국어  영어  수학  총점    평균  석차");
				for (ScoreDTO obj : arrayList)
				{
					System.out.printf("%s      %3s    %3d   %3d   %3d   %3d   %5.1f   %3d\n"
							, obj.getSid(), obj.getName()
							,obj.getKor(), obj.getEng(), obj.getMat()
							, obj.getTot(), obj.getAvg(), obj.getRank());
				}
				
				System.out.print("정말 삭제하시겠습니까?(Y/N) : ");
				String response = sc.next(); 		// Y or y or N or n or etc...
				
				if(response.contentEquals("Y") || response.equals("y"))
				{
					int result = dao.remove(sid);
					if(result > 0)
						System.out.println(">> 삭제가 완료되었습니다.");
				}
				else
				{
					System.out.println(">> 취소되었습니다.");
				}
			}
			else
			{
				System.out.println(">> 삭제 대상이 존재하지 않습니다.");
			}
			
			dao.close();

		} catch (Exception e)
		{
			System.out.println(e.toString());
		}
	}

}

 

ScoreMain 클래스

package com.test;

import java.util.Scanner;

public class ScoreMain
{
	public static void main(String[] args)
	{
		Scanner sc = new Scanner(System.in);
		Process obj = new Process();
		
		do
		{
			System.out.println();
			System.out.println("====[성적처리]====");
			System.out.println("1. 성적 입력");
			System.out.println("2. 성적 전체 출력");
			System.out.println("3. 이름 검색 출력");
			System.out.println("4. 성적 수정");
			System.out.println("5. 성적 삭제");
			System.out.println("==================");
			System.out.printf(">> 선택(1~5, -1종료) : ");
			
			String menus = sc.next();
			
			try
			{
				int menu = Integer.parseInt(menus);
				
				if(menu == -1)
				{
					System.out.println();
					System.out.println(">> 프로그램이 종료되었습니다.");
					return;
				}
				
				switch (menu)
				{
				case 1:
					// 성적 입력 기능 수행
					obj.sungjukInsert();
					break;
				case 2:
					// 성적 전체 출력 기능 수행
					obj.sungjukSelectAll();
					break;
				case 3:
					// 이름 검색 출력 기능 수행
					obj.sungjukSearchName();
					break;
				case 4:
					// 성적 수정 기능 수행
					obj.sungjukUpdate();
					break;
				case 5:
					// 성적 삭제 기능 수행
					obj.sungjukDelete();
					break;

				}
				
				
			} catch (Exception e)
			{
				System.out.println(e.toString());
			}
			
		} while(true);
	}
}
반응형

'DataBase > JDBC' 카테고리의 다른 글

CallableStatement  (0) 2020.11.08
PreparedStatement  (0) 2020.11.08
JDBC 프로그래밍 절차 및 구현  (0) 2020.10.31
JDBC 개념정리  (0) 2020.10.31
Comments