1

I have 2 DTOs "OrderItem" and "Ingredient", both classes has @ManyToMany annotation:

@Entity
@Table
@NoArgsConstructor
@Data
public class OrderItem {

    private @Id @GeneratedValue @NotNull long id;

    @ManyToOne(optional = false)
    @JoinColumn(nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    private Order order;

    @ManyToOne(optional = false)
    @JoinColumn(nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    private Food food;

    private int quantity;

    @ManyToMany(cascade=CascadeType.ALL)
    @JoinTable(
            name = "order_item_ingredient",
            joinColumns = @JoinColumn(name = "order_item_id"),
            inverseJoinColumns = @JoinColumn(name = "ingredient_name")
    )
    private Set<Ingredient> ingredients = new HashSet<>();
}

@Entity
@Table
@Data
@NoArgsConstructor
public class Ingredient {
    private @Id String ingredientName;
    private float basePrice;
    private boolean addable;
    @ManyToMany(mappedBy = "ingredients",cascade=CascadeType.ALL)
    private Set<Food> foods= new HashSet<>();

    @ManyToMany(mappedBy = "ingredients",cascade=CascadeType.ALL)
    private Set<OrderItem> orderItems= new HashSet<>();

    public Ingredient(String ingredientName, float basePrice, boolean addable) {
        this.ingredientName = ingredientName.toLowerCase();
        this.basePrice = basePrice;
        this.addable = addable;
    }
}

And I'm looking to add a new OrderItem using a POST request using the following @PostMapping controller function:

@PostMapping("{id}/orderItem")
public ResponseEntity<OrderItem> createMenuItem(
        @PathVariable(value = "id") Long orderId,
        @RequestBody OrderItem orderItem) {

    Order order = orderService.getOrder(orderId)
            .orElseThrow(() -> new ResourceNotFoundException("order '" + orderId + "' is not found"));

    orderItem.setOrder(order);
    orderItemRepository.save(orderItem);

    return new ResponseEntity<>(orderItem, HttpStatus.CREATED);
}

When I send a post request to localhost:8080/1/orderItem with the following body:

{
    "order":"1",
    "food":"burger",
    "quantity":"1"
}

It works fine and a new order_item database record is created, but when I send the same request with the following body:

{
    "order":"1",
    "food":"burger",
    "quantity":"1",
    "ingredients": [{"ingredientName":"leaf"}]
}

It fails and gives the following SQL error:

java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'leaf' for key 'ingredient.PRIMARY'

I know that this record already exists, but how do I tell Spring Boot that I want it to look for an existing Ingredient instead of trying to create a new one?

I have an ugly solution in my mind, and that is to send the OrderItem object alongside a list of strings where each element represents a primary key for Ingredient class, then iterate through that list element by element calling the repository to get the Ingredient object then manually add it to OrderItem.ingredients, but I'm sure that is not the best solution out there.

Balawi28
  • 119
  • 7

2 Answers2

0

Being defined on the OrderItem class, the relation ingredients is considered as a composition on the cascading strategy point of view. Therefore, the CascadeType.ALL implies the attempt to create the ingredient. To avoid this, you can change the direction of this relation reverse the mappedBy information. But then again, if you keep a CascadeType.ALL on the ingredient side, you will be in trouble if you create an ingredient with an existing orderItem. You can win on both sides an use CascadeType.ALL.

check JPA Hibernate many-to-many cascading

0

I have 2 DTOs "user" and "role", both classes has @ManyToMany annotation:

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    // Other attributes
    
    @ManyToMany
    @JoinTable(
        name = "user_role",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles = new HashSet<>();
    // Getters and Setters
}

@Entity
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    // Other attributes
    
    @ManyToMany(mappedBy = "roles")
    private Set<User> users = new HashSet<>();
    // Getters and Setters
}

And I'm looking to add a new UserRole using a POST request using the following @PostMapping controller function:

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserRepository userRepository; // Assuming UserRepository is a Spring Data JPA repository for User entity

    @Autowired
    private RoleRepository roleRepository;

// Endpoint to add roles to a user
    @PostMapping("/{userId}/roles")
    public ResponseEntity<User> addRolesToUser(@PathVariable Long userId, @RequestBody List<Long> roleIds) {
        Optional<User> user = userRepository.findById(userId);
        if (user.isPresent()) {
            Set<Role> roles = new HashSet<>();
            for (Long roleId : roleIds) {
                Optional<Role> role = roleRepository.findById(roleId);
                if (role.isPresent()) {
                    roles.add(role.get());
                }
            }
            user.get().getRoles().addAll(roles);
            userRepository.save(user.get());
            return ResponseEntity.ok(user.get());
        } else {
            return ResponseEntity.notFound().build();
        }
    }

    // Endpoint to remove roles from a user
    @DeleteMapping("/{userId}/roles/{roleId}")
    public ResponseEntity<User> removeRoleFromUser(@PathVariable Long userId, @PathVariable Long roleId) {
        Optional<User> user = userRepository.findById(userId);
        Optional<Role> role = roleRepository.findById(roleId);
        if (user.isPresent() && role.isPresent()) {
            user.get().getRoles().remove(role.get());
            userRepository.save(user.get());
            return ResponseEntity.ok(user.get());
        } else {
            return ResponseEntity.notFound().build();
        }
    }
}

postman request

POST localhost:8080/users/{userId}/roles
In body raw- json
[1,2,3]

Output: Record insert successfully

enter image description here

  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 16 '23 at 05:14