Web개발/Spring

[Spring] JdbcTemplate 클래스

noobdev 2020. 2. 10. 20:58

JDBC(Java Database Connectivity)는 자바에서 오랫동안 사용되어온 DB 연동기술이다.

하지만 JDBC는 문제점이 하나 있는데, 비즈니스 로직 메서드마다 반복 구현해야 한다는 점이다.

Spring에서는 이 문제를 해결하기 위해 JdbcTemplate라는 클래스를 지원하고 있다.

 

JdbcTemplate란 GoF 디자인 패턴 중 템플릿 메서드 패턴이 적용된 클래스로 템플릿 메서드란 코딩 순서가 정형화된 기술에 유용하게 쓰이는 디자인 패턴이다.

[Java] - [Java] 템플릿 메서드 (Template Method)

 

[Java] 템플릿 메서드 (Template Method)

템플릿 메서드는 객체지향 프로그래밍 디자인 패턴 중 하나로 같은 객체지향 프로그래밍인 c++에서도 동일하게 적용할 수 있으며 템플릿 메서드의 역할은 메서드 실행 순서와 시나리오를 정의하는 것이다. 즉 로직..

kj99658103.tistory.com

 

 


Spring에서 지원하는 JdbcTemplate 클래스를 이용하면 개발자는 반복되는 DB 연동 작업을 JdbcTemplate 클래스에 맡기고 비즈니스 로직 구현에만 신경을 쓰면 된다는 이점이 있다.

 

JdbcTemplate 클래스 사용을 위한 순서는 다음과 같다.

(1) 스프링 JDBC 설정(라이브러리 추가) -> (2) DataSource 설정 -> (3) JdbcTemplate을 이용한 비즈니스 로직 구현

 


(1) 스프링 JDBC 설정(라이브러리 추가) 

JdbcTemplate 클래스를 이용하기 위한 첫 번째 과정은 JDBC 라이브러리를 추가하는 작업이다.

 

아래 코드를 pom.xml 파일에 붙여 넣는다.

	<!-- JDBC -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-jdbc</artifactId>
		<version>${org.springframework-version}</version>
	</dependency>
		
	<!-- DBCP -->
	<dependency>
		<groupId>commons-dbcp</groupId>
		<artifactId>commons-dbcp</artifactId>
		<version>1.4</version>
	</dependency>

주석을 기준으로 위의 코드가 JDBC 라이브러리이고 아래 코드가 DBCP 라이브러리다.

 

 

 

(2) DataSource 설정

DataSource는 커넥션 풀(ConnectionPool)로 데이터베이스와의 연결을 미리 끝내 놓고 필요할 때마다 미리 연결해 놓은 상태를 이용해 빠르게 데이터 베이스와 연동하여 작업을 하기 위한 기술이다.

JdbcTemplate 클래스가 JDBC API를 이용해 DB 연동을 처리하려면 반드시 데이터베이스와 커넥션이 있어야 하므로 DataSource를 bean으로 등록하여 스프링 컨테이너에서 생성하도록 해야 한다.

 

<!-- Oracle DB DataSource 설정 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClass" value="oracle.jdbc.driver.OracleDriver" />
		<property name="url" value="jdbc:oracle:thin:@localhost:1521:XE" />
		<property name="username" value="유데이터베이스 저이름" />
		<property name="password" value="데이터베이스 비밀번호" />
	</bean>


<!-- H2 DB DataSource 설정 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="org.h2.Driver" />
		<property name="url" value="jdbc:h2:tcp://localhost/~/test" />
		<property name="username" value="유데이터베이스 저이름" />
		<property name="password" value="데이터베이스 비밀번호" />
	</bean>
	

DataSource 인터페이스를 구현한 클래스는 다양하며 위의 예제는 Apache에서 제공하는 BasicDataSource를 등록한 모습이다.

DataSource의 등록은 각 dbms마다 조금씩 다르기에 주의할 필요가 있다.

 

이 외에도 프로퍼티 파일을 활용한 DataSource 설정 방법도 있다.

프로퍼티 파일을 활용한 DataSource 설정

