Spring mysql jdbc 연결 - Spring mysql jdbc yeongyeol

이 글을 읽으면 스프링 JDBC를 통해 MySQL을 연동하는 과정과 코드을 알 수 있습니다.

스프링 jdbc

JDBC는 가장 오랫동안 자바 개발자들이 사용한 DB 연동 기술입니다. JDBC를 이용하여 DB 연동 프로그램을 개발하면 데이터베이스에 비종속적인 DB 연동 로직을 구현할 수 있습니다.

스프링 이러한 JDBC 기반의 DB 연동 프로그램을 쉽게 개발할 수 있도록 JdbcTemplate 클래스를 지원합니다.

JdbcTemplate는 GOF 디자인 패턴 중 템플릿 메소드 패턴이 적용된 클래스입니다. 템플릿 메소드 패턴은 복잡하고 반복되는 알고리즘을 캡슐화해서 재사용하는 패턴입니다. 따라서 반복되는 DB 연동 로직은 JdbcTemplace 클래스의 템플릿 메소드가 제공하고, 개발자는 달라지는 SQL 구문과 설정값만 신경 쓰면 됩니다. 

Spring mysql jdbc 연결 - Spring mysql jdbc yeongyeol

라이브러리 추가

스프링 JDBC를 사용하기위해 스프링 프로젝트의 pom.xml 파일에 DBCP 관련 <dependency>와 mysql connector <dependency> 설정을 추가합니다.(앞으로 예제는 Mysql 5.7 버전을 사용합니다.)

<!-- MySQL 설정 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
     <version>5.1.49</version>
</dependency>


<!-- Spring 설정 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>

<!--  DataSource 설정 -->
<dependency>

<groupId>commons-dbcp</groupId>

<artifactId>commons-dbcp</artifactId>

<version>1.4</version>

</dependency>


메이븐에서 위 라이브러리를 정상적으로 가져왔다면, 스프링 프로젝트의 Libraries - Maven Dependencies에서 아래 jar 파일들을 확인할 수 있습니다.

Spring mysql jdbc 연결 - Spring mysql jdbc yeongyeol
Spring mysql jdbc 연결 - Spring mysql jdbc yeongyeol

DataSource 설정

JdbcTemplate 클래스가 JDBC API를 이용하여 DB 연동을 처리하려면 데이터베이스로부터 커넥션을 얻어야 합니다. 따라서 JdbcTemplate 객체가 사용할 DataSource를 <bean> 등록하여 스프링 컨테이너가 생성하도록 합니다. src/main/resources 폴더에 스프링 설정 파일을 하나 생성한 후 다음 코드를 추가합니다.

📑applicationContext.xml

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
	<property name="driverClassName" value="org.mariadb.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://데이터베이스주소:포트번호/데이터베이스명?serverTimezone=Asia/Seoul&useSSL=false&characterEncoding=utf8"/>
	<property name="username" value="아이디"/>
	<property name="password" value="비밀번호"/>
</bean>

DataSource 인터페이스를 구현한 클래스 중 Apache의 BasicDataSource를 사용합니다. BasicDataSource 객체는 연결에 필요한 프로퍼티(property)들은 Setter 인젝션으로 설정합니다. 그리고 BasicDataSource 객체가 삭제되기 전 연결을 해제하기위해 close() 메소드를 destroy-method 속성으로 지정합니다.

위 코드에서 url, username, password를 직접 xml에 입력하지 않고, propertyPlaceholderConfigure를 이용하면 외부의 프로퍼티 파일을 참조하여 DataSource를 설정할 수 있습니다. src/main/resources 소스 폴더에 config 폴더를 생성하고, config 폴더에 database.properties 파일을 작성합니다.

📑database.properties

jdbc.driver=org.mariadb.jdbc.Driver
jdbc.url=jdbc:mysql://데이터베이스주소:포트번호/데이터베이스명?serverTimezone=Asia/Seoul&useSSL=false&characterEncoding=utf8
jdbc.username=아이디
jdbc.password=비밀번호

이제 Properties 파일에 설정된 프로퍼티들을 이용하여 DataSource를 설정하기위해 <context:property-placeholder> 엘리먼트를 사용합니다.

<context:property-placeholder>  사용하기 위해 context 접두어에 대한 네임스페이스를 지정하고, 네임스페이지와 관련된 xml:schemaLocation을 추가적으로 지정합니다.

마지막으로 프로퍼티 이름을 “${}” 구문으로 지정합니다. 모든 설정을 하면 application.xml 내부 코드는 아래와 같이 됩니다.

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-4.2.xsd">
    
