today_is

DAO 와 DTO (template 이용) - 1 본문

java

DAO 와 DTO (template 이용) - 1

ye_rang 2023. 12. 3. 17:16

오늘의 공부 목표 : DAO와 DTO를 사용하여 oracle DB와 연결하여 DB에 있는 데이터를 수정, 삭제, 추가 등의 변경을 할 수 있도록 자바 코드 작성해보자.

 

step 0 ) template 준비

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 oracle.jdbc.driver.OracleDriver;

public class JdbcTemplate {

	private Connection conn;
	private PreparedStatement pstmt;
	private ResultSet rs;
	
	private String url = "jdbc:oracle:thin:@192.168.1.100:1521:xe";
	private String username = "c##itbank";
	private String password = "it";
	
	private Connection getConnection() {
		Connection conn = null;
		try {
			Class.forName(OracleDriver.class.getName());
			conn = DriverManager.getConnection(url, username, password);
			
		} catch (SQLException e) {
			System.out.println("SQL 접속 예외 : " + e);
			e.printStackTrace();
			
		} catch (ClassNotFoundException e) {
			System.out.println("클래스를 불러올 수 없습니다 : " + e);
			e.printStackTrace();
		} 
		return conn;
	}

	/**
	 * @param <T> : DTO Class, 단일 DTO 클래스
	 * @param sql : SQL to run, 실행할 SQL
	 * @param rowMapper : how to mapping Relational to Object in JAVA (lambda)
	 * 					: select의 결과를 릴레이션에서 자바 객체로 어떻게 맵핑할지 함수를 가지는 람다 객체
	 * @return : Single DTO Object, 단일 DTO객체를 반환
	 */
	public <T> T queryForObject(String sql, RowMapper<T> rowMapper) {
		T ob = null;
		try {
			conn = getConnection();
			pstmt = conn.prepareStatement(sql);
			rs = pstmt.executeQuery();
			
			if(rs.next()) {
				ob = rowMapper.mapper(rs);
			}
			
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {		
			try {
				if(rs != null) rs.close();	
				if(pstmt != null) pstmt.close();
				if(conn != null) conn.close();
			} catch(SQLException e) {}
		}
		return ob;
	}

	/**
	 * @param <T> : DTO Class, 단일 DTO 클래스
	 * @param sql : SQL to run, 실행할 SQL
	 * @param rowMapper : how to mapping Relational to Object in JAVA (lambda)
	 * 					: select의 결과를 릴레이션에서 자바 객체로 어떻게 맵핑할지 함수를 가지는 람다 객체
	 * @return : List<DTO>, 여러 DTO를 List형태로 반환
	 */
	public <T> ArrayList<T> queryForList(String sql, RowMapper<T> rowMapper) { 
		ArrayList<T> list = new ArrayList<>();
		
		try {
			conn = getConnection();
			pstmt = conn.prepareStatement(sql);
			rs = pstmt.executeQuery();
			while(rs.next()) {
				T ob = rowMapper.mapper(rs);
				list.add(ob);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {		
			try {
				if(rs != null) rs.close();	
				if(pstmt != null) pstmt.close();
				if(conn != null) conn.close();
			} catch(SQLException e) {}
		}
		return list;
	}
	
	/**
	 * @param sql : SQL to run, 실행할 SQL
	 * @param args : PreparedStatement에 sql을 올려둔 후 ? 에 해당하는 값들을 가변인자로 전달(Object)
	 * @return : SQL(insert/update/delete)에 영향을 받은 줄 수(count of affected row, int)
	 */
	public int update(String sql, Object... args) {
		int seq = 0;
		int row = 0;
		try {
			conn = getConnection();
			pstmt = conn.prepareStatement(sql);
			for(int i = 0; i < args.length; i++) {
				switch(args[i].getClass().getName()) {
				case "java.lang.Integer":
					pstmt.setInt(++seq, Integer.parseInt(args[i].toString()));
					break;
				default:
					pstmt.setString(++seq, args[i].toString());
				}
			}
			row = pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {		
			try {
				if(rs != null) rs.close();	
				if(pstmt != null) pstmt.close();
				if(conn != null) conn.close();
			} catch(SQLException e) {}
		}
		return row;
	}

}

 

step 0 ) RowMapper 준비

import java.sql.ResultSet;
import java.sql.SQLException;

@FunctionalInterface
public interface RowMapper <T> {
	
	T mapper(ResultSet rs) throws SQLException;
}

 

 

 

step 1) DTO 작성

 

DTO : Data Transfer Object

-> 프로세스 간에 데이터를 전달하는 객체

 

특징 1) DTO에서 작성할 필드명은 DB에서 사용하는 필드명과 일치해야 sql구문을 올바르게 적용시켜 데이터를 변경할 수 있다.

특징 2) DTO에는 필드선언과 해당 필드에 대한 기본 생성자 및 getter & setter 가 선언되어 있어야 한다

 

 

