一篇文章了解mybatis-plus 3.x

MyBaits-Plus 3.X

一.简述

1. 简介

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LOJKsQJp-1575440845273)(en-resource://database/7135:1)]

2. 特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 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 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作#支持数据库

二.快速启动

1. Spring Boot

  1. 添加依赖
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.2.0</version>
</dependency>
  1. 配置:启动类添加 MapperScan 注解
@SpringBootApplication
//启动类添加此注解
@MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(QuickStartApplication.class, args);
    }
}
  1. 测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class SampleTest {

    @Resource
    private UserMapper userMapper;

    @Test
    public void testSelect() {
        System.out.println(("----- selectAll method test ------"));
        List<User> userList = userMapper.selectList(null);
        Assert.assertEquals(5, userList.size());
        userList.forEach(System.out::println);
    }
}

2. Spring MVC

  1. 添加依赖
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus</artifactId>
    <version>3.2.0</version>
</dependency>
  1. 配置
  • spring配置文件中配置MapperScan
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.baomidou.mybatisplus.samples.quickstart.mapper"/>
</bean>
  • mybatis配置文件中,修改 SqlSessionFactoryMyBatis-PlusSqlSessionFactory
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
</bean>
  1. 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:spring.xml"})
public class SampleTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelect() {
        System.out.println(("----- selectAll method test ------"));
        List<User> userList = userMapper.selectList(null);
        Assert.assertEquals(5, userList.size());
        userList.forEach(System.out::println);
    }

    @Test
    public void testSequence() {
        User u = new User();
        u.setName("Tomcat");
        u.setAge(18);
        u.setEmail("[email protected]");
        userMapper.insert(u);

        Long id1 = u.getId();
        u = new User();
        u.setName("Tomcat2");
        userMapper.insert(u);
        Assert.assertEquals("id should increase 1", id1 + 1, u.getId().longValue());
    }

    @Test
    public void testCustomizedSql() {
        System.out.println("maxAge=" + userMapper.selectMaxAge());
    }

    @Test
    public void testPagination() {
        Page<User> page = new Page<>(1, 3);
        userMapper.selectPage(page, null);
        Assert.assertTrue("total should not be 0", page.getTotal() != 0);
        for (User u : page.getRecords()) {
            System.out.println(u);
        }
        Assert.assertEquals("pagination should be 3 per page", 3, page.getRecords().size());
    }

}

三.核心功能

1. 代码生成器

官网链接:代码生成器

  1. 作用:基于模板生成代码
  2. 支持模板类型
    • Velocity(默认)
    • Freemarker
    • Beetl
    • 自定义模板

2. CRUD接口

官网链接:CRUD接口

  1. mapper层接口
//1. 继承BaseMapper,泛型中传入这个表的实体类
//2. 在其他地方通过@Autowire注入即可
public interface UserMapper extends BaseMapper<User> {}
  1. service层接口
//1. 继承IService,泛型中传入这个表的实体类
//2. 在其他地方通过@Autowire注入即可
public interface UserServcie extends IService<User> {}
  1. 参数
参数 作用
T entity 保存的时候用
IPage page 分页(需要配置分页插件)
wrapper(条件构造器) 用于生成sql的where条件
Collection entityList 批量保存或者更新
Collection<? extends Serializable> idList 批量删除
Map<String, Object> 查询的时候用

3. 条件构造器

官网链接: 条件构造器

  1. 作用:用于生成sql的where条件
  2. api
