【问题标题】:Hibernate saving data with onetomany relationshipHibernate 以单对多关系保存数据
【发布时间】:2021-02-13 23:56:43
【问题描述】:

我有两个相互关联的表,表“用户”和“地址”:

@Entity
@Table(name = "user")
@Data
@AllArgsConstructor
public class User{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @NotNull
    @Column(unique = true)
    private String user_name;

    @Column
    private String description;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    protected Set<Address> addresses= new HashSet<>();
}

在另一张桌子上:

@Entity
@Table(name = "address")
@Data
@AllArgsConstructor
public class Address{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @ManyToOne
    @JoinColumn(name = "user_id")
    protected User user;

    private String address;
}

我有这个包含用户数据和他拥有的地址的 json。我需要有一个 Spring Boot rest api 调用,我将接受这个 json。

所以我需要创建一个用户和两个与该用户相关的地址。

{
 
  "user_name": "example",
  "description": "this is a  user description",
  "addresses": [
    {
      "address": "this is a address 1"
    },
    {
      "address": "this is a address 2"
    }
  ]
}

我做了一个发布请求来创建具有一些地址的新用户:

@PostMapping ("/user/create")
public ResponseEntity post(@RequestBody User user) {
   // TO DO 
}

我的问题是我不知道如何处理 OneToMany 关系,基本上是创建一个带有地址的用户行?

有人可以通过为此提供解决方案来帮助我吗?我发现了很多关于如何分别在表上添加数据的示例,但没有任何例子是这样的。

有人可以帮我吗,因为我被卡住了。

【问题讨论】:

    标签: sql spring-boot hibernate jpa


    【解决方案1】:

    您的代码所做的正常行为是将用户(到USER 表)及其关联地址(到ADDRESS 表)保存。由于您已经拥有CascadeType.ALL,一旦您保存用户,这将保存地址。

    因此,在您的 post 方法中,只需调用 userService.save(user)userRepository.save(user) 即可。您应该找到在数据库中创建的一个新用户和两个具有关联用户 ID 的新地址。

    【讨论】:

    • 当我这样做时,代表 OneToMany 关系的字段 user_id 为空。你能通过提供一个例子来帮助我吗?
    • 您需要为每个地址设置用户。这是双向映射时必须具备的
    【解决方案2】:

    假设您正在使用 Spring Boot,因为您的标签表明这是我会做的。

    首先,我想将用于与用户通信的类型与我在应用程序中使用的类型分离。我想这样做的原因是,如果我更改数据库中的某些内容,我不想自动强制我的用户更改他们的应用程序。所以第一件事是创建代表我的 REST 端点的输入和输出的类。为了进一步让我的生活更轻松并确保没有人对这些类做任何事情,我将使它们成为不可变的、仅最终值的类,如下所示:

    import com.fasterxml.jackson.annotation.JsonProperty;
    import lombok.EqualsAndHashCode;
    import lombok.Getter;
    import lombok.ToString;
    
    @Getter
    @ToString
    @EqualsAndHashCode
    final class UserPayload {
        private final String user_name;
        private final String description;
    
        public UserPayload(@JsonProperty("user_name") String username, @JsonProperty("description") String description, @JsonProperty("addresses") List<Address> addresses) {
            this.user_name = user_name;
            this.description = description;
        }
    }
    

    由于我没有看到业务键,我假设您的应用程序用户需要通过其主键 ID 引用数据库中的用户实体,因此响应类将类似于:

    import com.fasterxml.jackson.annotation.JsonProperty;
    import lombok.EqualsAndHashCode;
    import lombok.Getter;
    import lombok.ToString;
    
    @Getter
    @ToString
    @EqualsAndHashCode
    final class UserResponse {
    
        private final Integer id;
        private final String user_name;
        private final String description;
        private final List<Integer> addressIds;
    
        public UserResponse(Integer id, String username, String description, List<Integer> addressIds) {
            this.id = id;
            this.user_name = user_name;
            this.description = description;
            this.addressIds = addressIds;
        }
    }
    

    请注意,我们的有效负载类不包含地址。这样做的原因是我希望能够自己创建地址,然后通过“user/{primary key}/address”端点将它们关联到用户。

    现在我们有了这些类,我们需要一个能够将这些东西保存到数据库的存储库。仓库可以写成:

    @Repository
    public interface UserRepository extends JpaRepository<User, Interger> {}
    

    现在我们也有了它,我们可以继续通过控制器将我们的东西写入数据库:

    @Autowired
    private UserRepository userRepo;
    
    @PostMapping ("/user/create")
    public ResponseEntity<UserResponse> post(@RequestBody UserPayload userPayload) {
        User user = new User();
        user.setUsername(userPayload.getUsername());
        user.setDescription(userPayload.setDescription());
        user.setAddresses(userPayload.getAddresses());
        userRepo.save(user);
        UserResponse userResponse = new UserResponse(user.getId(), user.getUsername(), user.getDescription(), new Arraylist<Integer>());
        ResponseEntity<UserResponse> responseUser = new ResponseEntity<>(userResponse, HttpStatus.valueOf(201));
        return responseUser;        
    }
    

    如果您对如何将地址添加到用户资源感兴趣,我很乐意填写详细信息。进一步注意,我忽略了为您的实体类编写设置器,但我假设它们存在。

    【讨论】:

    • 我使用 Spring Boot。我在实体上使用@AllArgsConstructor。我遇到的问题是因为我需要一个 rest api 调用,我需要在同一个调用中创建一个用户和一些与该用户相关的地址。基本上你的回答没有回答我的问题。
    • 这些类可以扩展为一次性创建用户和地址。你想让我这样做吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-11
    • 1970-01-01
    • 2012-12-05
    • 2020-11-28
    • 1970-01-01
    • 2018-12-29
    • 1970-01-01
    相关资源
    最近更新 更多