mybatis的使用姿势

整体结构图

忽略数据库连接配置,简单的步骤如下:

  • 从domain出发(或者称为do),定义和数据库表对应的实体类。

  • 再到mapper层(或者称为dao)。定义mapper接口,通过xml方式或者注解方式书写与数据库交互的语句。(SQL存在的地方)

  • 再到service层。通过依赖注入mapper接口,实现调用。

mybatis-plus的使用姿势


  • 从domain出发(或者称为do),定义和数据库表对应的实体类。

    • 使用@TableName注解,和数据库表进行绑定。还有@TableId用于指定表中的主键字段、@TableField用于指定表中的非主键字段等等。

  • 再到mapper层(或者称为dao)。

    • 这里的mapper接口,或者dao接口,直接继承BaseMapper<>接口。(也可以自定义很多方法)

    • BaseMapper<>接口包含了常规增删查改操作

    • 此时,可以通过dao接口,使用基本的增删查改操作。在其他地方依赖注入即可。但是按遵循规范啊

  • 再到service层。

    • service接口继承ISevice接口。

    • serviceImpl实现类继承ServiceImpl<>类,同时实现service接口。

    • 此时,可以在serviceImpl使用大量Mp内置的增删查改方法了。

do层

package com.mpdemo.mpdemo.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.time.LocalDateTime;

@Data
@TableName(value = "user")
public class User {
    /**
     * 用户id
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 用户名
     */
    private String username;

    /**
     * 密码
     */
    private String password;

    /**
     * 注册手机号
     */
    private String phone;

    /**
     * 详细信息
     */
    private String info;

    /**
     * 使用状态(1正常 2冻结)
     */
    private Integer status;

    /**
     * 账户余额
     */
    private Integer balance;

}

dao层

在dao层(或mapper层)

当我们实现BaseMapper的时候,MP就会帮我们把BaseMapper里的接口类全部实现代理,成为可以直接被我们调用的类,这个过程完全不需要我们去写xml,当然我们也可以在接口中写上我们自己自定义的方法,但是我们自定义的方法必须去写xml去映射方法(或者注解SQL),默认他会去扫描resources下的mapper文件夹下的xml。

因此BaseMapper就是帮我们写了很多的基本方法。

dao接口继承BaseMapper

@Mapper
public interface UserDao extends BaseMapper<User> {
}
public interface BaseMapper<T> extends Mapper<T> {
    int insert(T entity);

    int deleteById(Serializable id);

    int deleteByMap(@Param("cm") Map<String, Object> columnMap);

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

    int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    int updateById(@Param("et") T entity);

    int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);

    T selectById(Serializable id);

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

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

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

    Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);

    List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);

    List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);

    List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);

    <E extends IPage<T>> E selectPage(E page, @Param("ew") Wrapper<T> queryWrapper);

    <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param("ew") Wrapper<T> queryWrapper);
}

当dao接口继承BaseMapper<T>以后,在其他地方通过依赖注入dao,就可以使用增删查改操作了。

service层

接下来就是说IService接口和ServiceImpl,其实这两个类的存在也是很合理的,你想MP帮我们去实现了mapper接口,那mapper接口是不是得有Service接口和一个Service实现类啊?所以他其实帮我们连实现类也写好了,我们只需要去实现接口和继承实现类就好了。当然Service接口中我们也可以写自己的自定义方法但是同时我们也需要在ServiceImpl中自己去写我们自定义的方法,而从IService接口中实现的方法呢?ServiceImpl其实已经帮我们实现了,我们只要继承他就可以了。

注意,这里IUserService、IService不是必须的啊,ServiceImpl<>也继承了IService。我们自己也写一个是为了分层解耦,为了规范!

UserServiceImpl也可以不实现我们自己定义的IUserService接口,继承ServiceImpl<UserDao, User>即可。

public interface IUserService extends IService<User> {
}
@Service
public class UserServiceImpl extends ServiceImpl<UserDao, User> implements IService<User> {
}

可以看到系统提供的这个ServiceImpl类中有一个成员变量,他被@Autowired注解修饰也就是说这个成员变量是自动注入的,可以发现他的名字叫baseMapper,我们那个UserDao是继承自baseMapper的吧,所以我们把那个接口传进来,其实就是让这个实现类去自动注入我们的那个UserDao

因此,在UserServiceImpl可以使用baseMapper,实现一些自定义的方法。例如

@Repository
public class ArticleDao extends ServiceImpl<ArticleMapper, ArticleDO> {
    /**
     * ArticlePayRecordMapper 继承的 BaseMapper<ArticlePayRecordDO>
     * 因此可以直接使用baseMapper中的通用方法
     * 重新使用@Resource注入,则可以使用ArticleMapper自定义的方法
     */
    @Resource
    private ArticleDetailMapper articleDetailMapper;
    @Resource
    private ReadCountMapper readCountMapper;
    @Resource
    private ArticleMapper articleMapper;

    public ArticleDTO queryArticleDetail(Long articleId) {
        // 查询文章记录
        ArticleDO article = baseMapper.selectById(articleId);
        if (article == null || Objects.equals(article.getDeleted(), YesOrNoEnum.YES.getCode())) {
            return null;
        }

        // 查询文章正文
        ArticleDTO dto = ArticleConverter.toDto(article);
        if (showReviewContent(article)) {
            ArticleDetailDO detail = findLatestDetail(articleId);
            dto.setContent(detail.getContent());
        } else {
            // 对于审核中的文章,只有作者本人才能看到原文
            dto.setContent("### 文章审核中,请稍后再看");
        }
        return dto;
    }

这里baseMapper就是ArticleMapper,使用baseMapper可以调用常规的方法。

但是,如果需要使用ArticleMapper中自定义的方法,那就需要再次注入ArticleMapper了。