1

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

  1. 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.)
  2. I also tried to change the Cascade types to MERGE like in this solution
  3. I also tried using @Transactional on the method
  4. 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);
}
StitZle
  • 89
  • 3
  • 12

1 Answers1

0

You can try to use instead of orderRepository.save(order) use orderRespostiory.saveOrUpdate(order).