在使用mybatis时,我们可以指定jdbcType,对jdbcType一直有疑惑,有时候可以不加,有时候不加又会报错,网上对于jdbcType的解释也不是很全面,或者可能就是错的,今天专门研究了下,算是对这个知识点有了些了解。

首先说结论,对于如下一条insert语句(这里只是做测试,实际中肯定不会这么写),如果我们的age传的空,那么对于mysql数据库可以正常插入,对于oracle数据库,会报错“无效的列类型”。也就是说对于mysql数据库的插入来说,jdbcType是没用的,oracle数据库是有用的(值为null时有用)。

insert into test(id, name, age)
values (7, #{name,jdbcType=VARCHAR},#{age})

我没有测试更新的情况,但是我猜测结论应该是一样的。对于查询操作,在mybatis配置<resultMap>时也可以指定jdbcType,这里的jdbcType至少在我测试时也没发现有什么作用(mysql, oracle都是),当然可能只是我没发现,不代表没有作用,不作为编码建议。

insert操作时jdbcType的作用测试

这里只分析insert操作时,为什么mysql和oracle表现会不同。

我们用如下代码来进行测试。

mybatis mapper中的sql如下,因为oracle自增主键比较麻烦,这里为了方便写死了id,仅测试用。

    <insert id="testJdbcType">
        insert into t_user(id, name, age)
        values (7, #{name,jdbcType=VARCHAR}, #{age})
    </insert>

java测试代码如下,这里就不贴全部代码了,用过mybatis应该都能看懂。

    User user = new User();
    user.setName("zzj");
    user.setAge(null);
    mapper.testJdbcType(user);

然后我们分别连接mysql和oracle,查看执行结果,mysql可以正常插入,但是oracle数据库会报如下错误(Error setting null for parameter #2 with JdbcType OTHER .   java.sql.SQLException: 无效的列类型)

原因分析

mybatis中的增删改查基本都是用PreparedStatement来实现的,PreparedStatement有个setNull方法,方法签名如下:

void setNull(int parameterIndex, int sqlType) throws SQLException;

当我们要插入的值是null时,mybatis就会调用PreparedStatement的setNull来设置对应的参数。我们跟踪下mybatis执行过程,可以发现是在org.apache.ibatis.scripting.defaults.DefaultParameterHandler#setParameters这个方法中进行sql的参数设置。我们断点执行到这个方法,如下图。

从上图可以发现,由于我们age参数没有设置jdbcType,JdbcType jdbcType = parameterMapping.getJdbcType();这一行代码获取到的jdbcType为null,这时就会进入到if里,把jdbcType赋值为默认值,默认值是JdbcType.OTHER。然后接着执行第87行typeHandler.setParameter(ps, i + 1, value, jdbcType);,这一行就是对sql中的第二个参数进行设置,继续向下调试会发现,最终会调用PreparedStatement的setNull方法去设置null值,由于java.sql.PreparedStatement是个接口,具体怎么setNull,是由不同的数据库驱动程序去实现的。

我们先来看mysql的setNull方法,如下图。

可以看到,尽管第2个参数为sqlType,但是mysql实现中并没有用到这个参数,所以jdbcType的值对mysql是没有什么用的。

我们再来看oracle是如何实现setNull的,如下图,由于下载不到源码,所以oracle的代码看起来没那么直观,不过对于分析完全够用了。

 

 从上图可以看出,oracle的setNull会调用setNullCritical,而setNullCritical中又会用jdbcType这个参数调用getInternalType,这个方法内部是一长串的switch case判断,可以发现对于找不到的jdbcType,这个方法会抛出异常,这个异常就是上面我们做测试时的异常来源。而我们知道,上面mybatis把jdbcType默认设置成了JdbcType.OTHER,这个值oracle是不支持的,所以报错了。

有了上面的分析,我们就知道mybatis在插入数据时是怎么使用jdbcType的了,由此也可得出结论,对于插入操作,如果某个字段值为null,jdbcType的设置对mysql数据库没什么用,对oracle数据库有用。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