Mybatis - ArrayType Handler

nokui·2024년 6월 19일

Mybatis

목록 보기
1/1

PostgreSQL의 Array 데이터 타입을 사용하여 프로젝트 진행 도중 Mybatis 조회 시에 에러가 발생하는 것을 확인했다. 기본적으로 Mybatis 가 _지원하지 않는 데이터 타입을 읽고자 할 때는 사용자 정의 타입 핸들러 클래스를 만들고 등록하는 것이 필요하다.

1. 타입 핸들러

마이바티스가 PreparedStatement에 파라미터를 설정하고 ResultSet에서 값을 가져올때마다 TypeHandler는 적절한 자바 타입의 값을 가져오기 위해 사용된다.
-> 디폴트 TypeHandlers 안에는 Array 타입이 없으므로 사용자가 정의해서 사용한다.

2. 적용 예시

1) 사용자 정의 TypeHandler 클래스 생성

package com.t4e1.minihub.config;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

@MappedTypes(java.util.List.class)
@MappedJdbcTypes(JdbcType.ARRAY)
public class ArrayTypeHandler extends BaseTypeHandler<List<String>> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException {
        Array array = ps.getConnection().createArrayOf("varchar", parameter.toArray());
        ps.setArray(i, array);
    }

    @Override
    public List<String> getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return toList(rs.getArray(columnName));
    }

    @Override
    public List<String> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return toList(rs.getArray(columnIndex));
    }

    @Override
    public List<String> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return toList(cs.getArray(columnIndex));
    }

    private List<String> toList(Array pgArray) throws SQLException {
        if (pgArray == null)
            return new ArrayList<>();

        String[] strings = (String[]) pgArray.getArray();
        return containsOnlyNulls(strings) ? new ArrayList<>() : new ArrayList<>(List.of(strings));
    }

    private boolean containsOnlyNulls(String[] strings) {
        for (String s : strings) {
            if (s != null) {
                return false;
            }
        }
        return true;
    }
}


createArrayOf에는 테이블에서 사용하는 배열의 자료형을 입력한다. 여기서는 Varchar()[]로 컬럼을 만들었기 때문에 "varchar"를 넣어주었다.


2) 핸들러 등록

작성한 핸들러를 Mybatis에 등록해준다. mybatis-config.xml 파일을 사용하는 경우도 있지만, 여기서는 @Configuration 클래스를 사용해서 추가해본다.

package com.t4e1.minihub.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.Properties;

@Configuration
@MapperScan(basePackages = "com.t4e1.minihub", annotationClass = Mapper.class)
public class MybatisConfig {

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);

        Properties properties = new Properties();
        properties.setProperty("typeHandlers", "com.t4e1.minihub.config.ArrayTypeHandler");
        sessionFactory.setConfigurationProperties(properties);

        return sessionFactory.getObject();
    }
}

3) resultmap

마지막으로 Mapper.xml의 resultmap에 등록해준다.

    <resultMap id="recordList" type="com.t4e1.minihub.query.history.dto.RecordDTO">
        <id property="id" column="id"/>
        <result property="title" column="title"/>
        <result property="content" column="content"/>
        <result property="path" column="img_path"/>
        <result property="tags" column="tags" typeHandler="com.t4e1.minihub.config.ArrayTypeHandler"/>
        <result property="pwd" column="pwd"/>
    </resultMap>

typeHandler="" 를 등록할 때, 핸들러의 클래스명 만을 입력하니 핸들러를 찾을 수 없다는 에러가 발생했다. 전체 경로를 적어주자.


4) 결과

성공적으로 테스트 코드가 동작하며, 로깅용 출력에 tags가 배열로 나오는 것과 특정 배열에서 인덱스를 지정해서 값을 확인할 수 있는 것을 확인할 수 있다.


3. 이후 방향

원했던 결과를 얻었지만 아직 Mybatis에 대해 잘 이해하지 못하고 있다는 것을 느낄 수 있었다. SqlSessionFactory 나 Mybatis의 기본 동작에 대해서 별도로 정리할 필요가 있고, 사용자 정의 Handler의 각 메소드에 대해서도 한번 정리해 볼 것


profile
프로젝트 진행하면서 정리용

0개의 댓글