Mybatisplus从字面上面理解时Mybatis的加强版,它可以大大节省我们大量的工作时间,所有的增删改查代码它都可以在mybatisplus中自动的完成,其本质上上Mybatis的一个增强工具,在Mybatis的基础上只做增强不做改变,为简化开发,提高效率而生。
使用官方提供的快速开始文档
mybatisplususer插入数据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)
);
DELETE FROM user;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');
mysql mysql-connector-java
org.projectlombok lombok
com.baomidou mybatis-plus-boot-starter 3.3.1.tmp
spring:datasource:username: rootpassword: 123456url: jdbc:mysql://localhost:3306/mybatisplus?userSSL=false&useUnicode=true&characterEncoding=utf-8driver-class-name: com.mysql.cj.jdbc.Driver
@Data
@AllArgsConstructor
@NoArgsConstructor
public class user {private Long id;private String name;private Integer age;private String email;
}
使用mybatisplus之后只需要在对应的mapper上面继承基本的类BaseMapper即可(不需要像Mybatis那样繁琐)。到此所有的CRUD已经Mybatisplus帮我们自动编写完成十分方便(当然我们也可以在这里面定义自己的代码)。
@Repository //代表这个接口是Dao层的
public interface UserMapper extends BaseMapper {
}
@MapperScan("com.chailong.getspring.mapper") //扫描mapper的文件夹
@SpringBootApplication
public class GetspringApplication {public static void main(String[] args) {SpringApplication.run(GetspringApplication.class, args);}
@SpringBootTest
class GetspringApplicationTests {@Autowiredprivate UserMapper userMapper; //注入usermapper的bean@Testvoid contextLoads() {//seleclist的参数是一个wrapper(条件构造器)List userList=userMapper.selectList(null);//查询全部for (user user1 : userList) {System.out.println(user1);}}
}
运行结果:

8. 配置日志
在前面的操作过程中我们是具体看不到我们的sql语句的,为了能在控制台看到我们的sql语句,我们需要对日志进行配置
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
结果:

@Testvoid testInsert() {user usernew=new user();usernew.setName("chailong");usernew.setAge(11);usernew.setEmail("2012126846@qq.com");int result=userMapper.insert(usernew);System.out.println(result);}
结果:(发现自动插入了id)

2. 问题
思考这里为什么会自动生成唯一ID?
这就要涉及到分布式系统唯一id生成中的雪花算法
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。可以保证ID唯一性。
@TableId注解可以配置主键生成策略(下面是常见的策略)默认策略是ID_WORKER全局唯一ID
@Data
@AllArgsConstructor
@NoArgsConstructor
public class user {@TableId(type = IdType.ID_WORKER )private Long id;private String name;private Integer age;private String email;
}

3. 主键自增策略
- 在实体类字段上配置
@TableId(type=IdType.AUTO)- 勾选Mysql数据库中ID自增选项
public enum IdType {AUTO(0), //数据库id自增NONE(1),//未设置主键INPUT(2),//手动输入(数据库里面自己填)ID_WORKER(3),//默认全局唯一idUUID(4),//全局唯一idID_WORKER_STR(5);//ID_worker的字符串表示法
}
测试类代码
@Testvoid testUpdate() {user usernew=new user();usernew.setId(11);usernew.setName("jakiechai");usernew.setAge(11);usernew.setEmail("2012126846@qq.com");int i=userMapper.updateById(usernew);System.out.println(i);}
结果:

可以发现mybatis-plus帮我们实现了自动动态配置,这就需要讲到mybatis-plus的自动填充技术,下面介绍自动填充技术。
问题提出:在创建数据表中我们有些参数是不希望用户去手动填充的而是希望系统帮我们去自动填充(如表创建时间和表修改时间等)。阿里巴巴开发手册规定几乎所有数据表字段都必须要有一个gmt_create(创建时间)和gmt_modified(修改时间),而这两个参数是需要系统自动填充的;这就涉及到了自动填充技术。
在表中新增字段创建时间和修改时间设置默认值即可(修改表字段即可,注意pojo对应属性也要更改)
- 删除数据库默认值

- 实体类属性上加入相关注解
在TableField接口中有一个fill函数用于属性值的自动填充
FieldFill fill() default FieldFill.DEFAULT;
FieldFill的值表示填充的方法
public enum FieldFill {DEFAULT, //默认不填充INSERT, //插入时填充UPDATE, //更新时填充INSERT_UPDATE; //更新或插入时填充}
然后在类属性上加入@TableField注解
@Data
@AllArgsConstructor
@NoArgsConstructor
public class user {@TableId(type = IdType.AUTO )private int id;private String name;private Integer age;private String email;@TableField(fill = FieldFill.INSERT_UPDATE)private Date createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private Date updateTime;
}
- 在主目录下建立一个Handler包,专门用来放SpringBoot项目的处理器,编写处理器代码
@Slf4j //使用日志
@Component //处理器加入到ioc容器中
public class MyMetaObjectHandler implements MetaObjectHandler {//实现插入时自动填充@Overridepublic void insertFill(MetaObject metaObject) {log.info("start insert fill.....");//setFieldValByName(字段名,字段值,metaObject)this.setFieldValByName("createTime",new Date(),metaObject);this.setFieldValByName("updateTime",new Date(),metaObject);log.info("insert over");}//实现更新时自动填充@Overridepublic void updateFill(MetaObject metaObject) {log.info("start update fill.....");//setFieldValByName(字段名,字段值,metaObject)this.setFieldValByName("createTime",new Date(),metaObject);this.setFieldValByName("updateTime",new Date(),metaObject);log.info("update over");}
}
@Testvoid testInsert() {user usernew=new user();usernew.setName("menglei");usernew.setAge(11);usernew.setEmail("2012126846@qq.com");int result=userMapper.insert(usernew);System.out.println(result);}
运行结果:数据库插入成功(字段也帮我们自动填充了)

所谓的乐观锁,其实主要就是一种思想,因为乐观锁的操作过程中其实没有没有任何锁的参与,乐观锁只是和悲观锁相对,严格的说乐观锁不能称之为锁。所以要了解乐观锁的概念,通常与悲观锁对比起来看才更好理解,下面我们就通过乐观锁与悲观锁的对比来更好的理解乐观锁。乐观锁总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,只在更新的时候会判断一下在此期间别人有没有去更新这个数据。悲观锁总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞,直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
- 取出记录时获取当前version
- 更新时带上这个version
- 执行更新时,set version=nerVersion where version =oldVersion
- 如果version不对,就更新失败
//A执行更新update user set name="kuanshen",version=version+1where id=2 and version =1
//B执行更新update user set name="kuanshen",version=version+1where id=2 and version =1
假设B先完成了更新,version被改变,此时A就会更新失败,这就是乐观锁实现线程安全的机制
4. 使用Mybatis-plus的乐观锁插件
- 给数据表中加入version字段,并设置默认值为1

- 实体类同步,并给属性上加入乐观锁注解

- 在Handler文件下编写乐观锁的组件
@Configuration //声明这是一个配置类
@EnableTransactionManagement //事务
public class MybatisplusConfig {//注册乐观锁插件@Beanpublic OptimisticLockerInterceptor optimisticLockerInterceptor() {return new OptimisticLockerInterceptor();}}
- 测试乐观锁
@Testvoid testOptimisticLocker(){//1查询用户信息user usermy=userMapper.selectById(1);//2.修改用户信息usermy.setName("mike");usermy.setAge(11);usermy.setEmail("jake.com");//3.更新用户信息userMapper.updateById(usermy);}
运行结果:更新成功version加1(单线程不存在现场安全问题,一定会更新成功)

@Testvoid testOptimisticLocker(){//线程1(准备更新但是还没更新)user usermy=userMapper.selectById(1);usermy.setName("mike2");usermy.setAge(11);usermy.setEmail("jake.com");//线程2(插队在线程1之前执行了更新)user usermy2=userMapper.selectById(1);usermy2.setName("mike1");usermy2.setAge(11);usermy2.setEmail("mary.com");userMapper.updateById(usermy2);//线程2的更新操作userMapper.updateById(usermy);//线程1的更新操作(如果没有乐观锁就会把线程2更新的值直接覆盖掉)}
结果:乐观锁机制使得线程1没有更新

@Testvoid testquery(){System.out.println(userMapper.selectBatchIds(Arrays.asList(1,2,3)));}
结果:

2. 分页查询
在以前的ssm项目中几乎使用的都是原始的limit分页方式或者使用pageHelper等第三方插件,Mybaits-plus也为开发者准备了分页插件,开发直接使用即可,十分方便
- 配置分页组件(在配置类中加入分页的Bean就行)
@Beanpublic PaginationInterceptor pageInationInterceptor(){return new PaginationInterceptor();}
- 使用Page对象进行分页
@Testvoid testPage(){Page page=new Page<>(2,5);//当前页第2页,每页5条数据userMapper.selectPage(page,null);//null是给条件查询器参数(这里不使用)page.getRecords().forEach(System.out::println);}
结果:

@Testvoid testDeleteById(){int result=userMapper.deleteById(1);System.out.println(result);}
结果:

2. 逻辑删除
物理删除:从数据库中直接删除
逻辑删除:并没有从数据库中删除而是通过一个变量让其不能使用(达到了所谓删除的目的),用于防止数据的丢失,相当于回收站的作用
- 增加一个删除标记字段在数据库中

- pojo增加属性并使用注解
@TableLogicprivate int flage;
- 配置逻辑删除组件
@Beanpublic ISqlInjector sqlInjector(){return new LogicSqlInjector();}
- 配置文件中配置逻辑删除
mybatis-plus:global-config:db-config:logic-delete-value: 1logic-not-delete-value: 0
- 测试逻辑删除
@Testvoid logicdelet(){userMapper.deleteById(2); //逻辑删除2号用户}
结果:数据并没有删除(但flage变成了1即数据已经被逻辑删除了)

然后测试一下是否能查询到逻辑删除的数据:
@Testvoid testquery(){System.out.println(userMapper.selectById(2));}
结果:查询不到

慢sql:慢SQL指的是MySQL慢查询,是运行时间超过long_query_time值的SQL。真实的慢SQL通常会伴随着大量的行扫描、临时文件排序或者频繁的磁盘flush,直接影响就是磁盘IO升高,让正常的SQL变成了慢SQL,大面积执行超时。
在实际操作中我们会经常遇到一些慢sql,如何找出这些慢sql我们的Mybatis-plus也提供了 相应的插件:
性能分析插件:
性能分析拦截器,用于输出每条SQL语句及其执行时间。SQL性能执行分析,开发环境使用,超过指定时间停止运行,有助于发现问题。
//SQL执行效率插件@Bean@Profile({"dev","test"}) //设置 dev test 环境开启,保证我们的效率public PerformanceInterceptor performanceInterceptor(){PerformanceInterceptor performanceInterceptor=new PerformanceInterceptor();performanceInterceptor.setMaxTime(1); //设置sql的最大执行时间为1msperformanceInterceptor.setFormat(true);//是否开启格式化支持return performanceInterceptor;}
spring:profiles:active: dev
@Testvoid testPage(){Page page=new Page<>(2,5);userMapper.selectPage(page,null);page.getRecords().forEach(System.out::println);}
结果:sql运行时间为14ms超过了规定的时间所以执行失败

在前面使用的mybatis-plus提供的sql函数接口中,我们会发现我们把wrapper这个参数设置为了null,wrapper就是我们的条件构造器,用于写一些复杂的sql
@Testvoid contextLoads(){//查询name不为空的用户,并且邮箱不为空的用户,年龄>21的用户QueryWrapper wrapper=new QueryWrapper(); //创建条件构造器wrapper.isNotNull("name"); //name不为空的查询条件wrapper.isNotNull("email"); //邮箱不为空wrapper.ge("age",12); //年龄大于12userMapper.selectList(wrapper).forEach(System.out::println);}
结果:

@Testvoid contextLoads(){ //查询名字为menglei的用户//查询name不为空的用户,并且邮箱不为空的用户,年龄>21的用户QueryWrapper wrapper=new QueryWrapper(); //创建条件构造器wrapper.eq("name","menglei");userMapper.selectOne(wrapper);}
结果:

官网可以查看更多的使用方法
代码自动生成:AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
public static void main(String[] args) {//构建一个代码自动生成器对象AutoGenerator autoGenerator=new AutoGenerator();//配置策略//1全局配置GlobalConfig gc=new GlobalConfig();String property = System.getProperty("user.dir"); //获取用户目录gc.setOutputDir(property+"/src/src/main/java");//代码文件的生成路径gc.setAuthor("chailong"); //设置作者gc.setFileOverride(false);//是否覆盖gc.setServiceName("%sService");//去service的i前缀gc.setIdType(IdType.ID_WORKER); //设置Id自增gc.setDateType(DateType.ONLY_DATE);//设置日期类型gc.setSwagger2(true);//设置swaggerautoGenerator.setGlobalConfig(gc);//2. 设置数据源DataSourceConfig dataSourceConfig = new DataSourceConfig();dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/mybatisplus");dataSourceConfig.setUsername("root");dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");dataSourceConfig.setPassword("123456");dataSourceConfig.setDbType(DbType.MYSQL);autoGenerator.setDataSource(dataSourceConfig);//3.设置包PackageConfig packageConfig = new PackageConfig();packageConfig.setModuleName("blog");packageConfig.setParent("com.chailong.getspring");packageConfig.setEntity("entity");packageConfig.setMapper("mapper");packageConfig.setService("service");packageConfig.setController("controller");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);//自动生成lombokstrategy.setLogicDeleteFieldName("flage"); //设置逻辑删除名字//4.设置自动填充TableFill create_time = new TableFill("create_time", FieldFill.INSERT_UPDATE);TableFill update_time = new TableFill("update_time", FieldFill.INSERT_UPDATE);ArrayList arrayList = new ArrayList<>();arrayList.add(create_time);arrayList.add(update_time);strategy.setTableFillList(arrayList);//乐观锁strategy.setVersionFieldName("version");strategy.setRestControllerStyle(true);strategy.setControllerMappingHyphenStyle(true);autoGenerator.setStrategy(strategy);autoGenerator.execute();//执行}
运行结果:
