JAVA 웹개발 과정 국비 25일차 정리

25일차

※ 배우는 과정이므로 정확하지 않을 수 있습니다.
잘못된 점은 알려주시면 수정하도록 하겠습니다!


1. 변형 try-catch문

아래는 CafeMenuDAO 클래스의 insert 메소드이다.
만약 메소드가 콜되어서 코드를 차례대로 실행하다가 예외가 발생해버리면, throws Exception을 타고 main으로 예외가 전가되어버린다.
그러면 con.close() 까지 실행순서가 오지 않기 때문에 연결이 끊기지 않는 상황이 발생한다.
연결이 끊기지 않는 것은 치명적인 문제이기 때문에 꼭 해결해주어야 한다.

public int insert(String name, int price) throws Exception {
	Connection con = getConnection();
	String sql = "INSERT INTO cafe_menu VALUES(cafe_menu_seq.NEXTVAL,?,?)";
	PreparedStatement pstat = con.prepareStatement(sql);
	pstat.setString(1, name);
	pstat.setInt(2, price);
	int result = pstat.executeUpdate();
	
	con.commit();
	con.close();
	return result;
}

1. try-(catch)-finally

이제는 쓰지 않지만 오래된 코드에는 간혹 쓰인 것을 볼 수 있기 때문에 알아두어야 한다.

try문 : 예외가 생길 것 같은 코드를 담는다.
catch문 : 예외가 발생하면 어떤 처리를 할지 써주는 공간.
finally문 : 예외발생과는 상관 없이 구문이 끝나기 전 무조건 실행하는 공간.

con.close();를 finally문으로 빼면 con이 try문에 있기 때문에 인식하지 못하므로 try문 바깥으로 Connection con = null;을 빼준다.
아래와 같이 코드를 수정하면 예외가 발생하더라도 연결은 무조건 끊어지는 코드를 만들 수 있다.
catch문은 예외전가를 하기 때문에 생략할 수 있다.

public int insert(String name, int price) throws Exception {
	
	Connection con = null;
	try{
        con = getConnection();
        String sql = "INSERT INTO cafe_menu VALUES(cafe_menu_seq.NEXTVAL,?,?)";
        PreparedStatement pstat = con.prepareStatement(sql);
        pstat.setString(1, name);
        pstat.setInt(2, price);
        int result = pstat.executeUpdate();

        con.commit();
        return result;
	}finally {
		if (con != null) con.close();
	}
}

2. try with resource

try-finally 문의 단점을 보완해서 나온 try with resource는 아래와 같이 사용한다.
try와 { } 사이에 ( ) 를 삽입해주고, 연결을 꼭 해제해야하는 객체를 선언 및 할당한다. 그 외에 다른 코드는 넣을 수 없다.
try 소괄호 안 마지막 코드는 세미콜론(;)을 생략해줄 수 있다.

try(close를  호출해야하는 객체 선언  할당){
	// 예외 발생 예상되는 코드..
}catch(Exception e){
	// 예외 발생시 처리 코드..
}

Connection 객체와 PreparedStatement 객체는 꼭 close를 해주어야 하는 객체들이므로, try 소괄호( ) 안에 넣어준다.
prepareStatement 메소드의 인자는 sql이므로 PreparedStatement 객체 선언 코드보다 위에 있어야 하지만 try문 안에 넣어줄 수 없으므로 try문 바깥에 제일 위쪽으로 선언하여 변수 sql을 받을 수 있게 만든다.

public int insert(String name, int price) throws Exception {

	String sql = "INSERT INTO cafe_menu VALUES(cafe_menu_seq.NEXTVAL,?,?)";
	try(
		Connection con = this.getConnection();
		PreparedStatement pstat = con.prepareStatement(sql);
	){
	pstat.setString(1, name);
	pstat.setInt(2, price);
	int result = pstat.executeUpdate();
	con.commit();
	return result;
	}
}

3. try with resource 와 ResultSet 객체

