[error] ORA-06502: PL/SQL: 수치 또는 값 오류: 원시 변수 길이가 너무 깁니다 (For Select & Insert)
📌에러 환경
공지사항에 길이가 긴 글 등록 시 오류.
오라클 DB 사용.
게시글 데이터 타입은 BLOB.
🚨에러1
### Error updating database. Cause: java.sql.SQLException: ORA-06502: PL/SQL: 수치 또는 값 오류: 원시 변수 길이가 너무 깁니다
ORA-06512: "SYS.UTL_RAW"
📖 기존 sql
- Insert
<!-- 공지 사항 저장 -->
<update id="insertNotice" parameterType="NoticeVO">
MERGE INTO TB_NOTI_MGMT target
USING (
SELECT
#{sno} AS sno,
#{title} AS title,
UTL_RAW.CAST_TO_RAW(#{content}) AS cont
FROM DUAL
) source
ON target.SNO = #{sno}
WHEN MATCHED THEN
UPDATE SET
TITLE = source.title,
CONTENT = source.content
WHEN NOT MATCHED THEN
INSERT ( SNO, TITLE, CONTENT)
VALUES (source.sno, source. title, source.content)
</update>
- Select
<!-- 공지 수정화면 조회 -->
<select id="selectNotice" resultType="NoticeVO" parameterType="NoticeVO">
SELECT
A.SNO AS sno,
A. TITLE AS title,
UTL_RAW.CAST_TO_VARCHAR2(A.CONTENT) AS content,
A.VIEW_CNT AS viewCnt
FROM TB_NOTI_MGMT A
WHERE 1=1
AND A.SNO = #{sno}
</select>
🔥원인
- CAST_TO_RAW()는 문자열을 RAW로 변환하는데,
VARCHAR2 → RAW 변환 시 길이 제한은 32,767바이트(PL/SQL), 4000바이트(SQL)
🛠 해결방법
- UTL_RAW 함수를 쿼리에서 모두 제거
- 애플리케이션에서 String byte변환을 해주면 된다.
나는 관리자 시스템에서 공지사항 등록 및 수정, 일반 사용자 시스템에서 공지사항 조회로 나누어져 있어서 사용자 시스템은 vo방식만 이용하고, 관리자 시스템에서 등록할 때에는 서비스단에 적용했다.
2가지 방법 중 하나를 적용하면 된다.
✅1. JAVA(spring service)에서 String <-> byte 변환
📖 Insert
try {
String strContent = abstractVo.getInfo("content");
byte[] blobContent = strContent.getBytes("UTF-8");
abstractVo.add("content", blobContent);
} catch(Exception e) {
logger.error( e.getMessage() );
}
📖 Select
if(abstractVo == null) {
abstractVo.put("contentStr", "");
}else {
Byte[] ByteCont = (Byte[])abstractVo.get("content");
byte[] byteCont = ArrayUtils.toPrimitive(ByteCont);
String strContent = null;
try {
strContent = new String(byteCont, "UTF-8" );
}catch(Exception e) {
logger.error(e.getMessage());
}
abstractVo.put("contentStr", strContent);
}
✅2. Vo에서 변환
아래 vo가 이미 적용되어 있었기 때문에 sql만 지웠다.
- getCont()
- setCont(String cont)
- setCont(byte[] cont)
import java.nio.charset.StandardCharsets;
import java.util.List;
public class NoticeVO {
private String sno = "";
private String title = "";
private byte[] content;
private List<FileVO> delFiles;
private List<FileVO> uploadedFiles;
// 일반 필드 getter, setter 생략
@Override
public String toString() {
return "NoticeVO [sno=" + sno + ", title=" + title + ", content="+ content + "]";
}
public String getContent() { // BLOB 데이터를 문자열로 변환 (UTF-8 인코딩 사용)
if (content != null) {
return new String(content, StandardCharsets.UTF_8);
}
return null;
}
public void setContent(String content) { // 문자열을 BLOB(byte[])로 변환 (UTF-8 인코딩 사용)
if (cont != null) {
this.content = content.getBytes(StandardCharsets.UTF_8);
} else {
this.content = null;
}
}
public void setContent(byte[] content) { this.content = content; }
public List<FileVO> getUploadedFiles() { return uploadedFiles; }
public void setUploadedFiles(List<FileVO> uploadedFiles) { this.uploadedFiles = uploadedFiles; }
public List<FileVO> getDelFiles() { return delFiles; }
public void setDelFiles(List<FileVO> delFiles) { this.delFiles = delFiles; }
}
📌에러2
근데 위처럼 수정 후에도 에러가 발생했다.
Request processing failed; nested exception is org.springframework.jdbc.UncategorizedSQLException: Error attempting to get column 'CONT' from result set. Cause: java.sql.SQLException: 부적합한 열 유형: getString not implemented for class oracle.jdbc.driver.T4CBlobAccessor
🔥원인
- Oracle BLOB 타입 컬럼을 MyBatis가 getString() 으로 읽으려고 해서 발생
- BLOB은 getString()으로 직접 조회 불가
🛠 해결법
- resultMap 추가, jdbcType="BLOB" 과 javaType="byte[]" 명시하기
- VO 필드 타입을 byte[]로 맞춤
수정된 코드
<resultMap id="noticeResultMap" type="NoticeVO">
<result property="sno" column="SNO"/>
<result property="title" column="TITLE"/>
<result property="content" column="CONTENT" jdbcType="BLOB">
</resultMap>
<!-- 공지 수정화면 조회 -->
<select id="selectNotice" resultMap="noticeResultMap" parameterType="NoticeVO">
SELECT
A.SNO AS sno,
A.TITLE AS title,
A.CONTENT AS content
FROM TB_NOTI_MGMT A
WHERE 1=1
AND A.SNO = #{sno}
</select>
🚨에러2
<result property="cont" column="CONT" jdbcType="BLOB" javaType="byte[]"/> 일 때
Request processing failed; nested exception is org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: Could not set property 'cont' of 'class NoticeVO' with value '[Ljava.lang.Byte;@64c9ed7' Cause: java.lang.IllegalArgumentException: Unsupported type for cont: class [Ljava.lang.Byte;
<result property="cont" column="CONT" jdbcType="BLOB"> 일 때
Request processing failed; nested exception is org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: Could not set property 'cont' of 'class NoticeVO' with value 'oracle.sql.BLOB@757bfee0' Cause: java.lang.IllegalArgumentException: Unsupported type for cont: class oracle.sql.BLOB
🔥원인
- MyBatis가 Oracle 드라이버의 oracle.sql.BLOB 객체를 VO의 byte[] cont 필드에 바로 매핑 못함
- 타입이 달라서 setCont(byte[])에 바로 못 넣음
🛠 해결법
- MyBatis가 BLOB 데이터를 byte[]로 변환할 수 있도록 typeHandler="org.apache.ibatis.type.BlobTypeHandler" 명시
📖 수정된 코드
<resultMap id="noticeResultMap" type="NoticeVO">
<result property="sno" column="SNO"/>
<result property="title" column="TITLe"/>
<result property="content" column="CONTENT" jdbcType="BLOB" typeHandler="org.apache.ibatis.type.BlobTypeHandler"/>
</resultMap>
🚨에러3
Could not set property 'cont' of 'class NoticeVO' with value '[B@300941a7' Cause: java.lang.IllegalArgumentException: argument type mismatch
🔥원인
- MyBatis가 BLOB을 byte[]로 변환해서 넘겼는데,
- VO의 setCont 메서드 오버로딩(setCont(String), setCont(byte[]))으로 인해 리플렉션에서 어느 메서드 호출할지 혼란 발생
- 타입 불일치 에러 발생
🛠 해결법
- setCont(Object cont) 형태로 변경하여, 들어오는 값 타입을 런타임에 체크하고 적절히 처리하도록 구현
📖 수정된 코드
- getter는 String getCont() 등 필요한 형태로 구현
- MyBatis가 호출하는 메서드가 명확해져 타입 불일치 문제 해결
public void setContent(Object content) {
System.out.println("setContent called with type: " + (content == null ? "null" : content.getClass().getName()));
if (content instanceof byte[]) {
this.content = (byte[]) content;
} else if (content instanceof String) {
this.content = ((String) content).getBytes(StandardCharsets.UTF_8);
} else if (content == null) {
this.content = null;
} else {
throw new IllegalArgumentException("Unsupported type for content: " + content.getClass());
}
}
🚨INSERT 에러
### The error may involve adminMapper.insertNotice-Inline
### The error occurred while setting parameters
### SQL: MERGE INTO TB_NOTI_MGMT target USING ( SELECT ? AS sno, ? AS titl, ? AS cont ) source ON (target.SNO = ?) WHEN MATCHED THEN UPDATE SET TITL = source.titl, CONT = source.cont WHEN NOT MATCHED THEN INSERT (SNO, TITL, CONT) VALUES (source.sno, source.titl, source.cont)
### Cause: java.sql.SQLException: ORA-01465: 16진수의 지정이 부적합합니다
; uncategorized SQLException; SQL state [72000]; error code [1465]; ORA-01465: 16진수의 지정이 부적합합니다
; nested exception is java.sql.SQLException: ORA-01465: 16진수의 지정이 부적합합니다
🛠 해결방법
✅1. vo에 blob(mybatis에서 get), string(프론트에 반환) 타입 필드 각각 추가.
public class NoticeVO {
private String sno = "";
private String title = "";
private byte[] content;
private byte[] contentBlob;
public String getContent () {
// BLOB 데이터를 문자열로 변환 (UTF-8 인코딩 사용)
if (content != null) {
return new String(content , StandardCharsets.UTF_8);
}
return null;
}
public byte[] getContentBlob() {
return content ;
}
public void setContentBlob(byte[] contentBlob ) {
this.contentBlob = contentBlob;
}
}
✅2. 서비스 단에서 변환하여 삽입.
try {
String strContent = inputVO.getContent();
byte[] blobContent = strContent.getBytes("UTF-8");
inputVO.setContBlob(blobContent);
} catch(Exception e) {
logger.error( e.getMessage() );
}
<update id="insertNotice" parameterType="NoticeVO">
MERGE INTO TB_NOTI_MGMT target
USING (
SELECT
#{sno} AS sno,
#{title} AS title,
#{contentBlob, jdbcType=BLOB} AS content,
FROM DUAL
) source
ON target.SNO = #{sno}
WHEN MATCHED THEN
UPDATE SET
TITLE = source.title,
CONTENT = source.content
WHEN NOT MATCHED THEN
INSERT ( SNO, TITLE, CONTENT)
VALUES (source.sno, source.title, source.content)
</update>