0

I have three classes, User and Product, and their association class Order. I wanted to be able to add and delete rows in association table by editing Set in either User or Product. Adding is working, but when I remove some Orders from e.g. User, they are kept in DB.

Following this example I came up with this code.

@Getter
@Setter
@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private Set<Order> orders;

    public void setOrders(Set<Order> orders) {
        this.orders = orders;
        orders.forEach(order -> order.setUser(this));
    }
}

@Getter
@Setter
@Entity
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
    private Set<Order> orders;

    public void setOrders(Set<Order> orders) {
        this.orders = orders;
        orders.forEach(order -> order.setProduct(this));
    }
}

And association class:

@Getter
@Setter
@Entity
@NoArgsConstructor
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @ManyToOne
    @JoinColumn
    private Product product;

    @NotNull
    @ManyToOne
    @JoinColumn
    private User user;

    public Order(User user, Product product) {
        this.user = user;
        this.project = project;
    }

    @Override
    public boolean equals(Object o){
        if (this == o) return true;
        if (!(o instanceof Order)) return false;
        Engagement that = (Order) o;
        return Objects.equals(user.getUsername(), 
                           that.user.getUsername()) &&
               Objects.equals(product.getName(), 
                           that.product.getName());
    }

    @Override
    public int hashCode(){
        return Objects.hash(user.getUsername(), 
                           product.getName());
    }
}

in my OrderService class I add and edit set of Orders like this:

public addProductsToUser(User user, Set<Product> products){
    Set<Order> orders = products.stream().map(product -> new Order(user, product)).collect(Collectors.toSet());
    user.setOrders(orders);
    userRepository.save(user); //save from CrudRepository
}

I expected that when I updated User it would also update Orders so that Orders that are not referenced anymore would be deleted, but I get multiple entries of Orders I kept.

pajkeki
  • 17
  • 4

1 Answers1

0

I think you are looking for orphanRemoval = true. This specifies, that objects which are disconnected from the parent will be removed.

In your case the annotation would look something like this:

@OneToMany(mappedBy = "user", orphanRemoval = true, cascade = CascadeType.ALL)
private Set<Order> orders;

For more details and the difference between those two, see this post: What is the difference between cascade and orphan removal from DB?

G. Firm
  • 77
  • 5
  • 1
    This is what I was missing. Thank you. I experienced hibernate exception: 'A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance' but second comment on [this](https://stackoverflow.com/questions/5587482/hibernate-a-collection-with-cascade-all-delete-orphan-was-no-longer-referenc) solved it. – pajkeki Apr 11 '19 at 15:38