技术派整合MyBatis-Plus

Mybatis-Plus大家都熟悉了吧?是一个Mybatis的增强,提供了一些额外功能,比如条件构造器、分页插件、代码生成器等以便我们更专注于业务,而不是SQL语句的编写

官方教程:简介 | MyBatis-Plus

整合MyBatis-Plus

非常简单,第一步,在pom.xml中引入starter。

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

第二步,使用@MapperScan注解扫描mapper文件

@Configuration
@ComponentScan("com.github.paicoding.forum.service")
@MapperScan(basePackages = {
        "com.github.paicoding.forum.service.article.repository.mapper",
        "com.github.paicoding.forum.service.user.repository.mapper",
        "com.github.paicoding.forum.service.comment.repository.mapper",
        "com.github.paicoding.forum.service.config.repository.mapper",
        "com.github.paicoding.forum.service.statistics.repository.mapper",
        "com.github.paicoding.forum.service.notify.repository.mapper",})
public class ServiceAutoConfig {
}

ServiceAutoConfig 是单独的配置类,mapper接口按照业务进行了分类,mapper.xml放在resources目录下。

第三步,在application.yml文件中增加MyBatis-Plus的统一配置


# mybatis 相关统一配置
mybatis-plus:
  configuration:
    #开启下划线转驼峰
    map-underscore-to-camel-case: true

map-underscore-to-camel-case: true 的作用是将数据库表中的下划线命名方式映射为Java对象中的驼峰命名方式。例如,数据库表中的列名为user_name,对应Java对象的属性名为userName。

上面三部就完成了MyBatis-Plus和Spring Boot项目的整合,接下来,我么来介绍用法,包括新增、注解、查询、条件构造器、自定义SQL、分页查询、更新删除、AR模式、主键策略,以及通过service。

MyBatis-Plus的基本使用

Service CRUD

技术派中的通用的增删改查是通过MyBatis-Plus的service CRUD接口实现的。

比如说我们要保存一个文章的标签,可以通过这种方式。

@Autowired
private TagDao tagDao;

tagDao.save(tagDo);

1、tagDao是我们定义的数据访问对象(Data Access Object :DAO)它继承自MyBatis-Plus提供的ServiceImpl类。@Autowired注解将TagDao自动注入到当前类中。这是Spring提供的依赖注入(DI)功能,可以让我们在当前类中方便的使用TagDao

@Repository
public class TagDao extends ServiceImpl<TagMapper, TagDO> {

1.@Repository注解:这是Spring提供的注解,用于标识这个类是一个数据访问层(DAO)组件。Spring会自动扫描并将其实例化为一个Bean,方便在其他类中通过依赖注入(DI)使用

2. ServiceImpl<TagMapper, TagDO>: ServiceImpl是MyBatis-Plus提供的一个抽象类,提供了通用的CRUD方法。泛型参数<TagMapper, TagDO> 意味着TagDao类主要用来处理TagDO数据对象的数据库操作,并使用TagMapper接口定义的方法进行操作。

通过继承ServiceImpl类,TagDao就可以使用MyBatis-Plus提供的通用CRUD方法,如sava、getById、updateById等。这些方法已经实现了基本的数据库操作,通常无需自己编写SQL语句。

2、 参数tagDO是一个数据对象,表示数据库中的tag表。

@Data
@EqualsAndHashCode(callSuper = true)
@TableName("tag")
public class TagDO extends BaseDO {
    private static final long serialVersionUID = 3796460143933607644L;

    /**
     * 标签名称
     */
    private String tagName;

    /**
     * 标签类型:1-系统标签,2-自定义标签
     */
    private Integer tagType;

    /**
     * 状态:0-未发布,1-已发布
     */
    private Integer status;

    /**
     * 是否删除
     */
    private Integer deleted;
}

1.@Data注解是Lombok提供的,用于自动生成类的getter、setter、equals、hashCode和toString方法,简化了代码编写。

2. @EqualsAndHashCode(callSuper = true)注解也是lombok提供的注解,callSuper = true 表示要调用父类(BaseDO)的equals和hashCode方法。

BaseDO是我们自定义的DO基类,实现了 Serializable 接口,并且自定义了主键(@TableId(type = IdType.AUTO))表示自动增长,是MyBatis-Plus提供的注解,创建时间为 createTime 更新时间为 updateTime。

@Data
public class BaseDO implements Serializable {

