MyBatisPlus

MyBatisPlus

1、MyBatisPlus概述

​ MyBatis-Plus 是 MyBatis 的一个插件,它是在 MyBatis 的基础上进行了扩展和增强,目的是简化数据访问层的开发以及提高代码的可维护性,同时也提供了很多便捷的功能。

  1. 自动生成 MyBatis 的 mapper 接口以及对应的实现类。
  2. 提供了一些通用的 CRUD 方法,如插入、更新、删除、查询等。
  3. 支持 Lambda 表达式查询。
  4. 提供了分页查询的功能。
  5. 支持主键自动生成。
  6. 提供了代码生成器,可以快速生成代码。

主要特点:

  1. 无侵入:MyBatisPlus只是做增强,不做改变,为简化开发、提高效率提供了很多强大的功能,如自动化SQL注入、分页插件等。

  2. 便捷的CRUD操作:提供了强大且灵活的CRUD操作,包括一些常用的如插入、删除、更新和查询操作,使得CRUD操作更加简单。

  3. 多种主键策略:支持多种数据库主键生成策略,并默认自动识别数据库类型选择生成策略。

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

  5. 支持自定义全局通用操作:基于MyBatis的SQL注入原理,提供了全局通用的操作方法。

  6. 内置代码生成器:可快速生成Mapper、Model、Service、Controller层代码,提高开发效率。

  7. 内置分页插件:基于MyBatis物理分页,无需关心具体操作,你的接口直接接收一个Page对象,简单方便。

  8. 支持自定义全局通用方法:即领域模型,基于MyBatis的SQL注入原理,提供了全局通用的操作方法。

  9. 多租户SQL解析器:对代码零侵入,只需编写少量配置即可实现多租户表数据隔离。

  10. 动态表名SQL解析器:实现了动态表名的替换,非常适合多租户场景中根据用户隔离数据。

以上这些特点使得MyBatisPlus在Java持久层框架中具有很高的易用性和灵活性。

image-20230731095432802

框架结构(官网图)

sfdf

2、快速入门

快速开始 | MyBatis-Plus (baomidou.com)官网教程。简单明了。

1、先创建一张user表

CREATE TABLE user
(
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
    age INT(11) NULL DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
    PRIMARY KEY (id)
);

-- 真实开发中还要有,version(乐观锁)、deleted(逻辑排除)、gmt_create(创建时间)、gmt_modified(修改时间)

2、然后插入数据

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

image-20230731102952263

2.1、创建基础项目

3、新建spring boot项目,web模块、数据库驱动、lombok依赖都选上

fszdfsadf

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.0.5</version>
    </dependency>
</dependencies>

说明:使用ybatis-plus可以节省我们大量的代码,尽量不要同时导入mybatis和mybatis-plus,会有版本依赖冲突。

4、链接数据库,和mybatis一样

到application.properties配置数据库数据源

#配置数据源
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatisplus?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456

按照传统的方式接下来需要pojo—>dao(mapper配置,mapper接口,mapper.xml的增删改查)—>service—>controller

现在在dao层不需要那么麻烦

5、使用mybatis-plus之后只需要

  • pojo写好

    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private Long id;
        private String name;
        private int age;
        private String email;
    }
    
  • mapper接口,继承BaseMapper(CRUD都是它帮我们写了)

    //BaseMapper<User>泛型中传递的User后面的增删改查都基于user
    @Repository  //代表持久层
    public interface UserMapper extends BaseMapper<User> {
        //此时所有的crud已经结束,不需要配置很多文件了
    
    }
    
  • 测试,需要在主启动类上加上注解扫描到mapper接口

    //扫描mapper包
    @MapperScan("com.zm.mapper")
    @SpringBootApplication
    public class MybatisPlus01Application {
    
        public static void main(String[] args) {
            SpringApplication.run(MybatisPlus01Application.class, args);
        }
    
    }
    
    //单元测试,查出所有的用户
       @Autowired
        private UserMapper userMapper;
        @Test
        void contextLoads() {
            //selectList的参数为wapper是查询的条件,写为null就是无条件查询
            List<User> users = userMapper.selectList(null);
            for (User user : users) {
                System.out.println("user = " + user);
            }
        }
    
    

结果查询成功

image-20230731113030774

3、配置日志

​ 由于现在的sql看不见,所有我们需要打开日志看它是怎么执行的。

#配置输出日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

image-20230731145030888

4、CRUD扩展

