苍穹外卖dya02

单表增删改查设置

新增员工

1.需求分析设计

接口设计

  • 根据产品原型,为属性添加限制(例如 uid唯一,手机号11位数字等)

  • 方法post,路径/admin/employee

  • 前端给后端是json格式传入,后端给前端是Result封装json为JAVA对象

项目约定:管理端请求前缀为/admin,用户端请求/user为前缀

数据库设计

  • 数据库字段以及约束

2.代码开发

  • 根据接口设DTO

    也可以直接传给employee实体,但是当前端传来的json数据与实体中的属性差别较大,推荐使用DTO封装传来的属性

    @Data
    public class EmployeeDTO implements Serializable{
    private Long id;
    private String username;private String name;private String phone;private String sex;
    private String idNumber;
    }
    
  • DTO的值赋值给实体

    前提 属性名一致

    BeanUtils.copyproperties(DTO,entity)
    

    其他实体属性,需手动设置

    //设置密码
    employee.setPassword(DigestUtils.md5DigestAsHex("123456".getbytes));
    //getbytes是因为MD5是针对字节加密的,因此要进行转换
    

3.测试

1.接口文档(为主):

安全校验:设置拦截器(interceptor)获取http的header(即携带token的地方)

配置全局token:没有登陆,无法通过校验,配置token(拿到身份)进行测试

2.前后端联调

ngix登录,尝试添加员工,在数据库中看是否成功·

