spring boot 完整版规范工程代码(SpringBoot + Spring Data JPA + Service 分层 + DTO)
前端 ↔ DTO ↔ Controller ↔ Service ↔ Repository ↔ Entity(数据库)
核心规则:
- Entity:绑定数据库,绝不直接返回前端
- DTO:专门用于接口传输,隔离敏感字段
- Controller 只和 DTO 打交道
- Service 内部做 DTO ↔ Entity 转换(Mapper 映射逻辑)
一、包结构
plaintext
com.eazybytes.jobportal
├─ JobportalApplication 启动类
├─ entity
│ └─ Company.java // JPA数据库实体
├─ dto
│ └─ CompanyDTO.java // 对外传输DTO(无@Entity)
├─ repository
│ └─ CompanyRepository.java
├─ service
│ ├─ ICompanyService.java
│ └─ impl
│ └─ CompanyServiceImpl.java
└─ controller
└─ CompanyController.java
1)Entity 实体:Company.java(映射数据库表)
java
运行
package com.eazybytes.jobportal.entity; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Entity @Table(name = "company") @Data @NoArgsConstructor @AllArgsConstructor public class Company { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; private String description; // 假设存在敏感字段!不希望暴露给前端 private String adminPhone; }
2)DTO 对象:CompanyDTO.java(给前端使用)
⚠️ 不加@Entity!不和数据库绑定,只保留前端需要字段
java
运行
package com.eazybytes.jobportal.dto; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class CompanyDTO { private Integer id; private String name; private String description; // 注意:没有 adminPhone 敏感字段! }
3)Repository 持久层
java
运行
package com.eazybytes.jobportal.repository; import com.eazybytes.jobportal.entity.Company; import org.springframework.data.jpa.repository.JpaRepository; public interface CompanyRepository extends JpaRepository<Company, Integer> { // 仅数据库操作 }
4)Service 接口
❗注意:Service 方法出入参全部使用 DTO,不再向外暴露 Entity
java
运行
package com.eazybytes.jobportal.service; import com.eazybytes.jobportal.dto.CompanyDTO; import java.util.List; public interface ICompanyService { List<CompanyDTO> getAllCompanies(); CompanyDTO getCompanyById(Integer id); CompanyDTO createCompany(CompanyDTO dto); }
5)Service 实现类【重点:在这里写转换逻辑 Mapper Logic】
java
运行
package com.eazybytes.jobportal.service.impl; import com.eazybytes.jobportal.dto.CompanyDTO; import com.eazybytes.jobportal.entity.Company; import com.eazybytes.jobportal.repository.CompanyRepository; import com.eazybytes.jobportal.service.ICompanyService; import org.springframework.stereotype.Service; import java.util.List; import java.util.stream.Collectors; @Service public class CompanyServiceImpl implements ICompanyService { private final CompanyRepository companyRepository; // 构造器注入(官方推荐) public CompanyServiceImpl(CompanyRepository companyRepository) { this.companyRepository = companyRepository; } @Override public List<CompanyDTO> getAllCompanies() { // 1. 查询数据库,得到Entity列表 List<Company> entityList = companyRepository.findAll(); // 2. Mapper映射:Entity → DTO return entityList.stream().map(entity -> { CompanyDTO dto = new CompanyDTO(); dto.setId(entity.getId()); dto.setName(entity.getName()); dto.setDescription(entity.getDescription()); // 敏感字段adminPhone直接忽略,不传给前端 return dto; }).collect(Collectors.toList()); } @Override public CompanyDTO getCompanyById(Integer id) { Company entity = companyRepository.findById(id).orElse(null); if (entity == null) return null; // entity转dto CompanyDTO dto = new CompanyDTO(); dto.setId(entity.getId()); dto.setName(entity.getName()); dto.setDescription(entity.getDescription()); return dto; } @Override public CompanyDTO createCompany(CompanyDTO dto) { // DTO → Entity:接收前端参数转为数据库实体 Company entity = new Company(); entity.setName(dto.getName()); entity.setDescription(dto.getDescription()); entity.setAdminPhone("内部管理员号码"); // 后端内部填充敏感数据 Company savedEntity = companyRepository.save(entity); // 保存完成,再转回DTO返回前端 CompanyDTO resultDto = new CompanyDTO(); resultDto.setId(savedEntity.getId()); resultDto.setName(savedEntity.getName()); resultDto.setDescription(savedEntity.getDescription()); return resultDto; } }
6)Controller(只和 DTO 交互,完全看不到 Entity)
java
运行
package com.eazybytes.jobportal.controller; import com.eazybytes.jobportal.dto.CompanyDTO; import com.eazybytes.jobportal.service.ICompanyService; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/company") public class CompanyController { private final ICompanyService companyService; public CompanyController(ICompanyService companyService) { this.companyService = companyService; } @GetMapping public List<CompanyDTO> getAll() { return companyService.getAllCompanies(); } @GetMapping("/{id}") public CompanyDTO getById(@PathVariable Integer id) { return companyService.getCompanyById(id); } @PostMapping public CompanyDTO add(@RequestBody CompanyDTO companyDTO) { return companyService.createCompany(companyDTO); } }
二、完整数据流对照你那张 DTO 示意图
- 前端调用接口 → 传入
CompanyDTO - Controller → 调用 Service
- Service:DTO → Entity,调用 Repository 保存数据库
- 查询时:Repository 拿到
Company(Entity) - Service 内部做映射(Mapper Logic):Entity → DTO
- DTO 返回前端,敏感字段
adminPhone直接被过滤

浙公网安备 33010602011771号