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

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类。


具体修复步骤

  1. 修复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
}
  1. 创建复合主键类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
}
  1. 修复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
}
  1. 修复Repository:
public interface AddressRepository extends JpaRepository<Address, AddressId> {
}
  1. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 02:00:29