우선, DTO를 작성하기 위해서는 DB에 있는 데이터 중에서도 어떤 테이블을 이용할 것인지 명확해야 한다.

 

예를 들어, phonebook이라는 테이블이 있다고 가정하고 DTO를 작성해보자 !

 

 

phonebook 테이블 내부 

   String name (이름)

   String pnum (전화번호)

   int age (나이)

   String favorite (선호도)   --   y 와 n 만 입력 가능 

 

public class PhonebookDTO {

	private String name;		// 이름
	private String pnum;		// 전화번호
	private int age;			// 나이
	private String favorite;	// 즐겨찾기, (Y 혹은 N으로만 넣을 수 있음)
	
	@Override
	public String toString() {
		String form = "%s %13s %2s살 %s";
		form = String.format(form, name, pnum, age, (favorite.equals("Y") ? "❤" : "💔"));
		return form;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPnum() {
		return pnum;
	}
	public void setPnum(String pnum) {
		this.pnum = pnum;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getFavorite() {
		return favorite;
	}
	public void setFavorite(String favorite) {
		this.favorite = favorite;
	}
	
	
}

 

 

step 2) main 작성

 

main 은 사용자가 다루는 화면이라 생각하고, 기본적인 입.출력만 가능하도록 구현하자.

  -> 일반 사용자는 시스템이 어떻게 돌아가는지 알 수 없으나, 사용자가 원하는 것을 입력하여 그에 대한 처리된 결과를 시스템을 통해 부여  받기 때문이다

 

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


public class Main {
	static PhonebookDTO getDTOFromUserInput(Scanner sc) {
		PhonebookDTO dto = new PhonebookDTO();
		
		System.out.print("이름 입력 : ");
		dto.setName(sc.nextLine());	// dto.name = sc.nextLine();
		
		System.out.print("전화번호 입력 : ");
		dto.setPnum(sc.nextLine());
		
		System.out.print("나이 입력 : ");
		dto.setAge(Integer.parseInt(sc.nextLine()));
		
		System.out.print("즐겨찾기 (Y/N) : ");
		dto.setFavorite(sc.nextLine());
		
		return dto;
	}
    
	
	public static void main(String[] args) {
		Handler handler = new Handler();
		Scanner sc = new Scanner(System.in);
		List<PhonebookDTO> list = null;
		PhonebookDTO ob = null;
		int menu = -1, row;
		String name, pnum;
		
		while(menu != 0) {
			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("0. 종료");
			System.out.print("입력 >>> ");
			menu = Integer.parseInt(sc.nextLine());
			
			switch(menu) {
				case 1:
					list = handler.selectList();		// 리스트를 받아와서
					list.forEach(System.out::println);	// 출력한다
					break;
					
				case 2:
					ob = getDTOFromUserInput(sc);
					row = handler.insertPhonebook(ob);
					System.out.println(row != 0 ? "추가 성공" : "추가 실패");
					break;
					
				case 3:
					System.out.println("전화번호를 수정합니다");
					System.out.print("이름 입력 : ");
					name = sc.nextLine();
					System.out.print("변경할 전화번호 입력 : ");
					pnum = sc.nextLine();
					row = handler.updatePnum(name, pnum);
					System.out.println(row != 0 ? "수정 성공" : "수정 실패");
					break;
					
				case 4:
					System.out.println("즐겨찾기 등록 및 해제");
					System.out.print("즐겨찾기 상태를 변경할 사람의 이름 입력 : ");
					name = sc.nextLine();
					row = handler.updateFavorite(name);
					System.out.println(row != 0 ? "수정 성공" : "수정 실패");
					break;
					
				case 5:
					System.out.print("삭제할 사람의 이름 입력 : ");
					name = sc.nextLine();
					row = handler.delete(name);
					System.out.println(row != 0 ? "삭제 성공" : "삭제 실패");
					break;
					
				case 0:
					break;
			}
			System.out.println();
		} // end of while
		
		sc.close();
		System.out.println("프로그램을 종료합니다");
	}
}

 

 

step 3) handler 작성

 

handler 는 main과 dao를 이어주는 연결 역할을 수행한다.

 

