데이터 소스는 애플리케이션이 데이터베이스에 접근하기 위한 추상화된 연결 방식, 즉 커넥션(java.sql.Connection)을 제공하는 역할을 한다.
Commons DBCP, Tomcat JDBC Connection Pool 과 같이 서드파티가 제공하는 데이터 소스나 DriverManagerDataSource 같이 스프링 프레임워크가 테스트 용도로 제공하는 데이터 소스를 빈으로 등록해서 사용하는 방식.
데이터베이스 접속을 위한 사용자 ID와 패스워드, 접속 대상 URL 같은 데이터베이스 접속 정보를 애플리케이션이 직접 관리하고 데이터 소스에 설정해야한다.
어플리케이션 서버가 정의한 데이터 소스를 JNDI(Java Naming and Directory Interface)를 통해 가져와서 사용하는 방식.
데이터베이스에 접속하기 위한 각종 정보를 애플리케이션 서버에서 관리하기 때문에 애플리케이션으로부터 데이터베이스 관련 정보를 분리해낼 수 있으며 애플리케이션 서버가 제송하는 각종 관리 기능도 활용 가능.
HSQLDB, H2, Apache Derby 같은 내장형 데이터베이스에 접속하는 데이터 소스를 말한다.
<dependency>
<groupId>org.springframwork</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
@Configuration
@PropertySource("classpath:jdbc.properties")
public class PoolingDataSourceConfig {
@Bean(destroyMethod = "close")
public DataSource dataSource(
@Value("${database.driverClassName}") String driverClassName,
@Value("${database.url}") String url,
...
) {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
...
return dataSource;
}
SQL의 실제 내용과는 상관없이 공통적이면서도 반복적으로 수행되는 JDBC 처리를 개발자가 직접 구현하는 대신 프레임워크가 대행하는 기능 제공.
ex) 커넥션의 연결과 종료, SQL 문의 실행, SQL 문 실행 결과 행에 대한 반복 처리, 예외 처리
개발자가 구현할 부분은 다음과 같은 내용만 남는다.
SQL 문 정의, 파라미터 설정, ResultSet에서 결과를 가져온 후, 각 레코드별로 필요한 처리
NamedParameterJdbcTemplate 클래스는 데이터 바인딩 시 파라미터 이름을 사용할 수 있어서 '?'를 사용할 때보다 좀 더 직관적으로 데이터를 다룰 수 있게 해준다.
개발자가 직접 JdbcTemplate을 만들어 쓰기보다는 DI 컨테이너가 만든 JdbcTemplate을 @Autowired 애너테이션으로 주입받아서 쓰는 것이 더 일반적.
많이 활용되는 주요 메서드
@Component
public class JdbcRoomNameDao {
@Autowired
NamaedParameterJdbcTemplate namaedParameterJdbcTemplate;
public String findRoomNameById(String roomId) {
String sql = "SELECT room_name FROM room WHERE room_id = :roomId";
Map<String, Object> params = new HashMap<String, Object>();
params.put("roomId", roomId);
return namedParameterJdbcTemplate.queryForObject(sql, params, String.class);
}
}
Room room = new Room("A001", "임원 회의실", 10);
BeanPropertySqlParameterSource map = new BeanPropertySqlParameterSource(room);
queryForList는 개수가 0인 빈 List 반환. 그 밖의 메서드에서는 EmptyResultDataAccessException 예외 던짐.
데이터를 변경할 때는 변경 대상이 0건인 경우라도 예외를 발생시키지 않는다.
스프링 트랜잭션 처리 중심 PlatformTransactionManager 인터페이스.
대표적 구현 클래스
DataSourceTransactionManager : JDBC 및 마이바이트 등의 JDBC 기반 라이브러리로 데이터베이스에 접근하는 경우
HibernateTransactionManager : 하이버네이트를 이용해 데이터베이스에 접근하는 경우
..
단일 데이터 저장소에 대한 여러 조작을 하나의 논리적 단위로 처리하고 싶을 때.
DataSoureTransacitonManager 사용. 빈 id는 transactionManager로 하는 것이 좋다.
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven />
여러 데이터 저장소에 걸쳐서 적용되는 트랜잭션. 여러 데이터베이스를 사용하되, 각 데이터베이스에서 각각의 조작을 수행하고 그 조작들을 하나의 트랜잭션으로 묶어 모두 성공하거나 모두 실패한 것으로 처리해야 하는 경우.
JTA(Java Transaction API)라는 Java EE 사양으로 표준화됨. 어플리케이션 서버가 JTA의 구현 클래스를 제공.
JtaTransactionManager를 사용.
<tx:jta-transaction-manager />
@Transactional 사용
@Transactional 애너테이션을 빈의 public 메서드에 추가하여 대상 메서드의 시작 종료에 맞춰 트랜잭션을 시작, 커밋할 수 있다.
비검사 예외가 발생하서 메서드 처리가 중단될 때 자동으로 롤백된다.
@Transactional 속성
value : 여러 트랜잭션 관리자를 이용하는 경우 이용하는 트랜잭션 관리자의 qualifier를 지정.
transactionManager : value의 별칭
propagation : 트랜잭션 전파 방식 지정
isolation : 트랜잭션 격리 수준 지정
timeout : 트랜잭션 제한시간 지정. 기본값은 -1
readOnly : 트랜잭션 읽기전용 플래그
rollbackFor : 여기에 지정한 예외가 발생하면 트랜잭션 롤백. 지정하지 않으면 RuntimeException 같은 비검사 예외 발생시 롤백.
noRollbackFor : 여기 지정한 예외가 발생해도 롤백하지 않는다.
설정
@EnableTransactionManagement 애너테이션을 컨피규레이션 클래스에 부여해야한다.
@Configuration
@EnableTransactionManagement
public class TransactionManagerConfig {
@Autowired
DataSource dataSource;
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource);
}
}
@Transactional 의 isolation 속성 등에서 설정 가능.
스프링 프레임워크 트랜잭션 격리 수준
DEFAULT : 사용하는 DB의 기본 격리 수준 사용
READ_UNCOMMITTED : Dirty read, unrepeatable read, phantom read가 발생. 커밋되지 않은 변경 데이터를 다른 트랜잭션에서 참조하는 것을 허용
READ_COMMITTED : Dirty read를 방지, unrepeatable read, phantom read 발생. 커밋되지 않은 변경 데이터를 다른 트랜잭션에서 참조하는 것을 금지.
REPEATABLE_READ : Dirty read, unrepeatable read 방지, phantom read 발생.
SERIALIZABLE : Dirty read, unrepeatable read, phantom read 방지
트랜잭션 관리 대상이 되는 메서드 안에서 또 다른 트랜잭션 관리 대상이 되는 메서드를 호출한 경우 트랜잭션의 전파 방식을 고려해야 한다.
스프링 프레임워크 트랜잭션 전파 방식
REQUIRED : 이미 만들어진 트랜잭션이 존재한다면 해당 트랜잭션 관리 범위 안에 함께 들어감
REQUIRES_NEW : 이미 만들어진 트랜잭션 범위로 들어가지 않고 반드시 새로운 트랜잭션을 만든다.
MANDATORY : 이미 만들어진 트랜잭션 범위 안에 들어가야 한다. 아니면 예외가 발생
NEVER : 트랜잭션 관리 하지 않는다. 이미 만들어진 트랜잭션이 있다면 예외 발생
..
DataAccessException은 RuntimeException의 자식 클래스로 구현되어 비검사 예외.
JPA, JDBC, 하이버네이트와 같은 데이터 접근 방법의 차이와 Oracle, PostgreSQL, H2 와 같은 데이터베이스의 차이에서 발생하는 데이터 접근 예외는 모두 각자가 정의한 모양을 하고 있다.
스프링의 데이터 접근 기능에는 각 제품별로 정의된 서로 다른 예외 클래스를 데이터 접근 방법과 특정 제품에 종속되지 않는 공통적인 예외 클래스로 변환하는 기능이 있다.
예를들어 Oracle을 사용할 때 무결성 제약 오류가 발생하면 Oracle의 오류 코드 'ORA-00001'이 발생한다. 스프링에서는 DataIntegrityViolationException 으로 일관되게 처리한다.