SpringCloud微服务学习笔记(一)_MybatisPlus

MybatisPlus

MybatisPlus与Mybatis是合作关系
MybatisPlus本质上是提供了接口和父类,继承接口和父类可以省去常用业务代码的编写
maven坐标

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus</artifactId>
    <version>3.5.9</version>
</dependency>

启动类上加@MapperScan(“com.itheima.mp.mapper”)注解
配置文件中配置属性

mybatis-plus:
  configuration:
    # MyBatis 配置
    map-underscore-to-camel-case: true
  global-config:
    # 全局配置
    db-config:
      # 数据库配置
      id-type: auto
public interface UserMapper extends BaseMapper<User> {}
直接继承BaseMapper父类及指定实体类(字段与表中一一对应的),就可以用MybatisPlus了

核心功能

常用注解

  • @TableName,指定表名
  • @TableId,指定主键字段信息
  • @TableField,指定表中的普通字段信息
    约定大于配置
//实体类与数据库表严格遵守驼峰命名与'_'格式的转换
@Data
@TableName("user")//当数据库表与实体类名不一致需要指定
public class User {

    @TableId(value = "id",type = IdType.AUTO)//变量名与主键id不一致需要指定
	//主键自增需要指定type = IdType.AUTO,否则插入新数据时,默认按雪花算法生成主键id
    private Long id;

    @TableField("username")//变量名与字段名不一致需要指定
    private String username;


    @TableField(exist = false)//设定在表中无此password字段,返回null
    private String password;
	
	@TableField("`order`")//变量名与数据库关键字冲突
	private Integer order;
	
	@TableField("is_married")//成员变量以is开头,且为boolean值,不指定时,mp会认为isMarried在表中对应married字段
	private Boolean isMarried;
}

常用配置

一般不用配置,默认就行

mybatis-plus:
  configuration:
    # MyBatis 配置
    map-underscore-to-camel-case: true
  global-config:
    # 全局配置
    db-config:
      # 数据库配置
      id-type: auto

条件构造器

  • 构造语句的where部分
  • LambdaQueryWrapper\LambdaUpdateWrapper\QueryWrapper\UpdateWrapper
  • 尽量使用LambdaQueryWrapper和LambdaUpdateWrapper,避免硬编码
@Test
void testLambdaQueryWrapper(){
	LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
			.select(User::getId, User::getUsername, User::getPassword, User::getPhone, User::getBalance)
			.like(User::getUsername, "L")
			.ge(User::getBalance,1000);
	List<User> users = userMapper.selectList(wrapper);
}

自定义sql

但是以上的写法,本应写在mapper层的构造sql语句的代码却在service层实现,这是不符合业务代码规范的
除了where条件部分在Wrapper定义,其他部分自己在mapper或mapper映射xml中定义
service层

@Test
void testCustomSqlUpdate(){
	List<Long> ids = List.of(1L, 2L, 4L);
//        UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
//                .setSql("balance = balance - 200")
//                .in("id",ids);
	int amount = 200;
	QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id",ids);

	userMapper.updateBalanceByIds(wrapper,amount);
}

mapper层

void updateBalanceByIds(@Param(Constants.WRAPPER) QueryWrapper<User> wrapper,@Param("amount") int amount);

映射xml中

<update id="updateBalanceByIds">
	update user set balance = balance - #{amount} ${ew.customSqlSegment}
</update>

IService

特别简单常用的用类似userService.save(user);//省去service层和mapper层的业务逻辑代码
一般简单常用的用userMapper.insert(user);//省去mapper层的业务逻辑代码
其他的单表操作用Wrapper+自定义构造器的方式;//省去在xml文件中where那一大串的动态sql编写
多表查询还是只能用mybatis老老实实常规去写

  • public interface IUserService extends IService<User>//继承IService指定实体User
  • public class IUserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService//继承ServiceImpl指定mappper泛型UserMapper,使用Service时不仅可调用BaseMapper中的方法,还可调用UserMapper新增的方法
  • public interface UserMapper extends BaseMapper<User>//继承BaseMapper
    void testSaveUser(){
        User user = new User();
        user.setUsername("lilei");
        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);
    }
很好用的一个简化代码的工具
<dependency>
	<groupId>cn.hutool</groupId>
	<artifactId>hutool-all</artifactId>
	<version>5.8.16</version>
