I have an Object with a List of another object. It's mapped like this:
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "products")
public class Product extends DateAudit {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
@Size(min = 3, max = 30)
private String name;
@NotBlank
private String shortDescription;
@NotBlank
private String description;
@NotNull
private Double regularPrice;
private Double promotionPrice;
@NotNull
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id", nullable = false)
private Category category;
@NotNull
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "store_id", nullable = false)
private Store store;
@Size(max = 20)
private String sku;
private Double weight;
private Integer quantityInStock;
@NotNull
private Boolean notifyLowStock;
@OneToMany(cascade = CascadeType.ALL)
private List<Image> images = new ArrayList<Image>();
On the Image side, that's the maaping:
@Entity
@Table(name = "images")
public class Image {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
private String url;
What happens is: 1. I create my Product object and save it on the database. 2. I update this product object by adding images to it later like this:
Product product = repository.findById(productId);
Image image = new Image();
image.setUrl(url);
product.getImages().add(image);
repository.save(product);
This is what I get on my console everytime I add a new image to the product and save it:
When I add the first image:
2018-07-27 22:46:47.367 DEBUG 8580 --- [nio-5000-exec-3] org.hibernate.SQL : insert into images (url) values (?)
2018-07-27 22:46:48.307 DEBUG 8580 --- [nio-5000-exec-3] org.hibernate.SQL : insert into products_images (product_id, images_id) values (?, ?)
When I add one more image:
2018-07-27 22:47:09.955 DEBUG 8580 --- [nio-5000-exec-4] org.hibernate.SQL : delete from products_images where product_id=?
2018-07-27 22:47:09.957 DEBUG 8580 --- [nio-5000-exec-4] org.hibernate.SQL : insert into products_images (product_id, images_id) values (?, ?)
2018-07-27 22:47:09.958 DEBUG 8580 --- [nio-5000-exec-4] org.hibernate.SQL : insert into products_images (product_id, images_id) values (?, ?)
When I add the third image:
2018-07-27 22:47:32.314 DEBUG 8580 --- [nio-5000-exec-5] org.hibernate.SQL : delete from products_images where product_id=?
2018-07-27 22:47:32.316 DEBUG 8580 --- [nio-5000-exec-5] org.hibernate.SQL : insert into products_images (product_id, images_id) values (?, ?)
2018-07-27 22:47:32.318 DEBUG 8580 --- [nio-5000-exec-5] org.hibernate.SQL : insert into products_images (product_id, images_id) values (?, ?)
2018-07-27 22:47:32.319 DEBUG 8580 --- [nio-5000-exec-5] org.hibernate.SQL : insert into products_images (product_id, images_id) values (?, ?)
My question is: Is deleting the whole list and adding all of it back to the database the correct behaviour? I was expecting it do just add the new image, leaving the other images there. Instead if remove all images based on the productId and the add it all back again.
I retrieve the product right before updating it. I retrieve the product, I add the new image to the list and I call the save method.
Is that normal? Is there a way to avoid this delete?
Thanks
In this post, We will take a look at one to many mapping with spring data jpa. With a simple spring boot application, we will walkthrough through how to implement @OneToMany annotation the right way. In the object-relational model, the One-To-Many relationship refers to One parent entity that has a correspondence to zero or more child entities.
JPA 1.0 does not support a unidirectional OneToMany relationship without a JoinTable. JPA 2.0 will have support for a unidirectional OneToMany. In JPA 2.0 a @JoinColumn can be used on a OneToMany to define the foreign key, some JPA providers may support this already.
If your JPA provider does not support unidirectional OneToMany relationships, then you will need to either add a back reference ManyToOne or a JoinTable. In general it is best to use a JoinTable if you truly want to model a unidirectional OneToMany on the database.
In terms of coding, a bidirectional relationship is more complex to implement because the application is responsible for keeping both sides in synch according to JPA specification 5 (on page 42). Unfortunately the example given in the specification does not give more details, so it does not give an idea of the level of complexity.
In short, this needs either an order in the list, e.g. by Image.url
:
@OneToMany(cascade = CascadeType.ALL)
@OrderColumn(name = "url")
private List<Image> images = new ArrayList<>();
or don't worry about the order:
@OneToMany(cascade = CascadeType.ALL)
private Set<Image> images = new HashSet<>();
Either of those eliminates the delete
and extra insert
against products_images
.
The nearest thing I know of to a high-level explanation of this is here, though it's talking about @Embeddable
instead of @Entity
elements in the collection. It seems like Hibernate should have less issue identifying individual entities (with ids) in a collection, but it doesn't do this for an unsorted List
(or PersistentBag
in the Hibernate model).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With