猿蜕变13——一文彻底搞懂mybatis规范和标签

看过之前的蜕变系列文章,相信你对mybatis有了初步的认识。很多朋友编写mybatis比较随意,甚至工具生成的痕迹都经常见到,这个不是很好,sql是比较关键和核心的地方,不能骚整噢。我们今天进一步来了解下mybatis的一些规范写法和常用标签。

每月底工厂君会根据后台记录筛选转发文章到朋友圈的前三位的朋友,给与奖励,第一名100元,第二名50元,第三名30元的现金奖励。

猿蜕变同样是一个原创系列文章,帮助你从一个普通的小白,开始掌握一些行业内通用的框架技术知识以及锻炼你对系统设计能力的提升,完成属于你的蜕变,更多精彩内容,敬请大家关注公主号猿人工厂,点击猿人养成获取

现在又要告诉大家一个不幸的消息:数据库的命名是有规范的,基本上不太会出现数据库字段名和类中的属性名一一对应的情况。不要问我为什么,因为大家都这么搞,大家是得罪不起的,如果你把数据库字段名和类中的属性名一致了,准备承受dba来自地狱的怒吼吧。我们之前为了解决这个问题,在sql语句中使用了as 的别名语法,这样写其实是很费力的。Mybatis提供了resultMap来解决这个问题,我们回到正途使用resultMap。

resultType实际上是将数据库表字段与JavaBean中的属性建立映射关系,在编写SQL的时候使用resultMap,mybatis会依据resultMap中的映射关系将数据库返回的记录映射到对应的字段上。

修改TravelRouteMapper.xml增加一个resultMap

 <resultMap type="TravelRoute" id="TravelRouteType" >
      <result column = "travel_route_id"property="travelRouteId"/>      <result column = "travel_route_name"property="travelRouteName"/>      <result column = "travel_route_price"property="travelRoutePrice"/>      <result column = "travel_route_introduce"property="travelRouteIntroduce"/>      <result column = "travel_route_flag"property="travelRouteFlag"/>      <result column = "travel_route_date"property="travelRouteDate"/>      <result column = "isThemeTour"property="isThemeTour"/>      <result column = "travel_route_count"property="travelRouteCount"/>      <result column = "travel_route_cid"property="travelRouteCid"/>      <result column = "travel_route_image"property="travelRouteImage"/>      <result column ="travel_route_seller_id"property="travelRouteSellerId"/></resultMap>

我们将之前使用as 语法的queryTravelByPage和queryTravelById的as xxx去掉,将resultType改为TravelRoute。留下一个queryTravelByName当作复习语法的例子就好,就不赶尽杀绝了。

  <select id="queryTravelByPage"resultMap="TravelRouteType" parameterType="java.util.Map">        select       travel_route_id ,       travel_route_name ,       travel_route_price ,       travel_route_introduce ,       travel_route_flag ,       travel_route_date ,       isThemeTour ,       travel_route_count ,       travel_route_cid ,       travel_route_image ,       travel_route_seller_id          from   travel_route order by travel_route_id desc limit #{startRow},#{endRow}         </select>        <select id="queryTravelById"resultMap="TravelRouteType" parameterType="Long">        select       travel_route_id ,       travel_route_name ,       travel_route_price ,       travel_route_introduce ,       travel_route_flag ,       travel_route_date ,       isThemeTour ,       travel_route_count,       travel_route_cid ,       travel_route_image ,       travel_route_seller_id           from   travel_route where travel_route_id =#{travelRouteId}       </select>      

运行测试程序,保证代码修改正确!

之前编写的例子中,我们都自己实现了dao接口,其实mybatis提供了动态代理的方式,无需由开发人员编写实现类。使用mapper的动态代理,可以简化冗余代码的编写,也是实际开发中推荐的写法。接下来,我们就一起来看下怎样使用动态代理。

1.在TravelRouteMapper.xml中的mapper标签中使用namespace属性:

  •  
<mapper namespace="com.pz.route.dao.TravelRouteDao">

这样编写mybatis mapper文件与具体的接口com.pz.route.dao.TravelRouteDao建立映射关系。

2.编写的Dao必须遵守一个命名规范——方法名和mapper文件的id值保持一一对应的关系。

3.使用sqlSession创建dao的代理对象执行数据库操作:

