- JdbcTemplate을 이용하면 쿼리문과 코드가 섞이게 됨 → 유지보수가 힘들어짐
- 쿼리와 코드를 구분하기 위해 사용하는 것이 MyBatis
Getting Started
의존성, config 설정
mybatis.org — mybatis spring boot autoconfigure, config 설명
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.1'
spring: datasource: username: "root" password: "1234" url: "jdbc:mysql://localhost:3306/weareone" mybatis: type-aliases-package: com.example.springjpa.repository.domain # 해당 pojo object에 resultSet을 자동으로 매핑해줌 type-handlers-package: com.kdt.lecture.repository.typehandler configuration: map-underscore-to-camel-case: true # first_name, last_name -> firstName, lastName default-fetch-size: 100 # 한번에 몇개 가져올 거냐 default-statement-timeout: 30 #statement가 RDB와 통신하는 객체인데 여기서 timeout을 얼마로 할거냐 mapper-locations: classpath:mapper/*.xml
Mapper 코드 작성
// Using Annotation @Mapper public interface CustomerMapper { @Insert("INSERT INTO customers (id, first_name, last_name) VALUES(#{id}, #{firstName}, #{lastName})") void save(Customer customer); @Update("UPDATE customers SET first_name=#{firstName}, last_name=#{lastName} WHERE id=#{id}") void update(Customer customer); @Select("SELECT * FROM customers") List<Customer> findAll(); @Select("SELECT * FROM customers WHERE id = #{id}") Customer findById(@Param("id") long id); } ... // Using XML @Mapper public interface CustomerXmlMapper { void save(Customer customer); void update(Customer customer); Customer findById(long id); List<Customer> findAll(); }
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.kdt.lecture.repository.CustomerXmlMapper"> <insert id="save"> INSERT INTO customers (id, first_name, last_name) VALUES (#{id}, #{firstName}, #{lastName}) </insert> <update id="update"> UPDATE customers SET first_name=#{firstName}, last_name=#{lastName} WHERE id = #{id} </update> <select id="findById" resultType="customers"> SELECT * FROM customers WHERE id = #{id} </select> <select id="findAll" resultType="customers"> SELECT * FROM customers </select> </mapper>
XML 작성법
[MyBatis ] Mapper 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.kdt.lecture.repository.CustomerXmlMapper"> <insert id="save"> INSERT INTO customers (id, first_name, last_name) VALUES (#{id}, #{firstName}, #{lastName}) </insert> <update id="update"> UPDATE customers SET first_name=#{firstName}, last_name=#{lastName} WHERE id = #{id} </update> <select id="findById" resultType="customers"> SELECT * FROM customers WHERE id = #{id} </select> <select id="findAll" resultType="customers"> SELECT * FROM customers </select> </mapper>
- <mapper> tag에 namespace에는
@Mapper
인터페이스의 풀 경로를 입력
select
,insert
,update
,delete
의id
에 매핑할 method의 이름을 작성
INSERT, UPDATE
useGeneratedKeys
: MyBatis에게 DB의 auto increment 기능으로 부터 키값을 가져오는 기능을 활성화 하겠다 라는 옵션 설정
keyProperty
: getGeneratedKeys의 value를 어느 프로퍼티에 설정할 것인지. 즉 키값이 어떤 필드인지
<insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id"> insert into Author (username,password,email,bio) values (#{username},#{password},#{email},#{bio}) </insert>
insert
호출하고 나면 파라미터로 들어간 객체의pk
값에 DB에서auto_increment
된 값 할당됨
ResultMapping
resultMap을 이용하여 sql에 의해 반환되는 결과를 class에 매핑시킬 수 있음(컬럼명과 프로퍼티명이 다른 경우)
<typeAlias type="com.someapp.model.User" alias="User"/> <resultMap id="userResultMap" type="User"> <id property="id" column="user_id" /> <result property="username" column="user_name"/> <result property="password" column="hashed_password"/> </resultMap>
id
값은 객체 인스턴스를 비교할 때 사용되는 구분자 프로퍼티로 처리됨
Auto-mapping
<select id="selectUsers" resultMap="userResultMap"> select user_id as "id", user_name as "userName", hashed_password from some_table where id = #{id} </select> <resultMap id="userResultMap" type="User"> <result property="password" column="hashed_password"/> </resultMap>
- 위의 경우, id와 userName은 컬럼과 클래스 필드의 이름이 같아서 자동매핑이 진행되고, hashed_password의 경우 수동 매핑으로 처리됨
자동매핑에는 3가지가 있음
- NONE : 자동매핑 사용 x
- PARTIAL(default) : 조인 내부에 정의한 내포된 결과매핑을 제외하고 자동매핑
- FULL : 모든 것을 자동매핑
자동매핑 설정에 상관없이 구문 별로 autoMapping
속성을 추가해서 자동 매핑을 사용하거나 사용하지 않을 수도 있음
<resultMap id="userResultMap" type="User" autoMapping="false"> <result property="password" column="hashed_password"/> </resultMap>
Parameters
- 디폴트 설정으로
#{}
을 사용하게 되면 MyBatis에서는 PreparedStatement를 생성하고 거기에 파라미터를 대입함
#{firstName} #{middleInitial,jdbcType=VARCHAR} #{lastName}
- 메서드의 parameter가 클래스 일 때, 클래스의 프로퍼티를 파라미터로 넘겨줄 수 있음
<insert id="insertUser" parameterType="User"> insert into users (id, username, password) values (#{id}, #{username}, #{password}) </insert>
String Substitution
- String Substitution은 sql의 metadata(테이블 이름, 컬럼 이름)가 동적이어야 할 때 매우 유용함
@Select("select * from user where ${column} = #{value}") User findByColumn(@Param("column") String column, @Param("value") String value);
${column}
은 바로 값이 대치가 되고,#{value}
는PreparedStatement
로 대입됨