jdbc.driver = 드라이버
jdbc.url = url
jdbc.username = 데이터베이스 아이디
jdbc.password = 데이터베이스 비밀번호

프로퍼티 파일을 이용하려면 위의 코드대로 각 항목에 맞는 값을 적은 properties을 작성한 후 xml파일을 수정하면 된다.

	
    <!-- 프로퍼티 파일을 이용한 설정 -->
    
    <context:property-placeholder location="classpath:프로퍼티 파일위치와 이름" />
    
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="${jdbc.driver}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
</bean>

※ 프로퍼티 파일을 이용하기 위해서는 property-placeholder 엘리먼트를 선언해주어야 한다.

 

(3) JdbcTemplate을 이용한 비즈니스 로직 구현

위의 설정을 맞쳤다면 이제 비즈니스 로직을 구현하기만 하면 된다.

 

JdbcTemplate 클래스의 메서드는 어떤 sql 문을 사용하느냐 따라 나뉜다.

 

- update( ) 메서드

JdbcTemplate 클래스에서 INSERT, UPDATE, DELETE 문을 처리할 때는 update( ) 메서드를 이용한다.

update( ) 메서드의 기본형
int update(String sql, Object args)

위의 코드가 update( ) 메서드의 기본형이며 쿼리문에 "?"에 값을 설정하는 방법에 따라 크게 두 가지 사용법이 있다.

 

 

첫 번째는 SQL 구문에 설정된 "?" 수 만큼 매개변수로 전달하는 방식이다.

// SQL 문
private final String BOARD_INSERT = "insert into board(seq, title, writer, content)" +
                                        "values((select nvl(max(seq), 0)+1 from board),?, ?, ?)";

// 비즈니스 로직
public void insertBoard(BoardVO vo) {
		jdbcTemplate.update(BOARD_INSERT, vo.getTitle(), vo.getWriter(), vo.getContent());
	}

 

 

두 번째는 Object 배열에 "?" 수 만큼 값들을 세팅하여 배열 객체에 두 번째 인자로 전달하는 방식이다.

// SQL 문
private final String BOARD_INSERT = "insert into board(seq, title, writer, content)" +
                                        "values((select nvl(max(seq), 0)+1 from board),?, ?, ?)";

// 비즈니스 로직
public void insertBoard(BoardVO vo) {
		Object[] args = {vo.getTitle(), vo.getWriter(), vo.getContent()};
		jdbcTemplate.update(BOARD_INSERT, args);
	}

 

 

 

SELECT 문은 query계열의 메서드를 사용한다.

- queryForInt( ) 메서드

SELECT 구문으로 검색된 row의 숫자를 리턴 받을 때 사용한다.

int queryForInt(String sql)
int queryForInt(String sql, Object args)
int queryForInt(String sql Object[] args)

 

- queryForObject( ) 메서드

queryForObject( ) 메서드는 SELECT 구문의 실행 결과를 특정 VO로 매핑하여 리턴 받을 때 사용한다.

검색 결과가 2개 이상이거나 없다면 예외를 발생시킨다.

주의할 점은 반드시 RowMapper 객체를 지정해야한다는 점이다.

 

// RowMapper
public class BoardRowMapper implements RowMapper<BoardVO>{

	@Override
	public BoardVO mapRow(ResultSet rs, int rowNum) throws SQLException {
		BoardVO board = new BoardVO();
		board.setSeq(rs.getInt("SEQ"));
		board.setTitle(rs.getString("TITLE"));
		board.setWriter(rs.getString("WRITER"));
		board.setContent(rs.getString("CONTENT"));
		board.setRegDate(rs.getDate("REGDATE"));
		board.setCnt(rs.getInt("CNT"));
		return board;
	}
}

 RowMapper를 구현한 클래스를 하나 만든다. RowMapper의 타입과 제네릭은 VO클래스형으로 한다.

그 후 mapRow라는 메서드를 재정의한다.

mapRow메서드의 정의부에는 vo 객체의 setter 메서드와 ResultSet rs를 이용해 값을 매핑한다. 

ResultSet 객체의 매개변수로 값으로는 데이터베이스의 컬럼명을 지정한다.

 

