探索ORM宇宙:MyBatis-Plus的力量

**技术派项目源码地址 : **

**Mybatis-Plus 官网 : **MyBatis-Plus 🚀 为简化开发而生 (baomidou.com)
image.png

整合Mybatis-Plus

引入依赖
<mybaits-plus.version>3.5.2</mybaits-plus.version>

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>${mybaits-plus.version}</version>
</dependency>
使用 @MapperScan 注解扫描 mapper 文件
  • **如果Mapper包统一管理 可以直接在启动类加 @MapperScan **
@SpringBootApplication
@MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}
  • 如果不是统一管理就单独写一个配置Config来扫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 {
}
修改配置文件application.yml
mybatis-plus:
  configuration:
    #开启下划线转驼峰
    map-underscore-to-camel-case: true

MyBatis-Plus 的基本使用

Service CRUD
@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 接口定义的方法进行操作。

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

image.png

@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 方法。

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

  4. BaseDO 是我们自定义的 DO 基类,实现了 Serializable 接口,并且定义了主键 id(@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;
}
Mapper CRUD
  • 技术派中一些特殊的增删改查是通过 MyBatis-Plus 的 Mapper CRUD 接口实现的。
public interface ArticleDetailMapper extends BaseMapper<ArticleDetailDO> {
}
@Repository
public class ArticleDao extends ServiceImpl<ArticleMapper, ArticleDO> {
    @Resource
    private ArticleDetailMapper articleDetailMapper;
    
    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();
    }
}
  • 它继承自 MyBatis-Plus 的 BaseMapper 接口。
public interface BaseMapper<T> extends Mapper<T> {

    /**
     * 插入一条记录
     *
     * @param entity 实体对象
     */
    int insert(T entity);
    
    /**
     * 根据 entity 条件,删除记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
     */
    int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    /**
     * 根据 whereEntity 条件,更新记录
     *
     * @param entity        实体对象 (set 条件值,可以为 null)
     * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
     */
    int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
    
    /**
     * 根据 ID 查询
     *
     * @param id 主键ID
     */
    T selectById(Serializable id);
}
常用注解

@TableName:用于指定数据库表名,通常在实体类(DO 或 Entity)上使用。

@TableName("user")

@TableId:用于指定表中的主键字段。通常在实体类的主键属性上使用。

@TableId(value = "id", type = IdType.AUTO)

其中 value 表示主键字段名,type 表示主键生成策略。

@TableField:用于指定表中的非主键字段。可以用于实体类的属性上,以映射属性和数据库字段。

@TableField(value = "user_name", exist = true)

其中 value 表示数据库中的字段名,exist 表示该字段是否存在(默认为 true)。

@TableLogic:用于指定逻辑删除字段。指在数据库中标记某个记录已删除,而不是真正地删除记录。

@TableLogic(value = "0", delval = "1")

其中 value 表示未删除状态的默认值,delval 表示删除状态的值。

@Version:指定乐观锁字段。乐观锁是一种并发控制策略,解决多个线程同时修改同一条记录的问题。

@Version 
private Integer version

@EnumValue:用于指定枚举类型字段的映射。

@EnumValue 
private Integer status

@InterceptorIgnore:用于忽略 Mybatis-Plus 拦截器的处理。

@InterceptorIgnore(tenantLine = "true") // 表示忽略多租户拦截器。

MyBatis-Plus 查询方法

普通查询
  • 先注入对应Mapper
@Autowired
private ArticleTagDao articleTagDao;
ArticleDO article = articleTagDao.selectById(articleId);
articleTagDao.selectBatchIds(Arrays.asList(1,2));
Map<String, Object> map = new HashMap<>();
map.put("id", 15L);
List<ArticleDO> dtoList = baseMapper.selectByMap(map);
条件构造器 wrapper 查询

MyBatis-Plus 提供了一套强大的条件构造器(Wrapper),用于构建复杂的数据库查询条件。Wrapper 类允许开发者以链式调用的方式构造查询条件,无需编写繁琐的 SQL 语句,从而提高开发效率并减少 SQL 注入的风险。

☁️ 条件构造器 | Mybatis-Plus

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