<context:property-placeholder location="classpath:config/jdbc.properties"/>

<!-- DataSource 설정 -->
<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>

</beans>

JdbcTemplate 메소드

스프링 JDBC를 위한 기본 설정은 마무리되었습니다. 이제 JdbcTemplate 객체를 이용하여 DB 연동을 간단하게 처리할 수 있습니다.

지금부터 알아볼 예제는 다음과 같은 스키마를 갖는 BOARD 테이블을 통해 진행합니다.

Spring mysql jdbc 연결 - Spring mysql jdbc yeongyeol

update() 메소드

INSERT, UPDATE, DELETE 구문을 처리하기 위해 JdbcTemplate 클래스의 update 메소드를 사용합니다. update() 메소드의 사용법은 “?”에 값을 설정하는 방식에 따라 두 가지 형태가 존재합니다.

//SQL 명령
private final String BOARD_INSERT = "insert into board(seq, title, writer, content) values((select nvl(max(seq), 0)+1 from board),?,?,?)";
private final String BOARD_UPDATE = "update board set title=?, content=? where seq=?";
private final String BOARD_DELETE = "delete board where seq=?";
private final String BOARD_GET = "select * from board where seq=?";
private final String BOARD_LIST = "select * from board order by seq desc";

1. SQL 구문에 설정된 “?” 수만큼 값들을 차례대로 나열하는 방식

형식: int update(String sql, Object… args)

사용 예)

public void updateBoard(BoardVO vo){

	String BOARD_UPDATE = "update board set title=?, content=? where seq=?";
	int cnt = jdbcTemplate.update(BOARD_UPDATE, vo.getTitle(), vo.getContent(), vo.getSeq());
	System.out.println(cnt + “건 데이터 수정”);
}

2. Object 배열 객체에 SQL 구문에 설정된 “?” 수만큼의 값들을 세팅하여 배열 객체를 두 번째 인자로 전달하는 방식

형식: int update(String sql, Object[] args)

사용 예)

// 글 수정 

public void updateBoard(BoardVO vo) {

	String BOARD_UPDATE = "update board set title=?, content=? where seq=?";
	Object[] args= {vo.getTitle(), vo.getContent(), vo.getSeq()};
	int cnt = jdbcTemplate.update(BOARD_UPDATE, args);
	System.out.println(cnt + “건 데이터 수정”);
}

queryForInt() 메소드

SELECT 구문으로 검색된 정수값을 리턴받으려면 queryForInt() 메소드를 사용한다. 매개변수의 의미는 update() 메소드와 같다.

형식: int queryForInt(String sql)

        int queryForInt(String sq, Object… args)

        int queryForInt(String sql, Object[] args)

사용 예) 

// 전체 게시글 수 조회
public int getBoardTotalCount(BoardVo vo){

	String BOARD_TOT_COUNT = “select count(*) from board”;
	int count = jdbcTemplacte.queryForInt(BOARD_TOT_COUNT);
	System.out.println(“전체 게시글 수: ” + count + “건”);
}

queryForObject() 메소드

queryForObject() 메소드는 SELECT 구문의 실행 결과를 특정 자바 객체(Value Object)로 매핑하여 리턴받을 때 사용한다. 이때, 검색 결과가 없거나 검색 결과가 두 개 이상이면 IncorrectResultSizeDataAccessException 예외를 발생시킨다.

그리고 검색 결과를 자바 객체(Value Object)로 매핑할 RowMapper 객체를 반드시 지정해야 한다.

형식: Object queryForObject(String sql)

Object queryForObject(String sql, RowMapper<T> rowMapper)

Object queryForObject(String sql, Object[] args, RowMapper<T> rowMapper)

사용 예)

// 글 상세 조회 

public BoardVO getBoard(BoardVO vo) {

	String BOARD_GET = "select * from board where seq=?";
	Object[] args = { vo.getSeq() };
	return jdbcTemplacte.queryForObject(BOARD_GET, args, new BoardRowMapper());

}

검색 결과를 특정 VO(Value Object) 객체에 매핑하려면 RowMapper 인터페이스를 구현한 RowMapper 클래스가 필요하다. 즉, 테이블당 적어도 하나씩 RowMapper 클래스가 필요하다. RowMapper 인터페이스에는 mapRow() 메소드가 있어서 검색 결과로 얻어낸 Row 정보를 어떤 VO에 어떻게 매핑할지 구현할 수 있다.

다음은 Board 테이블에 대한 VO 클래스와 RowMapper 클래스이다. 아래 BoardRowMapper 클래스는 객체를 queryForObject 메소드의 매개변수로 넘겨주면, 스프링 컨테이너는 SQL 구문을 수행한 후 자동으로 RowMapper 객체의 mapRow() 메소드를 호출한다.

