黑马点评学习笔记02(Mabatis—plus)

前言

首先登陆界面首先还是基于session实现(我当时也很惊讶,怎么用这个😯,结果看了目录,后面就用Redis和Jwt令牌)可能就是一个过渡吧,先用Session实现一遍,然后才能突出后面的很厉害。其实里面还是有很多小知识点的(Mybatis-plus)很值得学习,这个Session实现就当小配角,陪跑🤪我们学。

先来看看登陆界面的代码实现:

UserController类 :

java 复制代码
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private IUserService userService;

    /**
     * 发送手机验证码
     */
    @PostMapping("code")
    public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {
        // TODO 发送短信验证码并保存验证码
        return userService.sendCode(phone, session);
    }

    /**
     * 登录功能
     * @param loginForm 登录参数,包含手机号、验证码;或者手机号、密码
     */
    @PostMapping("/login")
    public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){
        // TODO 实现登录功能
        return userService.login(loginForm, session);
    }

IUserService 接口:

java 复制代码
package com.hmdp.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.entity.User;

import javax.servlet.http.HttpSession;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
public interface IUserService extends IService<User> {

    Result sendCode(String phone, HttpSession session);
    Result login(LoginFormDTO loginForm, HttpSession session);

}

IUserService 接口实现类:

java 复制代码
package com.hmdp.service.impl;

import...

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;


    @Override
    public Result sendCode(String phone, HttpSession session) {
        //1.校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            // 2.如果不符合,返回错误信息
            return Result.fail("手机号格式错误!");
        }

        //3.符合,生成验证码
        String code = RandomUtil.randomNumbers(6);

        //4.保存验证码到session
        session.setAttribute("code", code);

        //5.发送验证码
        System.out.println("发送短信验证码成功,验证码:" + code);

        //5.返回ok
        return Result.ok();
    }

    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        //1.校验手机号
        String phone = loginForm.getPhone();
        if (RegexUtils.isPhoneInvalid(phone)) {
            // 2.如果不符合,返回错误信息
            return Result.fail("手机号格式错误!");
        }

        //2.校验验证码 不一致直接报错 从session中获取
        Object cachecode = session.getAttribute("code");
        String code = loginForm.getCode();
        if (cachecode == null || !cachecode.toString().equals(code)) {
            return Result.fail("验证码错误!");
        }

      

        //3.一致,根据手机号查询用户 select * from tb_user where phone = ?
        User user = query().eq("phone", phone).one();

        //4.判断用户是否存在
        if (user == null) {
            //5.不存在,创建新用户
            user = createUserWithPhone(phone);

        }

        //6.存在,保存用户信息到session
                session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));

        return Result.ok();
    }

    private User createUserWithPhone(String phone) {
        //1.创建用户
        User user = new User();
        user.setPhone(phone);
        user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
        //2.保存用户
        save(user);
        return user;

    }

}

就是很常见的基于session存储来实现的登陆界面,以及Interceptor拦截器,很简单,之前写苍穹外卖的时候都写过了,所以只总结一些新的东西。

我们在保存用户信息和查找用户验证码的时候,并没有像之前一样新建一个Mapper类用SQL语句查找,只是用到了save方法和query方法,这就用到了Mybatis-plus

🌟 MyBatis-Plus 是什么?

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

官方文档: https://baomidou.com

🙌登陆界面这里主要用到了MyBatis Plus的Service查询

通用 Service CRUD 封装IService接口,进一步封装 CRUD 采用 get 查询单行、remove删除、list 查询集合、page查询分页

使用步骤:

搭建项目:

  1. 引入依赖:
xml 复制代码
<!--mybatis plus场景-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>
  1. 启动类
java 复制代码
package com.hmdp;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("com.hmdp.mapper")
@SpringBootApplication
public class HmDianPingApplication {

    public static void main(String[] args) {
        SpringApplication.run(HmDianPingApplication.class, args);
    }

}
  1. application.yml
xml 复制代码
server:
  port: 8081
spring:
  application:
    name: hmdp
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/hmdp?useSSL=false&serverTimezone=UTC
    username: root
    password: 1234
  redis:
    host: localhost
    port: 6379
#    password: 123456
    lettuce:
      pool:
        max-active: 10
        max-idle: 10
        min-idle: 1
        time-between-eviction-runs: 10s
  jackson:
    default-property-inclusion: non_null # JSON处理时忽略非空字段
mybatis-plus:
  type-aliases-package: com.hmdp.entity # 别名扫描包
logging:
  level:
    com.hmdp: debug
  1. 定义一个UserService接口继承与MyBatisPlus提供的IService接口:
java 复制代码
在这里插入代码片package com.hmdp.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.entity.User;

import javax.servlet.http.HttpSession;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
public interface IUserService extends IService<User> {

    Result sendCode(String phone, HttpSession session);

    Result login(LoginFormDTO loginForm, HttpSession session);
}
  1. 定义一个UserService的实现类,并且继承与MyBatisPlus提供的ServiceImpl:
java 复制代码
package com.hmdp.service.impl;
import...

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;


    @Override
    public Result sendCode(String phone, HttpSession session) {
        //1.校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            // 2.如果不符合,返回错误信息
            return Result.fail("手机号格式错误!");
        }

        //3.符合,生成验证码
        String code = RandomUtil.randomNumbers(6);

        //4.保存验证码到session
