目录
一、背景
MybatisPlus可以节省大量时间,所有的CRUD代码都可以自动化完成
MyBatis-Plus是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
二、实现
1、起步依赖
可以替换掉Mybatis的依赖。
XML
<!--mybatisplus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
2、定义Mapper
自定义自己的Mapper继承MybatisPlus提供的BaseMapper接口:
指定操作的实体类表User
java
public interface UserMapper extends BaseMapper<User> {
}
并且其中不用再写方法,直接调用BaseMapper的方法。
java
public interface UserMapper extends BaseMapper<User> {
void saveUser(User user);
void deleteUser(Long id);
void updateUser(User user);
User queryUserById(@Param("id") Long id);
List<User> queryUserByIds(@Param("ids") List<Long> ids);
}
UserMapper.xml不用再写sql语句
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.itheima.mp.mapper.UserMapper">
</mapper>
而不是:
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.itheima.mp.mapper.UserMapper">
<insert id="saveUser" parameterType="com.itheima.mp.domain.po.User">
INSERT INTO `user` (`id`, `username`, `password`, `phone`, `info`, `balance`)
VALUES
(#{id}, #{username}, #{password}, #{phone}, #{info}, #{balance});
</insert>
<update id="updateUser" parameterType="com.itheima.mp.domain.po.User">
UPDATE `user`
<set>
<if test="username != null">
`username`=#{username}
</if>
<if test="password != null">
`password`=#{password}
</if>
<if test="phone != null">
`phone`=#{phone}
</if>
<if test="info != null">
`info`=#{info}
</if>
<if test="status != null">
`status`=#{status}
</if>
<if test="balance != null">
`balance`=#{balance}
</if>
</set>
WHERE `id`=#{id};
</update>
<delete id="deleteUser" parameterType="com.itheima.mp.domain.po.User">
DELETE FROM user WHERE id = #{id}
</delete>
<select id="queryUserById" resultType="com.itheima.mp.domain.po.User">
SELECT *
FROM user
WHERE id = #{id}
</select>
<select id="queryUserByIds" resultType="com.itheima.mp.domain.po.User">
SELECT *
FROM user
<if test="ids != null">
WHERE id IN
<foreach collection="ids" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</if>
LIMIT 10
</select>
</mapper>
调用BaseMapper的方法
java
userMapper.insert(user);
而不是:
java
userMapper.saveUser(user);
三、常用注解
它怎么知道访问哪张表,怎么知道表中有哪些信息?
MybatisPlus通过扫描实体类,并基于反射获取实体类信息作为数据库表信息。
约定:
• 类名驼峰转下划线作为表名
• 名为id的字段作为主键
• 变量名驼峰转下划线作为表的字段名
如果需要可以进行配置:
• @TableName:用来指定表名
• @TableId:用来指定表中的主键字段信息
• @TableField:用来指定表中的普通字段信息
使用@TableField的常见场景:
• 成员变量名与数据库字段名不一致
• 成员变量名以is开头,且是布尔值
• 成员变量名与数据库关键字冲突
• 成员变量不是数据库字段
四、常见配置
当你的sql语句比较复杂时,就需要自定义sql,即写xml文件。
application.yaml中:
TypeScript
mybatis-plus:
type-aliases-package: com.itheima.mp.domain.po #别名扫描包
mapper-locations: "classpath:mapper/*.xml" #mapper映射文件地址
configuration:
map-underscore-to-camel-case: true #开启下划线和驼峰的映射
cache-enabled: false #开启二级缓存
global-config:
db-config:
id-type: assign_id #默认id为雪花算法生成
update-strategy: not_null #默认更新策略: not_null(不为null则更新)
五、条件构造器(复杂条件)
执行复杂的(特殊的)sql语句。
基于QueryWrapper的查询
1、查询出名字中带o的;存款大于等于1000元的人的id、username、info、balance字段。
对于以下sql(模糊匹配)
sql
SELECT id,username,info,balance
FROM user
WHERE username LIKE ? AND balance >= ?
可以写出以下方法
java
void testQueryWrapper(){
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.select("id", "username", "info", "balance")
.like("username", "o")
.ge("balance", 1000);
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
2、更新用户名为jack的用户的余额为2000 (相等匹配:eq)
对于以下sql
sql
UPDATE user
SET balance = 2000
WHERE(username = "jack")
可以写出以下方法
java
@Test
void testUpdateNByQueryWrapper(){
User user = new User();
user.setBalance(2000);
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.eq("username","jack");
userMapper.update(user,wrapper);
}
基于UpdateWrapper的修改
1、更新id为1,2,4的用户的余额,扣200(
sql
UPDATE user
SET balance = balance - 200
WHERE id in (1,2,4)
java
@Test
void testUpdateWrapper(){
List<Long> ids = List.of(1L, 2L, 4L);
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
.setSql("balance = balance - 200")
.in("id", ids);
userMapper.update(null,wrapper);
}
六、自定义SQL(复杂条件)
我们可以利用MybatisPlus的Wrapper来构建复杂的Where条件,然后自己定义SQL语句中剩下的部分。
将id在指定范围的用户(例如1、2、4)的余额扣减指定值。
1、构建Lambda表达式,调用方法
java
@Test
void testCustomSqlUpdate(){
List<Long> ids = List.of(1L, 2L, 4L);
int amount = 200;
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.in("User::id", ids);
userMapper.updateBalance(amount,wrapper);
}
2、在mapper接口定义方法
java
void updateBalance(@Param("amount") int amount, @Param("ew") QueryWrapper<User> wrapper);
3、直接写JAVA代码或写xml
Generate statement->
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.itheima.mp.mapper.UserMapper">
<update id="updateBalance">
update user set balance = balance - #{amount} ${ew.customSqlSegment}
</update>
</mapper>
七、Lambda表达式(常用,硬编码)
运用了反射的机制(User::getId)。
使用Lambda表达式是为了解决硬编码问题。
硬编码:查询的条件写死,应该由用户输入的信息进行查询。
将以下查询代码
java
@Test
void testQueryWrapper(){
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.select("id", "username", "info", "balance")
.like("username", "o")
.ge("balance", 1000);
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
改为
java
@Test
void testLambdaQueryWrapper(){
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
.select(User::getId, User::getUsername, User::getInfo, User::getBalance)
.like(User::getUsername, "o")
.ge(User::getBalance, 1000);
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
八、Service接口(增删改查的另一种方法)
通过Service接口中的方法实现增删改查。
java
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService{
}
java
public interface IUserService extends IService<User> {
}
java
class IUserServiceTest {
@Autowired
private IUserService userService;
// 测试新增
@Test
void testSaveUser() {
User user = new User();
user.setId(5L);
user.setUsername("Lucy");
user.setPassword("123");
user.setPhone("18688990011");
user.setBalance(200);
user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
userService.save(user);
}
}
基于Restful风格实现下列接口(实际应用)
Swagger,Web依赖
XML
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.2.2</version>
</dependency>
<!--Web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Controller
增删改查功能
java
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
private final IUserService userService;
/**
* 新增用户
*
*/
@PostMapping
void save(UserFormDTO userFormDTO) {
User user = BeanUtil.copyProperties(userFormDTO, User.class);
userService.save(user);
}
/**
* 删除用户
*/
@PostMapping("{id}")
void delete(@PathVariable("id") Long id) {
userService.removeById(id);
}
}
九、插件(实现分页查询)
通过MybatisPlus的拦截器去拦截sql语句再进行拓展其功能,以实现分页查询。
1、配置插件
java
@Configuration
public class MybatisConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//1、创建分页插件
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
paginationInnerInterceptor.setMaxLimit(1000L);
//2、添加分页插件
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}
}
2、使用插件
java
@Component
public class ServiceTest {
@Autowired
private IUserService userService;
// 测试分页
void testPageQuery(){
int pageNo = 1, pageSize = 2;
//1.准备分页条件
//1.1.分页条件
Page<User> page = Page.of(pageNo, pageSize);
//1.2.排序条件
page.addOrder(new OrderItem("id", true));
page.addOrder(new OrderItem("balance", true));
//2.执行分页查询
Page<User> userPage = userService.page(page);
//3.获取分页结果
long total = userPage.getTotal();
System.out.println("总记录数:" + total);
long pages = userPage.getPages();
System.out.println("总页数:" + pages);
List<User> records = userPage.getRecords();
records.forEach(System.out::println);
}
}
实际开发(通用分页实体)
返回值:
这里需要定义3个实体:
UserQuery/PageQuery
:分页查询条件的实体,包含分页
、排序参数
、过滤条件
PageDTO
:分页结果实体,包含总条数
、总页数
、当前页数据
UserVO/AddressVO
:用户页面视图实体
UserQuery:
java
@Data
@ApiModel(description = "用户查询实体")
public class UserQuery extends PageQuery{
@ApiModelProperty("用户名关键字")
private String name;
@ApiModelProperty("用户状态: 1-正常, 2-冻结")
private Integer status;
@ApiModelProperty("余额最小值")
private Integer minBalance;
@ApiModelProperty("余额最大值")
private Integer maxBalance;
}
PageQuery:
java
@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {
@ApiModelProperty("页码")
private Integer pageNo;
@ApiModelProperty("每页大小")
private Integer pageSize;
@ApiModelProperty("排序字段")
private String sortBy;
@ApiModelProperty("是否升序")
private Boolean isAsc;
}
PageDTO:
java
@Data
@ApiModel(description = "分页响应实体")
public class PageDTO<T> {
@ApiModelProperty("总条数")
private Long total;
@ApiModelProperty("总页数")
private Long pages;
@ApiModelProperty("集合")
private List<T> list;
}
UserVO/AddressVO:
java
@Data
@ApiModel(description = "用户VO实体")
public class UserVO {
@ApiModelProperty("用户id")
private Long id;
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("详细信息")
private String info;
@ApiModelProperty("使用状态(1正常 2冻结)")
private Integer status;
@ApiModelProperty("账户余额")
private Integer balance;
@ApiModelProperty("收获地址列表")
private List<AddressVO> address;
}
Controller:
java
@ApiOperation("分页查询")
@GetMapping("/page")
public PageDTO<UserVO> queryUserPage(UserQuery query) {
return userService.queryUserPage(query);
}
service:(根据这段代码可以反推以上代码)
java
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService{
@Override
public PageDTO<UserVO> queryUserPage(UserQuery query) {
String name = query.getName();
Integer status = query.getStatus();
Integer minBalance = query.getMinBalance();
Integer maxBalance = query.getMaxBalance();
//1.构建分页条件
Page<User> page = new Page<>(query.getPageNo(), query.getPageSize());
//2.排序条件
if(StrUtil.isNotBlank(query.getSortBy())){
// 不为空
page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));
}else{
// 为空,默认按照更新时间排序
page.addOrder(new OrderItem("update_time", false));
}
//3.分页查询
Page<User> p = lambdaQuery()
.like(name != null, User::getUsername, name)
.eq(status != null, User::getStatus, status)
.ge(minBalance != null, User::getBalance, minBalance)
.le(maxBalance != null, User::getBalance, maxBalance)
.page(page);
//4.封装VO结果
PageDTO<UserVO> dto = new PageDTO<>();
//总条数
dto.setTotal(p.getTotal());
//总页数
dto.setPages(p.getPages());
//当前页数据
List<User> records = p.getRecords();
if(CollUtil.isEmpty(records)){
dto.setList(Collections.emptyList());
return dto;
}
dto.setList(BeanUtil.copyToList(records, UserVO.class));
return dto;
}
}