MyBatis_typeHandler, ENUM값 쓰기

빙그르·2023년 5월 18일

SPRING

목록 보기
1/9

myBatis_typeHandler

출처 : https://velog.io/@ghk4889/mybatis%EC%9D%98-custom-typehandler-%EB%A7%8C%EB%93%A4%EA%B8%B0JAVA-Enum-%ED%83%80%EC%9E%85

mybatis 공식 : https://mybatis.org/mybatis-3/configuration.html

DB에서의 type과 VO에서의 type이 다를 수 밖에 없는 Enum

EX

DB : tinyint(2)

DTO / VO : ENUM (int형으로 가져오는거 아님)


tb_status DB MySQL

status tinyint(2)

State.enum

public enum Status {
    SUCCESS(0, "인증 성공"),
    FAIL(1, "인증 실패"),
    NOT_VALUE(2, "값이 없음");

    private final int code;	// 이 code 변수를 mapper에서 씀
    private final String description;

    Status(int code, String description) {
        this.code = code;
        this.description = description;
    }
}

dto

public class StatusVO {
    private Status status;	// 이렇듯 ENUM 값을 매핑한다.

    public Status getStatus() {		
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }
}

///////////////////////////////// MVC는 생략하겠음 //////////////////////////////////


INSERT 시킬때

  • .code 이거 하나면 되더라

mapper

<insert id="insertstatus" parameterType="com.test.enum.StatusVO">
    INSERT INTO tb_status (
        status
    ) VALUES (
         #{status.code}
    )
</insert>

.code 하나면 된다. (enum쪽에서 int형을 code라는 변수로 정의했기에 .code인것)
이것의 원리는 모른다..?

https://mybatis.org/mybatis-3/configuration.html

공식문서에도 나와있지 않다. 아시는분은 댓글을 달아주세요?


SELECT 해올때는?

  • 두둥.. 여기는 정말 복잡하드라. 타입핸들러를 사용해야한다.
    mapper에서 select를 해올때 타입핸들러(여기서 타입 변환) -> dto를 거치게끔 해줘야 한다.
  • ENUM값이 아니라 description (한글설명) 값을 가져오고 싶다면?? (그냥 dto get 부분만 바꿔주면 돼)

mapper

<select id="selectStatus" parameterType="hashmap" resultType="com.test.enum.StatusVO">
    SELECT
        status
    FROM
        tb_statu AS a
</select>

dto

public class StatusVO {
    private Status status;	// 이렇듯 ENUM 값을 매핑한다.

    // description 을 가져오고 싶으면 (한글설명) 요렇게 get을 해오면 되고
    // 그냥 enum값 자체를 가져오고 싶으면 수정하지 않아도 된다.
    public String getErrorState() {
        return errorState.getDescription();	
    }
    
    public void setStatus(Status status) {
        this.status = status;
    }
}

enum의 code값을 이용해서 값을 매핑해줘야 하고, description을 가져와야 하니까 추가

State.enum 에 추가

public int getCode() {
    return this.code;
}
public String getDescription() {
    return this.description;
}

타입핸들러 생성

인터페이스 생성후 implements 시켜줄것

codeEnum.java

public interface CodeEnum {
    public int getCode();
}

State.enum

public enum Status implements CodeEnum {
        public int getCode();
}

타입핸들러를 생성해보자 (JDBC와 연관되어 있음)

StatusTypeHandler.java

//  @MappedTypes : 타입핸드러를 위해 만들어진 어노테이션이다.
// 매핑할 Java 유형을 지정하는 주석입니다
// https://mybatis.org/mybatis-3/apidocs/org/apache/ibatis/type/MappedTypes.html
@MappedTypes(State.class)
public class StateTypeHandler<E extends Enum<E> & CodeEnum> implements TypeHandler<CodeEnum> {

    private final Class<E> type;
    private final E[] enumConstants;

    public StateTypeHandler(Class<E> type) {
        if (type == null) {
            throw new IllegalArgumentException("type must not be null");
        }
        this.type = type;
        this.enumConstants = type.getEnumConstants();
        if(!type.isInterface() && this.enumConstants == null) {
            throw new IllegalArgumentException(type.getSimpleName() + " is not");
        }
    }

    @Override
    public void setParameter(PreparedStatement ps, int i, CodeEnum parameter, JdbcType jdbcType) throws SQLException {
        ps.setInt(i, parameter.getCode());
    }

    @Override
    /*
        ResultSet : 데이터베이스를 쿼리하는 문을 실행하여 생성되는 데이터베이스 결과 집합을 나타내는 데이터 테이블/
        개체 ResultSet는 현재 데이터 행을 가리키는 커서를 유지합니다. 처음 커서는 첫 번째 행 앞에 위치합니다. 
        이 next	메서드는 커서를 다음 행으로 이동하고 개체 false 에 더 이상 행이 없을 때 반환되기 때문에 
        루프에서 결과 집합을 반복하는 데 ResultSet사용할 수 있습니다 
	*/
    public CodeEnum getResult(ResultSet rs, String columnName) throws SQLException {
        return getIntCodeEnum(rs.getInt(columnName));
    }

    @Override
    public CodeEnum getResult(ResultSet rs, int columnIndex) throws SQLException {
        return getIntCodeEnum(rs.getInt(columnIndex));
    }

    @Override
    /*
    CallableStatement 는 ***PreparedStatement를 extends 한다
    SQL 문은 미리 컴파일되어 PreparedStatement개체에 저장됩니다. 그런 다음 이 개체를 사용하여 이 문을 여러 번 효율	적으로 실행할 수 있습니다.
    새 파일에 따로 저장
    */
    public CodeEnum getResult(CallableStatement cs, int columnIndex) throws SQLException {
        return getIntCodeEnum(cs.getInt(columnIndex));
    }

    public CodeEnum getCodeEnum(int code) {
        return Arrays.stream(enumConstants)
                .filter(o -> o.getCode() == code)	// 필터를 통해 여기서 매칭해준다.
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("not" + code + "to" + type.getSimpleName()));
    }
}

난 이부분이 아직 어렵다... 열심히 주석을 달아보자 / 공식문서는 요기

https://mybatis.org/mybatis-3/apidocs/org/apache/ibatis/type/TypeHandler.html

마지막 설정부분

mybatis-config.xml

<!-- 타입 핸들러 설정, 타입핸들러 일괄적용 시키기 (패키지) -->
<typeHandlers>
    <package name="com.test.test.typehandler"/>
</typeHandlers>

이때 주의해야할 점이 있다. mybatis 설정 xml 파일에선 태그들의 순서를 지켜야 된다.

https://mybatis.org/mybatis-3/ko/configuration.html


****참조

  • PreparedStatement

    캐시와 비슷한 느낌이다. 별도로 저장해놓고 다음부터 요청 받으면 재사용하는거
    원래 쿼리를 처리할때의 단계 : 쿼리분석 -> 최적화 -> 권한체크 -> 쿼리 실행
    프리페어 스테이트먼트를 하면? 쿼리분석 -> 최적화을 한번만 실행하고 따로 저장해둠 (즉 속도가 빨라짐)
    ex ) selece * from status where state=1.....하며 state=100 까지 100개의 쿼리를 조회한다고 치자. 이 얼마나 비효율적인 방식이냐 이를 위해 state=#{state}가 나오게 된것이다. 이게 PreparedStatement

    PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES
                                     SET SALARY = ? WHERE ID = ?");
    pstmt.setBigDecimal(1, sal);
    pstmt.setInt(2, 110592);	//  INTEGER메소드를 setInt사용

0개의 댓글