코딩항해기

[Spring] 템플릿 패턴 : JDBCTemplate 적용 (DAO 고도화) 본문

Spring

[Spring] 템플릿 패턴 : JDBCTemplate 적용 (DAO 고도화)

miniBcake 2024. 10. 18. 16:09

 

템플릿 패턴

복잡한 로직, 반복되는 로직을 캡슐화하는 패턴으로 중복되는 코드를 줄이고 유지보수를 편리하게 할 수 있다.

 

GoF 디자인패턴 : 템플릿 패턴
알고리즘의 구조를 메서드에 정의하고, 하위 클래스에서 알고리즘 구조의 변경없이 알고리즘을 재정의하는 패턴이다. 알고리즘이 단계별로 나뉘거나, 같은 역할을 하는 메서드이지만 여러 곳에서 다른 형태로 사용이 필요한 경우 유용한 패턴이다.

 

대표적인 예시로 JDBCTemplate가 있으며, JDBCTemplate를 적용하는 방법에 대해 정리한다.

 

 

JDBCTemplate

JDBC에 템플릿 패턴을 적용한 것으로 기존 반복되던 코드가 줄어들어 가독성이 좋고, 유지보수에 용이하다.

버전에 따라 의존 주입을 필요로 하기 때문에 의존 주입을 먼저 진행한다.

<!--JDBCTemplate-->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-jdbc</artifactId>
	<version>3.2.5.RELEASE</version>
</dependency>
<dependency>
	<groupId>commons-dbcp</groupId>
	<artifactId>commons-dbcp</artifactId>
	<version>1.4</version>
</dependency>

 

 

JDBCTemplate를 직접 생성하지 않고 기본 제공하는 JDBCTemplate를 사용할 예정이기 때문에 루트 컨테이너에서 빈 등록을 진행한다.

 

먼저 JDBCUtil 역할을 대신해 Connection을 해줄 객체가 필요하다. 이러한 객체를 DataSource라고 하며 JDBCUtil에서 driver이름과 url, 계정 이름, 비밀번호를 제공했듯이 DataSource에서도 주입을 해줄 필요가 있다.

    <!--Connection 객체를 만들 DataSource 객체 등록-->
    <bean class="org.apache.commons.dbcp.BasicDataSource" id="ds">
        <!--SI 주입을 통한 연결 정보 제공-->
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/minibcake"/>
        <property name="username" value="root"/>
        <property name="password" value="1234"/>
    </bean>

 

JDBCTemplate는 DataSource에 대해 의존성을 가지며 SI주입으로 방금 등록한 DataSource bean 객체를 주입할 수 있다.

    <!--JDBCTemplate 객체 등록-->
    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
        <!--SI 주입을 통한 Connection 객체 제공-->
        <property name="dataSource" ref="ds"/>
    </bean>

 

컨테이너에 객체를 생성했다면 이제 JDBCTemplate를 사용할 수 있다.

DAO에서 @Autowired를 통해 주입을 받아 사용할 수 있으며, selectAll, selectOne, CUD(insert, update, delete)마다 사용하는 메서드가 조금씩 다르다.

 

selectAll 

Object 배열 = {플레이스홀더 1번째 값, 플레이스 홀더 2번째 값 ...}; //플레이스 홀더에 들어가는 값 배열
jbdcTemplate.query("쿼리문", 배열명, 반환타입객체); //반환 타입이 어떻게 되는지, 어떤 컬럼과 매치되는지를 알려줄 반환타입 객체가 필요

 

seletOne

Object 배열 = {플레이스홀더 1번째 값, 플레이스 홀더 2번째 값 ...}; //플레이스 홀더에 들어가는 값 배열
jbdcTemplate.queryForObject("쿼리문", 배열명, 반환타입객체); //반환 타입이 어떻게 되는지, 어떤 컬럼과 매치되는지를 알려줄 반환타입 객체가 필요

 

CUD (insert, update, delete)

jbdcTemplate.update("쿼리문", 플레이스홀더 1번째 값, 플레이스 홀더 2번째 값 ...);

 

반환 타입을 알려줄 class 생성

//타입을 알려주고 컬럼과 필드값 매칭
class BoardRowMapper implements RowMapper<BoardDTO> {
    @Override
    public BoardDTO mapRow(ResultSet rs, int i) throws SQLException {
        BoardDTO data = new BoardDTO(); 
        data.setContent(rs.getString("CONTENT")); //해당 컬럼을 해당 필드로 매칭할 것임을 설정
        data.setWriter(rs.getString("WRITER"));
        data.setName(rs.getString("NAME"));
        data.setBid(rs.getInt("BID"));
        return data;
    }
}

 

 

