Spring Boot开发REST API存PostgreSQL时如何拆分表列至多autowire实体类
单表多实体类映射实现方案(PostgreSQL + Spring Boot)
核心认知校正
首先需要明确:实体类是JPA管理的数据库映射POJO,不属于Spring容器托管对象,无法通过@Autowired完成类注入。你需求里提到的“在Mall类中autowire两个详情类”,本质是类的组合持有关系,对应JPA提供的@Embeddable嵌入式映射能力——所有拆分类的字段全部映射到同一张MALL表,不需要额外创建关联表、外键,完全匹配单表多类拆分的需求。
具体实现步骤
1. 基础依赖引入
在STS创建的Spring Boot项目pom.xml中引入核心依赖,确保JPA能力、PostgreSQL驱动可用:
<dependencies> <!-- Spring Data JPA 核心依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- PostgreSQL 数据库驱动 --> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <!-- Web能力,用于编写REST接口 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
2. 拆分实体类实现
所有类统一放在com.mall.entity包下:
(1)BasicDetails 管理员基础信息映射类
用@Embeddable标记该类为可嵌入的映射结构,承载所有管理员相关字段:
package com.mall.entity; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; @Embeddable public class BasicDetails { // 映射MALL表的mallmanager_name列 @Column(name = "mallmanager_name") private String mallManagerName; // 映射MALL表的email列 @Column(name = "email") private String email; // 映射MALL表的mobile_no列 @Column(name = "mobile_no") private String mobileNo; // JPA映射要求必须保留无参构造 public BasicDetails() {} // 补全所有字段的getter、setter方法 public String getMallManagerName() { return mallManagerName; } public void setMallManagerName(String mallManagerName) { this.mallManagerName = mallManagerName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getMobileNo() { return mobileNo; } public void setMobileNo(String mobileNo) { this.mobileNo = mobileNo; } }
(2)AddressDetails 地址信息映射类
同样用@Embeddable标记,承载所有地址相关字段:
package com.mall.entity; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; @Embeddable public class AddressDetails { @Column(name = "address_1") private String address1; @Column(name = "address_2") private String address2; @Column(name = "address_3") private String address3; @Column(name = "pin") private String pin; @Column(name = "state") private String state; @Column(name = "country") private String country; public AddressDetails() {} // 补全所有字段的getter、setter方法 public String getAddress1() { return address1; } public void setAddress1(String address1) { this.address1 = address1; } public String getAddress2() { return address2; } public void setAddress2(String address2) { this.address2 = address2; } public String getAddress3() { return address3; } public void setAddress3(String address3) { this.address3 = address3; } public String getPin() { return pin; } public void setPin(String pin) { this.pin = pin; } public String getState() { return state; } public void setState(String state) { this.state = state; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } }
3. 主Mall实体类实现
作为映射MALL表的主实体,仅承载mall_id、mall_name字段,同时通过@Embedded引入两个拆分的详情类,所有字段最终全部映射到MALL单表:
package com.mall.entity; import jakarta.persistence.*; @Entity @Table(name = "MALL") public class Mall { // 主键,映射mall_id列 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "mall_id") private Long mallId; // 映射mall_name列 @Column(name = "mall_name") private String mallName; // 嵌入BasicDetails类,自动映射该类所有标注的数据库列 @Embedded private BasicDetails basicDetails; // 嵌入AddressDetails类,自动映射该类所有标注的数据库列 @Embedded private AddressDetails addressDetails; public Mall() {} // 补全所有字段的getter、setter方法 public Long getMallId() { return mallId; } public void setMallId(Long mallId) { this.mallId = mallId; } public String getMallName() { return mallName; } public void setMallName(String mallName) { this.mallName = mallName; } public BasicDetails getBasicDetails() { return basicDetails; } public void setBasicDetails(BasicDetails basicDetails) { this.basicDetails = basicDetails; } public AddressDetails getAddressDetails() { return addressDetails; } public void setAddressDetails(AddressDetails addressDetails) { this.addressDetails = addressDetails; } }
4. Repository与REST接口实现
(1)Mall数据访问层
package com.mall.repository; import com.mall.entity.Mall; import org.springframework.data.jpa.repository.JpaRepository; // 继承JpaRepository即可获得基础增删改查能力 public interface MallRepository extends JpaRepository<Mall, Long> { }
(2)REST接口编写
package com.mall.controller; import com.mall.entity.Mall; import com.mall.repository.MallRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/mall") public class MallController { // 这里的@Autowired是Spring Bean的注入,和实体类内部的组合关系完全不同 @Autowired private MallRepository mallRepository; // 新增商场数据接口 @PostMapping public Mall createMall(@RequestBody Mall mall) { return mallRepository.save(mall); } // 根据ID查询商场数据接口 @GetMapping("/{id}") public Mall getMallById(@PathVariable Long id) { return mallRepository.findById(id).orElse(null); } }
常见踩坑提醒
- 所有嵌入类、主实体类必须保留无参构造方法,否则JPA实例化映射对象时会直接报错
- 不要在实体类上添加
@Component等Spring容器注解,更不要在实体类成员上加@Autowired,否则注入值会为null,无法完成数据库字段映射 - 如果嵌入类的字段名和主表列名存在冲突,可以用
@AttributeOverride单独指定对应列名,本需求下的列名与拆分类字段完全对应,无需额外配置
内容的提问来源于stack exchange,提问作者Superman
相关产品推荐
相关产品推荐

