Reputation: 2913
In this spring boot app I have a one-to-many
mapping from Cart
to Item
where the serialization cycle is solved by ignoring the Cart
in Item
's serialization.
(So the Cart
field in Item
carries the @JsonBackReference
annocation)
Spring boot initializes the database successfully such that GET-ing all Cart
s gives the json
[
{
"id": 1, <-- cart 1
"items": [
{
"id": 0, <-- item 0
"itemName": "tooth brush"
},
{
"id": 1,
"itemName": "shampoo"
}
]
},
{
"id": 2,
"items": [
{
"id": 2,
"itemName": "body wash"
}
]
},
{
"id": 3,
"items": []
}
]
However when I want to add an Item
to an existing Cart
the new Item
is saved as null
@PostMapping("carts/{cartId}")
public Cart addItemToCart(@PathVariable("cartId") long cartId, @RequestBody Item item)
{
LOG.info("Trying to add ITEM: id={}, name={}, cart={} to CART: {}", item.getId(), item.getItemName(), item.getCart(), cartId);
Cart cart = dao.getById(cartId);
LOG.info("I) CART: id={}, items={}", cart.getId(),cart.getItems().stream().map(i -> i.getItemName()).collect(Collectors.toList()));
// also tried at this location: item.setCart(cart);
cart.getItems().add(item);
LOG.info("II) CART: id={}, items={}", cart.getId(),cart.getItems().stream().map(i -> i.getItemName()).collect(Collectors.toList()));
Cart res = dao.save(cart); // HERE CART SAVES NEW ITEM AS NULL
LOG.info("III) CART: id={}, items={}", res.getId(),res.getItems().stream().map(i -> i.getItemName()).collect(Collectors.toList()));
Cart resCheck = dao.getById(res.getId());
LOG.info("IV) CART: id={}, items={}", resCheck.getId(),resCheck.getItems().stream().map(i -> i.getItemName()).collect(Collectors.toList()));
return res;
}
Say I want to add an Item
with name "beer" to Cart
with cartid=1
. In my opinion, I only need the itemName
in the json because the Cart
field is ignored (@JsonBackReference
) and the itemid
is autogenerated.
So, posting
{
"itemName":"beer"
}
localhost:9200/demo/api/carts/1
gives the log (compare with above code)
Trying to add ITEM: id=0, name=beer, cart=null to CART: 1
I) CART: id=1, items=[tooth brush, shampoo]
II) CART: id=1, items=[tooth brush, beer, shampoo]
III) CART: id=1, items=[null, tooth brush, shampoo] <-- beer itemName is null after dao.save()
IV) CART: id=1, items=[null, tooth brush, shampoo]
And that's unsurprising because Cart
s and Item
s are joined on cartid
but where should the beer Item
get a cartid
from when serializing an Item
ignores any Cart
info! What also doesnt work if I POST
{
"itemName":"beer",
"cart":{
"id":1,
"items":[]
}
}
which also saves new item as null.
What I really want to avoid is switching JsonBackReference
and JsonManagedReference
.
How can I still add Item
s to Cart
s?
Here are the entity classes
@Entity
@Table(name="items")
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class Item
{
@Id
@GenericGenerator(name="itemIdGen" , strategy="increment")
@GeneratedValue(generator="itemIdGen")
@Column(name = "itemid", nullable=false)
private long id;
@Column(name="itemname")
private String itemName;
@ManyToOne
@JoinColumn(name = "cartid", nullable=false)
@JsonBackReference
private Cart cart;
/*...*/
}
@Entity
@Table(name="carts")
public class Cart
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "cartid", nullable=false)
private long id;
@Column(name="items")
@OneToMany(mappedBy = "cart")
@JsonManagedReference
private Set<Item> items;
/*..*/
}
Thanks for the help
P.S.: If you want to clone the repo linked in the beginning you also need to run the eureka server and clone the demo-commons project.
Upvotes: 1
Views: 5501
Reputation: 2913
The solution is to forget about referencing the entire Cart
object in Item
but just keep the cartid
(on which is merged)
@Entity
@Table(name="carts")
public class Cart
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "cartid", nullable=false)
private long id;
// @Column(name="items")
// @OneToMany(mappedBy = "cart")
// @JsonManagedReference
// private Set<Item> items;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name="cartid", referencedColumnName="cartid")
private Set<Item> items;
/*..*/
}
@Entity
@Table(name="items")
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class Item
{
@Id
@GenericGenerator(name="itemIdGen" , strategy="increment")
@GeneratedValue(generator="itemIdGen")
@Column(name = "itemid", nullable=false)
private long id;
@Column(name="itemname")
private String itemName;
// @ManyToOne
// @JoinColumn(name = "cartid", nullable=false)
// @JsonBackReference
// private Cart cart;
@Column(name="cartid")
private Long cartId;
/*..*/
}
Now I can just happily POST the Item
{
"itemName":"beer",
"cartid":1
}
Upvotes: 1