개주 훈련일지/🏋️ 전집중 호흡 훈련

JDBC) CRUD 메서드 정석 코드

lshfood2 2025. 11. 3. 16:47

model.dao에서 자주 사용되는

CRUD 메서드를 상황에 맞춰

가져다 쓰기 좋게 정리해보았다.

 

[공통사항 - 멤버변수/공유자원]

해당 BAO에서 어떤 쿼리문이 사용되는지를

우선적으로 파악하고 싶어하기 때문에!

쿼리문을 최상단에 올리게 되는데,

이런 경우에 멤버변수/공유자원에 해당된다.

 

멤버변수는 보통 생성자를 통해 초기화하는데

생성자 없이 초기화를 하였으므로 쿼리문은

공유자원이 되어 static을 사용하여 표기한다.

 

▼ DAO 클래스 공유자원 쿼리문 선언

public class BoardDAO {
	private static final String SELECT_ALL = "SELECT * FROM BOARD ORDER BY BID DESC";
	private static final String SELECT_ALL_TITLE = "SELECT * FROM BOARD WHERE TITLE LIKE ? ORDER BY BID DESC";
	private static final String SELECT_ALL_MID = "SELECT * FROM BOARD WHERE MID = ? ORDER BY BID DESC";
	private static final String SELECT_ALL_BCOUNT = "SELECT * FROM BOARD ORDER BY BCOUNT DESC, BID DESC";

	private static final String SELECT_ONE = "SELECT * FROM BOARD WHERE BID = ?";

	private static final String INSERT = "INSERT INTO BOARD(BID,TITLE,CONTENT,MID) VALUES((SELECT NVL(MAX(BID),100) FROM BOARD)+1,?,?,?)";
	private static final String DELETE = "DELETE FROM BOARD WHERE BID = ?";

	private static final String UPDATE_BCOUNT = "UPDATE BOARD SET BCOUNT = BCOUNT+1 WHERE BID = ?";
	private static final String UPDATE_TITLE = "UPDATE BOARD SET TITLE = ? WHERE BID = ?";
	private static final String UPDATE_CONTENT = "UPDATE BOARD SET CONTENT = ? WHERE BID = ?";

JDBC 활용을 하다보면 CRUD 생성 과정에서

1,2,4단계는 똑같고 3단계만 필요에 따라

바꿔주는 상황이 계속 반복되고 있다.

 

그래서 코드 결합도를 고려해 1,2,4를 

함수화, 모듈화, 컴포넌트화 하면 좋겠다.
> 즉 메서드로 처리하면 된다!

 

이를 위해 로직을 담을 별도의 Util 클래스를 만들어보자.

 

[JDBCUtil class]

▼ model common(공용) 로직 클래스 코딩

package model.common;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

// Util류 클래스들이 대체적으로 static(객체와 무관하게) 메서드를 로직으로 품고있어서 메서드만 호출하여 사용하는 경우가 多
public class JDBCUtil {
	private static final String driverName = "oracle.jdbc.driver.OracleDriver";
	private static final String url = "jdbc:oracle:thin:@localhost:1521:xe";
	private static final String user = "TEEMO";
	private static final String password = "1234";
	//driverName, url, user, password가 달라질때마다 util 클래스가 추가되어야한다.
	
	// 1,2 ▶ 함수화,모듈화,컴포넌트화
	public static Connection connect() {
		Connection conn = null;
		try {
			// 1. 드라이버 로드(적재)
			Class.forName(driverName);
			// 2. DB 연결
			conn = DriverManager.getConnection(url, user, password);
		} catch (Exception e) {
			e.printStackTrace();
		}		
		return conn;
	}
	
