MyBatis是一款十分风行的ORM框架,置信很多小伙伴都在应用。咱们常常会把它和MyBatis-Plus或者MBG一起应用,用多了之后对于其一些惯例操作就不太熟悉了。最近总结了下MyBatis的实用用法和技巧,心愿对大家有所帮忙!

SpringBoot实战电商我的项目mall(50k+star)地址:https://github.com/macrozheng/mall

MyBatis简介

MyBatis是一款优良的开源长久层框架,反对自定义SQL查问、存储过程和高级映射,目前在Github上已有17k+Star。在MyBatis中,咱们能够在XML中编写SQL语句,而后绑定到Java办法中,通过参数和后果集的主动映射来实现简单的查问逻辑。MyBatis打消了简直所有JDBC操作和手动绑定参数操作,应用起来十分不便!

在SpringBoot中集成

上面咱们来聊聊MyBatis在SpringBoot中的应用,首先咱们须要集成它。
  • pom.xml中增加MyBatis提供的Spring Boot Starter;
<dependency>    <groupId>org.mybatis.spring.boot</groupId>    <artifactId>mybatis-spring-boot-starter</artifactId>    <version>2.2.2</version></dependency>
  • 而后在application.yml中配置好编写SQL实现的xml文件门路,这里咱们寄存在resources/dao目录下;
mybatis:  mapper-locations:    - classpath:dao/*.xml
  • 而后增加Java配置,通过@MapperScan配置好Dao接口门路,这样就能够开始应用了。
/** * MyBatis配置类 * Created by macro on 2019/4/8. */@Configuration@MapperScan("com.macro.mall.tiny.dao")public class MyBatisConfig {}

根本应用

上面咱们来聊聊MyBatis的根本应用办法,涵盖了根本的增删改查操作。

表构造阐明

这里将以mall我的项目中权限治理模块相干表为例进行介绍,具体表构造如下。

我的项目构造阐明

本文示例应用了mall-learning我的项目中的mall-tiny-mybatis模块代码,具体我的项目构造如下。

select

  • 首先是查问操作,这里咱们当前台用户表ums_admin为例,编写一个依据ID查问用户的办法,先创立实体类UmsAdmin
public class UmsAdmin implements Serializable {    private Long id;    private String username;    private String password;    @ApiModelProperty(value = "头像")    private String icon;    @ApiModelProperty(value = "邮箱")    private String email;    @ApiModelProperty(value = "昵称")    private String nickName;    @ApiModelProperty(value = "备注信息")    private String note;    @ApiModelProperty(value = "创立工夫")    private Date createTime;    @ApiModelProperty(value = "最初登录工夫")    private Date loginTime;    @ApiModelProperty(value = "帐号启用状态:0->禁用;1->启用")    private Integer status;}
  • 而后创立数据操作的接口UmsAdminDao,再增加对应的办法;
/** * 自定义UmsAdmin表查问 * Created by macro on 2022/10/20. */@Repositorypublic interface UmsAdminDao {    /**     * 依据ID查问用户     */    UmsAdmin selectByIdSimple(Long id);}
  • 再创立xml文件UmsAdminDao.xml,增加查询方法的SQL实现;
<select id="selectByIdSimple" resultType="com.macro.mall.tiny.model.UmsAdmin">    select * from ums_admin where id = #{id}</select>
  • 而后编写测试类,注入Dao,调用Dao办法来进行测试;
/** * MyBatis基本操作测试 * Created by macro on 2022/10/20. */@SpringBootTestpublic class MyBatisBaseTest {    private static final Logger LOGGER = LoggerFactory.getLogger(MyBatisBaseTest.class);    @Autowired    private UmsAdminDao umsAdminDao;    @Test    void testSelectByIdSimple(){        UmsAdmin umsAdmin = umsAdminDao.selectByIdSimple(1L);        LOGGER.info("testSelectByIdSimple result={}",umsAdmin);    }}
  • 此时你会发现,对于一些数据库表中以下划线宰割的返回字段无奈主动映射,能够通过对字段取别名的形式来进行映射;
<select id="selectById" resultType="com.macro.mall.tiny.model.UmsAdmin">    select username,           password,           icon,           email,           nick_name   as nickName,           note,           create_time as createTime,           login_time  as loginTime,           status    from ums_admin    where id = #{id}</select>
  • 如果你感觉这种形式比拟麻烦,也能够通过在application.yml中开启全局下划线主动转驼峰性能来解决,集体习惯应用第一种。
mybatis:  configuration:    # 下划线主动转驼峰    map-underscore-to-camel-case: true

insert

  • 接下来咱们来编写一个插入单个用户的办法;