따라서, main으로 부터 호출 받아서 DAO에 전달하여 DAO의 연산이 끝나면 해당 처리 결과를 다시 main으로 반환 시킨다.

 

import java.util.List;

public class Handler {
	
	
	private PhonebookDAO dao = new PhonebookDAO();

	public List<PhonebookDTO> selectList() {
		List<PhonebookDTO> list = dao.selectList();	// dao에게 요청하여 리스트를 받는다
		return list;					// 받아온 리스트를 메인에게 반환한다
	}

	public int insertPhonebook(PhonebookDTO ob) {
		int row = dao.insert(ob);
		return row;
	}

	public int updatePnum(String name, String pnum) {
		PhonebookDTO dto = new PhonebookDTO();
		dto.setName(name);
		dto.setPnum(pnum);
		int row = dao.updatePnum(dto);
		return row;
	}

	public int updateFavorite(String name) {
		int row = dao.updateFavorite(name);
		return row;
	}

	public int delete(String name) {
		int row = dao.delete(name);
		return row;
	}

}

 

 

step 4) DAO 작성

 

DAO : Data Access Object

   -> 데이터에 접근하기 위해 쓰는 객체

 

특징 1) DB에 있는 원본 데이터에 접근하기 위해 DB 접속에 관련한 객체들을 선언해야한다

특징 2) handler의 호출을 받고 DTO의 데이터에 sql구문을 적용시켜 결과를 처리하고 handler로 반환한다

 

import java.util.List;

public class PhonebookDAO {

	// SQL과 다른 인자만 전달하면 쿼리를 수행하고 결과를 돌려주는 객체(도구)
	private JdbcTemplate template = new JdbcTemplate();

	public List<PhonebookDTO> selectList() {
		String sql = "select * from phonebook order by favorite desc, name";
		
		RowMapper<PhonebookDTO> mapper = (rs) -> {
			PhonebookDTO dto = new PhonebookDTO();
			dto.setAge(rs.getInt("age"));
			dto.setFavorite(rs.getString("favorite"));
			dto.setName(rs.getString("name"));
			dto.setPnum(rs.getString("pnum"));
			return dto;
		};
		
		List<PhonebookDTO> list = template.queryForList(sql, mapper);
		
		return list;
	}

	public int insert(PhonebookDTO ob) {
		String sql = "insert into phonebook values (?, ?, ?, ?)";
		Object[] args = { ob.getName(), ob.getPnum(), ob.getAge(), ob.getFavorite() };
		int row = template.update(sql, args);
		return row;
	}

	public int updatePnum(PhonebookDTO ob) {
		String sql = "update phonebook set pnum = ? where name = ?";
		Object[] args = { ob.getPnum(), ob.getName() };
		int row = template.update(sql, args);
		return row;
	}

	public int updateFavorite(String name) {
		String sql = "update phonebook "
				+ "		set favorite = decode(favorite, 'Y', 'N', 'N', 'Y') "
				+ "		where name = ?";
		int row = template.update(sql, name);
		return row;
	}

	public int delete(String name) {
		String sql = "delete from phonebook where name = ?";
		int row = template.update(sql, name);
		return row;
	}
	
	
}

 

 

----------------------------------------------------------------------------------------------------------------------

study_review >>  모든 프로그램은 기능에 따라 분류가 필요하다는 것을 알게 되었다. 

오늘 공부했던 주제로 기능을 나누어본다면, 크게 3개로 나누어 볼 수 있다. 

 

1. 화면을 보고 데이터를 직접 입.출력하는 기능  (= Main)

 

2. 원본 데이터에 접근하여, 새로운 연산을 통해 데이터를 변경할 기능 (= DAO)

 

3. 원본 데이터의 정보를 담고 있는 것 (=  DTO)

 

이외에도 Handler 나 Template 를 통해, 공통적으로 수행해야할 연산들을 한데 모아두어, 조금 더 간편하고 단순한 코드를 작성할 수 있다. 

 

항상 main이 있는 클래스는 단순한 입.출력만 가능하도록 만들어서 기능 분리를 확실히 하도록 하자 !

 

 

'java' 카테고리의 다른 글

[ socket ] ip 주소를 통해 간단한 메시지 주고받기  (0) 2023.12.06
DAO 와 DTO (template 이용 X) - 2  (0) 2023.12.05
[ exception ] 예외 처리  (0) 2023.12.03
[ function ]  (0) 2023.12.01
[ for ] 별찍기 - 심화  (0) 2023.12.01