4.1、insert 插入

测试中新加方法testInsert

//测试插入
@Test
public void testInsert(){
    User user = new User();
    user.setAge(18);
    user.setName("张三");
    user.setEmail("123456789@qq.com");
    int i = userMapper.insert(user);
    System.out.println("返回结果 = " + i);
    System.out.println(user);
}

我们并没有给它插入id但是它又自动生成了一个id,id会自动回填。

sadfasdfsdaf

数据库插入的id的默认值是全局唯一的id

4.2、主键生成策略,雪花算法

数据库中的主键(uuid,自增id,雪花算法)

​ 雪花算法(Snowflake Algorithm)是Twitter开源的一种分布式系统中生成唯一ID的解决方案。它是一种全局唯一ID生成器,特别适合分布式系统中生成唯一的订单号、商品ID等。雪花算法可以根据时间有序生成ID。

雪花算法生成的ID是一个64位的整数,由以下几部分组成:

  • 时间戳:41位,用于记录时间戳,精确到毫秒。
  • 工作机器id:10位,包括5位数据中心ID和5位机器ID,用于标识生成ID的机器。
  • 序列号:12位,用于记录同一毫秒内生成的不同ID。

​ 这种结构保证了每毫秒可以生成大约4096个唯一ID,而且因为有时间戳和工作机器id的存在,这些ID在全局都是唯一的。

​ 雪花算法的优点是生成的ID有序,且性能高,可以满足高并发的需求。但是,因为依赖时间,如果系统时间被调整,可能会导致生成的ID重复。

默认的ID_WORK全局唯一id

image-20230731152423737

这是MyBatis Plus中的一个枚举,用于指定主键生成策略。各个枚举值的含义如下:

  1. AUTO(0):数据库ID自增。数据库会自动为每个新记录分配一个唯一的ID值。这需要数据库支持自增主键,例如MySQL的AUTO_INCREMENT。

  2. NONE(1):无状态,该类型为未设置主键类型。这意味着你需要自己手动为每个新记录分配一个唯一的ID。

  3. INPUT(2):手动输入。这意味着你需要在插入数据时手动为每个新记录提供一个唯一的ID。

  4. ==ID_WORKER(3):全局唯一ID,Long类型的主键。使用Twitter的雪花算法(Snowflake Algorithm)生成全局唯一ID。==它也是mybais-plus默认的主键生成策略

  5. UUID(4):全局唯一ID,字符串类型的主键。使用UUID算法生成全局唯一ID。

  6. ID_WORKER_STR(5):全局唯一ID,字符串类型的主键。使用Twitter的雪花算法生成全局唯一ID,但是生成的ID会被转换为字符串。

实体类加上注解指定id字段使用数据库自增的方式生成主键。

public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private int age;
    private String email;
}

数据库的id字段把自增选上

image-20230731152637608

然后现在去执行同样的测试id就会在上一个的id基础上加一,上一个末尾两位是33,这一个是34.

image-20230731152914211

现在改成input ``@TableId(type = IdType.INPUT)重新测试,我们自己插入id为6

image-20230731164357902

4.3、update更新

测试更新

//测试更新
@Test
public void testUpdate(){
    User user = new User();
    //通过条件自动拼接动态sql
    user.setAge(18);
    user.setName("张三PLUS");
    user.setId(6L);
    //注意此时的更新虽然是ById但是参数是user
    int i = userMapper.updateById(user);
    System.out.println(i);
    System.out.println("user = " + user);
}

image-20230731164617766

image-20230731164651259

所有的sql都是自动帮你动态配置的

4.4、自动填充

创建时间,修改时间,这些操作都是需要自动化完成的。

所有的数据库表都要有gmt_created、gmt_modified,而且都得是自动化

数据库级别(工作中不允许随便修改数据库)

在表中新增字段create_time,update_time 默认时间CURRENT_TIMESTAMP

11598441544

同步一下实体类

public class User {
    @TableId(type = IdType.INPUT)
    private Long id;
    private String name;
    private int age;
    private String email;
    private Date createTime;
    private Date updateTime;
    
}

测试一个更新操作

image-20230731171535967

代码级别

把刚才的默认时间CURRENT_TIMESTAMP删除。

image-20230731171708597

实体类字段上需要添加注解

//字段添加填充内容
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;

编写填充处理器来处理这个注解MyMetaObjectHandler