    @TableId(type = IdType.AUTO)
    private Long id;

    private Date createTime;

    private Date updateTime;
}

3. @TableName("tag") 注解是MyBatis-Plus提供的注解,用于指定数据库表名。

4.另外定义了四个属性:tagName ( 标签名称) 、tagType( 标签类型)、status( 状态)、deleted ( 是否删除)。这些数据对映数据库表中的列。

来看技术派的演示实战

启动Redis、服务端、admin端,通过admin端新增一个标签。

在控制台就可以看到新增的标签了。

Mapper CRUD

MyBatis-Plus除了提供Service的CRUD,还提供了基于Mapper的CRUD。

技术派中的一些特殊的增删改查是通过MyBatis-Plus的Mapper CRUD 接口实现的。

比如说我们保存文章,可以通过下面这种方式。

@Repository
public class ArticleDao extends ServiceImpl<ArticleMapper, ArticleDO> {
    @Resource
    private ArticleDetailMapper articleDetailMapper;

    /**
     * 保存文章正文
     *
     * @param articleId
     * @param content
     * @return
     */
    public Long saveArticleContent(Long articleId, String content) {
        ArticleDetailDO detail = new ArticleDetailDO();
        detail.setArticleId(articleId);
        detail.setContent(content);
        detail.setVersion(1L);
        articleDetailMapper.insert(detail);
        return detail.getId();
    }
}

1.  ArticleDetailMapper 是我们在当前类中注入的一个Mapper接口

public interface ArticleDetailMapper extends BaseMapper<ArticleDetailDO> {
}

他继承自MyBatis-Plus的BaseMapper接口


/**
Mapper 继承这个接口之后,无需编写 mapper.xml文件,即可获得·CRUD功能
<p>这个 Mapper支持id泛型

*/
public interface BaseMapper<T> extends Mapper<T> {

    //插入一条记录 ,entity:实体对象
   int insert(T entity);

    //根据entity条件 ,删除记录  queryWrapper实体对象封装的操作类(可以为null,
    //里面的entity用于生成where语句)

    int delete(@Param("ew") Wrapper<T> queryWrapper);

    //根据wherEntity 条件,更新记录
    // entity 实体对象(set条件值 ,可以为null)
    //updateWrapper 实体对象操作类,可以为null,里面的entity用于生成where语句
    int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);
    
    //根据ID查询,id主键ID
     T selectById(Serializable id);




}

这样ArticleDetailMapper也就具备了基本的增删改查功能。

在浏览器中访问 http://localhost:8080/ 并测试

可以在控制台看到文章的插入信息。

删改后面会讲到。

常用注解

  • @TableName: 用于指定数据库表名,通常在实体类上使用,如@TableName("user")
  • @TableId: 用于指定表中的主键字段。通常在实体类的主键属性上使用。如:@TableId(value = "id" , type = IdType.AUTO),其中value表示主键字段名,type表示主键生成策略。
  • @TableFiled: 用于指定表中的非主键字段。可以用于实体类的属性上,以映射属性和数据库字段。如@TableFiled(value = "user_name" , exist = true),其中value表示数据库中的字段名,exist表示该字段是否存在,默认为true存在,false表示不存在。
  • @TableLogic: 用于指定逻辑删除字段。逻辑删除字段是指数据库中标记某个记录已删除,而不是真正删除的记录。如: TableLogic(value = "0" ,delval = "1"),其中value表示未删除状态的默认值,delval删除状态的值。
  • @Version: 用于指定乐观锁字段。乐观锁是一种并发控制机制策略,用于解决解决多个线程同时修改同一条记录的问题,如@Version private Integer version;
  • @EnumValue: 用于指定枚举类型的字段的映射,如: @EnumValue private Integer status;
  • @InterceptorIngnore: 用于忽略MyBatis-Plus拦截器的处理。如:@InterceptorIngnore(tenantLine = "true"),表示忽略多租户拦截器。

这几个是常见注解。

MyBatis -Plus 查询方法

普通查询

其中它的BaseMapper提供了多种查询方法,如技术派中根据ID查找文章是这样用的:

   // 查询文章记录
        ArticleDO article = baseMapper.selectById(articleId);

除此之外还有根据ID批量查询的selectBatchlds::

    List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);