查询方式 说明
setSqlSelect 设置 SELECT 查询字段
where WHERE 语句,拼接 + WHERE 条件
and AND 语句,拼接 + AND 字段=值
andNew AND 语句,拼接 + AND (字段=值)
or OR 语句,拼接 + OR 字段=值
orNew OR 语句,拼接 + OR (字段=值
eq 等于=
allEq 基于 map 内容等于=
ne 不等于<>
gt 大于>
ge 大于等于>=
lt 小于<
le 小于等于<=
like 模糊查询 LIKE
notLike 模糊查询 NOT LIKE
in IN 查询
notIn NOT IN 查询
isNull NULL 值查询
isNotNull IS NOT NULL
groupBy 分组 GROUP BY
having HAVING 关键词
orderBy 排序 ORDER BY
orderAsc ASC 排序 ORDER BY
orderDesc DESC 排序 ORDER BY
exists EXISTS 条件语句
notExists NOT EXISTS 条件语句
between BETWEEN 条件语句
notBetween NOT BETWEEN 条件语句
addFilter 自由拼接 SQL
last 拼接在最后,例如:last(“LIMIT 1”)
  1. 参数
参数 作用
boolean condition 表示该条件是否加入最后生成的sql中
R column 表示数据库字段(String类型)
T entity 查询的时候把entity需要查询的字段set好,然后传入,相当于where and and。。。
Collection entityList 批量保存或者更新
Collection<? extends Serializable> idList 批量删除
Map<String, Object> 和实体类作用差不多
String sql 用来拼接sql
  1. 注意:
    • 不支持 rpc调用Wrapper 进行传输(wrapper很重,非要用可以写dto)
    • 传入的map或者list为null,则不加入最后sql中
    • sql拼接有两种:applylast ,其中last 有sql注入风险
  2. 分类
    • AbstractWrapper :QueryWrapperUpdateWrapper 的父类
    • QueryWrapper :比 AbstractWrapper 多一个 select(String... sql) 方法
    • UpdateWrapper :比 AbstractWrapper 多一个 set(String column , Object val) 方法
  3. 使用wrapper自定义sql
    1. 作用:在自定义sql中使用构造器
    2. 方式:
      1. 注解方式
      2. xml方式

4. 分页插件

官网链接: 分页插件

  1. 配置
  • spring mvc
<!-- spring xml 方式 -->
<plugins>
    <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
        <property name="sqlParser" ref="自定义解析类、可以没有" />
        <property name="dialectClazz" value="自定义方言类、可以没有" />
    </plugin>
</plugins>
  • spring boot

//Spring boot方式
@EnableTransactionManagement
@Configuration
@MapperScan("com.baomidou.cloud.service.*.mapper*")
public class MybatisPlusConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        // paginationInterceptor.setOverflow(false);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        // paginationInterceptor.setLimit(500);
        return paginationInterceptor;
    }
}
  1. 使用
  • 测试类
   @Resource
    private UserMapper userMapper;
    @Test
    public void queryUserForPage(){
        IPage<User> userPage = new Page<>(2, 2);//参数一是当前页,参数二是每页个数
        userPage = userMapper.selectPage(userPage, null);
        List<User> list = userPage.getRecords();
        for(User user : list){
            System.out.println(user);
        }
    }
  • 向前端返回json

//----------------------包装类,用来保存分页需要的数据------------------------
@Data
public class UserVo {
    private Integer current;
    private Integer size;
    private Long total;
    private List<User> userList;
}
//--------------------------------Controller返回---------------------------------
   @GetMapping("queryUser")
   @ResponseBody
    public UserVo queryList(Integer current, Integer size) {
        /**
         * 这些代码应该写在service层
         */
        UserVo userVo = new UserVo();
        IPage<User> page = new Page<>(current, size);
        userMapper.selectPage(page, null);
        userVo.setCurrent(current);
        userVo.setSize(size);
        userVo.setTotal(page.getTotal());
        userVo.setUserList(page.getRecords());
        return userVo;
    }

5. Sequence主键

官网链接: Sequence主键

  1. 作用:唯一主键,保证全局不重复
  2. 步骤
  • 实体类添加两个注解:@KeySequence@TableId(value = "id", type = IdType.INPUT)
    • oracle 这里是 inputmysqlauto
@Data
@KeySequence("SEQ_USER")
public class User {
    
    @TableId(value = "id", type = IdType.INPUT)
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
  • 添加配置
@Configuration
public class MybatisPlusConfig {
    /**
     * sequence主键,需要配置一个主键生成器
     * 配合实体类注解 {@link KeySequence} + {@link TableId} type=INPUT
     * @return
     */
    @Bean
    public H2KeyGenerator h2KeyGenerator(){
        return new H2KeyGenerator();
    }
}

四.扩展功能

1. 逻辑删除

官网链接: 逻辑删除

  1. 效果

