MyBatisPlus

学习视频:https://www.bilibili.com/video/BV17E411N7KN/

学习文档(精彩文章):https://www.cnblogs.com/dt746294093/p/16526667.html

官网:https://baomidou.com/

0.创建数据库user表

DROP TABLE IF EXISTS 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(乐观锁)、delete(逻辑删除)、gmt_create、gmt_modified

1.创建项目

 

 

2.导入依赖

<!--数据库驱动-->
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.0</version> </dependency>

配置application.properties文件

spring.datasource.username=mp_root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3307/mybatisplus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

#mysql8驱动不同com.mysql.jdbcDriver需要添加的时区配置serverTimezone=GMT%2B8

pojo

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

mapper

//在对应的mapper上面继承基本的接口BaseMapper
@ResponseBody //代表持久层
public interface UserMapper extends BaseMapper<User> {
    //所有的CRUD操作都已经编写完成了
    //你不需要像以前的配置一大堆文件了
}

修改启动类

@MapperScan("com.xinyu.mapper")  //扫描mapper文件夹
@SpringBootApplication
public class MybatisPlusApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisPlusApplication.class, args);
    }

}

测试

@SpringBootTest
class MybatisPlusApplicationTests {
    //继承了BaseMapper,所有的方法都来自父类
    //我们也可以编写自己的扩展方法
    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoads() {
        //参数是一个Wrapper,条件构造器,这里我们先不用 null
        //查询全部用户
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }
}

 

配置日志

在application.properties配置

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

 

CRUD扩展

插入

// 测试插入
    @Test
    void testInsert() {
        User user = new User();
        user.setName("Dt");
        user.setAge(18);
        user.setEmail("746294093@qq.com");

        int result = userMapper.insert(user); // 帮我们自带生成Id
        System.out.println(result); // result为受影响行数
        System.out.println(user);   // 发现,id会自动回填
    }

 

数据id(去数据库设置自增)

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    // 对应数据库中的主键(uuid、自增id、雪花算法、redis)
    @TableId(type = IdType.AUTO)
    private Long id;    // 注意类型是Long,不然用到雪花算法,会产生0
    private String name;
    private Integer age;
    private String email;
}

其他源码分析

    AUTO(0),   //数据库id自增
    NONE(1),   // 未设置主键
    INPUT(2),  // 手动输入
    ID_WORKER(3),   // 默认的全局唯一id
    UUID(4),   // 全局唯一id uuid
    ID_WORKER_STR(5);  // ID_WORKER  字符串表示法

更新

@Test
void testUpdate() {
    User user = new User();
    // 通过条件自动拼装动态sql
    user.setId(6L);
    user.setName("KyDestroy");
    user.setAge(21);
    // 注意:方法名是updateById 但参数是一个 对象!
    int result = userMapper.updateById(user);
    System.out.println(result);
}

 

自动填充(数据库新增两个字段)

 //字段添加填充内容
    @TableField(fill = FieldFill.INSERT)  //自动插入
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)  //自动插入及更新
    private Date updateTime;

编写处理器来处理这个注解即可!(建立一个handler包)

@Slf4j
@Component  // 把处理器添加到IOC容器中
public class MyMetaObjectHandler implements MetaObjectHandler {
    // 插入时的填充策略
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        this.setFieldValByName("createTime", new Date(), metaObject);
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }

    // 更新时的填充策略
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }
}

 

乐观锁 : 故名思意十分乐观,它总是认为不会出现问题,无论干什么不去上锁!如果出现了问题,再次更新值测试

悲观锁:故名思意十分悲观,它总是认为总是出现问题,无论干什么都会上锁!再去操作!

乐观锁实现方式:

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

CAS的机制是自旋锁(乐观锁的一种)

数据库添加字段version(int 默认值1)

注册组件(config)

@EnableTransactionManagement
@MapperScan("com.dt.mapper")  //扫描mapper文件
@Configuration  //配置类
public class MyBatisPlusConfig {

    // 注册乐观锁插件
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }

}

测试

// 测试乐观锁成功案例
@Test
void testOptimisticLocker() {
    // 1、查询用户信息
    User user = userMapper.selectById(1L);
    // 2、修改用户信息
    user.setName("Dt");
    user.setEmail("746294093@qq.com");
    // 3、执行更新操作
    userMapper.updateById(user);
}

// 测试乐观锁失败案例
@Test
void testOptimisticLocker2() {
    // 线程 1
    User user = userMapper.selectById(1L);
    user.setName("Dt111");
    user.setEmail("746294093@qq.com");

    // 模拟另外一个线程执行插队操作
    User user2 = userMapper.selectById(1L);
    user2.setName("Dt222");
    user2.setEmail("746294093@qq.com");
    userMapper.updateById(user2);

    userMapper.updateById(user); // 如果没有乐观锁就会覆盖插队线程的值
}

查询

批量查询

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

条件查询

// 条件查询 map
@Test
void testSelectByBatchIds() {
    HashMap<String, Object> map = new HashMap<>();
    // 自定义查询
    map.put("name", "Dt");
    map.put("age", 18);

    List<User> userList = userMapper.selectByMap(map);
    userList.forEach(System.out::println);
}

条件查询

// 条件查询 map
@Test
void testSelectByBatchIds() {
    HashMap<String, Object> map = new HashMap<>();
    // 自定义查询
    map.put("name", "Dt");
    map.put("age", 18);

    List<User> userList = userMapper.selectByMap(map);
    userList.forEach(System.out::println);
}

