首页 > 学院 > 开发设计 > 正文

在使用Mybatis框架中遇到的bug

2019-11-06 08:39:57
字体:
来源:转载
供稿:网友

前段时间,看了看Mybatis官方文档,在自己跟着做了两个例子后,准备在实际项目中用一用。下面我从实招来,在项目中遇到的一些坑。

Mybatis框架的大体思路是,1.自定义SQL语句,2.将语句和对应的方法关联,3.使用之。套路如下:

1.SQL语句可以通过xml或者java代码代码生成对应的SQL语句或者通过诸如@Select注解来定义,都是ok的,官方推荐用xml,因为其表达能力更强,官方也做了更好的适配。 2.在语句与方法映射这一步,可以使用<mapper> 标签进行注册,然后通过sqlsession的一系列方法使用之,也可以自己定义一个接口,在接口中定义方法来实现映射。

关于第一部分: 这里写图片描述

前两种方式都可以写动态的SQL语句,第三种方式貌似只能写静态的SQL语句,这里动态的意思是指根据条件来添加对应的语句。 比如:

1.通过xml:

<?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="mappers.GoodsMapper"> <select id="selectGoods" resultType="com.zeng.domain.Goods" parameterType="map"> SELECT * FROM goods <where> <if test="platform != null"> platform = #{platform} </if> <if test="fundType != null"> AND fund_type = #{fundType} </if> </where> </select></mapper>

2.通过java代码:

