MybatisPlus

2023-10-29

在这里插入图片描述
MybatisPlus特点

  1. MyBatis-Plus是MyBatis的强大增强工具。它为MyBatis提供了许多有效的操作。你可以从MyBatis无缝切换到MyBatis-Plus。
  2. MyBatis-Plus可以自动注入基本的SQL片段;
  3. MyBatis-Plus有许多有用的插件(例如代码生成器,自动分页,性能分析等);

依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus</artifactId>
    <version>latest-version</version>
</dependency>

MybatisPlus的特性

**无侵入:**只做增强不做改变,引入它不会对现有工程产生影响;

**损耗小:**启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作;

**强大的 CRUD 操作:**内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求;

**支持 Lambda 形式调用:**通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错;

**支持主键自动生成:**支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题;

**支持 ActiveRecord 模式:**支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作;

**支持自定义全局通用操作:**支持全局通用方法注入( Write once, use anywhere );

**内置代码生成器:**采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用;

**内置分页插件:**基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询;

**分页插件支持多种数据库:**支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库;

**内置性能分析插件:**可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询;

**内置全局拦截插件:**提供全表删除 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

数据库支持

在这里插入图片描述

框架结构

在这里插入图片描述
mybatisplus是在mybatis的curd基础上及逆行增强,所以需要将mybatis中的SqlSessionFactory替换为调整 SqlSessionFactory 为 MyBatis-Plus 的 SqlSessionFactory
看一下xml中的配置—>>

<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
</bean>

所以在0xml中声明sqlSessionFactory实际为mybatisPlus中的;
在这里插入图片描述
查看源码–>>

 @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setVfs(SpringBootVFS.class);
   .........................省略......................
        return factory.getObject();
    }

我们获取一下sqlSessionFactory的全限定名,

package com.codem;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
//@MapperScan("com.codem.mapper")
public class MybatiplusApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(MybatiplusApplication.class, args);        System.out.println(context.getBean("sqlSessionFactory").getClass());
    }
}

全限定名—>


class org.apache.ibatis.session.defaults.DefaultSqlSessionFactory

当dataSource作为参数传入MybatisPlus的SqlSessionFactory时,会做一系列处理-----即增强了之前的SqlSessionFactory;

整个案例感受一下
表还是之前的经典表emp与dept

创建一个springboot项目
父依赖

<parent>      <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

mybatisplus启动器

<!--mybatisplus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>

mysql数据库配置

spring:
  datasource:
    #    使用druid链接池
    type: com.alibaba.druid.pool.DruidDataSource
    #    数据库要素
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/gavin?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: gavin
    password: 955945

mybatis配置

mybatis:
  type-aliases-package: com.gavin.pojo
  mapper-locations: classpath:com/gavin/mapper/*.xml #按照习惯来吧

自动配置的内容
MyBatis PlusAutoConfiguration配置类,MyBatisPlusProperties配置项前缀 mybatis-plus: ***就是对mybatis-plus的参数的设置

SQLSessionFactory已经配置好
mapperlocations 自动配置好的,默认值是classpath*:/mapper/**/*.xml 意为任意包路径下所有的mapper包下的xml文件

实体类

package com.codem.pojo;
import java.io.Serializable;
import java.util.Date;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.ibatis.annotations.Mapper;

/**
 * emp
 * @author 
 */

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp implements Serializable {
    private Integer empno;

    private String ename;

    private String job;

    private Integer mgr;

    private Date hiredate;

    private Integer sal;

    private Integer comm;

    private Integer deptno;

    private static final long serialVersionUID = 1L;
}

测试类

package codem;

import com.codem.MybatiplusApplication;
import com.codem.mapper.EmpMapper;
import com.codem.pojo.Emp;
import org.junit.jupiter.api.Test;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest(classes = MybatiplusApplication.class)
//@MapperScan("com.codem.mapper")//扫描mapper所在的包,或者在每一个mapper上夹注解@Mapper
public class MybatiplusApplicationTests {
    @Autowired
 private EmpMapper empMapper;
    @Test
   public void contextLoads() {
        List<Emp> emps = empMapper.selectAll();
        emps.forEach(System.out::println);
    }

}

mapper接口

package com.codem.mapper;

import com.codem.pojo.Emp;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper

public interface EmpMapper {

    List<Emp> selectAll();

}

结果—>>

Emp(empno=7369, ename=SMITH, job=CLERK, mgr=7902, hiredate=Wed Dec 17 00:00:00 CST 1980, sal=800, comm=null, deptno=20)
Emp(empno=7499, ename=ALLEN, job=SALESMAN, mgr=7698, hiredate=Fri Feb 20 00:00:00 CST 1981, sal=1600, comm=300, deptno=30)
Emp(empno=7521, ename=WARD, job=SALESMAN, mgr=7698, hiredate=Sun Feb 22 00:00:00 CST 1981, sal=1250, comm=500, deptno=30)

看起来跟之前springbootmybatis没有什么太大的区别,但是这只是基础,
MyBatis-Plus 提供了大量的个性化配置来满足不同复杂度的工程
Mybatisplus为我们提供了两个接口已简化我们对curd的编写

BaseMapper extends Mapper

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.baomidou.mybatisplus.core.mapper;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;

public interface BaseMapper<T> extends Mapper<T> {
    int insert(T entity);//插入

    int deleteById(Serializable id);//删除

    int deleteById(T entity);

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

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

    int deleteBatchIds(@Param("coll") Collection<?> 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);

    default T selectOne(@Param("ew") Wrapper<T> queryWrapper) {
        List<T> ts = this.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(ts)) {
            if (ts.size() != 1) {
                throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records", new Object[0]);
            } else {
                return ts.get(0);
            }
        } else {
            return null;
        }
    }

    default boolean exists(Wrapper<T> queryWrapper) {
        Long count = this.selectCount(queryWrapper);
        return null != count && count > 0L;
    }

    Long 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);

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

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

还有另一个接口—>>

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.baomidou.mybatisplus.extension.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import com.baomidou.mybatisplus.extension.kotlin.KtQueryChainWrapper;
import com.baomidou.mybatisplus.extension.kotlin.KtUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.transaction.annotation.Transactional;

public interface IService<T> {
    int DEFAULT_BATCH_SIZE = 1000;

    default boolean save(T entity) {//节点存储
        return SqlHelper.retBool(this.getBaseMapper().insert(entity));
    }

    @Transactional(
        rollbackFor = {Exception.class}//回滚
    )
    default boolean saveBatch(Collection<T> entityList) {
        return this.saveBatch(entityList, 1000);//存储,默认1000
    }

    boolean saveBatch(Collection<T> entityList, int batchSize);//节点存储自定义容量

    @Transactional(
        rollbackFor = {Exception.class}
    )
    default boolean saveOrUpdateBatch(Collection<T> entityList) {
        return this.saveOrUpdateBatch(entityList, 1000);
    }

    boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

