1. Mybatis-plus简单概括
Mybatis-plus是基于mybatis的,核心功能就是简化mybatis开发,提高效率。是国产的。
2. 快速入门
建表语句
create table `user`(
id int PRIMARY key auto_increment,
name varchar(32),
age int
)
insert into db_user values(null,"小米",23),(null,"小刚",21)
创建一个springboot项目,maven坐标如下
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
在resources文件夹下新建application.yml,添加如下内容
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8
username: root
password: 123456
mybatis-plus:
configuration:
# 在控制台打印sql语句的
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
编写User实体类
package com.cht.entity;
import lombok.Data;
@Data
public class User {
private Integer id;
private String name;
private Integer age;
}
再编写UserMapper类
package com.cht.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cht.entity.User;
public interface UserMapper extends BaseMapper<User> {
}
在springboot启动类上加上如下注解
@MapperScan("com.cht.mapper") //添加@MapperScan("com.cht.mapper")注解以后,com.cht.mapper包下面的接口类,在运行时,会通过动态代理生成相应的实现类
测试
package com.cht;
import com.cht.entity.User;
import com.cht.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class MybatisplusApplicationTests {
@Autowired
private UserMapper mapper;
@Test
void contextLoads() {
List<User> users = mapper.selectList(null);
for (User u:users) {
System.out.println(u);
}
}
}
3. 常用注解
让我们对实体类增加几个属性以及注解,如下:
@Data
@TableName(value = "db_user")
public class User {
@TableId(type = IdType.AUTO)
private Integer id;
private String name;
private Integer age;
@TableField(exist = false)
private String gender;
@TableField(select = false)
private String nation;
@TableField(value = "address")
private String dizhi;
@TableField(fill = FieldFill.INSERT)
private Date createDate;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateDate;
}
对数据库里的user表修改表名并增加三个字段,如下:
alter table user rename AS db_user;
alter table db_user add address VARCHAR(32);
alter table db_user add create_date datetime;
alter table db_user add update_date datetime;
@TableName
该注解用来映射数据库的表名,解决实体类名跟数据库表名不一致的情况,否则它会去数据库找类名首字母小写的表。
加在实体类上,如下:
@TableName(value = "对应的表名")
@TableField
映射表中的非主键字段,比如数据库中有个字段叫name,实体类User也有个属性叫name,这种情况是能够对应上的,但是数据库有个字段叫address,我实体类User有吗?没有,是不是只有一个叫dizhi的属性,而事实上dizhi就是要跟address对应的,所以,我们应该怎么让其对应,如上,是不是就在属性dizhi上加上@TableField(value = “address”)注解呀,这样不就可以对应上了吗?同时dizhi这个属性就成了address的别名。
看属性gender,它被注解@TableField(exist = false)标识了,那么这个exist表示什么意思?它表示是否存在,假设我们当前的这个属性gender,在表中对应的字段应该也是gender,但是在表中是不存在gender的,不存在就会报错,针对这种情况有两种解决办法,一种是在表中增加gender字段,另一种是我们确实不需要gender这个属性参与表中映射,那么我们可以把gender这个属性删掉,或者不删,就给它在注解上加上exist属性即可,值为false,表示不参与映射。
下一个,也就是nation属性,被@TableField(select = false)标识,看select,值有true和false,如果为false,表示不查询该属性。
最后一个,就是create_date和update_date了,两个分别代表创建时间和更新时间,看注解,fill表示是否自动填充,将对象存入数据库的时候,由MyBatis Plus自动给某些字段赋值,而触发填充的时机又是什么样的呢?如果是FieldFill.INSERT,表示在添加的时候就更新该字段,如果是FieldFill.INSERT_UPDATE,就表示在添加或者更新的时候更新该字段。既然知道自动填充的时机,那么填充啥内容?是不是就是时间呀,我们可以写一个自动填充的处理器,如下:
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
@TableId
跟@TableField意思一样,只不过@TableId服务于主键。如下:
@TableId(value = "id")
它还有另一个属性,叫type,表示主键的生成策略,我们知道,主键是有唯一性的,但不一定用自增的方式来保证唯一性,也可以用某种算法,比如雪花算法(snowflake算法),或者用UUID,或者是用redis来生成等等,都是可以用来生成主键的,我们可以通过IdType这个类来查看,有如下:
public enum IdType {
AUTO(0),
NONE(1),
INPUT(2),
ASSIGN_ID(3),
ASSIGN_UUID(4),
/** @deprecated */
@Deprecated /** 过时 */
ID_WORKER(3),
/** @deprecated */
@Deprecated
ID_WORKER_STR(3),
/** @deprecated */
@Deprecated
UUID(4);
}
重点掌握前5个,后三个都过时了,不管,如下:
| 值 | 描述 |
|---|---|
| AUTO | 数据库自增(以当前表的最大id为准,加1),无需手动赋值,手动赋值反而无效 |
| NONE | 未设置主键 |
| INPUT | 如果开发者没有手动赋值,则数据库通过自增的方式给主键赋值,如果开发者手动赋值,则存入该值。 |
| ASSIGN_ID | 主键类型为Long,Integer或String,默认采用雪花算法 |
| ASSIGN_UUID | 主键的数据类型必须是String,自动生成UUID进行赋值 |
@Version
标记乐观锁,通过version字段来保证数据的安全性,当修改数据的时候,会以version作为条件,当条件成立的时候才会修改成功。
什么意思呢?也就是它可以防止多个线程同时修改某一条数据,我们可以在数据库表中增加一条version字段,如下:
alter table db_user add version int DEFAULT 1;
然后在实体类User下增加一个字段,如下:
@Version
private Integer version;
还没完,还要写一个配置类,如下:
@Configuration
public class MyBatisPlusConfig {
//返回一个乐观锁的拦截器
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){
return new OptimisticLockerInterceptor();
}
}
这次就可以进行修改操作了,比如我要修改id为6的记录,那么一旦修改成功,字段version原本不是1吗?修改后就变为2了。后台sql语句如下:
UPDATE db_user SET name=?, age=?, create_date=?, update_date=?, version=? WHERE id=? AND version=?
也就是说,如果有两条线程同时获取同一条记录,那么两条线程拿到的version可能就是一样的,都是1,那么如果有一条线程最先修改成功,version变为2,但是后一条线程拿到的version还是原来的1,所以where后的第二个条件不成立,不成立就修改不成功咯。
@EnumValue
通用枚举类注解,将数据库字段映射成实体类的枚举类型成员变量。
我们可以在数据库表中再增加一个字段叫status,值的话要么1,要么0。然后实体类这边也要加上status属性,如下:
private StatusEnum status;
StatusEnum类如下:
package com.cht.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
public enum StatusEnum {
WORK(1,"上班"),
REST(0,"休息");
@EnumValue
private Integer code;
private String msg;
StatusEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
}
别忘了yml,如下:
mybatis-plus:
configuration:
...
type-enums-package:
com.cht.enums
如果数据库里的值是1,那么查出来的status就是WORK,为0就是REST。
StatusEnum类的另一种写法,就是不用@EnumValue注解,用实现接口的方式,如下:
public enum StatusEnum implements IEnum<Integer> {
WORK(1,"上班"),
REST(0,"休息");
@EnumValue
private Integer code;
private String msg;
StatusEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
@Override
public Integer getValue() {
return this.code;
}
}
@TableLogic
映射逻辑删除。所谓的逻辑删除不是真的删除,而是有一个字段,该字段有两个取值,分别是0或者1,0就代表未删除,1就代表删除。
所以,让我们在数据表中再增加一个字段,就叫deleted吧,如下:
alter table db_user add deleted int DEFAULT 0;
再在实体类上加上该字段,如下:
@TableLogic
private Integer deleted;
在yml下增加几句话,如下:
mybatis-plus:
...
global-config:
db-config:
logic-not-delete-value: 0
logic-delete-value: 1
4. CRUD详解
查询
@Test
void findAll() {
List<User> users = mapper.selectList(null); //null表示不加任何条件,也就是查询全部数据
for (User u:users) {
System.out.println(u);
}
}
@Test
void select1() {
//查询name等于小飞的人
QueryWrapper wrapper = new QueryWrapper();
wrapper.eq("name","小飞");
List<User> users = mapper.selectList(wrapper);
for (User u:users) {
System.out.println(u);
}
}
@Test
void select2() {
QueryWrapper wrapper = new QueryWrapper();
//多条件查询,查询name等于小路,age等于3的人
Map<String,Object> map = new HashMap<>();
map.put("name","小路");
map.put("age",3);
wrapper.allEq(map);
List<User> users = mapper.selectList(wrapper);
for (User u:users) {
System.out.println(u);
}
}
@Test
void select3() {
QueryWrapper wrapper = new QueryWrapper();
//查询年龄大于18岁的人
wrapper.gt("age",18); //还有像ne就是不等,ge就是大于等于,lt就是小于
List<User> users = mapper.selectList(wrapper);
for (User u:users) {
System.out.println(u);
}
}
@Test
void select4() {
QueryWrapper wrapper = new QueryWrapper();
//模糊查询,查名字里边带小的
wrapper.like("name","小");//还有像likeLeft表示%小,likeRight表示小%
List<User> users = mapper.selectList(wrapper);
for (User u:users) {
System.out.println(u);
}
}
@Test
void select5() {
QueryWrapper wrapper = new QueryWrapper();
wrapper.inSql("id","select id from db_user where id<4");
List<User> users = mapper.selectList(wrapper);
for (User u:users) {
System.out.println(u);
}
}
@Test
void select6() {
QueryWrapper wrapper = new QueryWrapper();
wrapper.orderByAsc("age");//对age进行升序排列
List<User> users = mapper.selectList(wrapper);
for (User u:users) {
System.out.println(u);
}
}
@Test
void select7() {
// System.out.println(mapper.selectById(6));
for (User user : mapper.selectBatchIds(Arrays.asList(3, 4, 5))) {
System.out.println(user);
}
}
@Test
void select8() {
Map<String,Object> map = new HashMap<>();
map.put("id",7);
mapper.selectByMap(map);//查询id等于7的记录
}
@Test
void select9() {
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.gt("id",1);
System.out.println(mapper.selectCount(queryWrapper));
}
@Test
void select10() {
//将查询的结果封装到map里面,注意它跟selectByMap的区别
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.gt("id",1);
List<Map> list = mapper.selectMaps(queryWrapper);
for (Map map : list) {
System.out.println(map);
}
}
@Test
void select11() {
// 分页查询
Page<User> page = new Page<>(1,2);//查询第1页,每一页查询2条记录
Page<User> userPage = mapper.selectPage(page, null);
System.out.println(userPage.getSize());//当前查出来的记录数
System.out.println(userPage.getTotal());//总记录数
List<User> records = userPage.getRecords();
for (User user:records){
System.out.println(user);
}
}
//注意以上要在配置类MyBatisPlusConfig写上这么一个方法才行,如下:
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
@Test
void select12() {
Page<Map<String,Object>> page = new Page<>(1,2);
List<Map<String, Object>> records = mapper.selectMapsPage(page, null).getRecords();
for (Map<String,Object> map:records){
for(String key:map.keySet()){
System.out.println("key:"+key+" "+"Value:"+map.get(key));
}
}
}
@Test
void select13() {
//查询所有的主键
List<Object> objects = mapper.selectObjs(null);
for (Object object:objects){
System.out.println(object);
}
}
@Test
void select14() {
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("id",6);
System.out.println(mapper.selectOne(queryWrapper));
}
自定义SQL(多表关联查询)
create table product(
category int ,
count int,
description varchar(32),
user_id int,
foreign key (user_id) references db_user(id)
)
insert into product values(1,10,"手机",3);
insert into product values(1,2,"电脑",4);
insert into product values(2,100,"电视",5);
insert into product values(2,200,"冰箱",6);
再创建一个ProductVo类,如下:
package com.cht.entity;
import lombok.Data;
@Data
public class ProductVO {
private Integer category;
private Integer count;
private String description;
private Integer userId;
private String userName;
}
再看UserMapper接口类,加上这么一句话,如下:
@Select("select p.*,u.name userName from product p,db_user u where p.user_id = u.id and u.id = #{id}")
List<ProductVO> productList(Integer id);
测试,如下:
@Test
void select15() {
List<ProductVO> productVOS = mapper.productList(6);
System.out.println(productVOS);
}
添加
@Test
void save(){
User user = new User();
user.setName("小b");
user.setAge(23);
mapper.insert(user);
System.out.println(user);//注意,id会自动回填
}
删除
@Test
void delete1() {
mapper.deleteBatchIds(Arrays.asList(7,8));
}
...
5. MyBatisPlus自动生成
根据数据表自动生成实体类,Mapper,Service,ServiceImpl,Controller。
首先,要想让它自动生成,先在pom.xml中导入依赖,如下:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.1.tmp</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>
package com.cht;
import com.baomidou.mybatisplus.annotation.DbType;
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.rules.NamingStrategy;
public class Main {
public static void main(String[] args) {
// 创建generator对象
AutoGenerator autoGenerator = new AutoGenerator();
// 数据源
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDbType(DbType.MYSQL);
dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8");
dataSourceConfig.setUsername("root");
dataSourceConfig.setPassword("123456");
dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
autoGenerator.setDataSource(dataSourceConfig);
//全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(System.getProperty("user.dir")+"/src/main/java");
globalConfig.setOpen(false);
globalConfig.setAuthor("wudongchenxu");
globalConfig.setServiceImplName("%sService");//因为生成出来的service接口类它的名字前面默认带有I,如果不想要,可以加上这一行
autoGenerator.setGlobalConfig(globalConfig);
//包信息
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.cht");
packageConfig.setModuleName("generator");
packageConfig.setController("controller");
packageConfig.setService("services");
packageConfig.setServiceImpl("services.impl");
packageConfig.setMapper("mapper");
packageConfig.setEntity("entity");
autoGenerator.setPackageInfo(packageConfig);
//配置策略
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setEntityLombokModel(true);
strategyConfig.setNaming(NamingStrategy.underline_to_camel);
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);//将数据库表里的下划线转为驼峰命名发,在生成实体类字段的时候
autoGenerator.setStrategy(strategyConfig);
autoGenerator.execute();
}
}
但要注意,yml里的数据源信息也要修改一下,如果一样就不用修改了,反正要与上对应。然后测试的时候别忘了加@MapperScan注解,包括实体类中以驼峰命名的属性加上@TableField与数据库里的字段对应,因为字段是下划线的,而实体类的属性是驼峰式的。
如果只是想生成部分表,那么在配置策略下加上这么一句话,如下:
strategyConfig.setInclude("db_user"); //写上你要生成的表名,注意它是可变参数,如果还有表,逗号隔开即可
6. 性能分析插件
MybatisPlus也提供了性能分析插件,如果超过这个时间就会停止运行,主要针对慢sql,用于输出每条sql语句及其执行时间。
maven坐标
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.8.2</version>
</dependency>
application.yml
spring:
datasource:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8
spy.properties
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2

3335

被折叠的 条评论
为什么被折叠?



