myBatis와 iBatis에서 쿼리를 실행할때 PrepareStatement 방식으로 작동을 하게 되고 이때 쿼리로 전달값은 값을 각각 아래처럼 적용한다.
<!-- ibatis 방식-->
<insert id="insertQuery" parameterType="java.util.Map">
INSERT INTO table (id , name, title) VALUES (#id#, #name#, #title#)
</select>
<!-- mybatis 방식 -->
<insert id="insertQuery">
INSERT INTO table (id , name, title) VALUES (#{id}, #{name}, #{title})
</insert>
그런데 이때 insert 하려는 값 중에 null이 전달되었을 경우 오라클에서는
uncategorized SQLException for SQL []; SQL state [99999]; error code [17004]; 부적합한 열 유형: 1111; nested exception is java.sql.SQLException: 부적합한 열 유형: 1111
이런 에러를 발생시키게 된다.
Spring을 사용했을 경우는 이런에러를 발생시킨다.
Caused by: org.springframework.jdbc.UncategorizedSQLException: Error setting null parameter. Most JDBC drivers require that the JdbcType must be specified for all nullable parameters. Cause: java.sql.SQLException : 부적합한 열 유형: 1111
그렇다고 항상 쿼리에서 null을 체크하는 방식을 사용하는것은 너무나도 귀찮은 일!! 그래서 if(myBatis)문 혹은 isNotEmpty(ibatis)를 사용하지 않는 방법이 있다. (자세히 보면 Spring Framework에서 발생시키는 에러에 답이있다.)
<!-- ibatis 방식-->
<insert id="insertQuery" parameterType="java.util.Map">
INSERT INTO table (id , name, title) VALUES (#id:VARCHAR#, #name:VARCHAR#, #title:VARCHAR#)
</insert>
<!-- mybatis 방식 -->
<insert id="insertQuery">
INSERT INTO table (id , name, title) VALUES (#{id, jdbcType=VARCHAR}, #{name, jdbcType=VARCHAR}, #{title, jdbcType=VARCHAR})
</insert>
위와 같이
전달되는 파라미터 값의 jdbcType이 무엇인지를 정해 주면 쿼리는 자연스럽게 null로 변경되어 insert query가 수행된다.(물론 해당 column이 NULL값을 저장가능한 column 이어야 한다)
지원하는 jdbcType은 아래와 같다.
- BIT
- FLOAT
- CHAR
- TIMESTAMP
- OTHER
- UNDEFINED
- TINYINT
- REAL
- VARCHAR
- BINARY
- BLOB
- NVARCHAR
- SMALLINT
- DOUBLE
- LONGVARCHAR
- VARBINARY
- CLOB
- NCHAR
- INTEGER
- NUMERIC
- DATE
- LONGVARBINARY
- BOOLEAN
- NCLOB
- BIGINT
- DECIMAL
- TIME
- NULL
- CURSOR
쿼리마다 적당이 jdbcType을 지정해서 사용하자.