Connection , PreparedStatement 외에도 ResultSet 도 연결을 해제해주어야 하는 객체 중에 하나이다.
그러나 ResultSet rs = pstat.executeQuery(); 코드만 try 소괄호 안에 넣으면 ? 안을 채워주는 코드 pstat.setString(1, writer);가 아래 순서에 존재하게되어 executeQuery가 sql문을 실행하지 못한다.
try문 안에 executeQuery 메소드보다 위쪽, pstat 변수 선언 코드보다 아래쪽에만 이 코드는 존재할 수 있는데, 위에서 말했듯이 try 소괄호 안에는 close가 필요한 객체 선언 및 생성 코드말고 다른 코드는 오지 못한다.

public List<MessageDTO> search(String writer) throws Exception {
	
	String sql = "SELECT * FROM Message WHERE writer=? ORDER BY 1";
	try(
            Connection con = this.getConnection();
            PreparedStatement pstat = con.prepareStatement(sql);
            //pstat.setString(1, writer);
            ResultSet rs = pstat.executeQuery();

해결 방법은 try with resource문을 중첩하여 사용하는 것이다.

public List<MessageDTO> search(String writer) throws Exception {
	
	String sql = "SELECT * FROM Message WHERE writer=? ORDER BY 1";
	try(
            Connection con = this.getConnection();
            PreparedStatement pstat = con.prepareStatement(sql);
            ){
		pstat.setString(1, writer);
		try(ResultSet rs = pstat.executeQuery();){
			List<MessageDTO> list = new ArrayList<>();
			while (rs.next()) {
				int seq = rs.getInt("seq");
				String twriter = rs.getString("writer");
				String message = rs.getString("message");
				
				MessageDTO dto = new MessageDTO(seq, twriter, message);
				list.add(dto);
			}
			return list;
		}
    }
}

ResultSet이 있을 때 무조건 중첩 try with resource문을 쓰는 것은 아니다.
ResultSet이 필요한데, ?를 채워줘야 하는 상황에서만 try with resource문을 중첩하여 사용한다.
일례로 selectAll 메소드는 ResultSet을 필요로 하지만 쿼리문에 ?가 없으므로 중첩 try with resource문을 사용하지 않는다.

2. 날짜를 다루는 기능들

1) System.currentTimeMillis() : long

현재 시간(1970년 1월 1일부터 현재까지의 초)을 long형으로 리턴한다.
리턴한 현재 시간을 timestamp 라고 한다.
밀리초이기 때문에 끝에서 4번째 자리가 초이다.

long timestamp = System.currentTimeMillis();
System.out.println(timestamp); // 출력값 : 1634142295352

2) SimpleDateFormat 클래스

timestamp를 원하는 모양으로 바꾸기 위해 사용한다.
SimpleDateFormat sdf = new SimpleDateFormat(“원하는 포맷 형태”); format 메소드 사용.

① sdf.format(Date d) : String

원하는 포맷 형태로 파라미터를 문자열로 바꿔준다.

SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
String dateString = sdf.format(timestamp);
System.out.println("현재 날짜 : " + dateString); 
// 현재 날짜 : 2021/10/14 02:39:29

② sdf.parse(String source) : Date

문자열을 Date 타입으로 변환해준다.
지정된 패턴으로 입력되지 않은 경우, 예외가 발생하기 때문에 try-catch문을 사용해주어야 한다.
즉, 지정된 패턴으로 입력되지 않으면 무조건 예외가 발생한다!!!

try{
	String str = "2008년 4월 20일";
	SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy년 M월 dd일");
	Date d = sdf2.parse(str);
}catch(Exception e) {
	e.printStackTrace();
}

3) Date 클래스

위의 코드를 이어서 보자.

Date d = sdf2.parse(str);

① d.getTime() : long

timestamp를 리턴한다.

SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");

try{
	String str = "2008년 4월 20일";
	SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy년 M월 dd일");
	Date d = sdf2.parse(str);
	
	String dateString2 = sdf.format(d.getTime());
	System.out.println("분석할 날짜 :" + dageString2);
	// 분석할 날짜 : 2008/04/20 12:00:00
	
}catch(Exception e) {
	e.printStackTrace();
}

댓글남기기