티스토리 뷰

초보 웹 개발자를 위한 스프링 5 프로그램 입문을 보고 복습 겸 개인 학습 정리입니다. (windows 기준)

 

 

 

JAVA에서는 JBDC API를 사용하거나 JPA, MyBatis 같은 기술을 이용해서 DB를 연동처리한다고 한다.

이 책(초보.. 스프링 5 입문)에서는 JBDC를 위해 스프링이 제공하는 JdbcTemplate사용법에 대해 설명하고 있다. 

사전에 DB 연동에 대해 알고 있지 않았기 때문에 JBDC에 대해 너무 잘 설명해 놓은 블로그가 있어서 보면서 공부하고 있다.

 

JBDC

Java DataBase Connectivity 약자 (자바 데이터베이스 연결)

DB에 연결해서 select, insert, update, delete 작업을 한다.

 

스프링이 제공하는 또 다른 장점 트랜잭션 관리가 쉽다는 것

스프링을 사용하면 트랜잭션을 적용하고 싶은 메서드에 @Transactional 애노테이션을 붙이면 커밋과 롤백 처리는 스프링이 알아서 처리한다.

※ 스프링이 제공하는 트랜잭션 기능을 사용하려면 spring-tx 모듈이 필요하지만 spring-jdbc모듈에 대한 의존을 추가하면 자동으로 spring-tx 모듈이 포함된다. pom.xml 파일에 spring-jdbc모듈을 추가했기 때문에 spring-tx 모듈을 추가하지 않아도 사용할 수 있다.

 

스프링의 특징

의존 주입 (DI) 지원

AOP (Aspect-Orignted Programing) 지원

MVC 제공

JDBC, JPA 연동 선언적 트랜젝션 DB 연동 지원

 

예제 프로젝트 만들기 (메이븐 기준)  예제 코드

sp5-chap08 프로젝트 폴더 생성

프로젝트(sp5-chap08) 하위 폴더로 src\main\java 생성

sp5-chap08 폴더 내부에 pom.xml 생성

+ pom.xml 파일에 spring-jdbc, tomcat-jdbc, mysql-connector-java dependency 추가

       spring-jdbc : JdbcTemplate 등 JDBC 연동에 필요한 기능을 제공

       tomcat-jdbc : DB 커넥션풀 기능을 제공

       mysql-connector-java : MySQL 연결에 필요한 JDBC 드라이버를 제공

이클립스에서 sp5-chap08 폴더에 생성한 메이븐 프로젝트 import

기존 chap03에서 작성한 예제코드 사용 (일부 코드 수정)

 

 

MemberDao 클래스에 기존 HashMap으로 설정되어 있던 코드 수정

→sp5-chap08/src/main/java/spring/MemberDao.java

package spring;

import java.util.Collection;

public class MemberDao {
	public Member selectByEmail(String email) {
		return null;
	}
	public void insert(Member member) {
	}
	public void update(Member member) {
	}
	public Collection<Member> selectAll() {
		return null;
	}
}

 

MySQL에 root사용자로 접속하여 아래 정리해 둔 쿼리를 실행하여 db사용자, 데이터베이스, 테이블을 생성하고,

spring5 계정으로 mysql에 접속 후 spring5 fs 데이터베이스에 member 테이블에 데이터를 넣어준다.

→sp5-chap08/src/sql/ddl.sql

create user 'spring5'@'localhost' identified by '0000';
// MySQL DB에 spring5 계정 생성 암호로 '0000'를 사용한다는 뜻

create database spring5fs character set=utf8;
// 한글 등 다국어 보관을 위해 utf-8이아닌 utf8을 사용

grant all privileges on spring5fs.* to 'spring5'@'localhost';
// spring5fs 데이터베이스에 spring5 계정이 접근할 수 있도록 권한 부여

create table spring5fs.MEMBER (
    ID int auto_increment primary key,
    EMAIL varchar(255),
    PASSWORD varchar(100),
    NAME varchar(100),
    REGDATE datetime,
    unique key (EMAIL) 
) engine=InnoDB character set = utf8;

// spring5fs 데이터베이스에 member 테이블로 들어가 데이터를 미리 생성한다.
insert into MEMBER (EMAIL, PASSWORD, NAME, REGDATE) values ('a@a.com', '1234', '김에이', now());

 

 

DataSource 설정

스프링이 제공하는 DB연동 기능은 DataSource를 사용해서 DB connection을 구한다.

