List<object> null when deserializing json in Spring Boot controller - java

I have a problem with a controller in a Spring Boot application. When I make a call on the controller (URI/call), the object list is always null.
{
"code": "TEST",
"name": "TEST NAME",
"groupe": "A1",
"list": [
{"type":"web", "link":"https://google.com/"},
{"type":"web", "link":"https://google2.com/"}
]
}
#PostMapping(value="/call")
public ResponseEntity<Void> ajouterEnvironnement(#RequestBody First first) {
first.getCode() // value : "TEST"
first.getList() // value : null
}
public class First {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String code;
private String name;
private String groupe;
#OneToMany(mappedBy = "first", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Second> list;
}
public class Second {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String type;
private String link;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "first_id", nullable = false)
private First first;
}

By writing this question, I was able to find a solution, so I share it:
Use the #JsonManagedReference and #JsonBackReference annotations.
More information here.
public class First {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String code;
private String name;
private String groupe;
#OneToMany(mappedBy = "first", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JsonManagedReference
private List<Second> listLiens;
}
public class Second {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String type;
private String link;
#ManyToOne(fetch = FetchType.EAGER)
#JsonBackReference
#JoinColumn(name = "first_id", nullable = false)
private First first;
}

Related

Spring Boot Data JPA detached entity passed to persist on #OneToMany and #OneToOne