用法也比较就简单

        List<Long> ids = list.stream().map(ReadCountDO::getDocumentId).collect(Collectors.toList());
        List<ArticleDO> result = baseMapper.selectBatchIds(ids);
baseMapper.selectBatchIds(Arrays.asList(1,2));

根据键值对查询的selectMap:

    List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);

用法如下(id为15)

Map<String , Object> map = new Hash<>();

map.put("id" , 15L);
List<ArticleDo> dtoList = baseMapper.selectByMap(map);

条件构造器

MyBatis-Plus 的Wrapper是一个条件构造器,用于简化复杂的SQL查询条件的构建。它提供了一系列易于使用的API,让你能够以来链式编程的方式编写查询条件,而不需要手动编写SQL语句。

加入我们来查询这样的一个结果,包含“j”且状态是已经发布的标签。我们可以这样来构建条件构造器:
 

QueryWrapper:  用于构建查询条件。它继承自AbstractWrapper,提供了各种查询条件的构造方法,如eq、ne、gt、ge、lt、le、like、isNull、orderBy等等。

通过上面的方法,我们将返回全部列,如果只想返回一部分,可以通过select 来设置查询字段。

但存在一个问题,数据库表发生变化,代码和数据库就会不匹配了,所以更好的使用Lambda的方式,技术派的条件构造器就用的这种方式。

比如查询标签。

    /**
     * 获取已上线 Tags 列表(分页)
     *
     * @return
     */
    public List<TagDTO> listOnlineTag(String key, PageParam pageParam) {
        LambdaQueryWrapper<TagDO> query = Wrappers.lambdaQuery();
        query.eq(TagDO::getStatus, PushStatusEnum.ONLINE.getCode())
                .eq(TagDO::getDeleted, YesOrNoEnum.NO.getCode())
                .and(!StringUtils.isEmpty(key), v -> v.like(TagDO::getTagName, key))
                .orderByDesc(TagDO::getId);
        if (pageParam != null) {
            query.last(PageParam.getLimitSql(pageParam));
        }
        List<TagDO> list = baseMapper.selectList(query);
        return ArticleConverter.toDtoList(list);
    }
  • 1.可以通过 Wrappers.lambdaQuery();静态方法创建一个Lambda条件构造器。
  • 2.eq(TagDO::getStatus, PushStatusEnum.ONLINE.getCode()):表示查询条件为status等于PushStatusEnum.ONLINE.getCode()的值(即查询线上的标签)
  • 3.eq(TagDO::getDeleted, YesOrNoEnum.NO.getCode()):表示查询条件为deleted等于esOrNoEnum.NO.getCode()的值(即查询未删除的记录)
  • 4.and(!StringUtils.isEmpty(key), v -> v.like(TagDO::getTagName, key)):表示如果key不为空,则添加一个查询条件,要求tag_name包含key。
  • 5.orderByDesc(TagDO::getId);:表示按照id字段降序排序。
  • 6.if (pageParam != null) { query.last(PageParam.getLimitSql(pageParam)); }:如果pageParam不为null,则添加分页参数。

这样的话,数据库字段和代码隔离,完全通过代码的方式去查询,再比如查询文章列表:

    /**
     * 文章列表(用于后台)
     *
     * @param pageParam
     * @return
     */
    public List<ArticleDO> listArticles(PageParam pageParam) {
        return lambdaQuery()
                .eq(ArticleDO::getDeleted, YesOrNoEnum.NO.getCode())
                .last(PageParam.getLimitSql(pageParam))
                .orderByDesc(ArticleDO::getId)
                .list();
    }

  • 1.lambdaQuery()是一个MyBatis-Plus的Iservice接口提供的一个默认方法,可以在Service中直接调用·返回一个Lambda条件构造器。
    default LambdaQueryChainWrapper<T> lambdaQuery() {
        return ChainWrappers.lambdaQueryChain(this.getBaseMapper());
    }
  • 2.eq(ArticleDO::getDeleted, YesOrNoEnum.NO.getCode()) :表示查询条件为delete等于YesOrNoEnum.No的值  ,即查询未删除的记录。
  • 3.  .last(PageParam.getLimitSql(pageParam)):在查询的最后添加一个分页语句,这里根据pageParam参数生成分页的SQL语句。
  • 4.  orderByDesc(ArticleDO::getId): 表示按照id字段降序排序。
  • 5.list():执行查询,并返回查询结果的列表。

