百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

MyBatis这样用,同事直呼哇塞,堪称最佳实践

bigegpt 2024-08-01 11:50 7 浏览

MyBatis是一款非常流行的ORM框架,相信很多小伙伴都在使用。我们经常会把它和MyBatis-Plus或者MBG一起使用,用多了之后对于其一些常规操作就不太熟悉了。最近总结了下MyBatis的实用用法和技巧,希望对大家有所帮助!

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.
 */
@Repository
public 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.
 */
@SpringBootTest
public 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.
 */
@Repository
public 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.
 */
@Repository
public 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.
 */
@Repository
public 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.
 */
@Repository
public 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.
 */
@Repository
public 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.
 */
@Repository
public 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.
 */
@Repository
public 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.
 */
@Data
public class UmsResourceExt extends UmsResource {

    private UmsResourceCategory category;
}
  • 例如我们需要编写一个根据资源ID获取资源及分类信息的方法;
/**
 * 自定义UmsResource表查询
 * Created by macro on 2022/10/20.
 */
@Repository
public 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.
 */
@Data
public class UmsResourceCategoryExt extends UmsResourceCategory {

    private List<UmsResource> resourceList;
}
  • 例如我们现在需要编写一个根据分类ID获取分类及对应资源的方法;
/**
 * 自定义UmsResourceCategory表查询
 * Created by macro on 2022/10/20.
 */
@Repository
public 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.
 */
@Service
public 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/mall-learning/tree/master/mall-tiny-mybatis

相关推荐

得物可观测平台架构升级:基于GreptimeDB的全新监控体系实践

一、摘要在前端可观测分析场景中,需要实时观测并处理多地、多环境的运行情况,以保障Web应用和移动端的可用性与性能。传统方案往往依赖代理Agent→消息队列→流计算引擎→OLAP存储...

warm-flow新春版:网关直连和流程图重构

本期主要解决了网关直连和流程图重构,可以自此之后可支持各种复杂的网关混合、多网关直连使用。-新增Ruoyi-Vue-Plus优秀开源集成案例更新日志[feat]导入、导出和保存等新增json格式支持...

扣子空间体验报告

在数字化时代,智能工具的应用正不断拓展到我们工作和生活的各个角落。从任务规划到项目执行,再到任务管理,作者深入探讨了这款工具在不同场景下的表现和潜力。通过具体的应用实例,文章展示了扣子空间如何帮助用户...

spider-flow:开源的可视化方式定义爬虫方案

spider-flow简介spider-flow是一个爬虫平台,以可视化推拽方式定义爬取流程,无需代码即可实现一个爬虫服务。spider-flow特性支持css选择器、正则提取支持JSON/XML格式...

solon-flow 你好世界!

solon-flow是一个基础级的流处理引擎(可用于业务规则、决策处理、计算编排、流程审批等......)。提供有“开放式”驱动定制支持,像jdbc有mysql或pgsql等驱动,可...

新一代开源爬虫平台:SpiderFlow

SpiderFlow:新一代爬虫平台,以图形化方式定义爬虫流程,不写代码即可完成爬虫。-精选真开源,释放新价值。概览Spider-Flow是一个开源的、面向所有用户的Web端爬虫构建平台,它使用Ja...

通过 SQL 训练机器学习模型的引擎

关注薪资待遇的同学应该知道,机器学习相关的岗位工资普遍偏高啊。同时随着各种通用机器学习框架的出现,机器学习的门槛也在逐渐降低,训练一个简单的机器学习模型变得不那么难。但是不得不承认对于一些数据相关的工...

鼠须管输入法rime for Mac

鼠须管输入法forMac是一款十分新颖的跨平台输入法软件,全名是中州韵输入法引擎,鼠须管输入法mac版不仅仅是一个输入法,而是一个输入法算法框架。Rime的基础架构十分精良,一套算法支持了拼音、...

Go语言 1.20 版本正式发布:新版详细介绍

Go1.20简介最新的Go版本1.20在Go1.19发布六个月后发布。它的大部分更改都在工具链、运行时和库的实现中。一如既往,该版本保持了Go1的兼容性承诺。我们期望几乎所...

iOS 10平台SpriteKit新特性之Tile Maps(上)

简介苹果公司在WWDC2016大会上向人们展示了一大批新的好东西。其中之一就是SpriteKitTileEditor。这款工具易于上手,而且看起来速度特别快。在本教程中,你将了解关于TileE...

程序员简历例句—范例Java、Python、C++模板

个人简介通用简介:有良好的代码风格,通过添加注释提高代码可读性,注重代码质量,研读过XXX,XXX等多个开源项目源码从而学习增强代码的健壮性与扩展性。具备良好的代码编程习惯及文档编写能力,参与多个高...

Telerik UI for iOS Q3 2015正式发布

近日,TelerikUIforiOS正式发布了Q32015。新版本新增对XCode7、Swift2.0和iOS9的支持,同时还新增了对数轴、不连续的日期时间轴等;改进TKDataPoin...

ios使用ijkplayer+nginx进行视频直播

上两节,我们讲到使用nginx和ngixn的rtmp模块搭建直播的服务器,接着我们讲解了在Android使用ijkplayer来作为我们的视频直播播放器,整个过程中,需要注意的就是ijlplayer编...

IOS技术分享|iOS快速生成开发文档(一)

前言对于开发人员而言,文档的作用不言而喻。文档不仅可以提高软件开发效率,还能便于以后的软件开发、使用和维护。本文主要讲述Objective-C快速生成开发文档工具appledoc。简介apple...

macOS下配置VS Code C++开发环境

本文介绍在苹果macOS操作系统下,配置VisualStudioCode的C/C++开发环境的过程,本环境使用Clang/LLVM编译器和调试器。一、前置条件本文默认前置条件是,您的开发设备已...