流殃的博客

| Comments

使用

1. 引入依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>

2. 配置数据库连接

spring:
  datasource:
    username: root
    password: 123
    url: jdbc:mysql://localhost:3306/mybatisPuls?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver

3. 编写pojo和mapper

pojo

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

mapper

//在对应的mapper中继承BaseMapper
@Repository //代表持久层 和@mapper作用一样,只是说明是持久层而已
public interface userMapper extends BaseMapper<user> {
//所有的crud操作都已经编写完成了
    //不需要像以前一样配置一大堆文件了
}

4. 在启动加入@scanmapper

@MapperScan("com.example.mapper")  //重点
@SpringBootApplication
public class DemoApplication {

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

}

5. 测试

@Test
void contextLoads() {
    //参数是一个wrapper,是一个条件构造器
    List<user> users = userMapper.selectList(null);
    users.forEach(System.out::println);
}

执行结果
image.png

配置日志

 org.apache.ibatis.logging.stdout.StdOutImpl是mabatis自带的日志,如果用log4j或者其他需要导入相应的依赖
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
   

image.png

CRUD扩展

insert

id默认会自动生成,默认使用的是雪花算法即id_worker

在主键上如下配置即可设置不同的主键策略

image.png
分布式唯一ID:https://juejin.im/post/5b3a23746fb9a024e15cad79

public enum IdType {
    AUTO(0),       //自增id
    NONE(1),       //没有
    INPUT(2),      //手动输入
    ID_WORKER(3),  //雪花算法
    UUID(4),       //uuid
    ID_WORKER_STR(5);  // 雪花算法的字符型形式

    private int key;

    private IdType(int key) {
        this.key = key;
    }

    public int getKey() {
        return this.key;
    }
}

update

@Test
void testUpdate(){
    user user = new user();
    //通过条件自动拼接sql
    user.setId(1L);
    user.setName("狂神");
    //updateById 名字虽然是通过id更新,但是参数却是user对象
    int i = userMapper.updateById(user);
    System.out.println(i);
}

自动填充

创建时间、修改时间!这个操作希望自动话完成

阿里巴巴开发手册:所有的数据库表:gmt_create gmt_modified 几乎所有的表都要配置上,而且要自动化

方式一:数据库级别

  1. 在表中新增 create_time update_time 字段

方式二:代码级别

@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;

编写处理器

@Slf4j
@Component   //一定不要忘记把处理器加入到spingboot的ioc容器中
public class MyDataObject implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start execute insert ");

        this.setFieldValByName("createTime",new Date(),metaObject);
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start execute update ");
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}

乐观锁

乐观锁:总是认为不会发生问题,无论干什么都不去加锁!如果出现了问题,再次更新值测试

悲观锁:总是认为会发生问题,无论干什么都加锁

乐观锁实现方式:

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

1. 给数据加个version字段

设置默认值为1

image.png

2. 给version字段加上相应注解

@Version
private int version;

3. 注册组件

//扫描mapper文件夹
@MapperScan("com.example.mapper")
@EnableTransactionManagement// 默认就是这样的
@Configuration
public class MybatisPlusConfig {
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
}

4. 测试

多线程下一定要加锁

//单线程情况下是可以成功的 乐观锁
@Test
void testOptimisticLocker(){
    user user = userMapper.selectById(1L);
    user.setName("kuangshen");
    user.setEmail("qq.com");
    userMapper.updateById(user);
}

//多线程会出错
@Test
void testOptimisticLocker2(){
    //线程1
    user user = userMapper.selectById(1L);
    user.setName("kuangshen");
    user.setEmail("qq.com");

    //模拟多线程情况
    //在user还没有执行更新操作之前,user2另一个线程修改了对应数据
    user user2 = userMapper.selectById(1L);
    user.setName("fail");
    user.setEmail("qq.com");
    //自旋锁多次尝试提交!
    userMapper.updateById(user);  //如果没有乐观锁,会覆盖插队线程的值
}

select

//测试查询
@Test
void testSelectById(){
    user user = userMapper.selectById(1L);
    System.out.println(user);
}
//测试批量查询
@Test
void testSelectBatchIds(){
    List<user> user = userMapper.selectBatchIds(Arrays.asList(1,2,3));
    user.forEach(System.out::println);
}
//条件查询 用map
@Test
void selectByMap(){
    HashMap<String, Object> map= new HashMap<>();
    map.put("name","Tom");
    List<user> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}

连表查询

1. 新建一个pojo类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class userClassName {
    private Long id;
    private String name;
    private Integer age;
    private String email;
    //字段填充内容
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    @Version  //乐观锁注解
    private int version;
    @TableLogic //逻辑删除
    private int deleted;
    
    //上面是 userPojo类,下面是classNamePojo类
    @TableId(type = IdType.AUTO)
    private int iid;
    private String className;
    private int classId;
}

2. 新建一个对应的Mapper

@Repository
public interface userClassNameMapper extends BaseMapper<userClassName> {
    @Select("select * from user u left join className c on u.id=c.iid  where  u.id=#{id}")
    List<userClassName> get(int id);
}

3. 测试

    //测试连表查询
    @Test
    void testLeftJoin11() {
        userClassNameMapper.get(1).forEach(System.out::println);
    }

