본문 바로가기

java/spring

[spring] Database && JDBC, jdbcTemplate

주제
오라클 db를 자바 프로젝트에 연결하기 위해 JDBC를 세팅한다.
JDBC의 문제점을 파악하고 대안으로서 JDBCTemlate을 사용한다.

 

Database 세팅


jdbc를 사용하기 위해서 오라클 db 설치 및 계정 세팅, sql developer 세팅을 하였다.

https://chartist1206.tistory.com/141

 

SQL 기본(SQL developer 사용하기)

테이블테이블이란 데이터베이스의 구조를 정의하는 방식을 의미한다. sql에서 테이블은 열과 행으로 이뤄진 표를 생각하면 된다. 각 열은 하위 데이터 종류를 나타내고 행은 각 열의 데이터를

chartist1206.tistory.com

 

사용할 프로젝트에서 오라클 db에 유저 정보를 저장해볼 것이므로 user 테이블을 다음 같이 생성하였다.

CREATE TABLE users (
id VARCHAR2(10) CONSTRAINT memid_pk PRIMARY KEY,
password VARCHAR2(10),
username VARCHAR2(15)
);

 

 

JDBC 세팅


 

pom.xml - 의존성 세팅

먼저 pom.xml 파일에 오라클 db용 JDBC를 추가해준다.

 

참고. 왜 dependency 태그로 의존성을 추가하지 않는걸까.

스프링 프로젝트에서 maven으로 의존성을 관리할 때는 기본적으로 denpendcy 태그를 이용하지만 Oracle db는 Oracle의 라이센스 정책 때문에 직접적으로 Maven Central Repository에 올려져 있지 않다. 따라서  repositories> 태그를 사용하여 Oracle JDBC 드라이버가 있는 외부 저장소를 등록한다.

그런 다음, dependency 태그로 해당 저장소에서 Oracle JDBC 드라이버를 의존성으로 추가한다.

<!-- 저장소 명시 -->
<repositories>
	<repository>
		<id>oracle</id>
		<name>ORACLE JDBC Repository</name>
		<url>http://maven.jahia.org/maven2</url>
	</repository>
</repositories>

<dependencies>
<!-- 다른 의존성 추가 -->

<!-- jdbc 드라이버 의존성-->
	<dependency>
		<groupId>com.oracle</groupId>
		<artifactId>ojdbc6</artifactId>
		<version>12.1.0.2</version>
	</dependency>
  </dependencies>

 

jdbc 드라이버 로딩 및 쿼리

dao에서 드라이버를 로딩하고 쿼리문을 다음같이 작성한다.

@Repository
public class userDao {

	public Map<String, User> DB;

	private String driver = "oracle.jdbc.driver.OracleDriver";
	private String url = "jdbc:oracle:thin:@localhost:1521:xe";
	private String userId = "c##taey";
	private String userpw = "sa1234";
	private Connection conn = null;
	private PreparedStatement pstmt = null;
	private ResultSet rs = null;

	public userDao() {
	}

	public boolean insertDao(User user) {
		int result = 0;

		try {
			Class.forName(driver);
			conn = DriverManager.getConnection(url, userId, userpw);
			String sql = "INSERT INTO USERS(id, password, username) VALUES (?,?,?)";

			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, user.getId());
			pstmt.setString(2, user.getPassword());
			pstmt.setString(3, user.getUsername());
			result = pstmt.executeUpdate();
			
			System.out.println("결과: "+result);

		} catch (Exception e) {
			// TODO: handle exception
			
		 e.printStackTrace();
		 return false;
		} finally {

			try {
				if (pstmt != null)
					pstmt.close();
				if (conn != null)
					conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				 e.printStackTrace();
				 return false;
			}

		}

		return true;
	}
}

 

이 방식의 문제

쿼리를 위해서 매 메소드 마다 보일러 플레이트(드라이버 로딩, 연결 생성, 스테이트먼트 생성, 사용 후 자원 해제) 등을 반복적으로 해야 한다. 이런 반복 작성을 해결해주는 것이 바로 jdbcTemplate 이다.

 

jdbcTemplate


jdbc의 역할은 다음과 같다.

 

  • SQL 쿼리 실행: SELECT, INSERT, UPDATE, DELETE 등 다양한 SQL 작업을 단순화
  • 자원 관리 및 단순화: JDBC 연결, 문서, 결과 집합 등의 자원을 자동으로 관리, 자원 누수 방지
  • 예외 처리: Spring의 DataAccessException 계층을 통해 표준화된 예외 처리를 제공
  • 결과 매핑: 쿼리 결과를 다양한 형태로 매핑(exL 단일 값, 객체, 객체 목록)

 

 

방법

DataSource 설정

dataSource는 jbbc template 세팅을 위해 데이터 베이스 연결 정보 (url, 아이디, 비밀번호) 를 저장 및 관리하는 클래스이다. 이는 두가지 클래스 중 하나를 선택하여 사용한다.

  • 스프링 | org.springframework.jdbc.datasource.DriverManagerDataSource
  • c3p0 | com.mchange.v2.c3p0. DriverManagerDataSource

 

pom.xml 에서 c3p0를 세팅

<repositories>
	<repository>
		<id>oracle</id>
		<name>ORACLE JDBC Repository</name>
		<url>http://maven.jahia.org/maven2</url>
	</repository>
</repositories>