Problem
I am trying to store an object in my Postgres database. This consists of the Order.class, (List) OrderDevice.class, and a Department.class.
The important thing is that the OrderDevices are always stored new in the DB, but a Department may already exist.
When I try to save the object to my database using save I get the following error message: (shown below)
I get the error message "detached entity passed to persist: com.niclas.model.OrderDevice" if the department does not exist yet, if the department exists the error message looks like this: "detached entity passed to persist: com.niclas.model.Department".
Solution attempts
This solution cannot be used because I do not use bidirectional mapping.
(I don't want to use a bidirectional mapping because I want to access the departments without an order.)
I also tried to change the Cascade types to MERGE like in this solution
I also tried using #Transactional on the method
I also tried to save the children in the database first and then the parent like this:
departmentRepository.save(order.getDepartment()); orderDeviceRepository.saveAll(order.getDevices()); orderRepository.save(order);
I hope I have described my good enough and I am happy about suggestions for solutions
Error.log
The log can be viewed here. (The formatting did not work here)
Order.class
#Entity
#Table(name = "orders")
public class Order extends AuditModel {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column(name = "order_id")
private String orderId;
#Column(name = "department_id")
private long departmentId;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "department", referencedColumnName = "id")
private Department department;
#JsonProperty("deviceList")
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "order_id", referencedColumnName = "order_id")
private List<OrderDevice> devices;
#JsonProperty("forename")
#Column(name = "sender_forename")
private String senderForename;
#JsonProperty("surname")
#Column(name = "sender_surname")
private String senderSurname;
#Column(name = "notes", columnDefinition = "TEXT")
private String notes;
#Column(name = "month")
private int month;
#Column(name = "year")
private int year;
public Order() {
}
... Getter/Setters
}
OrderDevice.class
#Entity
#Table(name = "order_devices")
public class OrderDevice extends AuditModel{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column( name = "order_id", insertable = false, updatable = false )
private String orderId;
#Column(name = "device_id")
private long deviceId;
#Column(name = "device_name")
private String deviceName;
#Column(name = "priceName")
private String priceName;
#Column(name = "price")
private double price;
#Column(name = "count")
private int count;
public OrderDevice() {
}
... Getters/Setters
}
Department.class
#Entity
#Table(name = "departments")
public class Department {
//TODO add Form Validation
//TODO better Naming for From Attributes on Frontend and Backend
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column(name = "department_name")
private String department;
#Column(name = "contact_person_forename")
private String forename;
#Column(name = "contact_person_surname")
private String surname;
#Column(name = "contact_person_mail")
private String mail;
#Column(name = "street")
private String street;
#Column(name = "house_number")
private String houseNumber;
#Column(name = "location")
private String location;
#Column(name = "postal_code")
private int postalCode;
#Column(name = "country")
private String country;
#Column(name = "auto_send_invoice")
private boolean autoSend;
#Column(name = "registered")
private boolean registered;
public Department() {
}
... Getter/Setters
}
OrderController.class
#Slf4j
#RestController
public class OrderController {
private final DepartmentRepository departmentRepository;
private final OrderRepository orderRepository;
private final OrderDeviceRepository orderDeviceRepository;
public OrderController(OrderRepository orderRepository, DepartmentRepository departmentRepository,
OrderDeviceRepository orderDeviceRepository) {
this.orderRepository = orderRepository;
this.departmentRepository = departmentRepository;
this.orderDeviceRepository = orderDeviceRepository;
}
#PostMapping("/orders/add")
public ResponseEntity<Order> addDepartment(#RequestBody Order order) throws JsonProcessingException {
order.setOrderId(order.generateOrderId());
DateTime dateTime = new DateTime();
order.setMonth(dateTime.getMonthOfYear());
order.setYear(dateTime.getYear());
order.getDevices().forEach(orderDevice -> {
orderDevice.setOrderId(order.getOrderId());
});
//departmentRepository.save(order.getDepartment());
//orderDeviceRepository.saveAll(order.getDevices());
orderRepository.save(order);
return new ResponseEntity<>(order, HttpStatus.CREATED);
}
Update
If the objects are created in this way, no error will occur and the order will be successfully saved in the database.
However, I don't understand why it works this way and not via ObjectMapper. Does anyone know why?
#PostMapping("/orders/add")
public ResponseEntity<Order> addDepartment(#RequestBody JsonNode jsonNode) throws JsonProcessingException {
Order order = new Order();
JsonNode departmentJson = jsonNode.get("department");
Department department;
if ( departmentJson.get("id").canConvertToInt() ) {
department = departmentRepository.findDepartmentById(departmentJson.get("id").asInt());
} else {
department = new Department();
department.setDepartment(departmentJson.get("department").asText());
department.setForename(departmentJson.get("forename").asText());
department.setSurname(departmentJson.get("surname").asText());
department.setMail(departmentJson.get("mail").asText());
department.setStreet(departmentJson.get("street").asText());
department.setHouseNumber(departmentJson.get("houseNumber").asText());
department.setLocation(departmentJson.get("location").asText());
department.setPostalCode(departmentJson.get("postalCode").asInt());
department.setCountry(departmentJson.get("country").asText());
department.setAutoSend(departmentJson.get("autoSend").asBoolean());
department.setRegistered(departmentJson.get("registered").asBoolean());
}
order.setDepartment(department);
order.setOrderId(order.generateOrderId());
order.setDepartmentId(department.getId());
List<OrderDevice> orderDevices = new ArrayList<>();
JsonNode devices = jsonNode.get("deviceList");
for (JsonNode node : devices) {
//TODO replace this mess with objectMapper
if (node.has("count") && node.get("count").asInt() != 0){
OrderDevice device = new OrderDevice();
device.setOrderId(order.getOrderId());
device.setDeviceId(node.get("id").asLong());
device.setDeviceName(node.get("deviceName").asText());
device.setPriceName(node.get("priceName").asText());
device.setPrice(node.get("price").asDouble());
device.setCount(node.get("count").asInt());
orderDevices.add(device);
}
}
order.setDevices(orderDevices);
order.setSenderForename(jsonNode.get("forename").asText());
order.setSenderSurname(jsonNode.get("surname").asText());
order.setNotes(jsonNode.get("notes").asText());
DateTime dateTime = new DateTime();
order.setMonth(dateTime.getMonthOfYear());
order.setYear(dateTime.getYear());
orderRepository.save(order);
return new ResponseEntity<>(order, HttpStatus.CREATED);
}
You can try to use instead of orderRepository.save(order) use orderRespostiory.saveOrUpdate(order).

Hibernate unable to map many students to a class

I hae 2 simple entities: Student and Class. I want to POST a student, where I specify the class it belongs to, but I've got stuck in hibernate mapping.
ClassModel.class
#Entity
#Table(name = "class" )
public class ClassModel implements Serializable {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#NotEmpty
#Size(max = 20)
#Column(name = "name")
private String name;
#Column(name = "tables")
private int tables;
#Column(name = "chairs")
private int chairs;
#Column(name = "teacher")
private String teacher;
(getters + setters)
StudentModel
#Entity
#Table(name = "student")
public class StudentModel implements Serializable {
#Id
#Column(name = "student_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int student_id;
#NotEmpty
#Column(name = "name")
#Size(max = 50)
private String name;
#Column(name = "age")
private int age;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id")
private ClassModel classModel;
(getters + setters)
}
StudentController.java
...
#Autowired
private StudentService studentService;
#Autowired
private ClassService classService;
#PostMapping(value = "/save")
public StudentModel save(#RequestBody StudentModel studentModel){
ClassModel classModel = classService.findById(studentModel.getClassId()).get();
studentModel.setClassModel(classModel);
return studentService.save(studentModel);
}
...
But when I make a request from Postman with the following body:
{
"name": "some name",
"age": 12,
"class_id": 1
}
I get the following error from hibernate:
Column 'class_id' cannot be null
Where is the mistake in my hibernate mapping?
It's how I have made working join in hibernate. Have a look:
TrainingEntity.java
#Id
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "animal_id", nullable = false, insertable = false, updatable = false)
private AnimalEntity animalEntity;
#Column(name = "animal_id")
private Integer animalId;
AnimalEntity.java
#Id
private Integer id;
#OneToMany(mappedBy = "animalEntity", fetch = FetchType.LAZY)
private List<TrainingEntity> trainingEntityList = new ArrayList<>();
So here is the join between AnimalEntity and TrainingEntity.
AnimalEntity have a list of TrainingEntities.
The mistake is in this line:
"class_id": 1
You're using column name instead of field name. You would have to replace class_id with classModel, where classModel would be an object. Other solution would be to find ClassModel by id from json and set it as parent to StudentModel.

ManyToMany relationship entries are not being displayed in Spring REST

I have two classes: MenuItem.java and Tag.java. I am using the Hibernate implementation of JPA, and using PagingAndSortingRepository. After creating a bunch of dummy MenuItems and Tags, these are displayed in my local database and can be accessed with either: localhost:8080/api/menuItems or localhost:8080/api/tags. Additionally, as described by my annotations in the files below, there exists a ManyToMany relationship between these two objects, and a Menu_Items_Tags table with the appropriate entries is also created in my database without any issue.
The problem I run into is that every time I attempt to access a given menu item's list of tags via: localhost:8080/api/menuItems/1/tags for example, I receive a 500 error and additionally have a repeating error code like this:
: HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext#726b0462<rs=HikariProxyResultSet#1877641821 wrapping Result set representing update count of 6>
Is there something I am doing wrong in either trying to request the Tags of a MenuItem or am I incorrectly setting up my #ManyToMany relationship?
MenuItem.java:
#Data
#Entity
public class MenuItem implements Serializable {
private static final long serialVersionUID = 1132661429342356177L;
public MenuItem() {
}
public MenuItem(String name, Double price, Integer inventory, String description, Set<Tag> tags) {
this.name = name;
this.price = price;
this.inventory = inventory;
this.description = description;
this.tags = tags;
}
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
#Column(name = "name", nullable = false, unique = true)
private String name;
#Column(name = "price", nullable = false)
private Double price;
#Column(name = "inventory", nullable = false)
private Integer inventory;
#Column(name = "description", nullable = false)
private String description;
#OneToMany(mappedBy = "menuItem")
private Set<Order> orders;
#ManyToMany
#JoinTable(
name="menu_item_tags",
joinColumns = #JoinColumn(name = "menu_item_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "tag_id", referencedColumnName = "id"))
#JsonManagedReference
private Set<Tag> tags;
}
Tag.java:
#Data
#Entity
public class Tag implements Serializable {
private static final long serialVersionUID = 1132661429342356176L;
public Tag() {
}
public Tag(String name) {
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name", unique = true)
private String name;
// #JsonIgnore
#ManyToMany(mappedBy = "tags")
#JsonBackReference
private Set<MenuItem> menuItems;
}

Hibernate doesn't change column for new parameters

I have a simple object which I want save in DB. But for time it was necessary to change someone parameters. I drop my old table but when I turn on my program - Hibernate still create DB with old columns.
For example:
My old class looked like this :
#Entity
#Table(name = "Tests")
public class Test {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private String description;
private boolean isFree;
private int status;
private String author;
private String section;
#OneToMany(mappedBy = "qTest", fetch = FetchType.LAZY, cascade = { CascadeType.ALL })
private List<Question> questions;
private String commentToAdmin;
...
and geters-seters
}
In table I have column "status" like INT.
But then I change this class parameter to String. Now my class looks like this:
#Entity
#Table(name = "Tests")
public class Test {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private String description;
private boolean isFree;
#Column(name = "statusT")
private String status;
private String author;
private String section;
#OneToMany(mappedBy = "qTest", fetch = FetchType.LAZY, cascade = { CascadeType.ALL })
private List<Question> questions;
private String commentToAdmin;
And geters-setters
}
But after drop old table and restart my application column "status" still "INT" and his name "status" (not "statust").
Please help me understand why it's happening

Hibernate Self reference

I'm trying to map with Hibernate an entity Product with self reference to other products.
The JSON sent to create a project is like this:
{"name":"chair", "description":"red chair",
"parent": {"name":"table","description":"red table"}
}
When I receive this json, I need to persist on DB the child product and set PARENT_PRODUCT_ID with the productId from parent attribute.
Some help, please?
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer productId;
#Column(name = "NAME")
private String name;
#Column(name = "DESCRIPTION")
private String description;
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="PRODUCT_ID")
private List<Image> images;
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="PRODUCT_ID")
private List<Product> children;
#ManyToOne
#JoinColumn(name = "PARENT_PRODUCT_ID")
private Product parent;
Image.java:
#Entity
#Table
public class Image implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer imageId;
#Column(name = "TYPE")
private String type;
#ManyToOne
#JoinColumn(name = "PRODUCT_ID", nullable = false)
private Product product;
In the oneToMany relationships, I think it should be like:
#OneToMany(cascade=CascadeType.ALL, mappedBy="parent")
private List<Product> children;

Categories