📑BoardVO.java

import java.sql.Date;

//VO(Value Object)
public class BoardVO {

	private int seq;
	private String title;
	private String writer;
	private String content;
	private Date regDate;
	private int cnt;

~ getter, setter 생략 ~

}

📑BoardRowMapper.java

import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;

public class BoardRowMapper implements RowMapper<BoardVO> {

	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;
	}
}

query() 메소드

queyForObject()가 SELECT 문으로 객체 하나를 검색할 때 사용하는 메소드라면, query() 메소드는 SELECT 문의 실행 결과가 목록일 때 사용한다. 기본 사용법은 queyForObject() 메소드와 같으며, 따라서 query() 메소드에서도 검색 결과를 VO 객체에 매핑하려면 RowMapper 객체를 사용한다.

query() 메소드가 실행되면 여러 건의 ROW 정보가 검색되며, 검색된 데이터 ROW 수만큼 RowMapper 객체의 mapRow() 메소드가 실행된다. 그리고 ROW 정보가 매핑된 VO 객체 여러 개가 List 컬렉션에 저장되어 리턴된다.

형식: List query(String sql)

List query(String sql, RowMapper<T> rowMapper)

List query(String sql, Object[] args, RowMapper<T> rowMapper)

사용 예)

// 글 목록 조회 

public List<BoardVO> getBoardList(BoardVO vo) {

	BOARD_LIST = "select * from board order by seq desc";
	return jdbcTemplacte.query(BOARD_LIST, new BoardRowMapper());

}

DAO 클래스 구현

지금까지 JdbcTemplate 메소드의 사용법을 알아보았다. 이제 해당 메소드들을 사용하여 DAO 클래스를 구현해보자. DAO 클래스에서 jdbcTemplate 객체를 얻는 방법은 두 가지이다.

1. JdbcDaoSupport 클래스 상속

import java.util.List;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;

@Repository
public class BoardDAOSpring extends JdbcDaoSupport{

	 //SQL 명령
	 private final String BOARD_INSERT = "insert into BOARD(seq, title, writer, content) values(?,?,?,?)";
	private final String BOARD_UPDATE = "update board set title=?, content=? where seq=?";
	private final String BOARD_DELETE = "delete board where seq=?";
	private final String BOARD_GET = "select * from board where seq=?";
	private final String BOARD_LIST = "select * from board order by seq desc";

	@Autowired
	public void setSuperDataSource(DataSource dataSource) {
		super.setDataSource(dataSource);
	}

	// CRUD 기능의 메소드 구현 
	// 글 등록 
	public void insertBoard(BoardVO vo) {
		System.out.println("===> Spring JDBC로 insertBoard() 기능 처리");
		getJdbcTemplate().update(BOARD_INSERT, vo.getSeq(), vo.getTitle(), vo.getWriter(), vo.getContent());
	}

	// 글 수정 
	public void updateBoard(BoardVO vo) {
		System.out.println("===> Spring JDBC로 updateBoard() 기능 처리");
		getJdbcTemplate().update(BOARD_UPDATE, vo.getTitle(), vo.getContent(), vo.getSeq());
	}

	// 글 삭제 
	public void deleteBoard(BoardVO vo) {
		System.out.println("===> Spring JDBC로 deleteBoard() 기능 처리");
		getJdbcTemplate().update(BOARD_DELETE, vo.getSeq());
	}

	// 글 상세 조회 
	public BoardVO getBoard(BoardVO vo) {
		System.out.println("===> Spring JDBC로 getBoard() 기능 처리");
		Object[] args = { vo.getSeq() };
		return getJdbcTemplate().queryForObject(BOARD_GET, args, new BoardRowMapper());
	}

	// 글 목록 조회 
	public List<BoardVO> getBoardList(BoardVO vo) {
		System.out.println("===> Spring JDBC로 getBoardList() 기능 처리");
		return getJdbcTemplate().query(BOARD_LIST, new BoardRowMapper());
	}
}

DAO 클래스를 구현할 때, JdbcDaoSupport 클래스를 부모 클래스로 지정하면 getJdbcTemplate() 메소드를 상속받을 수 있다. 그리고 getJdbcTemplate() 메소드를 호출하면 JdbcTemplate 객체가 리턴되어 모든 메소드를 JdbcTemplate 객체로 구현할 수 있다. 