DB 연동에 사용할 DataSource를 스프링 빈으로 등록하고  DB 연동 기능을 구현한 빈 객체는 DataSource를 주입받아 사용한다.

 

책 예제에서는 Tomcat JDBC 모듈을 사용했고 이 모듈은 javax.sql.DataSource를 구현한 DataSource 클래스를 제공한다.

 

→sp5-chap08/src/main/java/config/DbConfig.java

package config;

import org.apache.tomcat.jdbc.pool.DataSource;
// Tomcat JDBC 모듈의 커넥션 풀 기능을 제공하는 DataSource 구현 클래스.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DbConfig {
	// DataSource 를 스프링 빈으로 등록
	@Bean(destroyMethod = "close") // close 메서드는 커넥션 풀에 보관된 Connetcion을 닫는다
	public DataSource dataSource() {
		DataSource ds = new DataSource(); // DataSource 객체 생성
		ds.setDriverClassName("com.mysql.jdbc.Driver"); // jdbc 드라이버 클래스 지정
		ds.setUrl("jdbc:mysql://localhost/spring5fs?characterEncoding=utf8"); // jdbc url 지정
		ds.setUsername("spring5"); // db연결할 때 사용할 사용자 계정과 암호 지정
		ds.setPassword("0000");    // db연결할 때 사용할 사용자 계정과 암호 지정
		ds.setInitialSize(2); // 커넥션 풀을 초기화할 때 생성할 초기 커넥션 개수 지정
		ds.setMaxActive(10); // 커넥션 풀에서 가져올 수 있는 최대 커넥션 개수 지정
		ds.setTestWhileIdle(true); // 커넥션 풀에 유휴 상태로 있는 동안 검사할지 여부 지정
		ds.setMinEvictableIdleTimeMillis(60000 * 3);
		ds.setTimeBetweenEvictionRunsMillis(10 * 1000);
		return ds;
	}
}

 

설정 메서드 설명
.setlnitialSize(int) 커넥션 풀을 초기화할 때 생성할 초기 커넥션 개수 지정. 기본값은 10
.setMaxActive(int) 커넥션 풀에서 가져올수 있는 최대 커넥션 개수 지정. 기본값은 100
.setMinldel(int) 커넥션 풀에 유지할 수 있는 최대 커넥션 개수 지정. 기본값은 100
.setMaxWait(int) 커넥션 풀에서 커넥션을 가져올 때 최대 대기 시간을 밀리초 단위로 지정
기본 3000밀리초 (30초)
.setMaxAge(long) 최초 커넥션 연결 후 커넥션 최대 유효기간 밀리초 단위 지정. 기본값 0
.setValidationQuery(String) 커넥션이 유효한지 검사할 때 사용할 쿼리 지정.
기본값은 null  (null이면 검사를 하지 않는다.) 

언제 검사할지는 별도 설정.
"select 1" 또는 "select 1 from dual"과 같은 쿼리 주로 사용
.setTestWhileldle(boolean) 커넥션 풀에 유휴 상태로 유지할 최소 시간을 밀리초 단위로 지정.
기본값 6000 밀리초 (60초)
.setMinEvictableIdleTimeMillis(int) 커넥션 풀에 유휴 상태로 유지할 최소 시간을 밀리초 단위로 지정 
TestWhileldle가 true면 유휴 시간이 이 값을 초과한 커넥션 풀을 제거한다.
기본값  6000 밀리초 (60초)
.setTimeBetweenEvictionRunsMillis(int) 커넥션 풀에 유휴 커넥션을 검사할 주기 밀리초 단위로 지정
기본값  5000 밀리초 (50초)  *이 값을 1초 이하로 설정 할 수 없다.

 

 

 

Tomcat JDBC 모듈의  import org.apache.tomcat.jdbc.pool.DataSource 클래스는 커넥션 풀 기능을 제공하는 DataSource 구현 클래스. 이 클래스는 커넥션을 몇 개 만들지 지정할 수 있는 메서드를 제공한다. 주요 프로퍼티는 톰캣에서 확인이 가능하다. (책은 187p)

 

커넥션 풀은 커넥션을 생성하고 유지한다. 커넥션 풀에 커넥션을 요청하면 해당 커넥션은 활성(active) 상태가 되고, 커넥션을 다시 커넥션풀에 반환하면 유후(idle) 상태가 된다.

 

