1. 트랜잭션의 개념
논리적 단위로 어떤 한 부분의 작업이 완료되었다 하더라도 , 다른 부분의 작업이 완료되지 않을 경우 전체 취소되는 것입니다. 이때 , 작업이 완료되는 것을 커밋(commit)이라고 하고 , 작업이 취소되는 것을 롤백(rollback)이라고 합니다. 영화 예매를 할 경우 카드 결제 작업과 마일리지 적립 작업
2. 스프링 트랜잭션 사용방법
트랜잭션 처리를 하지 않았을 경우 rollback이 되지 않는 경우입니다
조건에 안 맞는 데이터가 들어왔을때 '매표소 직원' 테이블에는 제약조건에 걸린 값만 들어오지만
'카드 결제' 컬럼에선 제약조건이 없기때문에 그 데이터가 그대로 남아있다 => 문제생김!
3. 스프링 트랜잭션을 이용하기 위한 설정
*. servlet-context.xml
<beans:bean name="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<beans:property name="dataSource" ref="dataSource"></beans:property>
</beans:bean>
<beans:bean name="dao" class="com.javalec.spring_tran_apply.dao.TicketDao" >
<beans:property name="template" ref="template" />
<beans:property name="transactionManager" ref="transactionManager"></beans:property>
</beans:bean>
*. Dao
PlatformTransactionManager transactionManager;
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public void buyTicket(final TicketDto dto) {
TransactionDefinition definition = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(definition);
try {
template.update(new PreparedStatementCreator() {
}
});
template.update(new PreparedStatementCreator() {
}
});
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
}
실습
select * from card;
select * from ticket;
CREATE TABLE card
(consumerId VARCHAR2(10)
, amount NUMBER(1)
);
CREATE TABLE ticket
(consumerId VARCHAR2(10)
, countnum NUMBER(1) CHECK(countnum < 5)
);
제약조건 건 테이블 하나, 그냥 테이블 하나 생성
<!-- JDBC Template -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
프로젝트에 디펜던시 추가
<!-- dataSource 객체는 데이터 베이스 정보 포함-->
<beans:bean name="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<beans:property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<beans:property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
<beans:property name="username" value="scott"/>
<beans:property name="password" value="tiger"/>
</beans:bean>
<!-- JdbcTemplate 객체에 dataSource 객체를 포함-->
<beans:bean name="template" class="org.springframework.jdbc.core.JdbcTemplate">
<beans:property name="dataSource" ref="dataSource"/>
</beans:bean>
board _jdbc에 있던 servlet-context 복사
그 밑에
<beans:bean name="dao" class="com.lgy.spring_tran.dao.TicketDao">
<beans:property name="template" ref="template"></beans:property>
</beans:bean>
<beans:property name="template" ref="template"></beans:property>
</beans:bean>
추가.
package com.lgy.spring_tran.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class TicketDto {
private String consumerId;
// amount 갯수로 대체
// private int countnum;
private int amount;
}
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class TicketDto {
private String consumerId;
// amount 갯수로 대체
// private int countnum;
private int amount;
}
dto
package com.lgy.spring_tran.dao;
import com.lgy.spring_tran.dto.TicketDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@Slf4j
public class TicketDao {
JdbcTemplate template;
public void setTemplate(JdbcTemplate template) {
this.template = template;
}
public void buyTicket(final TicketDto dto){
// 카드결제처리
template.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
log.info("@# 카드 결제 처리");
String query = "insert into card(consumerId, amount) values(?,?)";
PreparedStatement pstmt = con.prepareStatement(query);
pstmt.setString(1, dto.getConsumerId());
pstmt.setInt(2, dto.getAmount());
return pstmt;
}
});
// 티켓 수령
template.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
log.info("@# 티켓 수령");
String query = "insert into ticket(consumerId, countnum) values(?,?)";
PreparedStatement pstmt = con.prepareStatement(query);
pstmt.setString(1, dto.getConsumerId());
pstmt.setInt(2, dto.getAmount());
return pstmt;
}
});
}
}
import com.lgy.spring_tran.dto.TicketDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@Slf4j
public class TicketDao {
JdbcTemplate template;
public void setTemplate(JdbcTemplate template) {
this.template = template;
}
public void buyTicket(final TicketDto dto){
// 카드결제처리
template.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
log.info("@# 카드 결제 처리");
String query = "insert into card(consumerId, amount) values(?,?)";
PreparedStatement pstmt = con.prepareStatement(query);
pstmt.setString(1, dto.getConsumerId());
pstmt.setInt(2, dto.getAmount());
return pstmt;
}
});
// 티켓 수령
template.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
log.info("@# 티켓 수령");
String query = "insert into ticket(consumerId, countnum) values(?,?)";
PreparedStatement pstmt = con.prepareStatement(query);
pstmt.setString(1, dto.getConsumerId());
pstmt.setInt(2, dto.getAmount());
return pstmt;
}
});
}
}
dao
return "redirect:buy_ticket";
}
@RequestMapping("buy_ticket")
public String buy_ticket(){
log.info("@# buy_ticket()");
return "buy_ticket";
}
}
@RequestMapping("buy_ticket")
public String buy_ticket(){
log.info("@# buy_ticket()");
return "buy_ticket";
}
homeController return값 바꿔주고 스트링 메소드 만들기
<%--
Created by IntelliJ IDEA.
User: GGG
Date: 2023-06-22
Time: 오후 3:38
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<p>카드 결제</p>
<form action="buy_ticket_card">
고객 아이디:<input type="text" name="consumerId"><br>
티켓 구매수:<input type="text" name="amount"><br>
<input type="submit" value="구매">
</form>
</body>
</html>
Created by IntelliJ IDEA.
User: GGG
Date: 2023-06-22
Time: 오후 3:38
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<p>카드 결제</p>
<form action="buy_ticket_card">
고객 아이디:<input type="text" name="consumerId"><br>
티켓 구매수:<input type="text" name="amount"><br>
<input type="submit" value="구매">
</form>
</body>
</html>
package com.lgy.spring_tran;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import com.lgy.spring_tran.dao.TicketDao;
import com.lgy.spring_tran.dto.TicketDto;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Handles requests for the application home page.
*/
@Controller
@Slf4j
public class HomeController {
private TicketDao dao;
@Autowired
public void setDao(TicketDao dao) {
this.dao = dao;
}
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Simply selects the home view to render by returning its name.
*/
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
return "redirect:buy_ticket";
}
@RequestMapping("buy_ticket")
public String buy_ticket(){
log.info("@# buy_ticket()");
return "buy_ticket";
}
@RequestMapping("buy_ticket_card")
public String buy_ticket_card(TicketDto ticketDto, Model model){
log.info("@# buy_ticket_card()");
log.info("@# ticketDto=>"+ticketDto);
dao.buyTicket(ticketDto);
model.addAttribute("ticketInfo", ticketDto);
return "buy_ticket_end";
}
}
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import com.lgy.spring_tran.dao.TicketDao;
import com.lgy.spring_tran.dto.TicketDto;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Handles requests for the application home page.
*/
@Controller
@Slf4j
public class HomeController {
private TicketDao dao;
@Autowired
public void setDao(TicketDao dao) {
this.dao = dao;
}
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Simply selects the home view to render by returning its name.
*/
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
return "redirect:buy_ticket";
}
@RequestMapping("buy_ticket")
public String buy_ticket(){
log.info("@# buy_ticket()");
return "buy_ticket";
}
@RequestMapping("buy_ticket_card")
public String buy_ticket_card(TicketDto ticketDto, Model model){
log.info("@# buy_ticket_card()");
log.info("@# ticketDto=>"+ticketDto);
dao.buyTicket(ticketDto);
model.addAttribute("ticketInfo", ticketDto);
return "buy_ticket_end";
}
}
<%--
Created by IntelliJ IDEA.
User: GGG
Date: 2023-06-22
Time: 오후 3:45
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
buy_ticket_end.jsp입니다.<br>
${ticketInfo.consumerId}<br>
${ticketInfo.amount}<br>
</body>
</html>
Created by IntelliJ IDEA.
User: GGG
Date: 2023-06-22
Time: 오후 3:45
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
buy_ticket_end.jsp입니다.<br>
${ticketInfo.consumerId}<br>
${ticketInfo.amount}<br>
</body>
</html>
4장 먼저
위반해보자(5장 넣어보기)
db를 보면?
///////////////커밋, 롤백 해보기
<!-- DataSourceTransactionManager-->
<beans:bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<beans:property name="dataSource" ref="dataSource"></beans:property>
</beans:bean>
<beans:bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<beans:property name="dataSource" ref="dataSource"></beans:property>
</beans:bean>
<!-- <beans:bean name="dao" class="com.lgy.spring_tran.dao.TicketDao">-->
<!-- <beans:property name="template" ref="template"></beans:property>-->
<!-- </beans:bean>-->
<beans:bean name="dao" class="com.lgy.spring_tran.dao.TicketDao">
<beans:property name="template" ref="template"></beans:property>
<!-- <beans:property name="template" ref="template"></beans:property>-->
<!-- </beans:bean>-->
<beans:bean name="dao" class="com.lgy.spring_tran.dao.TicketDao">
<beans:property name="template" ref="template"></beans:property>
<beans:property name="transactionManager" ref="transactionManager"></beans:property>
</beans:bean>servlet-context에 추가
package com.lgy.spring_tran.dao;
import com.lgy.spring_tran.dto.TicketDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@Slf4j
public class TicketDao {
JdbcTemplate template;
PlatformTransactionManager transactionManager;
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public void setTemplate(JdbcTemplate template) {
this.template = template;
}
public void buyTicket(final TicketDto dto){
TransactionDefinition definition = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(definition);
try {
// 카드결제처리
template.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
log.info("@# 카드 결제 처리");
String query = "insert into card(consumerId, amount) values(?,?)";
PreparedStatement pstmt = con.prepareStatement(query);
pstmt.setString(1, dto.getConsumerId());
pstmt.setInt(2, dto.getAmount());
return pstmt;
}
});
// 티켓 수령
template.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
log.info("@# 티켓 수령");
String query = "insert into ticket(consumerId, countnum) values(?,?)";
PreparedStatement pstmt = con.prepareStatement(query);
pstmt.setString(1, dto.getConsumerId());
pstmt.setInt(2, dto.getAmount());
return pstmt;
}
});
// 문제 없으면 카드 결제 처리, 티켓 수령 동시 처리
transactionManager.commit(status);
}catch (Exception e){
e.printStackTrace();
// 문제 있으면 취소 처리
transactionManager.rollback(status);
}
}
}
import com.lgy.spring_tran.dto.TicketDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@Slf4j
public class TicketDao {
JdbcTemplate template;
PlatformTransactionManager transactionManager;
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public void setTemplate(JdbcTemplate template) {
this.template = template;
}
public void buyTicket(final TicketDto dto){
TransactionDefinition definition = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(definition);
try {
// 카드결제처리
template.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
log.info("@# 카드 결제 처리");
String query = "insert into card(consumerId, amount) values(?,?)";
PreparedStatement pstmt = con.prepareStatement(query);
pstmt.setString(1, dto.getConsumerId());
pstmt.setInt(2, dto.getAmount());
return pstmt;
}
});
// 티켓 수령
template.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
log.info("@# 티켓 수령");
String query = "insert into ticket(consumerId, countnum) values(?,?)";
PreparedStatement pstmt = con.prepareStatement(query);
pstmt.setString(1, dto.getConsumerId());
pstmt.setInt(2, dto.getAmount());
return pstmt;
}
});
// 문제 없으면 카드 결제 처리, 티켓 수령 동시 처리
transactionManager.commit(status);
}catch (Exception e){
e.printStackTrace();
// 문제 있으면 취소 처리
transactionManager.rollback(status);
}
}
}
이제 넣으면?
로그에 violated됬다고써있고
정상 값만 들어가고 비정상값은 롤백되있는것을 확인할 수 있다.
'백 > spring' 카테고리의 다른 글
스프링 기본파싱전략과 json통신 (0) | 2023.07.30 |
---|---|
스프링 시큐리티 2- ng처리 (0) | 2023.07.12 |
스프링 시큐리티 1 (0) | 2023.07.11 |
파일 업로드 부터 수정까지 (0) | 2023.06.28 |
Spring Validator를 이용한 검증 (0) | 2023.06.22 |