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是一个一劳永逸的方法。

 

posted @ 2020-04-15 10:41  思思博士  阅读(606)  评论(0编辑  收藏  举报