/** * 自定义UmsAdmin表查问 * Created by macro on 2022/10/20. */@Repositorypublic interface UmsAdminDao {    /**     * 插入用户     */    int insert(UmsAdmin entity);}
  • 而后在xml中编写对应的SQL实现,这里须要留神的是如果想返回插入后的自增ID的话,须要应用selectKey标签进行配置。
<insert id="insert">    insert into ums_admin(username, password, icon, email, nick_name, note, create_time, login_time)    values (#{username}, #{password}, #{icon}, #{email}, #{nickName}, #{note}, #{createTime}, #{loginTime})    <selectKey keyColumn="id" resultType="long" keyProperty="id" order="AFTER">        SELECT LAST_INSERT_ID()    </selectKey></insert>

update

  • 接下来咱们来编写一个依据ID批改用户信息的办法;
/** * 自定义UmsAdmin表查问 * Created by macro on 2022/10/20. */@Repositorypublic interface UmsAdminDao {    /**     * 依据ID批改用户信息     */    int updateById(UmsAdmin entity);}
  • 而后在xml中编写对应的SQL实现。
<update id="updateById">    update ums_admin    set username = #{username},        password = #{password},        icon = #{icon},        email = #{email},        nick_name = #{nickName},        note = #{note},        create_time = #{createTime},        login_time = #{loginTime}    where id = #{id}</update>

delete

  • 接下来咱们来编写一个依据ID删除用户的办法;
/** * 自定义UmsAdmin表查问 * Created by macro on 2022/10/20. */@Repositorypublic interface UmsAdminDao {    /**     * 依据ID删除用户     */    int deleteById(Long id);}
  • 而后在xml中编写对应的SQL实现。
<delete id="deleteById">    delete from  ums_admin where id = #{id}</delete>

动静SQL

通过MyBatis的动静SQL性能,咱们能够灵便地在xml中实现各种简单的操作,动静SQL性能须要依赖MyBatis的各种标签,上面咱们就来学习下。

if

  • if标签能够实现判断逻辑,这里咱们以依据用户名和Email含糊查问用户为例,来聊聊它的应用;
/** * 自定义UmsAdmin表查问 * Created by macro on 2022/10/20. */@Repositorypublic interface UmsAdminDao {    /**     * 依据用户名和Email含糊查问用户     * 不输出查问所有     */    List<UmsAdmin> selectByUsernameAndEmailLike(@Param("username") String username, @Param("email") String email);}
  • xml中增加对应的SQL实现如下。
<select id="selectByUsernameAndEmailLike" resultType="com.macro.mall.tiny.model.UmsAdmin">    select username,           password,           icon,           email,           nick_name   as nickName,           note,           create_time as createTime,           login_time  as loginTime,           status    from ums_admin    where 1=1    <if test="username!=null and username!=''">        and username like concat('%',#{username},'%')    </if>    <if test="email!=null and email!=''">        and email like concat('%',#{email},'%')    </if></select>

choose

  • choose标签也能够实现判断逻辑,下面的例子中当咱们不输出用户名和Email时,会查问出全副用户,咱们如果想不查问出用户,能够应用它;
<select id="selectByUsernameAndEmailLike2" resultType="com.macro.mall.tiny.model.UmsAdmin">    select username,    password,    icon,    email,    nick_name as nickName,    note,    create_time as createTime,    login_time as loginTime,    status    from ums_admin    where 1=1    <choose>        <when test="username!=null and username!=''">            and username like concat('%',#{username},'%')        </when>        <when test="email!=null and email!=''">            and email like concat('%',#{email},'%')        </when>        <otherwise>            and 1=2        </otherwise>    </choose></select>

where

  • 下面的例子中咱们为了SQL拼接不出错,增加了where 1=1这样的语句,其实能够应用where标签来实现查问条件,当标签内没有内容时会主动去除where关键字,同时还会去除结尾多余的and关键字。
<select id="selectByUsernameAndEmailLike3" resultType="com.macro.mall.tiny.model.UmsAdmin">    select username,    password,    icon,    email,    nick_name as nickName,    note,    create_time as createTime,    login_time as loginTime,    status    from ums_admin    <where>        <if test="username!=null and username!=''">            and username like concat('%',#{username},'%')        </if>        <if test="email!=null and email!=''">            and email like concat('%',#{email},'%')        </if>    </where></select>

set

  • 当咱们拼接更新字段的语句时,也会面临同样的问题,此时能够应用set标签来解决,比方咱们当初想写一个依据ID选择性批改用户信息的办法;
/** * 自定义UmsAdmin表查问 * Created by macro on 2022/10/20. */@Repositorypublic interface UmsAdminDao {    /**     * 依据ID选择性批改用户信息     */    int updateByIdSelective(UmsAdmin entity);}
  • 办法对应的SQL实现如下,这里既防止了应用set关键字,也会将多余的逗号去除。
<update id="updateByIdSelective">    update ums_admin    <set>        <if test="username!=null and username!=''">            username = #{username},        </if>        <if test="password!=null and password!=''">            password = #{password},        </if>        <if test="icon!=null and icon!=''">            icon = #{icon},        </if>        <if test="email!=null and email!=''">            email = #{email},        </if>        <if test="nickName!=null and nickName!=''">            nick_name = #{nickName},        </if>        <if test="note!=null and note!=''">            note = #{note},        </if>        <if test="createTime!=null">            create_time = #{createTime},        </if>        <if test="loginTime!=null">            login_time = #{loginTime},        </if>    </set>    where id = #{id}</update>

foreach

  • 通过foreach咱们能够实现一些循环拼接SQL的逻辑,例如咱们当初须要编写一个批量插入用户的办法;
/** * 自定义UmsAdmin表查问 * Created by macro on 2022/10/20. */@Repositorypublic interface UmsAdminDao {    /**     * 批量插入用户     */    int insertBatch(@Param("entityList") List<UmsAdmin> adminList);}
  • 在xml中的对应SQL实现如下,在foreach标签中的内容会依据传入的汇合参数进行循环拼接;
<insert id="insertBatch">    insert into ums_admin(username, password, icon, email, nick_name, note, create_time, login_time) values    <foreach collection="entityList" separator="," item="item">        (#{item.username}, #{item.password}, #{item.icon}, #{item.email}, #{item.nickName}, #{item.note}, #{item.createTime}, #{item.loginTime})    </foreach></insert>
  • 再例如咱们当初须要编写一个依据用户ID批量查问的办法;
/** * 自定义UmsAdmin表查问 * Created by macro on 2022/10/20. */@Repositorypublic interface UmsAdminDao {    /**     * 依据用户ID批量查问     */    List<UmsAdmin> selectByIds(@Param("ids") List<Long> ids);}
  • 在xml中的对应SQL实现如下,咱们能够应用openclose属性指定拼接语句的前后缀。
<select id="selectByIds" resultType="com.macro.mall.tiny.model.UmsAdmin">    select username,           password,           icon,           email,           nick_name   as nickName,           note,           create_time as createTime,           login_time  as loginTime,           status    from ums_admin    where id in    <foreach collection="ids" item="item" open="(" close=")" separator=",">        #{item}    </foreach></select>

高级查问

介绍完MyBatis的基本操作后,咱们再来介绍下MyBatis的高级查问性能。

一对一映射

  • 在咱们平时进行SQL查问时,往往会有一对一的状况,比如说咱们这里有资源分类ums_resource_category和资源ums_resource两张表,资源和分类就是一对一的关系,如果你不想改变原实体类的话,能够编写一个扩大类继承UmsResource,并蕴含UmsResourceCategory属性;
/** * UmsResource扩大类 * Created by macro on 2022/10/20. */@Datapublic class UmsResourceExt extends UmsResource {    private UmsResourceCategory category;}
  • 例如咱们须要编写一个依据资源ID获取资源及分类信息的办法;
/** * 自定义UmsResource表查问 * Created by macro on 2022/10/20. */@Repositorypublic interface UmsResourceDao {    /**     * 依据资源ID获取资源及分类信息     */    UmsResourceExt selectResourceWithCategory(Long id);}
  • 在xml中的具体SQL实现如下,咱们能够通过给ums_resource_category表中字段取以category.xxx的别名来主动进行主动映射;
<select id="selectResourceWithCategory" resultType="com.macro.mall.tiny.domain.UmsResourceExt">    select ur.id,           ur.create_time  as createTime,           ur.name,           ur.url,           ur.description,           ur.category_id  as categoryId,           urc.id          as "category.id",           urc.name        as "category.name",           urc.sort        as "category.sort",           urc.create_time as "category.createTime"    from ums_resource ur             left join ums_resource_category urc on ur.category_id = urc.id    where ur.id = #{id}</select>
  • 当然除了这种形式以外,咱们还能够通过ResultMap+association标签来实现,不过在此之前咱们在编写xml文件的时候,个别习惯于先给以后文件写一个BaseResultMap,用于对以后表的字段和对象属性进行间接映射,例如在UmsResourceCategoryDao.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="com.macro.mall.tiny.dao.UmsResourceCategoryDao">    <resultMap id="BaseResultMap" type="com.macro.mall.tiny.model.UmsResourceCategory">        <id property="id" column="id"/>        <result property="createTime" column="create_time"/>        <result property="name" column="name"/>        <result property="sort" column="sort"/>    </resultMap></mapper>
  • UmsResourceDao.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="com.macro.mall.tiny.dao.UmsResourceDao">    <resultMap id="BaseResultMap" type="com.macro.mall.tiny.model.UmsResource">        <id property="id" column="id"/>        <result property="createTime" column="create_time"/>        <result property="name" column="name"/>        <result property="url" column="url"/>        <result property="description" column="description"/>        <result property="categoryId" column="category_id"/>    </resultMap></mapper>
  • 编写实现后,咱们的一对一ResultMap实现就很简略了,咱们能够应用association标签进行一对一治理,配置columnPrefix属性将匹配到的字段间接映射到关联对象中去;
<resultMap id="ResourceWithCategoryMap" type="com.macro.mall.tiny.domain.UmsResourceExt" extends="BaseResultMap">    <association property="category" resultMap="com.macro.mall.tiny.dao.UmsResourceCategoryDao.BaseResultMap" columnPrefix="category_"/></resultMap>
  • 而后再编写下Dao中办法对应SQL实现即可,这里间接应用下面的ResultMap,同时给ums_resource_category表中的字段指定了category_前缀以便于映射。
<select id="selectResourceWithCategory2" resultMap="ResourceWithCategoryMap">    select ur.id,           ur.create_time,           ur.name,           ur.url,           ur.description,           ur.category_id,           urc.id          as category_id,           urc.name        as category_name,           urc.sort        as category_sort,           urc.create_time as category_create_time    from ums_resource ur             left join ums_resource_category urc on ur.category_id = urc.id    where ur.id = #{id}</select>

一对多映射

  • 在编写SQL查问时,一对多的状况也比拟常见,例如这里的分类和资源就是一对多的状况;
/** * UmsResourceCategory扩大类 * Created by macro on 2022/10/20. */@Datapublic class UmsResourceCategoryExt extends UmsResourceCategory {    private List<UmsResource> resourceList;}
  • 例如咱们当初须要编写一个依据分类ID获取分类及对应资源的办法;
/** * 自定义UmsResourceCategory表查问 * Created by macro on 2022/10/20. */@Repositorypublic interface UmsResourceCategoryDao {    /**     * 依据分类ID获取分类及对应资源     */    UmsResourceCategoryExt selectCategoryWithResource(Long id);}
  • 在实现具体SQL前,咱们须要先在xml中配置一个ResultMap,通过collection标签建设一对多关系;
<resultMap id="selectCategoryWithResourceMap" type="com.macro.mall.tiny.domain.UmsResourceCategoryExt" extends="BaseResultMap">    <collection property="resourceList" columnPrefix="resource_" resultMap="com.macro.mall.tiny.dao.UmsResourceDao.BaseResultMap"/></resultMap>
  • 而后在xml中编写具体的SQL实现,应用该ResultMap。
<select id="selectCategoryWithResource" resultMap="selectCategoryWithResourceMap">    select urc.id,           urc.create_time,           urc.name,           urc.sort,           ur.id resource_id,           ur.create_time resource_create_time,           ur.name resource_name,           ur.url resource_url,           ur.description resource_description,           ur.category_id resource_category_id    from ums_resource_category urc    left join ums_resource ur on urc.id = ur.category_id    where urc.id = #{id}</select>

分页插件

  • 咱们平时实现查问逻辑时,往往还会遇到分页查问的需要,间接应用开源的PageHelper插件即可,首先在pom.xml中增加它的Starter;
<!--MyBatis分页插件--><dependency>    <groupId>com.github.pagehelper</groupId>    <artifactId>pagehelper-spring-boot-starter</artifactId>    <version>1.4.2</version></dependency>
  • 而后在查询方法之前应用它的startPage办法传入分页参数即可,分页后的失去的数据能够在PageInfo中获取到。
/** * UmsResource的Service接口实现类 * Created by macro on 2022/10/20. */@Servicepublic class UmsResourceServiceImpl implements UmsResourceService {    @Autowired    private UmsResourceDao umsResourceDao;    @Override    public PageInfo<UmsResource> page(Integer pageNum, Integer pageSize,Long categoryId) {        PageHelper.startPage(pageNum,pageSize);        List<UmsResource> resourceList = umsResourceDao.selectListByCategoryId(categoryId);        PageInfo<UmsResource> pageInfo = new PageInfo<>(resourceList);        return pageInfo;    }}

总结

本文次要介绍了MyBatis中一些比拟惯例的用法,涵盖了SpringBoot集成、根本查问、动静SQL和高级查问,倡议大家珍藏起来,在对MyBatis的用法有所忘记的时候拿进去看看。

我的项目源码地址

https://github.com/macrozheng...