반응형
스프링을 이용해 데이터베이스와 상호 작용
- 스프링은 JDBC 위에 추상 계층을 추가해 데이터베이스와 상호작용을 편리하게 만든다.
- 스프링은 하이버네이트와 마이바티스 등 ORM 프레임워크로 데이터베이스 상호 작용도 단순화 시킨다.
- MyBank
- 고객이 자신의 잔액 세부 정보를 살펴보고 금융거래 명세서(bankstatement)를 생성하며, 정기예금을 개설하고 체크북을 요청할 수 있게 해주는 인터넷 뱅킹 애플리케이션이다.
- BANK_ACCOUNT_DETAILS와 FIXED_DEPOSIT_DETAILS 테이블
BANK_ACCOUNT_DETAILS 1<-------------------1..* FIXED_DEPOSIT_DETAILS
- ACCOUNT_ID (PK) - FIXED_DEPOSIT_ID (PK)
- BALANCE_AMOUNT - ACCOUNT_ID (FK)
- LAST_TRANSCTION_TS - FD_CREATION_DATE
- AMOUNT
- TENURE
- ACTIVE
- 은행계좌정보는 BALANCE_AMOUNT 테이블, 정기예금정보는 FIXED_DEPOSIT_DETAILS 테이블
- 은행고객이 새 정기 예금을 개설하면 BANK_ACCOUNT_DETAILS의 BALANCE_AMOUNT에서 개설할 정기 예금 금액에 해당하는 잔액을 차감하고 FIXED_DEPOSIT_DETAILS 테이블에 새 정기 예금 정보를 저장한다.
BANK_ACCOUNT_DETAILS
- ACCOUNT_ID : 고객 은행 계좌를 유일하게 식별하는 계좌 식별자
- BALANCE_AMOUNT : 은행 계좡의 현재 잔액을 저장한다. 고객이 정기 예금을 개설하면 정기 예금액에 해당하는 금액을 컬럼에서 차감한다.
- LAST_TRANSCTION_TS : 계좌에서 마지막으로 거래가 일어난 날짜와 시간을 저장한다.
FIXED_DEPOSIT_DETAILS
- FIXED_DEPOSIT_ID : 정기예금을 유일하게 식별하는 정기 예금 식별자. MySQL 데이터베이스에서 자동으로 생성
- ACCOUNT_ID : 정기예금과 연관된 은행 계좌에 대한 외래키
- FD_CREATION_DATE : 정기예금이 만들어진 날짜
- AMOUNT : 정기예금금액
- TENURE : 정기 예금 만기를 저장한다 (단위-개월) 정기예금만기는 12개월이상 60개월 이하
- ACTIVE : 정기 예금이 현재 유효한지 여부를 표시한다.
- 스프링 JDBC 모듈로 MyBank 애플리케이션 개발
- 스프링 JDBC 모듈을 사용하면 연결을 열고 닫기, 트랜잭션 관리, 예외 처리 등의 저수준 세부사항을 스프링이 처리해주기 때문에 데이터 소스와의 상호작용이 단순해진다.
- 데이터 소스를 식별하는 javax.sql.DataSource 객체를 설정한다.
- 데이터베이스와 상호작용하기 위해 스프링 JDBC 모듈 클래스를 사용하는 DAO를 구현한다.
데이터 소스 설정
<context:property-placeholder location="classpath*:META-INF/spring/database.properties" />
<bean class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close" id="dataSource">
<property name="driverClassName" value="${database.driverClassName}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
</bean>
- dataSource 빈은 javax.sql.DataSource 객체이며, 데이터 소스로의 연결을 생성하는 팩토리 역할을 한다.
- BasicDataSource 클래스는 javax.sql.DataSource 인터페이스를 구현한 클래스로 연결 풀링 기능을 지원한다.
- BasicDataSource 클래스의 close 메서드는 풀에 있는 모든 유휴 연결을 닫는다.
- BasicDataSource 클래스의 빈 정의에서 destroy-method 속성이 close로 지정되어 있기 때문에 스프링 컨테이너가 dataSource 빈 인스턴스를 제거할 때 풀에 있는 유휴 연결도 모두 닫힌다.
자바 EE 환경에서 데이터 소스 설정
- 애플리케이션 서버에 배포하는 엔터프라이즈 애플리케이션을 개발한다면, 전형적인 경우 javax.sql.DataSource 객체를 애플리케이션 서버의 JNDI에 등록할 것이다.
- 스프링 jee 스키마의 엘리먼트를 사용해 JNDI에 바인드된 데이터 소스를 스프링 빈으로 사용할 수 있다.
<jee:jndi-lookup jndi-name="java:comp/env/jdbc/bankAppDb" id="dataSource" />
- jndi-name 속성은 javax.sql.dataSource 객체를 JNDI에 바인딩 할때 사용한 이름이고, id속성은 ApplicationContext에 빈을 등록할 때 사용하는 이름이다.
@Bean(destroyMethod="")
public DataSource dataSource() throws NamingException {
JndiLocatorDelegate delegate = JndiLocatorDelegate.creatDefaultResourceRefLocator();
return delegate.lookup("jdbc/bankAppDb", DataSource.class);
}
- JndiLocatorDelegate의 creatDefaultResourceRefLocator 메서드는 모든 JNDI 이름 검색에 자동으로 java:comp/env 접두사를 붙이라고 지정한다.
- JndiLocatorDelegate의 lookup 메서드는 실제 JNDI 검색을 수행해 주어진 이름을 찾는다.
- lookup 메서드의 두번재 인수는 JNDI 검색에서 얻어올 객체의 타입을 지정한다.
- destroyMethod 속성값을 ""로 설정한다. JNDI에 바인딩된 DataSource 객체의 생애주기는 애플리케이션 서버가 관리하므로, 스프링 컨테이너는 자신이 종료될 때 DataSource 객체의 정리 메서드를 호출하면 안된다.
스프링 JDBC 모듈 클래스를 사용하는 DAO
- 스프링 JDBC 모듈은 데이터베이스와 상호작용을 쉽게 만들어주는 여러 클래스를 제공한다.
JdbcTemplate
- JdbcTemplate 클래스는 Connection, Statement, ResultSet 객체를 관리하고, JDBC 예외를 잡아서 (IncorrectResultSetColumnCountException, CannotGetJdbcCoonnectionException)로 변환하며, 배치 연산을 수행하는 등의 일을 한다.
- JdbcTemplate은 javax.sql.DataSource를 둘러싼 래퍼 역할을 한다.
- JdbcTemplate 인스턴스는 보통 데이터베이스 연결을 얻을 때 사용할 javax.sql.DataSource 객체로 초기화 한다.
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
- JdbcTemplate 클래스가 javax.sql.DataSource 객체를 참조하는 dataSource 프로퍼티를 정의한 것이다.
- JdbcTemplate 인스턴스는 스레드 안전하다(thread-safe) 애플리케이션의 여러 DAO가 같은 JdbcTemplate 클래스 인스턴스를 통해 데이터베이스와 상호작용해도 된다는 뜻이다.
@Repository(value = "fixedDepositDao")
public class FixedDepositDaoImpl implements FixedDepositDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public int createFixedDeposit(final FixedDepositDetails fdd) {
final String sql = "insert into fixed_deposit_details(account_id, fd_creation_date, amount, tenure, active) "
+ "values(?, ?, ?, ?, ?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con)
throws SQLException {
PreparedStatement ps = con.prepareStatement(sql,
new String[] { "fixed_deposit_id" });
ps.setInt(1, fdd.getBankAccountId());
ps.setDate(2, new java.sql.Date(fdd.getFdCreationDate()
.getTime()));
ps.setInt(3, fdd.getFdAmount());
ps.setInt(4, fdd.getTenure());
ps.setString(5, fdd.getActive());
return ps;
}
}, keyHolder);
return keyHolder.getKey().intValue();
}
public FixedDepositDetails getFixedDeposit(final int fixedDepositId) {
final String sql = "select * from fixed_deposit_details where fixed_deposit_id = :fixedDepositId";
SqlParameterSource namedParameters = new MapSqlParameterSource(
"fixedDepositId", fixedDepositId);
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters,
new RowMapper<FixedDepositDetails>() {
public FixedDepositDetails mapRow(ResultSet rs, int rowNum)
throws SQLException {
FixedDepositDetails fdd = new FixedDepositDetails();
fdd.setActive(rs.getString("active"));
fdd.setBankAccountId(rs.getInt("account_id"));
fdd.setFdAmount(rs.getInt("amount"));
fdd.setFdCreationDate(rs.getDate("fd_creation_date"));
fdd.setFixedDepositId(rs.getInt("fixed_deposit_id"));
fdd.setTenure(rs.getInt("tenure"));
return fdd;
}
});
}
}
NamedParameterJdbcTemplate
- JdbcTemplate에서는 ? 위치 지정자를 사용해 SQL 문안에 파라미터를 지정한다.
- NamedParameterJdbcTemplate은 JdbcTemplate 인스턴스를 감싸는 래퍼로, SQL 문안에서 ? 대신 파라미터 이름을 사용할 수 있다.
<bean id="namedJdbcTemplate"
class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource" />
</bean>
SimpleJdbcInsert
- SimpleJdbcInsert 클래스는 데이터베이스 메타데이터를 활용해 테이블에 로우를 삽입하는 기본 SQL 삽입문을 쉽게 쓸 수 있다.
@Repository(value = "bankAccountDao")
public class BankAccountDaoImpl implements BankAccountDao {
private SimpleJdbcInsert insertBankAccountDetail;
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private void setDataSource(DataSource dataSource) {
this.insertBankAccountDetail = new SimpleJdbcInsert(dataSource).withTableName("bank_account_details")
.usingGeneratedKeyColumns("account_id");
}
@Override
public int createBankAccount(final BankAccountDetails bankAccountDetails) {
Map<String, Object> parameters = new HashMap<String, Object>(2);
parameters.put("balance_amount", bankAccountDetails.getBalanceAmount());
parameters.put("last_transaction_ts",
new java.sql.Date(bankAccountDetails.getLastTransactionTimestamp().getTime()));
Number key = insertBankAccountDetail.executeAndReturnKey(parameters);
return key.intValue();
}
public void subtractFromAccount(int bankAccountId, int amount) {
jdbcTemplate.update("update bank_account_details set balance_amount = ? where account_id = ?", amount,
bankAccountId);
}
}
- usingGeneratedKeyColumns 메서드는 자동생성 키를 폼하한 테이블 컬럼의 이름을 설정한다.
- SimpleJdbcInsert 클래스가 내부적으로 JdbcTemplate을 사용해 실제 SQL 삽입 연산을 수행한다.
public class BankApp {
private static Logger logger = LogManager.getLogger(BankApp.class);
public static void main(String args[]) throws Exception {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
"classpath:META-INF/spring/applicationContext.xml");
BankAccountService bankAccountService = context.getBean(BankAccountService.class);
BankAccountDetails bankAccountDetails = new BankAccountDetails();
bankAccountDetails.setBalanceAmount(1000);
bankAccountDetails.setLastTransactionTimestamp(new Date());
int bankAccountId = bankAccountService.createBankAccount(bankAccountDetails);
logger.info("Created bank account with id - " + bankAccountId);
FixedDepositService fixedDepositService = context.getBean(FixedDepositService.class);
FixedDepositDetails fdd = new FixedDepositDetails();
fdd.setActive("Y");
fdd.setBankAccountId(bankAccountId);
fdd.setFdCreationDate(new Date());
fdd.setFdAmount(500);
fdd.setTenure(12);
int fixedDepositId = fixedDepositService.createFixedDeposit(fdd);
logger.info("Created fixed deposit with id - " + fixedDepositId);
logger.info(fixedDepositService.getFixedDeposit(fixedDepositId));
context.close();
}
}
- 저장프로시저나 함수실행, SimpleJdbcCall 클래스를 사용해 저장 프로시저나 함수를 실행할 수 있다.
- 일괄갱신으로 JdbcTemplate의 batchUpdate 메서드를 사용하면 같은 PreparedStatement를 통해 여러 데이터베이스 갱신 호출을 일괄 실행할 수 있다.
- 객체지향적인 방식으로 관계형 데이터베이스 접근으로 MappingSqlQuery를 사용하면 ResultSet가 반환한 각 로우를 객체에 매핑할 수 있다.
- 내장 데이터베이스 인스턴스 설정으로 스프링 jdbc 스키마를 사용해 HSQL, H2 등 데이터베이스 인스턴스를 만들고, 데이터베이스 인스턴스를 javax.sql.DataSource 타입의 빈으로 스프링 컨테이너에 등록할 수 있다.
하이버네이트로 MyBank 애플리케이션 개발
- 스프링 ORM 모듈은 하이버네이트, 자바영속성 API(JPA), 자바 데이터 오브젝트와 통합할 수 있게 한다.
SessionFactory 인스턴스 설정
- SessionFactory는 하이버네이트 Session 객체를 생성하는 팩토리다.
- 영속적인 엔티티에 대한 생성, 읽기, 삭제, 갱신 연산을 수행하기 위해 DAO가 사용하는 객체가 Session 객체다.
- 스프링 org.springframework.orm.hibernate5.LocalSessionFactoryBean(FactoryBean 구현)은 DAO 클래스가 Session 인스턴스를 얻을 때 사용하는 SessionFactory 인스턴스를 만든다.
<context:property-placeholder
location="classpath*:META-INF/spring/database.properties" />
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="sample.spring" />
</bean>
<bean class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close" id="dataSource">
<property name="driverClassName" value="${database.driverClassName}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
</bean>
- dataSource 프로퍼티는 javax.sql.DataSource 타입 빈에 대한 참조를 지정한다.
- packagesToScan 프로퍼티는 스프링이 영속적 클래스를 찾는 대상 패키지을 지정한다.
- sample.spring 패키지에서 찾아 org.springframework.orm.hibernate5.LocalSessionFactoryBean을 사용해 자동으로 @Entity 애너테이션을 감지하도록 설정한다.
- packagesToScan 대신 annotatedClasses를 사용해서 모든 영속적 클래스를 직접 지정할 수도 있다.
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>sample.spring.bankapp.domain.FixedDepositDetails</value>
</list>
</property>
</bean>
하이버네이트 API를 사용하는 DAO
@Repository(value = "fixedDepositDao")
public class FixedDepositDaoImpl implements FixedDepositDao {
@Autowired
@Qualifier("sessionFactory")
private SessionFactory sessionFactory;
public int createFixedDeposit(final FixedDepositDetails fixedDepositDetails) {
sessionFactory.getCurrentSession().save(fixedDepositDetails);
return fixedDepositDetails.getFixedDepositId();
}
public FixedDepositDetails getFixedDeposit(final int fixedDepositId) {
String hql = "from FixedDepositDetails as fixedDepositDetails where fixedDepositDetails.fixedDepositId ="
+ fixedDepositId;
return (FixedDepositDetails) sessionFactory.getCurrentSession()
.createQuery(hql).uniqueResult();
}
}
- SessionFactory 인스턴스가 FixedDepositDaoImpl 인스턴스에 자동 연결된다.
- FactoryBean 구현인 LocalSessionFactoryBean이 SessionFactory의 getCurrentSession 메서드를 호출해서 Session 인스턴스를 얻는다.
- getCurrentSession 메서드 호출이 현재 트랜잭션이나 현재 스레드와 연관된 Session 객체를 반환한다.
- 스프링에게 트랜잭션 관리를 맡길 때는 getCurrentSession 메서드를 호출하는 것이 유용하다.
스프링을 통한 트랜잭션 관리
- 스프링의 트랜잭션 관리 추상화를 사용해 명시적으로 트랜잭션 시작, 종료, 커밋을 한다.
- 선언적인 트랜잭션 관리를 사용할 때는 스프링 @Transactional을 사용해 트랜잭션 안에서 실행하는 메서드를 지정한다.
프로그램을 사용한 트랜잭션 관리
- TransactionTemplate 클래스를 사용하거나 PlatformTransactionManager 인터페이스 구현을 사용하면 프로그램으로 트랜잭션을 관리할 수 있다.
- TransactionTemplate 클래스는 트랜잭션 시작과 커밋을 처리함으로써 트랜잭션 관리를 단순하게 해준다. (TransactionCallback 인터페이스만 구현하면 된다.)
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="txManager"/> <property name="isolationLevelName" value="ISOLATION_READ_UNCOMMITTED" /> <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED" /> </bean>
- TransactionTemplate의 transactionManager 프로퍼티는 트랜잭션 관리를 책임질 스프링 PlatformTransactionManager 구현을 참조한다.
- isolationLevelName 프로퍼티는 트랜잭션 매니저가 관리할 트랜잭션 격리 수준을 결정한다.
- propagationBehaviorName 프로퍼티는 트랜잭션 전파 방식을 설정한다.
- 스프링은 애플리케이션에서 사용할 데이터 접근 기술에 맞춰 선택할 수 있도록 몇가지 내장 PlatformTransactionManager 구현을 제공한다.
- DataSourceTransactionManager는 데이터베이스와 상호작용하기 위해 단순한 JDBC를 사용하는 애플리케이션에 적합하다.
- HibernateTransactionManager는 데이터베이스와 상호작용하기 위해 하이버네이트 Session을 사용하는 경우
- JpaTransactionManager 는 데이터 접근에 JPA의 EntityManager를 사용하는 경우
- HibernateTransactionManager, JpaTransactionManager는 데이터베이스 상호작용에 단순한 JDBC를 사용하는 경우도 지원한다.
- transactionManager 프로퍼티는 DataSourceTransactionManager 인스턴스를 참조한다. (단순 JDBC를 데이터 접근에 사용하기 때문)
@Service(value = "fixedDepositService")
public class FixedDepositServiceImpl implements FixedDepositService {
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
@Qualifier(value = "fixedDepositDao")
private FixedDepositDao myFixedDepositDao;
@Autowired
private BankAccountDao bankAccountDao;
@Override
public int createFixedDeposit(final FixedDepositDetails fixedDepositDetails)
throws Exception {
// -- create fixed deposit
transactionTemplate
.execute(new TransactionCallback<FixedDepositDetails>() {
@Override
public FixedDepositDetails doInTransaction(
TransactionStatus status) {
try {
myFixedDepositDao.createFixedDeposit(fixedDepositDetails);
bankAccountDao.subtractFromAccount(
fixedDepositDetails.getBankAccountId(), fixedDepositDetails.getFdAmount());
} catch (Exception e) {
status.setRollbackOnly();
}
return fixedDepositDetails;
}
});
return fixedDepositDetails.getFixedDepositId();
}
@Override
public FixedDepositDetails getFixedDeposit(int fixedDepositId) {
return myFixedDepositDao.getFixedDeposit(fixedDepositId);
}
}
public class BankApp {
private static Logger logger = LogManager.getLogger(BankApp.class);
public static void main(String args[]) throws Exception {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
"classpath:META-INF/spring/applicationContext.xml");
BankAccountService bankAccountService = context
.getBean(BankAccountService.class);
FixedDepositService fixedDepositService = context
.getBean(FixedDepositService.class);
BankAccountDetails bankAccountDetails = new BankAccountDetails();
bankAccountDetails.setBalanceAmount(1000);
bankAccountDetails.setLastTransactionTimestamp(new Date());
int bankAccountId = bankAccountService
.createBankAccount(bankAccountDetails);
logger.info("Created bank account (with balance amount 1000) with id - "
+ bankAccountId);
FixedDepositDetails fdd = new FixedDepositDetails();
fdd.setActive("Y");
fdd.setFdAmount(1500);
fdd.setBankAccountId(bankAccountId);
fdd.setFdCreationDate(new Date());
fdd.setTenure(12);
int fixedDepositId = fixedDepositService.createFixedDeposit(fdd);
logger.info("Created fixed deposit (for 1500 amount) with id - "
+ fixedDepositId
+ ". Check FIXED_DEPOSIT_DETAILS table to verify that the transaction was rolled back.");
context.close();
}
}
선언적 트랜잭션 관리
- 프로그램을 사용해 트랜잭션을 관리하면 애플리케이션 코드와 스프링에만 있는 클래스가 밀접하게 결합된다.
- 선언적 트랜잭션 관리를 사용하려면 메서드나 클래스에 스프링의 @Transactional을 설정한다.
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
- 엘리먼트의 transaction-manager 속성을 트랜잭션관리에 사용하기 위해 PlatformTransactionManager 구현에 대한 참조를 지정한다.
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
- 하이버네이트 ORM을 사용하는 경우 HibernateTransactionManager 트랜잭션 관리를 사용한다.
import org.springframework.transaction.annotation.Transactional;
@Service(value = "fixedDepositService")
public class FixedDepositServiceImpl implements FixedDepositService {
@Autowired
private FixedDepositDao myFixedDepositDao;
@Autowired
private BankAccountDao bankAccountDao;
@Override
@Transactional
public int createFixedDeposit(FixedDepositDetails fdd) throws Exception {
// -- create fixed deposit
bankAccountDao.subtractFromAccount(fdd.getBankAccountId()
.getAccountId(), fdd.getFdAmount());
return myFixedDepositDao.createFixedDeposit(fdd);
}
@Override
@Transactional
public FixedDepositDetails getFixedDeposit(int fixedDepositId) {
return myFixedDepositDao.getFixedDeposit(fixedDepositId);
}
}
- @Transactional을 createFixedDeposit 메서드에 설정한다.
- createFixedDeposit을 트랜잭션 안에서 실행한다는 뜻이다. transaction-manager 속성을 통해 지정한 트랜잭션 매니저를 사용해 트랜잭션을 관리한다.
- RuntimeException 예외가 발생하면 트랜잭션이 자동으로 롤백된다.
애플리케이션에 여러 트랜잭션 매니저가 정의된 경우, @Transactional 애너테이션의 transactionManager 속성을 통해 PlatformTransactionManager 구현의 빈 이름을 지정할 수 있다.
@Service
public class SomeServiceImpl implements SomeService {
@Transactional(transactionManager="tx1")
public int methodA() {}
@Transactional(transactionManager="tx2")
public int methodB() {}
}
<tx:annotation-driven/>
<bean id="tx1"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="tx2"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
- 엘리먼트에서는 transaction-manager 속성을 지정하지 않는다.
- @Transactional 애너테이션의 transactionManager 속성이 트랜잭션을 관리할 트랜잭션 매니저를 지정한다.
스프링의 JTA 지원
- 트랜잭션 내부에 트랜잭션이 필요한 자원이 여러 존재할 경우 JTA를 활용해 트랜잭션을 관리한다.
- JTA 트랜잭션을 관리할 때 사용할 수 있도록 JtaTransactionManager 클래스를 제공한다.
- 대부분 애플리케이션 서버 환경에서는 JtaTransactionManager가 적합할 것이다.
- 하지만 스프링은 서버의 개별적인 특성을 활용해 JTA 트랜잭션을 관리할 수 있도록 벤더별 JtaTransactionManager 구현도 제공한다.
- 자원별 트랜잭션 매니저(DataSourceTransactionManager, HibernateTransactionManager, JmsTransactionManager)는 ResourceTransactionManager 인터페이스를 구현하며, 한 가지 대상 자원과 연결된 트랜잭션만 관리한다. DataSource, SessionFactory, EntityManagerFactory와 관련된 트랜잭션을 관리한다.
- 스프링 tx 스키마는 애플리켕션이 배포된 애플리케이션 서버를 자동 감지해서 적당한 JTA 트랜잭션 매니저를 설정해주는 엘리먼트를 제공한다.
자바 기반을 사용하는 MyBank 개발
@Configuration
@PropertySource("classpath:/META-INF/database.properties")
@EnableTransactionManagement
public class DatabaseConfig {
@Autowired
private Environment env;
@Bean(destroyMethod = "close")
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(env.getProperty("database.driverClassName"));
dataSource.setUrl(env.getProperty("database.url"));
dataSource.setUsername(env.getProperty("database.username"));
dataSource.setPassword(env.getProperty("database.password"));
return dataSource;
}
@Bean
public SessionFactory sessionFactory(DataSource dataSource) {
LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(dataSource);
builder.scanPackages("sample.spring");
builder.setProperty("hibernate.show_sql", "true");
builder.setProperty("hibernate.id.new_generator_mappings", "false");
return builder.buildSessionFactory();
}
@Bean
public PlatformTransactionManager platformTransactionManager(SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}
}
- @EnableTransactionManagement 애너테이션은 스프링 tx 스키마의 엘리먼트와 같은 역할을 한다.
- @EnableTransactionManagement는 @Transactional 애너테이션을 지원을 활성화 한다.
- @Bean 설정한 platformTransactionManager 메서드는 스프링이 트랜잭션 관리에 사용할 HibernateTransactionManager 인스턴스를 반환한다.
배워서 바로 쓰는 스프링프레임워크
애시시 사린, 제이 샤르마 지음
오현석 옮김
반응형