    default boolean removeById(Serializable id) {
        return SqlHelper.retBool(this.getBaseMapper().deleteById(id));
    }

    default boolean removeById(Serializable id, boolean useFill) {
        throw new UnsupportedOperationException("不支持的方法!");
    }

    default boolean removeById(T entity) {
        return SqlHelper.retBool(this.getBaseMapper().deleteById(entity));
    }

    default boolean removeByMap(Map<String, Object> columnMap) {
        Assert.notEmpty(columnMap, "error: columnMap must not be empty", new Object[0]);
        return SqlHelper.retBool(this.getBaseMapper().deleteByMap(columnMap));
    }

    default boolean remove(Wrapper<T> queryWrapper) {
        return SqlHelper.retBool(this.getBaseMapper().delete(queryWrapper));
    }

    default boolean removeByIds(Collection<?> list) {
        return CollectionUtils.isEmpty(list) ? false : SqlHelper.retBool(this.getBaseMapper().deleteBatchIds(list));
    }

    @Transactional(
        rollbackFor = {Exception.class}
    )
    default boolean removeByIds(Collection<?> list, boolean useFill) {
        if (CollectionUtils.isEmpty(list)) {
            return false;
        } else {
            return useFill ? this.removeBatchByIds(list, true) : SqlHelper.retBool(this.getBaseMapper().deleteBatchIds(list));
        }
    }

    @Transactional(
        rollbackFor = {Exception.class}
    )
    default boolean removeBatchByIds(Collection<?> list) {
        return this.removeBatchByIds(list, 1000);
    }

    @Transactional(
        rollbackFor = {Exception.class}
    )
    default boolean removeBatchByIds(Collection<?> list, boolean useFill) {
        return this.removeBatchByIds(list, 1000, useFill);
    }

    default boolean removeBatchByIds(Collection<?> list, int batchSize) {
        throw new UnsupportedOperationException("不支持的方法!");
    }

    default boolean removeBatchByIds(Collection<?> list, int batchSize, boolean useFill) {
        throw new UnsupportedOperationException("不支持的方法!");
    }

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

    default boolean update(Wrapper<T> updateWrapper) {
        return this.update((Object)null, updateWrapper);
    }

    default boolean update(T entity, Wrapper<T> updateWrapper) {
        return SqlHelper.retBool(this.getBaseMapper().update(entity, updateWrapper));
    }

    @Transactional(
        rollbackFor = {Exception.class}
    )
    default boolean updateBatchById(Collection<T> entityList) {
        return this.updateBatchById(entityList, 1000);
    }

    boolean updateBatchById(Collection<T> entityList, int batchSize);

    boolean saveOrUpdate(T entity);

    default T getById(Serializable id) {
        return this.getBaseMapper().selectById(id);
    }

    default List<T> listByIds(Collection<? extends Serializable> idList) {
        return this.getBaseMapper().selectBatchIds(idList);
    }

    default List<T> listByMap(Map<String, Object> columnMap) {
        return this.getBaseMapper().selectByMap(columnMap);
    }

    default T getOne(Wrapper<T> queryWrapper) {
        return this.getOne(queryWrapper, true);
    }

    T getOne(Wrapper<T> queryWrapper, boolean throwEx);

    Map<String, Object> getMap(Wrapper<T> queryWrapper);

    <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

    default long count() {
        return this.count(Wrappers.emptyWrapper());
    }

    default long count(Wrapper<T> queryWrapper) {
        return SqlHelper.retCount(this.getBaseMapper().selectCount(queryWrapper));
    }

    default List<T> list(Wrapper<T> queryWrapper) {
        return this.getBaseMapper().selectList(queryWrapper);
    }

    default List<T> list() {
        return this.list(Wrappers.emptyWrapper());
    }

    default <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) {
        return this.getBaseMapper().selectPage(page, queryWrapper);
    }

    default <E extends IPage<T>> E page(E page) {
        return this.page(page, Wrappers.emptyWrapper());
    }

    default List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper) {
        return this.getBaseMapper().selectMaps(queryWrapper);
    }

    default List<Map<String, Object>> listMaps() {
        return this.listMaps(Wrappers.emptyWrapper());
    }

    default List<Object> listObjs() {
        return this.listObjs(Function.identity());
    }

    default <V> List<V> listObjs(Function<? super Object, V> mapper) {
        return this.listObjs(Wrappers.emptyWrapper(), mapper);
    }

    default List<Object> listObjs(Wrapper<T> queryWrapper) {
        return this.listObjs(queryWrapper, Function.identity());
    }

    default <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {
        return (List)this.getBaseMapper().selectObjs(queryWrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList());
    }

    default <E extends IPage<Map<String, Object>>> E pageMaps(E page, Wrapper<T> queryWrapper) {
        return this.getBaseMapper().selectMapsPage(page, queryWrapper);
    }

    default <E extends IPage<Map<String, Object>>> E pageMaps(E page) {
        return this.pageMaps(page, Wrappers.emptyWrapper());
    }

    BaseMapper<T> getBaseMapper();

    Class<T> getEntityClass();

    default QueryChainWrapper<T> query() {
        return ChainWrappers.queryChain(this.getBaseMapper());
    }

    default LambdaQueryChainWrapper<T> lambdaQuery() {
        return ChainWrappers.lambdaQueryChain(this.getBaseMapper());
    }

    default KtQueryChainWrapper<T> ktQuery() {
        return ChainWrappers.ktQueryChain(this.getBaseMapper(), this.getEntityClass());
    }

    default KtUpdateChainWrapper<T> ktUpdate() {
        return ChainWrappers.ktUpdateChain(this.getBaseMapper(), this.getEntityClass());
    }

    default UpdateChainWrapper<T> update() {
        return ChainWrappers.updateChain(this.getBaseMapper());
    }

    default LambdaUpdateChainWrapper<T> lambdaUpdate() {
        return ChainWrappers.lambdaUpdateChain(this.getBaseMapper());
    }

    default boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper) {
        return this.update(entity, updateWrapper) || this.saveOrUpdate(entity);
    }
}

在这个接口中也提供了一些方法,用于简化手写的curd以及批量提交/回滚

