You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot REST API:关联实体的JSON暴露控制问题

实现Spring Boot下Order与Item一对多关系的REST API

我来帮你梳理下如何实现这四个API端点,基于Spring Boot + JPA的组合来搞定一对多的关联和接口需求:

1. 先搞定实体类映射

首先要正确配置Order和Item的一对多关联,同时解决序列化时的循环引用问题,这里用@JsonView来灵活控制不同接口返回的字段,比固定注解更灵活。

第一步:定义视图接口(用来控制序列化字段)

先创建一个单独的View类,里面放不同场景的视图标记接口:

public class View {
    // 仅返回Order基础信息,不含Items
    public interface OrderWithoutItems {}
    // 返回Order基础信息+关联的Items
    public interface OrderWithItems extends OrderWithoutItems {}
    // 返回Item基础信息+所属的Order
    public interface ItemWithOrder {}
}

第二步:Order实体类

注意order是SQL关键字,所以表名用orders:

import jakarta.persistence.*;
import com.fasterxml.jackson.annotation.JsonView;
import java.time.LocalDateTime;
import java.util.List;

@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @JsonView(View.OrderWithoutItems.class)
    private Long id;

    @JsonView(View.OrderWithoutItems.class)
    private String orderNumber;

    @JsonView(View.OrderWithoutItems.class)
    private LocalDateTime createTime;

    // 一对多关联Item,懒加载提升性能
    @JsonView(View.OrderWithItems.class)
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Item> items;

    // 构造器、getter、setter省略
}

第三步:Item实体类

import jakarta.persistence.*;
import com.fasterxml.jackson.annotation.JsonView;

@Entity
@Table(name = "items")
public class Item {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @JsonView(View.ItemWithOrder.class)
    private Long id;

    @JsonView(View.ItemWithOrder.class)
    private String productName;

    @JsonView(View.ItemWithOrder.class)
    private Integer quantity;

    @JsonView(View.ItemWithOrder.class)
    private Double price;

    // 多对一关联Order,懒加载
    @JsonView(View.ItemWithOrder.class)
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id")
    private Order order;

    // 构造器、getter、setter省略
}

2. 数据访问层(Repository)

创建两个JPA Repository接口,用来操作数据库:

OrderRepository

import org.springframework.data.jpa.repository.JpaRepository;

public interface OrderRepository extends JpaRepository<Order, Long> {
}

ItemRepository

import org.springframework.data.jpa.repository.JpaRepository;

public interface ItemRepository extends JpaRepository<Item, Long> {
}

3. 控制器层(Controller)

实现你需要的四个API端点,用@JsonView指定每个接口返回的字段范围:

import org.springframework.web.bind.annotation.*;
import com.fasterxml.jackson.annotation.JsonView;
import java.util.List;
import java.util.NoSuchElementException;

@RestController
@RequestMapping("/api")
public class OrderItemController {
    private final OrderRepository orderRepository;
    private final ItemRepository itemRepository;

    // 构造器注入(Spring Boot 3+推荐方式)
    public OrderItemController(OrderRepository orderRepository, ItemRepository itemRepository) {
        this.orderRepository = orderRepository;
        this.itemRepository = itemRepository;
    }

    // 1. GET所有Orders:仅返回Order本身,排除关联的Items
    @JsonView(View.OrderWithoutItems.class)
    @GetMapping("/orders")
    public List<Order> getAllOrders() {
        return orderRepository.findAll();
    }

    // 2. GET单个Order:返回Order本身及其关联的Items
    @JsonView(View.OrderWithItems.class)
    @GetMapping("/orders/{id}")
    public Order getSingleOrder(@PathVariable Long id) {
        return orderRepository.findById(id)
                .orElseThrow(() -> new NoSuchElementException("Order not found with id: " + id));
    }

    // 3. GET单个Item:返回单个Item及其所属的Order
    @JsonView(View.ItemWithOrder.class)
    @GetMapping("/items/{id}")
    public Item getSingleItem(@PathVariable Long id) {
        return itemRepository.findById(id)
                .orElseThrow(() -> new NoSuchElementException("Item not found with id: " + id));
    }
}

4. 可选:统一异常处理

为了让接口返回更友好的错误信息,添加全局异常处理:

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import java.util.NoSuchElementException;

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(NoSuchElementException.class)
    public ResponseEntity<ErrorResponse> handleNotFound(NoSuchElementException ex) {
        ErrorResponse error = new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.getMessage());
        return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
    }

    // 自定义错误响应类
    private static class ErrorResponse {
        private int status;
        private String message;

        public ErrorResponse(int status, String message) {
            this.status = status;
            this.message = message;
        }

        // getter方法省略
    }
}

核心要点总结

  • 用@JsonView灵活控制不同接口的返回字段,既满足了不同接口的需求,又避免了序列化循环引用问题
  • 关联查询使用FetchType.LAZY懒加载,提升接口性能
  • 用NoSuchElementException配合全局异常处理,返回标准的404错误响应
  • 注意SQL关键字(如order表名改为orders)避免语法错误

内容的提问来源于stack exchange,提问作者Daniel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:29:32