트랜잭션
여러 개의 DML 명령문을 하나의 논리적인 작업 단위로 묶어서 관리하는 것
작업들을 한꺼번에 등록(ALL, commit)하거나 취소함(Nothing, rollback)
XML 파일에서 설정
애너테이션 이용
propagation: 트랜잭션 전파 규칙
isolation: 트랜잭션 격리 레벨
realOnly: 읽기 전용 여부
rollbackFor: rollback 예외 타입
timeout: 타임아웃 시간
propagation 하위 속성
isolation 하위 속성
계좌 테이블 생성 코드
create table cust_account (
accountNo varchar2(20) primary key,
custName varchar2(50),
balance number(20,4)
);
insert into cust_account(accountNo,custName,balance)
values('70-490-930','홍길동',10000000);
insert into cust_account(accountNo,custName,balance)
values('70-490-911','김유신',10000000);
commit;
select * from cust_account;
pro25/WebContent/WEB-INF/action-servlet.xml
...
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views/account/" />
<property name="suffix" value=".jsp"/>
</bean>
<bean id="accController class="com.spring.account.AccountController">
<property name="methodNameResolver"> <ref local="methodResolver"/> </property>
<property name="accService" ref="accService"/> <!-- accService bean 주입 -->
</bean>
<bean id="methodResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
<property name="mappings" >
<props>
<prop key="/account/sendMoney.do">sendMoney</prop> <!-- sendMoney 메소드 호출 -->
</props>
</property>
</bean>
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/account/*.do">accController</prop> <!-- accController bean 실행 -->
</props>
</property>
</bean>
</beans>
pro25/WebContent/WEB-INF/config/action-mybatis.xml
...
<bean id="accDAO" class="com.spring.account.AccountDAO">
<property name="sqlSession" ref="sqlSession" />
</bean>
<!-- DataSourceTransactionManager: dataSource bean 트랜잭션 적용 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="txManager" /> <!-- 애너테이션으로 transaction 사용 시 필요 -->
</beans>
pro25/WebContent/WEB-INF/config/action-service.xml
...
<!-- accDAO bean 주입 -->
<bean id="accService" class="com.spring.account.AccountService">
<property name="accDAO" ref="accDAO"/>
</bean>
</beans>
pro25/src/mybatis/mappers/account.xml
...
<mapper namespace="mapper.account">
<update id="updateBalance1">
<![CDATA[
update cust_account
set balance=balance-5000000
where
accountNo = '70-490-930'
]]>
</update>
<update id="updateBalance2">
<![CDATA[
update cust_account
set balance=balance+5000000
where
accountNo = '70-490-911'
]]>
</update>
</mapper>
pro25/src/com/spring/account/AccountController.java
package com.spring.account;
...
public class AccountController extends MultiActionController {
private AccountService accService;
public void setAccService(AccountService accService) { //setter
this.accService = accService;
}
public ModelAndView sendMoney(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav=new ModelAndView();
accService.sendMoney();
mav.setViewName("result");
return mav;
}
}
pro25/src/com/spring/account/AccountService.java
package com.spring.account;
...
@Transactional(propagation=Propagation.REQUIRED) //모든 메소드에 트랜잭션 적용
public class AccountService {
private AccountDAO accDAO;
public void setAccDAO(AccountDAO accDAO) { //setter
this.accDAO = accDAO;
}
public void sendMoney() throws Exception {
accDAO.updateBalance1(); //sql1 실행
accDAO.updateBalance2(); //sql2 실행
}
}
pro25/src/com/spring/account/AccountDAO.java
package com.spring.account;
...
public class AccountDAO {
private SqlSession sqlSession;
public void setSqlSession(SqlSession sqlSession) { //setter
this.sqlSession = sqlSession;
}
public void updateBalance1() throws DataAccessException {
sqlSession.update("mapper.account.updateBalance1"); //송금하는 계좌 금액 차감
}
public void updateBalance2() throws DataAccessException {
sqlSession.update("mapper.account.updateBalance2"); //송금받는 계좌 금액 증액
}
}
애너테이션
자바 코드 관련 설정 바로 가능
XML 설정 파일에서 bean 설정 필요
<context:component-scan> 태그@Controller: 지정 클래스 Controller bean 자동 변환
@Service: 지정 클래스 Service bean 자동 변환
@Repository: 지정 클래스 DAO bean 자동 변환
@Component: 지정 클래스 bean 자동 변환
매개변수를 자동으로 가져오는 애너테이션
getParameter 불필요
required 속성
true (default): 해당 매개변수 전달 필수, 없으면 예외 발생
false: 해당 매개변수 값이 없으면 null
pro26/WebContent/WEB-INF/action-servlet.xml
...
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/test/" />
<property name="suffix" value=".jsp"/>
</bean>
<!-- DefaultAnnotationHandlerMapping -->
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<!-- AnnotationMethodHandlerAdapter -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<context:component-scan base-package="com.spring"/> <!-- 애너테이션 적용 범위 설정 -->
</beans>
pro26/src/com/spring/ex01/MainController.java
package com.spring.ex01; //<component-scan>에서 설정한 패키지 또는 하위 패키지
...
/* 요청 단계: 1단계 */
@Controller("mainController") //bean 자동 변환
@RequestMapping("/test") //'/test' 요청 시 mainController bean에 전달
public class MainController {
/* 요청 단계: 2단계 */
@RequestMapping(value="/main1.do", method=RequestMethod.GET) //요청 구분
public ModelAndView main1(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav=new ModelAndView();
mav.addObject("msg","main1"); //전달할 데이터 값
mav.setViewName("main");
return mav;
}
/* 요청 단계: 2단계 */
@RequestMapping(value="/main2.do", method = RequestMethod.GET) //요청 구분
public ModelAndView main2(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav=new ModelAndView();
mav.addObject("msg","main2"); //전달할 데이터 값
mav.setViewName("main");
return mav;
}
}
pro26/WebContent/WEB-INF/views/test/main.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<% request.setCharacterEncoding("UTF-8"); %>
<html>
<head>
<meta charset="UTF-8">
<title>결과창</title>
</head>
<body>
<h1>안녕하세요!!</h1>
<h1>${msg <!-- 컨트롤러에서 넘어온 데이터 -->} 페이지입니다!!</h1>
</body>
</html>
pro26/WebContent/WEB-INF/web.xml
...
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
pro26/src/com/spring/ex02/LoginController.java
package com.spring.ex02;
...
@Controller("loginController")
public class LoginController {
@RequestMapping(value = { "/test/loginForm.do", "/test/loginForm2.do" }, method={RequestMethod.GET})
public ModelAndView loginForm(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav=new ModelAndView();
mav.setViewName("loginForm");
return mav;
}
@RequestMapping(value = "/test/login.do", method {RequestMethod.GET, RequestMethod.POST})
public ModelAndView login(HttpServletRequest request, HttpServletResponse response) throws Exception {
request.setCharacterEncoding("utf-8");
ModelAndView mav=new ModelAndView();
mav.setViewName("result");
String userID=request.getParameter("userID");
String userName =request.getParameter("userName");
mav.addObject("userID", userID);
mav.addObject("userName", userName);
return mav;
}
}
pro26/WebContent/WEB-INF/test/loginForm.jsp
...
<form method="post" action="${contextPath}/test/login.do">
<table width="400">
<tr>
<td>아이디 <input type="text" name="userID" size="10"></td>
</tr>
<tr>
<td>이름 <input type="text" name="userName" size="10"></td>
</tr>
...
pro26/WebContent/WEB-INF/test/result.jsp
...
<h1>아이디 : ${userID}</h1>
<h1>이름 : ${userName}</h1>
...
pro26/src/com/spring/ex02/LoginController.java
package com.spring.ex02;
...
@Controller("loginController")
public class LoginController {
...
@RequestMapping(value = "/test/login.do", method = {RequestMethod.GET, RequestMethod.POST})
public ModelAndView login (
@RequestParam("userID") String userID, //매개변수가 userID일 경우 변수 userID 설정
@RequestParam(value="userName", required=true) String userName, //매개변수가 userName일 경우 변수 userName 설정
@RequestParam(value="email", required=false) String email, //매개변수가 email일 경우 변수 userName 설정
HttpServletRequest request, HttpServletResponse response
) throws Exception {
request.setCharacterEncoding("utf-8");
ModelAndView mav = new ModelAndView();
mav.setViewName("result");
System.out.println("userID: "+userID);
System.out.println("userName: "+userName);
System.out.println("email: "+email);
mav.addObject("userID", userID);
mav.addObject("userName", userName);
return mav;
}
}
pro26/WebContent/WEB-INF/test/loginForm.jsp
...
<form method="post" action="${contextPath}/test/login2.do">
<input type="hidden" name="email" value="hong@test.com" /> <!-- @RequestParam email -->
<table width="400">
<tr>
<td>아이디 <input type="text" name="userID" size="10"></td> <!-- @RequestParam userID -->
</tr>
<tr>
<td>이름 <input type="text" name="userName" size="10"></td> <!-- @RequestParam userName -->
</tr>
...
pro26/src/com/spring/ex02/LoginController.java
package com.spring.ex02;
...
@Controller("loginController")
public class LoginController {
...
@RequestMapping(value = "/test/login.do", method = {RequestMethod.GET, RequestMethod.POST})
public ModelAndView login (
@RequestParam Map<String, String> info, HttpServletRequest request, HttpServletResponse response
) throws Exception {
/* 테스트용 */
String userID = info.get("userID");
String userName = info.get("userName");
System.out.println("userID: "+userID);
System.out.println("userName: "+userName);
mav.addObject("info", info);
mav.setViewName("result");
return mav;
}
}
pro26/WebContent/WEB-INF/views/test/result.jsp
...
<h1>아이디 : ${info.userID} </h1>
<h1>이름 : ${info.userName} </h1>
...
pro26/src/com/spring/ex02/LoginController.java
package com.spring.ex02;
...
@Controller("loginController")
public class LoginController {
...
@RequestMapping(value = "/test/login.do", method = { RequestMethod.GET, RequestMethod.POST })
public ModelAndView login ( @ModelAttribute("info") LoginVO loginVO, HttpServletRequest request, HttpServletResponse response ) throws Exception {
//info만으로 jsp에서 바로 접근 가능
request.setCharacterEncoding("utf-8");
ModelAndView mav = new ModelAndView();
System.out.println("userID: "+loginVO.getUserID());
System.out.println("userName: "+loginVO.getUserName());
mav.setViewName("result");
return mav;
}
}
pro26/src/com/spring/ex02/LoginVO.java
package com.spring.ex02;
public class LoginVO {
private String userID;
private String userName;
//각 속성에 대한 getter/setter 메서드
...
}
pro26/WebContent/WEB-INF/views/test/result.jsp
...
<h1>아이디 : ${info.userID} </h1> <!-- ModelAttribute info -->
<h1>이름 : ${info.userName} </h1>
...
별도의 뷰 정보를 전달할 필요가 없을 때 사용
pro26/src/com/spring/ex02/LoginController.java
package com.spring.ex02;
import org.springframework.ui.Model;
...
@Controller("loginController")
public class LoginController {
...
@RequestMapping(value = "/test/login5.do", method = {RequestMethod.GET, RequestMethod.POST})
public String login(Model model, HttpServletRequest request, HttpServletResponse response) throws Exception {
//메소드 호출 시 Model 클래스 객체 생성
request.setCharacterEncoding("utf-8");
/* model에 데이터 바인딩 */
model.addAttribute("userID", "hong");
model.addAttribute("userName", "홍길동");
return "result";
}
}
pro26/WebContent/WEB-INF/views/test/result.jsp
...
<h1>아이디 : ${userID}</h1>
<h1>이름 : ${userName}</h1>
...
클래스 bean을 자바 코드에서 직접 생성할 수 있도록 하는 bean 자동 주입 애너테이션
@Controller: controller bean
@Service: service bean
@Repository: DAO bean
@Component: VO bean
pro26/WebContent/WEB-INF/web.xml
...
<listener>
<listener-class> org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/config/action-mybatis.xml
</param-value>
</context-param>
...
pro26/WebContent/WEB-INF/action-servlet.xml
...
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/member/" /> <!-- jsp 경로 설정 -->
<property name="suffix" value=".jsp"/>
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<context:component-scan base-package="com.spring"/>
</beans>
pro26/WebContent/WEB-INF/config/action-mybatis.xml
스프링에서 제공하는 클래스 bean의 경우 xml 설정 필요
...
<bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource">
<property name="driver" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis/models/modelConfig.xml" />
<property name="mapperLocations" value="classpath:mybatis/mappers/*.xml" />
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"></constructor-arg>
</bean>
<!-- MemberDAO 설정 불필요 -->
</beans>
pro26/src/com/spring/member/controller/MemberControllerImpl.java
package com.spring.member.controller;
...
@Controller("memberController") //memberController bean 자동 생성
public class MemberControllerImpl implements MemberController {
/* memberService bean 자동 주입 */
@Autowired
private MemberService memberService;
/* memberVO bean 자동 주입 */
@Autowired
private MemberVO memberVO;
@Override
@RequestMapping(value="/member/listMembers.do", method = RequestMethod.GET)
public ModelAndView listMembers(HttpServletRequest request, HttpServletResponse response) throws Exception {
String viewName = getViewName(request);
List membersList = memberService.listMembers();
ModelAndView mav = new ModelAndView(viewName);
mav.addObject("membersList", membersList);
return mav;
}
@Override
@RequestMapping(value="/member/addMember.do", method = RequestMethod.POST)
public ModelAndView addMember(@ModelAttribute("member") MemberVO member, HttpServletRequest request, HttpServletResponse response) throws Exception { //MemberVO 객체 사용
request.setCharacterEncoding("utf-8");
int result = 0;
result = memberService.addMember(member); //memberVO 객체 전달 및 회원 등록
ModelAndView mav = new ModelAndView("redirect:/member/listMembers.do");
return mav;
}
@Override
@RequestMapping(value="/member/removeMember.do", method = RequestMethod.GET)
public ModelAndView removeMember(@RequestParam("id") String id, HttpServletRequest request, HttpServletResponse response) throws Exception {
request.setCharacterEncoding("utf-8");
memberService.removeMember(id);
ModelAndView mav = new ModelAndView("redirect:/member/listMembers.do");
return mav;
}
@RequestMapping(value = "/member/*Form.do", method = RequestMethod.GET) //요청명 정규식 검사
public ModelAndView form(HttpServletRequest request, HttpServletResponse response) throws Exception {
String viewName = getViewName(request);
ModelAndView mav = new ModelAndView();
mav.setViewName(viewName);
return mav;
}
...
pro26/src/com/spring/member/service/MemberServiceImpl.java
package com.spring.member.service;
...
@Service("memberService") //memberService bean 자동 생성
@Transactional(propagation = Propagation.REQUIRED)
public class MemberServiceImpl implements MemberService {
/* memberDAO bean 자동 주입 */
@Autowired
private MemberDAO memberDAO;
@Override
public List listMembers() throws DataAccessException {
List membersList = null;
membersList = memberDAO.selectAllMemberList();
return membersList;
}
@Override
public int addMember(MemberVO member) throws DataAccessException {
return memberDAO.insertMember(member);
}
@Override
public int removeMember(String id) throws DataAccessException {
return memberDAO.deleteMember(id);
}
}
pro26/src/com/spring/member/dao/MemberDAOImpl.java
package com.spring.member.dao;
...
@Repository("memberDAO") //memberDAO bean 자동 생성
public class MemberDAOImpl implements MemberDAO {
/* sqlSession bean 자동 주입 */
@Autowired
private SqlSession sqlSession;
@Override
public List selectAllMemberList() throws DataAccessException {
List<MemberVO> membersList = null;
membersList = sqlSession.selectList("mapper.member.selectAllMemberList");
return membersList;
}
@Override
public int insertMember(MemberVO memberVO) throws DataAccessException {
int result = sqlSession.insert("mapper.member.insertMember", memberVO);
return result;
}
@Override
public int deleteMember(String id) throws DataAccessException {
int result = sqlSession.delete("mapper.member.deleteMember", id);
return result;
}
}
pro26/src/com/spring/member/vo/MemberVO.java
package com.spring.member.vo;
import org.springframework.stereotype.Component;
@Component("memberVO") //memberVO bean 자동 생성
public class MemberVO {
private String id;
private String pwd;
private String name;
private String email;
private Date joinDate;
public MemberVO() { }
public MemberVO(String id, String pwd, String name, String email) {
this.id = id;
this.pwd = pwd;
this.name = name;
this.email = email;
}
//getter, setter
...
*자바 웹을 다루는 기술