๋ชฉ์ฐจ
MyBatis ๋ฑ์ฅ๋ฐฐ๊ฒฝ
๊ธฐ์กด์ 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๋ ์ด๋ป๊ฒ ์ปฌ๋ผ์ ๋งตํํ ๊น?
(์ฌ์ค ๋ด๊ฐ ์์ฒญ ํค๋งจ ๋ด์ฉ์ด๋ค..)
๋๋ 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>
์ฐ๊ด๋๋ ๊ฐ์ฒด ๋ชจ๋์๊ฒ ๋น ์์ฑ์ ๋๋ ๋ชจ๋ ํ๋๊ฐ ์๋ ์์ฑ์๊ฐ ์์ด์ผ ํ๋ค.
๋ง์ ํธํ๊ฒ ๋น ์์ฑ์๋ ๊ผญ ์ ์ธํด์ฃผ์..!
'๐พ Database' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[JPA] ์ฐ๊ด๊ด๊ณ ๋งคํ (0) | 2022.02.20 |
---|---|
[JPA] Java Persistence API ๋ฑ์ฅ๋ฐฐ๊ฒฝ, ์ฌ์ฉ๋ฐฉ๋ฒ (0) | 2022.01.28 |
[JDBC] ์ฌ์ฉ๋ฐฉ๋ฒ (0) | 2022.01.26 |
[MyBatis] String์ ๋๊ฒผ์๋ Out of range value for column ์ค๋ฅ ํด๊ฒฐ..? (0) | 2022.01.25 |
[MyBatis] ๊ฐ์ฒด ์์ ๋ฆฌ์คํธ, 1:N ๊ด๊ณ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ (feat. ResultMap, Association, Collection) (0) | 2022.01.20 |