데브코스 - 간단한 CRUD

류희수·2024년 9월 6일

데브코스 진행과정중에 하루짜리 간단한 프로젝트를 만든다고 해서
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을 두는 것으로 스프링에 등록된다.


Mapping & XML

@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도 작성완료 하였당

profile
자바를자바

0개의 댓글