Mybatis知识点汇总(不全)

Mybatis作为日常开发中最常见的持久层框架,相信大家已经很熟悉了。这里只列出我自己用到的、认为重要的知识点(不定期新增)。欢迎大家指正和补充。

 

1.Java数据类型和MySQL数据库数据类型的对应关系

常用的几个如下(左为Java数据类型,右为MySQL数据类型):

  1. java.lang.Integer — INGEGER

  2. java.lang.Long — BIGINT

  3. java.lang.BigDecimal — DECIMAL

  4. java.lang.String — VARCHAR

  5. java.uitl.Date — DATE

说明几点,对于java.lang.Boolean,在MySQL中可用CHAR类型的'Y(或y)'和'N(或n)'来表示,'1''0'也是可以的;如果存储的的是日期+时间,MySQL的DATETIME类型也能支持;超长文本和有预定格式的文本(比如博客的正文)建议使用TEXT型的数据类型来存储。

2.MyBatis之resultType返回值类型

2.1返回基本类型

这种情况里,常见的是返回字符串,举个例子

// mapper文件
String getSaltByAdminName(String adminName);

对应的xml文件

<!--xml文件-->
<select id="getSaltByAdminName" resultType="string">
        select salt from t_admin where userName=#{userName} and type=1
</select>

xml文件中的string是java.lang.String的别名。

2.2返回实体对象

// mapper文件
CommonUser getAuthorByBlogId(Long id);
<!--xml文件-->
<select id="getAuthorByBlogId" resultType="com.holic.blog.entity.CommonUser">
        SELECT a.id,a.nickName,a.avatar FROM t_admin a,t_blog b WHERE a.id=b.blogAdminId and b.id=#{id}
</select>

在resultType属性中指定实体对象的全限定路径。

2.3返回List

List<Long> getTagIdByBlogId(Long id);
<select id="getTagIdByBlogId" resultType="java.lang.Long">
   select tagId from t_link where blogId=#{id}
</select>

注意,resultType属性的值指向的是List集合内存储的数据的类型,而不是list。 以上示例都是查询,其实增删改也能返回值,只是不太常用。这里推荐一款IDEA插件--free-idea-mybatis,挺好用的,会在你的mapper文件和xml文件之间建立起关联,有问题容易排查,同时能帮你根据mapper文件的接口在xml中生成SQL。

3.MyBatis之动态标签

动态标签常用于有条件的CRUD中,来动态的组成SQL语句。

3.1Insert

先上栗子

<insert id="saveBlog" parameterType="com.holic.blog.entity.Blog" useGeneratedKeys="true" keyProperty="id">
        insert into t_blog
        <trim prefix="(" suffix=")" suffixOverrides=",">
          <if test="title != null and title !=''">
              title,
          </if>
          <if test="description != null and description != ''">
              description,
          </if>
        </trim>
        <trim prefix=" values (" suffix=")" suffixOverrides=",">
            <if test="title != null and title !=''">
                #{title},
            </if>
            <if test="description != null and description != ''">
                #{description},
            </if>
        </trim>
</insert>

insert的基本语法是insert into table_name(field1,field2,...) values(value1,value2,...),现在再看示例,是不是很明白了: MyBatis的trim标签在此用于去除sql语句中多余逗号,同时给sql语句拼接"("、")"以及"values(" 等;if标签则是用来过滤的,如果入参不满足条件则不拼接SQL。还有个题外话,useGeneratedKeys属性为true表明插入时主键自增,keyProperty表示将生成的主键值保存到入参的哪个字段里,这里还是保存到主键字段id。

3.2update

第二个栗子

 <update id="updateBlogById" parameterType="com.holic.blog.entity.Blog">
        update t_blog a
        <set>
            <if test="title != null and title != ''">
                a.title=#{title},
            </if>
            <if test="content != null and content != ''">
                a.content=#{content},
            </if>
            <if test="description != null and description != ''">
                a.description=#{description},
            </if>
        </set>
        where a.id=#{id}
</update>

update的语法有多种,这里使用的是update table_name as a set a.field1=value1 where a.field2=value2。set标签和trim标签的作用类似,去掉多余的逗号。if标签的作用则完全相同。

3.3select

还是个栗子

<select id="findAllBlogBySearch" resultType="com.holic.blog.entity.example.ShowBlogForAdmin" parameterType="com.holic.blog.entity.example.SearchBlogForAdmin">
      select a.title,a.recommend,a.published,a.updateDate,b.name typeName,a.id from t_blog a,t_type b
      <where>
          a.blogTypeId=b.id
          <if test="title != null and title != ''">
              and title = #{title}
          </if>
          <if test="typeId != null and typeId != ''">
              and blogTypeId = #{typeId}
          </if>
          <if test="recommend != null and recommend !=''">
              and recommend=#{recommend}
          </if>
      </where>
