today_is
DAO 와 DTO (template 이용 X) - 2 본문
오늘의 목표 >> DB와 연동하여 DAO 와 DTO 를 이용한 데이터 변경을 해보자
1. 이번에는 template 을 사용하지 않고, 작성하기
2. sql구문을 조금 더 복잡하게 작성하여 새로운 데이터를 추출해보기
문제 >>
1) employees 테이블에서 first_name, salary, hire_date, 그리고 입사후 몇 년이 지났는지 조회하는 자바 프로그램을 작성
2) 데이터베이스에서 날짜 형식의 데이터를 컬럼에 저장했으나 해당 시점의 날짜만 기록되어 있고, 현재 시점으로부터 얼마나 오래되었는지 계산
3) 직원들이 입사 후 몇 년 지났는지 확인할 수 있도록 정렬해서 출력
(단, 이미 DB에 데이터가 생성되어있다고 가정)
step 1 > DTO 작성
tip (1) !! DTO를 작성할때에는 연결된 DB 테이블의 구조를 복사해서 미리 주석으로 적어둔다.
상단에 있는 주석을 보면서 DTO 클래스의 멤버 필드를 작성한다면, 자료형과 필드명을 잘못 작성할 확률이 줄어든다
tip (2) !! DTO에 tostring을 오버라이딩 해두고 계속 쓰면 앞으로는 일일이 출력형식을 따로 지정하지 않아도 된다.
import java.util.Date;
public class EmployeesDTO {
// ★★ 항상 DB 테이블과 자바 필드의 자료형을 동일하게 해야한다.
// 만약 중간에 자료형 바꿀시에는 getter setter 전부 다 바꾸기 !
// <DB 에 있는 테이블 구조>
// FIRST_NAME VARCHAR2(20 BYTE)
// SALARY NUMBER(8,2)
// HIRE_DATE DATE
private String first_name;
private int salary;
private Date hire_date;
private int hire_year;
// @Override 출력할때 직접 형식을 지정하지 않을거면 toString을 오버라이딩하여 사용하자.
// public String toString() {
// String s = String.format("%s %d %s %d", first_name, salary, hire_date , hire_year);
// return s;
// }
public String getFirst_name() {
return first_name;
}
public void setFirst_name(String first_name) {
this.first_name = first_name;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
public Date getHire_date() {
return hire_date;
}
public void setHire_date(Date hire_date) {
this.hire_date = hire_date;
}
public int getHire_year() {
return hire_year;
}
public void setHire_year(int hire_year) {
this.hire_year = hire_year;
}
}
step 2 > DAO 작성
DAO 에서는 DB 연결과 관련된 내용이 작성되어 있어야한다
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 java.util.List;
public class EmployeesDAO {
// FIRST_NAME VARCHAR2(20 BYTE)
// SALARY NUMBER(8,2)
// HIRE_DATE DATE
private Connection conn;
private PreparedStatement pstmt;
private ResultSet rs;
private String url = "jdbc:oracle:thin:@192.168.1.100:1521:xe";
private String username = "hr";
private String password = "hr";
private Connection getConnection() { // getConnection() : DB 접속을 위한 메서드
Connection conn = null;
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
conn = DriverManager.getConnection(url, username, password);
} catch (Exception e) {
System.out.println("DB 접속 에러 : " + e);
e.printStackTrace();
}
return conn;
}
public List<EmployeesDTO> showHireYear() { // showHireYear() : 근속년수를 보여주는 메서드
ArrayList<EmployeesDTO> list = new ArrayList<>();
String sql = " select first_name, salary, hire_date, "
+ " abs(extract(year from hire_date) - extract(year from sysdate)) as year_diff "
+ " from employees "
+ " order by year_diff ";
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while(rs.next()) { // 다음 줄이 있다면 계속 출력
EmployeesDTO dto = new EmployeesDTO();
dto.setFirst_name(rs.getString("first_name")); // 출력하고자 하는 필드만 적기
dto.setSalary(rs.getInt("salary")); // 모든 필드를 다 적어두지 않아도 된다 !!
dto.setHire_date(rs.getDate("hire_date"));
dto.setHire_year(rs.getInt("year_diff"));
list.add(dto);
}
} catch (SQLException e) { // sql 구문에 대한 예외처리
e.printStackTrace();
} finally {
try {
if(rs != null) rs.close(); // 출력이 다 끝나면 항상 close()를 해주자.
if(pstmt != null) pstmt.close();
if(conn != null) conn.close();
} catch (Exception e) {}
}
return list; // list 자체를 반환
}
}
sql 구문 >>
" select first_name, salary, hire_date, abs(extract(year from hire_date) - extract(year from sysdate)) as year_diff from employees order by year_diff "
sql 구문을 하나씩 분석해보자 !
1) select
select first_name, salary, hire_date, year_diff
-> 4개의 필드를 추출하겠다.
2) year_diff
year_diff 에는 연산이 있다.
2-1) abs : 절대값
2-2) extract(year from hire_date) - extract(year from sysdate) : 입사일자 - 현재날짜 (=근속년수)
2-3) as : 별칭. 연산이 끝난 필드를 year_diff 라고 부르자
3) from 테이블명
from employees
4) order by 필드명 (asc/desc)
필드명을 기준으로 정렬한다.
<정렬방식>
asc : 오름차순 -> 정렬방식을 생략한다면 기본값은 asc
desc : 내림차순
step 3 > main 작성
import java.util.List;
public class Main {
public static void main(String[] args) {
EmployeesDAO dao = new EmployeesDAO();
List<EmployeesDTO> list = dao.showHireYear();
for(EmployeesDTO dto : list) {
System.out.printf("이름 : %10s , 급여 : %10d , 근무시작일자 : %10s , 근속년수 : %10d년"
, dto.getFirst_name(), dto.getSalary(), dto.getHire_date(), dto.getHire_year());
System.out.println();
}
}
}
for(EmployeesDTO dto : list) {
System.out.printf("이름 : %10s , 급여 : %10d , 근무시작일자 : %10s , 근속년수 : %10d년" , dto.getFirst_name(), dto.getSalary(), dto.getHire_date(), dto.getHire_year());
-> 이 부분은 DTO 에 tostring 을 오버라이딩해서 처리할 수도 있다
study_review >>
기능에 따른 메서드를 미리 만들어두고 호출해서 사용하면 코드를 좀 더 편리하게 쓸 수 있다.
또한, 코드의 재사용성을 높여서 코드의 길이도 줄일 수 있는 장점이 있다.
오늘 작성한 코드에서도 조금 더 줄일 수 있는 부분을 발견하였다.
예컨대, close() 를 따로 메서드로 만들어주는 방법이다.
private void close() { // DAO 내부에서 호출하도록 만들었다. private
try {
if(rs != null) rs.close();
if(pstmt != null) pstmt.close();
if(conn != null) conn.close();
} catch (Exception e) {}
}
close() 를 미리 만들어둔다면, 매 함수마다 conn, pstmt, rs 객체를 닫아주는 코드를 작성하지 않아도 된다.
// 변경전 >>
finally {
try {
if(rs != null) rs.close();
if(pstmt != null) pstmt.close();
if(conn != null) conn.close();
}
// 변경후 >>
finally {
close(); // 미리 만들어둔 함수를 그냥 호출해서 사용하기만 하면 된다
}
이렇게 비교해서 보면, 차이가 꽤나 크다.
매번 객체들은 닫아주어야하는데, 그 코드를 또 작성하면 귀찮기 때문이다.
처음에는 반복되는 코드를 찾기 어려웠는데, 같은 코드를 여러번 작성해보니 한눈에 들어왔다.
아무래도 편리하게 코드 작성하는 방법을 터득하려면 반복만이 살길이다 !! 아자
'java' 카테고리의 다른 글
[ jsp ] request 와 response 의 내장객체 (0) | 2023.12.07 |
---|---|
[ socket ] ip 주소를 통해 간단한 메시지 주고받기 (0) | 2023.12.06 |
DAO 와 DTO (template 이용) - 1 (0) | 2023.12.03 |
[ exception ] 예외 처리 (0) | 2023.12.03 |
[ function ] (0) | 2023.12.01 |