所以在自动给注入时我们service,或者mapper可以注入
在这里插入图片描述
接口实现类—ServiceImpl

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.baomidou.mybatisplus.extension.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import org.apache.ibatis.binding.MapperMethod.ParamMap;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
    protected Log log = LogFactory.getLog(this.getClass());
    @Autowired
    protected M baseMapper;
    protected Class<T> entityClass = this.currentModelClass();
    protected Class<M> mapperClass = this.currentMapperClass();

    public ServiceImpl() {
    }

    public M getBaseMapper() {
        return this.baseMapper;
    }

    public Class<T> getEntityClass() {
        return this.entityClass;
    }

    /** @deprecated */
    @Deprecated
    protected boolean retBool(Integer result) {
        return SqlHelper.retBool(result);
    }

    protected Class<M> currentMapperClass() {
        return ReflectionKit.getSuperClassGenericType(this.getClass(), ServiceImpl.class, 0);
    }

    protected Class<T> currentModelClass() {
        return ReflectionKit.getSuperClassGenericType(this.getClass(), ServiceImpl.class, 1);
    }

    /** @deprecated */
    @Deprecated
    protected SqlSession sqlSessionBatch() {
        return SqlHelper.sqlSessionBatch(this.entityClass);
    }

    /** @deprecated */
    @Deprecated
    protected void closeSqlSession(SqlSession sqlSession) {
        SqlSessionUtils.closeSqlSession(sqlSession, GlobalConfigUtils.currentSessionFactory(this.entityClass));
    }

    /** @deprecated */
    @Deprecated
    protected String sqlStatement(SqlMethod sqlMethod) {
        return SqlHelper.table(this.entityClass).getSqlStatement(sqlMethod.getMethod());
    }

    @Transactional(
        rollbackFor = {Exception.class}
    )
    public boolean saveBatch(Collection<T> entityList, int batchSize) {
        String sqlStatement = this.getSqlStatement(SqlMethod.INSERT_ONE);
        return this.executeBatch(entityList, batchSize, (sqlSession, entity) -> {
            sqlSession.insert(sqlStatement, entity);
        });
    }

    protected String getSqlStatement(SqlMethod sqlMethod) {
        return SqlHelper.getSqlStatement(this.mapperClass, sqlMethod);
    }

    @Transactional(
        rollbackFor = {Exception.class}
    )
    public boolean saveOrUpdate(T entity) {
        if (null == entity) {
            return false;
        } else {
            TableInfo tableInfo = TableInfoHelper.getTableInfo(this.entityClass);
            Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!", new Object[0]);
            String keyProperty = tableInfo.getKeyProperty();
            Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!", new Object[0]);
            Object idVal = tableInfo.getPropertyValue(entity, tableInfo.getKeyProperty());
            return !StringUtils.checkValNull(idVal) && !Objects.isNull(this.getById((Serializable)idVal)) ? this.updateById(entity) : this.save(entity);
        }
    }

    @Transactional(
        rollbackFor = {Exception.class}
    )
    public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(this.entityClass);
        Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!", new Object[0]);
        String keyProperty = tableInfo.getKeyProperty();
        Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!", new Object[0]);
        return SqlHelper.saveOrUpdateBatch(this.entityClass, this.mapperClass, this.log, entityList, batchSize, (sqlSession, entity) -> {
            Object idVal = tableInfo.getPropertyValue(entity, keyProperty);
            return StringUtils.checkValNull(idVal) || CollectionUtils.isEmpty(sqlSession.selectList(this.getSqlStatement(SqlMethod.SELECT_BY_ID), entity));
        }, (sqlSession, entity) -> {
            ParamMap<T> param = new ParamMap();
            param.put("et", entity);
            sqlSession.update(this.getSqlStatement(SqlMethod.UPDATE_BY_ID), param);
        });
    }

    @Transactional(
        rollbackFor = {Exception.class}
    )
    public boolean updateBatchById(Collection<T> entityList, int batchSize) {
        String sqlStatement = this.getSqlStatement(SqlMethod.UPDATE_BY_ID);
        return this.executeBatch(entityList, batchSize, (sqlSession, entity) -> {
            ParamMap<T> param = new ParamMap();
            param.put("et", entity);
            sqlSession.update(sqlStatement, param);
        });
    }

    public T getOne(Wrapper<T> queryWrapper, boolean throwEx) {
        return throwEx ? this.baseMapper.selectOne(queryWrapper) : SqlHelper.getObject(this.log, this.baseMapper.selectList(queryWrapper));
    }

    public Map<String, Object> getMap(Wrapper<T> queryWrapper) {
        return (Map)SqlHelper.getObject(this.log, this.baseMapper.selectMaps(queryWrapper));
    }

    public <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {
        return SqlHelper.getObject(this.log, this.listObjs(queryWrapper, mapper));
    }

    /** @deprecated */
    @Deprecated
    protected boolean executeBatch(Consumer<SqlSession> consumer) {
        return SqlHelper.executeBatch(this.entityClass, this.log, consumer);
    }

    protected <E> boolean executeBatch(Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
        return SqlHelper.executeBatch(this.entityClass, this.log, list, batchSize, consumer);
    }

    protected <E> boolean executeBatch(Collection<E> list, BiConsumer<SqlSession, E> consumer) {
        return this.executeBatch(list, 1000, consumer);
    }

    public boolean removeById(Serializable id) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(this.getEntityClass());
        return tableInfo.isWithLogicDelete() && tableInfo.isWithUpdateFill() ? this.removeById(id, true) : SqlHelper.retBool(this.getBaseMapper().deleteById(id));
    }

    @Transactional(
        rollbackFor = {Exception.class}
    )
    public boolean removeByIds(Collection<?> list) {
        if (CollectionUtils.isEmpty(list)) {
            return false;
        } else {
            TableInfo tableInfo = TableInfoHelper.getTableInfo(this.getEntityClass());
            return tableInfo.isWithLogicDelete() && tableInfo.isWithUpdateFill() ? this.removeBatchByIds(list, true) : SqlHelper.retBool(this.getBaseMapper().deleteBatchIds(list));
        }
    }

    public boolean removeById(Serializable id, boolean useFill) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(this.entityClass);
        if (useFill && tableInfo.isWithLogicDelete() && !this.entityClass.isAssignableFrom(id.getClass())) {
            T instance = tableInfo.newInstance();
            tableInfo.setPropertyValue(instance, tableInfo.getKeyProperty(), new Object[]{id});
            return this.removeById(instance);
        } else {
            return SqlHelper.retBool(this.getBaseMapper().deleteById(id));
        }
    }

    @Transactional(
        rollbackFor = {Exception.class}
    )
    public boolean removeBatchByIds(Collection<?> list, int batchSize) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(this.entityClass);
        return this.removeBatchByIds(list, batchSize, tableInfo.isWithLogicDelete() && tableInfo.isWithUpdateFill());
    }

    @Transactional(
        rollbackFor = {Exception.class}
    )
    public boolean removeBatchByIds(Collection<?> list, int batchSize, boolean useFill) {
        String sqlStatement = this.getSqlStatement(SqlMethod.DELETE_BY_ID);
        TableInfo tableInfo = TableInfoHelper.getTableInfo(this.entityClass);
        return this.executeBatch(list, batchSize, (sqlSession, e) -> {
            if (useFill && tableInfo.isWithLogicDelete()) {
                if (this.entityClass.isAssignableFrom(e.getClass())) {
                    sqlSession.update(sqlStatement, e);
                } else {
                    T instance = tableInfo.newInstance();
                    tableInfo.setPropertyValue(instance, tableInfo.getKeyProperty(), new Object[]{e});
                    sqlSession.update(sqlStatement, instance);
                }
            } else {
                sqlSession.update(sqlStatement, e);
            }

        });
    }
}

通过实现类来实现接口,最终完成功能;