DataSource#getConnection()을 실행하면 커넥션 풀에 커넥션을 가져와 커넥션은 활성(active) 상태가 되고, 반대로 커넥션을 종료(close)하면 커넥션은 풀로 돌아가 유후(idle) 상태가 된다.

 

 

커넥션을 구하고 종료하는 클래스 예시

→sp5-chap08/src/main/java/dbquery/DbQuery.java

package dbquery;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

public class DbQuery {
	private DataSource dataSource;

	public DbQuery(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	public int count() {
		Connection conn = null;
		try {
			conn = dataSource.getConnection(); // 풀에서 구함
			try (Statement stmt = conn.createStatement(); 
            			ResultSet rs = stmt.executeQuery("select count(*) from MEMBER")) {
				    rs.next();
				    return rs.getInt(1);
			    }
		} catch (SQLException e) {
			throw new RuntimeException(e);
		} finally {
			if (conn != null)
				try {
					conn.close(); // 풀에 반환
				} catch (SQLException e) {
				}
		}
	}

}

 

 

public int count() {
Connection conn = null;
    try {
            conn = dataSource.getConnection();  커넥션 풀에서 커넥션을 가져와 커넥션이 활성 상태가 된다.

... 중간 생략

 

} finally {
    if (conn != null)
        try {
            conn.close(); 커넥션 사용이 끝나 커넥션을 종료하면 커넥션을 끊지 않고 커넥션 풀에 반환한다.

                                      풀에 반환된 커넥션은 유휴(idle) 상태가 된다.
}

 

 커넥션 풀을 사용하는 이유는 성능 때문.

매번 생성할 때마다 연결시간이 소모되기 때문에 커넥션 풀을 사용하면 미리 생성했다 필요할 때 커넥션을 꺼내 쓰므로 구하는 시간이 줄어 전체 응답 시간도 짧아진다. 그래서 초기화할 때 최소 수준의 커넥션을 미리 생성하는 것이 좋다. (setlnitialSize(int)  사용)

 

☆ 커넥션 풀에 생성된 커넥션은 지속적으로 재사용된다. 

커넥션이 영원히 유지되는 것은 아니며, DBMS 설정에 따라 지정 시간 내에 쿼리를 실행하지 않으면 연결을 끊기도 한다. 만약 유휴 상태의 커넥션 있고 DBMS가 해당 커넥션과의 연결을 끊지만 이 커넥션은 풀에 여전히 남아있다. 이때 다시 이 커넥션을 풀에서 가져와 사용하면 연결이 끊긴 커넥션이기 때문에 익셉션이 발생한다.

 

익셉션을 방지하기 위해 커넥션 풀이 유효한지 주기적으로 검사해야 하는데 관련된 속성이 MinEvictableIdleTimeMillis, TimeBetweenEvictionRunsMillis가 있다.

 

아래 코드에서 코드를 자세히 살펴보면 아래와 같다. 

TestWhileIdle(true) 커넥션이 유효한지 여부를 검사하는 여부를 지정한다. 

MinEvictableIdleTimeMillis(60000 * 3) 최소 유휴 시간을 지정한다.

TimeBetweenEvictionRunsMillis(10 * 1000)  유휴 커넥션을 검사할 주기를 초단위로 지정한다.

 

@Bean(destroyMethod = "close") // close 메서드는 커넥션 풀에 보관된 Connetcion을 닫는다
	public DataSource dataSource() {
		DataSource ds = new DataSource(); // DataSource 객체 생성
		ds.setDriverClassName("com.mysql.jdbc.Driver"); // jdbc 드라이버 클래스 지정
		ds.setUrl("jdbc:mysql://localhost/spring5fs?characterEncoding=utf8"); // jdbc url 지정
		ds.setUsername("spring5"); // db연결할 때 사용할 사용자 계정과 암호 지정
		ds.setPassword("0000");    // db연결할 때 사용할 사용자 계정과 암호 지정
		ds.setInitialSize(2); // 커넥션 풀을 초기화할 때 생성할 초기 커넥션 개수 지정
		ds.setMaxActive(10); // 커넥션 풀에서 가져올 수 있는 최대 커넥션 개수 지정
		ds.setTestWhileIdle(true); // 커넥션 풀에 유휴 상태로 있는 동안 검사할지 여부 지정
		ds.setMinEvictableIdleTimeMillis(60000 * 3); // 최소 유휴 시간 3분
		ds.setTimeBetweenEvictionRunsMillis(10 * 1000); // 10초 추가
		return ds;
	}

 

댓글