</select>

查询应该是最常见的了,这里where标签来动态的拼接查询条件,if标签作为判断。

MybatisGenerator可以根据数据库表结构生成mapper文件和xml文件,有兴趣的小伙伴可以试试。

4.当mapper接口的入参多于1时的处理办法

下面的查询语句会报错

CommonUser checkAdminByUserNameAndPassWord(String userName, String passWord);
<select id="checkAdminByUserNameAndPassWord" resultMap="admin">
        select * from t_admin where userName=#{userName} and passWord=#{passWord}
</select>

原因就在于Mybatis不知道接口方法的两个参数和SQL中占位符的对应关系。处理方法有两种: 一是修改mapper中的接口,添加@Param(注意包名,org.apache.ibatis.annotations.Param)注解。

CommonUser checkAdminByUserNameAndPassWord(@Param("userName") String userName, @Param("passWord") String passWord);

xml文件不变

<select id="checkAdminByUserNameAndPassWord" resultMap="admin">
        select * from t_admin where userName=#{userName} and passWord=#{passWord}
</select>

注解中指定的名字即为xml文件中的占位符名。

第二种办法是修改xml文件,指定占位符名在mapper接口参数中的序号(从0开始)。

<select id="checkAdminByUserNameAndPassWord" resultMap="admin">
        select * from t_admin where userName=#{0} and passWord=#{1}
</select>

mapper接口不变

CommonUser checkAdminByUserNameAndPassWord(String userName, String passWord);

接口中参数userName在第一位,所以xml中userName对应的是0,以此类推。 我使用了第一种方法,这样SQL的可读性高一点。

5.模糊查询

模糊查询时会涉及到老生常谈的SQL注入的问题。

like '%${param}%'

这样的写法能实现模糊查询,但有SQL注入的风险,所以使用下面的写法

