데브코스 진행과정중에 하루짜리 간단한 프로젝트를 만든다고 해서
JPA 대신 Mybatis를 연습해볼겸 써보려고 한다.
CREATE TABLE products
(
product_id BINARY(16) PRIMARY KEY,
product_name VARCHAR(20) NOT NULL,
category VARCHAR(50) NOT NULL,
price bigint NOT NULL,
description VARCHAR(500) DEFAULT NULL,
created_at datetime(6) NOT NULL,
updated_at datetime(6) DEFAULT NULL
);
CREATE TABLE orders
(
order_id binary(16) PRIMARY KEY,
email VARCHAR(50) NOT NULL,
address VARCHAR(200) NOT NULL,
postcode VARCHAR(200) NOT NULL,
order_status VARCHAR(50) NOT NULL,
created_at datetime(6) NOT NULL,
updated_at datetime(6) DEFAULT NULL
);
CREATE TABLE order_items
(
seq bigint NOT NULL PRIMARY KEY AUTO_INCREMENT,
order_id binary(16) NOT NULL,
product_id binary(16) NOT NULL,
category VARCHAR(50) NOT NULL,
price bigint NOT NULL,
quantity int NOT NULL,
created_at datetime(6) NOT NULL,
updated_at datetime(6) DEFAULT NULL,
INDEX (order_id),
CONSTRAINT fk_order_items_to_order FOREIGN KEY (order_id) REFERENCES orders (order_id) ON DELETE CASCADE,
CONSTRAINT fk_order_items_to_product FOREIGN KEY (product_id) REFERENCES products (product_id)
);
스키마는 이렇게 주어졌다.
엔티티를 만들고 매핑을 수동으로 해야한다...!
그동안 JPA가 너무 편하게 매핑해줬다...
근데 왜 매핑해야 하나요?
SQL 쿼리와 객체 매핑: MyBatis는 SQL 쿼리 결과를 자바 객체로 변환하고, 자바 객체의 데이터를 SQL 쿼리로 변환하는 기능을 제공한다.
매퍼 인터페이스와 XML 파일을 통해 SQL 쿼리를 정의하고, 이를 자바 객체와 연결.
성능 최적화: SQL 쿼리를 직접 작성하므로 JPA의 ORM 오버헤드 없이 성능을 최적화할 수 있음!!
복잡한 쿼리나 성능이 중요한 경우 MyBatis의 SQL 제어가 유리할 수 있다. (간단한 CRUD는 JPA가 더 좋은 것 같다!)
유연성: MyBatis는 XML 파일이나 애너테이션을 통해 SQL 쿼리를 자유롭게 작성할 수 있다는 게 가장 큰 장점인 것 같다!
@Getter
@Setter
public class Product {
private byte[] productId;
private String productName;
private String category;
private long price;
private String description;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
@Mapper
public interface ProductMapper {
Product findById(byte[] productId);
List<Product> findAll();
void insert(Product product);
void update(Product product);
void delete(byte[] productId);
}
<?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.example.dev_coffee.mapper.ProductMapper">
<!-- 제품 정보를 ID로 조회 -->
<select id="findById" parameterType="byte[]" resultType="com.example.dev_coffee.entity.Product">
SELECT * FROM products WHERE product_id = #{productId}
</select>
<!-- 모든 제품 정보 조회 -->
<select id="findAll" resultType="com.example.dev_coffee.entity.Product">
SELECT
product_id AS productId,
product_name AS productName,
category,
price,
description,
created_at AS createdAt,
updated_at AS updatedAt
FROM products
</select>
<!-- 새로운 제품 정보 추가 -->
<insert id="insert" parameterType="com.example.dev_coffee.entity.Product">
INSERT INTO products (product_id, product_name, category, price, description, created_at, updated_at)
VALUES (#{productId}, #{productName}, #{category}, #{price}, #{description}, #{createdAt}, #{updatedAt})
</insert>
<!-- 제품 정보 업데이트 -->
<update id="update" parameterType="com.example.dev_coffee.entity.Product">
UPDATE products
SET product_name = #{productName},
category = #{category},
price = #{price},
description = #{description},
updated_at = #{updatedAt}
WHERE product_id = #{productId}
</update>
<!-- 제품 정보 삭제 -->
<delete id="delete" parameterType="byte[]">
DELETE FROM products WHERE product_id = #{productId}
</delete>
</mapper>
JPA처럼 자동으로 매핑이 안되서 실제로 바꿔줘야 함
created_name 이런것들
Product와 기본적인 CRUD 생성
/**
* 상품 서비스를 관리하는 클래스
*/
@Service
@RequiredArgsConstructor
public class ProductService {
private final ProductMapper productMapper;
@Transactional
public void addProduct(@RequestBody ProductDTO productDTO) {
Product product = new Product();
product.setProductName(productDTO.getProductName());
product.setCategory(productDTO.getCategory());
product.setPrice(productDTO.getPrice());
product.setCreatedAt(setCurrentDate());
product.setUpdatedAt(setCurrentDate()); // 처음 업데이트 날짜도 생성날짜로 리턴
product.setDescription(productDTO.getDescription());
productMapper.insert(product);
}
@Transactional
public void updateProduct(@RequestBody ProductUpdateDTO productUpdateDTO) {
Product product = new Product();
product.setProductName(productUpdateDTO.getProductName());
product.setCategory(productUpdateDTO.getCategory());
product.setPrice(productUpdateDTO.getPrice());
product.setDescription(productUpdateDTO.getDescription());
product.setUpdatedAt(setCurrentDate());
productMapper.update(product);
}
@Transactional
public List<Product> readProduct(@RequestParam("id") byte[] id) {
return productMapper.findAll();
}
@Transactional
public void deleteProductById(@RequestParam("id") byte[] id) {
productMapper.delete(id);
}
// 날짜 시간 메소드
public LocalDateTime setCurrentDate() {
return LocalDateTime.now();
}
}
어? 근데 mybatis는 어떻게 스프링으로 등록하지? mybatiss는 JPA Repositroty와 다르게
XML설정파일에 MapperFactoryBean을 두는 것으로 스프링에 등록된다.
@Mapper
public interface OrderMapper {
Order findById(byte[] orderId);
List<Order> findAll();
void insert(Order order);
void update(Order order);
void delete(byte[] orderId);
}
간단하게 필요할 것 같은 메소드 5개를 만들었다.
<mapper namespace="com.example.dev_coffee.config.OrderMapper">
<select id="findById" parameterType="byte[]" resultType="Order">
SELECT * FROM orders WHERE order_id = #{orderId}
</select>
<select id="findAll" resultType="Order">
SELECT * FROM orders
</select>
<insert>
insert id="insert" parameterType="Order">
INSERT INTO orders (order_id, email, address, postcode, order_status, created_at, updated_at)
VALUES (#{orderId}, #{email}, #{address}, #{postcode}, #{orderStatus}, #{createdAt}, #{updatedAt})
</insert>
<update id="update" parameterType="Order">
UPDATE orders
SET email = #{email}, address = #{address}, postcode = #{postcode}, order_status = #{orderStatus}, updated_at = #{updatedAt}
WHERE order_id = #{orderId}
</update>
<delete id="delete" parameterType="byte[]">
DELETE FROM orders WHERE order_id = #{orderId}
</delete>
</mapper>
....
그냥 JPA 할걸..
아무튼 연습하는 거니까~ ㅋㅋ 그래도 3중4중 복잡한 조인문에서는 Mybatis를 쓰는것이 훨씬 유연할 것 같다!
나중에 그라찌에 프로젝트 매장 통계쿼리문에서 사용해봐야겠다!
<mapper namespace="com.example.OrderItemMapper">
<select id="findById" parameterType="long" resultType="OrderItem">
SELECT * FROM order_items WHERE seq = #{seq}
</select>
<select id="findByOrderId" parameterType="byte[]" resultType="OrderItem">
SELECT * FROM order_items WHERE order_id = #{orderId}
</select>
<insert id="insert" parameterType="OrderItem">
INSERT INTO order_items (seq, order_id, product_id, category, price, quantity, created_at, updated_at)
VALUES (#{seq}, #{orderId}, #{productId}, #{category}, #{price}, #{quantity}, #{createdAt}, #{updatedAt})
</insert>
<update id="update" parameterType="OrderItem">
UPDATE order_items
SET order_id = #{orderId}, product_id = #{productId}, category = #{category}, price = #{price}, quantity = #{quantity}, updated_at = #{updatedAt}
WHERE seq = #{seq}
</update>
<delete id="delete" parameterType="long">
DELETE FROM order_items WHERE seq = #{seq}
</delete>
</mapper>
orderItem도 작성완료 하였당