注意事项:

  • 填充原理是直接给entity的属性设置值!!!
  • 注解则是指定该属性在对应情况下必有值,如果无值则入库会是null
  • MetaObjectHandler提供的默认方法的策略均为:如果属性有值则不覆盖,如果填充值为null则不填充
  • 字段必须声明TableField注解,属性fill选择对应策略,该声明告知Mybatis-Plus需要预留注入SQL字段
  • 填充处理器MyMetaObjectHandler在 Spring Boot 中需要声明@Component@Bean注入
  • 要想根据注解FieldFill.xxx字段名以及字段类型来区分必须使用父类的strictInsertFill或者strictUpdateFill方法
  • 不需要根据任何来区分可以使用父类的fillStrategy方法
  • update(T t,Wrapper updateWrapper)时t不能为空,否则自动填充失效
  • 当自定义mapper方法需要走填充时,建议按下列注解方式添加参数注解(如果使用编译参数保留的情况下,变量名字与注解名字保持一致也行)

image-20230731173155378

@Component  //加载到ioc容器中
public class MyMetaObjectHandler implements MetaObjectHandler {
    //插入时的填充策略
    @Override
    public void insertFill(MetaObject metaObject) {
        System.out.println("执行插入填充策略-------》");
        this.setFieldValByName("createTime",new Date(),metaObject);
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
    //更新时的填充策略
    @Override
    public void updateFill(MetaObject metaObject) {
        System.out.println("执行更新填充策略-------》");
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}

测试插入一个用户,然后再修改它

//测试插入
@Test
public void testInsert(){
    User user = new User();
    user.setAge(18);
    user.setName("李四");
    user.setEmail("123456789@qq.com");
    int i = userMapper.insert(user);
    System.out.println("返回结果 = " + i);
    System.out.println(user);
}
 //测试更新
    @Test
    public void testUpdate(){
        User user = new User();
        //通过条件自动拼接动态sql
        user.setAge(20);
        user.setName("张三PLUS");
        user.setId(1685908219644792838L);
        //注意此时的更新虽然是ById但是参数是user
        int i = userMapper.updateById(user);
        System.out.println(i);
        System.out.println("user = " + user);
    }

插入之后

image-20230731174143697

再修改,发现只有修改时间updateTime的时间变化了

image-20230731174501028

4.5、乐观锁处理

​ 乐观锁是一种在读取数据时不会立即对数据进行加锁的并发控制机制。它的基本思想是假设多个事务在并发执行时不会彼此冲突,因此在执行操作时不会立即进行加锁,而是在准备更新数据时才检查在此期间是否有其他事务也修改了这些数据。

​ 乐观锁通常使用数据版本(Version)记录机制实现。在读取数据时,会同时读取出数据版本。在提交更新时,会检查数据版本是否发生变化。如果数据版本没有变化,说明在此期间没有其他事务修改过数据,那么就可以提交更新。如果数据版本发生了变化,说明在此期间有其他事务修改过数据,那么就需要回滚当前的事务,并根据具体的策略决定是重试还是放弃操作。

​ 乐观锁适用于读多写少的应用场景,因为在这种场景下,事务之间的冲突概率较低,使用乐观锁可以减少锁的开销,提高并发性能。但是,如果在一个高并发的写多的场景下使用乐观锁,可能会导致大量的事务冲突,从而导致大量的事务需要回滚和重试,这会降低性能。


​ 悲观锁是一种在读取数据时就会立即对数据进行加锁的并发控制机制。它的基本思想是假设多个事务在并发执行时可能会彼此冲突,因此在执行操作时就需要立即进行加锁,以防止其他事务同时修改数据。

​ 悲观锁通常使用数据库提供的锁机制实现,例如行锁或表锁。在读取数据时,会立即对数据加锁,防止其他事务修改数据。在事务完成后,会释放锁,允许其他事务访问数据。

​ 悲观锁适用于写多读少的应用场景,因为在这种场景下,事务之间的冲突概率较高,使用悲观锁可以确保数据的一致性。但是,悲观锁的开销较大,因为需要频繁地进行加锁和解锁操作,这会降低并发性能。此外,如果不正确地使用悲观锁,可能会导致死锁。

​ 与乐观锁相比,悲观锁提供了更强的数据一致性保证,但是并发性能较低。在选择使用乐观锁还是悲观锁时,需要根据应用的具体需求和场景进行权衡。

当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:

  • 取出记录时,获取当前 version
  • 更新时,带上这个 version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果 version 不对,就更新失败

乐观锁配置需要两步

1、配置插件(官网方法)

  • springxml方式

    <bean class="com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor" id="optimisticLockerInnerInterceptor"/>
    
    <bean id="mybatisPlusInterceptor" class="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor">
        <property name="interceptors">
            <list>
                <ref bean="optimisticLockerInnerInterceptor"/>
            </list>
        </property>
    </bean>
    
  • springboot注解方式

    // Spring Boot 方式
    @Configuration
    @MapperScan("按需修改")
    public class MybatisPlusConfig {
        /**
         * 旧版
         */
        @Bean
        public OptimisticLockerInterceptor optimisticLockerInterceptor() {
            return new OptimisticLockerInterceptor();
        }
    
        /**
         * 新版
         */
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
            mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
            return mybatisPlusInterceptor;
        }
    }
    

2、在实体类字段上加上@Version注解

先在表中新加version字段,并设置初始值为1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Hc4l4TO-1690885684543)(https://s2.loli.net/2023/08/01/Mt6QrClmTYKNdz8.png)]

@Version
private Integer version;

编写插件配置类,这里使用的是旧版的

//扫描mapper包
@MapperScan("com.zm.mapper")
@EnableTransactionManagement
@Configuration
public class MyMybatisConfig {
    /**
     * 旧版
     */
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
}

测试乐观锁

测试成功的案例,单线程没有其他的干扰正常情况下可以成功

//单线程乐观锁成功案例
@Test
public void testVersion(){
    //1 获取用户信息
    User user = userMapper.selectById(1L);
    //2 修改用户信息
    user.setName("乐观锁测试");
    //3 执行更新操作
    userMapper.updateById(user);
    
}

此时更新的时候就把乐观锁带上了

image-20230801094940215

​ 测试失败的案例,模拟多线程,会被插队执行同一个数据,线程2抢先执行更新操作的话,此时的version的值变成2了,线程1再去执行更新,更新时的sql中version值不符合条件就无法执行成功。如果此时没有乐观锁存在,线程2修改的数据就会被线程1给覆盖掉。

//模拟多线程乐观锁失败案例
@Test
public void testVersion2(){
    //线程1
    User user = userMapper.selectById(1L);
    user.setName("乐观锁测试11");

    //线程2抢先修改了
    User user2 = userMapper.selectById(1L);
    user2.setName("乐观锁测试22");
    userMapper.updateById(user2);
    //此时线程1的修改就失败了,如果没有乐观锁的话,线程2修改的值会被线程1覆盖掉
    userMapper.updateById(user);

}

4656

image-20230801100456867

4.6、查询操作

查询单个

//单个查询
@Test
public void selectUserByid(){
    System.out.println(userMapper.selectById(1L));
}

批量查询多个

//批量查询多个
@Test
public void select2(){
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
    users.forEach(System.out::println);
}

可以看到sql条件使用的就是IN

image-20230801102020814

条件查询简单的使用map封装条件

//条件查询使用map操作
@Test
public void select3(){
    HashMap<String, Object> map = new HashMap<>();
    //查询名字为张三,年龄为18的用户
    map.put("name","张三");
    map.put("age",18);
    List<User> users = userMapper.selectByMap(map);
    System.out.println(users);
}

image-20230801102841974

分页查询
  • 原始的limit分页
  • pageHelper第三方插件
  • MyBatisPlus内置分页插件

使用MyBatisPlus内置分页插件

1、配置拦截器组件

在MyMybatisConfig中配置

//分页插件
@Bean
public PaginationInterceptor paginationInterceptor(){
    return new PaginationInterceptor();
}

2、直接使用Page对象

测试

//分页查询
@Test
public void page(){
    Page<User> page = new Page<>(1,3);  //参数1是当前页,参数2是页面大小
    userMapper.selectPage(page,null);
    page.getRecords().forEach(System.out::println);
}

image-20230801111631381

改为第二页

5131315

page.getTotal(); //统计有多少条数据,使用了count函数

image-20230801112036618

4.7、删除操作,逻辑删除

删除之前的数据

16546146416

删除单个,13号

//删除单个
@Test
public void delete1(){
    userMapper.deleteById(13L);
}

image-20230801153834626

image-20230801154025620

批量删除多个

//删除多个
@Test
public void delete2(){
    int i = userMapper.deleteBatchIds(Arrays.asList(10, 11, 12));
    System.out.println(i);
}

sdf45456s

sfgsdgjsidufhgidfbnogb

使用map进行条件删除

//使用map条件删除
@Test
public void delete3(){
    HashMap<String, Object> map = new HashMap<>();
    //删除名字为李四,年龄为18的人
    map.put("name","李四");
    map.put("age",18);
    userMapper.deleteByMap(map);
}

image-20230801154634964

image-20230801154653454

逻辑删除
  • 物理删除,从数据库直接删除
  • 逻辑删除,没有从数据库中删除,只是通过变量来让它失效,让它查询不到,deleted=0—>deleted=1,查询的时候带上条件deleted=0
  • 管理员可以看到被删除的记录,类似于回收站防止数据丢失

数据库中新添加字段deleted默认值为0

image-20230801155631972

pojo中添加这个字段,并添加注解

@TableLogic
private Integer deleted;

添加配置

//逻辑删除组件
@Bean
public ISqlInjector sqlInjector(){
    return new LogicSqlInjector();
}

spring boot配置文件也需要配置

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
#配置逻辑删除not-delete-value=0代表没有删除 ,delete-value=1代表删除了
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0

现在去删除1号

sfsdfsdfsdrferf

可以看到它走的删除其实是修改,把deleted修改成了1,而数据库的数据仍然存在

image-20230801162400840

现在去查询看能不能查到

image-20230801162518901

显然查询的时候会带上deleted=0的条件而现在deleted=1,所以查询不到

5、性能分析插件

​ 在平时的开发中,会遇到一些慢sql,MP也有性能分析插件,可以设置sql执行时间超过这个时间就停止运行。

导入插件,这里设置最大时间不超过1ms,实际开发中一般都是100ms

//性能分析插件
@Bean
@Profile({"dev","test"})
public PerformanceInterceptor performanceInterceptor(){
    PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
    performanceInterceptor.setMaxTime(1);  //设置最大不超过1ms
    performanceInterceptor.setFormat(true); //开启格式化支持
    return performanceInterceptor;
}

测试使用,查询全部的用户

image-20230801164218427

执行了37ms,超时了,所以报错让你重新修改

6、条件构造器wrapper

条件构造器 | MyBatis-Plus (baomidou.com)

使用wrapper可以实现复杂的sql,对比之前的使用map,map是使用键值对,只能查询简单的。

而wrapper是一个对象,我们使用复杂的sql就是使用它的一些方法来实现复杂sql拼接

一些主要方法

  1. eq(String column, Object val):等于,生成的SQL片段为column = val。

  2. ne(String column, Object val):不等于,生成的SQL片段为column <> val。

  3. gt(String column, Object val):大于,生成的SQL片段为column > val。

  4. ge(String column, Object val):大于等于,生成的SQL片段为column >= val。

  5. lt(String column, Object val):小于,生成的SQL片段为column < val。

  6. le(String column, Object val):小于等于,生成的SQL片段为column <= val。

  7. between(String column, Object val1, Object val2):介于两个值之间,生成的SQL片段为column between val1 and val2。

  8. notBetween(String column, Object val1, Object val2):不介于两个值之间,生成的SQL片段为column not between val1 and val2。

  9. like(String column, Object val):模糊查询,生成的SQL片段为column like %val%。

  10. notLike(String column, Object val):不模糊查询,生成的SQL片段为column not like %val%。

  11. isNull(String column):字段为空,生成的SQL片段为column is null。

  12. isNotNull(String column):字段不为空,生成的SQL片段为column is not null。

  13. in(String column, Collection<?> values):字段在集合中,生成的SQL片段为column in (values)。

  14. notIn(String column, Collection<?> values):字段不在集合中,生成的SQL片段为column not in (values)。

  15. orderByAsc(String… columns):按照指定字段升序排序。

  16. orderByDesc(String… columns):按照指定字段降序排序。

测试1

@Test
void contextLoads() {
    //selectList的参数为wapper是查询的条件
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //查询名字和邮箱不为空,以及年龄大于等于18的
    //也是链式
    wrapper
            .isNotNull("name")
            .isNotNull("email")
            .ge("age",18);
    userMapper.selectList(wrapper).forEach(System.out::println);
}

image-20230801170248140

测试二

@Test
void test2(){
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("name","张三");
    //只查询一个,这样就和map类似了
    System.out.println(userMapper.selectOne(wrapper));
}

image-20230801171333422

测试三

@Test
void test3(){
    //查询年龄在20-28之间的
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.between("age",20,28);
    //只查询一个,这样就和map类似了
     userMapper.selectList(wrapper).forEach(System.out::println);
}

image-20230801172029168

测试四模糊查询like likeRight右边就是 x% ||左边就是 %x

@Test
void test4(){
    //模糊查询  邮箱是t开头,名字中不包含o的
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper
            .notLike("name","o")
            .likeRight("email","t");
   List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
        maps.forEach(System.out::println);
}

image-20230801172823655

测试五

@Test
void test5(){
    //id在子查询中 in
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.inSql("id","select id from user where id < 3");
    List<Object> objects = userMapper.selectObjs(wrapper);
    objects.forEach(System.out::println);
}

image-20230801173643350

测试六 升降序

@Test
void test6(){
    //根据id排序
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.orderByDesc("id");
    List<Object> objects = userMapper.selectObjs(wrapper);
    objects.forEach(System.out::println);
}

image-20230801174417397

7、代码自动生成器

pojo、dao、service、controller全都自动编写

代码生成器(旧) | MyBatis-Plus (baomidou.com)

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

官网的示例代码

// 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
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 + "/src/main/java");
        gc.setAuthor("jobob");
        gc.setOpen(false);
        // gc.setSwagger2(true); 实体属性 Swagger2 注解
        mpg.setGlobalConfig(gc);

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

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(scanner("模块名"));
        pc.setParent("com.baomidou.ant");
        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();
    }

}