当有mybatis_plus前缀时会自动去检查下面的配置,比如映射文件地址—>“classpath*:/mapper/**/*.xml”

在这里插入图片描述
mybatis与mybatis-plus基本配置对比


mybatis:
  type-aliases-package: com.codem.pojo
  mapper-locations: classpath:com/codem/mapper/*.xml #按照习惯来吧
#  mybatisplus在mybatis基础上进行强化,但并不是改变mybatis
mybatis-plus:
  type-aliases-package: com.codem.pojo
  mapper-locations: classpath:com/codem/mapper/*.xml #默认值 classpath*:/mapper/**/*.xml

mybatisPlus实现curd代码—>>
目录结构
在这里插入图片描述
mapper接口----->>

package com.codem.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.codem.pojo.Emp;
public interface EmpMapper extends BaseMapper<Emp> {
}

service接口

package com.codem.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.codem.pojo.Emp;
import org.apache.ibatis.annotations.Mapper;
public interface EmpService extends IService<Emp> {
}

service接口实现类

package com.codem.service.imp;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.codem.mapper.EmpMapper;
import com.codem.pojo.Emp;
import com.codem.service.EmpService;
import org.springframework.stereotype.Service;

@Service
public class empServiceImpl extends ServiceImpl<EmpMapper, Emp> implements EmpService {
}

配置注解@SpringBootTest(classes = MybatiplusApplication.class),因为测试类也需要启动springboot环境;
所以在测试类之前还需要在springboot启动类上做一些配置–

package com.codem;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
@MapperScan("com.codem.mapper")//扫描包而不是单个注解mapper
public class MybatiplusApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(MybatiplusApplication.class, args);
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String definitionName : beanDefinitionNames) {
            System.out.println(definitionName);
        }
        System.out.println("---------------------");
        System.out.println(context.getBean("sqlSessionFactory").getClass());
    }

}

测试类---->>

package codem;

import com.codem.MybatiplusApplication;
import com.codem.mapper.EmpMapper;
import com.codem.pojo.Emp;
import com.codem.service.EmpService;
import org.junit.jupiter.api.Test;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest(classes = MybatiplusApplication.class)
//@MapperScan("com.codem.mapper")
public class MybatiplusApplicationTests {
   /* @Autowired
 private EmpMapper empMapper;*/

    @Autowired
    EmpService empService;
    @Test
   public void contextLoads() {
        List<Emp> emps = empService.list();
//        List<Emp> emps = empMapper.selectAll();
        emps.forEach(System.out::println);
    }

}

跟之前对比,对于一些基本的curd我们就不需要在xml配置文件中写对应的sql语句了,但是还是需要xml映射文件的;

MyBatis-Plus 配置参数详解

MybatisPlus中的注解

mybatisplus注解所在的包

mybatis-plus-annotation

在这里插入图片描述

常用的注解

@TableName
表注解

//指定对应的表名,    指定映射 resultMap,可以理解为类构造对应mapper中的resultMap
//autoResultMap---自动构建resultMap

@TableName(value = "emp" ,resultMap ="empconstructor",autoResultMap = true)
public class Emp implements Serializable {
    

@TableId
主键注解

//主键字段名, 主键--自增
   @TableId(value = "empno",type = IdType.AUTO)
    private Integer empno;
    //IdType.INPUT----insert前自行set主键值

在这里插入图片描述
@TableField
属性/字段注解(非主键)

    //属性名与字段名不一致时可以加注解使得其匹配,插入的策略---非空,还有update,where等
    @TableField(value = "ename",insertStrategy = FieldStrategy.NOT_NULL)
    private String ename;

关于jdbcTypetypeHandler以及numericScale的说明:
numericScale只生效于 update 的sql. jdbcType和typeHandler如果不配合@TableName#autoResultMap = true一起使用,也只生效于 update 的sql. 对于typeHandler如果你的字段类型和set进去的类型为equals关系,则只需要让你的typeHandler让Mybatis加载到即可,不需要使用注解

在这里插入图片描述
在这里插入图片描述

@OrderBy
排序,作用域字段/属性/注解上

//指定排列顺序优先级低于 wrapper 条件查询
    @OrderBy(asc = false)
//主键字段名,设置主键--自增
  @TableId(value = "empno",type = IdType.AUTO)
    private Integer empno;

其他注解的使用API

代码自动生成

代码生成器(3.4.1+版本)

AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

MyBatis-Plus 从 3.0.3 之后移除了代码生成器与模板引擎的默认依赖,需要手动添加相关依赖:

引入代码自动生成的依赖(非最新版本)---->>

 <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>

依赖分析
在这里插入图片描述
MyBatis-Plus 支持 Velocity(默认)、Freemarker、Beetl,可以采用自定义模板引擎。

<!--        模板引擎-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

注意!如果您选择了非默认引擎,需要在 AutoGenerator 中 设置模板引擎。

官方教程

案例—>>
代码生成器代码

package com;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class CodeGenerator {

    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        //设置生成代码的上一级目录
        help.append("请输入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }

    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        //设置代码生成的位置,默认在根目录下
        gc.setOutputDir(projectPath + "/demo/src/main/java");
        gc.setAuthor("codeM");
        gc.setOpen(false);
        // gc.setSwagger2(true); 实体属性 Swagger2 注解
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/gavin?useUnicode=true&useSSL=false&characterEncoding=utf8");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("gavin");
        dsc.setPassword("955945");
        mpg.setDataSource(dsc);

        // 包配置D:\Program Data\idea2019Data\mybatiplus\demo
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(scanner("模块"));
        //设置java下的顶级目录
        pc.setParent("com");
        mpg.setPackageInfo(pc);

        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        // 如果模板引擎是 freemarker
        String templatePath = "/templates/mapper.xml.ftl";
        // 如果模板引擎是 velocity
        // String templatePath = "/templates/mapper.xml.vm";

        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        /*
        cfg.setFileCreate(new IFileCreate() {
            @Override
            public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
                // 判断自定义文件夹是否需要创建
                checkDir("调用默认方法创建的目录,自定义目录用");
                if (fileType == FileType.MAPPER) {
                    // 已经生成 mapper 文件判断存在,不想重新生成返回 false
                    return !new File(filePath).exists();
                }
                // 允许生成模板文件
                return true;
            }
        });
        */
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();

        // 配置自定义输出模板
        //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
        // templateConfig.setEntity("templates/entity2.java");
        // templateConfig.setService();
        // templateConfig.setController();

        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
//        继承的父类
//        strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        // 公共父类
//        strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
        // 写于父类中的公共字段
        strategy.setSuperEntityColumns("id");
        strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }

}

目录结构
在这里插入图片描述
基本按照上面那个模板来就好,
这个模板的思路是---->>

