为了便于SEO搜索到,首先把报错内容贴出来吧
不同版本的Oracle驱动会报不同的错
1 <dependency> 2 <groupId>com.oracle</groupId> 3 <artifactId>ojdbc6</artifactId> 4 <version>1.0</version> 5 </dependency>
报错如下:
Error updating database. Cause: org.apache.ibatis.type.TypeException: Could not set parameters for mapping: ParameterMapping{property=‘name‘, mode=IN, javaType=class java.lang.String, jdbcType=null, numericScale=null, resultMapId=‘null‘, jdbcTypeName=‘null‘, expression=‘null‘}. Cause: org.apache.ibatis.type.TypeException: Error setting null for parameter #1 with JdbcType OTHER . Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. Cause: java.sql.SQLException: 无效的列类型: 1111
<dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc4</artifactId> <version>1.0</version> </dependency>
报错如下:
Error updating database. Cause: org.apache.ibatis.type.TypeException: Could not set parameters for mapping: ParameterMapping{property=‘name‘, mode=IN, javaType=class java.lang.String, jdbcType=null, numericScale=null, resultMapId=‘null‘, jdbcTypeName=‘null‘, expression=‘null‘}. Cause: org.apache.ibatis.type.TypeException: Error setting null for parameter #1 with JdbcType OTHER . Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. Cause: java.sql.SQLException: 无效的列类型
有异常那就一点一点的对着MyBatis调试追踪吧。避免啰嗦,就用ojdbc6调试吧;因为ojbc6与mybatis的最新版本搭配更稳定。
至于为什么不稳定可以看看我的这篇博客:MyBatis+Oracle时出现的错误: Method oracle/jdbc/driver/OracleResultSetImpl.isClosed()Z is abstract
便于源码分析,还是先上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 11 <environments default="dev"> 12 <environment id="dev"> 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 </mapper>
Main方法入口:
1 public static void main(String[] args) throws Exception{ 2 SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); 3 SqlSessionFactory sqlSessionFactory=builder.build(Resources.getResourceAsStream("mybatis-oracle-config.xml"),"dev"); 4 SqlSession sqlSession=sqlSessionFactory.openSession(true); 5 IUserMapper userMapper=sqlSession.getMapper(IUserMapper.class); 6 User user=new User(); 7 //此处不设置,故意插入null数据 8 //user.setName("gggg"); 9 user.setAge(20); 10 int count=userMapper.insertUser(user); 11 System.out.println(count == 1 ? "插入成功" : "插入失败"); 12 sqlSession.close(); 13 }
运行结果就是上面的报错内容了。
我们直接从SimpleExecutor.java执行器开始分析吧。
不了解执行器的可以看看我的这篇博客:MyBatis中Executor源码解析之BatchExecutor搞不懂
这个地方的stmt是指向OraclePreparedStatementWrapper.java这个类的;
看来这个是Oracle驱动提供的类,继承了JDBC的Statement接口
同时这个handler是指向RoutingStatementHandler类
第88行代码是开始进行sql参数进行设置的方法。我们追踪进去看看是如何实现的。
直接去PreparedStatementHandler类吧;因为RoutingStatmentHandler继承自PreparedStatmentHandler类。
继续看setParameters()源码:
1 @Override 2 public void setParameters(PreparedStatement ps) { 3 //获取该sql中所有的参数映射对象 4 List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); 5 if (parameterMappings != null) { 6 for (int i = 0; i < parameterMappings.size(); i++) { 7 ParameterMapping parameterMapping = parameterMappings.get(i); 8 //如果不是出参 9 if (parameterMapping.getMode() != ParameterMode.OUT) { 10 Object value; 11 //获取参数的属性名,比如name,age 12 String propertyName = parameterMapping.getProperty(); 13 MetaObject metaObject = configuration.newMetaObject(parameterObject); 14 //获取参数的预设值,比如name=5,这里value就是5 15 value = metaObject.getValue(propertyName); 16 //根据参数获取类型转换器 17 TypeHandler typeHandler = parameterMapping.getTypeHandler(); 18 //获取jdbc类型,这里是枚举;如果是空着,返回other枚举值,并且枚举的code属性值是1111 19 JdbcType jdbcType = parameterMapping.getJdbcType(); 20 //这行条件基本不会执行,因为jdbcType在build时候,始终都会有值,空值的话默认是other枚举 21 if (value == null && jdbcType == null) { 22 jdbcType = configuration.getJdbcTypeForNull(); 23 } 24 //参数设置开始交给类型转换器进行赋值 25 typeHandler.setParameter(ps, i + 1, value, jdbcType); 26 } 27 } 28 } 29 }
去除了干扰的代码,添加了注释,继续想下追踪
typeHandler指向StringTypeHandler类,这里面没有seParameter()方法,直接去父级BaseTypeHandler类中找吧。
setParameter()源码
去除多余干扰的代码
1 @Override 2 public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException { 3 //参数值为空 4 if (parameter == null) { 5 //jdbcType为空,这里不可能为空,最起码是默认枚举other 6 if (jdbcType == null) { 7 throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters."); 8 } 9 try { 10 /** 11 i是参数位置,第一个参数这里就是1 12 jdbcType.TYPE_CODE是枚举的编码值,这里空值是1111· 13 **/ 14 ps.setNull(i, jdbcType.TYPE_CODE); 15 } catch (SQLException e) { 16 //这里的异常内容是不是很熟悉,就是我们在控制台看到的内容。看来异常就是上面setNull方法抛出的了 17 throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " 18 + "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " 19 + "Cause: " + e, e); 20 } 21 } //如果不是空值,就直接走这里了 else{setNonNullParameter(ps, i, parameter, jdbcType);} 22 }
继续查看
setNull()方法源码
继续去T4CPreparedStatement中查看setNull()源码
继续追踪setNullCritical()源码
继续追踪到getInternalType()源码
1 int getInternalType(int var1) throws SQLException { 2 boolean var2 = false; 3 short var4; 4 switch(var1) { 5 case -104: 6 var4 = 183; 7 break; 8 case -103: 9 var4 = 182; 10 break; 11 case -102: 12 var4 = 231; 13 break; 14 case -101: 15 var4 = 181; 16 break; 17 case -100: 18 case 93: 19 var4 = 180; 20 break; 21 case -16: 22 case -1: 23 var4 = 8; 24 break; 25 case -15: 26 case -9: 27 case 12: 28 var4 = 1; 29 break; 30 case -14: 31 var4 = 998; 32 break; 33 case -13: 34 var4 = 114; 35 break; 36 case -10: 37 var4 = 102; 38 break; 39 case -8: 40 var4 = 104; 41 break; 42 case -7: 43 case -6: 44 case -5: 45 case 2: 46 case 3: 47 case 4: 48 case 5: 49 case 6: 50 case 7: 51 case 8: 52 var4 = 6; 53 break; 54 case -4: 55 var4 = 24; 56 break; 57 case -3: 58 case -2: 59 var4 = 23; 60 break; 61 case 0: 62 var4 = 995; 63 break; 64 case 1: 65 var4 = 96; 66 break; 67 case 70: 68 var4 = 1; 69 break; 70 case 91: 71 case 92: 72 var4 = 12; 73 break; 74 case 100: 75 var4 = 100; 76 break; 77 case 101: 78 var4 = 101; 79 break; 80 case 999: 81 var4 = 999; 82 break; 83 case 2002: 84 case 2003: 85 case 2007: 86 case 2008: 87 case 2009: 88 var4 = 109; 89 break; 90 case 2004: 91 var4 = 113; 92 break; 93 case 2005: 94 case 2011: 95 var4 = 112; 96 break; 97 case 2006: 98 var4 = 111; 99 break; 100 default: 101 SQLException var3 = DatabaseError.createSqlException(this.getConnectionDuringExceptionHandling(), 4, Integer.toString(var1)); 102 var3.fillInStackTrace(); 103 throw var3; 104 } 105 106 return var4; 107 }
因为case中没有1111匹配项,所以只能进入default中了。
default中定义了一个异常类,并在最后义无反顾的throw掉了。一个空值的赋值处理总算告一段落了。
这个地方不是太明白什么意思,这些case 后面的数值都代表什么意思,我看只有oracle驱动开发的人才能明白了。
这个地方的设计好奇怪啊;
原文地址:https://www.cnblogs.com/guoyansi19900907/p/12696023.html