🧩 MyBatis resultMap 자동 생성
(Oracle 기반, PL/SQL 스크립트)
MyBatis를 사용할 때 가장 귀찮은 것 중 하나가 바로 resultMap 작성입니다.
특히 테이블 컬럼이 많거나 VO가 많을수록 반복 작업이 많아지고 실수도 잦아집니다.
그래서 Oracle에서 테이블 구조를 기반으로 resultMap을 자동으로 생성해주는 PL/SQL 스크립트를 정리해봤습니다.
✅ 특징
- resultMap XML 구조 자동 생성
- PK 컬럼은 <id> 태그로 생성
- 컬럼명은 camelCase로 자동 변환
- 컬렉션/association은 제외하고 단일 테이블 기준으로만 생성
💻 사용 방법
- Oracle SQL Developer, Toad 등에서 실행
- v_target_table 변수에 대상 테이블명을 넣고 실행
SET SERVEROUTPUT ON;
DECLARE
v_result_line VARCHAR2(4000);
v_last_column_id NUMBER;
v_property_name VARCHAR2(1000);
v_target_table VARCHAR2(30) := UPPER('BUSI_OVSEE'); -- 여기서 테이블명 설정
BEGIN
-- 마지막 컬럼 ID 조회
SELECT MAX(COLUMN_ID)
INTO v_last_column_id
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = v_target_table;
DBMS_OUTPUT.PUT_LINE(' <resultMap id="resultMapId" type="com.example.' || INITCAP(LOWER(v_target_table)) || 'VO">');
FOR col IN (
SELECT utc.COLUMN_NAME, utc.COLUMN_ID,
CASE WHEN EXISTS (
SELECT 1 FROM USER_CONS_COLUMNS ucc
JOIN USER_CONSTRAINTS uc
ON ucc.CONSTRAINT_NAME = uc.CONSTRAINT_NAME
WHERE uc.CONSTRAINT_TYPE = 'P'
AND uc.TABLE_NAME = utc.TABLE_NAME
AND ucc.COLUMN_NAME = utc.COLUMN_NAME
) THEN 'Y' ELSE 'N' END AS IS_PK
FROM USER_TAB_COLUMNS utc
WHERE utc.TABLE_NAME = v_target_table
ORDER BY utc.COLUMN_ID
) LOOP
-- camelCase property name 생성
SELECT
LOWER(SUBSTR(REPLACE(INITCAP(REPLACE(LOWER(col.COLUMN_NAME), '_', ' ')), ' ', ''), 1, 1)) ||
SUBSTR(REPLACE(INITCAP(REPLACE(LOWER(col.COLUMN_NAME), '_', ' ')), ' ', ''), 2)
INTO v_property_name
FROM DUAL;
IF col.IS_PK = 'Y' THEN
v_result_line := ' <id property="' || v_property_name || '" column="' || col.COLUMN_NAME || '"/>';
ELSE
v_result_line := ' <result property="' || v_property_name || '" column="' || col.COLUMN_NAME || '"/>';
END IF;
DBMS_OUTPUT.PUT_LINE(v_result_line);
END LOOP;
-- collection + 내부 result 자동 생성
FOR child IN (
SELECT * FROM (
SELECT c_fk.TABLE_NAME AS CHILD_TABLE, a.COLUMN_NAME
FROM USER_CONSTRAINTS c_pk
JOIN USER_CONSTRAINTS c_fk ON c_pk.CONSTRAINT_NAME = c_fk.R_CONSTRAINT_NAME
JOIN USER_CONS_COLUMNS a ON c_fk.CONSTRAINT_NAME = a.CONSTRAINT_NAME
WHERE c_pk.CONSTRAINT_TYPE = 'P'
AND c_pk.TABLE_NAME = v_target_table
)
) LOOP
SELECT
LOWER(SUBSTR(REPLACE(INITCAP(REPLACE(LOWER(child.CHILD_TABLE), '_', ' ')), ' ', ''), 1, 1)) ||
SUBSTR(REPLACE(INITCAP(REPLACE(LOWER(child.CHILD_TABLE), '_', ' ')), ' ', ''), 2)
INTO v_property_name
FROM DUAL;
DBMS_OUTPUT.PUT_LINE(' <collection property="' || v_property_name || 'List" ofType="com.example.' || INITCAP(LOWER(child.CHILD_TABLE)) || 'VO">');
FOR subcol IN (
SELECT utc.COLUMN_NAME,
CASE WHEN EXISTS (
SELECT 1 FROM USER_CONS_COLUMNS ucc
JOIN USER_CONSTRAINTS uc
ON ucc.CONSTRAINT_NAME = uc.CONSTRAINT_NAME
WHERE uc.CONSTRAINT_TYPE = 'P'
AND uc.TABLE_NAME = utc.TABLE_NAME
AND ucc.COLUMN_NAME = utc.COLUMN_NAME
) THEN 'Y' ELSE 'N' END AS IS_PK
FROM USER_TAB_COLUMNS utc
WHERE utc.TABLE_NAME = child.CHILD_TABLE
ORDER BY utc.COLUMN_ID
) LOOP
SELECT
LOWER(SUBSTR(REPLACE(INITCAP(REPLACE(LOWER(subcol.COLUMN_NAME), '_', ' ')), ' ', ''), 1, 1)) ||
SUBSTR(REPLACE(INITCAP(REPLACE(LOWER(subcol.COLUMN_NAME), '_', ' ')), ' ', ''), 2)
INTO v_property_name
FROM DUAL;
IF subcol.IS_PK = 'Y' THEN
DBMS_OUTPUT.PUT_LINE(' <id property="' || v_property_name || '" column="' || subcol.COLUMN_NAME || '"/>');
ELSE
DBMS_OUTPUT.PUT_LINE(' <result property="' || v_property_name || '" column="' || subcol.COLUMN_NAME || '"/>');
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE(' </collection>');
END LOOP;
DBMS_OUTPUT.PUT_LINE(' </resultMap>');
END;
/
🧪 출력 예시 (EMP 테이블 기준)
<resultMap id="resultMapId" type="com.example.EmpVO">
<id property="empNo" column="EMP_NO"/>
<result property="busiCode" column="BUSI_CODE"/>
<result property="comNum" column="COM_NUM"/>
<result property="memberid" column="MEMBERID"/>
<result property="empDate" column="EMP_DATE"/>
<result property="empIns" column="EMP_INS"/>
<result property="part" column="PART"/>
<result property="state" column="STATE"/>
<result property="memName" column="MEM_NAME"/>
<!-- 하위 테이블과의 관계 (예: EMP -> EMP_HISTORY) -->
<collection property="empHistories" ofType="com.example.EmpHistoryVO">
<id property="historyId" column="HISTORY_ID"/>
<result property="empNo" column="EMP_NO"/>
<result property="changeDate" column="CHANGE_DATE"/>
<result property="status" column="STATUS"/>
</collection>
</resultMap>
🔚 마무리
- 이 스크립트를 활용하면 MyBatis VO 매핑 작업 시간을 크게 줄일 수 있습니다.
- 추후 VO 클래스 자동 생성이나 SQL + resultMap 묶음 생성도 확장 가능합니다.
'DB' 카테고리의 다른 글
[DB] sqldeveloper 자동완성 등록 (0) | 2024.10.30 |
---|---|
[DB] MERGE INTO (0) | 2024.07.31 |
[DB] 셀프 조인 (0) | 2024.07.31 |
[DB] 신입들이 읽어 보면 좋을 사이트 링크 (0) | 2024.05.27 |
[DB] 댓글 계층형 쿼리 (0) | 2024.04.03 |