트랜잭션을 처리하는 aop를 context로 선언해서 auto로 관리해보고

insert api를 만들어줘보자.


알다시피 트랜잭션은 데이터베이스의 처리하는 단위이다. 즉 데이터베이스의 내용을 커밋해주는 역할도하며,

이 트랜잭션은 뭔가 데이터가 정상적이지못하거나 웹서버가 문제가 있어 중간에 죽으면

insert되던것을 롤백을 해야하는 임무도 더러있다.


현재 우리의 코드에서는 트랜잭션을 처리하는 부분이 존재하지않다.

이때문에 insert 과정중에 크리티컬 에러가 생겨도 롤백을 하지않는다. 이문제는 서비스단에서

엄청 큰문제를 초래 할 수 있다. 데이터는 정말 중요하다. 


그래서 트랜잭션을 여러가지방법으로 처리할수 있게 스프링에서 제공해주지만 내가써본바로는 aop만큼

편한방법을 찾지 못하였다. aop에 대해서 모른다면 스프링에대해서 좀더 공부해보길 추천드린다.


aop로 전체 서비스 객체에 트랜잭션을 걸 수가 있다. 그리고 따로 트랜잭션처리를 안해도 문제가 생기면 알아서 rollback까지

해준다.


자 이제 시작해보자. 일단 처음 해야할작업은 context-transaction.xml를 만드는것이다.

 <?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:tx="http://www.springframework.org/schema/tx"

xsi:schemaLocation="http://www.springframework.org/schema/beans 

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/tx 

http://www.springframework.org/schema/tx/spring-tx.xsd

http://www.springframework.org/schema/aop 

http://www.springframework.org/schema/aop/spring-aop.xsd">


<tx:annotation-driven />

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<property name="dataSource" ref="dataSource"/>

</bean>

<tx:advice id="txAdvice" transaction-manager="txManager">

<tx:attributes>

<tx:method name="get*" read-only="true" />

   <tx:method name="add*" propagation="REQUIRED" />

<tx:method name="modify*" propagation="REQUIRED" />

<tx:method name="remove*" propagation="REQUIRED" />

</tx:attributes>

</tx:advice>

<aop:config>

<aop:pointcut id="requiredTx" expression="execution(* com.pratice.project.service.impl.*Impl.*(..))"/>

<aop:advisor advice-ref="txAdvice" pointcut-ref="requiredTx" />

</aop:config>

</beans>


 


txManager는 dataSource를 트랜잭션 메니저가 트랜잭션을 처리하겠다는 의미고 txAdvice는 txManager에서 설정된 데이터소스에 접근하는 트랜잭션 중에 메서드 명중에 앞글자를 따서 select ,add,modify,remove등이 앞으로오는 메서드가있으면 select 같은경우 read only만 해주고 나머지는 propagation="REQUIRED" 로 트랜잭션 처리를 하겠다는 뜻이다. 즉 값이 변화가생기면 commit을 해주거나 에러가생기면 롤백도 해준다는것이다.


마지막으로 aop에 특정 service 패키지내에 Impl로 끝나는 클래스들을 포인트컷을 걸어두고 advice에 txAdvice를 걸어주면 끝난다.


정상적으로됐다면 이제 트랜잭션처리는 끝난것이다.


간단하지않은가? 

이제 저 메서드의 해당하는 api를 추가만 해주면된다. insert에 해당하는 내용을 한번 만들어보자.

지금까지 전부 get로 했지만 이번에는 POST로 한번 짜보도록해보자.


먼저 IGetDao의 내용을 추가해주자.

 package com.pratice.project.dao;


import com.pratice.project.model.Name;

import com.pratice.project.model.User;


public interface IGetDao {

public Name selectName(String name);

public User selectUser(String id);

public void addUser(User user);

}


 보다시피 addUser이라는 메서드가 트랜잭션 관리하는 txAdvice랑 일치한다. add가 붙어있다. 파라미터는 user그자체를 받고 그 user객체를 insert하겠다는 의미이다. 


이제 쿼리를 작성해주자.

 <?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper

  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">




<mapper namespace="com.pratice.project.dao.IGetDao">



<select id="selectName" parameterType="String" resultType="name">

 <![CDATA[

 SELECT UserName

         FROM usertbl

         WHERE UserName = #{name}

        ]]>

</select>


<select id="selectUser" parameterType="String" resultType="user">

 <![CDATA[

 SELECT *

         FROM usertbl

         WHERE UserID = #{userID}

        ]]>

</select>


