Mybatis-Plus
目录
1、简介
Mybatis-plus
简介:Mybatis
增强工具,只做增强,不作改变,简化开发,提高效率。
官网地址:https://mybatis.plus/
github
项目地址:https://github.com/baomidou/mybatis-plus
框架结构:
MP在mybatis启动的时候,它在mybatis的xml和注解注入之后,紧接着反射分析实体,然后注入到底层容器中。就是注入crud之类的。注入之前MP会进行判断,是否已经注入同样的方法,如果注入,就不在注入。它的注入时机在容器启动时,所以MP使用crud、本身是无性能损耗的。
特性:
- 无侵入,损耗小、强大的CRUD操作
- 支持Lambda形式调用、支持多种数据库
- 支持主键自动生成、支持ActiveRecord模式
- 支持自定义全局通用操作、支持关键词自动转义
- 内置代码生成器、内置分页插件,内置性能分析插件
- 内置全局拦截插件、内置sql注入剥离器
2、快速入门
表:
#创建用户表
CREATE TABLE user (
id BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主键',
name VARCHAR(30) DEFAULT NULL COMMENT '姓名',
age INT(11) DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
manager_id BIGINT(20) DEFAULT NULL COMMENT '直属上级id',
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
CONSTRAINT manager_fk FOREIGN KEY (manager_id)
REFERENCES user (id)
) ENGINE=INNODB CHARSET=UTF8;
#初始化数据:
INSERT INTO user (id, name, age, email, manager_id
, create_time)
VALUES (1087982257332887553, '大boss', 40, 'boss@baomidou.com', NULL
, '2019-01-11 14:20:20'),
(1088248166370832385, '王天风', 25, 'wtf@baomidou.com', 1087982257332887553
, '2019-02-05 11:12:22'),
(1088250446457389058, '李艺伟', 28, 'lyw@baomidou.com', 1088248166370832385
, '2019-02-14 08:31:16'),
(1094590409767661570, '张雨琪', 31, 'zjq@baomidou.com', 1088248166370832385
, '2019-01-14 09:15:15'),
(1094592041087729666, '刘红雨', 32, 'lhm@baomidou.com', 1088248166370832385
, '2019-01-14 09:48:16');
新建一个springboot工程。
依赖:
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
配置文件:
#mysql
#mysql8.0的驱动类变成了com.mysql.cj.jdbc.Driver
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis-plus?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT
spring.datasource.username=root
spring.datasource.password=123456
logging.level.root=WARN
logging.level.com.example.mybatisplusdemo.dao=TRACE
#级别 内容 换行
logging.pattern.console=%p%m%n
实体类:
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
//直属上级
private Long managerId;
private LocalDateTime createTime;
}
dao:
public interface UserMapper extends BaseMapper<User> {
}
启动类:
@MapperScan("com.example.mybatisplusdemo.dao")
测试:
@Test
void contextLoads() {
//查询表中所有记录
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
3、基本使用
传统模式
通用mapper
BaseMapper类中,含有常用的方法。
/**
* 插入
*/
@Test
public void insert(){
User user=new User();
user.setId(1094592041087729667l);
user.setName("张三");
user.setAge(31);
user.setManagerId(1088250446457389058l);
user.setCreateTime(LocalDateTime.now());
int insert = userMapper.insert(user);
System.out.println(insert);
}
常用注解
@Data
//数据库的表名是`mp_user`,但是我实体类的名想叫`User`,不想用驼峰命名法,这时就可以用`@TableName`
@TableName("mp_user")
public class User {
//标识这个字段是表的主键
@TableId
private Long id;
//当前字段和数据库中的字段不一样时,可以直接指定
@TableField("name")
private String realName;
private Integer age;
private String email;
//直属上级
private Long managerId;
private LocalDateTime createTime;
}
排除非表字段的三种方式
实体类中的remark字段,在表中是不存在的,映射不上是会报错的
@Data
//数据库的表名是`mp_user`,但是我实体类的名想叫`User`,不想用驼峰命名法,这时就可以用`@TableName`
@TableName("mp_user")
public class User {
//标识这个字段是表的主键
@TableId
private Long id;
//当前字段和数据库中的字段不一样时,可以直接指定
@TableField("name")
private String realName;
private Integer age;
private String email;
//直属上级
private Long managerId;
private LocalDateTime createTime;
//备注,这个字段在数据库中是不存在的
private String remark;
}
解决方式一:添加关键字transient
//备注,这个字段在数据库中是不存在的
//transient关键字标记的成员变量不参与序列化过程。
private transient String remark;
解决方式二:设置为静态变量
private static String remark;
解决方式三:
//备注,这个字段在数据库中是不存在的
@TableField(exist = false)
private String remark;
4、MyBatis-Plus查询方法
普通查询:
@Test
public void selects(){
//1、根据主键id查询
// User user = userMapper.selectById(1087982257332887553l);
// System.out.println(user);
//2、根据主键id,批量查询。注意:List.of()在jdk9才有这种写法
// List<User> users = userMapper.selectBatchIds(List.of(1094592041087729668l, 1094592041087729667l));
// users.forEach(System.out::println);
//3、根据指定条件查询数据
List<User> users = userMapper.selectByMap(Map.of("name", "王天风", "age", 25));
users.forEach(System.out::println);
}
条件构造器查询:
//小于lt 小于等于le
//大于gt 大于等于ge
@Test
public void selectByWrapper(){
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
//1、name like "%雨%" and age<40
// userQueryWrapper.like("name","雨").lt("age",40);
//2,name like "%雨%" and age between 20 and 40 and email is not null
// userQueryWrapper.like("name","雨").between("age",20,40).isNotNull("email");
//3、name like '王%' or age>=25 order by age desc,id asc
// userQueryWrapper.likeRight("name","王").or().ge("age",25).orderByDesc("age").orderByAsc("id");
//4、date_format(create_time,'%Y-%m-%d')='2019-02-14' and manager_id in (select id from user where name like '王%')
//可以避免sql注入的风险date_format(create_time,'%Y-%m-%d')={0}
// userQueryWrapper.apply("date_format(create_time,'%Y-%m-%d')={0}","2019-02-14").inSql("manager_id","select id from mp_user where name like '王%'");
//5、name like '王%' and (age<40 or email is not null)
// userQueryWrapper.likeRight("name","王").and(wq->wq.lt("age",40).or().isNotNull("email"));
//6、name like '王%' or (age<40 and age>20 and email is not null)
// userQueryWrapper.likeRight("name","王").or(wq->wq.lt("age",40).
// gt("age",20).isNotNull("email"));
//7、(age<40 or email is not null) and name like '王%'
//or的优先级是小于and的,如果没有前面的括号,结果是不一样的
// userQueryWrapper.nested(wq->wq.lt("age",40).or().isNotNull("email"))
// .likeRight("name","王");
//8、age in (30、31、34、35)
// userQueryWrapper.in("age",List.of(30,31,34,35));
//9、limit 1
userQueryWrapper.in("age",List.of(30,31,34,35)).last("limit 1");
List<User> users = userMapper.selectList(userQueryWrapper);
users.forEach(System.out::println);
}
5、select只列出指定的列
@Test
public void selectByWrapper(){
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
//10、默认是显示所有列,select显示指定的列
// userQueryWrapper.select("id","name").in("age",List.of(30,31,34,35)).last("limit 1");
userQueryWrapper.in("age",List.of(30,31,34,35)).last("limit 1")
.select(User.class,info->!info.getColumn().equals("create_time")&&!info.getColumn().equals("manager_id"));
List<User> users = userMapper.selectList(userQueryWrapper);
users.forEach(System.out::println);
}
6、condition作用
@Test
void testCondition(){
String name="王";
String email="";
condition(name,email);
}
private void condition(String name ,String email){
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
//condition作用判断是否满足条件,满足就执行当前语句
userQueryWrapper.like(!StringUtils.isEmpty(name),"name",name)
.like(!StringUtils.isEmpty(email),"email",email);
List<User> users = userMapper.selectList(userQueryWrapper);
users.forEach(System.out::println);
}
7、实体作为条件
@Test
void testCondition1(){
User user=new User();
user.setRealName("刘雨红");
user.setAge(32);
//实体作为条件,相当于WHERE name=? AND age=?
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(user);
//默认是等于,如果我想WHERE name like? AND age=? 该怎么办呢?
//User的name字段,注解@TableField添加属性condition = SqlCondition.LIKE
// @TableField(value="name",condition = SqlCondition.LIKE )
// private String realName;
List<User> users = userMapper.selectList(userQueryWrapper);
users.forEach(System.out::println);
}
8、AllEq用法
@Test
void testAllEq(){
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
Map<String, Object> params = new HashMap<>();
params.put("name","王天风");
params.put("age",null);
//相当于name = ? AND age = ?
//如果age为null,相当于name = ? AND age IS NULL,如果想忽略掉为null的allEq(params,false),name = ?
// userQueryWrapper.allEq(params,false);
//加入条件,不为name的字段才加入为条件,相当于age IS NULL
userQueryWrapper.allEq((k,v)->!k.equals("name"),params);
List<User> users = userMapper.selectList(userQueryWrapper);
users.forEach(System.out::println);
}
9、其他使用条件构造器的方法
1、selectMaps:根据条件构造器,查询全部记录
@Test
void testMap(){
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
// select avg(age) avg_age,min(age) min_age,max(age) max_age
// from user
// group by manager_id
// having sum(age) <500
userQueryWrapper.select("avg(age) avg_age","min(age) min_age","max(age) max_age")
.groupBy("manager_id").having("sum(age)<{0}",500);
//查询出的记录,一个记录就是一个map
List<Map<String, Object>> maps = userMapper.selectMaps(userQueryWrapper);
maps.forEach(System.out::println);
}
2、selectObjs:根据 Wrapper 条件,查询全部记录,注意: 只返回第一个字段的值
@Test
void testObjects(){
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
//查询id和name两个字段
userQueryWrapper.select("id","name").like("name","雨").lt("age",40);
//但是selectObjs只返回第一个字段的值,也就是id的值
List<Object> maps = userMapper.selectObjs(userQueryWrapper);
maps.forEach(System.out::println);
}
3、selectCount:根据 Wrapper 条件,查询总记录数
@Test
void testCount(){
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.like("name","雨").lt("age",40);
//查询记录数的
Integer count = userMapper.selectCount(userQueryWrapper);
System.out.println("总记录数:"+count);
}
4、selectOne:根据 entity 条件,查询一条记录
@Test
void testOne(){
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.like("name","刘红雨").lt("age",40);
//根据条件构造器,查询一条记录
User user = userMapper.selectOne(userQueryWrapper);
System.out.println(user.toString());
}
5、lamdba条件构造器
@Test
void testlamdba(){
LambdaQueryWrapper<User> lambda = new QueryWrapper<User>().lambda();
lambda.like(User::getRealName,"雨").lt(User::getAge,40);
List<User> users = userMapper.selectList(lambda);
for (User user : users) {
System.out.println(user);
}
}
@Test
void testlamdba2(){
List<User> userList = new LambdaQueryChainWrapper<User>(userMapper)
.like(User::getRealName, "雨").ge(User::getAge, 20).list();
userList.forEach(System.out::println);
}
6、自定义sql
方式一:
UserMapper:
public interface UserMapper extends BaseMapper<User> {
//不用加where
@Select("select * from mp_user ${ew.customSqlSegment}")
List<User> selectAll(@Param(Constants.WRAPPER) Wrapper<User> wrappers);
}
使用:
@Test
void testlamdba(){
LambdaQueryWrapper<User> lambda = new QueryWrapper<User>().lambda();
lambda.like(User::getRealName,"雨").lt(User::getAge,40);
//使用自定义的sql
List<User> users = userMapper.selectAll(lambda);
for (User user : users) {
System.out.println(user);
}
}
方式第二:使用mapper映射文件
配置文件:
映射文件:
10、分页
配置类:
@Configuration
public class MbatisPlusConfig {
@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
void testPage1(){
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.ge("age",26);
//第一个参数当前页,第二个参数,每页显示条数,
// 第三个参数
// true表示查出所有记录数,再分页:是查询两次
// SELECT COUNT(1) FROM mp_user WHERE (age >= ?);
// SELECT id,name AS realName,age,email,manager_id,create_time FROM mp_user WHERE (age >= ?) LIMIT ?
// false表示只查询分页的数据,查询一次
// SELECT id,name AS realName,age,email,manager_id,create_time FROM mp_user WHERE (age >= ?) LIMIT ?
//有些场景就是不需要总数,就可以设置为false,提高性能
Page<User> userPage = new Page<>(1, 2,true);
IPage<User> userPage1 = userMapper.selectPage(userPage, userQueryWrapper);
// userMapper.selectMapsPage()相似的
System.out.println("总页数:"+userPage1.getPages());
System.out.println("总记录数:"+userPage1.getTotal());
List<User> records = userPage1.getRecords();
records.forEach(System.out::println);
}
11、更新
1、updateById:根据主键id去更新一条记录
@Test
void testUpdate(){
User user=new User();
user.setId(1088248166370832385l);
user.setAge(30);
int i = userMapper.updateById(user);
System.out.println("影响记录数:"+i);
}
2、update:根据实体对象和条件构造器,来更新记录
@Test
void testUpdateWithWrapper(){
//条件构造器
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper.eq("name","李艺伟").eq("age",28);
//要更新的内容
User user = new User();
user.setEmail("lyw2019@qq.com");
user.setAge(30);
//更新
int i = userMapper.update(user, userUpdateWrapper);
System.out.println("影响记录数:"+i);
}
3、lamdba形式
@Test
void testUpdateWithWrapper1(){
LambdaUpdateWrapper<User> lambdaUpdate = Wrappers.lambdaUpdate();
lambdaUpdate.eq(User::getRealName,"李艺伟").eq(User::getAge,30).set(User::getAge,31);
int update = userMapper.update(null, lambdaUpdate);
System.out.println("影响记录数:"+update);
}
12、删除
1、deleteById:根据主键id删除
2、deleteByMap:根据条件删除
@Test
void deleteByMap(){
Map<String, Object> columMap = new HashMap<>();
columMap.put("name","向后");
columMap.put("age",24);
int i = userMapper.deleteByMap(columMap);
System.out.println("影响记录数:"+i);
}
3、deleteBatchIds:批量删除
13、ActiveRecord模式[AR模式]
实体类
Mapper要继承BaseMapper
测试:直接使用实体类进行增删改查
@Test
void insert(){
User u=new User();
u.setRealName("xxx");
u.setAge(16);
u.setEmail("aa@qq.com");
boolean insert = u.insert();
}
@Test
void select(){
User user = new User();
User user1 = user.selectById(1088248166370832385l);
System.out.println(user1);
}
14、主键策略
1、IdType.AUTO:数据库ID自增,确保数据库设置了 ID自增 否则无效
@TableId(type = IdType.AUTO)
private Long id;
更多
@Getter
public enum IdType {
/**
* 数据库ID自增
* <p>该类型请确保数据库设置了 ID自增 否则无效</p>
*/
AUTO(0),
/**
* 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
*/
NONE(1),
/**
* 用户输入ID
* <p>该类型可以通过自己注册自动填充插件进行填充</p>
*/
INPUT(2),
/* 以下3种类型、只有当插入对象ID 为空,才自动填充。如果手动设置了id,就不生效,就使用设置的id */
/**
* 分配ID (主键类型为number或string),
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
*
* @since 3.3.0
*/
ASSIGN_ID(3),
/**
* 分配UUID (主键类型为 string)
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
*/
ASSIGN_UUID(4),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_ID}
*/
@Deprecated
ID_WORKER(3),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_ID}
*/
@Deprecated
ID_WORKER_STR(3),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_UUID}
*/
@Deprecated
UUID(4);
private final int key;
IdType(int key) {
this.key = key;
}
}
也可以在配置文件里,设置就不用了每一个实体类都设置主键策略了
例
mybatis-plus.global-config.db-config.id-type=uuid
15、通用service
public interface UserService extends IService<User> {
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
@Autowired
private UserService userService;
@Test
void getOne(){
//false多余一个,不报错,只返回第一个
User one = userService.getOne(Wrappers.<User>lambdaQuery().gt(User::getAge, 25),false);
System.out.println(one);
//传入一个集合,如果对象有id的就是更新,没有的就是新增
// userService.saveOrUpdateBatch()
//查询的链式调用
List<User> userList = userService.lambdaQuery().gt(User::getAge, 25).like(User::getRealName, "雨").list();
//更新的链式调用
boolean update = userService.lambdaUpdate().eq(User::getAge, 25).set(User::getAge, 26).update();
//删除的链式调用
boolean remove = userService.lambdaUpdate().eq(User::getAge, 25).remove();
}
本文地址:https://blog.csdn.net/weixin_42412601/article/details/111827181