DB의 연동 방법 5가지
1. DB서버 직접 이용
2. JDBC 사용
3. 스프링 JDBC 사용
4. MyBatis와 같은 프레임워크 사용
5. JPA 사용
스프링에서 DB 연동을 위해 3개의 모듈을 추가합니다.(Mysql 기준)
spring-jdbc : JdbcTemplate 등 JDBC 연동에 필요한 기능을 제공합니다.
tomcat-jdbc : DB 커넥션 풀 기능을 제공합니다.
mysql-connector-java : MySql연결에 필요한 드라이버를 제공합니다.
트랜잭션을 위해선 spring - tx가 필요한데 spring-jdbc에 의존되어져 있습니다.
DataSource
JDBC API는 DataSource를 이용하여 DB연결을 정의합니다.
conn = DataSource.getConnection()을 사용하여 Connection 객체를 구할 수 있습니다.
DB연동에 사용할 DataSource를 스프링 Bean으로 등록하여 DB연동 기능을 가진 Bean 객체는 DataSource를 주입받아 사용합니다.
DataSource클래스는 tomcat-jdbc모듈에서 제공하고 이 클래스를 Bean으로 등록하여 사용합니다.
이때 setter를 이용하여 Drivername, url, name, password와 커넥션 풀을 설정합니다.
JDBCTemplate
스프링 사용시 JDBCTemplate을 사용하여 쿼리를 실행합니다.
- select쿼리 실행을 위한 query()
List<T> query(String query, RowMapper<T> rowMapper, Object args)
query()메소드를 실행시키면 JDBCTemplate은 커넥션을 준비해서 Statement객체를 만들고 매개변수에 주었던 query를 주입시킵니다.
이 Statement가 DB서버에서 동작이 되고 결과로 나오는 ResultSet객체는 RowMapper의 mapRow메소드를 동작시켜 JDBCTemplate안에서 커서를 next()하여 동작시키고 각 행을 mapRow메소드에 지정한 규칙대로 객체의 멤버에 이동시키고 그 객체들을 모아 리스트로 만들어 리턴 시킵니다.
public interface RowMapper<T>{
T mapRow(ResultSet rs, int row Num) throw SQLException;
}
RowMapper의 mapRow()메소드는 ResultSet에서 한 행의 데이터를 읽어와 자바 객체로 변환하는 매퍼 기능을 구현합니다. 이는 임의 클래스나 람다식으로 구현할 수 있고 RowMapper를 구현한 클래스를 만들게 되면 동일한 RowMapper를 여러곳에서 사용이 가능합니다.
쿼리 실행 결과가 행이 한개일 경우에는 queryForObject메소드를 사용하는데 행이 한개가 아닐시 익셉션이 발생하게 됩니다.
- insert, update, delete 쿼리 실행을 위한 update()
int update(String query, Object args)
int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder)
쿼리에서 사용할 값을 인자로 전달하지 않고 미리 세터를 이용하여 사용하는 방법으로 PreparedStatementCreator를 사용하는데 자동으로 생성되는 키값을 구할때 주로 사용합니다.
KeyHolder를 먼저 준비하고 PreparedStatementCreator를 매개변수로 하는 update를 실행하는데 preparedStatementCreator의 createPreparedStatement메소드를 구현하면 preparedStatement를 생성해 쿼리문에 파라미터를 set합니다. 이때 auto-increment되는 컬럼값을 prepareStatement 생성시 배열의 형태로 매개변수에 줍니다. 그러면 update의 매개변수로 준 keyHolder에 이 값이 들어가게 되고 keyHolder를 이용해 값을 get 하고 set하여 사용합니다.
스프링은 JDBC-API를 사용하는데 익셉션이 발생할 경우 DataAceesExeption으로 변환시킵니다.
그 이유는 4가지가 있습니다.
1. 연동기술에 상관없이 동일한 익셉션이 나오게 하기 위해
2. 익셉션 이름만 보고 익셉션의 성격을 알 수 있게 하기 위해
3. 트랜잭션 매니저를 통해 트랜잭션을 수행할 때 롤백 되게 하기 위해
4. RuntimeException이므로 필요한 경우에만 처리하기 위해
커넥션 풀
커넥션 생성하는 기간은 매우 길고 새로운 요청 때 마다 커넥션을 생성하는 것은 과부하가 많이 듭니다.
커넥션 풀은 일정 개수의 커넥션을 미리 만들어 두고 요청시 커넥션 풀에서 커넥션을 가져와 사용하고 다시 풀에 반납하는 기법입니다.
커넥션을 요청 때마다 생성하지 않고 동시 사용자가 많더라도 커넥션을 일정 개수 유지하기 떄문에 부하를 줄이고 많은 동시 사용자를 처리할 수 있습니다.
DataSource에서 커넥션 풀 기능을 제공합니다.
커넥션 풀에서 요청시 리턴된 커넥션은 활성 상태이고 반납되면 사라지지 않고 유휴상태가 되어 다음 요청을 기다립니다.
커넥션은 지속적으로 사용되지만 영원히 지속되는 것은 아니므로 DBMS 설정에 따라 일정시간 쿼리를 실행하지 않을 경우 끊키게 되고 이 상태에서 요청시 익셉션이 발생하게 됩니다. 그러므로 주기적인 검사가 필요합니다.
setMaxActive(int) : 활성 가능한 최대 커넥션 수 지정
setMaxWait(int) : 최대 활성 커넥션 수를 넘어 요청시에 대기시간 설정, 시간이 지나면 익셉션 발생
setInitailSize(int) : 초기화시 미리 만들어 놓을 커넥션의 수
setTestWhileIdle(ture) : 유휴 커넥션 검사
트랜잭션
여러 쿼리를 논리적으로 하나의 작업으로 묶어주는 기능입니다.
하나의 작업이라도 실패하면 기존 상태로 되돌리는 롤백이 있으며 모든 쿼리가 성공하면 DB에 반영하는 커밋을 수행하게 됩니다.
기존 JDBC에서 직접 사용시엔 setAutoCommit(false)로 하여 트랜잭션을 시작했고 이후 commit()과 rollback()을 사용하여 트랜잭션을 사용했습니다.
하지만 이 방식은 코드를 누락하기 쉬우며 구조적으로 반복되는 문제점이 있었습니다.
스프링에서는 트랜잭션 범위에서 실행할 메소드에 @Transactional 어노테이션을 붙여 처리합니다.
이를 위해서는 두가지 설정이 필요합니다.
설정 클래스에서는 @EnableTransactionManagement를 사용해 @Transactional을 활성화 하고 구현 기술에 상관 없도록 스프링이 제공하는 매니저 인터페이스 PlatformTransactionManager 객체를 Bean 객체로 등록합니다.
연동 기술에 따라 매니저가 다른데 JDBC는 DataSourceTransactionManager입니다.
객체를 생성한 이후 datasource를 주입합니다.
트랜잭션은 공통 기능 중 하나이기 때문에 @Transactional 어노테이션은 트랜잭션을 처리하기 위해 내부적으로 AOP를 이용하여 프록시를 통해 처리가 됩니다.
@EnableTransactionManagement 사용시 @Transactional 어노테이션이 적용된 Bean 객체에 해당하는 프록시 객체를 생성하여 사용합니다. 이때 매니저가 미리 만들어져 있기 때문에 Joinpoint로 부르는것이 아닌 프록시에서 직접 핵심 기능인 쿼리 기능을 수행합니다.
=> 공통 기능에서 트랜잭션을 수행하고 핵심 기능에서 쿼리 기능을 수행하고 성공할 경우 커밋되고 아닐 경우 익셉션이 발생해 롤백됩니다.
런타임 익셉션이 아닐 경우 롤백하지 않기 때문에 DataAcessException으로 변환해서 사용합니다.
@Transactioinal 속성에 rollbackFor=SQLException.class로 지정할 경우 SQL익셉션일때도 롤백을 하고 여러개는 배열로 지정합니다.
Propagation의 REQUIRED는 메소드를 수행하는데 트랜잭션이 필요한 것을 말하며 현재 진행중인 트랜잭션이 존재하면 해당 트랜잭션을 사용하고 존재하지 않으면 새로운 트랜잭션을 생성합니다.
'학습(구) > Spring 요약' 카테고리의 다른 글
Spring - 요청 경로 매핑과 요청 파라미터, 리다이렉트 (0) | 2020.10.19 |
---|---|
Spring - MVC (0) | 2020.10.19 |
Spring - AOP (0) | 2020.10.18 |
Spring - Bean라이프사이클과 스코프 (0) | 2020.10.16 |
Spring - 자동 Bean 등록 (0) | 2020.10.16 |