package com.zeng;import org.apache.ibatis.jdbc.SQL;import java.util.Map;/** * Created by zeng on 17-3-1. */public class GoodsDaoSelectPRovider { static String platform; static String fundType; static Integer pageSize; static Integer pageNum; public static String findGoods(Map<String, Object> params) { platform = (String)params.get("platform"); fundType = (String)params.get("fundType"); pageSize = (Integer)params.get("pageSize"); pageNum = (Integer) params.get("pageNum"); //实现1// StringBuffer buffer = new StringBuffer();// buffer.append("SELECT * FROM goods WHERE 1=1 ");// if (!("all".equals(platform))) {// buffer.append("AND platform=#{platform} ");// }// if (!("all".equals(fundType))) {// buffer.append("AND fund_type=#{fundType} ");// }//// buffer.append("LIMIT #{pageNum},#{pageSize}");//// return buffer.toString(); //实现2 return new SQL(){{ SELECT("*"); FROM("goods"); if (!("all".equals(platform))) { WHERE("platform=#{platform}"); } if (!("all".equals(fundType))) { WHERE("fund_type=#{fundType}"); } } }.toString()+" LIMIT #{pageNum},#{pageSize}"; }}

上述代码实现1和实现2都能够实现,都需要注意在字符串拼接时空格的使用,推荐使用第2种实现方式。

3.通过注解:

@Mapperpublic interface CityMapper { @Select("select * from ciry where state=#{state}") City findByState(@Param("state")String state);}

这种方式的实现的缺点,上面已经说过,不能实现动态SQL语句。

关于第2部分:

存在两个方式,一种是在xml中配置<mapper>标签,另一种是直接定义mapper的interface,通过<@Mapper>注解来实现。

那么问题来了!

是不是第一部分的3种方式与第二部分的2种方式都能够一一组合,从而形成6种不同的写法呢?(这让我写到了回字是不是有4种写法呢?QAQ),经过我的试验,大概可能的组合如下: 这里写图片描述

组合1:github传送门

要点: 1.

sqlSession.selectList(id,parms,rowBounds)

2. 传入多参数需要用map传递,所以parameterType="map" 3. 在mybatis-config.xml中定义:

<mappers> <mapper resource="mappers/GoodsMapper.xml"/></mappers>

输出正常:

测试有值platform: baidu and fund_type: successplatform: baidu and fund_type: successplatform: baidu and fund_type: success测试平台platform: taobao and fund_type: half successplatform: taobao and fund_type: success测试typeplatform: baidu and fund_type: successplatform: taobao and fund_type: successplatform: baidu and fund_type: successplatform: baidu and fund_type: success测试无值测试无值后是否正常运行platform: baidu and fund_type: successplatform: baidu and fund_type: successplatform: baidu and fund_type: success

组合2:github传送门

注意下面两个属性有可能重复定义了<mapper> (如果在mybatis-config.xml中也注册了相同mapper,那么肯定是重复定义了哈)从而会出现如下异常: 属性:

mybatis.config-location=classpath:mybatis-config.xmlmybatis.mapper-locations=classpath:GoodsMapper.xml

异常:

Mapped Statements collection already contains value for com.zeng.GoodsMapper.selectGoods

通常,我们在mybatis-config.xml中还会定义其他一些东西,所以最好就是不要添加mybatis.mapper-locations属性,所有的mapper都交给mybatis-config.xml 定义。

而且,值得注意的是,即使在项目中有mybatis.config.xml文件,但是如果没在springBoot配置文件application.properties中加入:

mybatis.config-location=classpath:mybatis-config.xml

这种情况下,该mybatis的配置是不生效的

要点: 1.定义mapper interface:

public interface GoodsMapper { //必须加param注解,不然只能通过param1,param2等没有绑定value的方式实现引用 List<Goods> selectGoods(@Param(value="pageStart") int pageStart, @Param(value="pageSize") int pageSize, @Param(value = "platform") String platform,@Param(value = "fundType") String fundType);}

2.在mapper.xml中通过命令空间绑定对应的interface:

<?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"><!--绑定对应interface--><mapper namespace="com.zeng.GoodsMapper"> <select id="selectGoods" resultType="com.zeng.domain.Goods"> SELECT * FROM goods <where> <if test="platform != null"> platform = #{platform} </if> <if test="fundType != null"> AND fund_type = #{fundType} </if> </where> LIMIT #{pageStart},#{pageSize} </select></mapper>

完美运行。

组合3:github传送门

要点: 1.在GoodsDaoSelectProvider中使用java代码定义sql语句,动态性方面可以有很好的表示。代码前面已经给出过。

2.定义mapper与其绑定:

@Mapperpublic interface GoodsMapper { @SelectProvider(type = GoodsDaoSelectProvider.class, method = "findGoods") List<Goods> selectGoods(@Param(value = "pageNum")int pageNum, @Param(value = "pageSize")int pageSize, @Param(value = "platform")String platform, @Param(value = "fundType") String fundType);}

这样就ok了。

组合4:github传送门

要点: 1.所有的定义都在mapper interface中都可以完成:

@Mapperpublic interface GoodsMapper { //其动态性很受限制 @Select("select * from goods where platform=#{platform} and fund_type=#{fundType} limit #{pageStart},#{pageSize}") List<Goods> selectGoods(@Param(value="pageStart") int pageStart, @Param(value="pageSize") int pageSize, @Param(value = "platform") String platform,@Param(value = "fundType") String fundType);}

但是其动态性很受限制

这里,总结一下,上述4种组合的适用场合

1.对于比较简单的sql语句,可以使用注解的方式,但是动态性受限。 2.需要动态性的场合,java语句与xml都能够胜任,我认为java语句动态性会更强,因为xml实现动态性是基于标签的,而java语句实现是基于代码逻辑。 3.更通用的场合,推荐使用组合2,通俗易懂。

本来写到这里就应该完了,但是基于在实验时发现的一些坑,我决定看看 mybatis-spring-boot-starter中有哪些重要属性: 在mybatis-spring-boot-autoconfigure中查看Configuration一节。

另外,在这次调bug过程中,领会了一个重要的思路:在google+stackvoerflow无解的情况下,一个高质量的方式是去springBoot对应starter的github查看issue,里面兴许有问题的答案!比如: mybatis-spring-boot-starter的issue


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表