like concat('%',#{param},'%')

使用MySQL的函数来拼接'%',#的使用避免了SQL注入,完美。

6.批量插入数据

如果同时插入的数据比较多,在java代码中循坏调用单条插入的语句效率较低,考虑使用Mybatis的批量插入来实现。 mapper接口

int saveLink(List<Link> linkList);

注意入参,是个List,集合存储的是一个对象。 SQL语句

<insert id="saveLink" parameterType="java.util.List" useGeneratedKeys="true">
      insert into t_link (blogId, tagId) values
                                                <foreach collection="list" separator="," item="item">
                                                    (#{item.blogId}, #{item.tagId})
                                                </foreach>
</insert>

insert语句中parameterType属性也是List。批量插入的语法就如示例中那样。现在解释一下foreach标签中的各个属性:

  1. collection属性,该属性是必须指定的,但是在不同情况 下,该属性的值是不一样的:(1)如果传入的是单参数且参数类型是一个List的时候,collection属性值为list;(2)如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array;(3)如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map
  2. separator表示在每次进行迭代之间以什么符号作为分隔符,一般是逗号
  3. item表示集合中每一个元素进行迭代时的别名,相当于入参List里的某个元素,item.blogId就可以拿到Link对象中的blogId属性的值。

7.Mybatis分页查询

这里推荐一个分页插件:pagehelper,简单易用,上手快。 全部用法可以去项目的git主页查看,这里只介绍我自己的使用经验。PageHelper类有一个静态方法startPage(),入参是页码和每页大小

  /**
     * 开始分页
     *
     * @param pageNum  页码
     * @param pageSize 每页显示数量
     */
    public static <E> Page<E> startPage(int pageNum, int pageSize) {
        return startPage(pageNum, pageSize, DEFAULT_COUNT);
    }

把这个方法在想要进行分页查询的方法上紧挨着调用就行,就像这样

@Override
public PageInfo<Type> listType(Integer pageNum, Integer pageSize) {
    // 获取第pageNum页,每一页pageSize条内容,默认查询总数count
    // 紧跟在startPage方法后的第一个MyBatis 查询方法会被进行分页
    PageHelper.startPage(pageNum, pageSize);
    List<Type> list = typeMapper.findAllType();
    // 用PageInfo对结果进行包装
    PageInfo page = new PageInfo(list);
    return page;
}

这是个基本的用法,不过也够用了。使用这个插件需要点配置,配置文件(命名为mybatis-config.xml,不唯一):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <plugins>
        <!-- com.github.pagehelper为PageHelper类所在包名 -->
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <!-- 设置为true时,offset会当成 pageNum 使用,limit 和 pageSize 含义相同 -->
            <!-- 和startPage中的pageNum效果一样-->
            <property name="offsetAsPageNum" value="true"/>
            <!-- 该参数默认为false -->
            <!-- 设置为true时,使用RowBounds分页会进行count查询;开启PageInfo类的使用 -->
            <property name="rowBoundsWithCount" value="true"/>
            <!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 -->
            <!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型)-->
            <property name="pageSizeZero" value="false"/>
            <!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
            <!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
            <!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
            <property name="reasonable" value="true"/>
        </plugin>
    </plugins>
</configuration>

在yml配置文件中引用它:

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

这里稍微注意一下配置文件的位置,根目录下是classpath后直接跟文件名,否则要带上路径。

8.关于Order By

OrderBy默认是升序。

SELECT a.validatecode FROM t_code a WHERE a.codeId = '18345970836' ORDER BY a.makedate,a.maketime;

上面的SQL和下面这个结果一样

SELECT a.validatecode FROM t_code a WHERE a.codeId = '18345970836' ORDER BY a.makedate ASC,a.maketime ASC;

可以看出,如果对多个字段都有排序的需要,那么每个字段后都要有排序标识(ASC或者DESC),不写则按升序处理。开发的时候要求查询到最新的验证码,一开始的SQL是这样的

SELECT a.validatecode FROM t_code a WHERE a.codeId = '18345970836' ORDER BY a.makedate,a.maketime DESC LIMIT 1;

很明显是错误的,因为日期是升序,所以测试的时候一直报错,后来才意识到必须对每个要排序的字段添加排序标识,改正后如下

SELECT a.validatecode FROM t_code a WHERE a.codeId = '18345970836' ORDER BY a.makedate DESC,a.maketime DESC LIMIT 1;

这样日期和时间都是降序,那么查询到的才是最新的验证码。

9.Left(Right) Join

下面这张图讲的挺清楚的

简单的描述一下,left join将关键字左侧表的数据根据where条件过滤后列出;再根据on条件匹配关键字右侧表的数据,有符合条件的列出,没有则为null。right join与left join相反。 需要注意table_a a left join table_b b on a.id=b.id and ...类似这样的SQL,是不会过滤记录的,只会决定显示关键字右侧表记录的条数,左侧表的数据一定会全部展示。想要过滤记录还是要靠where子句来完成。就像下边这样的

SELECT a.item,b.score FROM t_item a LEFT JOIN t_score b on a.id=b.id WHERE a.id=4;

where跟在on的后边。

10.批量更新

10.1多行记录更新为同一个值

 <update  id = "updatePushStatus" parameterType = "java.util.List">
  update
     SYS_TEXT_PUSH
  SET
    PUSH_STATUS = 1,
    LAST_UPDATE_DATE = NOW()
  WHERE
      PUSH_ID  IN
<foreach  collection='list'  item = 'item' open= '(' separator=',' close =')'>
  #{item}
</foreach>
</update>

10.2每行更新的值不同

	<update id="updateActWeight" parameterType="java.util.List">
		<foreach  collection='list' item = 'item' index = 'index' separator=";">
		UPDATE t_wl_bill_detail SET ACT_WEIGHT = #{item.actWeight}
		WHERE MATERIAL_ID = #{item.materialId} and BILL_ID = #{item.billId}
		</foreach>
	</update>

 如果报错,需要在连接mysql的url上加 &allowMultiQueries=true;

11.动态SQL对于数值的判断

当planSendGoodsAmount 的值为0时,下边标签的判断结果是false

<if test="planSendGoodsAmount != null and planSendGoodsAmount != ''"> PLAN_SEND_GOODS_AMOUNT = (IFNULL(PLAN_SEND_GOODS_AMOUNT, 0) + #{planSendGoodsAmount}), </if>

其中 planSendGoodsAmount 字段是BigDecimal类型的。原因在于planSendGoodsAmount !=''这个条件,值为0时会返回false。 因为Mybatis解析表达式使用的是OGNL,对于数值的判断如下:

If the object is a Number, its double-precision floating-point value is compared with zero; non-zero is treated as true, zero as false;

可见,对象是一个Number类型,值为0时将被解析为false。解决办法就是去掉第二个判断条件,因为数值类型的值没必要和''做比较。



 

未完待续。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值