적용

먼저, JDBCTemplate를 적용할 기존 BoardCateDAO 코드이다.

더보기
@Repository
public class BoardCateDAO {
	private final String INSERT = "INSERT INTO BB_BOARD_CATEGORY (CATEGORY_NAME) VALUES (?)";
	private final String UPDATE = "UPDATE BB_BOARD_CATEGORY SET CATEGORY_NAME = ? WHERE CATEGORY_NUM = ?";
	private final String DELETE = "DELETE FROM BB_BOARD_CATEGORY WHERE CATEGORY_NUM = ?";
	private final String SELECTALL = "SELECT CATEGORY_NUM, CATEGORY_NAME FROM BB_BOARD_CATEGORY ORDER BY CATEGORY_NUM";
	
	public boolean insert(BoardCateDTO boardCateDTO) {
		System.out.println("log: BoardCategory insert start");
		Connection conn = JDBCUtil.connect();
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(INSERT);
			pstmt.setString(1, boardCateDTO.getBoardCateName()); //카테고리명
			//넘어온 값 확인 로그
			System.out.println("log: parameter getBoardCateName : "+boardCateDTO.getBoardCateName());
			if(pstmt.executeUpdate() <= 0) { 
				//쿼리는 정상적으로 실행됐으나 실패
				System.err.println("log: BoardCategory insert execute fail");
				return false;
			}
		} catch (SQLException e) {
			System.err.println("log: BoardCategory insert SQLException fail");
			return false;
		} catch (Exception e) {
			System.err.println("log: BoardCategory insert Exception fail");
			return false;
		} finally {
			//연결해제
			if(!JDBCUtil.disconnect(conn, pstmt)) {
				//연결해제 실패
				System.err.println("log: BoardCategory insert disconnect fail");
				return false;
			}
			System.out.println("log: BoardCategory insert end");
		}
		System.out.println("log: BoardCategory insert true");
		return true;
	}
	
	public boolean update(BoardCateDTO boardCateDTO) {
		System.out.println("log: BoardCategory update start");
		Connection conn = JDBCUtil.connect();
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(UPDATE);
			pstmt.setString(1, boardCateDTO.getBoardCateName()); //카테고리명
			pstmt.setInt(2, boardCateDTO.getBoardCateNum()); //카테고리 번호
			//넘어온 값 확인 로그
			System.out.println("log: parameter getBoardCateName : "+boardCateDTO.getBoardCateName());
			System.out.println("log: parameter getBoardCateNum : "+boardCateDTO.getBoardCateNum());
			if(pstmt.executeUpdate() <= 0) { 
				//쿼리는 정상적으로 실행됐으나 실패
				System.err.println("log: BoardCategory update execute fail");
				return false;
			}
		} catch (SQLException e) {
			System.err.println("log: BoardCategory update SQLException fail");
			return false;
		} catch (Exception e) {
			System.err.println("log: BoardCategory update Exception fail");
			return false;
		} finally {
			//연결해제
			if(!JDBCUtil.disconnect(conn, pstmt)) {
				//연결해제 실패
				System.err.println("log: BoardCategory update disconnect fail");
				return false;
			}
			System.out.println("log: BoardCategory update end");
		}
		System.out.println("log: BoardCategory update true");
		return true;
	}
	
	public boolean delete(BoardCateDTO boardCateDTO) {
		System.out.println("log: BoardCategory delete start");
		Connection conn = JDBCUtil.connect();
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(DELETE);
			pstmt.setInt(1, boardCateDTO.getBoardCateNum()); //카테고리 번호
			//넘어온 값 확인 로그
			System.out.println("log: parameter getBoardCateNum : "+boardCateDTO.getBoardCateNum());
			if(pstmt.executeUpdate() <= 0) { 
				//쿼리는 정상적으로 실행됐으나 실패
				System.err.println("log: BoardCategory delete execute fail");
				return false;
			}
		} catch (SQLException e) {
			System.err.println("log: BoardCategory delete SQLException fail");
			return false;
		} catch (Exception e) {
			System.err.println("log: BoardCategory delete Exception fail");
			return false;
		} finally {
			//연결해제
			if(!JDBCUtil.disconnect(conn, pstmt)) {
				//연결해제 실패
				System.err.println("log: BoardCategory delete disconnect fail");
				return false;
			}
			System.out.println("log: BoardCategory delete end");
		}
		System.out.println("log: BoardCategory delete true");
		return true;
	}
	
	public ArrayList<BoardCateDTO> selectAll(BoardCateDTO boardCateDTO) {
		System.out.println("log: BoardCategory selectAll start");
		ArrayList<BoardCateDTO> datas = new ArrayList<>();
		Connection conn = JDBCUtil.connect();
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(SELECTALL);
			ResultSet rs = pstmt.executeQuery();
			while(rs.next()) { 
				BoardCateDTO data = new BoardCateDTO();
				data.setBoardCateNum(rs.getInt("CATEGORY_NUM")); 		//카테고리 번호
				data.setBoardCateName(rs.getString("CATEGORY_NAME"));	//카테고리 명
				//반환된 객체 리스트에 추가
				datas.add(data); 
				System.out.print("| result "+data.getBoardCateNum());
			}
			rs.close();
			System.out.println("end");
		} catch (SQLException e) {
			System.err.println("log: BoardCategory selectAll SQLException fail");
			datas.clear();//잔여데이터 삭제
		} catch (Exception e) {
			System.err.println("log: BoardCategory selectAll Exception fail");
			datas.clear();//잔여데이터 삭제
		} finally {
			//연결해제
			if(!JDBCUtil.disconnect(conn, pstmt)) {
				//연결해제 실패
				System.err.println("log: BoardCategory selectAll disconnect fail");
				datas.clear();//잔여데이터 삭제
			}
			System.out.println("log: BoardCategory selectAll end");
		}
		System.out.println("log: BoardCategory selectAll return datas");
		return datas;
	}
	
	private BoardCateDTO selectOne(BoardCateDTO boardCateDTO) {
		BoardCateDTO data = null;
		return data;
	}
}
//반환 타입 설정
class BoardCateMapper implements RowMapper<BoardCateDTO> {
    @Override
    public BoardCateDTO mapRow(ResultSet resultSet, int i) throws SQLException {
        BoardCateDTO data = new BoardCateDTO();
        data.setBoardCateName(resultSet.getString("CATEGORY_NAME"));
        data.setBoardCateNum(resultSet.getInt("CATEGORY_NUM"));
        return data;
    }
}