MyBatis-Plus自定义SQL

MyBatis-Plus支持自定义SQL语句,我们可以在接口中编写自定义SQL方法,并使用注解加自定义的SQL语句。

技术派在位信登录的时候会执行这条SQL语句:

public interface UserMapper extends BaseMapper<UserDO> {
    /**
     * 根据三方唯一id进行查询
     *
     * @param accountId
     * @return
     */
    @Select("select * from user where third_account_id = #{account_id} limit 1")
    UserDO getByThirdAccountId(@Param("account_id") String accountId);
}

接口中定义了一个名为getByThirdAccountId的方法,它接收一个名为accountId的参数。该方法使用了@Select注解,这个注解用于编写自定义的SQL查询。select * from user where third_account_id = #{account_id} limit 1,他会根据传入的account_id参数查询user表中的记录。

同时,方法参数accountId使用了@Param注解,指定了参数在SQL语句中的名称为ccount_id,这样,在执行SQL语句时,MyBatis会将参数值替换到对应的位置上。

我们来测试一下。

结果:

除此之外,技术派中还使用了xml的方式,用来定义一些复杂的SQL。比如说,我们要统计网站的PV、UV,那么我们在resources目录下新建一个名为QueryCountMapper.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.github.paicoding.forum.service.statistics.repository.mapper.RequestCountMapper">

    <select id="getPvTotalCount" resultType="java.lang.Long">
        select sum(cnt) from request_count
    </select>

    <select id="getPvDayList" resultType="com.github.paicoding.forum.api.model.vo.statistics.dto.StatisticsDayDTO">
        SELECT sum(cnt) as count, date
        FROM request_count
        group by date order by date asc
        limit #{day};
    </select>

    <select id="getUvDayList" resultType="com.github.paicoding.forum.api.model.vo.statistics.dto.StatisticsDayDTO">
        SELECT count(*) as count, date
        FROM request_count
        group by date order by date asc
        limit #{day};
    </select>

</mapper>

1.在resources目录下的好处是,MyBatis-Plus默认帮我们配置了xml的位置,这样我们就不需要在application.yml中配置了。

该XML文件定义了一个名为RequestCountMapper的映射器,它包含了三个自定义查询:getPvTotalCount、getPvDayList、getUvDayList。他与

com.github.paicoding.forum.service.statistics.repository.mapper.RequestCountMapper相匹配。

/**
 * 请求计数mapper接口
 *
 * @author louzai
 * @date 2022-10-1
 */
public interface RequestCountMapper extends BaseMapper<RequestCountDO> {

    /**
     * 获取 PV 总数
     *
     * @return
     */
    Long getPvTotalCount();

    /**
     * 获取 PV 数据列表
     * @param day
     * @return
     */
    List<StatisticsDayDTO> getPvDayList(@Param("day") Integer day);

    /**
     * 获取 UV 数据列表
     *
     * @param day
     * @return
     */
    List<StatisticsDayDTO> getUvDayList(@Param("day") Integer day);
}
3.  getPvTotalCount();查询:返回类型为java.lang.Long,查询语句为select sum(cnt) from request_count。此查询计算request_count表中所有记录的cnt列值之和。

4.getPvDayList查询: 返回类型为 StatisticsDayDTO 。此查询根据传入的day参数获取按日期分组的请求数量统计信息,并按日期升序排序。

5. getUvDayList查询: 返回类型同样为StatisticsDayDTO。此查询根据传入的day参数获取按日期分组的唯一访客数量统计信息,并按照日期升序排列。

打开admin端,查看这三项数据

MyBatis-Plus更新和删除

更新