分页查询

在config中配置插件

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

测试

// 测试分页查询
@Test
void testPage() {
    // 参数一:current 当前页
    // 参数二:size 页面大小
    // 使用了分页插件之后,所有的分页操作也变得简单的!
    Page<User> page = new Page<>(1, 5);
    userMapper.selectPage(page, null);

    page.getRecords().forEach(System.out::println);
    System.out.println(page.getTotal()); // page.getTotal() 总共有多少页
}

 delete删除

// 测试删除
@Test
void testDeleteById() {
    userMapper.deleteById(1548320342353760262L);
}

根据 id 批量删除记录

// 通过id批量删除
@Test
void testDeleteBatchIds() {
    userMapper.deleteBatchIds(Arrays.asList(1548320342353760260L, 1548320342353760259L));
}

根据 map 删除记录

// 通过map删除
@Test
void testDeleteByMap() {
    HashMap<String, Object> map = new HashMap<>();
    map.put("name", "Dt");
    userMapper.deleteByMap(map);
}

 逻辑删除(防止数据丢失)

物理删除 :从数据库中直接移除

逻辑删除 :再数据库中没有被移除,而是通过一个变量来让他失效! deleted = 0 ==>> deleted = 1   //默认值为0

@TableLogic // 逻辑删除
private Integer deleted;

application.properties配置

# 逻辑删除
# 逻辑已删除值(默认为 1)
mybatis-plus.global-config.db-config.logic-delete-value=1
# 逻辑未删除值(默认为 0)
mybatis-plus.global-config.db-config.logic-not-delete-value=0

config文件中注入逻辑删除组件

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

可能遇到的问题:LogicSqlInjector()  ---提示错误,原因:mybatis-plus高版本无需注入逻辑删除组件,注释掉即可

 

性能分析插件

1.在application.properties配置环境为dev或者 test 环境!

# 设置开发环境
spring.profiles.active=dev

2.在config文件注入组件

/**
 * SQL执行效率插件
 */
@Bean
@Profile({"dev","test"})// 设置 dev test 环境开启,保证我们的效率
public PerformanceInterceptor performanceInterceptor() {
    PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
    performanceInterceptor.setMaxTime(100); // ms设置sql执行的最大时间,如果超过了则不执行
    performanceInterceptor.setFormat(true); // 是否格式化代码
    return performanceInterceptor;
}

3.测试

@Test
void contextLoads() {
    // 参数是个Wapper,条件构造器,没有条件就写null
    // 查询全部用户
    List<User> userList = userMapper.selectList(null);
    userList.forEach(System.out::println);
}

可能遇到的问题:PerformanceInterceptor报错,该插件3.2.0以上版本一处使用第三方扩展 执行sql分析打印功能

 

条件构造器Wrapper

测试---建立一个测试类:WrapperTest

@SpringBootTest
public class WrapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoadsTest() {
        // 查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于12岁的
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper
                .isNotNull("name")
                .isNotNull("email")
                .ge("age", 12);
        userMapper.selectList(wrapper).forEach(System.out::println); // 和我们刚才学习的map对比一下
    }

    @Test
    void test2(){
        //查询单个数据
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("name","bb");    // 查询一个数据,出现多个结果使用List或者 Map
        User user = userMapper.selectOne(wrapper);   
        System.out.println(user);
    }

@Test void test3() {
// 查询年龄在20到30岁之间的用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age", 20, 30); // 区间
Integer count = userMapper.selectCount(wrapper); // 查询结果数
System.out.println(count);
}
}

 

代码自动生成

dao、pojo、service、controller都给我自己去编写完成!

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

1.导入依赖

<!-- 模板引擎 -->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.0</version>
</dependency>

2.配置

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.util.ArrayList;
//代码自动生成器
public class KuangCode {
    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("%sService"); // 去Service的I前缀
        gc.setIdType(IdType.ID_WORKER); // 主键策略全局唯一
        gc.setDateType(DateType.ONLY_DATE); // 日期时间
        gc.setSwagger2(true);    // 是否配置Swagger文档
        mpg.setGlobalConfig(gc);
        
        //2、设置数据源
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/kuang_community?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.kuang"); //两个加起来就是 com.kuang.blog
        pc.setEntity("entity");    // 设置实体类包的名字
        pc.setMapper("mapper");    // 设置mapper类包的名字
        pc.setService("service");    //设置service类包的名字
        pc.setController("controller");    // 设置controller类包的名字
        mpg.setPackageInfo(pc);
        
        //4、策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude("blog_tags","course","links","sys_settings","user_record","user_say"); // 设置要映射的表名,可以设置多个
        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); // gmt_create为自动填充字段名
        TableFill gmtModified = new TableFill("gmt_modified",FieldFill.INSERT_UPDATE); // gmt_modified为自动填充字段名
        ArrayList<TableFill> tableFills = new ArrayList<>();
        tableFills.add(gmtCreate);
        tableFills.add(gmtModified);
        strategy.setTableFillList(tableFills);
        // 乐观锁
        strategy.setVersionFieldName("version");
        strategy.setRestControllerStyle(true); //是否开启controller的rest使用风格
        strategy.setControllerMappingHyphenStyle(true); //localhost:8080/hello_id_2
        mpg.setStrategy(strategy);
        mpg.execute(); //执行
    }
}

 

posted @ 2022-10-27 15:40  歆鱼  阅读(30)  评论(0编辑  收藏  举报