之所以把写关于MyBatis开发过程中遇到的坑写下来并变成文章,第一个是对自己开发MyBatis的过程中遇到的问题的一个总结,同事也是希望其他看到本博文的网友可以少遇到一些坑。
1. 异常篇
-
无效绑定
提示无效的绑定的方式(映射错误)(比如没有找到那个findAll 的方法)
原因:出在了XML Mapper文件上,去对应的XML文件查看。
可能是:
1). namespace 错误
2). resultMap 错误(type错误、属性错误) -
查询结果集超过一个
此异常也是开发过程中常见的坑,比如public User findUserByUsername()
这样的查询,方法返回的是一个User对象,但实际数据有可能是可以查询出多条的。很多时候开发的时候由于数据不够真实或者测试的不够充分,问题往往不会暴露出来,等到生产环境才会暴露出来。对于此类问题避免的方法就是对应返回一个非集合类的时候,需要特别注意查询是不是会返回多条记录的问题(单字段查询的话可以看看此字段数据库是不是有唯一性索引),如果不确定那么可以加上limit 1比较保险。 -
字段映射不正确
### Error querying database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'name' in 'field list'
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'name' in 'field list'
; bad SQL grammar []
主要原因:
- Mybatis XML中的SQL语句查询的列,不在数据库中。
- 新增SQL语句中的列不在数据库中,或列对应的值,数据类型不一致。
解决办法: 根据项目实际情况,通常有以下三种解决办法: - 修改SQL语句中,将不存在的列从语句中去掉。
- 在数据库中,新增该不存在的列。
- 在新增时,不要使用中文符号的``表示字符串。也就是~符号对应的键。
- join时多个表表中含有相同的字段但是未使用别名
### Error querying database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'oid' in field list is ambiguous
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'oid' in field list is ambiguous
; SQL []; Column 'oid' in field list is ambiguous
2. 其他潜在的问题
- insert写成select
在通常的情况下,此写法是可以正常运行的。不过在有一些公司自定义的MyBatis时(像我们公司为了支持分库分表)开发了一套自定义的mybatis组件,这个组件如果select标签里面写insert时就会报错。
<select id="insert" resultMap="BaseResultMap">
insert into user (id, name, password) values
(#{id}, #{name}, #{password})
</select>
所以大家标签和语句还是需要对应的好,否则有可能会出现不可预知的错误。
- select中的resultMap属性不正确
一些Eclipse或idea有根据Mapper接口自动生成xml的功能,比如作者之前安装的一个插件就会把所有的mapper中的方法生成<select>的标签的的属性是resultType而不是resultMap,这样user_id
属性的值就不能得到正确的映射了。
<select id="selectAll" resultType="com.bfbm.mybatis.entity.User">
select user_id, name, password
from user
</select>
正确的方法是使用resultMap
或者select语句中有_
的字段都增加AS
<select id="selectAll" resultType="com.bfbm.mybatis.entity.User">
select user_id AS userId, name, password
from user
</select>
- #和$混用的问题
看官方文档如何描述的http://www.mybatis.org/mybatis-3/sqlmap-xml.html#select
可以看到官方的问题写的很清楚,使用#,MyBatis会使用PreparedStatement来进行SQL查询,可以防止SQL注入。而$更多的是用来访问配置文件中的属性的。所以大家在自己的SQL中还是应该经量使用#。
image.png -
在使用resultMap的时候,要把ID写在第一行,否则的话,就会报错。
- 时间戳的使用
很多表一般会给一个create_time和update_time两个字段。很多小伙伴一般在Java中去手动设置两个字段的值。其实更好的办法是使用数据自带的时间戳函数
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(255) DEFAULT NULL COMMENT '姓名',
`password` varchar(255) DEFAULT NULL COMMENT '密码',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- 可以不用if choose等语句就经量少用
有一些开发,很多代码都是从别人的Mapper文件copy过来的,很多情况下也思考为什么。比如<if> <choose>这些条件语句的使用,如果你的参数是必传的话,那么就不要写<if>,这样可以减少不必要的运算和使代码清晰。比如你们公司的DBA看见这样的语句,知道你的索引使用的对吗?
<select id="selectByXxx" parameterType="com.bfbm.mybatis.entity.Student">
select
<include refid="Base_Column_List" />
from student
<where>
<if test="mobile != null">
and mobile = #{mobile,jdbcType=VARCHAR},
</if>
<if test="name != null">
`and name` = #{name,jdbcType=VARCHAR},
</if>
<if test="userId != null">
and user_id = #{userId,jdbcType=INTEGER},
</if>
</where>
</select>
比如此例中的userId如果已经是添加了索引,而且是必填的,那么就不要写<if>啦,直接最后写user_id = #{userId}
就可以了。
更多Java开发相关技术文章请关注巴分巴秒官方简书号。
网友评论