[Java] DAO와 DTO (1)

2024. 10. 4. 21:26Java

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 1 DTO 작성

DTO : Data Transfer Object

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

 

특징

  • DTO에서 작성할 필드명은 DB에서 사용하는 필드명과 일치해야 sql구문을 올바르게 적용시켜 데이터를 변경할 수 있다.
  • 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

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

 

특징

  • DB에 있는 원본 데이터에 접근하기 위해 DB 접속에 관련한 객체들을 선언 해야한다.
  • 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;
	}		
}

 


 

코드를 작성할 때 기능에 따라 분류하는 것의 중요성(?), 필요성을 좀 느끼게 된 것 같다.

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

 

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

 

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

 

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

 

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

 

DB와 연동을 해보니까 점점 나의 능력이 올라가는 듯한 느낌을 받았다.

그만큼 해야할 공부도 늘어났다...

sql공부를 시작해야 할 것 같다.

'Java' 카테고리의 다른 글

[Java] 상속 (extends)  (0) 2024.10.05
[Java] DAO와 DTO (2)  (0) 2024.10.04
[Java] 프로그래머스 문제 풀이에 유용한 String 메서드  (0) 2024.10.04
[Java] Exception  (0) 2024.10.04
[Java] function 함수  (0) 2024.10.04