<selectKey keyProperty="sortNo" resultType="int" order="BEFORE">
SELECT NVL(MAX(SORT_NO), 0) + 1
FROM BOARD
WHERE BOARD_TYPE = #{boardType}
</selectKey>
MERGE INTO BOARD A
USING (
SELECT #{id} ID
, #{boardType} BOARD_TYPE
, #{titleKo} TITLE_KO
, #{contentKo} CONTENT_KO
, #{titleEn} TITLE_EN
, #{contentEn} CONTENT_EN
, #{sortNo} SORT_NO
, #{useYn} USE_YN
, #{popupYn} POPUP_YN
, #{popupFr} POPUP_FR
, #{popupTo} POPUP_TO
, #{__param.userId} CREATE_USER
, CURRENT_DATE CREATE_DATE
, #{__param.userId} UPDATE_USER
, CURRENT_DATE UPDATE_DATE
FROM DUAL
) B
ON (A.ID = B.ID)
WHEN MATCHED THEN
UPDATE
SET TITLE_KO = B.TITLE_KO
, CONTENT_KO = B.CONTENT_KO
, TITLE_EN = B.TITLE_EN
, CONTENT_EN = B.CONTENT_EN
, POPUP_YN = B.POPUP_YN
, POPUP_FR = B.POPUP_FR
, POPUP_TO = B.POPUP_TO
, USE_YN = B.USE_YN
, UPDATE_USER = B.UPDATE_USER
, UPDATE_DATE = B.UPDATE_DATE
WHEN NOT MATCHED THEN
INSERT (ID, BOARD_TYPE, TITLE_KO, CONTENT_KO, TITLE_EN
,CONTENT_EN, SORT_NO, USE_YN, POPUP_YN, POPUP_FR
,POPUP_TO, CREATE_USER, CREATE_DATE, UPDATE_USER, UPDATE_DATE
)
VALUES (B.ID, B.BOARD_TYPE, B.TITLE_KO, B.CONTENT_KO, B.TITLE_EN
,B.CONTENT_EN, B.SORT_NO, B.USE_YN, B.POPUP_YN, B.POPUP_FR
,B.POPUP_TO, B.CREATE_USER, B.CREATE_DATE, B.UPDATE_USER, B.UPDATE_DATE
)
공지사항, 이용 안내 등 게시판 형태를 가지고 있는 테이블에
데이터를 INSERT하는 평범한 SQL이다.
Mybatis + ORACLE DB를 사용하고 있다.
다만 문제가 됐던 부분은, 현재 게시판에서 글을 작성할 때 사용하는
스마트에디터에서 이미지 업로드 시 발생했는데,
크기가 큰 이미지를 업로드 할 경우 에러가 발생하는 점이었다.
BOARD 테이블의 구조는 이렇다.
첨부파일 업로드 시 따로 업로드 되지 않고, BASE64로 인코딩 되어 문자열 형태로 DB에 업로드 된다.
따라서 크기가 큰 이미지는 엄청나게 긴 문자열이 될 수 밖에 없다.
"ORA-01461: LONG 값은 LONG 열에 삽입할 때만 바인드할 수 있습니다."
"ORA-01461: can bind a LONG value only for insert into a LONG column"
이미지가 작을 경우는 들어가는데, 이미지가 클 경우는 안 들어간다?
Base64 인코딩 후 문자열 길이의 문제라고 추정했다.
검색해보니 여러 케이스가 있다고 하는데,
2. 데이터 타입이 VARCHAR2인 컬럼에 4000byte 이상의 값을 넣으려 할 경우
내 경우는 CLOB타입이라 제외.
어느 케이스로도 해결이 되지 않아서
문제로 추정되는 컬럼인 CONTENT_KO
컬럼과 CONTENT_EN
컬럼쪽 INSERT를 주석처리 하니 에러 없이 처리가 됐다.
CONTENT_
컬럼의 값이 너무 커서 생기는 문제라고 확신했다.
MERGE INTO BOARD A
USING (SELECT #{id} ID FROM DUAL) B
ON (A.ID = B.ID)
WHEN MATCHED THEN
UPDATE
SET TITLE_KO = #{titleKo}
, TITLE_EN = #{titleEn}
, POPUP_YN = #{popupYn}
, POPUP_FR = #{popupFr}
, POPUP_TO = #{popupTo}
, USE_YN = #{useYn}
, UPDATE_USER = #{__param.userId}
, UPDATE_DATE = CURRENT_DATE
, CONTENT_KO = #{contentKo}
, CONTENT_EN = #{contentEn}
WHEN NOT MATCHED THEN
INSERT (ID, BOARD_TYPE, TITLE_KO, TITLE_EN
,SORT_NO, USE_YN, POPUP_YN, POPUP_FR
,POPUP_TO, CREATE_USER, CREATE_DATE, UPDATE_USER, UPDATE_DATE, CONTENT_KO,CONTENT_EN
)
VALUES (#{id}, #{boardType}, #{titleKo}, #{titleEn}
,#{sortNo}, #{useYn}, #{popupYn}, #{popupFr}
,#{popupTo}, #{__param.userId}, CURRENT_DATE, #{__param.userId}, CURRENT_DATE, #{contentKo}, #{contentEn}
)
UPDATE문의 WHERE절에 서브쿼리가 들어갈 경우 생기는 문제라는 글을 봐서
USING절의 CONTENT_
컬럼을 서브쿼리로 이용하는 부분을 빼주었다.
UPSERT에 필요한 PK값인 ID만 USING절에 이용하고,
나머지는 가져온 파라미터를 이용하게 변경했다.
그리고 발생한 에러.
"ORA-03146: invalid buffer length for TTC field"
"ORA-03146: TTC 필드에 대한 버퍼 길이가 부적합합니다"
큰 데이터가 들어가는 컬럼을 뒤쪽으로 보내면 해결이 된다는 글을 보아서
적용해봤지만 소용이 없었다. 어떨때는 되고, 어떨때는 안되는 현상도 생겼다.
<selectKey keyProperty="sortNo" resultType="int" order="BEFORE">
SELECT NVL(MAX(SORT_NO), 0) + 1
FROM BOARD
WHERE BOARD_TYPE = #{boardType}
</selectKey>
MERGE INTO BOARD A
USING (SELECT #{id} ID FROM DUAL) B
ON (A.ID = B.ID)
WHEN MATCHED THEN
UPDATE
SET TITLE_KO = #{titleKo}
, TITLE_EN = #{titleEn}
, POPUP_YN = #{popupYn}
, POPUP_FR = #{popupFr}
, POPUP_TO = #{popupTo}
, USE_YN = #{useYn}
, UPDATE_USER = #{__param.userId}
, UPDATE_DATE = CURRENT_DATE
, CONTENT_KO = EMPTY_CLOB()
, CONTENT_EN = EMPTY_CLOB()
WHEN NOT MATCHED THEN
INSERT (ID, BOARD_TYPE, TITLE_KO, TITLE_EN
,SORT_NO, USE_YN, POPUP_YN, POPUP_FR
,POPUP_TO, CREATE_USER, CREATE_DATE, UPDATE_USER, UPDATE_DATE, CONTENT_KO,CONTENT_EN
)
VALUES (#{id}, #{boardType}, #{titleKo}, #{titleEn}
,#{sortNo}, #{useYn}, #{popupYn}, #{popupFr}
,#{popupTo}, #{__param.userId}, CURRENT_DATE, #{__param.userId}, CURRENT_DATE, EMPTY_CLOB(), EMPTY_CLOB()
)
버퍼 공간이 부족하다? 그럼 가장 크기를 많이 차지는 CONTENT_
만 INSERT를 분리시키자고 생각했다.
UPSERT 단계에서 처음에 들어가는 데이터는 EMPTY_CLOB()
으로 빈 CLOB 데이터를 넣어주고, 해당 row의 PK값으로 CONTENT_
만 UPDATE 시켜주기로 했다.
UPDATE BOARD A
SET CONTENT_KO = #{contentKo}
, CONTENT_EN = #{contentEn}
WHERE A.ID = #{id}
이제 이미지는 문제 없이 INSERT 되는것을 확인 했으나,
전임자가 용량 제한 등을 만들어놓지 않아서 그 부분은 작업이 필요하다.
굳이 쿼리를 분리시켜서 DAO 호출을 2번하게 만든 점은 아쉽지만
해결이 된 걸 다행으로 생각해야겠다.