Spring Boot myBatis with JPA - 예제코드
2020. 8. 23. 22:50ㆍSpring Data/MyBatis With JPA
반응형
myBatis와 JPA 동시에 적용하기 위한 예제코드를 작성해 보자
개발순서는 아래와 같다.
- MySQL과 PostgreSQL에 테이블을 생성한다.
- Domain 작성
- Mapper 작성
- JPA Respository 작성
- Service 작성
- Controller 작성
1. 테이블생성
PostgreSQL 테이블 생성 스크립트
DROP TABLE public.orderm;
CREATE TABLE public.orderm (
order_id character varying(15) NOT NULL,
orderer_id character varying(15) NOT NULL,
order_dtm character varying(14) NOT NULL,
total_order_amt bigint NOT NULL,
order_status character varying(1) NOT NULL DEFAULT 'T'::character varying
)
TABLESPACE pg_default;
ALTER TABLE public.orderm ADD
CONSTRAINT orderm_pk PRIMARY KEY ( order_id );
DROP TABLE public.order_detail;
CREATE TABLE public.order_detail (
order_id character varying(15) NOT NULL,
order_seq smallint NOT NULL,
prod_cd character varying(10) NOT NULL,
qty smallint NOT NULL,
order_amt bigint NOT NULL
)
TABLESPACE pg_default;
ALTER TABLE public.order_detail ADD
CONSTRAINT order_detail_pk PRIMARY KEY ( order_id, order_seq );
MySQL 테이블 생성 스크립트
CREATE TABLE `orderm` (
`order_id` varchar(15) NOT NULL,
`orderer_id` varchar(15) NOT NULL,
`order_dtm` varchar(14) NOT NULL,
`total_order_amt` bigint(20) NOT NULL,
`order_status` varchar(1) NOT NULL DEFAULT 'T',
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `order_detail` (
`order_id` varchar(15) NOT NULL,
`order_seq` smallint(5) NOT NULL,
`prod_cd` varchar(10) NOT NULL,
`qty` smallint(5) NOT NULL,
`order_amt` bigint(20) NOT NULL,
PRIMARY KEY (`order_id`,`order_seq`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2. Domain 작성
JPA 연동을 위한 Entity Bean 기준으로 작성한다. Entity Bean 으로 작성하더라도 MyBatis 연동시 DTO로 사용가능 하다.
Domain 클래스 작성시 Lombok 을 사용하여서 별도의 get/set 메소드는 생성하지 않는다.
package com.roopy.domain;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import org.apache.commons.lang3.builder.MultilineRecursiveToStringStyle;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Entity
@Data
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Orderm {
@Id
@Column(name = "order_id", nullable = false, length = 15)
public String orderId;
@Column(name = "orderer_id", nullable = false, length = 35)
public String ordererId;
@Column(name = "order_dtm", nullable = false, length = 14)
public String orderDtm;
@Column(name = "total_order_amt", nullable = false)
public Integer totalOrderAmt;
@Column(name = "order_status", nullable = false, length = 1)
public String orderStatus;
@OneToMany(mappedBy="orderm",cascade=CascadeType.ALL)
public List<OrderDetail> orderDetails;
@Override
public String toString() {
return new ReflectionToStringBuilder(this, new MultilineRecursiveToStringStyle()).toString();
}
orderm 에 대응되는 Entity 클래스
package com.roopy.domain;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.apache.commons.lang3.builder.MultilineRecursiveToStringStyle;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Entity
@Data
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "order_detail")
@IdClass(OrderDetailIdentity.class)
public class OrderDetail implements Serializable {
private static final long serialVersionUID = -178105276989733750L;
@Id
@Column(name = "order_id", nullable = false, length = 15)
public String orderId;
@Id
@Column(name = "order_seq", nullable = false)
public int orderSeq;
@Column(name = "prod_cd", nullable = false, length = 10)
public String prodCd;
@Column(name = "qty", nullable = false)
public int qty;
@Column(name = "order_amt", nullable = false)
public int orderAmt;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns(value = {
@JoinColumn(name = "order_id", referencedColumnName = "order_id", insertable = false, updatable = false)
})
public Orderm orderm;
@Override
public String toString() {
return new ReflectionToStringBuilder(this, new MultilineRecursiveToStringStyle()).toString();
}
}
order_detail 에 대응되는 Entity 클래스
package com.roopy.domain;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Data
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Embeddable
public class OrderDetailIdentity implements Serializable {
private static final long serialVersionUID = 2339510329475348248L;
@Column(name = "order_id", nullable = false, length = 15)
public String orderId;
@Column(name = "order_seq", nullable = false)
public int orderSeq;
public OrderDetailIdentity() {
}
public OrderDetailIdentity(String orderId, int orderSeq) {
super();
this.orderId = orderId;
this.orderSeq = orderSeq;
}
}
order_detail 테이블은 복합키로 이루어 지므로 복합키 적용을 위한 별도의 Key 클래스 작성
JPA 개발시 가장 중요한 부분이 Domain 클래스 작성이다. 특히나 복합키 처리는 까다로운 문제일수도 있다.
이와 관련하여서 배달의민족 블로그에 잘정리된 글이 있어서 공유합니다.
https://woowabros.github.io/experience/2019/01/04/composit-key-jpa.html
3. Mapper 작성
주문정보조회를 위한 myBatis 인터페이스 클래스 생성 및 Mapper 파일을 작성한다.
package com.roopy.persistence.pgsql.mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import com.roopy.domain.Orderm;
@Repository
public interface OrderMapper {
public Orderm findOrder(@Param("orderId") String orderId);
}
<?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.roopy.persistence.pgsql.mapper.OrderMapper">
<!--
=====================================================================
주문정보조회결과 ResultMap
=====================================================================
-->
<resultMap type="Orderm" id="orderResultMap">
<id property="orderId" column="order_id" />
<result property="orderer_id" column="ordererId" />
<result property="order_dtm" column="orderDtm" />
<result property="total_order_amt" column="totalOrderAmt" />
<result property="order_status" column="orderStatus" />
<collection property="orderDetails"
column="{orderId=order_id}"
javaType="java.util.ArrayList"
ofType="OrderDetail"
select="findOrderDetailByOrderId"/>
</resultMap>
<!--
=====================================================================
주문정보조회
=====================================================================
-->
<select id="findOrder" resultMap="orderResultMap">
select order_id
, orderer_id
, order_dtm
, total_order_amt
, order_status
from orderm
where order_id = #{orderId}
</select>
<!--
=====================================================================
주문상세정보조회
=====================================================================
-->
<select id="findOrderDetailByOrderId" resultType="OrderDetail">
select order_id
, order_seq
, prod_cd
, qty
, order_amt
from order_detail
where order_id = #{orderId}
</select>
</mapper>
하나의 쿼리로 Join 으로 만들 수 도 있지만, 개발 사이트가 큰경우 쿼리의 재사용성 및 여러가지 이유로 위와 같이 ResultMap을 사용하는 것이 좋다는 생각이 든다.
4. JPA Repository 작성
package com.roopy.persistence.mysql.repository;
import com.roopy.domain.Orderm;
public interface OrderRepositoryCustom {
public Orderm saveOrder(Orderm order);
}
package com.roopy.persistence.mysql.repository;
import org.springframework.data.repository.Repository;
import com.roopy.domain.Orderm;
public interface OrderRepository extends Repository<Orderm, String>, OrderRepositoryCustom {
}
package com.roopy.persistence.mysql.repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import com.roopy.domain.Orderm;
public class OrderRepositoryImpl implements OrderRepositoryCustom {
@PersistenceContext
EntityManager em;
@Override
public Orderm saveOrder(Orderm order) {
return em.merge(order);
}
}
5. Service 작성
package com.roopy.service;
import com.roopy.domain.Orderm;
public interface OrderService {
public Orderm saveOrder(String orderId) throws Exception;
}
package com.roopy.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.roopy.domain.Orderm;
import com.roopy.persistence.mysql.repository.OrderRepository;
import com.roopy.persistence.pgsql.mapper.OrderMapper;
import com.roopy.service.OrderService;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderRepository orderRepository;
@Override
@Transactional
public Orderm saveOrder(String orderId) throws Exception {
// 주문정보 조회
Orderm order = orderMapper.findOrder(orderId);
// 주문정보 저장
if (null != order) {
order = orderRepository.saveOrder(order);
}
return order;
}
}
6. Controller 작성
package com.roopy.controller;
import java.util.HashMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.roopy.domain.Orderm;
import com.roopy.service.OrderService;
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping(value = "/order/{orderId}")
public Orderm saveOrder(HttpServletRequest request, @PathVariable String orderId, HttpServletResponse response,
@RequestParam HashMap<String, String> param) throws Exception {
Orderm order = orderService.saveOrder(orderId);;
return order;
}
}
7. 테스트
http://localhost:9091/order/{orderId}
반응형
'Spring Data > MyBatis With JPA' 카테고리의 다른 글
Spring Boot myBatis with JPA - 프로젝트 개요 및 설정 (0) | 2020.08.16 |
---|