RowMapper를 구현했다면 이제 비즈니스 로직을 구현할 차례이다.

DAO 클래스에서 insert문을 이용한 비즈니스 로직을 구현한다.

// SQL 문
private final String BOARD_GET = "select * from board where seq = ?";

// 비즈니스 로직
public BoardVO getBoard(BoardVO vo) {
		Object[] args = {vo.getSeq()};
		return jdbcTemplate.queryForObject(BOARD_GET, args, new BoardRowMapper());
	}

 

 

- query( ) 메서드

query( ) 메서드는 SELECT 문의 결과가 하나가 아닌 여러 개일때 사용한다.

예를 들어 게시판의 글목록을 불러오는 로직 등에서 쓰인다.

query( ) 메서드도 queryForObject( ) 메서드와 마찬가지로 RowMapper를 반드시 지정해야하며 row의 갯수만큼 RowMapper의 mapRow메서드가 실행되어 여러개의 vo객체가 List로 반환된다.

// SQL문
private final String BOARD_LIST = "select * from board order by seq desc";

// 비즈니스 로직
public List<BoardVO> getBoardList(BoardVO vo) {
		return jdbcTemplate.query(BOARD_LIST, new BoardRowMapper());
	}

 

 

 

각 메서드에 대해 알았다면 이제 DAO클래스에서 JdbcTemplate 객체를 얻어와야한다.

JdbcTemplate 객체를 얻는 방법은 크게 2가지가 있다.

 

 

첫 번째 방법은 JdbcDaoSupport 클래스를 상속 받는 것이다.

@Repository
public class BoardDAOSpring extends JdbcDaoSupport{
	
	@Autowired
	public void setSuperDataSource(DataSource dataSource) {
		super.setDataSource(dataSource);
        
        // SQL
        private final String BOARD_INSERT = "insert into board(seq, title, writer, content) values((select nvl(max(seq), 0)+1 from board),?, ?, ?)";
        
        // 글 등록
	public void insertBoard(BoardVO vo) {
		getJdbcTemplate().update(BOARD_INSERT, vo.getTitle(), vo.getWriter(), vo.getContent());
	}
	}
 }   

JdbcDaoSupport 클래스를 상속받으면 getJdbcTemplate( ) 메서드를 사용할 수 있게 되며 getJdbcTemplate( ) 메서드를 호출하면 모든 메서드를 JdbcTemplate 객체로 구현할 수 있다.

주의할 점은 getJdbcTemplate( ) 메서드가 JdbcTemplate 객체를 리턴하려면 DataSource객체를 가지고 있어야한다.

따라서 반드시 부모 클래스인 JdbcDaoSupport 클래스의 setDataSource 메서드를 호출해야한다.

 

 

두 번째 방법은 JdbcTemplate 클래스를 bean으로 등록하여 의존성을 주입하는 방법이다.

위에서 JdbcTemplate 메서드를 설명할 때 사용한 예제와 같은 방법이다.

일단 JdbcTemplate 클래스를 bean으로 등록한다.

<!-- Spring JDBC 설정 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource" />
	</bean>

그 후 DAO 클래스에서 JdbcTemplate를 주입 받는다.

@Repository
public class BoardDAOSpring {
	
    // JdbcTemplate 자동 주입
	@Autowired
	private JdbcTemplate jdbcTemplate;
    
    // SQL
    private final String BOARD_INSERT = "insert into board(seq, title, writer, content) values((select nvl(max(seq), 0)+1 from board),?, ?, ?)";
    
    // 글 등록
	public void insertBoard(BoardVO vo) {
		jdbcTemplate.update(BOARD_INSERT, vo.getTitle(), vo.getWriter(), vo.getContent());
	}
   } 

주입받은 JdbcTemplate 객체로 위에서 설명한 각 메서드에 접근하여 비즈니스 로직을 처리할 수 있다.

 

 

 

본 포스팅은 필자가 공부한 내용을 정리한 것으로 오류가 존재할 수 있습니다.
참고 및 코드 출처 : 스프링 퀵 스타트