@Test       public void testQueryTravelByIdProxy(){              SqlSession sqlSession=null;              try{               sqlSession=MyBatisUtil.getSqlSession();                           TravelRouteDaotravelRouteDao=sqlSession.getMapper(TravelRouteDao.class);                           TravelRoute travelRoute=travelRouteDao.queryTravelById(515L);              sqlSession.commit();              System.out.println(travelRoute);              }catch(Exception e){                     e.printStackTrace();              }finally{                     if(null!=sqlSession){                            sqlSession.close();                     }              }                    }

通过SqlSession对象的getMapper方法,将要获取的dao接口的class属性传入,会返回MyBatis会使用动态代理的方式创建对象,该对象是有jdk的动态代理自动生成的。由于我们的Sqlsession默认的是手动提交的事务方式,所以我们还是需要在程序中commit。不过,将dao的实现类去除掉之后,代码更加简洁。

我们之前编写的updateById方法其实存在一个问题——如果传入的数据是null或者是空字符串,对应的字段也会被设置为null或者是空字符串,其实这样提供方法比较危险,在实际开发的过程中,往往希望只对传入的数据做更新。还有一个场景,我们上网的时候发现页面上有多个组合查询条件,输入某一个查询条件就匹配一项查询结果,输入多个查询条件,就要求查询结果匹配多个查询条件。针对这样的场景,mybatis提供了动态sql技术来减少开发人员的开发工作。

if标签
      

我们使用if标签来解决数据更新的控制问题——对于一些字段,必须有值传入才做更新: 

<update id="updateById">      update         travel_route       set       <if test="travelRouteName!= null and travelRouteName != ''">        travel_route_name=#{travelRouteName},       </if>        <if test="travelRoutePrice!= null and travelRoutePrice >0 ">        travel_route_price=#{travelRoutePrice},       </if>        <if test="travelRouteIntroduce!= null and travelRouteIntroduce != ''">       travel_route_introduce=#{travelRouteIntroduce},       </if>        <if test="travelRouteFlag!= null ">        travel_route_flag=#{travelRouteFlag},       </if>       <if test="travelRouteFlag!= null ">        travel_route_date=#{travelRouteDate},       </if>        <if test="travelRouteFlag!= null ">        isThemeTour=#{isThemeTour},       </if>        <if test="travelRouteCount!= null and travelRouteCount > 0 ">        travel_route_count=#{travelRouteCount},       </if>        <if test="travelRouteCid!= null and travelRouteCid > 0 ">        travel_route_cid=#{travelRouteCid},       </if>        <if test="travelRouteImage!= null and travelRouteImage != ''">        travel_route_image=#{travelRouteImage},       </if>        <if test="travelRouteSellerId!= null and travelRouteSellerId >0 ">       travel_route_seller_id=#{travelRouteSellerId}       </if>      where travel_route_id =#{travelRouteId}      </update>

运行测试用例,确保代码没有问题!

where标签
 

     我们来实现一个新的功能,实现复合查询:如果输入了线路名称,我们就按线路名称模糊查询,如果输入了价格我们就查询大于输入价格的线路。我们可以通过where标签来实现它。

在mapper中编写sql:

<select id="queryTravelByQuery"resultMap="TravelRouteType" parameterType="TravelRoute">        select       travel_route_id ,       travel_route_name ,       travel_route_price ,       travel_route_introduce ,       travel_route_flag ,       travel_route_date ,       isThemeTour ,       travel_route_count,       travel_route_cid ,       travel_route_image ,       travel_route_seller_id           from travel_route       <where>          <if test="travelRouteName!= null and travelRouteName != ''">           and travel_route_name like  '%' #{travelRouteName} '%'         </if>         <if test="travelRoutePrice!= null and travelRoutePrice>0 ">           andtravel_route_price>#{travelRoutePrice}         </if>        </where>       </select>
编写方法接口:/**     * 模糊查询TravelRoute列表     * @param travelRoute     * @return     */List<TravelRoute>queryTravelByQuery(TravelRoute travelRoute);

由于项目为了展示之前的例子,解决编译问题 实现类实现一个空方法就好,使用动态代理的方式是无需编写实现类的:

