首先
首先Mybatis-plus是对Mybatis的增强,他能够无缝衔接Mybatis,也就是说即使我使用了BaseMapper,但是我还是可以写属于自己的mapper函数,还是可以用xml对应实现,这并不冲突
pom依赖
首先pom依赖,这里springboot版本过高会不兼容
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.note</groupId>
<artifactId>note</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>note</name>
<description>note</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</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>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.4.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
mapper
该mapper继承BaseMapper就可以使用其中的一些方法
@Repository
public interface UserMapper extends BaseMapper<User> {
}
insert
userMapper.insert(user);
MyBatis-Plus默认的主键策略是:ID_WORKER 全局唯一ID (基于雪花算法的策略生成id)
deleteById
userMapper.deleteById(1511604736883720194L)
deleteBatchIds
List<Long> idList = Arrays.asList(6L, 7L, 9L);
int result = userMapper.deleteBatchIds(idList);
deleteByMap
Map<String, Object> map = new HashMap<>();
map.put("age", 28);
map.put("name", "Test08");
int result = userMapper.deleteByMap(map);
updateById
user.setId(4L);
user.setName("李四");
int result = userMapper.updateById(user);
selectById
User user = userMapper.selectById(1L);
selectBatchIds
List<Long> idList = Arrays.asList(1L,2L,3L);
List<User> list = userMapper.selectBatchIds(idList)
selectByMap
Map<String, Object> map = new HashMap<>();
map.put("age", 28);
map.put("name", "Tom");
List<User> list = userMapper.selectByMap(map);
Service
官方:持久层接口 | MyBatis-Plus (baomidou.com)
public interface UserService extends IService<User> {
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
常用注解
@TableName
mapper通过泛型User匹配数据库的表,支持驼峰,但是如果表名是tb_user,那么就匹配不了了,可以在User类头部加入@TableName(“tb_user”)
这个问题可以通过配置文件解决,在application.properties中配置
#设置Mybatis-Plus操作表的默认前缀
mybatis-plus.global-config.db-config.table-prefix=t_
@TableId
1.在User字段中的某个属性,如uid头上,会把uid作为主键,insert的时候通过雪花算法填充
2.@TableId(value="xxxx")添加在某个属性上,可以用于把该属性映射到表中的XXX字段,并把该字段作为主键
3.@TableId的type属性
type属性用来定义主键策略
- IdType.ASSIGN_ID(默认):基于雪花算法的策略生成数据id,与数据库id是否设置自增无关
- IdType.AUTO:使用数据库的自增策略,要确保数据库设置了id自增, 否则无效
@TableField
Mybatis-plus支持驼峰转换,比如属性为UserName但是数据库中为user_name,他是可以识别的,但是如果属性为username,表中为name,就需要在该属性上加上@TableField("name")
@TableLogic
@TableLogic
private Integer isDelete;实现逻辑删除
Wrapper
QueryWrapper
1.查询
构建QueryWrapper对象,构造查询条件,借用mapper的selectList包装查询对象
//查询用户名包含a,年龄在20到30之间,并且邮箱不为null的用户信息
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "a")
.between("age", 20, 30)
.isNotNull("email")
.orderByAsc("age");
List<User> list = userMapper.selectList(queryWrapper);
2.删除
建QueryWrapper对象,构造查询条件,借用mapper的delete删除查询对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
int result = userMapper.delete(queryWrapper);//删除email为空的所有记录
3.更新
建QueryWrapper对象,构造查询条件,借用mapper的update更新查询到的对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//将(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息修改
//UPDATE t_user SET age=?, email=? WHERE (username LIKE ? AND age > ? OR email IS NULL)
queryWrapper
.like("name", "a")
.gt("age", 20)
.or()
.isNull("email");
User user = new User();
user.setEmail("user@test.com");
int result = userMapper.update(user, queryWrapper);
4.选择一些列
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "a")
.between("age", 20, 30)
.isNotNull("email")
.orderByAsc("age");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(user -> System.out.println(user));
queryWrapper.select("name", "age");
//selectMaps()返回Map集合列表,通常配合select()使用,避免User对象中没有被查询到的列值为null
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
5.构造子查询
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id", "select id from user where id <= 3");
List<User> list = userMapper.selectList(queryWrapper);
UpdateWrapper
可以认为还是用UpdateWrapper构造了查询结果,最终还是用的userMapper的update更新
UpdateWrapper<User> updateWrapper=new UpdateWrapper<>();
updateWrapper.like("name","a")
.and(i->i.gt("age",20).or().isNull("email"));
updateWrapper.set("email","user@test08.com");
int result = userMapper.update(null,updateWrapper);
注意:int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);函数的作用是根据给定的updateWrapper
条件,更新数据库中与之匹配的记录,将这些记录的数据更新为entity
所代表的内容。
condition
对标mybatis的条件查询,如果哪个字段不为空,那么这个字段也要参与select
1.使用if
String username = null;
Integer ageBegin = 18;
Integer ageEnd = 20;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//StringUtils.isNotBlank()判断某字符串是否不为空且长度不为0且不由空白符(whitespace)构成
if(StringUtils.isNotBlank(username)){
queryWrapper.like("name","a");
}
if(ageBegin != null){
queryWrapper.ge("age", ageBegin);
}
if(ageEnd != null){
queryWrapper.le("age", ageEnd);
}
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (age >=? AND age <= ?)
List<User> users = userMapper.selectList(queryWrapper);
2.使用带condition参数的重载方法构建查 询条件,简化代码的编写
LambdaQueryWrapper
相对于QueryWrapper 只修改了对于数据库字段的直写,之前直接写"age",现在通过pojo类映射数据库字段
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
//StringUtils.isNotBlank()判断某字符串是否不为空且长度不为0且不由空白符(whitespace)构成
queryWrapper.like(StringUtils.isNotBlank(username),User::getName,username)
.ge(ageBegin!=null,User::getAge,ageBegin)
.le(ageEnd!=null,User::getAge,ageEnd);
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (age >=? AND age <= ?)
List<User> users = userMapper.selectList(queryWrapper);
LambdaUpdateWrapper
插件
分页插件
法一:
这样就可以在service层实现分页查询了
1.添加配置类
@Configuration
@MapperScan("com.hua.mybatisplus.mapper") //可以将主类中的注解移到此处
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
2.调用mapper实现分页查询
Page<User> page = new Page<>(1,3);
userMapper.selectPage(page,null);//这里第二个参数可以传入一个Wrapper帮助实现限制查询
//获取分页数据
List<User> list = page.getRecords();
法二:使用xml自定义分页查询
userMapper.java
Page<User> selectPageVo(
//page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位
Page<User> page,
//age 年龄 限制条件
Integer age);
userMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.note.note.mapper.UserMapper">
<!-- Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);-->
<!--SQL片段,记录基础字段-->
<!-- Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);-->
<!--SQL片段,记录基础字段-->
<sql id="BaseColumns">id,name,age,email</sql>
<select id="selectPageVo" resultType="com.note.note.domain.User">
SELECT <include refid="BaseColumns"></include> FROM user
WHERE age > #{age}
</select>
</mapper>
对比一下mybatis+pagehelper
具体来说,当
PageHelper.startPage(categoryPageQueryDTO.getPage(),categoryPageQueryDTO.getPageSize())
被调用时,PageHelper 会拦截接下来的第一条查询 SQL 语句,并向其中注入相应的limit
子句来实现分页查询。同时,PageHelper 会利用 ThreadLocal 线程变量来保存分页的信息,以确保只有在该查询执行期间有效。
//service层
public PageResult pageQuery(CategoryPageQueryDTO categoryPageQueryDTO) {
PageHelper.startPage(categoryPageQueryDTO.getPage(),categoryPageQueryDTO.getPageSize());
//下一条sql进行分页,自动加入limit关键字分页
Page<Category> page = categoryMapper.pageQuery(categoryPageQueryDTO);
return new PageResult(page.getTotal(), page.getResult());
}
//mapper
Page<Category> pageQuery(CategoryPageQueryDTO categoryPageQueryDTO);
//xml
<select id="pageQuery" resultType="com.sky.entity.Category">
select * from category
<where>
<if test="name != null and name != ''">
and name like concat('%',#{name},'%')
</if>
<if test="type != null">
and type = #{type}
</if>
</where>
order by sort asc , create_time desc
</select>
乐观锁
如果a想要对价格+50,b想-30,两个人同时读到价格100,那么最后结果可能是150/70,而不是120,可以通过加上乐观锁版本号,每次修改都++,每次修改前核对版本号,如果版本号和预期相同才修改,CAS
做法就是加上@Version字段,配置拦截器(加上乐观锁)
@Configuration
@MapperScan("com.note.note.mapper") //可以将主类中的注解移到此处
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor my() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
枚举
@Getter
public enum SexEnum {
MALE(1,"男"),
FEMALE(2,"女");
@EnumValue //当一个枚举类型需要写入数据库的时候,就把注解标识的属性的值存储到数据库中
private Integer sex;
private String sexName;
SexEnum(Integer sex, String sexName) {
this.sex = sex;
this.sexName = sexName;
}
}
扩展
DB静态工具
//如果需要联合查询,如在user中需要查询这个user的所有address,就需要注入addressMapper,而在address中有时又要查询对应的user信息,就需要在address中注入userMapper,这样形成了循环依赖(springboot解决了这个问题)
//所以引入静态工具
Integer id=1;
List<Address> list = Db.lambdaQuery(Address.class).eq(Address::getUserId, id).list();
JSON
这样可以把数据库中读到的json类型的数据直接映射为java类