I'm trying to make a post method that accepts a 'food' item and saves it. The food entity has two foreign fields called "restaurant" and "menu". When I send the request, for some reason, Spring boot is trying to access the id field of the restaurant from the menu object. How can I fix this error?
Here are my entities.
@Entity
@Getter
@Setter
@NoArgsConstructor
@JsonSerialize(using = MenuSerializer.class)
public class Menu {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "restaurant_id", nullable = false)
@JsonIgnore
private Restaruant restaurant;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
@JoinColumn(name = "menu_id")
private List<Food> foods = new ArrayList<Food>();
@Override
public String toString() {
return "Menu [id=" + id + ", name=" + name + ", restaurant=" + restaurant + ", foods=" + foods + "]";
}
}
@Entity
@Getter
@Setter
@NoArgsConstructor
public class Food {
@Id
@GeneratedValue( strategy = GenerationType.IDENTITY)
private Long Id;
private String name;
private String picture;
private Double price;
@Column(columnDefinition = "text")
private String description;
private String category;
private Double discount;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "menu_id", nullable = false)
@JsonIncludeProperties({"id"})
private Menu menu;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "restaurant_id", nullable = false)
@JsonIncludeProperties({"id"})
private Restaruant restaurant;
@OneToMany(mappedBy = "food")
@JsonIgnore
private List<CartItem> cartItems = new ArrayList<>();
private Boolean available;
@Override
public String toString() {
return "Food [Id=" + Id + ", name=" + name + ", picture=" + picture + ", price=" + price + ", description="
+ description + ", category=" + category + ", discount=" + discount + ", menu=" + menu + ", restaurant="
+ restaurant + ", cartItems=" + cartItems + ", available=" + available + "]";
}
}
@Entity
@Setter
@Getter
@NoArgsConstructor
public class Restaruant {
@Id
@GeneratedValue ( strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String profile;
private Boolean available;
@Column(columnDefinition = "text")
private String description;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "owner_id")
@JsonIgnore
private User owner;
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "address_id", referencedColumnName = "id")
@JsonIgnore
private Address address;
@ManyToOne
@JoinColumn(name = "region_id")
@JsonIgnore
private Region region;
}
And here is my Controller method. There is nothing fancy here.
@PostMapping("/create")
public ResponseEntity<Food> createFood(@RequestBody Food food, Principal principal) {
//restaurantService.validateRestaurantOwner(food.getRestaurant().getId(), principal.getName());
System.out.println(food);
Food createdFood = foodService.saveFood(food);
return ResponseEntity.status(HttpStatus.CREATED).body(createdFood);
}
I printed out the food object before saving, so that I can see what it looks like.
Food [Id=null, name=jkla;f, picture=null, price=10.0, description=ffff, category=jfla, discount=1.0, menu=Menu [id=3, name=null, restaurant=null, foods=[]], restaurant=com.hostmdy.food.domain.Restaruant@4c91c71a, cartItems=[], available=true]
The object is just what I expected. I don't know if there's anything wrong with it. This is the error message I'm getting.
[2m2024-09-10T18:01:34.590+06:30[0;39m [33m WARN[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36m.w.s.m.s.DefaultHandlerExceptionResolver[0;39m [2m:[0;39m Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Cannot invoke "com.hostmdy.food.domain.Restaruant.getId()" because the return value of "com.hostmdy.food.domain.Menu.getRestaurant()" is null]
Spring is trying to access Menu.getRestaurant(). Which it shouldn't be doing.
I also printed all debug lines in case you guys may want to see.
Executing identity-insert immediately
[2m2024-09-10T18:01:34.546+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36morg.hibernate.SQL [0;39m [2m:[0;39m insert into food (available,category,description,discount,menu_id,name,picture,price,restaurant_id,id) values (?,?,?,?,?,?,?,?,?,default)
[2m2024-09-10T18:01:34.548+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36morg.hibernate.orm.results [0;39m [2m:[0;39m Initializer list is empty
[2m2024-09-10T18:01:34.548+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36morg.hibernate.orm.results.loading [0;39m [2m:[0;39m Calling top-level assembler (0 / 1) : org.hibernate.sql.results.graph.basic.BasicResultAssembler@7d41471c
[2m2024-09-10T18:01:34.548+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36morg.hibernate.orm.results [0;39m [2m:[0;39m Extracted JDBC value [0] - [7]
[2m2024-09-10T18:01:34.551+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36mo.h.id.IdentifierGeneratorHelper [0;39m [2m:[0;39m Extracted generated values [com.hostmdy.food.domain.Food]: [Ljava.lang.Object;@643eaf3f
[2m2024-09-10T18:01:34.554+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36mo.s.orm.jpa.JpaTransactionManager [0;39m [2m:[0;39m Initiating transaction commit
[2m2024-09-10T18:01:34.555+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36mo.s.orm.jpa.JpaTransactionManager [0;39m [2m:[0;39m Committing JPA transaction on EntityManager [SessionImpl(610926081<open>)]
[2m2024-09-10T18:01:34.555+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36mo.h.e.t.internal.TransactionImpl [0;39m [2m:[0;39m committing
[2m2024-09-10T18:01:34.555+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36mo.h.e.i.AbstractFlushingEventListener [0;39m [2m:[0;39m Processing flush-time cascades
[2m2024-09-10T18:01:34.556+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36mo.h.e.i.AbstractFlushingEventListener [0;39m [2m:[0;39m Dirty checking collections
[2m2024-09-10T18:01:34.557+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36mo.hibernate.engine.internal.Collections [0;39m [2m:[0;39m Collection found: [com.hostmdy.food.domain.Food.cartItems#7], was: [<unreferenced>] (initialized)
[2m2024-09-10T18:01:34.558+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36mo.h.e.i.AbstractFlushingEventListener [0;39m [2m:[0;39m Flushed: 0 insertions, 0 updates, 0 deletions to 1 objects
[2m2024-09-10T18:01:34.558+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36mo.h.e.i.AbstractFlushingEventListener [0;39m [2m:[0;39m Flushed: 1 (re)creations, 0 updates, 0 removals to 1 collections
[2m2024-09-10T18:01:34.565+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36mo.hibernate.internal.util.EntityPrinter [0;39m [2m:[0;39m Listing entities:
[2m2024-09-10T18:01:34.567+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36mo.hibernate.internal.util.EntityPrinter [0;39m [2m:[0;39m com.hostmdy.food.domain.Food{price=10.0, restaurant=com.hostmdy.food.domain.Restaruant#1, available=true, name=jkla;f, description=ffff, discount=1.0, Id=7, cartItems=[], category=jfla, menu=com.hostmdy.food.domain.Menu#3, picture=null}
[2m2024-09-10T18:01:34.568+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36mo.s.orm.jpa.JpaTransactionManager [0;39m [2m:[0;39m Not closing pre-bound JPA EntityManager after transaction
[2m2024-09-10T18:01:34.579+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36mo.s.w.s.m.m.a.HttpEntityMethodProcessor [0;39m [2m:[0;39m Using 'application/json', given [*/*] and supported [application/json, application/*+json]
[2m2024-09-10T18:01:34.580+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36mo.s.w.s.m.m.a.HttpEntityMethodProcessor [0;39m [2m:[0;39m Writing [Food [Id=7, name=jkla;f, picture=null, price=10.0, description=ffff, category=jfla, discount=1.0, me (truncated)...]
[2m2024-09-10T18:01:34.590+06:30[0;39m [33m WARN[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36m.w.s.m.s.DefaultHandlerExceptionResolver[0;39m [2m:[0;39m Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Cannot invoke "com.hostmdy.food.domain.Restaruant.getId()" because the return value of "com.hostmdy.food.domain.Menu.getRestaurant()" is null]
[2m2024-09-10T18:01:34.590+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36mo.j.s.OpenEntityManagerInViewInterceptor[0;39m [2m:[0;39m Closing JPA EntityManager in OpenEntityManagerInViewInterceptor
[2m2024-09-10T18:01:34.593+06:30[0;39m [32mDEBUG[0;39m [35m9736[0;39m [2m---[0;39m [2m[food_delivery_api] [nio-8686-exec-1][0;39m [2m[0;39m[36mo.s.web.servlet.DispatcherServlet [0;39m [2m:[0;39m Completed 500 INTERNAL_SERVER_ERROR
I also tried creating custom deserializer and it still didn't work.
The problem comes from @JsonIncludeProperties({"id"})
. It will try to serialize the property because of that.
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "restaurant_id", nullable = false)
@JsonIncludeProperties({"id"}) //this
private Restaruant restaurant;
You can fix it by removing those annotations from Food
. If another controller was relying on that, I would suggest you to use @JsonView
to have different serialization depending on the context.