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);
}