Spring Boot中实现一个递归获取省市区行政区划代码
写于20240924 10:23
在Spring Boot中实现一个递归获取省市区行政区划代码的功能,可以按照以下步骤进行。我们将使用Spring Data JPA来与数据库交互,并构建一个递归的方法来获取层级数据。
首先这里数据库使用
mysql
,
jdk17
springboot3.3.2
下面开始实际案例分析:
1、定义实体类 Geographic
首先,定义一个实体类 Geographic
,它将映射到数据库中的行政区划表。假设表中有字段 id
、name
和 parentId
来表示每个区域的唯一标识、名称及其父区域的ID。
package com.dependencies.springdatajpa.entity;
import jakarta.persistence.*;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* @author zhizhou 2024/9/24 09:56
*/
@Data
@Entity
@Table(name = "geographic")
public class Geographic {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String code;
@Column(name = "parent_id")
private Long parentId;
@Transient
private List<Geographic> children = new ArrayList<>();
}
2、创建 GeographicRepository
使用Spring Data JPA创建一个仓库接口,以便于查询数据库。
package com.dependencies.springdatajpa.repository;
import com.dependencies.springdatajpa.entity.Geographic;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @author zhizhou 2024/9/24 09:55
*/
@Repository
public interface GeographicRepository extends JpaRepository<Geographic, Long> {
List<Geographic> findByParentId(Long parentId);
}
3、实现 GeographicService
在服务层中实现递归获取行政区划数据的方法 getGeographicListData
。
package com.dependencies.springdatajpa.service;
import com.dependencies.springdatajpa.entity.Geographic;
import com.dependencies.springdatajpa.repository.GeographicRepository;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
/**
* @author zhizhou 2024/9/24 09:55
*/
@Service
public class GeographicService {
@Autowired
private GeographicRepository geographicRepository;
/**
* V2 递归获取行政区划数据
* @param parentId 父级ID,根节点传入null或0
* @return 行政区划列表
*/
public List<Geographic> getGeographicListData(Long parentId) {
List<Geographic> list = geographicRepository.findByParentId(parentId);
for (Geographic geographic : list) {
// 递归获取子节点
List<Geographic> children = getGeographicListData(geographic.getId());
geographic.setChildren(children);
}
return list;
}
//V2 批量查询:一次性查询所有数据,并在内存中构建树结构
public List<Geographic> getGeographicListDataAll(Long parentId) {
List<Geographic> allGeographics = geographicRepository.findAll();
return buildTree(allGeographics, parentId);
}
private List<Geographic> buildTree(List<Geographic> all, Long parentId) {
List<Geographic> tree = new ArrayList<>();
for (Geographic geo : all) {
if (geo.getParentId().equals(parentId)) {
geo.setChildren(buildTree(all, geo.getId()));
tree.add(geo);
}
}
return tree;
}
}
4、创建 GeographicController
控制器
为了让前端能够调用该服务,我们可以创建一个控制器来暴露一个REST API端点。
获取行政区划列表这里提供了两种思路,依据实际场景进行实践,数据层级相对较小并发量较小时都可以采用,但当书记层级较深,频繁的查询数据库会造成没必要的资源消耗。
这里推荐第二种方案,批量查询:一次性查询所有数据,并在内存中构建树结构,省市区大概3180多条,数据量相对还好,内存大概不到1M,500KB左右。
package com.dependencies.springdatajpa.controller;
import com.dependencies.springdatajpa.entity.Geographic;
import com.dependencies.springdatajpa.service.GeographicService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author zhizhou 2024/9/24 09:54
*/
@RestController
@RequestMapping("/api/geographics")
public class GeographicController {
@Autowired
private GeographicService geographicService;
/**
* 获取行政区划列表(遍历查询数据库)
* @param parentId 父级ID,可选参数,根节点传入null或不传
* @return 行政区划列表
*/
@GetMapping
public List<Geographic> getGeographics(@RequestParam(value = "parentId", required = false) Long parentId) {
// 如果parentId为空,设为0或null,根据实际情况
if (parentId == null) {
parentId = 0L; // 假设根节点的parentId为0
}
return geographicService.getGeographicListData(parentId);
}
/**
* 获取行政区划列表(批量查询:一次性查询所有数据,并在内存中构建树结构,省市区大概3180多条,数据量相对还好,内存大概不到1M,500KB左右)
* @param parentId 父级ID,可选参数,根节点传入null或不传
* @return 行政区划列表
*/
@GetMapping(value="/all")
public List<Geographic> getGeographicsAll(@RequestParam(value = "parentId", required = false) Long parentId) {
// 如果parentId为空,设为0或null,根据实际情况
if (parentId == null) {
parentId = 0L; // 假设根节点的parentId为0
}
return geographicService.getGeographicListDataAll(parentId);
}
}
5、数据库配置文件
确保在 application.properties
中正确配置了数据库连接。使用MySQL数据库:
spring.application.name=spring-data-jpa
# MySQL Database configuration
spring.datasource.url=jdbc:mysql://localhost:3306/spring-dependencies?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
6、 示例数据
确保数据库中有相应的行政区划数据。例如:
id | name | code | parent_id |
---|---|---|---|
1 | 北京市 | 110000 | 0 |
2 | 朝阳区 | 110105 | 1 |
3 | 海淀区 | 110108 | 1 |
4 | 上海市 | 310000 | 0 |
5 | 浦东新区 | 310115 | 4 |
6 | 徐汇区 | 310104 | 4 |
7、debug测试
启动Spring Boot应用后,可以通过以下方式测试API:
-
获取所有省级行政区(假设parentId=0为省级)
GET http://localhost:8080/api/geographics?parentId=0
-
获取特定省市的子级行政区
GET http://localhost:8080/api/geographics?parentId=1
-
获取特定省市的子级行政区
GET http://localhost:8080/api/geographics/all?parentId=1