4. 完善功能

  • 用户名重复异常信息未处理

    描述:将重复员工信息进行注册报错500

    解决:将控制台报错信息交给全局异常handler进行捕获,状态变为200,这里是将后台报错信息传给前面

    注意:如果登录token过期了,会返回401,需要重新获取,重新配置全局token

    @ExceptionHandler
        public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
            //Duplicate entry 'wu' for key 'idx_username'
            //目的为了提示xx已经存在 1.捕获这条信息2.以空格分割转数组元素,读取第三个数组,result对象传出去
            String message = ex.getMessage();
            //如果是此种异常处理办法
            if(message.contains("Duplicate entry" )){
                String[] split=message.split(" ");
                String username = split[2];
                String msg = username + MessageConstant.ALRERDY_LIVE;
                return Result.error(msg);
    
            }
            else {
                String msg = MessageConstant.UNKNOWN_ERROR;
                return Result.error(msg);
            }
    
        }
    
    }
    
  • 创建人和修改人设置成固定值了“serviceimpl里面的save方法”

    描述:在新增用户时,修改者和创建者(当前登录账号)是默认值,需要改为动态获取

    已知账号登陆时,需要先传给后端,后端生成JWT令牌,JWT令牌传给前端,前端每次请求后端都携带一个token并经过后端校验后执行操作。

    controller里jwt令牌创建是包含用户id,因此可以通过解码token得到id,传导给impl.save方法.

    controller–>service需要通道Threadlocal,因为每次前端传来请求就会分配一个线程,其中controller/intercreptor/service在同一个线程中,可以通过线程的局部变量ThreadLocal传导中间变量。

    安全隐患:线程会复用,一定要在 finally(写在拦截器) 中 remove(),否则下个请求可能“捡到”上一个用户的上下文,使用标准的 Spring Security 配置:不用自己清理,SecurityContextPersistenceFilter 会清掉。
    但如果你自己写 ThreadLocal(绕过 Spring Security),那就必须自己在 finally 里 remove()

    //自己写
    public static Long getUserId() {
            return userIdHolder.get();
        }
    //用spring
    public static Long getUserId() {
            return ThreadLocal.get();
        }
    /**在 Filter/Interceptor 中验证 JWT → 构造认证对象 → 放进 SecurityContext(封装的工具类本质上基于 ThreadLocal),然后在 Controller/Service 用 @AuthenticationPrincipal 或封装的 UserContext.get() 取即可。
    

分页查询

public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
        //select *from limit 0,10
        //mybatis 通过pagehelper 拦截器将其拆分为两句SQL执行
        //此句记录当前进程的页面也页面条目
        PageHelper.startPage(employeePageQueryDTO.getPage(),employeePageQueryDTO.getPageSize());

        //必须返回page,sql语句执行,拦截器生效,开始改写SQL语句注入计数信息和分页信息
        Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);
        Long total = page.getTotal();
        //这里使用实体而非传输对象(DTO)是因为数据库返回的是实体,与上面的对应,不同层之间数据传输用对象
        List<Employee> records = page.getResult();

        return new PageResult(total, records);
    }

给前端返回的日期以数组形式存在

解决·

·//方式一:在属性上加入注解,对日期进行格式化,JsonFonmat(pattern = "yyyy-MN-dd HH:mm: ss")
                                    private LocalDateTime dpdateTime;

·//方式二:在WebMvcConfiguration中扩展Spring MVC的消息转换器,统一对日期类型进行格式化处理
s
**
*扩展mvc框架的消息转换器*@param converters
*l
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("开始扩展消息转换器...");
//创建—个消息转化器对象
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpNessageConverter();//设置对象转换器,可以将ava对象转为json字符串
converter.setobjectMapper(new JacksonobjectMapper( ));
//将我们自己的转换器放入spring MVc框架的容器中
converters.add(o, converter) ;

知识补充

1.@Autowired:依赖注入

@Autowired
private EmployeeService employeeService;

//Spring,请帮我把 EmployeeService 这个对象准备好,并自动塞进(注入)这个 employeeService 变量里。”

项目含义
@Autowired让 Spring 自动注入对象(依赖注入)
EmployeeService你要注入的对象类型(接口 / 类)
不用 new由 Spring 统一管理对象的创建与销毁
好处解耦、可维护、方便测试与扩展

2.数据流完整流程(Controller → Service → Mapper → DB)

前端给后端传数据的时候,数据以DTO的形式存在,而后端和数据库传数据的时候,数据需要以实体的形式存在

前端发请求(JSON)

DTO(接收请求的数据)

Controller 接收 DTO

Service 层处理业务逻辑(Service(接口定义,@Autowired)—>Serviceimpl(具体实现))

Entity / PO(对应数据库表)

Mapper (操作数据库的接口无需 Impl,MyBatis 自动实现)

数据库(真实存储数据)

Entity(查询结果封装成对象)

VO(封装成返回前端的视图对象)

Controller 封装 Result 返回给前端

3.DTO与实体的关系,mapper与持久层的关系

DTO是前端传给后端的,后端给数据库(持久层)需要使用mapper

4.Mapper与Service关系

层级是否要自己写实现类实现逻辑写在哪里谁来完成“接口 + 实现”的拼接
Service✅ 是(serviceimpl)写在 ServiceImpl自己写代码
Mapper❌ 否写在 XML 或注解中✅ MyBatis 自动拼接
//MAPPER流程
//serviceimpl.java
@Autowired
public Employeemapper employeemapper//注入
    //将DTO转为实体对象
    //相同属性赋值
    BeanUntils.copyproperties(DTO,ENETITY)
    //其他属性手动赋值,注意常量
    //转换完成插入到数据库mapper
    employeemapper.insert(entity)
//Employeemapper.java
    //在这里定义insert方法的实现
    //这里使用注解实现(单表增删改查较为简单),复杂的可写入XML实现
     @Insert("SQL语句")
    void insert(Employee employee);
//这里方法为空是约定俗成的,因为mybatis会自动执行注解里面的东西,mapper里面只是接口定义所以直接为空即可,mybatis会把二者自动组合在一起,
 
  1. 拓展XML写mapper定义办法(适合复杂场景开发)

    //接口定义
    @Mapper
    public interface EmployeeMapper {
        void insert(Employee employee);
        Employee getById(Long id);
    }
    
    //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.xxx.mapper.EmployeeMapper">
    
        <!-- 插入操作 -->
        <insert id="insert" parameterType="Employee">
            INSERT INTO sky_take_out.employee
            (name, username, password, phone, sex, id_number, create_time, update_time, create_user, update_user, status)
            VALUES
            (#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})
        </insert>
    </mapper>
     
    //在application.yaml配置mybatis
      mybatis:
      mapper-locations: classpath:mapper/*.xml
      type-aliases-package: com.xxx.entity
    
    
    
  2. idea连接数据库

    右侧边栏datasource→mysql→下载相关配置【下载失败使用下面的problem里的修复功能会自动扫描缺失的jar包】→输入数据库用户名和密码→左下角测试连接→连接成功apply,完成连接,右键点击连接名称,选择想要连接的数据库。(默认是·default)

    IDEA自动提示sql语句设置

    setting-Language–SQL dialects两个都设置为mysql,返回sql语句:alt+enter选择language injection选择mysql。

7.常见http状态码含义

状态码中文解释出现原因谁的错
200成功数据或操作正常✅双方都正常
401未认证Token 问题/权限不足❌客户端问题
500服务器错误代码或数据库异常❌服务端问题

8.ThreadLocal
ThreadLocal并不是一个Thread,而是Thread的局部变量。
ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。

使用方法

txt
ThreadLocal常用方法:
. public void set(T value)设置当前线程的线程局部变量的值.

public T get() 返回当前线程所对应的线程局部变量的值
. public void remove() 移除当前线程的线程局部变量
一般被封装起来使用

9.PageHelper与SQL

startPage 在 Service 里调用,SQL 在 Mapper 里执行,两个文件需要通信,所以用 ThreadLocal 作为它们的共同变量,和前面的controller要和service通信一样,通过threadlocal

  • pagehelper在mybatics启动时自动注册为拦截器。识别到startpage就记录当前threadlocal page,pagesize
  • 当执行sql语句(select all)时 ,自动拦截变成1.select all 2.limit offset pagesize

如果不通过pagehelper,也可以自己在mapper里面写SQL语句,等效

SELECT COUNT(*) FROM employee WHERE name LIKE CONCAT('%', #{name}, '%');
SELECT * FROM employee WHERE name LIKE CONCAT('%', #{name}, '%') LIMIT #{offset}, #{pageSize};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值