MyBatis plus
简介
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
愿景
我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。
#特性
-
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
-
损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
#支持数据库
-
mysql 、 mariadb 、 oracle 、 db2 、 h2 、 hsql 、 sqlite 、 postgresql 、 sqlserver
-
达梦数据库 、 虚谷数据库 、 人大金仓数据库
#框架结构
快速使用
在 Spring Boot 启动类中添加 @MapperScan
注解,扫描 Mapper 文件夹:
@SpringBootApplication @MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper") public class Application { public static void main(String[] args) { SpringApplication.run(QuickStartApplication.class, args); } }
编写实体类 User.java
(此处使用了 Lombok 简化代码)
@Data public class User { private Long id; private String name; private Integer age; private String email; }
编写Mapper类 UserMapper.java
public interface UserMapper extends BaseMapper<User> { }
测试查询,无条件
@RunWith(SpringRunner.class) @SpringBootTest public class PulsTest { @Autowired private UserMapper userMapper; @Test public void testSelect() { List<User> userList = userMapper.selectList(null); for (User user : userList) { System.err.println(user); } }
配置日志
#日志配置
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
CRUD扩展
插入
@Test public void testInsert(){ User user=new User(); user.setName("军鹏"); user.setAge(20); user.setEmail("1234@qq.com"); int insert = userMapper.insert(user); System.err.println(insert); System.err.println(user); }
自增
@Data public class User { @TableId(type = IdType.AUTO) //AUTO为自增,注意数据库也要设为自增 private int id; private String name; private int age; private String email; }
修改
@Test public void testUpdata(){ User user=new User(); user.setId(7); user.setName("博洋"); user.setAge(24); int i = userMapper.updateById(user); //参数是实体类对象 System.err.println(i+"::::"+user); }
其他源码
public enum IdType { AUTO(0), //自增 NONE(1), //未设置主键 INPUT(2), //手动设置 一但设置,必须填写 ASSIGN_ID(3),//默认的全局唯一id ASSIGN_UUID(4), //全球唯一id
删除
@Test public void testdelete(){ int i = userMapper.deleteById(1); System.err.println(i); }
自动填充
创建时间,修改时间!这些操作都是自动化完成,不希望手动更新!
阿里巴巴开发手册:所有的数据库:gmt_create、gmt_modified几乎所有表都要配置上!而且需要自动化!
代码级别
在实体类属性上添加注解
@TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updataTime;
编写处理器来处理这个注解即可
@Slf4j @Component public class MyMetaObjectHandler implements MetaObjectHandler { //插入时的填充策略 @Override public void insertFill(MetaObject metaObject) { log.info("start insert fill ...."); // 第一个为数据库插入时间字段,第二为当前时间 this.setFieldValByName("createTime",new Date(),metaObject); this.setFieldValByName("updataTime",new Date(),metaObject); } //更新时的填充策略 @Override public void updateFill(MetaObject metaObject) { log.info("start update fill ...."); this.setFieldValByName("updataTime",new Date(),metaObject); } }
测试观察时间即可!
乐观锁&悲观锁
乐观锁:顾名思义十分乐观,他总是认为不会出现问题,无论干什么都不会加锁
悲观锁:顾名思义十分悲观,它总是以为都会出现问题,无论干什么都会加锁
乐观锁需要数库加个version字段
实体类加上注解
@Version private Integer version;
写一个配置类、
@MapperScan("com.ysl.mapper") @EnableTransactionManagement @Configuration public class MyMybatisPlusConfig { @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } }
测试
@Test public void testle(){ User user = userMapper.selectById(10); user.setName("博洋c"); user.setAge(23); userMapper.updateById(user); }
失败测试
@Test public void testle2(){ User user = userMapper.selectById(10); user.setName("博洋aaaaa"); user.setAge(23); User user2 = userMapper.selectById(10); user2.setName("博洋bbbbb"); user2.setAge(23); userMapper.updateById(user); //如果没有乐观锁就会覆盖插队线程的值 }
根据id或多个id查询
@Test public void testSelectById(){ User user = userMapper.selectById(2); System.err.println(user); List<User> users = userMapper.selectBatchIds(Arrays.asList(2, 3, 4)); users.forEach(System.out::println); }
根据map查询实现动态sql
@Test public void testSelectMap(){ Map<String,Object> map=new HashMap<String,Object>(); map.put("name","军鹏"); map.put("age",20); List<User> users = userMapper.selectByMap(map); users.forEach(System.out::println); }
分页
现在配置的类中配置beane
@MapperScan("com.ysl.mapper") @EnableTransactionManagement @Configuration public class MyMybatisPlusConfig { //乐观锁插件 @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } //分页插件 @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false // paginationInterceptor.setOverflow(false); // 设置最大单页限制数量,默认 500 条,-1 不受限制 // paginationInterceptor.setLimit(500); // 开启 count 的 join 优化,只针对部分 left join paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true)); return paginationInterceptor; } }
测试
//分页查询 @Test public void testSelectPage(){ //第一个参数:当前页 //第二个参数:当前页大小 Page<User> page = new Page<>(1,5); userMapper.selectPage(page, null); //获得数据 page.getRecords().forEach(System.err::println); System.err.println(page.getCurrent()+"::"+page.getSize()+"::"+page.getTotal()+"::"+page.getPages()); }
删除操作
根据id
@Test public void testDelect(){ //根据id删除单个 userMapper.deleteById(10); //根据id删除多个 userMapper.deleteBatchIds(Arrays.asList(9,8)); }
根据map
@Test public void testDelectByMap(){ //根据map删除 Map<String,Object> map=new HashMap<String,Object>(); map.put("id",7); map.put("name","b博洋"); userMapper.deleteByMap(map); }
逻辑删除
物理删除:从数据库中直接移除
逻辑删除:没有从数据库中移除,而是通过一个变量让它失效 deleted=0 == deleted=1
管理员可以查看删除的数据,防止数据丢失,类似回收站
测试一下,数据库加入deleted字段默认为0
实体类属性上加注解
//逻辑删除 @TableLogic private int deleted;
配置yml文件
mybatis-plus: global-config: db-config: logic-delete-field: flag #全局逻辑删除字段值 3.3.0开始支持,详情看下面。 logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
测试删除
记录还在数据库但是deleted的值为1了
测试查询
数据库明明有值,就是查不到
查询的时候,MyBitas plus 会自动拼接上 deleted字段为条件
条件构造器
Wrapper
测试
@Test public void testSelect() { //查询邮箱不为空,并且年龄大于15岁 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.isNotNull("email").ge("age",15); List<User> users = userMapper.selectList(wrapper); users.forEach(System.err::println); } @Test public void testSelect3() { //查询年龄在20-30之间 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.between("age",20,30); Integer user = userMapper.selectCount(wrapper); System.err.println(user); } @Test public void testSelect4() { //模糊查询,查名字不包含e的 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.notLike("name","e"); List<Map<String, Object>> user = userMapper.selectMaps(wrapper); user.forEach(System.err::println); } @Test public void testSelect5() { //子查询 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.inSql("id","select id from user where id>3"); List<Object> user = userMapper.selectObjs(wrapper); user.forEach(System.err::println); } @Test public void testSelect6() { //通过id排序 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.orderByDesc("id"); List<User> users = userMapper.selectList(wrapper); users.forEach(System.err::println); }
代码自动生成器
//代码自动生成器生成器 public class KuangCode { public static void main(String[] args) { //需要构建一个代码自动生成器对象 AutoGenerator mpg = new AutoGenerator(); //配置策略 //1.全局配置 GlobalConfig gc = new GlobalConfig(); String property = System.getProperty("user.dir"); gc.setOutputDir(property+"/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); mpg.setGlobalConfig(gc); //2.设置数据源 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("1234"); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc); //3.包的配置 PackageConfig pc = new PackageConfig(); pc.setModuleName("blog"); pc.setParent("com.ysl"); pc.setEntity("pojo"); pc.setMapper("mapper"); pc.setService("service"); pc.setController("controller"); mpg.setPackageInfo(pc); //4.策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setInclude("zby"); strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setEntityLombokModel(true);//自动设置Lombok strategy.setLogicDeleteFieldName("deleted"); //自动填充配置 create_time updata_time TableFill createTime = new TableFill("create_time", FieldFill.INSERT); TableFill updataTime = new TableFill("updata_time", FieldFill.INSERT_UPDATE); ArrayList<TableFill> tableFills = new ArrayList<>(); tableFills.add(createTime); tableFills.add(updataTime); strategy.setTableFillList(tableFills); //乐观锁 strategy.setVersionFieldName("version"); strategy.setRestControllerStyle(true); strategy.setControllerMappingHyphenStyle(true);//localhost:8080/hello_id_2 mpg.setStrategy(strategy); mpg.execute(); } }