模仿的示例代码

// 代码自动生成器
public class AutoGeneratorTest {
    public static void main(String[] args) {
        // 需要构建一个 代码自动生成器 对象
        AutoGenerator mpg = new AutoGenerator();
        // 配置策略

        // 1、全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("自动生成的");
        gc.setOpen(false);
        gc.setFileOverride(false);  // 是否覆盖
        gc.setServiceName("%Serive"); // 服务接口,去Service的I前缀
        gc.setIdType(IdType.ID_WORKER); // 主键生成策略
        gc.setDateType(DateType.ONLY_DATE);
        gc.setSwagger2(true);

        // 给代码自动生成器注入配置
        mpg.setGlobalConfig(gc);

        // 2、 设置数据源
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2b8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 3、包的配置

        PackageConfig pc = new PackageConfig();
        pc.setModuleName("blog");
        pc.setParent("com.zm");
        pc.setEntity("entity");
        pc.setMapper("mapper");
        pc.setService("service");
        pc.setController("controller");

        mpg.setPackageInfo(pc);

        // 4、策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude("user"); // 设置要映射的表名
        strategy.setNaming(NamingStrategy.underline_to_camel);  // 内置下划线转驼峰命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);  // 自动Lombok

        strategy.setLogicDeleteFieldName("deleted");  // 逻辑删除字段

        // 自动填充策略
        TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
        TableFill gmtModifid = new TableFill("gmt_modifid", FieldFill.INSERT);

        ArrayList<TableFill> tableFills = new ArrayList<>();
        tableFills.add(gmtCreate);
        tableFills.add(gmtModifid);
        strategy.setTableFillList(tableFills);

        // 乐观锁
        strategy.setVersionFieldName("version");

        strategy.setRestControllerStyle(true);
        strategy.setControllerMappingHyphenStyle(true); // Localhost:8080/hello_id_2

        mpg.setStrategy(strategy);

        // 执行
        mpg.execute();
    }
}
r");
        pc.setService("service");
        pc.setController("controller");

        mpg.setPackageInfo(pc);

        // 4、策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude("user"); // 设置要映射的表名
        strategy.setNaming(NamingStrategy.underline_to_camel);  // 内置下划线转驼峰命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);  // 自动Lombok

        strategy.setLogicDeleteFieldName("deleted");  // 逻辑删除字段

        // 自动填充策略
        TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
        TableFill gmtModifid = new TableFill("gmt_modifid", FieldFill.INSERT);

        ArrayList<TableFill> tableFills = new ArrayList<>();
        tableFills.add(gmtCreate);
        tableFills.add(gmtModifid);
        strategy.setTableFillList(tableFills);

        // 乐观锁
        strategy.setVersionFieldName("version");

        strategy.setRestControllerStyle(true);
        strategy.setControllerMappingHyphenStyle(true); // Localhost:8080/hello_id_2

        mpg.setStrategy(strategy);

        // 执行
        mpg.execute();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fiercezm

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值