ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [MyBatis] 매퍼 xml파일과 매퍼 인터페이스를 사용한 CRUD
    JAVA 2022. 7. 24. 23:16

    먼저 테이블이 있어야 하므로, 다음과 같은 컬럼과 값을 가진 tbl_member 테이블을 생성한다.

     

    CREATE TABLE tbl_member(
          userid varchar2(50) not null primary key,
          userpw varchar2(100) not null,
          username varchar2(100) not null,
          regdate date default sysdate, 
          updatedate date default sysdate,
          enabled char(1) default '1');

     

     

    그리고 도메인 패키지에 UserVO 클래스를 만들고 다룰 데이터들을 필드로 지정했다.

    package org.zerock.myapp.domain;
    
    import lombok.Value;
    
    @Value
    public class UserVO {
    	private String userid;
    	private String userpw;
    	private String username;
    } // end class

     

     


    1. xml파일

     

    src/main/resources에 mappers패키지를 만들고 UserMapper.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="mappers.UserMapper">
    
        <insert id="insertNewUser">
            INSERT INTO tbl_member (userid, userpw, username)
            VALUES ( #{userid}, #{userpw}, #{username} )
        </insert>
    </mapper>

     

    상단의 DOCTYPE mapper는 반드시 있어야 한다.

     

    namespace에 이름을 지정하고, select / insert / delete / update 태그에 각각 SQL문을 작성한다.

    보통 관례상 namespace는 패키지명.파일명으로 설정하고, id는 SQL문을 수행시킬 메소드 이름으로 정한다.

    네임 스페이스가 다르면 아이디가 중복되어도 된다.

     

    #{}은 메소드 수행시 직접 값을 넣어줄 때 사용한다. 이름은 마음대로 지정해도 되지만, 열이름으로 작성하는 것이 좋다.

     

    @Test
    @Order(4)
    @DisplayName("insertNewUser")
    @Timeout(value = 10, unit = TimeUnit.SECONDS)
    void insertNewUser() {
        log.trace("insertNewUser() invoked.");
    
        SqlSession sqlSession = this.sqlSessionFactory.openSession(false);	// SET AUTOCOMMIT OFF
    
        try(sqlSession;) {
    
            String namespace = "mappers.UserMapper";
            String sqlId = "insertNewUser";
    
            String sql = new StringBuffer()
                    .append(namespace)
                    .append(".")
                    .append(sqlId)
                    .toString();
    
            for(int i=1; i<=10; i++) {
                Map<String, Object> params = new HashMap<>();
    
                params.put("userid", "USER_"+i);
                params.put("userpw", "PASS_"+i);
                params.put("username", "NAME_"+i);
    
                // Map 객체를 파라미터로 전달하면서 Mapped Statement 수행(DML)
                int insertedRows = sqlSession.insert(sql, params);
                log.info("\t+ insertedRows: {}", insertedRows);
            } // for
    
            sqlSession.commit();	// TCL : ALL or Nothing
            log.info("\t+ Commited.");
        } catch(Exception e) {
            sqlSession.rollback(); 	// TCL : All or Nothing
            log.info("\t+ Rolled back.");
    
            throw e;
        } // try-with-resources
    } // insertNewUser

     

     

    메소드를 실행하는 테스트코드를 만들었다.

     

    namespace.id 형식으로 이름을 만들어서 주면 마이바티스가 경로를 찾은 후 해당하는 SQL문을 실행한다.

    sqlSession의 insert메소드의 두번째 매개변수로 맵객체를 만들어서 주면 #{}안에 입력한 이름과 일치하는 키를 찾아 값으로 넣어준다. 맵객체를 넣어도 되고, 자바빈즈 클래스를 넣어줄 수도 있다. 클래스는 따로 만들어야하기 때문에, 맵객체를 이용하는게 더 간편해보인다.

     

    DML문장을 수행할땐 오토커밋을 해제하고 직접 커밋 또는 롤백을 결정하는게 좋다.  try-catch블록을 사용해서 오류가 발생했을 경우 rollback하도록 작성했다.

     

     

    메소드를 실행하면 데이터가 잘 들어온 것을 확인할 수 있다.

     

     

     

     


     

    2. 인터페이스

     

    매퍼 xml파일 대신 인터페이스로도 SQL문장을 매핑할 수 있다. 이때 마이바티스의 어노테이션을 이용한다.

     

    package org.zerock.myapp.mapper;
    
    import java.util.List;
    
    import org.apache.ibatis.annotations.Delete;
    import org.apache.ibatis.annotations.Insert;
    import org.apache.ibatis.annotations.Param;
    import org.apache.ibatis.annotations.Select;
    import org.zerock.myapp.domain.UserVO;
    
    public interface UserMapper {
    	
    	@Select(
    		"SELECT userid, userpw, username "
    		+ "FROM tbl_member "
    		+ "WHERE userid > #{userid} AND username > #{username}")
    	public abstract List<UserVO> selectUsers(
    			@Param("userid") String userid, 
    			@Param("username") String username);
    	
    	@Insert(
    		"INSERT INTO tbl_member "
    		+ "(userid, userpw, username) "
    		+ "VALUES (#{userid}, #{userpw}, #{username})")
    	public abstract int insertUser(
    			@Param("userid") String userid, 
    			@Param("userpw") String userpw, 
    			@Param("username") String username);
    	
    	@Delete("DELETE FROM tbl_member WHERE userid = #{userid}")
    	public abstract int deleteUser(@Param("userid") String userid);
    } // end interface

     

     

    추상 메소드를 만들고, 어노테이션 안에 SQL문을 작성한다.

    DML, DQL문장별 어노테이션이 존재한다. 직접 값을 입력하고 싶을 땐 xml파일과 마찬가지로 #{}문법을 사용한다.

    #{}을 사용했을 땐 메소드 수행시 값을 파라미터로 넘겨줄 수 있도록 매개변수를 만들어준다. 이 때 @Param어노테이션을 사용한다. @Param 속성으로 #{}안에 작성한 이름을 지정하면 해당 매개변수를 값으로 넣어준다.

     

    @Test
    void 인터페이스를사용한방식() {
        log.trace("인터페이스를사용한방식() invoked.");
    
        SqlSession sqlSession = this.sqlSessionFactory.openSession();
    
        // Mapper Interface를 이용하여 SQL 문장처리
        try(sqlSession;) {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            assertNotNull(mapper);
    
            mapper.selectUsers("USER_3", "NAME_4");
            mapper.insertUser("홍길동", "1234", "닉네임");
            mapper.deleteUser("홍길동");
        } // try-with-resources
    } // 인터페이스를사용한방식

     

    SqlSession클래스의 .getMapper()메소드를 사용해 매퍼를 얻어준다. 작성한 인터페이스를 매개변수로 지정해야 하는데, clazz객체를 요구하기 때문에 클래스 객체로 넣어준다.

     

    그 후 내장된 메소드를 각각 호출하면 알아서 SQL문장에 파라미터를 넘기고 수행한다.

     

     

    DriverSpy를 사용했기 때문에 로그를 찍지 않아도 상세하게 알려준다.

    정상적으로 조회, 삽입, 삭제가 된 모습이다.

     

    간단한 SQL문장을 실행할 땐 인터페이스 방식이 간편하고, 복잡한 쿼리문을 수행할 땐 xml파일을 이용하는 것이 좋다.

    댓글

Designed by Tistory.