SpringBoot创建Address时外键为空报错,如何用ID关联User?
问题描述
我用SpringBoot开发CRUD功能,在PostgreSQL中创建Address时遇到问题。传入的数据如下:
{ "street": "string", "users": 1 }
出现报错:
org.postgresql.util.PSQLException: ERROR: null value in column "teste" of relation "address" violates not-null constraint Detalhe: Failing row contains (string, null).
需求是创建包含street字符串并关联User的Address,希望通过User的ID作为外键关联User表,但创建失败。以下是相关代码:
Address实体类
@Entity @Table(name="address") @IdClass(Address.class) public class Address implements Serializable { @Id @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "teste") private User users; @Id @Column(name="street") private String street; /// Getter and setter }
User实体类
@Entity @Table(name="username") @Embeddable public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private long id; @OneToMany(fetch = FetchType.LAZY, mappedBy = "users") private List<Address> adress; //Getter and setter }
AddressDTO
public class AddressDto { @NotBlank private String street; @NotNull private long users; public AddressDto() {} public AddressDto(String street, User user) { this.street = street; this.users = user.getId(); } public long getUsers() { return users; } public void setUsers(long users) { this.users = users; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } }
Repository
public interface AddressRepository extends JpaRepository<Address, User> { }
问题原因及解决方案
1. 实体类注解核心错误
- User类误用@Embeddable:
@Embeddable用于标记可嵌入到其他实体的类,而User是独立的业务实体,必须移除该注解,否则JPA无法将其识别为独立的数据库表。 - Address的@IdClass配置错误:
@IdClass需要指定专门的复合主键类,不能用Address自身作为主键类。因为你把users和street设为复合主键,必须创建单独的AddressId类封装这两个字段,且要实现Serializable、重写equals和hashCode方法。
2. 外键关联未正确赋值
DTO传入的users:1是User的ID,但转换为Address实体时,没有将ID转换为对应的User实体对象。JPA保存实体时需要关联完整的User实例,而非单纯的ID值,否则users字段会为null,导致外键列teste为空,触发非空约束报错。
3. Repository泛型参数错误
JpaRepository<Address, User>的第二个参数是主键类型,由于Address是复合主键,这里应该指定复合主键类(比如AddressId),而非User类。
具体修复步骤
- 修复User实体:移除
@Embeddable注解,确保它是独立实体:
@Entity @Table(name="username") public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private long id; @OneToMany(fetch = FetchType.LAZY, mappedBy = "users") private List<Address> adress; //Getter and setter }
- 创建复合主键类AddressId:
import java.util.Objects; public class AddressId implements Serializable { private User users; private String street; public AddressId() {} public AddressId(User users, String street) { this.users = users; this.street = street; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AddressId addressId = (AddressId) o; return Objects.equals(users, addressId.users) && Objects.equals(street, addressId.street); } @Override public int hashCode() { return Objects.hash(users, street); } // Getter和Setter }
- 修复Address实体:将
@IdClass指向AddressId,明确外键关联User的id字段:
@Entity @Table(name="address") @IdClass(AddressId.class) public class Address implements Serializable { @Id @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "teste", referencedColumnName = "id", nullable = false) private User users; @Id @Column(name="street", nullable = false) private String street; /// Getter and setter }
- 修复Repository:
public interface AddressRepository extends JpaRepository<Address, AddressId> { }
- DTO转实体时正确关联User:
在Service层中,先通过ID查询出User实体,再赋值给Address:
@Service public class AddressService { @Autowired private AddressRepository addressRepo; @Autowired private UserRepository userRepo; public Address createAddress(AddressDto dto) { User user = userRepo.findById(dto.getUsers()) .orElseThrow(() -> new RuntimeException("用户不存在")); Address address = new Address(); address.setStreet(dto.getStreet()); address.setUsers(user); return addressRepo.save(address); } }
内容的提问来源于stack exchange,提问作者ffffffff
相关产品推荐
相关产品推荐

