-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[7주차](임주민, 오형석, 이승철) #7
Comments
@transactional 사용법
우선순위
활용방법
테스트 코드에서의 @transactional 사용법
@transactional 을 활용하면 얼마나 편리해지는 것일까?예시 -> 고객 a가 고객 b에게 5000원을 송금하는 상황
두 가지 작업을 하나의 transaction으로 묶고싶은 상황이다. a 잔고에서 5000원을 감소시킬 동안 에러가 발생한다면 commit을 하지 않고(데이터 실제 반영을 하지 않고), 롤백을 하며, b 잔고에 5000원을 증가시키는 작업 또한 수행하면 안된다. 이 작업은 Service 단에서 수행 될 것이며, 원래라면 서비스 코드에 DataSource가 필요할 것이다. 예시코드 @Slf4j
@RequiredArgsConstructor
public class MemberServiceV2 {
private final DataSource dataSource;
private final MemberRepositoryV2 memberRepository;
public void accountTransfer(String fromId, String toId, int money) throws SQLException {
Connection con = dataSource.getConnection();
try {
con.setAutoCommit(false); //트랜잭션 시작
bizLogic(con, fromId, toId, money); //비즈니스 로직
con.commit(); //성공시 커밋
} catch (Exception e) {
con.rollback(); //실패시 롤백
throw new IllegalStateException(e);
} finally {
release(con);
}
}
private void bizLogic(Connection con, String fromId, String toId, int
money) throws SQLException {
Member fromMember = memberRepository.findById(con, fromId);
Member toMember = memberRepository.findById(con, toId);
memberRepository.update(con, fromId, fromMember.getMoney() - money);
validation(toMember);
memberRepository.update(con, toId, toMember.getMoney() + money);
}
private void validation(Member toMember) {
if (toMember.getMemberId().equals("ex")) {
throw new IllegalStateException("이체중 예외 발생");
}
}
private void release(Connection con) {
if (con != null) {
try {
con.setAutoCommit(true); //커넥션 풀 고려
con.close();
} catch (Exception e) {
log.info("error", e);
}
}
}
} public class MemberRepositoryV2 {
private final DataSource dataSource;
public MemberRepositoryV2(DataSource dataSource) {
this.dataSource = dataSource;
}
public Member findById(Connection con, String memberId) throws SQLException {
String sql = "select * from member where member_id = ?";
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
pstmt = con.prepareStatement(sql);
pstmt.setString(1, memberId);
rs = pstmt.executeQuery();
if (rs.next()) {
Member member = new Member();
member.setMemberId(rs.getString("member_id"));
member.setMoney(rs.getInt("money"));
return member;
} else {
throw new NoSuchElementException("member not found memberId=" + memberId);
}
} catch (SQLException e) {
log.error("db error", e);
throw e;
} finally {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(pstmt);
}
}
public void update(Connection con, String memberId, int money) throws SQLException {
String sql = "update member set money=? where member_id=?";
PreparedStatement pstmt = null;
try {
pstmt = con.prepareStatement(sql);
pstmt.setInt(1, money);
pstmt.setString(2, memberId);
pstmt.executeUpdate();
} catch (SQLException e) {
log.error("db error", e);
throw e;
} finally {
//connection은 여기서 닫지 않는다.
JdbcUtils.closeStatement(pstmt);
}
}
private void close(Connection con, Statement stmt, ResultSet rs) {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(stmt);
JdbcUtils.closeConnection(con);
}
private Connection getConnection() throws SQLException {
Connection con = dataSource.getConnection();
log.info("get connection={} class={}", con, con.getClass());
return con;
}
} 문제점 3가지
해결법 1 -> TransactionManagerDataSourceUtils 로 커넥션을 가져온다!
해결법 2 -> TransactionTemplate.반복적인 트랜잭션을 위해서 사용되는 반복되는 커밋, 롤백 코드를 제거하자. public void accountTransfer(String fromId, String toId, int money) throws SQLException {
txTemplate.executeWithoutResult((status) -> {
try {
bizLogic(fromId, toId, money); // 비지니스 로직
} catch (SQLException e) {
throw new IllegalStateException(e);
}
});
} transactionTemplate이 하는 역할비즈니스 로직이 정상 수행되면 커밋한다. ** 그러나 결국에는 서비스에 트랜잭션을 처리하는 기술로직이 포함된 것이다** 스프링 AOP를 통해 프록시를 도입하면 문제를 깔끔하게 해결할 수 있다. @Transactional
public void accountTransfer(String fromId, String toId, int money) throws SQLException {
bizLogic(fromId, toId, money);
} ** 그 길었던 코드가 @transaction 어노테이션 코드 하나로 정리된다. ** |
7장 스프링 핵심 기술의 응용목표 SQL과 DAO의 분리데이터 액세스 로직은 바뀌지 않더라도 DB 테이블, 필드 이름, SQL 문장은 바뀔 가능성 0 👉 - XML 설정 이용한 분리SQL을 스프링의 XML 설정파일로 빼내는 것
XML 설정의 UserDao 빈에 sqlAdd 프로퍼티 추가 후 SQL 넣어줌 한계 👉 매번 새로운 SQL이 필요할 때마다 프로퍼티를 추가하고 DI를 위한 변수와 수정자 메소드도 만들어줘야 함 - SQL 맵 프로퍼티 방식SQL을 하나의 컬렉션으로 담아두는 방법
정리 SQL 제공 서비스DAO가 사용할 SQL을 제공해주는 기능을 독립시킬 필요 존재 -> 독립적인 SQL 제공 서비스 - SQL 서비스 인터페이스클라이언트인 DAO를 SQL 서비스의 구현에서 독립적으로 만들도록 인터페이스 사용, DI로 구현 클래스 오브젝트 주입해주기 DAO가 사용할 SQL 서비스의 기능
인터페이스의 분리와 자기참조 빈sqlService 인퍼페이스의 구현 방법 - XML 파일 매핑스프링의 XML 설정파일에서
- JAXB(Java Architecture for XML Binding)XML에 담긴 정보를 파일에서 읽어오는 방법 중 하나 XML 문서의 구조를 정의한
JAXB 용어 XML 파일 이용하는 SQL 서비스JAXB를 이용해 XML 문서를 변환 후 SqlService에 적용할 차례 - XML SQL 서비스 구현DAO가 SQL을 요청할 때마다 매번 XML 파일 읽어서 SQL 찾는 건 비효율적 XML을 한 번만 읽고 읽은 내용은 어딘가에 저장 후 요청 올 때마다 사용 👉 생성자에서 JAXB를 이용해 XML로 된 SQL 문서를 읽어와 변환된 Sql오브젝트들을 맵으로 옮겨서 저장해뒀다가 DAO의 요청에 따라 SQL을 찾아서 전달하는 방식 시도 - 개선생성자에서 예외가 발생할 수 있는 복잡한 초기화 작업을 다루는 것은 좋지 X 상속도 불편하며 보안에도 문제 생길 가능성 존재 👉 별도의 초기화 메소드 사용 후 오브젝트 만드는 시점에서 초기화 메소드 한 번 호출해주기 빈의 초기화 작업XmlSqlService 오브젝트는 빈이므로 제어권이 스프링에게 있어 생성과 초기화는 스프링이 담당 스프링은 빈 오브젝트 생성하고 DI 작업을 수행해서 프로퍼티 모두 주입 후 미리 지정한 초기화 메소드 호출해주는 기능 가지고 있음
인터페이스 분리XMl 대신 다른 포맷의 파일에서도 SQL을 읽어오게 하는 법 - 책임에 따른 인터페이스 정의책임 1) SQL 정보를 외부의 리소스(XML, 엑셀 파일, DB)로부터 읽어오는 것 책임 2) 읽어온 SQL을 보관해두고 있다가 필요할 때 제공 SqlService의 구현 클래스가 변경 가능한 책임을 가진 두가지 타입의 오브젝트를 사용하도록 만든다 - 다중 인터페이스 구현과 간접 참조XmlSqlService 클래스 하나가 SqlService, SqlReader, SqlRegistry라는 세 개의 인터페이스를 구현해도 상관 X 책임에 따라 분리되지 않았던 XmlSqlService 클래스를 일단 세부화된 책임을 정의한 인터페이스를 구현하도록 만드는 작업 시도 SqlService의 메소드에서 - 자기참조 빈 설정이제 빈 설정을 통해 실제 DI가 일어나도록 하기 스프링은 프로퍼티의 ref 항목에 자기 자신 넣는 거 허용 -> sqlService를 구현한 메소드와 초기화 메소드는 외부에서 DI된 오브젝트라고 생각하고 결국 자신의 메소드에 접근 자기참조 빈 만드는 방식은 책임과 관심사가 복잡하게 얽혀 있어서 확장 힘들고 변경에 취약한 구조의 클래스를 유연한 구조로 만들려고 할 때 처음 시도해볼 수 있는 방법 - 디폴트 의존관계를 갖는 빈 만들기특정 의존 오브젝트가 대부분의 환경에서 거의 디폴트라고 해도 좋을 만큼 기본적으로 사용될 가능성이 있는 경우 사용 디폴트 의존관계 : 외부에서 DI 받지 않는 경우 기본적으로 자동 적용되는 의존관계 DI 설정이 없을 경우 디폴트로 적용하고 싶은 의존 오브젝트를 생성자에서 넣어줌
단점 서비스 추상화 적용OXM 서비스 추상화- OXM(Object-XML Mapping)XML과 자바 오브젝트를 매핑해서 상호 변환해주는 기술 서비스 추상화 스프링은 OXM에 대해서도 서비스 추상화 기능을 제공 스프링이 제공하는 OXM 추상화 서비스 인터페이스
|
The text was updated successfully, but these errors were encountered: