Kotlin Spring + Mybatis + Mysql 샘플 데이터 토이프로젝트 -3 Mybatis 기본 CRUD 예제

선종우·2024년 5월 7일
0

Spring 노트

목록 보기
3/10
  • 예전에 Mybatis를 사용했을 때는 XML기반으로 작성했던 기억이었다. 공식 문서를 보니 Mybatis 3부터는 애노테이션 기반의 코드 작성도 가능하다고 한다.

  • 애노테이션으로는 XML이 제공하는 모든 기능을 활용할 수는 없다고 한다. 하지만 요즘은 애노테이션 코드 작성이 더 익숙하기 때문에 우선 애노테이션 기반으로 간단하게 Mybatis를 활용해보았다.
    Mybatis API

  • 따라서 아래 제시되는 코드는 Mybatis의 기능을 완벽하게 사용할 목적보다는 Mybatis를 Jpa와 유사한 방식으로 사용해보기 위함이다.


  • 우선 Jpa와 유사하게 레포지토리 인터페이스(Mapper 인터페이스)를 정의한다.
  • 기본적으로 Mybatis는 CRUD를 위한 기본 애노테이션을 제공한다.
  • 애노테이션 내 쿼리문은 mybatis 쿼리문을 작성하는 형식으로 작성한다.
  • 기본적으로 쿼리의 결과는 객체로 자동 매핑해준다.
@Mapper
interface EmployeeMapper{
	@Select("SELECT * FROM employees WHERE emp_no = #{empNo}")
	fun findByEmpNo(empNo: Int): Employee?
    
    @Select("SELECT * FROM $EMPLOYEE_TABLE " +
            "WHERE hire_date BETWEEN #{startDate} AND #{endDate} " +
            "ORDER BY hire_date ASC " +
            "LIMIT #{pageRequest.pageNumber}, #{pageRequest.pageSize}")
    fun findBetweenStartDateAndEndDateOrderByASC(startDate: LocalDate, endDate: LocalDate, pageRequest: PageRequest): List<Employee>
 	
    @Delete("DELETE FROM $EMPLOYEE_TABLE " +
            "WHERE emp_no = #{empNo}")
    fun deleteEmployeeByEmpNo(empNo: Int): Boolean 
}
  • 애노테이션을 이용해 직관적인 쿼리메소드를 구현할 수 있다. 또한 ORM도 자동으로 해주니 매우 편리하게 코드를 작성할 수 있다.

  • Mybatis의 강점 중 하나는 동적 쿼리 작성이다. JPA의 경우 QueryDsl등을 이용해야 하지만 Mybatis는 기본적으로 QueryDsl과 같은 기능을 제공해준다.
@Mapper
interface EmployeeMapper{
	//생략
   @UpdateProvider(type = EmployeeSqlBuilder::class, method = "buildUpdateEmployee")
    fun updateEmployee(
        empNo: Int,
        birthDate: LocalDate?,
        firstName: String?,
        lastName: String?,
        gender: Gender?,
        hireDate: LocalDate?
    ): Boolean 
    
   class EmployeeSqlBuilder {
        fun buildUpdateEmployee(
            empNo: Int,
            birthDate: LocalDate?,
            firstName: String?,
            lastName: String?,
            gender: Gender?,
            hireDate: LocalDate?
        ): String {
            return object : SQL() {
                init {
                    UPDATE(EMPLOYEE_TABLE)
                    if (birthDate != null) SET("birth_date = #{birthDate}")
                    if (firstName != null) SET("first_name = #{firstName}")
                    if (lastName != null) SET("last_name = #{lastName}")
                    if (gender != null) SET("gender = #{gender}")
                    if (hireDate != null) SET("hire_date = #{hireDate}")
                    WHERE("emp_no = #{empNo}")
                }
            }.toString()
        }
    }
}
  • JPA의 경우 엔티티에 변경이 일어날 경우 모든 칼럼을 업데이트 한다.(DynamicUpdate를 사용하지 않을 경우) Mybatis는 조건문을 이용해 동적으로 쿼리문을 쉽게 만들 수 있다. 동적인 Sql을 만들기 위해서는 Mapper 인터페이스 내에 inner class(static도 상관 없음)로 SqlBuilder Class를 작성한다. 딱히 특정 인터페이스를 구현하는 게 아니기 때문에 이름은 자유롭게 지으면 된다.
  • 핵심은 SQL() 클래스를 이용해 쿼리를 생성하는 것이다. 예제는 kotlin이기 때문에 init{}을 사용해지만 공식문서에서는 static block을 사용한다. 복잡해보이지만 아래와 같은 순서를 따르면 된다.
    1. Mapper 클래스에 동적 sql을 생성하는 inner class를 선언한다.
    2. 클래스에는 동적 sql을 생성하는 메소드를 생성한다. 각 메소드는 sql을 string으로 반환한다.
    3. 각 메소드 내부에는 SQL클래스를 익명클래스 문법을 이용해 확장한다.
  • sql 문을 작성할 때는 각 sql문에 대응하는 메소드(WHERE(), SET() 등)를 이용하면 된다.

  • Insert 시 key를 자동생성하고 싶을 경우 @Options 애노테이션을 사용하면 된다.
  • Jpa와 같이 엔티티를 save메소드의 인자로 넘겨주면 자동생성된 id 값을 객체에 할당해준다.(autoGenerated인 경우)
@Mapper
interface EmployeeMapper{
	//생략
    @Insert("INSERT INTO employees (birth_date, first_name, last_name, gender, hire_date) values(#{birthDate}, #{firstName}, #{lastName}, #{gender}, #{hireDate})")
    @Options(useGeneratedKeys = true, keyColumn = "emp_no", keyProperty = "empNo")
    fun saveEmployee(
        employee: Employee
    ): Int
}

0개의 댓글