트랜잭션
트랜잭션은 두 개 이상의 쿼리를 한 작업으로 실행해야할 때 사용합니다.
@Override
@Transactional // db 동시에
public void insert(CommentDTO comment) {
// 댓글 추가
mapper.insert(comment);
// 댓글 수 증감
bmapper.updateReplyCnt(comment.getBnum(), 1);
}
이와 같은 메소드를 처리하기 위해 어떻게 해야 될까요?
root-context.xml
Check Namespaces
tx를 체크해줍니다.
<!-- 트랜잭션 -->
<tx:annotation-driven/>
<!-- TransactionManager 에러 처리-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven/> 으로 트랜잭션 동작을 활성화시킵니다.
DataSourceTransactionManager : JDBC 및 mybatis 등의 JDBC 기반 라이브러리로 데이터베이스에 접근하는 경우에 이용합니다.
CommentServiceImpl.java
@Service
public class CommentServiceImpl implements CommentService{
@Autowired
CommentMapper cmapper;
@Autowired
BoardMapper bmapper;
@Override
@Transactional // db 동시에
public void insert(CommentDTO comment) {
// 댓글 추가
cmapper.insert(comment);
// 댓글 수 증감
bmapper.updateReplyCnt(comment.getBnum(), 1);
}
@Transactional // 어노테이션을 통해 트랜잭션 처리
mybatis를 통해 mapper 객체를 이용하여 DB 접근합니다.
mapper.java
public interface CommentMapper
{
public void insert(CommentDTO comment);
,,,
public interface BoardMapper
{
public void updateReplyCnt(@Param("bnum")int num, @Param("amount")int mount); // xml 에서 사용할 수 있도록 hashmap 형태로 만든다.
...
CommentMapper.xml
<mapper namespace="com.board.mapper.CommentMapper">
<insert id="insert">
insert into commentboard(userid, content,regdate,bnum) values(#{userid}, #{content},now(), #{bnum})
</insert>
</mapper>
BoardMapper.xml
<mapper namespace="com.board.mapper.BoardMapper">
<update id="updateReplyCnt">
update board set replaycnt= replaycnt+ #{amount}
where num=#{bnum}
</update>
</mapper>
삭제하기(Delete)
@Service 단에서 @Transactional 처리
//JPA의 엔티티가 영속성 컨텍스트를 통해 관리되면 1차 캐시, 변경 감지(더티 체킹) 등 가지는 이점이 많다.
//하지만 영속성 컨텍스트는 변경 감지를 위해 스냅샷 인스턴스를 보관하기 때문에 더 많은 메모리를 사용하게 된다.
//단순 조회의 수가 많아질수록 더 많은 메모리가 낭비되기 때문에 최적화해줄 필요가 있다.
// 메모리 최적화 (스냅샷 보관 X)
@Transactional(readOnly = true)
@Service
public class BoardService
{
@Autowired
BoardRepository boardRepository;
@Transactional // 2개의 트랜잭션 처리 도중 에러가 났을때 자동 rollback 위함
public void insert(Board board, User user) {
board.setUser(user);
boardRepository.save(board);
}
public List<Board> findAll() {
return boardRepository.findAll();
}
public Long count() {
return boardRepository.count();
}
public Board findById(Long num) {
return boardRepository.findById(num).get();
}
@Transactional // 2개의 트랜잭션 처리 도중 에러가 났을때 자동 rollback 위함, 바로 DB 반영
public void delete(Long num) {
boardRepository.deleteByNum(num);
}
}
JPA의 엔티티가 영속성 컨텍스트를 통해 관리되면 1차 캐시, 변경 감지(더티 체킹) 등 가지는 이점이 많습니다.
하지만 영속성 컨텍스트는 변경 감지를 위해 스냅샷 인스턴스를 보관하기 때문에 더 많은 메모리를 사용하게 된니다.
단순 조회의 수가 많아질수록 더 많은 메모리가 낭비되기 때문에 최적화해줄 필요가 있니다.
메모리 최적화 (스냅샷 보관 X)
* 2개의 트랜잭션 처리 도중 에러가 났을때 자동 rollback 위함,
수정, 삭제의 경우 바로 DB 반영
@Repository
public interface BoardRepository extends JpaRepository<Board, Long>
{
void deleteByNum(Long num);
}
수정 하기(Update)
// 수정하기 --> 더티 체킹
// 영속성 컨텍스트에 있는 객체 (Board) 먼저 구하고 set
@Transactional
public void update(Board board) {
Board b = boardRepository.findById(board.getNum()).get();
b.setTitle(board.getTitle());
b.setContent(board.getContent());
}
Hibernate: select board0_.num as num1_0_0_, board0_.content as content2_0_0_, board0_.hitcount as hitcount3_0_0_, board0_.regdate as regdate4_0_0_, board0_.replycnt as replycnt5_0_0_, board0_.title as title6_0_0_, board0_.id as id8_0_0_, board0_.writer as writer7_0_0_ from tbl_board4 board0_ where board0_.num=?
Hibernate: update tbl_board4 set content=?, hitcount=?, regdate=?, replycnt=?, title=?, id=?, writer=? where num=?
@Transactional
propagation 흔히 쓰는 3개 정리
· REQUIRED (default) : 이미 시작된 트랜잭션이 있으면 참여하고 없으면 새로 시작합니다.
· REQUIRES_NEW : 항상 새로운 트랜잭션을 시작한다. 부모와 자식 중 문제가 생기지 않으면 커밋, 문제 생기면 롤백
· SUPPORTS : 이미 시작된 트랜잭션이 있으면 참여하고, 없으면 트랜잭션없이 진행합니다.
조회 메소드 같은 경우는 propagation="SUPPORTS" 와 read-only="true" 를 넣어서 트랜잭션을 태우는게 성능적으로 유리하다고합니다.
@Transactional(propagation=Propagation.REQUIRES_NEW, rollbackFor=Exception.classs)
@Override
@Transactional
public OrderResponse placeOrder(OrderRequest orderRequest) {
// get order
Order order = orderRequest.getOrder();
// set status : inprogress
order.setStatus("INPROGRESS");
// set 주문 번호
order.setOrderTackingNumber(UUID.randomUUID().toString());
// save
orderRepository.save(order);
Payment payment= orderRequest.getPayment();
// 결제 실패시 예외 처리
if(!payment.getType().equals("DEBIT")){
throw new PaymentException("Payment card type do not support");
}
// payment set order id
payment.setOrderId(order.getId());
// payment 저장
paymentRepository.save(payment);
OrderResponse orderResponse = new OrderResponse();
orderResponse.setOrderTackingNumber(order.getOrderTackingNumber());
orderResponse.setStatus(order.getStatus());
orderResponse.setMessage("SUCCESS");
return orderResponse;
}
orderRepository.save(order); 이후 강제 예외를 발생 시켰습니다.
@Transactional 이 없을 경우 orderRepository.save(order); 으로 인해
orders 테이블에 값이 삽입 되었습니다.
Database 결과
https://oingdaddy.tistory.com/28
https://devocean.sk.com/blog/techBoardDetail.do?ID=163799
- propagation 은 로직 처리를 수행할때 트랜잭션이 어떻게 수행되어야할지에 대한 설정이다. 이 설정을 통해서 트랜잭션을 필요에 따라 구분되어 사용할 수 있게 된다.
- isolation level은 DBMS 데이터에 대해서 동시성 처리가 일어날때 어떠한 전략을 사용할지 레벨을 설정하는 것이며, 다른 트랜잭션 하에서 데이터를 읽을때 어떻게 데이터가 보이는지에 대한 설정이다.
'Back-end > Spring Framework' 카테고리의 다른 글
spring lagacy project / 생성, 설정, 오류 처리, maven, mybatis | 히카리 cp (0) | 2022.05.06 |
---|---|
java spring 커넥션 풀 , jdbcTemplate (0) | 2020.08.13 |
java spring 리다이렉트, 인터셉트 (0) | 2020.08.12 |
java spring 세션 & 쿠키 (0) | 2020.08.10 |
java spring , Controller 설정팁 (0) | 2020.08.09 |
댓글