我们来先看个最简单的,直接调用Service的updateById方法,也就时根据ID更新,比如更新标签的内容:

    public void saveTag(TagReq tagReq) {
        TagDO tagDO = TagStructMapper.INSTANCE.toDO(tagReq);

        // 先写 MySQL
        if (NumUtil.nullOrZero(tagReq.getTagId())) {
            tagDao.save(tagDO);
        } else {
            tagDO.setId(tagReq.getTagId());
            tagDao.updateById(tagDO);
        }

Service的update其实是对Mapper得update做一个封装。

    default boolean save(T entity) {
        return SqlHelper.retBool(this.getBaseMapper().insert(entity));
    }

后台把 “ 技术派” 的标签修改为 “ 技术派Π”。

后台可以看到SQL语句日志。

也可以通过xml的形式,当批量修改消息状态时,技术派是这样更新的。


    <update id="updateNoticeRead">
        update notify_msg set `state` = 1 where `id` in
        <foreach collection="ids" open="(" close=")" separator="," item="id" index="index">
            #{id}
        </foreach>
    </update>

对用的mapper是这样写得:

    /**
     * 标记消息为已阅读
     *
     * @param ids
     */
    void updateNoticeRead(@Param("ids") List<Long> ids);

删除

技术派中的删除都是逻辑上的删除,不是物理删除,就时修改delete字段,而不是真的把记录从表里面删除,所以,最终调用的还是update方法,比如删除文章。

    @Override
    public void deleteArticle(Long articleId) {
        ArticleDO dto = articleDao.getById(articleId);
        if (dto != null && dto.getDeleted() != YesOrNoEnum.YES.getCode()) {
            // 查询该文章是否关联了教程,如果已经关联了教程,则不能删除
            long count = columnArticleDao.count(
                    Wrappers.<ColumnArticleDO>lambdaQuery().eq(ColumnArticleDO::getArticleId, articleId));

            if (count > 0) {
                throw ExceptionUtil.of(StatusEnum.ARTICLE_RELATION_TUTORIAL, articleId, "请先解除文章与教程的关联关系");
            }

            dto.setDeleted(YesOrNoEnum.YES.getCode());
            articleDao.updateById(dto);

            // 发布文章删除事件
            SpringUtil.publishEvent(new ArticleMsgEvent<>(this, ArticleEventEnum.DELETE, dto));
        } else {
            throw ExceptionUtil.of(StatusEnum.ARTICLE_NOT_EXISTS, articleId);
        }
    }

MyBatis-Plus主键策略

技术派中的主键目前采用的是自增策略,也就是说,数据库表的ID会设置位Auto Increment(自动递增)。

然后,实体类DO会继承BaseDO,比如分类  CategoryDO:

@Data
@EqualsAndHashCode(callSuper = true)
@TableName("category")
public class CategoryDO extends BaseDO {

    private static final long serialVersionUID = 1L;

    /**
     * 类目名称
     */
    private String categoryName;

    /**
     * 状态:0-未发布,1-已发布
     */
    private Integer status;

    /**
     * 排序
     */
    @TableField("`rank`")
    private Integer rank;

    private Integer deleted;
}

其中BaseDO为MyBatis-Plus提供的基类,内部的id字段已经添加了@TableId(type = IdType.AUTO) 注解 ,。

@Data
public class BaseDO implements Serializable {

    @TableId(type = IdType.AUTO)
    private Long id;

    private Date createTime;

    private Date updateTime;
}

在插入数据时,无需设置主键值,数据库会自动分配主键值。

除了IdType.AUTO,MyBatis-Plus 还提供了其他几种策略。比如说Idtype.NONE:无主键策略。表示不使用任何主键生成策略,主键值需要手动设置。

IdType.ID_WORKER: 使用雪花算法生成分布式唯一的ID。插入数据时,MyBtis-Plus会自动生成一个雪花ID作为主键值。

小结:

MyBatis-Plus得基本使用:

  • Spring Boot整合MyBatis-Plus:如何引入依赖,并配置数据源和配置类。
  • MyBatis-Plus的基本使用: 如何创建实体类和Mapper接口,并在Service层和Mapper层中使用MyBatis-Plus提供的通用CRUD方法。
  • MyBatis-Plus的查询方法: MyBatis-Plus各种查询方法,MyBatis-Plus条件构造器时中带你。(QueryWrapper和LambdaQueryWrapper)。
  • MyBatis-Plus自定义SQL: 如何在MyBatis-Plus中使用自定义SQL语句,包括在Mapper接口中使用注解定义SQL和在XML文件中编写SQL。
  • MyBatis-Plus更新和删除: MyBatis-Plus提供的更新和逻辑删除方法。
  • MyBatis-Plus主键策略: MyBatis-Plus如何生成主键,支持的主键生成策略,如何使用@TableId注解配置主键策略。

  • 30
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值