测试结果:
userClassName(id=1, name=fail, age=18, email=qq.com, createTime=null, updateTime=null, version=3, deleted=0, iid=1, className=一年级二班, classId=12)
在iid之前是 user,之后是className

分页查询

1. 配置分页

//扫描mapper文件夹
@MapperScan("com.example.mapper")
@EnableTransactionManagement// 默认就是这样的
@Configuration
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
}

2.直接就可以使用了

//测试分页查询
@Test
void testPage(){
    //参数一:当前页,参数二:页面大小
    Page<user> page = new Page<>(2,5);
    userMapper.selectPage(page,null);
    page.getRecords().forEach(System.out::println);
}

delete

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

直接执行相应的方法就行

image.png

逻辑删除

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

逻辑删除:在数据库中没有删除,通过一个字段让他失效

管理员可以看到被删除的记录!防止数据的丢失,类似于回收站!

1. 在数据库中增加一个deleted字段

image.png

2. 实体类中增加属性

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

3. 配置

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

数据库中 deleted默认为1

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      logic-not-delete-value: 1
      logic-delete-value: 0

4. 测试

执行的是删除操作,但是实际上是更新操作

@Bean
public ISqlInjector sqlInjector() {
    return new LogicSqlInjector();
}

image.png
查询的时候会自动过滤掉deleted为0的信息,即被逻辑删除了,查询不到该值了

image.png

性能分析插件

作用:用于输出每条 SQL 语句及其执行时间

1. 导入插件

    @Bean
    @Profile({"dev","test"})// 设置 dev test 环境开启
    public PerformanceInterceptor performanceInterceptor() {
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        performanceInterceptor.setMaxTime(100);//设置sql最大执行时间,超过不执行
        performanceInterceptor.setFormat(true);
        return performanceInterceptor;
    }

2. 设置为开发 或测试环境

spring:
  profiles:
    active: dev

3. 测试

查询所有用户

@Test
void contextLoads() {
    //参数是一个wrapper,是一个条件构造器
    List<user> users = userMapper.selectList(null);
    users.forEach(System.out::println);
}

image.png

条件构造器

参考官方文档执行即可

@Test
public void test(){
    QueryWrapper<user> wrapper = new QueryWrapper<>();
    wrapper.isNotNull("name")
            .isNotNull("email")
            .ge("age",21);
    userMapper.selectList(wrapper).forEach(System.out::println);
}

//模糊查询
@Test
public void test2(){
    QueryWrapper<user> wrapper = new QueryWrapper<>();
    wrapper.like("name","e");
    userMapper.selectList(wrapper).forEach(System.out::println);
}
//内查询
    @Test
    public void test3(){
        QueryWrapper<user> wrapper = new QueryWrapper<>();
        wrapper.inSql("id","select id from user where id>3");
        List<Object> list = userMapper.selectObjs(wrapper);
        list.forEach(System.out::println);
    }

代码生成器

写一个相关配置类,运行即可

public static void main(String[] args) {
    AutoGenerator autoGenerator = new AutoGenerator();
    //配置策略
    //1.全局配置
    GlobalConfig config = new GlobalConfig();
    //获取用户目录
    String property = System.getProperty("user.dir");
    config.setOutputDir(property+"/src//main/java");
    config.setAuthor("shy");
    config.setOpen(false);
    config.setFileOverride(false);//是否覆盖
    config.setServiceName("%sService"); //去除servie的i前缀
    config.setIdType(IdType.ID_WORKER);
    config.setDateType(DateType.ONLY_DATE);
    config.setSwagger2(true);   //自动配置swagger文档
    autoGenerator.setGlobalConfig(config);
    //2. 设置数据源
    DataSourceConfig dataSourceConfig = new DataSourceConfig();
    dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/mybatisPuls?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false");
    dataSourceConfig.setUsername("root");
    dataSourceConfig.setPassword("123");
    dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
    dataSourceConfig.setDbType(DbType.MYSQL);
    autoGenerator.setDataSource(dataSourceConfig);


    //3. 包的配置
    PackageConfig packageConfig = new PackageConfig();
    packageConfig.setModuleName("test");
    packageConfig.setParent("com.example");
    packageConfig.setEntity("entity");
    packageConfig.setController("controller");
    packageConfig.setMapper("mapper");
    packageConfig.setService("service");
    autoGenerator.setPackageInfo(packageConfig);

    //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 insertTime =  new TableFill("create_time", FieldFill.INSERT);
    TableFill updateTime =  new TableFill("update_time", FieldFill.INSERT_UPDATE);
    List<TableFill> objects = new ArrayList<>();
    objects.add(insertTime);
    objects.add(updateTime);
    strategy.setTableFillList(objects);
    //乐观锁配置
    strategy.setVersionFieldName("version");
    strategy.setRestControllerStyle(true);//开启restFul的驼峰命名
    strategy.setControllerMappingHyphenStyle(true);

    autoGenerator.setStrategy(strategy);

    autoGenerator.execute();

}

排除非表字段的三种方式

建立实体类的时候,正常的时候是会将数据库中所有字段映射的,但是有时候需要用到一些信息,这些信息不需要存入到数据库中,而是有其他用途,这时候,就要用下面的东西了。

无序列化
private transient String remark;           transient就是表示这个变量是非表字段
有序列化
private static String remark;           生成gat set方法
@TableField(exist=false)
private String remark;        

Comments

评论