先看一个简单的demo:
数据库有一个user表,status类型设置为tinyint(1), 具体的结构如下:
mysql> show create table user \G*************************** 1. row ***************************Table: userCreate Table: CREATE TABLE `user` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(10) DEFAULT NULL,`status` tinyint(1) DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin11 row in set (0.00 sec)mysql> select * from user \G*************************** 1. row ***************************id: 1name: astatus: 2
再看下mybatis的mapper.xml:
<!--这两个查询的区别在于返回类型,一个是返回User对象,一个是返回Map--><select id="selectById" resultType="com.mamcharge.techc.action_audit.domain.User">select * from userwhere id=#{id}</select><select id="selectByIdReturnMap" resultType="map">select * from userwhere id=#{id}</select>
做一个测试看一下输出结果:
@Testpublic void selectByIdTest(){User u = userMapper.selectById(1L);System.out.println(u);//User(id=1, name=a, status=2)}@Testpublic void selectByIdReturnMapTest(){Map<String, Object> map = userMapper.selectByIdReturnMap(1L);System.out.println(map);//{name=a, id=1, status=true}}
有木有感觉很意外,status字段在返回对象User的情况下能正确取到值,但是在返回Map的情况下,竟然却返回了true!
啥原因呢?
我们用最原始的jdbc执行下这个查询看下输出结果是啥:
Connection connection = DriverManager.getConnection(url, user, password);Statement stmt= connection.createStatement();ResultSet rs = stmt.executeQuery("select * from user where id = 1");while(rs.next()){System.out.println(rs.getInt("status") + "," +rs.getBoolean("status"));//2,true}
上面的代码可以看出来,status字段既可以用getInt读出来,也可以用getBoolean读出来,那么问题可能就出在mybatis在在映射ResultSet的时候!如果返回类型是User对象,mybatis是可以明确的知道status的类型是Integer,那么肯定就会调用getInt()了,但是,如果返回类型是Map,根本不知道status的类型,那就只能用数据库字段的默认的类型了,那么默认的类型是啥呢?
while(rs.next()){ResultSetMetaData metaData = rs.getMetaData();System.out.println(metaData.getColumnClassName(3));//java.lang.BooleanSystem.out.println(metaData.getColumnType(3));//-7System.out.println(metaData.getColumnTypeName(3));//TINYINT}
mysql默认的tinyint(1)的类型是Boolean,因此mybatis就会把status当成boolean来处理了,具体是不是这样的呢,我们来看下mybatis的处理:
看一下mybatis的ResultSetWrapper的源码:
public TypeHandler<?> getTypeHandler(Class<?> propertyType, String columnName) {TypeHandler<?> handler = null;Map<Class<?>, TypeHandler<?>> columnHandlers = typeHandlerMap.get(columnName);if (columnHandlers == null) {columnHandlers = new HashMap<Class<?>, TypeHandler<?>>();typeHandlerMap.put(columnName, columnHandlers);} else {handler = columnHandlers.get(propertyType);}if (handler == null) {//这个是bitJdbcType jdbcType = getJdbcType(columnName);//propertyType是Integer或者Object,得到的handler是IntegerTypeHandler或者UnknownTypeHandlerhandler = typeHandlerRegistry.getTypeHandler(propertyType, jdbcType);// Replicate logic of UnknownTypeHandler#resolveTypeHandler// See issue #59 comment 10// 如果handler是UnknownTypeHandlerif (handler == null || handler instanceof UnknownTypeHandler) {final int index = columnNames.indexOf(columnName);//这里会取metaData.getColumnClassName,也就是booleanfinal Class<?> javaType = resolveClass(classNames.get(index));if (javaType != null && jdbcType != null) {//javaType=boolean jdbcType=bit,得到的handler是BooleanTypeHandler,最终就会调用rs.getBooleanhandler = typeHandlerRegistry.getTypeHandler(javaType, jdbcType);} else if (javaType != null) {handler = typeHandlerRegistry.getTypeHandler(javaType);} else if (jdbcType != null) {handler = typeHandlerRegistry.getTypeHandler(jdbcType);}}if (handler == null || handler instanceof UnknownTypeHandler) {handler = new ObjectTypeHandler();}columnHandlers.put(propertyType, handler);}return handler;}
如果我们把数据库的字段类型改一下呢:
mysql> alter table user modify status tinyint(4);Query OK, 0 rows affected (0.02 sec)Records: 0 Duplicates: 0 Warnings: 0
此时得到的数据库类型是什么呢:
while(rs.next()){ResultSetMetaData metaData = rs.getMetaData();System.out.println(metaData.getColumnClassName(3));//java.lang.IntegerSystem.out.println(metaData.getColumnType(3));//-6System.out.println(metaData.getColumnTypeName(3));//TINYINT}
这个时候无论用User对象还是Map都可以正确返回数据了。
还有一种解决办法是添加url参数tinyInt1isBit=false,同样可以不把tinyint(1)当成是boolean,mysql默认是把它当成boolean的,参考:https://www.jianshu.com/p/6885cad1cb14。
结论
(1)mysql的tinyint(1)和tinyint(4)实际都是占用8个bit位,千万不要设置tinyint(1),除非仅仅用到了0和1。
(2)如果设置了tinyint(1),可以在url中添加tinyInt1isBit=false来获取到实际的值,而非true、false。




