简体   繁体   中英

Hibernate saving data with onetomany relationship

I have two tables which are related to each other, table "user" and "address":

@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<>();
}

While in the other table:

@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;
}

I have this json with user data and the addresses that he has. I need to have one Spring Boot rest api call where I will accept this json.

So I will need to create a user and two addresses related to that user.

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

I did a post request to create new user with some addresses:

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

The problem that I have is that I don't know how to handle the OneToMany relationship, basically creation of a user row with addresses?

Can someone help me by providing me a solution for this? I found many examples on how to add data on tables separately, but not any case like this.

Can someone help me cause I'm stuck.

The normal behavior your code does is that it will save the user (to USER table) and its associated addresses (to ADDRESS table). Since you already have CascadeType.ALL this will save addresses once you save the user.

So, in your post method, just call userService.save(user) or userRepository.save(user) and you're done. You should find a new user created in the database and two new addresses with the associated user ID.

Assuming that you are using Spring Boot as your tags indicate here is what I would do.

First thing is that I would like to decouple the types that I use to communicate with my users from the types that I use within my application. Reason I want to do this is because if I change something inside my database I do not want to automatically force my users to make changes to their applications. So the first thing is to create classes which represent the inputs and outputs for my REST endpoints. Further to make my life easy and to ensure that nobody does anything with these classes I will make them immutable, final value-only classes as follows:

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;
    }
}

Since I do not see a business key I assume that your application users will need to reference to the User entities in the database via their primary key IDs, so the response class would be something like :

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;
    }
}

Note that our payload class does not contain addresses. Reason for this is that I would expect to be able to create addresses on their own, and then associate them to user via "user/{primary key}/address" endpoint.

Now that we have these classes we need a repository to be able to save these things to database. Repository can be written as:

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

Now that we have that too we can move on to writing our thing to database via controller:

@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;        
}

If you are interested about how you would add addresses to user resources I will be glad to fill in the details. Further note that I have neglected to write setters for your entity classes, but I assume they are present.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM