MyBatis+Oracle在执行insert时空值报错之从源码寻找解决办法
为了便于测试,先给出Demo代码:
mybatis-oracle-config.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 3 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 4 5 <configuration> 6 <properties> 7 <property name="driver" value="oracle.jdbc.driver.OracleDriver"/> 8 <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521/orcl"/> 9 </properties> 10 <environments default="dev"> 11 <environment id="dev"> 12 <transactionManager type="JDBC" /> 13 <dataSource type="POOLED"> 14 <property name="driver" value="${driver}"></property> 15 <property name="url" value="${url}"></property> 16 <property name="username" value="gys"></property> 17 <property name="password" value="gys"></property> 18 </dataSource> 19 </environment> 20 21 </environments> 22 <mappers> 23 <mapper resource="mapper/oracle/user.xml"></mapper> 24 </mappers> 25 </configuration>
user.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 3 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 4 <mapper namespace="dao.oracle.IUserMapper"> 5 <insert id="insertUser" parameterType="model.oracle.User"> 6 insert into users 7 (name,age) 8 values 9 (#{name},#{age}) 10 </insert> 11 12 </mapper>
main()入口方法
public static void main(String[] args) throws Exception{ SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory=builder.build(Resources.getResourceAsStream("mybatis-oracle-config.xml"),"dev"); SqlSession sqlSession=sqlSessionFactory.openSession(true); IUserMapper userMapper=sqlSession.getMapper(IUserMapper.class); User user=new User(); //user.setName("a");//故意注释,不设置。模拟空值 user.setAge(20); int count=userMapper.insertUser(user); System.out.println(count == 1 ? "插入成功" : "插入失败"); List<User> list=userMapper.getUserList(); for (User user1 : list) { System.out.println(user1.toString()); } sqlSession.close(); }
源码分析请参考这篇博客:Mybatis+Oracle搭配insert空值报错之myBatis+mysql驱动+oracle驱动的源码分析
总结一下空值报错的原因:
mybatis在build阶段,不知道这个参数的具体jdbcType类型,mybatis会给他一个默认的1111编号;
在mybatis运行阶段,空值 +111编号条件就使得MyBatis去调用了Oracle驱动中预编译器的setNull()方法;
因为oracle不识别1111编号,所以直接就抛出了异常。
方法1:构建时办法
在user.xml的sql中,给参数指明具体的jdbcType类型,让oracle预编译器能够知道以varchar或者NUMERIC方式处理这种空值。
修改后的代码如下红色地方。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="dao.oracle.IUserMapper"> <insert id="insertUser" parameterType="model.oracle.User"> insert into users (name,age) values (#{name,jdbcType=VARCHAR},#{age}) </insert> </mapper>
方法2:运行时办法
编写自定义的typeHandler。
当MyBatis不知道具体的jdbcType类型时,在自定义typeHandler中指定jdbcType。
数据库中常用的就两种字段,一个VARCHAR字段,一个NUMBERIC字段。所以定义两个typeHandler。
public class MyNumberNullTypeHandler extends IntegerTypeHandler { @Override public void setParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType) throws SQLException { super.setParameter(ps, i, parameter, JdbcType.NUMERIC); } }
public class MyStringNullTypeHandler extends StringTypeHandler { @Override public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { super.setParameter(ps,i,parameter,JdbcType.VARCHAR); } }
mybatis-oracle-config.xml中的properties后面添加如下配置
<typeHandlers> <typeHandler handler="handler.oracle.MyStringNullTypeHandler" /> <typeHandler handler="handler.oracle.MyIntegerNullTypeHandler" /> </typeHandlers>
综合比较后感觉方法2是一个一劳永逸的方法。