    1. 删除时:update user set deleted=1 where id =1 and deleted=0
    2. 查找时:select * from user where deleted=0
  2. 添加配置:如果与默认配置相同,则不需要配置

mybatis-plus:
  global-config:
    db-config:
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
  1. 实体类添加字段配置
@TableLogicprivate 
Integer deleted;

2. 自动填充

官网链接: 自动填充

  1. 实体类添加注解
    /**
     * 创建时间
     */
    @TableField(fill=FieldFill.INSERT)
    private Date gmtCreate;
    /**
     * 修改时间
     */
    @TableField(fill=FieldFill.INSERT_UPDATE)
    private Date gmtModified;
  1. 定义处理类
public class MybatisObjectHandler extends MetaObjectHandler{

    @Override
    public void insertFill(MetaObject metaObject) {
        //新增时填充的字段
        setFieldValByName("gmtCreate", new Date(), metaObject);
        setFieldValByName("gmtModified", new Date(), metaObject);

    }

    @Override
    public void updateFill(MetaObject metaObject) {
        //更新时 需要填充字段
        setFieldValByName("gmtModified", new Date(), metaObject);
    }
}
  1. 添加全局配置
<!----------------------------------------------xml配置------------------------------------------------->
<bean id="globalConfig" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
    <!--
        AUTO->`0`("数据库ID自增")QW
         INPUT->`1`(用户输入ID")
        ID_WORKER->`2`("全局唯一ID")
        UUID->`3`("全局唯一ID")
    -->
    <property name="idType" value="2" />
    <property name="metaObjectHandler" ref="mybatisObjectHandler"></property>
</bean>

<bean id="mybatisObjectHandler" class="cn.lqdev.learning.mybatisplus.samples.config.MybatisObjectHandler"/>
//------------------------------------------------srpingboot配置------------------------------------------------
public MybatisSqlSessionFactoryBean sqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws IOException {
        MybatisSqlSessionFactoryBean mybatisPlus = new MybatisSqlSessionFactoryBean();

        //加载数据源
        mybatisPlus.setDataSource(dataSource);
        
        //全局配置
        GlobalConfig globalConfig  = new GlobalConfig();
        //配置填充器
        globalConfig.setMetaObjectHandler(new MetaObjectHandlerConfig());
        mybatisPlus.setGlobalConfig(globalConfig);
        
        return mybatisPlus;
    }

3. sql注入器

官网链接:sql注入器

  1. 创建自定义sql方法类 MyInsertAll,继承 AbstractMethod
public class MyInsertAll extends AbstractMethod {

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        String sql = "insert into %s %s values %s";
        StringBuilder fieldSql = new StringBuilder();
        fieldSql.append(tableInfo.getKeyColumn()).append(",");
        StringBuilder valueSql = new StringBuilder();
        valueSql.append("#{").append(tableInfo.getKeyProperty()).append("},");
        tableInfo.getFieldList().forEach(x->{
            fieldSql.append(x.getColumn()).append(",");
            valueSql.append("#{").append(x.getProperty()).append("},");
        });
        fieldSql.delete(fieldSql.length()-1, fieldSql.length());
        fieldSql.insert(0, "(");
        fieldSql.append(")");
        valueSql.insert(0, "(");
        valueSql.delete(valueSql.length()-1, valueSql.length());
        valueSql.append(")");
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, String.format(sql, tableInfo.getTableName(), fieldSql.toString(), valueSql.toString()), modelClass);
        return this.addInsertMappedStatement(mapperClass, modelClass, "myInsertAll", sqlSource, new NoKeyGenerator(), null, null);
    }
}
  1. 创建sql注入类,继承MyLogicSqlInjector ,并继承 DefaultSqlInjector , 复写 getMethodList 方法;并配置
public class MyLogicSqlInjector extends DefaultSqlInjector {

    /**
     * 如果只需增加方法,保留MP自带方法
     * 可以super.getMethodList() 再add
     * @return
     */
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
        methodList.add(new MyInsertAll());
        return methodList;
    }
}
  1. 编写自定义BaseMapper接口 MyBaseMapper<T> 继承 BaseMapper<T>,添加自定义sql方法
public interface MyBaseMapper<T> extends BaseMapper<T> {
    /**
     * 自定义通用方法
     */
    int myInsertAll(T entity);
}
  1. 业务层mapper继承自定义baseMapper MyBaseMapper
