티스토리 뷰
부제: Spring으로 JDBC연동하기, Spring으로 DB조회하기
★관련 링크
Spring과 DB이야기 - 2. JdbcTemplate에서의 UPDATE, INSERT
Spring과 DB이야기 - 3. Transaction 처리
JDBC의 구현과 DataSource
JAVA를 활용하여 애플리케이션을 개발할 때, DB접근이 필요하면 보통 JDBC를 쓴다. JDBC를 사용해본 사람이라면 아래와 같은 방식으로 개발했을 것이다.
public Student getSingleStudent (String studentNum) throws SQLException { Student student; try(Connection conn = DriverManager.getConnection ("jdbc:oracle:this:@ServerIP:SID", "ID", "Password"); PreparedStatement pstmt = conn.prepareStatement ("SELECT * FROM STUDENT WHERE name = ?"); ResultSet rs = pstmt.executeQuery()) { if(rs.next()) { student = new Student(rs.getString("studentNum"), rs.getString("name"), rs.getString("email")); return student; } else { return null; } } catch (SQLException se) { se.printStackTrace(); throw se; } }
이처럼 DriverManager를 사용하여 데이터 소스에 연결하면 소스코드에 JDBC 드라이버 클래스이름 및 드라이버 URL을 직접 입력해야하므로 이식성이 떨어진다.
그래서 JDBC API는 DriverManager 외에 DataSource라는 것도 제공한다. DataSource에 대해 Oracle 홈페이지를 뒤져보니 아래와 같은 설명이 있었다.
The DataSource
interface is implemented by a driver vendor. There are three types of implementations:
- Basic implementation -- produces a standard
Connection
object - Connection pooling implementation -- produces a
Connection
object that will automatically participate in connection pooling. This implementation works with a middle-tier connection pooling manager. - Distributed transaction implementation -- produces a
Connection
object that may be used for distributed transactions and almost always participates in connection pooling. This implementation works with a middle-tier transaction manager and almost always with a connection pooling manager.
DataSource는 기본적인 연결과 Connection Pooling, 그리고 트랜젝션 처리까지 담당하며 JDBC Driver vendor(Oracle,Mysql 등)별로 구현되어 있다. (vendor별로 구현 방법이 다르지는 않다.) 아래는 구글링을 통해 구한 Mysql및 Oracle을 DataSource를 사용하여 연결한 예제이다.
package com.journaldev.jdbc.datasource; import java.io.FileInputStream; import java.io.IOException; import java.sql.SQLException; import java.util.Properties; import javax.sql.DataSource; import oracle.jdbc.pool.OracleDataSource; import com.mysql.jdbc.jdbc2.optional.MysqlDataSource; public class MyDataSourceFactory { public static DataSource getMySQLDataSource() { Properties props = new Properties(); FileInputStream fis = null; MysqlDataSource mysqlDS = null; try { fis = new FileInputStream("db.properties"); props.load(fis); mysqlDS = new MysqlDataSource(); mysqlDS.setURL(props.getProperty("MYSQL_DB_URL")); mysqlDS.setUser(props.getProperty("MYSQL_DB_USERNAME")); mysqlDS.setPassword(props.getProperty("MYSQL_DB_PASSWORD")); } catch (IOException e) { e.printStackTrace(); } return mysqlDS; } public static DataSource getOracleDataSource(){ Properties props = new Properties(); FileInputStream fis = null; OracleDataSource oracleDS = null; try { fis = new FileInputStream("db.properties"); props.load(fis); oracleDS = new OracleDataSource(); oracleDS.setURL(props.getProperty("ORACLE_DB_URL")); oracleDS.setUser(props.getProperty("ORACLE_DB_USERNAME")); oracleDS.setPassword(props.getProperty("ORACLE_DB_PASSWORD")); } catch (IOException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return oracleDS; } }
(출처: https://www.journaldev.com/2509/java-datasource-jdbc-datasource-example)
"db.properties"라는 파일에 접속정보를 저장한 뒤, 이를 읽어서 처리하는 방식이다. 이전의 DriverManager를 활용한 방식보다 더 편해졌다.
Spring에서도 이와 같은 DataSource를 사용하여 DB Connection을 구현한다. DataSource를 Spring bean으로 등록한 뒤, 이를 주입받아 사용하는 방식이다. 지금부터 설정 예를 살펴보도록 하자.
Spring에서 JDBC를 활용하여 DB연동
1. 의존성 추가
아래와 같이 처리하면 되겠다. 이 예제에서는 Connection Pool을 처리하기 위해, c3P0를 사용했고, DBMS는 mysql을 사용할 것이다. (Mysql 설치는 생략)
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>mysql-connector-java</artifactId> <version>6.0.5.RELEASE</version> </dependency>
2. Bean 설정
Spring 레퍼런스에서 소개하는 Bean 설정 예제를 아래에 첨부하니 참고하기 바란다.
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <context:property-placeholder location="jdbc.properties"/>
여기서, c3p0의 ComboPooledDataSource를 사용했다. 이는 DataSource의 구현 클래스로, 커넥션 풀을 다룰 수 있는 기능을 제공한다. 그래서 커넥션 풀에 관련한 추가 프로퍼티가 있는데, 주로 사용하는 것은 아래와 같다.
프로퍼티명 |
설명 |
inintailPoolSize |
초기 커넥션 풀의 크기. [default = 3] |
maxPoolSize |
커넥션 풀의 최대 크기. [default = 15] |
minPoolSize |
커넥션 풀의 최소 크기. [default = 3] |
maxIdleTime |
지정한 시간(초) 동안 사용되지 않는 커넥션을 제거함 [default = 0(제거안함)] |
checkoutTimeout |
풀에서 커넥션을 가져올 때 대기시간(밀리초). 시간초과 발생시, SQLException 발생 [default = 0(대기안함)] |
idleConnectionTestPeriod |
풀 속에 있는 커넥션의 테스트 주기(초) [default = 0(테스트 안함)] |
3. JdbcTemplate 활용
이제 Dao클래스에 JdbcTemplate를 활용하여 조회 쿼리를 실행해보자. 먼저 JdbcTemplate은 아래와 같이 활용하면 된다.
public class StudentDao { private JdbcTemplate jdbcTemplate; @AutoWired public StudentDao (DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } }
한편, JdbcTemplate 클래스는 SELECT 쿼리 실행을 위한 query() 메소드를 제공한다. query() 메소드는 아래와 같다.
- List<T> query(String sql, RowMapper<T> rowMapper)
- List<T> query(String sql, Object [] args, RowMapper<T> rowMapper)
- List<T> query(String sql, RowMapper<T> rowMapper, Object... args)
query() 메소드는 파라미터의 sql을 실행하고, 결과로 나온 ResultSet을 RowMapper가 JAVA의 객체로 변환한다. 그리고 sql 파라미터가 인덱스 기반 파라미터를 가진 쿼리(preparedStatement의 물음인 경우, 각 파라미터의 값을 args로 지정한다.
결국, RowMapper라는 클래스가 핵심인 것을 직감할 수 있다. 아래 예제를 살펴보자
public List<Student> selectByName(String name) { String sql = "select * from STUDENT where name = ?"; List<Student> results = jdbcTemplate.query(sql, new RowMapper<Student>() { @Override public Student mapRow(ResultSet rs, int rowNum) throws SQLException { Student student = new Student( rs.getStudentNum("studentNum"), rs.getName("name"), rs.getEmail("email")); return student; } }, name); return results; }
5~16번째 줄을 확인해보면, 이 예제에서는 RowMapper를 내부에서 직접 구현하여 처리했다. 비슷한 RowMapper가 다른 메소드에서도 반복된다면 RowMapper를 구현한 클래스를 따로 선언하여 처리해도 된다.
한편 query()메소드의 결과가 0건이더라도 null이 아닌 EMPTYLIST가 반환된다. 그래서 위 예제의 19번째 줄과 같이 별도의 NullPointerException 회피가 필요 없다.
-끝-
★관련 링크
Spring과 DB이야기 - 2. JdbcTemplate에서의 UPDATE, INSERT
Spring과 DB이야기 - 3. Transaction 처리
출처 및 참고
최범균, 『스프링4 프로그래밍 입문』, 가메출판사(2015)
https://docs.spring.io/
https://www.journaldev.com/
'IT > 개발팁' 카테고리의 다른 글
Spring과 DB이야기 - 3. Transaction 처리 (0) | 2017.12.17 |
---|---|
Annotation을 활용한 의존자동주입(DI) (0) | 2017.11.26 |
Spring과 DB이야기 - 2. JdbcTemplate에서의 UPDATE, INSERT (0) | 2017.11.19 |
Spring Cloud AWS를 활용하여 Spring Boot에서 AWS S3 연동하기 (1) | 2017.09.18 |
Intelli J에서 JUnit 생성하기(Spring Boot) (0) | 2017.04.23 |