Mybatis-Plus
搭配官网食用更加。
简介
- 以mybatis为基础只增强,不改变。
- 为简化开发、提高效率而生
特性
(见官网)
框架结构
(见官网)
初识MP
快速开始
-
新建数据库并插入数据
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');
-
新建SpringBoot工程并导入依赖
-
新建Spring Boot工程,略…
-
导入依赖
<!--在导入MP时不要同时导入Mybatis的依赖,否则会导致版本差异。--> <!--数据库驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!--MP启动器--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3</version> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
-
-
连接数据库(配置文件)
properties.yml
spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useUnicode=true username: root password: 1234
-
java类的编写
-
创建实体类User.java
@Data @NoArgsConstructor @AllArgsConstructor public class User implements Serializable { private Long id; private String name; private Integer age; private String email; private Date createTime; private Date updateTime; }
-
创建Mapper类,UserMapper.java
@Mapper // 此处可以不标注Mapper,二在启动类上使用@ScanMapper来进行扫描 public interface UserMapper extends BaseMapper<User> {}
-
-
测试类中测试
@SpringBootTest class MybatisPlusApplicationTests { @Autowired UserMapper userMapper; @Test void contextLoads() { userMapper.selectList(null).forEach(System.out::println); } }
控制台输出:
User(id=1, name=Jone, age=18, email=test1@baomidou.com) User(id=2, name=Jack, age=20, email=test2@baomidou.com) User(id=3, name=Tom, age=28, email=test3@baomidou.com) User(id=4, name=Sandy, age=21, email=test4@baomidou.com) User(id=5, name=Billie, age=24, email=test5@baomidou.com)
自此已经完成了最简单的MP的使用,但是在并无法得知sql是怎样的。
配置显示SQL(日志)
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
控制台输出增加:
==> Preparing: SELECT id,name,age,email,create_time,update_time FROM user
==> Parameters:
<== Columns: id, name, age, email, create_time, update_time
<== Row: 1, Jone, 18, test1@baomidou.com, null, null
<== Row: 2, Jack, 20, test2@baomidou.com, null, null
<== Row: 3, Tom, 28, test3@baomidou.com, null, null
<== Row: 4, Sandy, 21, test4@baomidou.com, null, null
<== Row: 5, Billie, 24, test5@baomidou.com, null, null
<== Total: 5
这样就可以知道查询的过程是什么样的。
CRUD
Insert
@Autowired
UserMapper userMapper;
@Test
void contextLoads() {
userMapper.insert(new User().setAge(3).setEmail("504863638@qq.com").setName("ljq"));//此处链式操作需要在实体类上增加注解@Accessors(chain = true)
userMapper.selectList(null).forEach(System.out::println);
}
上述插入操作,不需要设置主键id的值,在插入时会生成唯一id值,然后插入到数据库中。日志:
==> Preparing: INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
==> Parameters: 1432591579369041922(Long), ljq(String), 3(Integer), 504863638@qq.com(String)
<== Updates: 1
主键生成策略
1.主键自增
-
在实体类的主键字段上标注
@TableId(type = IdType.AUTO)
-
数据库的主键设置为自增
alter table user modify id bigint auto_increment comment '主键ID';
2.雪花算法
在实体类的主键字段上标注即可。
@TableId(type = IdType.AUTO)
雪花算法:
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。可以保证几乎全球唯一!
3.其他策略
IdType
值 | 描述 |
---|---|
AUTO | 数据库ID自增 |
NONE | 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) |
INPUT | insert前自行set主键值 |
ASSIGN_ID | 分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator 的方法nextId (默认实现类为DefaultIdentifierGenerator 雪花算法) |
ASSIGN_UUID | 分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator 的方法nextUUID (默认default方法) |
4.自定义策略(官网)
Delete
@Autowired
UserMapper userMapper;
@Test
void contextLoads() {
//通过id单个删除,id为Long时需要在后面加上L
userMapper.deleteById(1432591579369041922L);
//通过id批量删除
userMapper.deleteBatchIds(Arrays.asList(1432594905707966465L,1432598944994582529L));
//通过map删除,key为表中字段名称,value为字段名称对应的将要删除的数据,删除所有符合条件的数据。
HashMap<String , Object> map = new HashMap<>();
map.put("name", "ljq");
userMapper.deleteByMap(map);
//通过Wrapper删除,具体使用可以查看下面wrapper的使用
userMapper.delete(wapper);
}
逻辑删除
删除分为物理删除与逻辑删除
- 物理删除:从数据库中删除
- 逻辑删除:在数据库中并不移除数据,而是通过添加一个字段来让这条数据失效。
-
在数据表中添加字段deleted
alter table user add deleted int default 1 not null comment '逻辑删除标识: 已删除:0 未删除:1';
-
配置文件
mybatis-plus: global-config: db-config: logic-delete-value: 0 # 逻辑删除所应该有的表示值 logic-not-delete-value: 1 # 逻辑未删除所应该有的表示值 # logic-delete-field: deleted # 如果设置了字段,不需要再在实体类上添加注解
-
实体类中添加相应的属性,并在属性上添加注解@TableLogic
@TableLogic private Integer deleted;
执行删除操作
后,实际上是执行了更新操作
。
Update
@Autowired
UserMapper userMapper;
@Test
void contextLoads() {
//通过对应的id进行更新
userMapper.updateById(new User().setId(1432598947737657346L).setName("ljq").setEmail("504863638@qq.com"));
}
Select
@Autowired
UserMapper userMapper;
@Test
void contextLoads() {
//根据id查数据
userMapper.selectById(1L);
//根据Id批量查询数据
userMapper.selectBatchIds(Arrays.asList(1L, 2L));
//根据条件批量查询
HashMap<String, Object> map = new HashMap<>();
map.put("age", 28);
List<User> userList = userMapper.selectByMap(map);
//根据wrapper条件个性化查询,wrapper处解释
}
核心功能
条件构造器wrapper
用来写一些复杂的SQL语句,官方页面里面有非常多的条件,直接去官网查看每个所代表的含义。
使用示例
@Autowired
UserMapper userMapper;
@Test
void contextLoads() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.isNotNull("name")
.ge("age", 21);
userMapper.selectList(wrapper);
}
分页查询
分页方法:
- 原始的limit
- pageHelper第三方插件
- MP内置
MP内置的分页插件
-
配置MP配置类,将插件注册到容器中。MybatisPlusConfig.java
@Component public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor paginationInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
-
直接使用Page对象
@Autowired UserMapper userMapper; @Test void contextLoads() { Page<User> page = new Page<>(/*当前页*/2, /*页面大小*/2); userMapper.selectPage(page, null);//查询的值放入Page对象中 page.getRecords().forEach(System.out::println);//通过Page对象来操作 }
自动填充
这个功能就是为了节约人力,根据一定条件来填充相应的值。
比如自动填充创建时间、修改时间等。
数据库级别填充
mysql5.7以上支持创建时间函数(默认值)已经更新时添加、更新。但是更改数据库不为一个较好的处理方式。
-
数据库中新增字段create_time、update_time
-
将实体类中也添加这两个属性,保持与数据库的同步。
private Date createTime; private Date updateTime;
代码级别
-
添加两个字段
-
实体类同步属性,并在属性字段上添加注解
@TableField(fill = FieldFill.INSERT)//插入时自动填充 private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE)//更新或者插入时填充 private Date updateTime;
-
自定义MetaObjectHandler的实现类
@Component @Slf4j public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { log.info("insert..."); this.fillStrategy(metaObject, "createTime", new Date()); this.fillStrategy(metaObject, "updateTime", new Date()); } @Override public void updateFill(MetaObject metaObject) { log.info("update..."); this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); } }
自此插入时间时即可发现,已经可以自动填充两个时间。
乐观锁
- 悲观锁:非常悲观,全部上锁。
- 乐观锁:非常乐观,全不上锁,出现问题再次更新至测试。
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
配置乐观锁
1.添加字段
- 数据库添加字段version
- 实体类中添加相应的属性
2.配置插件
MP配置文件:
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
3.添加注解
@Version
private Integer version;
插件主体
MybatisPlusInterceptor(since 3.4.0)
核心插件,MP使用插件只需要将需要使用的功能添加到该插件对象中。
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(/*此处添加功能*/);
return interceptor;
}
目前已有的功能:
- 自动分页: PaginationInnerInterceptor
- 多租户: TenantLineInnerInterceptor
- 动态表名: DynamicTableNameInnerInterceptor
- 乐观锁: OptimisticLockerInnerInterceptor
- sql性能规范: IllegalSQLInnerInterceptor
- 防止全表更新与删除: BlockAttackInnerInterceptor
注意:
使用多个功能需要注意顺序关系,建议使用如下顺序
- 多租户,动态表名
- 分页,乐观锁
- sql性能规范,防止全表更新与删除
总结: 对sql进行单次改造的优先放入,不对sql进行改造的最后放入
代码自动生成
导入依赖
MyBatis-Plus 从 3.0.3
之后移除了代码生成器与模板引擎的默认依赖,需要手动添加相关依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
代码自动生成类
随意命名。
将需要的条件设置到AutoGenerator对象中。
public class AutoCode {
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("LiuJiaqi");
gc.setOpen(false);
gc.setFileOverride(false);
// 是否覆盖
gc.setServiceName("%sService");
// 去Service的I前缀
gc.setIdType(IdType.ASSIGN_ID);
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=utf8&serverTimezone=Asia/Shanghai");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("4033");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
//3、包的配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("test");
pc.setParent("com.ljq");
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("create_time", FieldFill.INSERT);
TableFill gmtModified = new TableFill("update_time", FieldFill.INSERT_UPDATE);
ArrayList<TableFill> tableFills = new ArrayList<>();
tableFills.add(gmtCreate);
tableFills.add(gmtModified);
strategy.setTableFillList(tableFills);
// 乐观锁
strategy.setVersionFieldName("version");
strategy.setRestControllerStyle(true);
strategy.setControllerMappingHyphenStyle(true);
//localhost:8080/hello_id_2
mpg.setStrategy(strategy);
mpg.execute(); //执行
}
}
推荐遇见狂神说java(疯狂证明不是白嫖)