随着项目的开发,有时候需要使用新的ORM,使用mybatis的项目需要集成mybatis-plus,可以参考以下方式

1 导入mybatis-plus的相关依赖

        <!-- mybatis-plus,注释掉 org.mybatis.spring.boot -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis.plus.boot.starter.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-generator</artifactId>
                </exclusion>
                <exclusion>
                    <artifactId>jsqlparser</artifactId>
                    <groupId>com.github.jsqlparser</groupId>
                </exclusion>
            </exclusions>
        </dependency>

2 如果项目中存在sqlSessionFactory的配置信息,那么需要处理一下分页拦截器

@Configuration
public class MyBatisConfig {
    @Autowired
    private Environment env;
    @Autowired
**    private MybatisPlusInterceptor mybatisPlusInterceptor;**

    static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";

    public static String setTypeAliasesPackage(String typeAliasesPackage) {
        ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
        MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
        List<String> allResult = new ArrayList<String>();
        try {
            for (String aliasesPackage : typeAliasesPackage.split(",")) {
                List<String> result = new ArrayList<String>();
                aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                        + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
                Resource[] resources = resolver.getResources(aliasesPackage);
                if (resources != null && resources.length > 0) {
                    MetadataReader metadataReader = null;
                    for (Resource resource : resources) {
                        if (resource.isReadable()) {
                            metadataReader = metadataReaderFactory.getMetadataReader(resource);
                            try {
                                result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
                            } catch (ClassNotFoundException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
                if (result.size() > 0) {
                    HashSet<String> hashResult = new HashSet<String>(result);
                    allResult.addAll(hashResult);
                }
            }
            if (allResult.size() > 0) {
                typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
            } else {
                throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return typeAliasesPackage;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
        String mapperLocations = env.getProperty("mybatis.mapperLocations");
        String configLocation = env.getProperty("mybatis.configLocation");
        typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
        VFS.addImplClass(SpringBootVFS.class);

//        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        //改为使用mybaits-plus
        **final MybatisSqlSessionFactoryBean sessionFactory=new MybatisSqlSessionFactoryBean();**
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
        sessionFactory.setMapperLocations(new 			PathMatchingResourcePatternResolver().getResources(mapperLocations));
        sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));

**        sessionFactory.setPlugins(mybatisPlusInterceptor);
**
        return sessionFactory.getObject();
    }
}


/**
 * Mybatis Plus 配置
 *
 * @author ruoyi
 */
@EnableTransactionManagement(proxyTargetClass = true)
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 分页插件
        interceptor.addInnerInterceptor(paginationInnerInterceptor());
        // 乐观锁插件
        interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
        // 阻断插件
        interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
        return interceptor;
    }

    /**
     * 分页插件,自动识别数据库类型 https://baomidou.com/guide/interceptor-pagination.html
     */
    public PaginationInnerInterceptor paginationInnerInterceptor() {
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        // 设置数据库类型为mysql
        paginationInnerInterceptor.setDbType(DbType.MYSQL);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        paginationInnerInterceptor.setMaxLimit(-1L);
        return paginationInnerInterceptor;
    }

    /**
     * 乐观锁插件 https://baomidou.com/guide/interceptor-optimistic-locker.html
     */
    public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
        return new OptimisticLockerInnerInterceptor();
    }

    /**
     * 如果是对全表的删除或更新操作,就会终止该操作 https://baomidou.com/guide/interceptor-block-attack.html
     */
    public BlockAttackInnerInterceptor blockAttackInnerInterceptor() {
        return new BlockAttackInnerInterceptor();
    }
}

3 Mybatis-Plus常用点

1. yml配置

以下为整理的常用yml配置,配置后无需配置mybatis且无需引入mybatis依赖

mybatis-plus:
  mapper-locations: classpath:mapper/**/*.xml  # mapper映射文件(默认存在)
  type-aliases-package: com.itheima.mp.demain # 别名包
  configuration:
    map-underscore-to-camel-case: true # 是否开启下划线与驼峰的映射(默认打开)
    cache-enabled: false # 是否开启二级缓存(默认关闭)
  global-config:      # 全局配置
    db-config:
      id-type: auto #id+1自增策略(默认assign_id雪花算法)
      update-strategy: not_empty #null与''都忽略(默认not_null:只忽略null)
      # 下面的配置:将所有的删除语句更改为修改语句
      logic-delete-field: isDelete # 是否删除;全局逻辑删除的实体字段名,字段类型可以是boolean、integer
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

2 注解用法

以下为整理的常用注解,基本都是标记类表之间的对应关系

@Data
@TableName("tb_user")
public class User {
    //此处配置主键策略优先级 > yml文件
    @TableId(value = "id",type = IdType.AUTO)
    private Long id;
    //与数据库关键字冲突(必加)
    @TableField("`order`")
    private Integer order;
    //is前缀且类型为Boolean(必加)
    @TableField("is_delete")
    private Boolean isDelete;
    //数据表中不存在的(必加)
    @TableField(exist = false)
    private String address;
}

3 DB静态工具

Db静态工具跟IService功能几乎一致,主要用来处理依赖冲突问题,在两个service中可以相互依赖

import com.baomidou.mybatisplus.extension.toolkit.Db;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Autowired
    private AddressMapper addressMapper;//不推荐
    @Override
    public void test() {
        //在UserServiceImpl中不推荐注入AddressMapper
        addressMapper.saveAddress(new Address());
        Db.lambdaUpdate(Address.class).update(new Address());//推荐使用Db静态工具
    }
}

4 枚举处理器