</dependency>
简单基础业务
@Api(tags = "用户管理接口")
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
    private final IUserService userService;


    @ApiOperation("新增用户接口")
    @PostMapping
    public void saveUser(@RequestBody UserFormDTO userFormDTO){
        User user = new User();
        BeanUtils.copyProperties(userFormDTO, user);
        userService.save(user);
    }

    @ApiOperation("根据id删除用户")
    @GetMapping("/{id}")
    public void deleteUserById(@ApiParam("用户id") @PathVariable("id") Long id){
        userService.removeById(id);
    }

    @ApiOperation("根据id查询用户")
    @GetMapping("/{id}")
    public UserVO queryUserById(@ApiParam("用户id") @PathVariable("id") Long id){
        User user = userService.getById(id);
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        return userVO;
    }

    @ApiOperation("根据id批量查询用户")
    @GetMapping
    public List<UserVO> queryUserByIds(@ApiParam("用户id集合") @PathVariable("ids") List<Long> ids){
        List<User> users = userService.listByIds(ids);
        return BeanUtil.copyToList(users, UserVO.class);
    }


}
复杂业务
@PutMapping("/{id}/deduction/{money}")
@ApiOperation("扣减用户余额")
public void deductMoneyById(@ApiParam("用户id") @PathVariable("id") Long id,
@ApiParam("扣减的金额") @PathVariable("money") Integer money){
	userService.deductionBalance(id,money);
}
@Service
public class IUserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Override
    public void deductionBalance(Long id, Integer money) {
        //查询用户
        User user = this.getById(id);

        //校验用户状态
        if (user==null || user.getStatus()==2){
            throw  new RuntimeException("用户状态异常");
        }
        //校验余额是否充足
        if(user.getBalance()<money){
            throw new RuntimeException("用户余额不足");
        }
        //扣减余额

        baseMapper.deductBalance(id,money);

    }
}
lambda方法
@Override
public List<User> queryUsers(UserQuery query) {

	String name = query.getName();
	Integer status = query.getStatus();
	Integer minBalance = query.getMinBalance();
	Integer maxBalance = query.getMaxBalance();

	return 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)
			.list();

}
public void deductionBalance(Long id, Integer money) {
	//查询用户
	User user = this.getById(id);

	//校验用户状态
	if (user==null || user.getStatus()==2){
		throw  new RuntimeException("用户状态异常");
	}
	//校验余额是否充足
	if(user.getBalance()<money){
		throw new RuntimeException("用户余额不足");
	}
	//扣减余额

	int remainBalance = user.getBalance()-money;
	lambdaUpdate()
			.set(User::getBalance,remainBalance)
			.set(remainBalance==0,User::getStatus,2)
			.eq(User::getId,id)
			.eq(User::getBalance,user.getBalance())//乐观锁,防止多个用户同时访问的情况
			.update();

}
Iservice的批量新增

sql的一次提交相当于一次网络请求,代码执行时网络请求最耗时

private User buildUser(int i){
	User user = new User();
	user.setUsername("user_"+i);
	user.setPassword("123456");
	user.setPhone(""+(18688190000L+i));
	user.setBalance(2000);
	user.setInfo("{\"age\":24}");
	user.setCreateTime(LocalDateTime.now());
	user.setUpdateTime(LocalDateTime.now());
	return user;
}

@Test
void testSaveOneByOne(){
	long b = System.currentTimeMillis();
	for (int i = 1; i < 100000; i++) {
		userService.save(buildUser(i));
	}
	long e = System.currentTimeMillis();
	System.out.println("耗时:"+(e-b));
	//耗时:77189ms,提交100000次
}

@Test
void testSaveBatch(){
	List<User> list = new ArrayList<>(1000);
	long b = System.currentTimeMillis();
	for (int i = 1; i < 100000; i++) {
		list.add(buildUser(i));
		if(i%1000==0){
			userService.saveBatch(list);//批量提交基于JDBC的预编译
			list.clear();
		}
	}
	long e = System.currentTimeMillis();
	System.out.println("耗时:"+(e-b));
	//rewriteBatchedStatements=false,耗时:10078ms,提交1000次
	//rewriteBatchedStatements=true,,mysql的配置,可以通过写动态sql实现,但这样太费事了,耗时:4414ms,提交1次,1000次预编译成1次
}

