๐Ÿ’พ Database

[MyBatis] ๋™์ž‘์›๋ฆฌ, ์‚ฌ์šฉ๋ฐฉ๋ฒ• ์ •๋ฆฌ

์—ฐ_์šฐ๋ฆฌ 2022. 1. 27. 02:36
๋ฐ˜์‘ํ˜•

๋ชฉ์ฐจ

     

    MyBatis ๋“ฑ์žฅ๋ฐฐ๊ฒฝ

     

    [JDBC] ์‚ฌ์šฉ๋ฐฉ๋ฒ•

    JDBC : JAVA DataBase Connectivity ๊ธฐ์กด ์ž๋ฐ”์—์„œ๋Š” DB๋ฅผ ์กฐ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด์„œ JDBC API๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค. JDBC๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ข…๋ฅ˜์— ์ƒ๊ด€์—†์ด JDBC๋งŒ ์•Œ๋ฉด ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ ์ผ๊ด€๋œ ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ• 

    lotuus.tistory.com

    ๊ธฐ์กด์˜ JDBC API๋ฅผ ๋ณด๋ฉด DBMS์™€ ์—ฐ๋™ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์–ป์–ด์˜ค๊ธฐ์œ„ํ•œ ์ค€๋น„์šด๋™์— ๋Œ€ํ•œ ์ฝ”๋“œ๊ฐ€ ์ ˆ๋ฐ˜์ด๋‹ค.

    ์—ฌ๊ธฐ์„œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์ถ”๊ฐ€๋œ๋‹ค๋ฉด ์ฝ”๋“œ๊ฐ€ ๋” ๋ณต์žกํ•ด์ง€๋Š” ๊ฒƒ์€ ๋ฌผ๋ก ์ด๊ณ ,

    ํ•œ ํŒŒ์ผ ์•ˆ์—์„œ ๋„ˆ๋ฌด ๋งŽ์€ ์—ญํ• ์„ ํ•ด๋‚ด์•ผํ•œ๋‹ค (DB์—ฐ๊ฒฐ.. DB๊ฒฐ๊ณผ๊ฐ’ ์ฒ˜๋ฆฌ... ResultSet๊ณผ ๊ฐ์ฒด ๋งตํ•‘.. ๊ฐ์ฒด๋ฅผ ๊ฐ€์ง€๊ณ  ๋น„์ฆˆ๋‹ˆ์Šค๋กœ์ง ๊ตฌํ˜„...)

    ๋˜ํ•œ ์ด ์—ฐ๊ฒฐํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋‹ค๋ฅธ ํŒŒ์ผ์—์„œ๋„ ์ค‘๋ณต๋˜์–ด ๋‚˜ํƒ€๋‚˜๊ฒŒ๋œ๋‹ค. ํ•ต์‹ฌ์€ DB์—์„œ ๊ฐ€์ ธ์˜จ ๊ฐ’์œผ๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ธ๋ฐ..

    ์œ„์™€ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ํ•ด์†Œํ•˜๊ณ ์ž ๋‚˜์˜จ ๊ฒƒ์ด MyBatis์ด๋‹ค.

     

    MyBatis๋Š”

    1. SQL๋ฌธ์„ ์กฐ์ž‘ํ•˜๊ธฐ ์ „, ํ›„์˜ Connection ๋ฐ ๊ฐ์ฒด ๋งตํ•‘ ๋“ฑ์„ ๋Œ€์‹ ํ•ด์ฃผ์–ด ์ฝ”๋“œ์˜ ์ค‘๋ณต๊ณผ ์ž‘์„ฑ์„ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๊ฒŒํ•ด์ค€๋‹ค.

    2. SQL๋ฌธ์„ ์ฝ”๋“œ์™€ ๋ถ„๋ฆฌํ•˜์—ฌ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์—ฌ์ค€๋‹ค.

    3. SQL ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ VO๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ์ž๋™ Mapping์„ ์ง€์›ํ•ด์ค€๋‹ค.

     

     


     

    MyBatis ๋™์ž‘์›๋ฆฌ

    1. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์‹œ SqlSessionFactoryBuilder๊ฐ€ ์„ค์ •ํŒŒ์ผ์„ ์ฐธ๊ณ ํ•˜์—ฌ SqlSessionFactory ์ƒ์„ฑ

    2. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ DB์ž‘์—… ์‹œ SqlSessionFactory๊ฐ€ SqlSession๊ฐ์ฒด ์ƒ์„ฑ

    3. ์ƒ์„ฑ๋œ SqlSession์„ ์ฐธ๊ณ ํ•ด์„œ mapper์ธํ„ฐํŽ˜์ด์Šค ํ˜ธ์ถœ (SqlSession์— mapper์ •๋ณด๊ฐ€ ๋‚ด์žฅ๋˜์–ด์žˆ๋‹ค)

    5. mapper๊ฐ€ SqlSession์„ ํ˜ธ์ถœํ•˜์—ฌ SQL์‹คํ–‰

     

     

     

     * DataSource

    JDBC ์‚ฌ์šฉ ์‹œ์—๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ‘๊ทผํ• ๋•Œ๋งˆ๋‹ค Connection์„ ์—ฐ๊ฒฐํ•˜๊ณ  ๋Š์–ด์ฃผ์—ˆ๋‹ค.

    ์ด Connection ์ž‘์—…์„ ์ค„์ด๊ธฐ ์œ„ํ•ด ๋ฏธ๋ฆฌ Connection์„ ์ƒ์„ฑํ•ด์„œ Connection Pool ์ด๋ผ๋Š” ๊ณณ์— ์ €์žฅํ•˜๊ธฐ๋กœํ–ˆ๊ณ 

    ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ‘๊ทผํ• ๋•Œ๋งˆ๋‹ค Connection Pool์—์„œ ๋ฏธ๋ฆฌ ์ƒ์„ฑ๋œ Connection์„ ์ œ๊ณต๋ฐ›๊ณ  ๋‹ค์‹œ ๋Œ๋ ค์ฃผ๊ธฐ๋กœ ํ–ˆ๋‹ค.

    DataSource๋Š” ์ž๋ฐ”์—์„œ Connection Pool์„ ์ง€์›ํ•ด์ฃผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค.

     

    Spring์—์„œ DataSource๋Š” application.propertiesํŒŒ์ผ๋กœ ์„ค์ • ๊ฐ€๋Šฅํ•˜๋‹ค.

    spring.datasource.driver-class-name = ๋“œ๋ผ์ด๋ฒ„๋ช…
    spring.datasource.url = url์ž…๋ ฅ
    spring.datasource.username = ์œ ์ €๋ช…
    spring.datasource.password = ๋น„๋ฐ€๋ฒˆํ˜ธ

     

     * SqlSession

    MyBatis์˜ ์ปดํฌ๋„ŒํŠธ๋กœ SQL์„ ์‹คํ–‰ํ•˜๊ณ  ํŠธ๋žœ์žญ์…˜์„ ์ œ์–ดํ•ด์ค€๋‹ค.

    Spring Boot์—์„œ๋Š” Spring-Boot-Starter-MyBatis๋ฅผ dependencyํ•ด์ฃผ๋ฉด ๋ณ„๋‹ค๋ฅธ ์„ค์ • ์—†์ด ๋ฐ”๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค

     

     

     * Mapper.xml Or Mapper Interface

    ์‹ค์ œ SQL๊ตฌ๋ฌธ ์ž‘์„ฑํ•˜๋Š” ๊ณณ์ด๋‹ค. ์•„๋ž˜์—์„œ ์ž์„ธํžˆ ์‚ดํŽด๋ณด์ž

     

     


     

    MyBatis ์—ฐ๋™ ๋ฐ ์‚ฌ์šฉ๋ฐฉ๋ฒ• (Spring Boot ๊ธฐ์ค€)

    1. build.gradle์— mybatis-spring-boot-starter์™€ ์ž์‹ ์ด ์‚ฌ์šฉํ•  DMBS์˜ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

    (maven repository์—์„œ ํ™•์ธ ๊ฐ€๋Šฅ)

    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.1'
    
    implementation 'org.mariadb.jdbc:mariadb-java-client:2.7.5'

     

    2. application.properties ํŒŒ์ผ์— DataSource ์„ค์ •์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

    spring.datasource.driver-class-name = org.mariadb.jdbc.Driver
    spring.datasource.url = jdbc:mariadb://localhost:3306/test
    spring.datasource.username = root
    spring.datasource.password = 1234
    
    mybatis.type-aliases-package = com.example.demo.domain
    # xml mapper์—์„œ resultType์— Member๋งŒ ์ ์„ ์ˆ˜ ์žˆ๊ฒŒํ•ด์ค€๋‹ค.
    # type-aliases-package๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š์œผ๋ฉด 
    # com.example.demo.domain.Member ์ด๋Ÿฐ์‹์œผ๋กœ ํŒจํ‚ค์ง€ ๊ฒฝ๋กœ๋ฅผ ์ „๋ถ€ ์จ์ฃผ์–ด์•ผํ•œ๋‹ค.
    
    mybatis.mapper-locations = mybatis/**/*.xml
    # resources ํด๋” ๋ฐ‘์— mybatis๋ผ๋Š” ํด๋”๊ฐ€ ์žˆ๋Š”๋ฐ
    # mybatis ํด๋” ํ•˜์œ„ ๋ ˆ๋ฒจ์— ์ƒ๊ด€์—†์ด ๋ชจ๋‘, ์–ด๋–ค ์ด๋ฆ„์„ ๊ฐ€์ง„ xmlํŒŒ์ผ๋„ ๋ชจ๋‘ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

     

    3. ํ…Œ์ŠคํŠธ์šฉ ํ…Œ์ด๋ธ” ์ƒ์„ฑ

     

    4. ํ…Œ์ŠคํŠธ์šฉ domain ์ƒ์„ฑ

    package com.example.demo.domain;
    
    import lombok.Getter;
    import lombok.Setter;
    import lombok.ToString;
    
    @Getter @ToString
    public class Member {
        private Long id;
        private String name;
        private int age;
    
        public Member(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public Member(Long id, String name, int age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }
        // ์ƒ์„ฑ์ž์— ๋Œ€ํ•ด์„œ ํ• ๋ง์ด ๋งŽ๋‹ค..ใ…œใ…œ ๋งตํ•‘๋ถ€๋ถ„ ํ™•์ธํ•˜์ž
    
    }

     

    5. ํ…Œ์ŠคํŠธ์šฉ repository, mapper.xml ์ƒ์„ฑ

    package com.example.demo.repository;
    
    import com.example.demo.domain.Member;
    import org.apache.ibatis.annotations.*;
    import org.springframework.stereotype.Repository;
    
    @Mapper
    @Repository
    public interface MemberRepository {
    
        @Insert("insert into member(name, age) values(#{name}, #{age})")
        @Options(useGeneratedKeys = true, keyProperty = "id")
        void insertAnno(Member member);
    
        @Select("select * from member where id=#{id}")
        Member selectAnno(Long id);
    
    	//mapper์ฐธ๊ณ 
        void insertXml(Member member);
    
    	//mapper์ฐธ๊ณ 
        Member selectXml(Long id);
    
    }
    /resources/mybatis/MemberMapper.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.example.demo.repository.MemberRepository">
        <insert id="insertXml" useGeneratedKeys="true" keyProperty="id">
            insert into member(name, age) values(#{name}, #{age})
        </insert>
    
        <select id="selectXml" resultType="Member" parameterType="Long">
            select * from member where id = #{id}
        </select>
    </mapper>

     

     

    6. ํ…Œ์ŠคํŠธ์šฉ ์ปจํŠธ๋กค๋Ÿฌ ์ƒ์„ฑ

    package com.example.demo.controller;
    
    import com.example.demo.domain.Member;
    import com.example.demo.repository.MemberRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class MemberController {
        @Autowired private MemberRepository memberRepository;
    
        @GetMapping("/member")
        public String memberTest(){
            Member memberA = new Member("Lisa", 20);
            Member memberB = new Member("Alice", 22);
    
            memberRepository.insertAnno(memberA);
            memberRepository.insertXml(memberB);
    
            Member memberC = memberRepository.selectAnno(memberA.getId());
            Member memberD = memberRepository.selectAnno(memberB.getId());
            System.out.println(memberC);	//Member(id=1, name=Lisa, age=20)
            System.out.println(memberD);	//Member(id=2, name=Alice, age=22)
    
            return "Ok";
        }
    }

     

     

    MyBatis ๊ฐ„๋žตํ•œ ์‚ฌ์šฉ๋ฐฉ๋ฒ•

    mapper๋Š” 2๊ฐ€์ง€ ๋ฐฉ์‹์ด ์žˆ๋‹ค. @Mapper๋ฅผ ์ด์šฉํ•œ ์–ด๋…ธํ…Œ์ด์…˜ ๋ฐฉ์‹, Xml์„ ์ด์šฉํ•œ ๋ฐฉ์‹

     

    1. @Mapper๋ฅผ ์ด์šฉํ•œ ์–ด๋…ธํ…Œ์ด์…˜ ๋ฐฉ์‹

    @Mapper๋Š” @Repository์™€ ํ•จ๊ป˜ ์ž‘์„ฑํ•˜๋ฉด๋˜๊ณ  interface๋กœ ์„ ์–ธํ•ด์•ผํ•œ๋‹ค.

    ๊ทธ๋Ÿฌ๋ฉด ์Šคํ”„๋ง๋ถ€ํŠธ์—์„œ ์ž๋™์œผ๋กœ Mapper๋กœ ์ธ์‹ํ•ด์„œ ๋‹ค๋ฅธ ์„ค์ • ์—†์ด๋„ DBMS ์กฐ์ž‘์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

     

    @Select(), @Insert(), @Update(), @Delete() ๊ฐ๊ฐ ์•Œ๋งž๋Š” SQL๋ฌธ์„ ์ž‘์„ฑํ•˜๋ฉด๋œ๋‹ค.

    ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋„˜๊ธธ๋•Œ #{ํŒŒ๋ผ๋ฏธํ„ฐ๋ช…}์€ ' '์ด ๋ถ™๊ณ , ${ํŒŒ๋ผ๋ฏธํ„ฐ๋ช…}์€ ' '์ด ๋ถ™์ง€ ์•Š๋Š”๋‹ค.

     

    @Insert๋ฌธ์€ @Options(useGeneratedKeys = true, keyProperty = "id")์™€ ๊ฐ™์ด ์“ฐ์ผ ๋•Œ๊ฐ€ ๋งŽ์€๋ฐ

    keyProperty์˜ id๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ AUTO_INCREMENT๋กœ ์ง€์ •ํ•ด๋‘์—ˆ๊ธฐ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์‚ฝ์ž…๋˜์–ด์•ผ๋งŒ ์ƒ์„ฑ๋˜๋Š” ๊ฐ’์ด๋‹ค. ์ด ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋ ค๋ฉด ์‚ฝ์ž…ํ•˜๊ณ  ๋‹ค์‹œ selectํ•ด์•ผํ•˜๋‚˜?? ๋ผ๋Š” ์ƒ๊ฐ๋„ ๋“œ๋Š”๋ฐ useGeneratedKeys๋ฅผ true๋กœ ์„ค์ •ํ•˜๋ฉด select ํ•˜์ง€ ์•Š์•„๋„ Member์— ์ž๋™์œผ๋กœ id๊ฐ’์ด ๋“ค์–ด๊ฐ€๊ฒŒํ•ด์ค€๋‹ค.

     

    2. XML์„ ์ด์šฉํ•œ ๋ฐฉ์‹

    XMLํŒŒ์ผ์— <mapper>ํƒœ๊ทธ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  namespace์— Repository์˜ ํŒจํ‚ค์ง€๊ฒฝ๋กœ๋ฅผ ์ ๋Š”๋‹ค.

     

    <select>, <insert>, <update>, <delete> ํƒœ๊ทธ ์•ˆ์— ์•Œ๋งž๋Š” SQL๋ฌธ์„ ์ž‘์„ฑํ•˜๋ฉด๋œ๋‹ค.

    ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ์–ด๋…ธํ…Œ์ด์…˜ ๋ฐฉ์‹๊ณผ ๋™์ผํ•˜๊ฒŒ #, $๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

     

    ์ฃผ๋กœ ๊ฐ„๋‹จํ•œ SQL๋ฌธ์€ ์–ด๋…ธํ…Œ์ด์…˜ ๋ฐฉ์‹์œผ๋กœ, ๋™์ ์ธ ์ฒ˜๋ฆฌ๊ฐ€ ์ด๋ฃจ์–ด์ ธ์•ผํ•˜๋Š” SQL๋ฌธ์€ XML์— ์ž‘์„ฑํ•˜๊ธฐ๋•Œ๋ฌธ์—

    ์—ฐ๊ด€๊ด€๊ณ„ ๋งตํ•‘์— ํ•ด๋‹นํ•˜๋Š” <resultMap>, <association>, <collection>

    ๋™์ ์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์กฐ์ž‘ํ•˜๋Š” <trim>, <if>, <choose>, <where> ๋“ฑ์„ ์‚ฌ์šฉํ•˜๊ฒŒ๋œ๋‹ค.


    ๋™์  SQL

     

    MyBatis – ๋งˆ์ด๋ฐ”ํ‹ฐ์Šค 3 | ๋™์  SQL

    ๋™์  SQL ๋งˆ์ด๋ฐ”ํ‹ฐ์Šค์˜ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋Š” ๋™์  SQL์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. JDBC๋‚˜ ๋‹ค๋ฅธ ์œ ์‚ฌํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•ด๋ณธ ๊ฒฝํ—˜์ด ์žˆ๋‹ค๋ฉด ๋™์ ์œผ๋กœ SQL ์„ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ์ด ์–ผ๋งˆ๋‚˜ ํž˜๋“  ์ž‘์—…์ธ์ง€

    mybatis.org


     

    MyBatis๋Š” ์–ด๋–ป๊ฒŒ ์ปฌ๋Ÿผ์„ ๋งตํ•‘ํ• ๊นŒ?

    (์‚ฌ์‹ค ๋‚ด๊ฐ€ ์—„์ฒญ ํ—ค๋งจ ๋‚ด์šฉ์ด๋‹ค..)

     

    ๋‚˜๋Š” Member๊ฐ์ฒด๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†๊ฒŒ ๋งŒ๋“ค๊ณ  ์‹ถ๋‹ค.

    ์ƒ์„ฑ ์‹œ์— ํ•„์š”ํ•œ name, age์— ๋Œ€ํ•œ ์ƒ์„ฑ์ž๋งŒ ๋งŒ๋“ค์–ด๋†“๊ณ  setter๋„ ์—†๋Š” ์ƒํƒœ์ด๋‹ค.

    package com.example.demo.domain;
    
    import lombok.Getter;
    import lombok.ToString;
    
    @Getter @ToString
    public class Member {
        private Long id;
        private String name;
        private int age;
    
        public Member(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
    package com.example.demo.repository;
    
    import com.example.demo.domain.Member;
    import org.apache.ibatis.annotations.*;
    import org.springframework.stereotype.Repository;
    
    @Mapper
    @Repository
    public interface MemberRepository {
    
        @Select("select * from member")
        Member selectAnno(Long id);
    }

    ์ด ์ƒํƒœ์—์„œ selectAnno๋ฅผ ์‹คํ–‰ํ•ด๋ณด์ž

     

    ์•„๋ž˜์™€ ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. DB์—์„œ SQL๋ฌธ์„ ์‹คํ–‰ํ–ˆ์„ ๋• ๊ฒฐ๊ณผ๊ฐ€ ์ž˜ ๋‚˜์˜จ๋‹ค.. ใ…Žใ…Ž ์™œ ๋งตํ•‘์ด ์•ˆ๋˜๋Š”๊ฑธ๊นŒ

    Error attempting to get column 'name' from result set. Cause: java.sql.SQLException: Out of range value for column 'name' : value Lisa ; Out of range value for column 'name' : value Lisa; nested exception is java.sql.SQLException: Out of range value for column 'name' : value Lisa
    org.springframework.dao.DataIntegrityViolationException: Error attempting to get column 'name' from result set.  Cause: java.sql.SQLException: Out of range value for column 'name' : value Lisa
    ; Out of range value for column 'name' : value Lisa; nested exception is java.sql.SQLException: Out of range value for column 'name' : value Lisa
    
    ..... ์ค‘๋žต ....
    
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.15.jar:5.3.15]
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.15.jar:5.3.15]
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.15.jar:5.3.15]
    	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ~[spring-tx-5.3.15.jar:5.3.15]
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.15.jar:5.3.15]
    	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.15.jar:5.3.15]
    	at com.sun.proxy.$Proxy110.selectAnno(Unknown Source) ~[na:na]
    	at com.example.demo.controller.MemberController.memberTest(MemberController.java:25) ~[main/:na]

     

    ๋‚˜๋„ ๋ฐ”๋ณด๊ฐ™์•˜๋˜๊ฒŒใ…œ ๋‹จ์ˆœํžˆ selectํ• ๋•Œ id, name, age ๋ชจ๋‘ ๋ฐ›์•„์˜ค๋‹ˆ ํ•„๋“œ์— ๋งตํ•‘๋˜์„œ ๊ฐ์ฒด ์ง ! ํ•˜๊ณ  ๋‚˜์˜ฌ์ค„์•Œ์•˜๋Š”๋ฐ

    ์กฐ๊ธˆ๋งŒ ์ƒ๊ฐํ•ด๋ณด๋ฉด ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๋„, ๋ชจ๋“  ํ•„๋“œ๊ฐ€ ์žˆ๋Š” ์ƒ์„ฑ์ž๋„ ์•„๋‹Œ ์ƒ์„ฑ์ž๊ฐ€ ๋ฒ„์ “์ด ์žˆ๋Š”๋ฐ ๊ฐ์ฒด๊ฐ€ ๋‚˜์˜ค๋Š”๊ฒƒ๋„ ์ด์ƒํ•˜๋‹ค..

     

    MyBatis๋Š” ์ปฌ๋Ÿผ์„ ์•„๋ž˜์™€ ๊ฐ™์ด ๋งตํ•‘ํ•œ๋‹ค.

    1. ํด๋ž˜์Šค์— setter๊ฐ€ ์žˆ์œผ๋ฉด setter๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

    2. setter๊ฐ€ ์—†๋‹ค๋ฉด ํ•„๋“œ ์ด๋ฆ„์œผ๋กœ ๋งตํ•‘ํ•œ๋‹ค.

    3. ์ง์ ‘ ์ •์˜ํ•œ ์ƒ์„ฑ์ž(๋ชจ๋“  ํ•„๋“œ๊ฐ€ ์žˆ๋Š” ์ƒ์„ฑ์ž ํฌํ•จ)๋Š” DB ์ถœ๋ ฅ ์ปฌ๋Ÿผ์ˆœ์„œ์™€ ์ƒ์„ฑ์ž์— ์ •์˜๋œ ํŒŒ๋ผ๋ฏธํ„ฐ ์ˆœ์„œ๊ฐ€ ๊ฐ™์•„์•ผํ•œ๋‹ค.

    4. ๊ธฐ๋ณธ ์ƒ์„ฑ์ž ๋˜๋Š” ์ˆœ์„œ๋ฅผ ๋งž์ถ˜ ๋ชจ๋“  ํ•„๋“œ๊ฐ€ ์žˆ๋Š” ์ƒ์„ฑ์ž๋ฅผ ๋ฐ˜๋“œ์‹œ ์ƒ์„ฑํ•ด์ฃผ์ž

     

     


     

    MyBatis 1:1 ๊ด€๊ณ„ ๋งตํ•‘

    xml์—์„œ <resultMap>์•ˆ์— <Association>  ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์ž.

     

    member ํ…Œ์ด๋ธ”

    address ํ…Œ์ด๋ธ”

    package com.example.demo.domain;
    
    import lombok.Getter;
    import lombok.Setter;
    import lombok.ToString;
    
    import java.util.Collections;
    import java.util.List;
    
    @Getter @ToString
    public class Member {
        private Long id;
        private String name;
        private int age;
        private Address address;
    
        public Member(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
    //    public Member(Long id, String name, int age, Address address) {
    //        this.id = id;
    //        this.name = name;
    //        this.age = age;
    //        this.address = address;
    //    }  
        // No constructor found in com.example.demo.domain.Member matching [java.lang.Long, java.lang.String, java.lang.Integer, java.lang.Long, java.lang.Long, java.lang.String]
    
        public Member() {
        }
        //OK
    }
    package com.example.demo.domain;
    
    import lombok.Getter;
    import lombok.ToString;
    
    @Getter @ToString
    public class Address {
        private Long memberId;
        private Long postcode;
        private String detailAddress;
    
        public Address(Long postcode, String detailAddress) {
            this.postcode = postcode;
            this.detailAddress = detailAddress;
        }
    
    //    public Address(Long memberId, Long postcode, String detailAddress) {
    //        this.memberId = memberId;
    //        this.postcode = postcode;
    //        this.detailAddress = detailAddress;
    //    } 
    
        public Address() {
        }
        //OK
    }
    package com.example.demo.repository;
    
    import com.example.demo.domain.Member;
    import org.apache.ibatis.annotations.*;
    import org.springframework.stereotype.Repository;
    
    import java.util.Map;
    
    @Mapper
    @Repository
    public interface MemberRepository {
    
        @Select("select member.*, address.* " +
                "from member join address on member.id=address.memberid " +
                "where member.id = #{id}")
        @ResultMap("MemberAddress")
        Member selectAnno(Long id);
    
    }
    <?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.demo.repository.MemberRepository">
    
        <resultMap id="Address" type="Address">
            <result property="memberId" column="memberid"></result>
            <result property="postcode" column="postcode"></result>
            <result property="detailAddress" column="detailaddress"></result>
        </resultMap>
        <resultMap id="MemberAddress" type="Member">
            <id property="id" column="id"></id>
            <result property="name" column="name"></result>
            <result property="age" column="age"></result>
            <association property="address" resultMap="Address"></association>
        </resultMap>
    
    </mapper>

    <association>์€ ํŠน์ดํ•˜๊ฒŒ ๋ชจ๋“  ํ•„๋“œ์˜ ์ƒ์„ฑ์ž๊ฐ€ ์žˆ์œผ๋ฉด ์ž‘๋™ํ•˜์ง€ ์•Š์•˜๋‹ค

    Member, Address ๋‘˜๋‹ค ๋ชจ๋“  ํ•„๋“œ์˜ ์ƒ์„ฑ์ž๊ฐ€ ์žˆ์œผ๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๊ณ 

    Member, Address ๋‘˜๋‹ค ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๋งŒ ์žˆ์–ด์•ผ์ง€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.

     

    ํ  ์–ด์ฐจํ”ผ member์™€ address์— ๊ฒน์น˜๋Š” ํ•„๋“œ๋ช…์ด ์—†์œผ๋‹ˆ๊นŒ resultmap์„ ์•ˆ์จ๋„ ๋˜์ง€์•Š์„๊นŒ?! ํ–ˆ๋Š”๋ฐ

    address๊ฐ€ null๋กœ ๋‚˜์˜จ๋‹ค.

     

     


    MyBatis 1:N ๊ด€๊ณ„ ๋งตํ•‘

    xml์—์„œ <resultMap>์•ˆ์— <Collection> ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์ž.

    <Collection>์— ๋Œ€์‘๋˜๋Š” @Results ์–ด๋…ธํ…Œ์ด์…˜์€ ์‚ฌ์šฉํ•˜์ง€๋ง์ž! select๋ฅผ ์ง€์ •ํ•˜๋ฉด์„œ N+1 ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

     

    ๊ฐ์ฒด ์•ˆ์— ๊ธฐ๋ณธํƒ€์ž… ๋ฆฌ์ŠคํŠธ์ผ๋•Œ ( List<String> )

    member ํ…Œ์ด๋ธ”

    book ํ…Œ์ด๋ธ”

    package com.example.demo.domain;
    
    import lombok.Getter;
    import lombok.Setter;
    import lombok.ToString;
    
    import java.util.Collections;
    import java.util.List;
    
    @Getter @ToString
    public class Member {
        private Long id;
        private String name;
        private int age;
        private List<String> bookname;
    
        public Member(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public Member() {
        }
        //โ˜…โ˜…โ˜…โ˜…โ˜…
    }
    package com.example.demo.repository;
    
    import com.example.demo.domain.Member;
    import org.apache.ibatis.annotations.*;
    import org.springframework.stereotype.Repository;
    
    import java.util.Map;
    
    @Mapper
    @Repository
    public interface MemberRepository {
    
        @Select("select member.*, book.bookname " +
                "from member join book on member.id=book.memberid " +
                "where member.id = #{id}")
        @ResultMap("MemberBookList")
        Member selectAnno(Long id);
    
    }
    <?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.demo.repository.MemberRepository">
    
        <resultMap id="MemberBookList" type="Member">
            <id property="id" column="id"></id>
            <result property="name" column="name"></result>
            <result property="age" column="age"></result>
            <collection property="bookname" ofType="String" javaType="List">
                <result column="bookname"></result>
            </collection>
            
            <!-- <collection property="bookname" column="bookname" ofType="String" javaType="List"></collection> -->
            <!-- ์œ„์—์ฒ˜๋Ÿผ๋„ ๊ฐ€๋Šฅํ•จ -->
        </resultMap>
    
    </mapper>

     

     

    ์—ฌ๊ธฐ์„œ ์ด๊ฒƒ์ €๊ฒƒ ํ•ด๋ณด๋‹ค๊ฐ€ ์ด๋Ÿฐ ์˜ค๋ฅ˜๊ฐ€ ๋‚ฌ๋‹ค

    No constructor found in com.example.demo.domain.Member matching [java.lang.Long, java.lang.String, java.lang.Integer, java.lang.String]

     

    ๊ทธ๋ž˜์„œ ํ˜น์‹œ๋‚˜ ํ•˜๋Š” ๋งˆ์Œ์— ํ•ด๋ดค๋”๋‹ˆ..

    package com.example.demo.domain;
    
    import lombok.Getter;
    import lombok.Setter;
    import lombok.ToString;
    
    import java.util.Collections;
    import java.util.List;
    
    @Getter @ToString
    public class Member {
        private Long id;
        private String name;
        private int age;
        private List<Book> bookname;
    
        public Member(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public Member(Long id, String name, int age, String bookname) {
            this.id = id;
            this.name = name;
            this.age = age;
            //์—‡..? ๋œ๋‹ค
        }//โ˜…โ˜…โ˜…โ˜…โ˜…โ˜…
    }

    ์ด๋ ‡๊ฒŒ ํ•ด๋„ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ์—ˆ๋‹ค (?!)

    ์ƒ์„ฑ์ž๋Š” String์œผ๋กœ bookname์„ ๋ฐ›์ง€๋งŒ ๊ฐ’์„ ์ €์žฅํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค.

    ๋งˆ์ด๋ฐ”ํ‹ฐ์Šค๋ฅผ ์†์ด๋Š” ๊ผผ์ˆ˜๋ฅผ ์•Œ์•„๋‚ธ๋Š๋‚Œ..?ใ…‹ใ…‹ใ…‹ใ…‹

     

    ๊ฐ์ฒด ์•ˆ์— ๊ฐ์ฒดํƒ€์ž… ๋ฆฌ์ŠคํŠธ์ผ๋•Œ ( List<Object> )

    member ํ…Œ์ด๋ธ”

    book ํ…Œ์ด๋ธ”

    package com.example.demo.domain;
    
    import lombok.Getter;
    import lombok.Setter;
    import lombok.ToString;
    
    import java.util.Collections;
    import java.util.List;
    
    @Getter @ToString
    public class Member {
        private Long id;
        private String name;
        private int age;
        private List<Book> books;
    
        public Member(String name, int age) {
            this.name = name;
            this.age = age;
        }
        
        public Member() {
        }
        //โ˜…โ˜…โ˜…โ˜…โ˜…
    
    }
    package com.example.demo.domain;
    
    import lombok.Getter;
    import lombok.ToString;
    
    @Getter @ToString
    public class Book {
        private Long memberId;
        private String bookName;
        private int price;
    
        public Book(String bookName, int price) {
            this.bookName = bookName;
            this.price = price;
        }
    
        public Book() {
        }//โ˜…โ˜…โ˜…โ˜…โ˜…
    
    }
    package com.example.demo.repository;
    
    import com.example.demo.domain.Member;
    import org.apache.ibatis.annotations.*;
    import org.springframework.stereotype.Repository;
    
    import java.util.Map;
    
    @Mapper
    @Repository
    public interface MemberRepository {
    
        @Select("select member.*, book.* " +
                "from member join book on member.id=book.memberid " +
                "where member.id = #{id}")
        @ResultMap("MemberBookList")
        Member selectAnno(Long id);
    
    }
    <?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.demo.repository.MemberRepository">
    
        <resultMap id="BookList" type="Book">
            <result property="memberId" column="memberid"></result>
            <result property="bookName" column="bookname"></result>
            <result property="price" column="price"></result>
        </resultMap>
        
        <resultMap id="MemberBookList" type="Member">
            <id property="id" column="id"></id>
            <result property="name" column="name"></result>
            <result property="age" column="age"></result>
            <collection property="books" resultMap="BookList"></collection>
        </resultMap>
    
    </mapper>

    ๋‘˜๋‹ค ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๊ฐ€ ์žˆ์„ ๋•Œ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๋‹ค! ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด book์— ์˜ค๋ฅ˜๊ฐ€ ์žˆ์–ด๋„ member์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๊ฑธ๋ ค๋ฒ„๋ฆฌ๋”๋ผ..

     

    ๊ทธ๋ฆฌ๊ณ  ์ด๊ฒƒ๋„ ๊ผผ์ˆ˜๋กœ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๊ฐ™๋‹ค

    package com.example.demo.domain;
    
    import lombok.Getter;
    import lombok.ToString;
    
    @Getter @ToString
    public class Book {
        private Long memberId;
        private String bookName;
        private int price;
    
        public Book(String bookName, int price) {
            this.bookName = bookName;
            this.price = price;
        }
    
    //    public Book(Long a, String b, int c, Long d, String e, int f) {
    //
    //    }
        //No constructor found in com.example.demo.domain.Book matching [java.lang.Long, java.lang.String, java.lang.Integer, java.lang.Long, java.lang.String, java.lang.Intege
        //๊ฐ€์งœ ์ƒ์„ฑ์ž..
    }

    ์ด ๋ฐฉ๋ฒ•์€ ์›ฌ๋งŒํ•ด์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๊ฒŒ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค

     


    MyBatis๋ฅผ ์“ฐ๋ฉด์„œ ํ—ท๊ฐˆ๋ฆฌ๋˜ ๊ฒƒ๋“ค์„ ์ญ‰ ์ •๋ฆฌํ•ด๋ดค๋‹ค

     

    ์š”์•ฝํ•˜์ž๋ฉด, 

    1:1 ๋งตํ•‘ ์‹œ์—๋Š” <association>

    ์—ฐ๊ด€๋˜๋Š” ๊ฐ์ฒด ๋ชจ๋‘์—๊ฒŒ ๋นˆ ์ƒ์„ฑ์ž๋งŒ(!!) ์žˆ์–ด์•ผ ํ•œ๋‹ค.

    ๋ชจ๋“  ํ•„๋“œ๊ฐ€ ์žˆ๋Š” ์ƒ์„ฑ์ž๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค

     

    1:N ๋งตํ•‘ ์‹œ์—๋Š” <collection>

    ์—ฐ๊ด€๋˜๋Š” ๊ฐ์ฒด ๋ชจ๋‘์—๊ฒŒ ๋นˆ ์ƒ์„ฑ์ž ๋˜๋Š” ๋ชจ๋“  ํ•„๋“œ๊ฐ€ ์žˆ๋Š” ์ƒ์„ฑ์ž๊ฐ€ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

     

     

    ๋งˆ์Œ ํŽธํ•˜๊ฒŒ ๋นˆ ์ƒ์„ฑ์ž๋Š” ๊ผญ ์„ ์–ธํ•ด์ฃผ์ž..!

    ๋ฐ˜์‘ํ˜•
    • ๋„ค์ด๋ฒ„ ๋ธ”๋Ÿฌ๊ทธ ๊ณต์œ ํ•˜๊ธฐ
    • ํŽ˜์ด์Šค๋ถ ๊ณต์œ ํ•˜๊ธฐ
    • ํŠธ์œ„ํ„ฐ ๊ณต์œ ํ•˜๊ธฐ
    • ๊ตฌ๊ธ€ ํ”Œ๋Ÿฌ์Šค ๊ณต์œ ํ•˜๊ธฐ
    • ์นด์นด์˜คํ†ก ๊ณต์œ ํ•˜๊ธฐ