  1. 首先获取指定的文件夹(scanner)
  2. 然后获取配置内容,
  3. 链接数据库
  4. 定义模板
    mybatis支持的模板引擎—
    在这里插入图片描述
    如果使用Thymeleaf,则需要自定义模板,
    这就需要继承
    com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine
    自定义代码模板
//指定自定义模板路径, 位置:/resources/templates/entity2.java.ftl(或者是.vm)
//注意不要带上.ftl(或者是.vm), 会根据使用的模板引擎自动识别
TemplateConfig templateConfig = new TemplateConfig()
    .setEntity("templates/entity2.java");

AutoGenerator mpg = new AutoGenerator();
//配置自定义模板
mpg.setTemplate(templateConfig);

//自定义属性注入
InjectionConfig injectionConfig = new InjectionConfig() {
    //自定义属性注入:abc
    //在.ftl(或者是.vm)模板中,通过${cfg.abc}获取属性
    @Override
    public void initMap() {
        Map<String, Object> map = new HashMap<>();
        map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
        this.setMap(map);
    }
};
AutoGenerator mpg = new AutoGenerator();
//配置自定义属性注入
mpg.setCfg(injectionConfig);
entity2.java.ftl
自定义属性注入abc=${cfg.abc}

entity2.java.vm
自定义属性注入abc=$!{cfg.abc}
#字段其他信息查询注入

代码生成器(3.5.1+版本)

文件位置配置–>>

location.properties

jdbc_url=jdbc:mysql://127.0.0.1:3306/gavin?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
jdbc_username=gavin
jdbc_password=955945
location_path=zzy\\src\\main/\\java
mapper_path=zzy\\src\\main\\resources\\mapper

代码快速自动生成

package com;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.apache.ibatis.io.Resources;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Properties;

/**
 * @author Gavin
 */
public class FastAutoGeneratorTest {
    public static void main(String[] args) throws IOException {

        InputStream FTT = Resources.getResourceAsStream("config/location.properties");
        Properties properties= new Properties();
        properties.load(FTT);
        String url = properties.getProperty("jdbc_url");
        String username = properties.getProperty("jdbc_username");
        String password = properties.getProperty("jdbc_password");
        String location_path = properties.getProperty("location_path");
        String mapper_path = properties.getProperty("mapper_path");
        FastAutoGenerator.create(url, username,password)
                .globalConfig(builder -> {
                    builder.author("codeM") // 设置作者
                            .enableSwagger() // 开启 swagger 模式
                            .fileOverride() // 覆盖已生成文件
                            .outputDir(location_path); // 指定输出目录
                })
                //D:\Program Data\idea2019Data\mybatiplus\zzy\src\main\java\com
                .packageConfig(builder -> {
                    builder.parent("com") // 设置父包名
                            .moduleName("gavin") // 设置父包模块名/ /zzy/src/main/resources/mapper
                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml, mapper_path)); // 设置mapperXml生成路径
                })
                .strategyConfig(builder -> {
                    builder.addInclude("dept") // 设置表名----要生成实体
                            .addTablePrefix("t_", "c_"); // 设置过滤表前缀
                })
                .templateEngine(new FreemarkerTemplateEngine())  // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();
    }

}

注意仅适用 3.5.1 以上版本,对历史版本坑会存在不兼容问题)

