2024. 10. 4. 21:26ㆍJava
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 |