扩展功能

代码生成器

MyBatisPlus插件(二次元头像),很好用,自动生成entity,controller等基本代码

静态工具

为了避免循环依赖,就是UserService里需要注入AddressService,AddressService里需要注入UserService的情况

@Override
public UserVO queryUserAndAddressById(Long id) {
	//根据id查询用户信息和地址信息
	User user = this.getById(id);
	if (user==null||user.getStatus()==2){
		throw  new RuntimeException("用户状态异常");
	}
	List<Address> addresses = Db.lambdaQuery(Address.class)
			.eq(Address::getUserId,id)
			.list();
	UserVO userVO = BeanUtil.copyProperties(user,UserVO.class);
	if(CollUtil.isNotEmpty(addresses)){
		userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));
	}
	return userVO;
}

逻辑删除

比如用户在前端点击删除订单,但订单数据不会在后端被删除,因为很重要,这时就是逻辑删除
思路就是在表中添加一个字段标记数据是否被删除,删除了置为1,查询时只查询标记为0的数据
但是mp已经设定这样的sql规则,只需在配置文件中配置逻辑删除字段名和值

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: flag
      logic-delete-value: 1
      logic-not-delete-value: 0

枚举处理器

为了适配java数据类型和sql数据类型之间的转换
配置信息

mybatis-plus:
  configuration:
    default-enum-type-handler: xx.xx.xx.MyEnumTypeHandler

加注解@EnumValue,@JsonValue

@Getter
@AllArgsConstructor
public enum GradeEnum {
    PRIMARY(1, "小学"),
    SECONDARY(2, "中学"),
    HIGH(3, "高中");

    @EnumValue // 标记数据库存的值是code
	@JsonValue
    private final int code;
    // 其他属性...
}

json处理器

java中实体类向sql中json数据的转换

//如果是对象,直接用MP内置的Handler,JacksonTypeHandler或FastjsonTypeHandler
 @TableField(typeHandler = FastjsonTypeHandler.class)
//@TableField(typeHandler = JacksonTypeHandler.class)
private CompaniesProject companiesProject;
//然后在类上增加上 autoResultMap = true 属性
@TableName(value = "table_name",autoResultMap = true)

插件功能

分页插件(简单条件分页查询)

//需要配置mp的第三方类
@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;

    }

}
void testPageQuery(){
	int pageNo = 1, pageSize = 2;
	//1.准备分页条件
	//分页条件
	Page<User> page = Page.of(pageNo, pageSize);
	//排序条件
	page.addOrder(new OrderItem("balance", true));
	page.addOrder(new OrderItem("id", true));
	//2.分页查询
	Page<User> p = userService.page(page);
	//3.解析
	long total = p.getTotal();
	System.out.println(total);
	long pages = p.getPages();
	System.out.println(pages);
	List<User> records = p.getRecords();
	records.forEach(System.out::println);
}

通用分页实体(分页查询一般实现)

在Service层

@Override
public PageDTO<UserVO> queryUsersPage(UserQuery query) {
	String name = query.getName();
	Integer status = query.getStatus();
	//构建查询条件
	Page<User>  page = Page.of(query.getPageNo(), query.getPageSize());
	//排序条件
	if(StrUtil.isNotBlank(query.getSortBy())){
		//排序字段不为空
		page.addOrder(new OrderItem(query.getSortBy(),query.getIsAsc()));
	}
	else{
		//为空,默认按照更新时间排序
		page.addOrder(new OrderItem("update_time",query.getIsAsc()));
	}
	//分页查询
	Page<User> p = lambdaQuery()
			.like(name != null, User::getUsername, name)
			.eq(status != null, User::getStatus, status)
			.page(page);
	//封装VO结果
	PageDTO<UserVO> pageDTO = new PageDTO<>();
	pageDTO.setTotal(p.getPages());
	pageDTO.setPages(p.getPages());
	List<User> users = p.getRecords();
	if(CollUtil.isEmpty(users)){
		pageDTO.setList(Collections.emptyList());
		return pageDTO;
	}
	List<UserVO> userVOS = BeanUtil.copyToList(users, UserVO.class);
	pageDTO.setList(userVOS);
	//返回
	return pageDTO;
}

通用分页实体与MP转换

听不懂,回来再看吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值