<dependencies>
	<!-- DB -->
	<dependency>
		<groupId>com.oracle</groupId>
		<artifactId>ojdbc6</artifactId>
		<version>12.1.0.2</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-jdbc</artifactId>
		<version>4.1.6.RELEASE</version>
	</dependency>
	<dependency>
		<groupId>com.mchange</groupId>
		<artifactId>c3p0</artifactId>
		<version>0.9.5</version>
	</dependency>
</dependencies>

 

class에서 사용 예시1

생성자에서 dataSource와 JdbcTemplate를 세팅하면 이후 메소드에서는 별도의 세팅없이 바로 사용가능하다.

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.bs.user.service.entity.User;
import com.mchange.v2.c3p0.DriverManagerDataSource;

@Repository
public class userDao {
	private String driver = "oracle.jdbc.driver.OracleDriver";
	private String url = "jdbc:oracle:thin:@localhost:1521:xe";
	private String userId = "c##taey";
	private String userpw = "sa1234";
	private JdbcTemplate template;
	private DriverManagerDataSource dataSource;

	public userDao() {
		dataSource = new DriverManagerDataSource();
		dataSource.setDriverClass(driver);
		dataSource.setJdbcUrl(url);
		dataSource.setUser(userId);
		dataSource.setPassword(userpw);

		template = new JdbcTemplate();
		template.setDataSource(dataSource);
	}

	public boolean insertDao(User user) {
		int result = 0;
		final String sql = "INSERT INTO USERS (id,password,username) VALUES (?,?,?)";
		result = template.update(sql, user.getId(), user.getPassword(), user.getUsername());
		System.out.println("result: " + result);
		return true;
	}


}

 

class에서 사용 예시2

select처럼 결과를 조회하는 경우, 특정한 형식으로 매핑 가능하다.

template.query에서 사용된 기능을 살펴보면 다음과 같다.

  • PreparedStatementSetter | 인터페이스의 구현물. 파라미터 값 설정
  • RowMapper | 인터페이스의 구현물. ResultSet의 각 행을 순회 및 참조하여 매핑한다.
public User selectDao(String id, String password) {
	List<User> users = null; 
	final String sql ="SELECT * FROM users WHERE id = ? AND password = ?";
	users = template.query(sql, new PreparedStatementSetter() {

		@Override
		public void setValues(PreparedStatement pstmt) throws SQLException {
			// TODO Auto-generated method stub
			pstmt.setString(1, id);
			pstmt.setString(2, password);
			
		}
		
	}, new RowMapper<User>() {

		@Override
		public User mapRow(ResultSet rs, int rowNum) throws SQLException {
			// TODO Auto-generated method stub
			
			User user = new User();
			user.setId(rs.getString("id"));
			user.setPassword(rs.getString("password"));
			return user;
		}
	});

	System.out.println(users.get(0));
	
	if(users.isEmpty()) return null;
	
	
		return users.get(0);
	}

 

Connection pool


커넥션 풀이란, 사전에 커넥션을 미리 만들어 놓고 유저의 요청 시 빌려 사용하는 것이다. 이는 유저의 요청 시점에 만드는 것에 비해서 요청이 많을 시에 서버 부하를 막을 수 있다.

 

세팅 방법1: new 연산자

	public userDao() {
		dataSource =new ComboPooledDataSource();
		try {
			dataSource.setDriverClass(driver);
			dataSource.setJdbcUrl(url);
			dataSource.setUser(userId);
			dataSource.setPassword(userpw);
			
			// 커넥션 풀 세팅
			dataSource.setMinPoolSize(5);
			dataSource.setAcquireIncrement(5);
			dataSource.setMaxPoolSize(20);
			dataSource.setMaxStatements(5);
		} catch (PropertyVetoException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

 

세팅은 순서대로, 최소 커넥션 풀 개수, 한번에 생성되는 커넥션 풀 개수, 최대 커넥션 풀 개수, 캐싱하는 statment 객체 수를 의미한다.

 

세팅 방법2: 스프링 설정 파일.xml

xml로 세팅하고 autowired로 가져오면 된다.

<beans:bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<beans:property name="driverClass" value="oracle.jdbc.driver.OracleDriver" />
<beans:property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:xe" />
<beans:property name="user" value="$계정 아이디" />
<beans:property name="password" value="$ 계정 비밀번호" />
<beans:property name="maxPoolSize" value="200" />
<beans:property name="checkoutTimeout" value="60000" />
<beans:property name="maxIdleTime" value="1800" />
<beans:property name="idleConnectionTestPeriod" value="600" />
</beans:bean

 

세팅 방법3: 자바 configure 어노테이션 기반 세팅

사용할 때는 autowired를 사용

package com.bs.user.config;

import java.beans.PropertyVetoException;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.mchange.v2.c3p0.ComboPooledDataSource;

@Configuration
public class DBconfig {

	@Bean
	public ComboPooledDataSource dataSource() throws PropertyVetoException {

		ComboPooledDataSource dataSource =new ComboPooledDataSource();
		dataSource.setDriverClass("oracle.jdbc.driver.OracleDriver");
		dataSource.setJdbcUrl("jdbc:oracle:thin:@localhost:1521:xe");
		dataSource.setUser("유저 아이디");
		dataSource.setPassword("비밀번호");
		dataSource.setMaxPoolSize(200);
		dataSource.setCheckoutTimeout(60000);
		dataSource.setMaxIdleTime(1800);
		dataSource.setIdleConnectionTestPeriod(600);
		
		return dataSource;
	}
}
	@Autowired
	public userDao(ComboPooledDataSource dataSource) {
		this.template =new JdbcTemplate(dataSource);

	}