토비의 스프링을 보고 정리한 내용이다. 서비스 추상화에 대해서 포스팅 하고자 한다.
문제가 있을 시, lshn1007@hanyant.ac.kr 로 메일 바랍니다.
Connection c = dataSource.getConnection();
c.setAutoCommit(false); // 트랜잭션 시작
try {
PreparedStatement ps1 = c.preparedStatement("update users ... ");
ps1.executeUpdate();
PreparedStatement ps2 = c.preparedStatement("delete users ... ");
ps2.executeUpdate();
c.commit(); // 트랜잭션 커밋
} catch (Exception e) {
c.rollback(); // 트랜잭션 롤백
}
c.close();
Connection을 가져와 사용하고 닫는 사이에서 일어난다. 트랜잭션의 시작과 종료는 Connection 오브젝트를 통해 이뤄지기 때문이다.commit() 또는 rollback() 메소드가 호출될 때까지의 작업이 하나의 트랜잭션으로 묶인다.setAutoCommit(false)로 트랜잭션의 시작을 선언하고 commit() 또는 rollback()으로 트랜잭션을 종료하는 작업을 트랜잭션의 경계 설정이라고 한다.로컬 트랜잭션이라고도 한다.public void upgradeLevels() throws Exception {
TransactionSynchronizationManager.initSynchronization();
Connection c = DataSourceUtils.getConnection(dataSource);
c.setAutoCommit(false);
try {
List<User> users = userDao.getAll();
for (User user : users) {
upgrade(level);
}
c.commit();
} catch (Exception e) {
c.rollback();
throw e;
} finally {
DataSourceUtils.releaseConnection(c, dataSource);
TransactionSynchronizationManager.unbindResource(this.dataSource);
TransactionSynchronizationManager.clearSynchronization();
}
}
TransactionSynchronizationManager 이다. 이 클래스를 이용해 먼저 트랜잭션 동기화 작업을 초기화하도록 요청한다.DataSourceUtils의 getConnection() 메소드를 사용하면 DB 커넥션을 생성하고, 이 Connection 오브젝트를 트랜잭션 동기화에 사용하도록 저장소에 바인딩해준다.JTA(Java Transaction API)를 제공하고 있다.InitialContext ctx = new InitialContext();
// JNDI를 이용해 서버의 UserTransaction Object를 가져온다.
UserTransaction tx = (UserTransaction) ctx.lookup(USER_TX_JNDI_NAME);
tx.begin();
// JNDI로 가져온 DataSource를 사용해야 한다.
Connection c = dataSource.getConnection();
try {
/**
* Data access 코드
*/
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
} finally {
c.close();
}
외울 필요는 없고 방법이 존재한다는 것만 기억하자
PlatformTransactionManager이다. JDBC의 로컬 트랜잭션을 이용한다면 이 인터페이스를 구현한 DataSourceTransactionManager를 사용하면 된다.package org.springframework.mail;
...
public interface MailSender {
void send(SimpleMailMessage simpleMessage) throws MailException;
void send(SimpleMailMessage[] simpleMessages) throws MailException;
}
- 이 인터페이스는
SimpleMailMessage라는 인터페이스를 구현한 클래스에 담긴 메일 메시지를 전송하는 메소드로 구성되어 있다.- 이를 구체화한
JavaMailSenderImpl를 구현해서 사용하면 된다.
private void sendUpgradeEmail(User user) {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost("mail.server.com");
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(user.getEmail());
message.setFrom("kswoeqwelas@naver.com");
message.setSubject("Upgrade 안내");
message.setText("사용자님의 등급이 " + user.getLevel() + "로 업그레이드 되었습니다.");
mailSender.send(message);
}
테스트 동안에 실제로 동작하는 것처럼 보이게 만들어 놓은 더미 객체로 아무런 기능을 수행하지 않는다.
- 행위를 검증하기 위해 사용되는 객체로 테스트 대상 오브젝트의 메소드가 돌려주는 결과뿐 아니라 테스트 오브젝트가 간접적으로 의존 오브젝트에 넘기는 값과 그 행위 자체에 대해서도 검증하고 싶다면, assertThat() 만으로는 검증이 불가능하다. 이런 경우에는 테스트 대상 오브젝트와 의존 오브젝트 사이에서 일어나는 일을 검증할 수 있도록 특별히 설계된 목 오브젝트(mock object)를 사용해야 한다.
- 목 오브젝트는 테스트 오브젝트와 자신의 사이에서 일어나는 커뮤니케이션 내용을 저장해뒀다가 테스트 결과를 검증하는 데 활용할 수 있다.
static class MockMailSender implements MailSender {
private List<String> requests = new ArrayList<>();
public List<String> getRequests() {
return requests;
}
// UserService로부터 전송 요청을 받은 메일 주소를 저장해두고 이를 읽을 수 있게 한다.
public void send(SimpleMailMessage mailMessage) throws MailException {
requests.add(mailMessage.getTo()[0]);
}
public void send(SimpleMailMessage[] mailMessages) throws MailException {
}
}
위 MockMailSender 클래스를 활용한 테스트 케이스이다.
@Test
void upgradeLevels() throws Exception {
userDao.deleteAll();
for (User user : users) userDao.add(user);
// 메일 발송 결과를 테스트할 수 있도록 목 오브젝트를 만들어 UserService에 주입해준다.
MockMailSender mockMailSender = new MockMailSender();
userService.setMailSender(mockMailSender);
userService.upgradeLevels();
this.checkLevelUpgraded(users.get(0), false);
this.checkLevelUpgraded(users.get(1), true);
this.checkLevelUpgraded(users.get(2), false);
this.checkLevelUpgraded(users.get(3), true);
this.checkLevelUpgraded(users.get(4), false);
// 목 오브젝트에 저장된 메일 수신자 목록을 가져와 업그레이드 대상과 일치하는지 확인한다.
List<String> request = mockMailSender.getRequests();
assertEquals(2, request.size());
assertEquals(users.get(1).getEmail(), request.get(0));
assertEquals(users.get(3).getEmail(), request.get(1));
}