단, getJdbcTemplate() 메소드가 JdbcTemplate 객체를 리턴하려면 DataSource 객체를 가지고 있어야 하므로 부모 클래스의 JdbcDaoSupport 클래스의 setDataSource() 메소드를 호출하여 데이터소스 객체를 의존성 주입해야 한다. 

@Autowired
public void setSuperDataSource(DataSource dataSource) {
	super.setDataSource(dataSource);
}

메소드 위에 @Autowired를 붙이면 해당 메소드를 스프링 컨테이너가 자동으로 호출하며, 이때 메소드 매개변수 타입을 확인하고 해당 타입의 객체가 메모리에 존재하면 그 객체를 인자로 넘겨준다.

2. JdbcTemplate 클래스를 <bean> 등록하고, 의존성 주입

먼저 스프링 설정 파일()에 JdbcTemplate 클래스를 <bean>등록한다.

<!-- DataSource 설정 -->
<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>

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

이제 BoardDAOSpring 클래스를 만들고, 클래스 내부에 JdbcTemplate 타입 변수를 선언하고, @Autowired 어노테이션을 이용해서 JdbcTemplate 객체에 DataSource 객체를 의존성 주입하자. 

import java.util.List;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;

@Repository
public class BoardDAOSpring extends JdbcDaoSupport{

@Autowired
private JdbcTemplate jdbcTemplate;

	 //SQL 명령
	 private final String BOARD_INSERT = "insert into BOARD(seq, title, writer, content) values(?,?,?,?)";
	private final String BOARD_UPDATE = "update board set title=?, content=? where seq=?";
	private final String BOARD_DELETE = "delete board where seq=?";
	private final String BOARD_GET = "select * from board where seq=?";
	private final String BOARD_LIST = "select * from board order by seq desc";


	// CRUD 기능의 메소드 구현 
	// 글 등록 
	public void insertBoard(BoardVO vo) {
		System.out.println("===> Spring JDBC로 insertBoard() 기능 처리");
		getJdbcTemplate().update(BOARD_INSERT, vo.getSeq(), vo.getTitle(), vo.getWriter(), vo.getContent());
	}

	// 글 수정 
	public void updateBoard(BoardVO vo) {
		System.out.println("===> Spring JDBC로 updateBoard() 기능 처리");
		getJdbcTemplate().update(BOARD_UPDATE, vo.getTitle(), vo.getContent(), vo.getSeq());
	}

	// 글 삭제 
	public void deleteBoard(BoardVO vo) {
		System.out.println("===> Spring JDBC로 deleteBoard() 기능 처리");
		getJdbcTemplate().update(BOARD_DELETE, vo.getSeq());
	}

	// 글 상세 조회 
	public BoardVO getBoard(BoardVO vo) {
		System.out.println("===> Spring JDBC로 getBoard() 기능 처리");
		Object[] args = { vo.getSeq() };
		return getJdbcTemplate().queryForObject(BOARD_GET, args, new BoardRowMapper());
	}

	// 글 목록 조회 
	public List<BoardVO> getBoardList(BoardVO vo) {
		System.out.println("===> Spring JDBC로 getBoardList() 기능 처리");
		return getJdbcTemplate().query(BOARD_LIST, new BoardRowMapper());
	}
}

지금까지 JdbcTemplate 객체를 이용해서 BoardDAOSpring 클래스를 구현했습니다. 이제  DAO 클래스를 사용할 클라이언트 프로그램을 만들어 실행합니다. 다음 코드는 글을 등록하고, 등록한 글 목록은 가져오는 코드입니다.

📑BoardServiceClient.java

import java.util.List;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class BoardServiceClient {

    public static void main(String[] args) {

        //1. Spring 컨테이너를 구동한다.
        AbstractApplicationContext container = new GenericXmlApplicationContext("applicationContext.xml");
       
        //2. Spring 컨테이너로부터 BoardDAOSpring 객체를 요청(Lookup)한다.
        BoardDAOSpring boardDAO = (BoardDAOSpring) container.getBean("boardDAO");
        
        //3. 글 등록 기능 테스트
        BoardVO vo = new BoardVO();
        vo.setSeq(12);
        vo.setTitle("제목");
        vo.setWriter("작성자");
        vo.setContent("내용");
        boardDAO.insertBoard(vo);
       
        //4. 글 목록 검색 기능 테스트
        List<BoardVO> boardList = boardDAO.getBoardList(vo);
        for(BoardVO board : boardList) {
            System.out.println("---> " + board.toString());
        }

        //5.  Spring 컨테이너 종료
        container.close();

    }
}

결과

Spring mysql jdbc 연결 - Spring mysql jdbc yeongyeol

출처

스프링 퀵 스타트