//        session.setAttribute("code", code);

        //4.保存验证码到redis
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);

        //5.发送验证码
        System.out.println("发送短信验证码成功,验证码:" + code);

        //5.返回ok
        return Result.ok();
    }

    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        //1.校验手机号
        String phone = loginForm.getPhone();
        if (RegexUtils.isPhoneInvalid(phone)) {
            // 2.如果不符合,返回错误信息
            return Result.fail("手机号格式错误!");
        }

        //2.校验验证码 不一致直接报错 从session中获取
//        Object cachecode = session.getAttribute("code");
//        String code = loginForm.getCode();
//        if (cachecode == null || !cachecode.toString().equals(code)) {
//            return Result.fail("验证码错误!");
//        }

        //2.校验验证码 从redis中获取  不一致直接报错
        String cachecode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
        String code = loginForm.getCode();
        if (cachecode == null || !cachecode.toString().equals(code)) {
            return Result.fail("验证码错误!");
        }


        //3.一致,根据手机号查询用户 select * from tb_user where phone = ?
        User user = query().eq("phone", phone).one();

        //4.判断用户是否存在
        if (user == null) {
            //5.不存在,创建新用户
            user = createUserWithPhone(phone);

        }

        //6.存在,保存用户信息到session
//        session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));

        //6.存在,保存用户信息到redis

        //6.1 随机生成token,作为登录令牌
        String token = UUID.randomUUID().toString(true);

        //6.2 将用户对象转为hash存储
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO,new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue( true)
                        .setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()));

        //6.3 存储
        String tokenkey = LOGIN_USER_KEY + token;
        stringRedisTemplate.opsForHash().putAll( tokenkey, userMap);
        //6.4设置token有效期
        stringRedisTemplate.expire( tokenkey, LOGIN_USER_TTL, TimeUnit.MINUTES);

        //6.4返回token

        return Result.ok(token);
    }

    private User createUserWithPhone(String phone) {
        //1.创建用户
        User user = new User();
        user.setPhone(phone);
        user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
        //2.保存用户
        save(user);
        return user;

    }

}

把数据库代码提取出来

java 复制代码
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    public Result login(LoginFormDTO loginForm, HttpSession session) {
        ...
        User user = query().eq("phone", phone).one(); // ← 这里调用了 query()
        save(user); // ← 这里调用了 save()
    }

    private User createUserWithPhone(String phone) {
        User user = new User();
        user.setPhone(phone);
        user.setNickName("user_" + RandomUtil.randomString(10));
        save(user); // ← 保存用户
        return user;
    }
}

📚 一、save() 方法

来源:ServiceImpl 类

java 复制代码
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {

    @Autowired
    protected M baseMapper; // 自动注入你的 UserMapper

    @Override
    public boolean save(T entity) {
        return baseMapper.insert(entity) == 1; // 调用 BaseMapper 的 insert 方法
    }
}

所以:

java 复制代码
save(user);

实际上就是

java 复制代码
this.baseMapper.insert(user); // 通过 UserMapper 执行 SQL 插入
sql 复制代码
    save(user)
        ↓
ServiceImpl.save(user)
        ↓
baseMapper.insert(user)  ←← 这才是真正的数据库操作
        ↓
UserMapper.insert(user)  (由 MyBatis 执行 SQL)

🔍 三、query().eq("phone", phone).one() 又是怎么回事?

这是一套 链式编程 API,也是 MyBatis-Plus 的高级功能。

java 复制代码
query().eq("phone", phone).one();

等价于

java 复制代码
new LambdaQueryChainWrapper<User>(baseMapper)
    .eq(User::getPhone, phone)
    .one();

它做了什么?

  • query() → 创建一个查询构造器(内部使用 baseMapper)
  • .eq("phone", phone) → 添加条件:phone = ?
  • .one() → 执行查询,返回一条记录(相当于 SELECT * FROM tb_user WHERE phone = ? LIMIT 1)
sql 复制代码
    query().eq("phone", phone).one()
        ↓
new LambdaQueryChainWrapper<>(baseMapper)
        ↓
wrapper.eq("phone", phone)
        ↓
wrapper.one() → baseMapper.selectOne(wrapper)
        ↓
执行 SQL:SELECT * FROM tb_user WHERE phone = ? LIMIT 1

小白啊!!!写的不好轻喷啊🤯如果觉得写的不好,点个赞吧🤪(批评是我写作的动力)

...。。。。。。。。。。。...

...。。。。。。。。。。。...

相关推荐
_Brooke_21 小时前
CS336笔记2-Architectures,Hyperparameters
笔记
笨鸟笃行21 小时前
百日挑战——单词篇(第十五天)
学习
Nebula_g21 小时前
C语言应用实例:斐波那契数列与其其他应用
c语言·开发语言·后端·学习·算法
奋斗的牛马1 天前
FPGA—ZYNQ学习spi(六)
单片机·嵌入式硬件·学习·fpga开发·信息与通信
被遗忘的旋律.1 天前
Linux驱动开发笔记(十九)——IIC(AP3216C驱动+MPU6050驱动)
linux·驱动开发·笔记
我命由我123451 天前
Photoshop - Photoshop 工具栏(24)磁性套索工具
学习·ui·职场和发展·求职招聘·职场发展·课程设计·美工
自由日记1 天前
css学习9
前端·css·学习
wyiyiyi1 天前
【数据结构+算法】进栈顺序推算、卡特兰数与逆波兰表达式
汇编·数据结构·笔记·算法
Larry_Yanan1 天前
QML学习笔记(五十二)QML与C++交互:数据转换——时间和日期
开发语言·c++·笔记·qt·学习·ui·交互
TL滕1 天前
从0开始学算法——第二天(时间、空间复杂度)
数据结构·笔记·学习·算法