枚举处理器主要用来处理表中数据存储为0,1状态时,通过枚举类对0,1表示内容添加备注

  • 1 配置全局枚举处理器
mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler #全局枚举处理器
  • 2 编写枚举类
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;
@Getter
public enum UserStatus {
    STOP_USING(1,"已停用"),
    USING(0,"启用中");
    @EnumValue //表示与数据库交互的值
    private final Integer status;
    @JsonValue //表示返回给前端的值
    private final String desc;
    UserStatus(Integer status, String desc) {
        this.status = status;
        this.desc = desc;
    }
  • 3 查询数据,查询0状态的用户,返回启用中(注:直接打印不会出现想要字符)
@PostMapping("test")
public List<User> test01(){
    return userService.lambdaQuery()
            .eq(User::getStatus,UserStatus.USING).list();
}

5 dto与vo的分页查询

mybatis-plus依旧可以使用常规分页方法,这边介绍mybatis-plus自带的。

  • 1 添加分页插件,配置mp的拦截器
@Configuration
public class MybatisConfig {
   @Bean
   public MybatisPlusInterceptor mybatisPlusInterceptor() {
       // 1.初始化核心插件
       MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
       // 2.添加分页插件
        PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        pageInterceptor.setMaxLimit(1000L); // 设置分页上限
               interceptor.addInnerInterceptor(pageInterceptor);
               return interceptor;
   }
}

    1. 写两个分页父类,先介绍参数类
@Data
public class PageParam {
    /**
     * 页码
     */
    @TableField(exist = false)
    private Integer pageNum = 1;
    /**
     * 总页数
     */
    @TableField(exist = false)
    private Integer pageSize = 5;
    /**
     * 排序字段
     */
    @TableField(exist = false)
    private String sortBy;
    /**
     * 是否升序
     */
    @TableField(exist = false)
    private Boolean isAsc;

    /**
     * 空排序字段传递排序规则
     * @param items
     * @return
     * @param <T>
     */
    public <T> Page<T> toMpPage(OrderItem ... items){
        // 分页条件
        Page<T> page = Page.of(pageNum, pageSize);
        if(StrUtil.isNotBlank(sortBy)){
            page.addOrder(new OrderItem(sortBy, isAsc));
        }else if(sortBy != null){
            page.addOrder(items);
        }
        return page;
    }

    /**
     * 设定默认值:"更新字段"降序
     * @return
     * @param <T>
     */
    public <T> Page<T> toMpPageDefaultSortByUpdateTime(){
        return toMpPage(new OrderItem("update_time", false));
    }
}

  • 3 返回类型
@Data
public class PageResult<T> {
    /**
     * 总条数
     */
    private Long total;
    /**
     * 总页数
     */
    private Long pages;
    /**
     * 当前页数据
     */
    private List<T> list;

    /**
     * 通过BeanUtil拷贝的形式,实现 dto -> vo
     * @param po
     * @param clazz
     * @return
     * @param <PO>
     * @param <VO>
     */
    public static <PO, VO> PageResult<VO> of(Page<PO> po, Class<VO> clazz){
        PageResult<VO> result = new PageResult<>();
        // 总条数
        result.setTotal(po.getTotal());
        // 总页数
        result.setPages(po.getPages());
        // 当前页数据
        List<PO> records = po.getRecords();
        if(CollUtil.isEmpty(records)){
            result.setList(Collections.emptyList());
            return result;
        }
        // 拷贝VO
        result.setList(BeanUtil.copyToList(records, clazz));
        return result;
    }

    /**
     * 通过传递函数式接口定义规则,实现 dto -> vo
     * @param po
     * @param convertor
     * @return
     * @param <PO>
     * @param <VO>
     */
    public static <PO, VO> PageResult<VO> of(Page<PO> po, Function<PO, VO> convertor){
        PageResult<VO> result = new PageResult<>();
        // 总条数
        result.setTotal(po.getTotal());
        // 总页数
        result.setPages(po.getPages());
        // 当前页数据
        List<PO> records = po.getRecords();
        if(CollUtil.isEmpty(records)){
            result.setList(Collections.emptyList());
            return result;
        }
        // 拷贝VO
        result.setList(records.stream().map(convertor).collect(Collectors.toList()));
        return result;
    }
    
}


  • 4 使用例子
PageResult<UserVo> list = userService.pageSelect(user);

public PageResult<UserVo> pageSelect(User user) {
    //通过传递来的参数点出方法包装为page对象
    Page<User> page = user.toMpPageDefaultSortByUpdateTime();

    //分页+条件查询
    String username = user.getUsername();
    Page<User> pageSelect = lambdaQuery()
            .like(username != null, User::getUsername, username)
            .page(page);

    //一般情况,使用基础的BeanUtil赋值,需要Vo和user字段名一致
    return PageResult.of(pageSelect, UserVo.class);
}

PageResult<UserVo> list = userService.pageSelect2(user);
@Override
public PageResult<UserVo> pageSelect2(User user) {
    //通过传递来的参数点出方法包装为page对象
    Page<User> page = user.toMpPageDefaultSortByUpdateTime();
    
    //分页+条件查询
    String username = user.getUsername();
    Page<User> pageSelect = lambdaQuery()
            .like(username != null, User::getUsername, username)
            .page(page);

    return PageResult.of(pageSelect,
            //传递匿名内部类 - 处理查询出的数据与Vo的转化
            userTest -> {
                //这里演示拷贝赋值+处理username最后两个字符为**
                UserVo userVo = BeanUtil.copyProperties(userTest, UserVo.class);
                userVo.setTest(userVo.getUsername().substring(0, userVo.getUsername().length() - 2) + "**");
                return userVo;
            });
}