记录遇到的问题---->>路径问题
如图所示,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6i9OGYRy-1644313641190)(https://img-mid.csdnimg.cn/release/static/image/mid/ask/155522313446148.png "#left")]

解决方法—把 / 改成 (要转义)

在这里插入图片描述
目录结构
在这里插入图片描述

交互式生成代码—>>

package com;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;
import org.apache.ibatis.io.Resources;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;

public class CodeGenerator {

    public static void main(String[] args) throws IOException {

        InputStream FTT = Resources.getResourceAsStream("config/location.properties");

        Properties properties = new Properties();
        properties.load(FTT);
        String url = properties.getProperty("jdbc_url");
        String username = properties.getProperty("jdbc_username");
        String password = properties.getProperty("jdbc_password");
        String location_path = properties.getProperty("location_path");
        String mapper_path = properties.getProperty("mapper_path");
        FastAutoGenerator.create(url, username,password)
                // 全局配置
                .globalConfig((scanner, builder) -> {builder.author(scanner.apply("请输入作者名称?")).fileOverride().outputDir(location_path);})
                // 包配置
                .packageConfig((scanner, builder) -> builder.parent(scanner.apply("请输入包全限定名?")))
                // 策略配置
                .strategyConfig((scanner, builder) -> builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?所有输入 all")))
                        .controllerBuilder().enableRestStyle().enableHyphenStyle()
                        .entityBuilder().enableLombok().addTableFills(
                                new Column("create_time", FieldFill.INSERT)
                        ).build()) .templateEngine(new FreemarkerTemplateEngine())
                /*
                    模板引擎配置,默认 Velocity 可选模板引擎 Beetl 或 Freemarker
                   .templateEngine(new BeetlTemplateEngine())
                   .templateEngine(new FreemarkerTemplateEngine())
                 */
                .execute();


    }

    // 处理 all 情况
    protected static List<String> getTables(String tables) {
        return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
    }
}

数据库配置–>

new DataSourceConfig.
    Builder("jdbc:mysql://127.0.0.1:3306/数据库","root","123456").build();

全局配置

new GlobalConfig.Builder()
    .fileOverride()//覆盖已生成的文件
    .outputDir("/codem/zzy")//输出目录
    .author("codem")//作者
    .enableKotlin()//kotlin模式.默认false
    .enableSwagger()// swagger 模式.默认 false
    .dateType(DateType.TIME_PACK)//时间策略
    .commentDate("yyyy-MM-dd")//注释日期
    .build();

包配置

new PackageConfig.Builder()
    parent("com.gavin.mybatisplus.samples.generator")//父包名
    .moduleName("sys")//模块名
    .entity("po")//实体类包名
    .service("service")//service包名
    .serviceImpl("service.impl")
    .mapper("mapper")
    .mapperXml("mapper.xml")
    .controller("controller")
    .other("other")
    .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://"))//路径配置
    .build();

模板配置

new TemplateConfig.Builder()
    .disable(TemplateType.ENTITY)
    .entity("/templates/entity.java")
    .service("/templates/service.java")
    .serviceImpl("/templates/serviceImpl.java")
    .mapper("/templates/mapper.java")
    .mapperXml("/templates/mapper.xml")
    .controller("/templates/controller.java")
    .build();

在这里插入图片描述
注入配置

new InjectionConfig.Builder()
    .beforeOutputFile((tableInfo, objectMap) -> {
    System.out.println("tableInfo: " + tableInfo.getEntityName() + " objectMap: " + objectMap.size());
    })
    .customMap(Collections.singletonMap("test", "baomidou"))
    .customFile(Collections.singletonMap("test.txt", "/templates/test.vm"))
    .build();

在这里插入图片描述
策略配置

new StrategyConfig.Builder()
    .enableCapitalMode()
    .enableSkipView()
    .disableSqlFilter()
    .likeTable(new LikeTable("USER"))
    .addInclude("t_simple")
    .addTablePrefix("t_", "c_")
    .addFieldSuffix("_flag")
    .build();

Entity策略配置

new StrategyConfig.Builder()
    .entityBuilder()
    .superClass(BaseEntity.class)
    .disableSerialVersionUID()
    .enableChainModel()
    .enableLombok()
    .enableRemoveIsPrefix()
    .enableTableFieldAnnotation()
    .enableActiveRecord()
    .versionColumnName("version")
    .versionPropertyName("version")
    .logicDeleteColumnName("deleted")
    .logicDeletePropertyName("deleteFlag")
    .naming(NamingStrategy.no_change)
    .columnNaming(NamingStrategy.underline_to_camel)
    .addSuperEntityColumns("id", "created_by", "created_time", "updated_by", "updated_time")
    .addIgnoreColumns("age")
    .addTableFills(new Column("create_time", FieldFill.INSERT))
    .addTableFills(new Property("updateTime", FieldFill.INSERT_UPDATE))
    .idType(IdType.AUTO)
    .formatFileName("%sEntity")
    .build();

Service策略配置

new StrategyConfig.Builder()
    .serviceBuilder()
    .superServiceClass(BaseService.class)
    .superServiceImplClass(BaseServiceImpl.class)
    .formatServiceFileName("%sService")
    .formatServiceImplFileName("%sServiceImp")
    .build();

Controller策略配置

new StrategyConfig.Builder()
    .controllerBuilder()
    .superClass(BaseController.class)
    .enableHyphenStyle()
    .enableRestStyle()
    .formatFileName("%sAction")
    .build();

Mapper策略配置

new StrategyConfig.Builder()
    .mapperBuilder()
    .superClass(BaseMapper.class)
    .enableMapperAnnotation()
    .enableBaseResultMap()
    .enableBaseColumnList()
    .cache(MyMapperCache.class)
    .formatMapperFileName("%sDao")
    .formatXmlFileName("%sXml")
    .build();

CURD

Service CRUD 接口

通用 Service CRUD 封装IService (opens new window)接口,进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆

泛型 T 为任意实体对象

建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类
对象 Wrapper 为 条件构造器

在自动生成代码的基础上去实现增删查改

package gavin;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("gavin/mapper")//mapper扫描
public class ZzyApplication {

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

}

CURD方法

save方法

// 插入一条记录(选择字段,策略插入)
boolean save(T entity);
// 插入(批量)
boolean saveBatch(Collection<T> entityList);
// 插入(批量)
boolean saveBatch(Collection<T> entityList, int batchSize);
package com.zzy;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import gavin.ZzyApplication;
import gavin.entity.Dept;
import gavin.service.DeptService;
import org.junit.jupiter.api.Test;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.ArrayList;
import java.util.List;

@SpringBootTest(classes = ZzyApplication.class)
class ZzyApplicationTests {
@Autowired
    private DeptService deptService;
    @Test
    void contextLoads() {
        List<Dept> list = deptService.list();
        list.forEach(System.out::println);
    }
//    带条件查询
    @Test
    void contextLoads2() {
        //插入数据
        deptService.save(new Dept(50,"Manager","YT"));
        //批量插入数据
List<Dept> list= new ArrayList<Dept>(2);
list.add(new Dept(60,"Manager","YT"));
list.add(new Dept(70,"Manager","YTT"));
list.add(new Dept(80,"Manager","YTTT"));
//批量提交,默认1000
deptService.saveBatch(list);
//批量插入数据
List<Dept> depts= new ArrayList<Dept>(2);
depts.add(new Dept(90,"Manager","YT"));
depts.add(new Dept(100,"Manager","YTT"));
depts.add(new Dept(110,"Manager","YTTT"));
depts.add(new Dept(120,"Manager","YTTTT"));

//批量提交,每次提交2个
deptService.saveBatch(depts,2);
        List<Dept> list1 = deptService.list();
        list1.forEach(System.out::println);
    }

}

SaveOrUpdate
1,该方法要求有TableId注解,如果没有TableId主键注解,则报错com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: error: can not execute. because can not find column for id from entity!
2,根据是否有主键值来决定是执行更新还是存储,如果有则更新,否则就存储,
3,如果带有条件构造器,那么满足条件的执行,否则不执行;

// TableId 注解  存在更新记录,否插入一条记录
boolean saveOrUpdate(T entity);
// 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);

// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList);
// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

实践代码—>>

 @Test
    void contextLoads3() {
        //该方法要求有TableId注解,
        //根据是否有主键值来决定是执行更新还是存储,
        //如果有则更新,否则就存储,
       Dept dept = new Dept(180, "Manager", "黑龙江");
       deptService.saveOrUpdate(dept);
    }
      

原来数据—>>
在这里插入图片描述
执行后;
在这里插入图片描述
带有条件构造器的部分代码

        Dept dept1= new Dept(180, "Manager", "黑龙江齐齐哈尔");
        UpdateWrapper<Dept> wrappers= new UpdateWrapper();
        wrappers.ge("deptno",100);//相当于deptno>=100的,都更新了
        deptService.saveOrUpdate(dept1,wrappers);

结果—大于deptno>=100的都更新了
在这里插入图片描述
当没有大于100的纪录时,会将该值插入;


//        批量修改插入
        List<Dept> list= new ArrayList<Dept>(2);
     list.add(new Dept(300,"Clerk","QD"));//存在
     list.add(new Dept(400,"Clerk","QQD"));//不存在
     list.add(new Dept(500,"Clerk","QDD"));//不存在
        deptService.saveOrUpdateBatch(list);
        //        批量修改插入
        List<Dept> list1= new ArrayList<Dept>(2);
     list.add(new Dept(3000,"Clerk","QD"));//存在
     list.add(new Dept(4000,"Clerk","QQD"));//不存在
     list.add(new Dept(5000,"Clerk","QDD"));//不存在
        deptService.saveOrUpdateBatch(list,2);

运行结果
在这里插入图片描述
Remove

// 根据 entity 条件,删除记录
boolean remove(Wrapper<T> queryWrapper);
// 根据 ID 删除
boolean removeById(Serializable id);
// 根据 columnMap 条件,删除记录
boolean removeByMap(Map<String, Object> columnMap);
// 删除(根据ID 批量删除)
boolean removeByIds(Collection<? extends Serializable> idList);

实践代码–>

 @Test
    void contextLoads4() {
        Dept dept = new Dept(180, "Manager", "黑龙江");
        deptService.removeById(dept);
        
        UpdateWrapper<Dept> wrappers= new UpdateWrapper();
        wrappers.ge("deptno",100);
        deptService.remove(wrappers);
        List<Dept> list= new ArrayList<Dept>(2);
        list.add(new Dept(300,"Clerk","QD"));//存在
        list.add(new Dept(400,"Clerk","QQD"));//不存在
        list.add(new Dept(500,"Clerk","QDD"));//不存在
            deptService.removeBatchByIds(list,2)   ; 
       deptService.removeById(90);//可以直接根据主键删除
      //可以根据map来删除
         Map<String ,Object>map = new HashMap();
        map.put("deptno",70);
        deptService.removeByMap(map);
    }

在这里插入图片描述
Update

// 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
boolean update(Wrapper<T> updateWrapper);
// 根据 whereWrapper 条件,更新记录
boolean update(T updateEntity, Wrapper<T> whereWrapper);
// 根据 ID 选择修改
boolean updateById(T entity);
// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList);
// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList, int batchSize);

跟上面remove类似,实践代码略

Get

// 根据 ID 查询
T getById(Serializable id);
// 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")
T getOne(Wrapper<T> queryWrapper);
// 根据 Wrapper,查询一条记录
T getOne(Wrapper<T> queryWrapper, boolean throwEx);
// 根据 Wrapper,查询一条记录
Map<String, Object> getMap(Wrapper<T> queryWrapper);
// 根据 Wrapper,查询一条记录
<V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

List

// 查询所有
List<T> list();
// 查询列表
List<T> list(Wrapper<T> queryWrapper);
// 查询(根据ID 批量查询)
Collection<T> listByIds(Collection<? extends Serializable> idList);

// 查询(根据 columnMap 条件)
Collection<T> listByMap(Map<String, Object> columnMap);
// 查询所有列表
List<Map<String, Object>> listMaps();
// 查询列表
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
// 查询全部记录
List<Object> listObjs();
// 查询全部记录
<V> List<V> listObjs(Function<? super Object, V> mapper);
// 根据 Wrapper 条件,查询全部记录
List<Object> listObjs(Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录
<V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

实践代码–>

 @Test
    void contextLoad5() {
        QueryWrapper<Dept> wrappers= new QueryWrapper<>();
        Map<String ,Object>map = new HashMap();
        map.put("deptno",30);
// 查询(根据 columnMap 条件)
        List<Dept> list = deptService.listByMap(map);
        list.forEach(System.out::println);
/*
        Map<String ,Object>map1 = new HashMap();
        map.put("loc","YT");
        */
        System.out.println("------------------");
        List<Map<String, Object>> maps = deptService.listMaps(wrappers.gt("deptno",20));
        maps.forEach(System.out::println);
        
        deptService.listObjs(wrappers).forEach(System.out::println);
    }

Page

// 无条件分页查询
IPage<T> page(IPage<T> page);
// 条件分页查询
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
// 无条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page);
// 条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);

分页—实现类

public class Page<T> implements IPage<T> {
    private static final long serialVersionUID = 8545996863226528798L;
    protected List<T> records;
    protected long total;
    protected long size;
    protected long current;
    protected List<OrderItem> orders;
    protected boolean optimizeCountSql;
    protected boolean searchCount;
    protected boolean optimizeJoinOfCountSql;
    protected String countId;
    protected Long maxLimit;

实现分页功能

  @Test
    void contextLoads6() {
        Page<Dept> pageB= new Page<Dept>(2,2);//可以通过构造方法直接指定分页情况
        //  page.setSize(2); //可通过方法设置属性
        Page<Dept> page = deptService.page(pageB);
        List<Dept> list = page.getRecords();
//        list.forEach(System.out::println);
        for (Dept dept : list) {
            System.out.println(dept);
        }
        System.out.println("---------------");
        Map<String ,Object> map=  new HashMap<>();
        map.put("deptno",60);
        Page< Map<String ,Object>> page1=new Page<>(2,2);
        Page<Map<String, Object>> mapPage = deptService.pageMaps(page1);
        mapPage.getRecords().forEach(System.out::println);
        
    }

运行结果–
在这里插入图片描述
发现并没有分页的效果,debug一下发现,没有limit条件,
在这里插入图片描述

在这里插入图片描述
原来随着mybatisplus的本本更迭,需要配置interceptor来配合实现分页
如果是3.4.1版本的mubatisplus
做如下配置

@Configuration
@MapperScan("com.gavin.mapper")
public class MyInterceptorConfig {
       @Bean
       public PaginationInterceptor paginationInterceptor() {
           PaginationInterceptor paginationInterceptor = new PaginationInterceptor();                    paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
           return paginationInterceptor;
       }

最新版本有些改动----改成了PaginationInnerInterceptor


@Configuration
@MapperScan("com.gavin.mapper")
public class MyInterceptorConfig {

    // 最新版

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

}

在这里插入图片描述
Count

// 查询总记录数
int count();
// 根据 Wrapper 条件,查询总记录数
int count(Wrapper<T> queryWrapper);

略…

Chain

#query
// 链式查询 普通
QueryChainWrapper<T> query();
// 链式查询 lambda 式。注意:不支持 Kotlin
LambdaQueryChainWrapper<T> lambdaQuery(); 
  @Test
    void contextLoads7() {
        
        QueryChainWrapper<Dept> query = deptService.query()
        //        可以继续查询,这就类似于 stringbuffer.stringbuilder
       .groupBy("loc")
        .ge("deptno", 30)
         .in("deptno",40,50,60);
        BaseMapper<Dept> baseMapper = query.getBaseMapper();
        List<Dept> deptno = baseMapper.selectList(new QueryWrapper<Dept>().gt("deptno", 40));
        deptno.forEach(System.out::println);
        System.out.println("---------------");
        deptService.query().eq("loc","YT").list().forEach(System.out::println);
        System.out.println("---------------");
        LambdaQueryChainWrapper<Dept> dequery = deptService.lambdaQuery();
        dequery.eq(Dept::getDeptno,"YT").list().forEach(System.out::println);
    }

在这里插入图片描述
update


// 链式更改 普通
UpdateChainWrapper<T> update();
// 链式更改 lambda 式。注意:不支持 Kotlin 
LambdaUpdateChainWrapper<T> lambdaUpdate();

// 示例:
update().eq("column", value).remove();
lambdaUpdate().eq(Entity::getId, value).update(entity);

略…同链式查询

Mapper CRUD 接口说明:

通用 CRUD 封装BaseMapper (opens new window)接口,为 Mybatis-Plus 启动时自动解析实体表关系映射转换为 Mybatis 内部对象注入容器
泛型 T 为任意实体对象
参数 Serializable 为任意类型主键 Mybatis-Plus 不推荐使用复合主键约定每一张表都有自己的唯一 id 主键
对象 Wrapper 为 条件构造器

insert

// 插入一条记录
int insert(T entity);

实践代码…略

Delete

// 根据 entity 条件,删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 删除(根据ID 批量删除)
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 ID 删除
int deleteById(Serializable id);
// 根据 columnMap 条件,删除记录
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

实践代码

@Test
    void contextLoads8() {
deptMapper.delete(new QueryWrapper<Dept>().eq("deptno",80));
    }

Update

// 根据 whereWrapper 条件,更新记录
int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper);
// 根据 ID 修改
int updateById(@Param(Constants.ENTITY) T entity);

Select

// 根据 ID 查询
T selectById(Serializable id);
// 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 查询(根据ID 批量查询)
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 entity 条件,查询全部记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据 columnMap 条件)
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 根据 entity 条件,查询全部记录(并翻页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

mapper 层 选装件
说明:

选装件位于 com.baomidou.mybatisplus.extension.injector.methods 包下 需要配合Sql 注入器使用,案例(opens new window)
使用详细见源码注释(opens new window)

#AlwaysUpdateSomeColumnById

int alwaysUpdateSomeColumnById(T entity);

#insertBatchSomeColumn

int insertBatchSomeColumn(List<T> entityList);

#logicDeleteByIdWithFill

int logicDeleteByIdWithFill(T entity);

用service层操作是推荐的,mapper层操作不太推荐;

这里要学习
一下Wappers
Wappers是一个条件构造器,
AbstractWrapper

QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类
用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为

有一些curd条件
在这里插入图片描述

更多内容请查看---->>>
Wappers的API

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MybatisPlus 的相关文章

随机推荐

  • Nginx 使用 HTTPS(准备证书和私钥)

    文章目录 Nginx生成自签名证书和配置Nginx HTTPS 准备证书和私钥 准备证书和私钥 Nginx生成自签名证书和配置Nginx HTTPS 准备证书和私钥 准备证书和私钥 生成私钥 openssl genrsa des3 out
  • 网络RJ45接口详解

    RJ45 简介 图 1 RJ45模块 RJ45模块用于实现PHY之间的互连 包括PHY芯片经信号变压器与RJ45接口相连 如图 1所示 RJ45连接器由插头和插座组成 RJ45插头又称水晶头 如图 3 10所示 这两种元件组成的连接器连接于
  • mmdetection训练数据遇到的问题

    1 Permission denied bash compile sh Permission denied 没有操作权限 改为赋予最高权限 777 chmod 777 compile sh 2 cuda问题 unable to execut
  • 无锁同步-C++11之Atomic和CAS

    首页 联系 订阅 无锁同步 C 11之Atomic和CAS 1 概要 本文是无锁同步系列文章的第一篇 主要探讨C 11中的Atomic 我们知道在C 11中引入了mutex和方便优雅的lock guard 但是有时候我们想要的是性能更高的无
  • 合并单元格

    protected void gridView PreRender object sender EventArgs e MergeRows gridView 合並單元格 public static void MergeRows GridVi
  • QT 子线程 更改UI

    一 问题原因 QT和Android类似 不能在子线程中直接更新UI 否则会有崩溃 二 实现方式 方式有几种 我这里只说一种 使用 信号机制 三 原理 原理其实是信号槽机制 槽连接方式 有以下四种 如果未设置默认 auto 即可跨线程通信 D
  • 蓝桥杯单片机(十三)AT24C02(EEPROM)

    AT24C02是一个2K位串行CMOS E2PROM 内部含有256个8位字节 CATALYST公司的先进CMOS技术实质上减少了器件的功耗 AT24C02有一个16字节页写缓冲器 该器件通过IIC总线接口进行操作 有一个专门的写保护功能
  • TL431应用电路与LTspice仿真

    TL431应用电路与LTspice仿真 文章目录 TL431应用电路 简介 应用电路1 稳压源 应用电路2 电压比较器 应用电路3 隔离型反馈电路 LTspice仿真 模型导入和使用 spice模型 原理图符号 放置模型 暂态仿真 DC扫描
  • 最大公约数,最小公倍数,素数等问题

    1 两个数的 最小公倍数 等于两个数的乘积除以最大公约数 scm a b a b gcd a b 所以主要是最大公约数问题 gcd 问题 辗转相除法 依据就是欧几里得定理 gcd a b gcd b a b def gcd a b whil
  • Docker三剑客——Compose

    前面介绍了Docker三剑客中的两个 今天我们介绍一下三剑客中的最后一个 docker compose 接下来的内容 我们还是从五个方面来讲解 主要包括如下 Compose简介 Compose安装与卸载 Compose常用命令 Compos
  • LinearEyeDepth 推导过程

    转载自 冯乐乐的 Unity Shader 入门精要 获取深度和法线纹理 虽然在Unity里获取深度和法线纹理的代码非常简单 但是我们有必要在这之前首先了解它们背后的实现原理 深度纹理实际上就是一张渲染纹理 只不过它里面存储的像素值不是颜色
  • 谈谈前华为荣耀软件测试工程师校招面试(已拿到offer)

    截止到现在 一共参加了2次笔试 2次面试 具体时间参照截图 机试 一共三道编程题 共500分好像 全对了一道 就是提交后通过测试 另外两道写完了 但是提交测试没通过 性格测试 就正常选了 保证前后一致啊 因为前面问到的后面还会有 不然会提示
  • 英语学习频道

    1 美国在线 www aol com 2 美国白宫 www whitehouse gov 3 路透网 中文 http cn reuters com 英文 http www reuters com 4 环球网 中文 http www huan
  • JS实现贪吃蛇

    JS实现贪吃蛇 1 结构 创建一个盒子box作为蛇的身体 当前盒子中只有一个子元素 代表此时蛇的长度为1 在创建一个盒子food作为贪吃蛇的食物 div div div div div div 2 CSS 设置蛇和食物的样式 这里注意蛇和食
  • 一文带你理解@RefreshScope注解实现动态刷新原理

    概述 RefeshScope这个注解想必大家都用过 在微服务配置中心的场景下经常出现 他可以用来刷新Bean中的属性配置 那大家对他的实现原理了解吗 它为什么可以做到动态刷新呢 注解的作用 RefreshScope注解是Spring Clo
  • linux 服务器的node 安装

    1 先下载 node 当然下载的是符合linux 系统的 我当时下载的是 node v16 18 0 linux x64 tar xz 2 把 node v16 18 0 linux x64 tar xz 放到linux 服务器上 我当时是
  • select poll epoll iocp kqueue

    为什么epoll kqueue比select高级 答案是 他们无轮询 因为他们用callback取代了 想想看 当套接字比较多的时候 每次select 都要通过遍历FD SETSIZE个Socket来完成调度 不管哪个Socket是活跃的
  • Go 小项目1 - 家庭收支记账软件

    Go 小项目1 家庭收支记账软件 一 需求 1 模拟实现基于文本界面的 家庭记账软件 2 该软件能够记录家庭的收入 支出 并能够打印收支明细表 二 编码 1 功能 1 完成主菜单的编写 2 显示明细和登记收入 3 登记支出 三 实现 实现1
  • linux shell脚本使用echo输出空行与空格

    使用echo输出空行与空格到文件中 输出空行 echo gt gt temp 自动换行 输出空格 echo n gt gt temp 不换行 插入换行符 echo n gt gt file
  • MybatisPlus

    MybatisPlus特点 MyBatis Plus是MyBatis的强大增强工具 它为MyBatis提供了许多有效的操作 你可以从MyBatis无缝切换到MyBatis Plus MyBatis Plus可以自动注入基本的SQL片段 My