@Override       public List<TravelRoute>queryTravelByQuery(TravelRoute travelRoute) {              // TODO Auto-generated method stub              returnnull;       }编写测试方法:@Test       publicvoid testQueryTravelRouteByQuerydProxy(){              SqlSession sqlSession=null;              try{               sqlSession=MyBatisUtil.getSqlSession();              TravelRouteDaotravelRouteDao=(TravelRouteDao) sqlSession.getMapper(TravelRouteDao.class);              TravelRoute travelRoute = new TravelRoute();              travelRoute.setTravelRouteName("春节");              travelRoute.setTravelRoutePrice(100d);              travelRoute.setTravelRouteDate("2019-10-26");              List<TravelRoute> list=travelRouteDao.queryTravelByQuery(travelRoute);              System.out.println(list);              }catch(Exception e){                     e.printStackTrace();              }finally{                     if(null!=sqlSession){                            sqlSession.close();                     }              }                    }

运行测试用例,看看效果!

choose标签


           我们再提一个小要求,如果用户没有传入travelRouteName也没有传入travelRoutePrice,程序就不返回数据。当然我们可以在程序中做出判断,不过mybatis提供了choose标签也可以实现上述要求。在mapper中编写sql:

  <select id="queryTravelByChooseQuery"resultMap="TravelRouteType" parameterType="TravelRoute">        select       travel_route_id ,       travel_route_name ,       travel_route_price ,       travel_route_introduce ,       travel_route_flag ,       travel_route_date ,       isThemeTour ,       travel_route_count,       travel_route_cid ,       travel_route_image ,       travel_route_seller_id           from travel_route       <where>         <choose>          <when test="travelRouteName!= null and travelRouteName != ''">           and travel_route_name like  '%' #{travelRouteName} '%'         </when>         <when test="travelRoutePrice!= null and travelRoutePrice >0 ">           andtravel_route_price>#{travelRoutePrice}         </when>         <otherwise>              1 >2          </otherwise>        </choose>                </where>       </select>

   

编写dao接口和实现类

/**     * 模糊查询TravelRoute列表     * @param travelRoute     * @return     */   List<TravelRoute> queryTravelByChooseQuery(TravelRoutetravelRoute);编写测试方法@Test       publicvoid testQueryTravelRouteByChooseQuerydProxy(){              SqlSession sqlSession=null;              try{               sqlSession=MyBatisUtil.getSqlSession();              TravelRouteDao travelRouteDao=(TravelRouteDao)sqlSession.getMapper(TravelRouteDao.class);              TravelRoute travelRoute = new TravelRoute();              List<TravelRoute> list=travelRouteDao.queryTravelByChooseQuery(travelRoute);              System.out.println(list);              }catch(Exception e){                     e.printStackTrace();              }finally{                     if(null!=sqlSession){                            sqlSession.close();                     }              }                    }

          运行程序,我们发现返回了一个空的list.

  choose标签有多个when子标签,但是只能有一个otherwise子标签,有点类似java中的switch语句,我们在子标签中使用了1>2这种错误条件,在sql层面就屏蔽了数据返回的可能性。

foreach标签遍历数组

      我们再来完成一个小功能,传入一组id号,批量放回线路数据。我们需要使用到sql语句中的in语句,当然,也可以用拼接字符串的方式实现它,但是mybatis提供了foreach标签,实现了循环拼接的功能。

     foreach标签的属性

§  collection 表示要遍历的集合类型,可以传入List和数组。

§  open、close、separator 用于 SQL 拼接,open表示由什么字符开始,colse表示由什么字符结束,separator表示拼接字符串的分隔符。

我们来编写一个例子使用下foreach标签,在mapper文件中编写下面内容:

<select id="queryTravelByForEach"resultMap="TravelRouteType" >        select       travel_route_id ,       travel_route_name ,       travel_route_price ,       travel_route_introduce ,       travel_route_flag ,       travel_route_date ,       isThemeTour ,       travel_route_count,       travel_route_cid ,       travel_route_image ,       travel_route_seller_id           from travel_route       <where>         <choose>       <when test="list != nulland list.size>0">        travel_route_id IN        <foreach collection="list"open="(" close=")"item="id" separator=",">            #{id}        </foreach>        </when>          <otherwise>              1 >2          </otherwise>        </choose>       </where>           </select>   

编写dao接口方法和接口实现:

 /**     * 根据travelRouteIdList返回列表     * @param travelRouteIdList     * @return     */   List<TravelRoute> queryTravelByForEach(List<Long>travelRouteIdList);编写测试方法:@Test       publicvoid testQueryTravelRouteByForEachProxy(){              List<Long> idList= Arrays.asList(1L,2L,3L);              SqlSession sqlSession=null;              try{               sqlSession=MyBatisUtil.getSqlSession();              TravelRouteDaotravelRouteDao=(TravelRouteDao) sqlSession.getMapper(TravelRouteDao.class);              TravelRoute travelRoute = new TravelRoute();              List<TravelRoute> list=travelRouteDao.queryTravelByForEach(idList);              System.out.println(list);              }catch(Exception e){                     e.printStackTrace();              }finally{                     if(null!=sqlSession){                            sqlSession.close();                     }              }                    } 

  sql标签


    我们发现编写select语句时需要每次都写很多字段名,每写一条语句就需要写一次,这样很累,当然你要是不怕dba地狱的怒吼和你的Leader的天堂的神罚的话,可以写select * 来解决。Mybatis提供sql标签来解决sql的复用问题,使用sql标签可以把公共的sql语句片段提取出来,在需要使用的时候写上include标签就可以引入公用的sql语句片段了。我们将公共的查询字段使用sql标签提取出来:

  <sql id = "commonselect">        travel_route_id ,       travel_route_name ,       travel_route_price ,       travel_route_introduce ,       travel_route_flag ,       travel_route_date ,       isThemeTour ,       travel_route_count,       travel_route_cid ,       travel_route_image ,       travel_route_seller_id       </sql>

为了验证一下,我们修改下之前的queryTravelByForEach语句

<select id="queryTravelByForEach"resultMap="TravelRouteType" >        select        <include refid="commonselect"/>          from travel_route       <where>         <choose>       <when test="list != nulland list.size>0">        travel_route_id IN        <foreach collection="list"open="(" close=")"item="id" separator=",">            #{id}        </foreach>        </when>          <otherwise>              1 >2          </otherwise>        </choose>       </where>       </select>

运行下测试用例,看看效果

注意噢,由于mybatis使用的时xml,在xml中有些符号,是不能直接使用的,遇到这种情况,我们可以使用实体符号代替。下面就是一些数学符号和实体符号之间的关系。

原符号      实体符号

  <         &lt;

  <=        &lt;=

  >         &gt;

  >=        &gt;=

  &         &amp;

  "         &quot;

  '         &apos;

     这里要强调一下<,>=,<=,&,一定不能直接在xml中出现。

我们修改下queryTravelByQuery的程序要求,要求返回大于等于输入价格的线路,示例:

<select id="queryTravelByQuery"resultMap="TravelRouteType" parameterType="TravelRoute">        select       travel_route_id ,       travel_route_name ,       travel_route_price ,       travel_route_introduce ,       travel_route_flag ,       travel_route_date ,       isThemeTour ,       travel_route_count,       travel_route_cid ,       travel_route_image ,       travel_route_seller_id           from travel_route       <where>          <if test="travelRouteName!= null and travelRouteName != ''">           and travel_route_name like  '%' #{travelRouteName} '%'         </if>         <if test="travelRoutePrice!= null and travelRoutePrice >0 ">           and travel_route_price&gt;=#{travelRoutePrice}         </if>        </where>       </select>

   

除了使用特殊符号之外,我们还可以将特殊符号放到<![CDATA[ ]]>里面,这里面的内容xml是不会转义的

<select id="queryTravelByQuery"resultMap="TravelRouteType" parameterType="TravelRoute">        select       travel_route_id ,       travel_route_name ,       travel_route_price ,       travel_route_introduce ,       travel_route_flag ,       travel_route_date ,       isThemeTour ,       travel_route_count,       travel_route_cid ,       travel_route_image ,       travel_route_seller_id           from travel_route       <where>          <if test="travelRouteName!= null and travelRouteName != ''">           and travel_route_name like  '%' #{travelRouteName} '%'         </if>         <if test="travelRoutePrice!= null and travelRoutePrice >0 ">           and <![CDATA[travel_route_price >= #{travelRoutePrice}]]>         </if>        </where>        </select>

我建了一个技术群,群里有很多高手,加小编微信,备注:学习。带你见识更多的高手,帮你快速成长。

猜你喜欢

转载自blog.csdn.net/hzldds2019/article/details/106562620