public interface UserMapper extends MyBaseMapper<User> { }
  1. 使用
@RunWith(SpringRunner.class)
@SpringBootTest
public class DeluxeTest {
    
    @Resource
    private UserMapper mapper;

    @Test
    public void myInsertAll(){
        long id =1008888L;
        User u = new User().setEmail("[email protected]").setVersion(1).setDeleted(0).setId(id);
        mapper.myInsertAll(u);

        User user = mapper.selectById(id);
        Assert.assertNotNull(user);
        Assert.assertNotNull(user.getCreateTime());
    }
}
   

4. 动态数据源

官网链接:动态数据源

  1. 添加依赖
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  <version>${version}</version>
</dependency>
  1. 添加配置
spring:
  datasource:
    dynamic:
      datasource:
        master:
          username: sa
          password: ""
          url: jdbc:h2:mem:test
          driver-class-name: org.h2.Driver
          schema: db/schema.sql
        slave_1:
          username: sa
          password: ""
          url: jdbc:h2:mem:test
          driver-class-name: org.h2.Driver
        slave_2:
          username: sa
          password: ""
          url: jdbc:h2:mem:test
          driver-class-name: org.h2.Driver
        slave_3:
          username: sa
          password: ""
          url: jdbc:h2:mem:test
          driver-class-name: org.h2.Driver

在这里插入图片描述

  1. serviceImpl或者mapper接口的方法上添加依赖@DS
@Service
public class UserServiceImpl implements UserService {

  @Resource
  private UserMapper userMapper;

  //不配置,就是默认数据源
  @Override
  public void addUser(User user) {
    userMapper.addUser(user.getName(), user.getAge());
  }

  @DS("slave_1")
  @Override
  public List selectUsersFromDs() {
    return userMapper.selectUsers();
  }

  @DS("slave")
  @Override
  public List selectUserFromDsGroup() {
    return userMapper.selectUsers();
  }
}

5. ActiveRecord 模式

  • 实体类 继承 Model类 即可使用CRUD接口
/**
 * 通用CURD示例
 * @author oKong
 *
 */
@RunWith(SpringRunner.class)
//SpringBootTest 是springboot 用于测试的注解,可指定启动类或者测试环境等,这里直接默认。
@SpringBootTest 
@Slf4j
public class GeneralTest {

    @Autowired
    IUserService userService;

    @Test
    public void testInsert() {
        User user = new User();
        user.setCode("001");
        user.setName("okong-insert");
        //默认的插入策略为:FieldStrategy.NOT_NULL,即:判断 null
        //对应在mapper.xml时写法为:<if test="field!=null">
        //这个可以修改的,设置字段的@TableField(strategy=FieldStrategy.NOT_EMPTY)
        //所以这个时候,为null的字段是不会更新的,也可以开启性能插件,查看sql语句就可以知道
        userService.insert(user);

        //新增所有字段,
        userService.insertAllColumn(user);
        log.info("新增结束");
    }

    @Test
    public void testUpdate() {

        User user = new User();
        user.setCode("101");
        user.setName("oKong-insert");
        //这就是ActiveRecord的功能
        user.insert();
        //也可以直接 userService.insert(user);

        //更新
        User updUser = new User();
        updUser.setId(user.getId());
        updUser.setName("okong-upd");

        updUser.updateById();
        log.info("更新结束");
    }

    @Test
    public void testDelete() {
        User user = new User();
        user.setCode("101");
        user.setName("oKong-delete");

        user.insert();

        //删除
        user.deleteById();
        log.info("删除结束");

    }

    @Test
    public void testSelect() {
        User user = new User();
        user.setCode("201");
        user.setName("oKong-selecdt");

        user.insert();

        log.info("查询:{}",user.selectById());
    }
}

6. 乐观锁

官网链接:乐观锁

当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式:

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败
  1. 添加配置
<!--------------------------------xml方式配置--------------------------------->
<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>
//--------------------------------springboot配置-----------------------------------
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
    return new OptimisticLockerInterceptor();
}
  1. 注解实体类中代表version字段(表中也要有)
@Version
private Integer version;
发布了96 篇原创文章 · 获赞 26 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq919694688/article/details/103386347