-
[Spring] 게시판 만들기(16) - 트랜잭션(Transaction) 설정Spring/4.3.x - 게시판 만들기 2018. 12. 19. 09:17반응형
처리 과정 중 한 가지라도 실패할 경우 처리 과정을 취소시킴으로써 데이터의 무결성을 보장하기 위한 트랜잭션 설정을 진행하도록 하겠습니다.
1. pom.xml에 의존성 추가
트랜잭션과 관련된 의존성을 추가하세요.(aspectjweaver.jar, cglib-nodep.jar)
pom.xml
더보기123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.spring</groupId><artifactId>board</artifactId><name>board</name><packaging>war</packaging><version>1.0.0-BUILD-SNAPSHOT</version><properties><java-version>1.8</java-version><org.springframework-version>4.3.6.RELEASE</org.springframework-version><junit.version>4.12</junit.version><org.aspectj-version>1.6.10</org.aspectj-version><org.slf4j.version>1.7.21</org.slf4j.version><logback.version>1.1.7</logback.version><mysql.version>5.1.46</mysql.version><mybatis.version>3.4.6</mybatis.version><mybatis.spring.version>1.3.2</mybatis.spring.version><jackson.version>2.9.5</jackson.version><cglib.version>3.2.3</cglib.version></properties><dependencies><!-- Spring --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${org.springframework-version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>${org.springframework-version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>${org.springframework-version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>${org.springframework-version}</version></dependency><!-- AspectJ --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>${org.aspectj-version}</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>${org.aspectj-version}</version></dependency><!-- CGLIB --><dependency><groupId>cglib</groupId><artifactId>cglib-nodep</artifactId><version>${cglib.version}</version></dependency><!-- Logging --><dependency><groupId>org.slf4j</groupId><artifactId>jcl-over-slf4j</artifactId><version>${org.slf4j.version}</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>${logback.version}</version></dependency><!-- @Inject --><dependency><groupId>javax.inject</groupId><artifactId>javax.inject</artifactId><version>1</version></dependency><!-- Servlet --><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.5</version><scope>provided</scope></dependency><dependency><groupId>javax.servlet.jsp</groupId><artifactId>jsp-api</artifactId><version>2.1</version><scope>provided</scope></dependency><dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency><!-- Test --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version><scope>test</scope></dependency><!-- MySQL --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version></dependency><!-- MyBatis --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>${mybatis.version}</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>${mybatis.spring.version}</version></dependency><!-- Jackson --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId><version>${jackson.version}</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>${jackson.version}</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>${jackson.version}</version></dependency></dependencies><build><plugins><plugin><artifactId>maven-eclipse-plugin</artifactId><version>2.9</version><configuration><additionalProjectnatures><projectnature>org.springframework.ide.eclipse.core.springnature</projectnature></additionalProjectnatures><additionalBuildcommands><buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand></additionalBuildcommands><downloadSources>true</downloadSources><downloadJavadocs>true</downloadJavadocs></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>2.5.1</version><configuration><source>1.8</source><target>1.8</target><compilerArgument>-Xlint:all</compilerArgument><showWarnings>true</showWarnings><showDeprecation>true</showDeprecation></configuration></plugin><plugin><groupId>org.codehaus.mojo</groupId><artifactId>exec-maven-plugin</artifactId><version>1.2.1</version><configuration><mainClass>org.test.int1.Main</mainClass></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>2.19.1</version><configuration><skipTests>true</skipTests></configuration></plugin></plugins></build></project>cs 2. servlet-context.xml 수정
include-filter와 use-default-filters 옵션들을 사용하여 디폴트 애노테이션들을 스캔 대상에서 제외하고 @Controller을 스캔할 수 있도록 설정하세요.
servlet-context.xml
더보기123456789101112131415161718192021222324252627282930313233343536373839404142<?xml version="1.0" encoding="UTF-8"?><beans:beans xmlns="http://www.springframework.org/schema/mvc"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:beans="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure --><!-- Enables the Spring MVC @Controller programming model --><annotation-driven /><!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the $/resources directory --><resources mapping="/resources/**" location="/resources/" /><resources mapping="/css/**" location="/css/" /><resources mapping="/img/**" location="/img/" /><resources mapping="/js/**" location="/js/" /><!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory --><beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><beans:property name="prefix" value="/WEB-INF/views/" /><beans:property name="suffix" value=".jsp" /></beans:bean><context:component-scan base-package="com.spring.board" use-default-filters="false"><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /></context:component-scan><mvc:interceptors><mvc:interceptor><mvc:mapping path="/**"/><beans:bean id="loggerInterceptor" class="com.spring.board.common.LoggerInterceptor"></beans:bean></mvc:interceptor></mvc:interceptors><beans:bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" /></beans:beans>cs 3. root-context.xml 수정
- include-filter와 use-default-filters 옵션들을 사용하여 디폴트 애노테이션들을 스캔 대상에서 제외하고 @Service와 @Repository을 스캔할 수 있도록 설정하세요.
- tx 네임스페이스를 추가한 후 <tx:advice> 태그, <tx:attributes> 태그, <tx:method> 태그를 이용해서 트랜잭션 속성을 정의하세요.root-context.xml
더보기1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xmlns:jdbc="http://www.springframework.org/schema/jdbc"xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsdhttp://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd"><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://localhost:3306/board?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC" /><property name="username" value="board" /><property name="password" value="password1!" /></bean><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource" /><property name="configLocation" value="classpath:mybatis-config.xml" /><property name="mapperLocations" value="classpath:mappers/**/*.xml"></property></bean><bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache"><constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/></bean><context:component-scan base-package="com.spring.board" use-default-filters="false"><context:include-filter type="annotation" expression="org.springframework.stereotype.Service" /><context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" /></context:component-scan><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><aop:config proxy-target-class="true"><aop:pointcut id="serviceOperation" expression="execution(public * com.spring.board..service.*Service.*(..))" /><aop:advisor id="transactionAdvisor" pointcut-ref="serviceOperation" advice-ref="txAdvice"/></aop:config><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="insert*" rollback-for="Exception"/><tx:method name="update*" rollback-for="Exception"/><tx:method name="delete*" rollback-for="Exception"/></tx:attributes></tx:advice></beans>cs 4. 트랜잭션(Transaction) 설정 확인
insertBoard를 복사하여 id는 insertBoardFail로 변경하고 parameter 중 #{board_writer}를 #{board_writer1}로 변경하여 BoardForm에서 getter를 찾지 못하게 하여 insert 시에 에러가 발생하도록 한 후 트랜잭션이 적용되는지 확인하겠습니다.
BoardService.java
더보기123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899package com.spring.board.service;import java.io.FileNotFoundException;import java.util.List;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import com.spring.board.dao.BoardDao;import com.spring.board.dto.BoardDto;import com.spring.board.form.BoardForm;@Service()public class BoardService {@Autowiredprivate BoardDao boardDao;/** 게시판 - 목록 조회 */public List<BoardDto> getBoardList(BoardForm boardForm) throws Exception {return boardDao.getBoardList(boardForm);}/** 게시판 - 상세 조회 */public BoardDto getBoardDetail(BoardForm boardForm) throws Exception {BoardDto boardDto = new BoardDto();String searchType = boardForm.getSearch_type();if ("S".equals(searchType)) {int updateCnt = boardDao.updateBoardHits(boardForm);if (updateCnt > 0) {boardDto = boardDao.getBoardDetail(boardForm);}} else {boardDto = boardDao.getBoardDetail(boardForm);}return boardDto;}/** 게시판 - 등록 */public BoardDto insertBoard(BoardForm boardForm) throws Exception {BoardDto boardDto = new BoardDto();int insertCnt = 0;insertCnt = boardDao.insertBoard(boardForm);insertCnt = boardDao.insertBoardFail(boardForm);if (insertCnt > 0) {boardDto.setResult("SUCCESS");} else {boardDto.setResult("FAIL");}return boardDto;}/** 게시판 - 삭제 */public BoardDto deleteBoard(BoardForm boardForm) throws Exception {BoardDto boardDto = new BoardDto();int deleteCnt = boardDao.deleteBoard(boardForm);if (deleteCnt > 0) {boardDto.setResult("SUCCESS");} else {boardDto.setResult("FAIL");}return boardDto;}/** 게시판 - 수정 */public BoardDto updateBoard(BoardForm boardForm) throws Exception {BoardDto boardDto = new BoardDto();int deleteCnt = boardDao.updateBoard(boardForm);if (deleteCnt > 0) {boardDto.setResult("SUCCESS");} else {boardDto.setResult("FAIL");}return boardDto;}}cs BoardDao.java
더보기123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960package com.spring.board.dao;import java.util.List;import javax.annotation.Resource;import org.apache.ibatis.session.SqlSession;import org.springframework.stereotype.Repository;import com.spring.board.dto.BoardDto;import com.spring.board.form.BoardForm;@Repositorypublic class BoardDao {@Resource(name = "sqlSession")private SqlSession sqlSession;private static final String NAMESPACE = "com.spring.board.boardMapper";/** 게시판 - 목록 조회 */public List<BoardDto> getBoardList(BoardForm boardForm) throws Exception {return sqlSession.selectList(NAMESPACE + ".getBoardList", boardForm);}/** 게시판 - 조회 수 수정 */public int updateBoardHits(BoardForm boardForm) throws Exception {return sqlSession.update(NAMESPACE + ".updateBoardHits", boardForm);}/** 게시판 - 상세 조회 */public BoardDto getBoardDetail(BoardForm boardForm) throws Exception {return sqlSession.selectOne(NAMESPACE + ".getBoardDetail", boardForm);}/** 게시판 - 등록 */public int insertBoard(BoardForm boardForm) throws Exception {return sqlSession.insert(NAMESPACE + ".insertBoard", boardForm);}/** 게시판 - 등록 실패(트랜잭션 테스트) */public int insertBoardFail(BoardForm boardForm) throws Exception {return sqlSession.insert(NAMESPACE + ".insertBoardFail", boardForm);}/** 게시판 - 삭제 */public int deleteBoard(BoardForm boardForm) throws Exception {return sqlSession.delete(NAMESPACE + ".deleteBoard", boardForm);}/** 게시판 - 수정 */public int updateBoard(BoardForm boardForm) throws Exception {return sqlSession.update(NAMESPACE + ".updateBoard", boardForm);}}cs boardMapper.xml
더보기123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.spring.board.boardMapper"><select id="getBoardList" parameterType="com.spring.board.form.BoardForm" resultType="com.spring.board.dto.BoardDto">SELECT BOARD_SEQ, BOARD_RE_REF, BOARD_RE_LEV, BOARD_RE_SEQ, BOARD_WRITER, BOARD_SUBJECT, BOARD_CONTENT, BOARD_HITS, DEL_YN, INS_USER_ID, CAST( DATE_FORMAT( INS_DATE, '%Y-%m-%d %H:%i:%s' ) AS CHAR(19) ) AS INS_DATE, UPD_USER_ID, CAST( DATE_FORMAT( UPD_DATE, '%Y-%m-%d %H:%i:%s' ) AS CHAR(19) ) AS UPD_DATEFROM BOARD.TB_BOARDORDER BY BOARD_RE_REF DESC, BOARD_RE_SEQ ASC</select><delete id="updateBoardHits" parameterType="com.spring.board.form.BoardForm">UPDATE BOARD.TB_BOARDSET BOARD_HITS = BOARD_HITS + 1, UPD_USER_ID = 'NONMEMBER', UPD_DATE = NOW()WHERE BOARD_SEQ = #{board_seq}</delete><select id="getBoardDetail" parameterType="com.spring.board.form.BoardForm" resultType="com.spring.board.dto.BoardDto">SELECT BOARD_SEQ, BOARD_RE_REF, BOARD_RE_LEV, BOARD_RE_SEQ, BOARD_WRITER, BOARD_SUBJECT, BOARD_CONTENT, BOARD_HITS, DEL_YN, INS_USER_ID, CAST( DATE_FORMAT( INS_DATE, '%Y-%m-%d %H:%i:%s' ) AS CHAR(19) ) AS INS_DATE, UPD_USER_ID, CAST( DATE_FORMAT( UPD_DATE, '%Y-%m-%d %H:%i:%s' ) AS CHAR(19) ) AS UPD_DATEFROM BOARD.TB_BOARDWHERE BOARD_SEQ = #{board_seq}</select><insert id="insertBoard" parameterType="com.spring.board.form.BoardForm">INSERTINTO BOARD.TB_BOARD(BOARD_RE_REF, BOARD_RE_LEV, BOARD_RE_SEQ, BOARD_WRITER, BOARD_SUBJECT, BOARD_CONTENT, INS_USER_ID, INS_DATE, UPD_USER_ID, UPD_DATE)VALUES(0, 0, 0, #{board_writer}, #{board_subject}, #{board_content}, 'NONMEMBER', NOW(), 'NONMEMBER', NOW())</insert><insert id="insertBoardFail" parameterType="com.spring.board.form.BoardForm">INSERTINTO BOARD.TB_BOARD(BOARD_RE_REF, BOARD_RE_LEV, BOARD_RE_SEQ, BOARD_WRITER, BOARD_SUBJECT, BOARD_CONTENT, INS_USER_ID, INS_DATE, UPD_USER_ID, UPD_DATE)VALUES(0, 0, 0, #{board_writer1}, #{board_subject}, #{board_content}, 'NONMEMBER', NOW(), 'NONMEMBER', NOW())</insert><delete id="deleteBoard" parameterType="com.spring.board.form.BoardForm">DELETEFROM BOARD.TB_BOARDWHERE BOARD_SEQ = #{board_seq}</delete><delete id="updateBoard" parameterType="com.spring.board.form.BoardForm">UPDATE BOARD.TB_BOARDSET BOARD_SUBJECT = #{board_subject}, BOARD_CONTENT = #{board_content}, UPD_USER_ID = 'NONMEMBER', UPD_DATE = NOW()WHERE BOARD_SEQ = #{board_seq}</delete></mapper>cs insertBoard는 insert는 성공, insertBoardFail는 insert 실패한 후 테이블에 값이 저장되었는지 확인해보세요. 값이 저장되지 않으면 정상적으로 트랜잭션이 적용된 것 입니다. 정상적으로 적용된 것이 확인되셨으면, boardService.java에서 boardinsertBoardFail 메서드를 호출하는 부분은 삭제 또는 주석 처리하세요.
소스 코드는 Github Repository - https://github.com/tychejin1218/board_v1 (branch : section16)를 참조하세요.
Github에서 프로젝트 가져오기 - https://tychejin.tistory.com/33반응형'Spring > 4.3.x - 게시판 만들기' 카테고리의 다른 글
[Spring] 게시판 만들기(18) - 계층형 게시판(답글쓰기) 적용 (11) 2018.12.19 [Spring] 게시판 만들기(17) - 페이징(Paging) 처리 (31) 2018.12.19 [Spring] 게시판 만들기(15) - 게시글 등록(INSERT), 수정(UPDATE), 삭제(DELETE), 상세 조회(SELECT) (28) 2018.12.17 [Spring] 게시판 만들기(14) - 게시글 조회(SELECT) (28) 2018.12.17 [Spring] 게시판 만들기(13) - mapper 및 json 설정 (3) 2018.12.14