缘博客
发布日期

Web实战:部门查询与前后端联调

作者
  • 姓名
    社交账号

部门列表查询

部门管理的第一个功能是查询全部部门。

接口:

请求方式:GET
请求路径:/depts
请求参数:无

查询结果通过统一的 Result 返回。


三层架构中的调用流程

查询部门时,每一层的职责:

浏览器
  ↓ GET /depts
Controller
Service
Mapper
MySQL

具体职责:

Controller
接收请求
调用Service
封装响应

Service
处理业务逻辑
调用Mapper

Mapper
执行SQL
访问数据库

SQL:

select *
from dept
order by update_time desc;

Mapper层

@Mapper
public interface DeptMapper {

    @Select("""
        select *
        from dept
        order by update_time desc
        """)
    List<Dept> findAll();
}

Service层

接口:

public interface DeptService {
    List<Dept> findAll();
}

实现类:

@Service
public class DeptServiceImpl implements DeptService {

    @Autowired
    private DeptMapper deptMapper;

    @Override
    public List<Dept> findAll() {
        return deptMapper.findAll();
    }
}

Controller层

@RestController
public class DeptController {

    @Autowired
    private DeptService deptService;

    @GetMapping("/depts")
    public Result findAll() {
        List<Dept> deptList = deptService.findAll();
        return Result.success(deptList);
    }
}

完整调用链:

GET /depts
DeptController.findAll()
DeptService.findAll()
DeptMapper.findAll()
select * from dept ...
List<Dept>
Result.success(...)
JSON响应

MyBatis数据封装

MyBatis 查询数据后,会尝试将数据库字段自动封装到实体对象。

如果名称一致:

数据库字段:name
Java属性:name

可以直接完成映射。

但部门表中:

create_time
update_time

Java 实体类通常写成:

private LocalDateTime createTime;
private LocalDateTime updateTime;

名称不完全一致,因此需要处理映射关系。


解决字段映射问题

手动结果映射

@Results({
    @Result(column = "create_time", property = "createTime"),
    @Result(column = "update_time", property = "updateTime")
})
@Select("""
    select id, name, create_time, update_time
    from dept
    order by update_time desc
    """)
List<Dept> findAll();

SQL起别名

@Select("""
    select id,
           name,
           create_time createTime,
           update_time updateTime
    from dept
    order by update_time desc
    """)
List<Dept> findAll();

开启驼峰映射

更常用的方式是在配置文件中开启:

mybatis:
  configuration:
    map-underscore-to-camel-case: true

之后:

create_time → createTime
update_time → updateTime

即可自动映射。


前后端联调

后端接口测试通过之后,还需要与前端项目进行联调。

前端项目运行在:

http://localhost:90

后端 Tomcat 运行在:

http://localhost:8080

前端实际发送请求:

http://localhost:90/api/depts

但后端真正的接口地址是:

http://localhost:8080/depts

这里通过 Nginx 完成反向代理。


什么是反向代理

反向代理可以理解为:客户端只访问代理服务器,再由代理服务器把请求转发到真正的后端服务器。

浏览器
Nginx :90
Spring Boot / Tomcat :8080

反向代理可以带来:

  • 后端地址对客户端透明
  • 统一请求入口
  • 配置更灵活
  • 可以实现负载均衡

Nginx反向代理配置

server {
    listen 90;

    location ^~ /api/ {
        rewrite ^/api/(.*)$ /$1 break;
        proxy_pass http://localhost:8080;
    }
}

前端发送:

http://localhost:90/api/depts

匹配:

location ^~ /api/

再通过:

rewrite ^/api/(.*)$ /$1 break;

将:

/api/depts

重写为:

/depts

最后:

proxy_pass http://localhost:8080;

转发到:

http://localhost:8080/depts

整个过程:

localhost:90/api/depts
      Nginx
        ↓ 去掉 /api
localhost:8080/depts
   Spring Boot

小结

查询部门这个功能把后端 Web 的完整调用链串了起来:

前端
Nginx
Controller
Service
Mapper
MySQL

同时还涉及 MyBatis 自动结果封装、下划线转驼峰以及 Nginx 反向代理。