<insert id="addUser" parameterType="user">

 <![CDATA[

insert into userTbl(UserID,UserName,UserAge,Phone,Comment) values 

(#{userID},#{userName},#{userAge},#{phone},#{comment});

   ]]>

</insert>

</mapper>

 

insert문이 추가된것이보인다. 여기서 몇가지 주의할점이있다. parametertype를 클래스 형태로 받으면 굳이 따로 변화할 필요없이 #{필드명}을 쓰면 알아서 클래스 객체의 있는 정보를 가지고와서 쓸수 있다. 단! 여기서 필드명의 첫글자는 소문자로 해주어야한다. 이것은 마이바티스 규약 사항처럼 보이는데 대문자로 하면 인식을 못하니 꼭 주의해야한다. 


그리고 보시다시피 클래스 User 객체의 필드들의 이름과 database의 컬럼이름이 똑같은것[단! 클래스 User객체의 필드들의 앞글자는 소문자이다.]을 알 수 있다. 이점을 항상 유의하면서 코드와 쿼리를 작성하기를 바란다.


이제 쿼리가 작성됐다면 Service interface를 정의해주자.

 package com.pratice.project.service;


import com.pratice.project.model.Name;

import com.pratice.project.model.User;


public interface TestService {

public Name getName(String name);

public User getUser(String id);

public void addUser(User user);

}


 

이제 Impl 실행부에 구현을 적어주자

 package com.pratice.project.service.impl;


import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;


import com.pratice.project.dao.IGetDao;

import com.pratice.project.model.Name;

import com.pratice.project.model.User;

import com.pratice.project.service.TestService;


@Service

public class TestServiceImpl implements TestService{


@Autowired

private IGetDao mapper;

@Override

public Name getName(String name) {

// TODO Auto-generated method stub

Name Testreturn = mapper.selectName(name);

return Testreturn;

}

@Override

public User getUser(String id) {

// TODO Auto-generated method stub

User user=mapper.selectUser(id);

return user;

}


@Override

public void addUser(User user) {

// TODO Auto-generated method stub

mapper.addUser(user);

}


}


 


보시다시피 별내용이 없다 단순히 mapper.addUser한 내용이고 파라미터로는 user를 받겠다는 소리다.


여기까지 됐다면 아까처럼 controller를 추가할차례다.

 package com.pratice.project.controller;


import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.bind.annotation.RestController;


import com.pratice.project.model.Name;

import com.pratice.project.model.User;

import com.pratice.project.service.TestService;


@RestController

public class TestController {


@Autowired

TestService testservice;

@RequestMapping(value = "/test/rest", method = RequestMethod.GET)

public Name getName(HttpServletRequest request, HttpServletResponse response,

@RequestParam(value="name")String name) {

Name rt=testservice.getName(name);

return rt;

}

@RequestMapping(value = "/user", method = RequestMethod.GET)

public User getUser(HttpServletRequest request, HttpServletResponse response,

@RequestParam(value="userID")String userID) {

User rt=testservice.getUser(userID);

return rt;

}

@RequestMapping(value = "/adduser", method = RequestMethod.POST)

public void addUser(HttpServletRequest request, HttpServletResponse response,@RequestBody User user) {

testservice.addUser(user);

}

}


기존 컨트롤러 메서드랑 좀 틀린 부분이 보이지않는가? 바로 @RequestBody User user가 아래붙어있는데 이부분은 리퀘스트로 오는부분을 json형태로 받을 수 있도록한것이며 json으로 받은데이터를 user객체에 대입해준것이다. 그리고 선언한 서비스 객체의 addUser를 해준것이다.


이제 정상적으로 동작하는지 RestClient로 확인해보자


위와같이 insert api란 것을 새로 save하고 url를 적어주고 Post를 선택해주자.

Post를 선택하면 아래와같이 json을 작성하는 부분이 출력된다. 그부분에 해당과 같이 작성해주자.



 { "userID":"goo92",

  "userName":"goomankim",

  "userAge":"27",

  "phone":"010...",

  "comment":"gogo"

}

 

보다시피 클래스 user객체의 필드명과 대소문자가 정확하게 일치한다! 정확히 일치해야 값을 얻어올 수 있으니 반드시 주의하기바란다.


이제 send를 눌러보자. 아마 아래 아무런 데이터 반응이 없다고 뜨면 성공적이다(이부분은 다음 챕터에서 보완을 하는 내용을 기재하겠다)


이제 데이터베이스의 내용을 확인해보자(select * fromusertbl)




위와같이 데이터베이스에 정확하게 들어간것을 확인 할 수 있다.


다음챕터에서는 에러처리를 하는 리턴문과 여러 데이터들을 한꺼번에 리스트형태로 리턴하는것을 기재할예정이다.



'DEV > SPRING' 카테고리의 다른 글

Rest Service 구현[7]  (0) 2018.05.11
Rest Service 구현[6]  (0) 2018.05.11
Rest Service 구현[4]  (0) 2018.05.11
Rest Service 구현[3]  (0) 2018.05.11
Rest Service 구현[2]  (0) 2018.05.11

+ Recent posts