@Test
public void testWrapper() {
    QueryWrapper<TagDO> wrapper = new QueryWrapper<>();
    // 包含“j”且状态是已发布
    wrapper.like("tag_name", "j").eq("status", 1);
    BaseMapper<TagDO> baseMapper = tagDao.getBaseMapper();
    List<TagDO> tagList = baseMapper.selectList(wrapper);
    tagList.forEach(System.out::println);
}
  • 通过表的字段总感觉很不舒服,万一哪天数据库表发生变化怎么办呢?代码和数据库就不匹配了呀。
  • 更优雅的做法是采用 Lambda 的方式,技术派中的条件构造器就用的这种方式。

public List<ArticleDO> listArticles(PageParam pageParam) {
    return lambdaQuery()
            .eq(ArticleDO::getDeleted, YesOrNoEnum.NO.getCode())
            .last(PageParam.getLimitSql(pageParam))
            .orderByDesc(ArticleDO::getId)
            .list();
}

MyBatis-Plus 自定义 SQL

  • 在 Mapper 接口中编写自定义 SQL 方法,并使用注解添加自定义的 SQL 语句。
  • 方法参数 accountId 使用了 @Param 注解,指定了参数在 SQL 语句中的名称为 account_id。
  • 这样,在执行 SQL 语句时,MyBatis 会将参数值替换到对应的位置上。
public interface UserMapper extends BaseMapper<UserDO> {
    /**
     * 根据三方唯一id进行查询
     */
    @Select("select * from user where third_account_id = #{account_id} limit 1")
    UserDO getByThirdAccountId(@Param("account_id") String accountId);
}
  • 除此之外,技术派中还使用了 xml 的方式,用来定义一些复杂的 SQL。
  • 在 resources 目录下的好处是,MyBatis-Plus 默认帮我们配置了 xml 的位置。
  • 这样我们就不需要在 application.yml 中再配置了。
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);
}
<?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>
  • 该 XML 文件定义了一个 RequestCountMapper 的映射器, 与 RequestCountMapper 相匹配。
  • 它包含了三个自定义查询:getPvTotalCount、getPvDayList 和 getUvDayList。

MyBatis-Plus 更新和删除

更新操作
  • 普通更新 : 直接调用 Service 的 updateById 方法,也就是根据 ID 更新
public void saveTag(TagReq tagReq) {
    TagDO tagDO = ArticleConverter.toDO(tagReq);
    if (NumUtil.nullOrZero(tagReq.getTagId())) {
        tagDao.save(tagDO);
    } else {
        tagDO.setId(tagReq.getTagId());
        tagDao.updateById(tagDO);
    }
}
  • 也可以通过 xml 的形式,当批量修改消息的状态时,技术派是通过这种方式更新的
void updateNoticeRead(@Param("ids") List<Long> ids);
<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>
删除操作

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

public void deleteArticle(Long articleId, Long loginUserId) {
    ArticleDO dto = articleDao.getById(articleId);
    if (dto != null && !Objects.equals(dto.getUserId(), loginUserId)) {
        // 没有权限
        throw ExceptionUtil.of(StatusEnum.FORBID_ERROR_MIXED, "请确认文章是否属于您!");
    }

    if (dto != null && dto.getDeleted() != YesOrNoEnum.YES.getCode()) {
        dto.setDeleted(YesOrNoEnum.YES.getCode());
        articleDao.updateById(dto);

        // 发布文章删除事件
        SpringUtil.publishEvent(new ArticleMsgEvent<>(this, ArticleEventEnum.DELETE, articleId));
    }
}

MyBatis-Plus 主键策略

主键自增策略
@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;
}
无主键策略
  • 不使用任何主键生成策略,主键值需要手动设置。
public class User {
    @TableId(type = IdType.NONE)
    private Long id;
    // ...
}
UUID主键策略
  • 插入数据时,MyBatis-Plus 会自动生成一个 UUID 值作为主键值。
public class User {
    @TableId(type = IdType.UUID)
    private String id;
    // ...
}
雪花算法主键策略
  • 雪花算法生成分布式唯一 ID
public class User {
    @TableId(type = IdType.ID_WORKER)
    private Long id;
    // ...
}
  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值