@Repository
@Slf4j
public class BoardCateDAO2 {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    //실행 종료는 AOP 기능을 활용한 로그 처리

    private final String INSERT = "INSERT INTO BB_BOARD_CATEGORY (CATEGORY_NAME) VALUES (?)";
    private final String UPDATE = "UPDATE BB_BOARD_CATEGORY SET CATEGORY_NAME = ? WHERE CATEGORY_NUM = ?";
    private final String DELETE = "DELETE FROM BB_BOARD_CATEGORY WHERE CATEGORY_NUM = ?";
    private final String SELECTALL = "SELECT CATEGORY_NUM, CATEGORY_NAME FROM BB_BOARD_CATEGORY ORDER BY CATEGORY_NUM";

    public boolean insert(BoardCateDTO boardCateDTO) {
        log.info("Insert BoardCateDTO getBoardCateName: [{}]", boardCateDTO.getBoardCateName());
        return jdbcTemplate.update(INSERT, boardCateDTO.getBoardCateName()) > 0;
    }

    public boolean update(BoardCateDTO boardCateDTO) {
        log.info("Update BoardCateDTO getBoardCateName : [{}] getBoardCateNum : [{}]", boardCateDTO.getBoardCateName(), boardCateDTO.getBoardCateNum());
        return jdbcTemplate.update(UPDATE, boardCateDTO.getBoardCateName(), boardCateDTO.getBoardCateNum()) > 0;
    }

    public boolean delete(BoardCateDTO boardCateDTO) {
        log.info("Delete BoardCateDTO getBoardCateNum : [{}]", boardCateDTO.getBoardCateNum());
        return jdbcTemplate.update(DELETE, boardCateDTO.getBoardCateNum()) > 0;
    }

    public List<BoardCateDTO> selectAll(BoardCateDTO boardCateDTO) {
        //Object[] args = {};넣을 값이 없으므로 생략
        //반환타입이 List
        return jdbcTemplate.query(SELECTALL, new BoardCateMapper());
    }

    private BoardCateDTO selectOne(BoardCateDTO boardCateDTO) {
        BoardCateDTO data = null;
        return data;
    }
}