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

Spring Boot与H2内存数据库间JSON数据和@Entity类的存取方案咨询

问题解答

1. INSERT语句正确性确认

你写的INSERT语句完全正确,H2数据库支持直接将标准JSON格式的字符串用单引号包裹后存入JSON类型字段,你使用JSON数组存储多桌台信息的写法符合语法要求。

2. 核心问题解决方案

这里提供两种实现方案,优先推荐JPA属性转换器方案,无需手动处理序列化/反序列化逻辑,代码更简洁:

方案1:使用JPA属性转换器(推荐)

步骤1:创建JSON序列化转换器

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import java.util.List;

@Converter(autoApply = false)
public class BookTableListConverter implements AttributeConverter<List<BookTable>, String> {
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public String convertToDatabaseColumn(List<BookTable> bookTables) {
        try {
            return objectMapper.writeValueAsString(bookTables);
        } catch (Exception e) {
            throw new RuntimeException("JSON序列化失败", e);
        }
    }

    @Override
    public List<BookTable> convertToEntityAttribute(String s) {
        try {
            return objectMapper.readValue(s, new TypeReference<List<BookTable>>() {});
        } catch (Exception e) {
            throw new RuntimeException("JSON反序列化失败", e);
        }
    }
}

步骤2:修改Restaurant实体类

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "restaurant")
public class Restaurant {
    @Id
    @Column(name="ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(name="NAME")
    private String name;

    // 建议替换为java.time.LocalDate,避免旧Date类的线程安全问题
    @Column(name="BOOKING_DATE")
    private Date bookingDate;

    @Column(name = "TABLES", columnDefinition = "json")
    @Convert(converter = BookTableListConverter.class)
    private List<BookTable> tables; // 直接用List存储,不需要String类型
}

步骤3:调整Service层代码

// 新增餐厅方法直接保存即可,转换器会自动处理JSON转换
public void AddNewRestaurant(Restaurant restaurant) {
    // 如果需要同时保存桌台到BOOKTABLE表,可以在这里遍历tables调用tableService.save批量保存
    restaurantRepository.save(restaurant);
}

// 查询方法不需要额外修改,转换器会自动将JSON字段转为List<BookTable>
public Restaurant getRestaurant(Long id) {
    return restaurantRepository.findById(id)
            .orElseThrow(() -> new RuntimeException("未找到对应餐厅")); // 替换为自定义业务异常更规范
}

方案2:手动序列化/反序列化(兼容旧代码)

如果你不想修改现有实体的String类型tables字段,可以直接在Service层手动处理转换:

POST存储逻辑

public void AddNewRestaurant(Restaurant restaurant, List<BookTable> tables) throws JsonProcessingException {
    ObjectMapper objectMapper = new ObjectMapper();
    // 将List序列化为JSON字符串存入tables字段
    String tablesJson = objectMapper.writeValueAsString(tables);
    restaurant.setTables(tablesJson);
    restaurantRepository.save(restaurant);
}

GET读取转换逻辑

// 单个查询转换
public List<BookTable> getTablesByRestaurantId(Long id) throws JsonProcessingException {
    Restaurant restaurant = restaurantRepository.findById(id).orElseThrow();
    ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper.readValue(restaurant.getTables(), new TypeReference<List<BookTable>>() {});
}

// 批量查询转换可以遍历列表逐个处理

额外优化建议

  1. 如果你的业务中BookTable和Restaurant是强关联关系,也可以考虑去掉Restaurant表的JSON字段,改用@OneToMany关联映射,直接关联BOOKTABLE表,更符合关系型数据库设计规范
  2. 建议给查询接口添加异常处理,当ID不存在时返回404状态码而非500
  3. ObjectMapper可以作为单例注入Spring容器,不要每次调用都新建对象,提升性能

内容的提问来源于stack exchange,提问作者Jasmira Richelle Fernandes

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.19 16:15:05