	// 4 ▶ 함수화,모듈화,컴포넌트화
	public static void disconnect(Connection conn,PreparedStatement pstmt) {
		try {
			// 4. DB 연결 해제
			pstmt.close();
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

[Create] - 추가

인자로 받은 객체에서 정보를 꺼내

pstmt에 세터를 적용시키고 DB에 추가한다.

이후 성공 실패 유무를 반환한다.

 

▼ Create(=insert) 메서드 코딩

public boolean insert(BoardDTO boardDTO){
    // DB 연결 얻기
    Connection conn = JDBCUtil.connect();

    // PreparedStatement는 finally에서 닫기 위해 try 바깥에서 선언
    PreparedStatement pstmt = null;
    try {
        // 미리 정의된 INSERT SQL로 PreparedStatement 생성       
        pstmt = conn.prepareStatement(INSERT);

        // SQL 내 첫 번째 ? 에 title 값을 바인딩
        pstmt.setString(1, boardDTO.getTitle());

        // 두 번째 ? 에 content 값을 바인딩
        pstmt.setString(2, boardDTO.getContent());

        // 세 번째 ? 에 작성자 ID(mid) 값을 바인딩
        pstmt.setString(3, boardDTO.getMid());

        // INSERT, UPDATE, DELETE 계열은 executeUpdate() 호출 — 변경된 행(row) 수를 반환
        int result = pstmt.executeUpdate();

        // result는 영향을 받은 행의 수(정상적으로 1이 되어야 함)
        // 0 이하면 삽입 실패로 간주하고 false 반환
        if(result <= 0) {
            return false;
        }
    } catch (SQLException e) {
        // 예외 발생 시 스택트레이스 출력
        e.printStackTrace();
        // 예외 발생하면 실패로 처리
        return false;
    } finally {
        // PreparedStatement와 Connection을 정리(닫기)       
        JDBCUtil.disconnect(conn, pstmt);
    }
    // 정상적으로 한 행이 삽입되었으면 true 반환
    return true;
}

[Update] - 변경

업데이트 항목이 어떤 사항인지 분기점(Condition)과

equals 비교를 통해 확인, 확인 조건은 제어문으로 관리한다.

제어문이 종료되면 DB업데이트 결과를 받고

성공 실패 유무를 반환한다.

 

▼ Update 메서드 코딩

public boolean update(BoardDTO boardDTO){
    // DB 연결 획득
    Connection conn = JDBCUtil.connect();

    PreparedStatement pstmt = null;
    try {
        // 어떤 UPDATE를 할지 조건문으로 구분
        // (조건문에 따라 서로 다른 SQL을 사용)

        // 조회수 증가(BCOUNT 업데이트)
        if(boardDTO.getCondition().equals("UPDATE_BCOUNT")) {
            pstmt = conn.prepareStatement(UPDATE_BCOUNT);
            // ex) UPDATE BOARD SET BCOUNT = BCOUNT+1 WHERE BID = ?
            // WHERE BID=?에 들어갈 파라미터 (게시글번호)
            pstmt.setInt(1, boardDTO.getBid());
        }

        // 내용(Content) 수정
        else if(boardDTO.getCondition().equals("UPDATE_CONTENT")) {
            pstmt = conn.prepareStatement(UPDATE_CONTENT);
            // ex) UPDATE BOARD SET CONTENT=? WHERE BID=?
            pstmt.setString(1, boardDTO.getContent()); // CONTENT 값 바인딩
            pstmt.setInt(2, boardDTO.getBid()); // BID 값 바인딩
        }

        // 제목(Title) 수정
        else if(boardDTO.getCondition().equals("UPDATE_TITLE")) {
            pstmt = conn.prepareStatement(UPDATE_TITLE);
            // ex) UPDATE BOARD SET TITLE=? WHERE BID=?
            pstmt.setString(1, boardDTO.getTitle()); // TITLE 값 바인딩
            pstmt.setInt(2, boardDTO.getBid()); // BID 값 바인딩
        }

        // UPDATE 실행 → 영향 받은 row 수 반환
        int result = pstmt.executeUpdate();

        // 영향받은 행이 1 이상이어야 정상 처리
        if(result <= 0) {
            // 업데이트 실패한 경우
            return false;
        }
    } catch (SQLException e) {
        // DB 동작 중 예외 발생 → 실패 처리
        e.printStackTrace();
        return false;
    } finally {
        // PreparedStatement, Connection 정리
        JDBCUtil.disconnect(conn, pstmt);
    }

    // 성공적으로 UPDATE 마무리된 경우 true
    return true;
}

[Delete] - 삭제

삭제해야할 타겟을 탐색해야 하므로

PK값 기준으로 타겟을 찾게 된다.

넘겨받은 id(PK)를 활용하여 대상을 찾고

DB에서 삭제하거나 등급을 변경시킨 후

성공 실패 유무를 반환한다.

 

▼ Delete 메서드 코딩

public boolean delete(BoardDTO boardDTO){
    // DB 연결 객체 얻기 ( Connection 생성 )
    Connection conn = JDBCUtil.connect();

    // PreparedStatement는 finally에서 닫아줘야 하므로 try 밖에서 선언
    PreparedStatement pstmt = null;

    try {
        // DELETE SQL 준비
        pstmt = conn.prepareStatement(DELETE);

        // WHERE BID=? 에 들어갈 값 바인딩 (삭제할 게시글 번호)
        pstmt.setInt(1, boardDTO.getBid());

        // DELETE 실행 → 영향받은 row 수 반환
        int result = pstmt.executeUpdate();

        // 삭제된 행이 1개 이상이어야 정상 delete
        if(result <= 0) {
            return false; // 삭제 실패
        }
    } catch (SQLException e) {
        // SQL 실행 중 오류가 발생했을 때
        e.printStackTrace();
        return false;
    } finally {
        // Connection, PreparedStatement 자원 정리
        JDBCUtil.disconnect(conn, pstmt);
    }

    // 정상적으로 삭제되었을 경우 true 반환
    return true;
}

[R-All] - 전부 읽기(여러개 반환)

ALL을 반환해야 하기 때문에

반환할 배열을 먼저 만들어주고(datas)

인자에서 담긴 정보를 data객체에 담아

datas 배열에 넣어서 반환한다.

 

▼ Read All/Select All 메서드 코딩

public ArrayList<BoardDTO> selectAll(BoardDTO boardDTO){
    // 조회결과들(여러 개)을 저장할 배열리스트 준비
    ArrayList<BoardDTO> datas = new ArrayList<BoardDTO>();

    // 1,2 ▶ DB연결을 별도클래스(JDBCUtil)에서 처리하게 만든 것 = 함수화, 모듈화 개념
    Connection conn = JDBCUtil.connect();

    // 3 PreparedStatement는 finally에서 닫아야하므로 try 밖에서 선언
    PreparedStatement pstmt = null;
    try {
        // 조건에 따라 다른 SQL문을 사용
        // getCondition() 값이 "ALL" 이면 전체조회용 SQL 사용
        if(boardDTO.getCondition().equals("ALL")) { 
            pstmt = conn.prepareStatement(SELECT_ALL);
        }
        // 제목 검색 조건이면 제목을 조건으로 SQL을 사용
        else if(boardDTO.getCondition().equals("TITLE")) {
            pstmt = conn.prepareStatement(SELECT_ALL_TITLE);
            pstmt.setString(1, boardDTO.getTitle()); 
            // ★ 물음표(?)에 boardDTO의 title 값을 바인딩
        }
        // 작성자ID(mid)로 검색하는 조건
        else if(boardDTO.getCondition().equals("MID")) {
            pstmt = conn.prepareStatement(SELECT_ALL_MID);
            pstmt.setString(1, boardDTO.getMid()); 
            // ★ 작성자ID값 바인딩
        }
        // 조회수순 정렬 같은 조건
        else if(boardDTO.getCondition().equals("BCOUNT")) {
            pstmt = conn.prepareStatement(SELECT_ALL_BCOUNT);
        }

        // 쿼리 실행하고 결과집합(ResultSet)을 받음
        ResultSet rs = pstmt.executeQuery();

        // 결과집합을 순서대로 한줄씩 읽기
        while(rs.next()) {
            // 레코드 하나 → DTO 하나
            BoardDTO data = new BoardDTO();
            // DB컬럼에서 값을 꺼내서 DTO 객체에 담는다
            data.setBid(rs.getInt("BID"));
            data.setTitle(rs.getString("TITLE"));
            data.setContent(rs.getString("CONTENT"));
            data.setMid(rs.getString("MID"));
            data.setBcount(rs.getInt("BCOUNT"));
            // 완성된 DTO 한 개를 datas 리스트에 추가
            datas.add(data);
        }
    } catch (SQLException e) {
        // DB 수행도중 예외 발생 시 로그 출력
        e.printStackTrace();
    } finally {
        // 4 ▶ DB연결 종료도 모듈화 시켜둔 함수 (자원정리)
        JDBCUtil.disconnect(conn, pstmt);
    }

    // 완성된 결과리스트 리턴
    return datas;
}

[R-One] - 한 개 읽기(1개만 반환)

One을 반환하기 때문에 datas가 아닌

객체 리턴이 된다. = return data;

찾아온 데이터가 있다면 (rs.next가 null이 아니라면)

그 데이터의 정보로 data 객체의 정보를 세터해서 반환한다.

 

▼ Read One/Select One 메서드 코딩

public BoardDTO selectOne(BoardDTO boardDTO){
    // 조회 결과를 담을 변수
	//조회 실패(레코드 없음) 시 null을 반환하도록 초기값을 null로 둠
    BoardDTO data = null;

    // DB 연결을 얻어오는 메서드
    Connection conn = JDBCUtil.connect();

    // PreparedStatement를 여기서 선언! finally 블록에서 닫기 위해 try 바깥에서 선언함
    PreparedStatement pstmt = null;
    try {
        pstmt = conn.prepareStatement(SELECT_ONE);
        // SQL의 첫 번째 물음표(?)에 파라미터를 설정
        // 전달된 boardDTO의 bid(게시글 ID)를 이용해 조회 대상 레코드를 지정
        pstmt.setInt(1, boardDTO.getBid());

        // 쿼리 실행. SELECT문은 ResultSet을 반환
        ResultSet rs = pstmt.executeQuery();

        // ResultSet에서 한 행(row)이라도 존재하면 true. 
        // 존재하면 DTO를 만들어 값을 채워 반환 준비
        if(rs.next()) {
            // 조회 결과를 담을 DTO 객체 생성
            data = new BoardDTO();

            // 컬럼 이름으로 값을 꺼내어 DTO의 필드에 세팅
            data.setBid(rs.getInt("BID"));
            data.setTitle(rs.getString("TITLE"));
            data.setContent(rs.getString("CONTENT"));
            data.setMid(rs.getString("MID"));
            data.setBcount(rs.getInt("BCOUNT"));
        }
    } catch (SQLException e) {
        // SQL 실행 중 예외가 발생하면 스택트레이스를 출력.
        e.printStackTrace();
    } finally {
        // JDBCUtil.disconnect(conn, pstmt)로 자원 정리를 수행.
        JDBCUtil.disconnect(conn, pstmt);
    }

    // 조회 결과 DTO 반환. 레코드가 없으면 null 반환.
    return data;
}