어느 레스토랑에서 메뉴와 갯수를 주문하여 테이블에 저장하는 테이블이다
하지만 만약 저장에 실패했을경우 트랜잭션을 Rollback 처리해야한다
그것을 스프링 AOP기능 이용하여 실습하였습니다.
우선 테이블 목록입니다.
TBL_ORDER
TBL_ORDER_MENU
각 객체를 저장할수있는 VO 파일입니다.
Order.java
package com.memory4.javaconfig.model.vo;
import java.util.List;
public class Order {
private int code;
private String date;
private String time;
private int orderPrice;
private List<OrderMenu> menuList;
public Order() { }
public Order(int code, String date, String time, int orderPrice) {
super();
this.code = code;
this.date = date;
this.time = time;
this.orderPrice = orderPrice;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public int getOrderPrice() {
return orderPrice;
}
public void setOrderPrice(int orderPrice) {
this.orderPrice = orderPrice;
}
public List<OrderMenu> getMenuList() {
return menuList;
}
public void setMenuList(List<OrderMenu> menuList) {
this.menuList = menuList;
}
@Override
public String toString() {
return "Order [code=" + code + ", date=" + date + ", time=" + time + ", orderPrice=" + orderPrice
+ ", menuList=" + menuList + "]";
}
}
OrderMenu.java
package com.memory4.javaconfig.model.vo;
public class OrderMenu {
private int orderCode;
private int menuCode;
private int amount;
public OrderMenu() { }
public OrderMenu(int orderCode, int menuCode, int amount) {
super();
this.orderCode = orderCode;
this.menuCode = menuCode;
this.amount = amount;
}
public int getOrderCode() {
return orderCode;
}
public void setOrderCode(int orderCode) {
this.orderCode = orderCode;
}
public int getMenuCode() {
return menuCode;
}
public void setMenuCode(int menuCode) {
this.menuCode = menuCode;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
@Override
public String toString() {
return "OrderMenu [orderCode=" + orderCode + ", menuCode=" + menuCode + ", amount=" + amount + "]";
}
}
이아래부터는 애너테이션버전과 xml버전으로 나뉩니다.
1. xml 버전
Application.java(main)
메인 클래스에서 실행시
주문 메뉴코드, 주문 수량 을 입력하여 List<OrderMenu> 에 저장하고
사용자가 주문을 더하지않는다고 'N' 의값을 입력하게 되면 반복이 종료되어 Order 객체에 값이 필드에 저장됩니다.
저장한 Order 객체의값을 Service에 보내어
비즈니스로직 실행후 1또는 0의 값이 돌아오게되고
값이 0일시 실패 하였다는 메세지가 출력되고,
값이 1일시 성공 하였다는 메세지가 출력됩니다.
package com.memory4.xmlconfig.run;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Scanner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import com.memory4.xmlconfig.model.service.OrderService;
import com.memory4.xmlconfig.model.vo.Order;
import com.memory4.xmlconfig.model.vo.OrderMenu;
public class Application {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
ApplicationContext context = new GenericXmlApplicationContext("com/memory4/xmlconfig/config/spring-context.xml");
List<OrderMenu> orderMenuList = new ArrayList<>();
do {
System.out.println("========== 레스토랑 음식 주문 서비스 ==========");
System.out.print("어떤 메뉴를 주문하시겠습니까?(코드입력) : ");
int menuCode = sc.nextInt();
System.out.print("주문 수량을 입력해주세요 : ");
int amount = sc.nextInt();
sc.nextLine();
System.out.print("다른 메뉴를 추가로 주문하시겠습니까?(Y/N) : ");
char orderYN = sc.nextLine().toUpperCase().charAt(0);
OrderMenu orderMenu = new OrderMenu();
orderMenu.setMenuCode(menuCode);
orderMenu.setAmount(amount);
orderMenuList.add(orderMenu);
if(orderYN != 'Y') break;
}while(true);
Order order = new Order();
SimpleDateFormat date = new SimpleDateFormat("yyyyMMdd");
SimpleDateFormat time = new SimpleDateFormat("HH:mm:ss");
String dateString = date.format(new Date());
String timeString = time.format(new Date());
order.setDate(dateString);
order.setTime(timeString);
order.setMenuList(orderMenuList);
OrderService orderService = context.getBean("orderService", OrderService.class);
System.out.println(order);
int result = orderService.register(order);
if(result > 0) {
System.out.println("메뉴 추가에 성공하였습니다.");
}else {
System.out.println("메뉴 추가에 실패하였습니다.");
}
}
}
OrderService.interface
package com.memory4.xmlconfig.model.service;
import com.memory4.xmlconfig.model.vo.Order;
public interface OrderService {
int register(Order order);
}
OrderServiceImpl.java
스프링 IOC로 관리위해 @Componet 의 하위 애너테이션인 Service로 빈을 등록하고
빈 이름은 "orderService" 입니다.
전달받은 Order객체를 이용해 우선 TBL_ORDER 테이블먼저 INSERT 진행합니다.
그후 INSERT진행후 돌아온값이 0이상이면 그다음 INSERT작업 진행합니다.
LIST에 담겨있는 SIZE() 만큼 반복문으로 INSERT를 진행하고
만약 result1 과 result2의 값이 0이상이면 1반환하고
값이 하나라도 0이면 0을 반환합니다.(1:성공/0:실패)
package com.memory4.xmlconfig.model.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.memory4.xmlconfig.model.dao.OrderMapper;
import com.memory4.xmlconfig.model.vo.Order;
import com.memory4.xmlconfig.model.vo.OrderMenu;
@Service("orderService")
public class OrderServiceImpl implements OrderService{
private OrderMapper orderMapper;
@Autowired
public OrderServiceImpl(OrderMapper orderMapper) {
this.orderMapper = orderMapper;
}
@Override
public int register(Order order) {
int result1 = orderMapper.insertMenu(order);
List<OrderMenu> orderMenuList = order.getMenuList();
int result2 = 0;
for(OrderMenu orderMenu : orderMenuList) {
result2 += orderMapper.insertOrderMenu(orderMenu);
}
int result = 0;
if(result1 > 0 && result2 == orderMenuList.size()) {
result = 1;
}
return result;
}
}
OrderMapper.interface
package com.memory4.xmlconfig.model.dao;
import com.memory4.xmlconfig.model.vo.Order;
import com.memory4.xmlconfig.model.vo.OrderMenu;
public interface OrderMapper {
int insertMenu(Order order);
int insertOrderMenu(OrderMenu orderMenu);
}
OrderMapper.xml
mybatis 프레임워크 의 쿼리문을 작성한 파일입니다.
<?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.memory4.xmlconfig.model.dao.OrderMapper">
<insert id="insertMenu" parameterType="com.memory4.xmlconfig.model.vo.Order">
INSERT
INTO TBL_ORDER
(
ORDER_CODE
, ORDER_DATE
, ORDER_TIME
, TOTAL_ORDER_PRICE
)
VALUES
(
SEQ_ORDER_CODE.NEXTVAL
, #{date}
, #{time}
, #{orderPrice}
)
</insert>
<insert id="insertOrderMenu" parameterType="com.memory4.xmlconfig.model.vo.OrderMenu">
<selectKey resultType="_int" keyProperty="orderCode" order="BEFORE">
SELECT
SEQ_ORDER_CODE.CURRVAL
FROM DUAL
</selectKey>
INSERT
INTO TBL_ORDER_MENU
(
ORDER_CODE
, MENU_CODE
, ORDER_AMOUNT
)
VALUES
(
#{orderCode}
, #{menuCode}
, #{amount}
)
</insert>
</mapper>
mybatis-config.xml
mybatis 기본설정 xml파일입니다
스프링사용시 mybatis 연결설정은 이 파일에서 설정하지 않고 spring-context.xml 파일에서 설정을 진행합니다.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
spring-context.xml
스프링 기본설정 xml 파일입니다.
우선 커넥션 연결정보는 properties에 저장해두었고, 그 저장되어있는값을
읽어와 BasicDataSource 객체를 빈에등록하고 프로퍼티의 값을 setter 이용해 값을 주입합니다.
SqlSessionFactoryBean객체를 빈에 등록하고 dataSource 객체를 주입하고, mybatis 설정파일 위치를 지정해줍니다.
실질적으로 커넥션할때 사용하는 SqlSessionTemplate 객체를 빈에 등록하고 생성자이용하여 위에 생성한 Bean 객체를 주입해줍니다.
1. 데이터소스 생성
2. 팩토리 빈 생성
3. 템플릿 생성
이순서로 작업하면 편리합니다.
<?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:context="http://www.springframework.org/schema/context"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<context:property-placeholder location="connection-info.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${oracle.dev.driver}"/>
<property name="url" value="${oracle.dev.url}"/>
<property name="username" value="${oracle.dev.username}"/>
<property name="password" value="${oracle.dev.password}"/>
<property name="defaultAutoCommit" value="false"/>
</bean>
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="com/memory4/xmlconfig/config/mybatis-config.xml"/>
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"/>
</bean>
<context:component-scan base-package="com.memory4.xmlconfig"/>
<mybatis-spring:scan base-package="com.memory4.xmlconfig.model.dao"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<aop:aspectj-autoproxy proxy-target-class="true"/>
<tx:advice id="txVisor" transaction-manager="transactionManager" >
<tx:attributes>
<tx:method name="select*" read-only="true" rollback-for="Exception"/>
<tx:method name="register*" propagation="REQUIRED" isolation="SERIALIZABLE" rollback-for="Exception"/>
<tx:method name="delete*" propagation="REQUIRED" isolation="SERIALIZABLE" rollback-for="Exception"/>
<tx:method name="modify*" propagation="REQUIRED" isolation="SERIALIZABLE" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<aop:config proxy-target-class="true">
<aop:pointcut expression="execution(* com.memory4.xmlconfig.model.service..*(..))" id="serviceMethod"/>
<aop:advisor id="transactionAdvisor" advice-ref="txVisor" pointcut-ref="serviceMethod"/>
</aop:config>
</beans>
실행화면
만약 메뉴를 추가하다가 오류가나게 되면 rollback되는처리는 위에 spring-context.xml 파일의 <aop:> <tx:> 부분으로 처리하였습니다.
2. xml+애너테이션 버전
설명은 애너테이션에 작성하였습니다.
방법은비슷하지만 애너테이션이 조금 더 소스가 간단합니다.
Application.java(main)
package com.memory4.javaconfig.run;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Scanner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import com.memory4.javaconfig.model.service.OrderService;
import com.memory4.javaconfig.model.vo.Order;
import com.memory4.javaconfig.model.vo.OrderMenu;
public class Application {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
ApplicationContext context = new GenericXmlApplicationContext("com/memory4/javaconfig/config/spring-context.xml");
List<OrderMenu> orderMenuList = new ArrayList<>();
do {
System.out.println("========== 레스토랑 음식 주문 서비스 ==========");
System.out.print("어떤 메뉴를 주문하시겠습니까?(코드입력) : ");
int menuCode = sc.nextInt();
System.out.print("주문 수량을 입력해주세요 : ");
int amount = sc.nextInt();
sc.nextLine();
System.out.print("다른 메뉴를 추가로 주문하시겠습니까?(Y/N) : ");
char orderYN = sc.nextLine().toUpperCase().charAt(0);
OrderMenu orderMenu = new OrderMenu();
orderMenu.setMenuCode(menuCode);
orderMenu.setAmount(amount);
orderMenuList.add(orderMenu);
if(orderYN != 'Y') break;
}while(true);
Order order = new Order();
SimpleDateFormat date = new SimpleDateFormat("yyyyMMdd");
SimpleDateFormat time = new SimpleDateFormat("HH:mm:ss");
String dateString = date.format(new Date());
String timeString = time.format(new Date());
order.setDate(dateString);
order.setTime(timeString);
order.setMenuList(orderMenuList);
OrderService orderService = context.getBean("orderService", OrderService.class);
System.out.println(order);
int result = orderService.register(order);
if(result > 0) {
System.out.println("메뉴 추가에 성공하였습니다.");
}else {
System.out.println("메뉴 추가에 실패하였습니다.");
}
}
}
OrderService.interface
package com.memory4.javaconfig.model.service;
import com.memory4.javaconfig.model.vo.Order;
public interface OrderService {
int register(Order order);
}
OrderServiceImpl.java
package com.memory4.javaconfig.model.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.memory4.javaconfig.model.dao.OrderMapper;
import com.memory4.javaconfig.model.vo.Order;
import com.memory4.javaconfig.model.vo.OrderMenu;
@Service("orderService")
public class OrderServiceImpl implements OrderService{
private OrderMapper orderMapper;
@Autowired
public OrderServiceImpl(OrderMapper orderMapper) {
this.orderMapper = orderMapper;
}
@Override
@Transactional(isolation = Isolation.SERIALIZABLE, propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
public int register(Order order) {
int result1 = orderMapper.insertMenu(order);
List<OrderMenu> orderMenuList = order.getMenuList();
int result2 = 0;
for(OrderMenu orderMenu : orderMenuList) {
result2 += orderMapper.insertOrderMenu(orderMenu);
}
int result = 0;
if(result1 > 0 && result2 == orderMenuList.size()) {
result = 1;
}
return result;
}
}
OrderMapper.interface
package com.memory4.javaconfig.model.dao;
import com.memory4.javaconfig.model.vo.Order;
import com.memory4.javaconfig.model.vo.OrderMenu;
public interface OrderMapper {
int insertMenu(Order order);
int insertOrderMenu(OrderMenu orderMenu);
}
OrderMapper.xml
<?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.memory4.javaconfig.model.dao.OrderMapper">
<insert id="insertMenu" parameterType="com.memory4.javaconfig.model.vo.Order">
INSERT
INTO TBL_ORDER
(
ORDER_CODE
, ORDER_DATE
, ORDER_TIME
, TOTAL_ORDER_PRICE
)
VALUES
(
SEQ_ORDER_CODE.NEXTVAL
, #{date}
, #{time}
, #{orderPrice}
)
</insert>
<insert id="insertOrderMenu" parameterType="com.memory4.javaconfig.model.vo.OrderMenu">
<selectKey resultType="_int" keyProperty="orderCode" order="BEFORE">
SELECT
SEQ_ORDER_CODE.CURRVAL
FROM DUAL
</selectKey>
INSERT
INTO TBL_ORDER_MENU
(
ORDER_CODE
, MENU_CODE
, ORDER_AMOUNT
)
VALUES
(
#{orderCode}
, #{menuCode}
, #{amount}
)
</insert>
</mapper>
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
spring-context.xml
xml버전과 다른점은 <tx> 접두사를 애너테이션 방식으로 바꾼것이고
ServiceImpl.java에서 확인할수있습니다.
<?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:context="http://www.springframework.org/schema/context"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<context:property-placeholder location="connection-info.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${oracle.dev.driver}"/>
<property name="url" value="${oracle.dev.url}"/>
<property name="username" value="${oracle.dev.username}"/>
<property name="password" value="${oracle.dev.password}"/>
<property name="defaultAutoCommit" value="false"/>
</bean>
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="com/memory4/javaconfig/config/mybatis-config.xml"/>
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"/>
</bean>
<context:component-scan base-package="com.memory4.javaconfig"/>
<mybatis-spring:scan base-package="com.memory4.javaconfig.model.dao"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<aop:aspectj-autoproxy proxy-target-class="true"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
실행화면
'프로그래밍 > Spring' 카테고리의 다른 글
Spring - 12. pom.xml 기본설정 (0) | 2021.10.26 |
---|---|
Spring - 12. MAVEN (0) | 2021.10.26 |
Spring - 10. Transactions (0) | 2021.10.25 |
Spring - 9. mapperscan (0) | 2021.10.25 |
Spring - 6_2. Spring JDBC (XML) (0) | 2021.10.25 |