0

My table-structure is as follows. I have sales, that contains one or more shipments. Each shipment consists of one or more batches. What i try to achieve is that when i save a sale that i have removed shipments from, the shipments gets deleted and the batches are orphaned.

I have the following classes (shortened for brevity):

@Entity
public class Sale {
    @Id
    String orderNumber;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "sale")
    List<Shipment> shipments;
}

@Entity
public class Shipment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Integer id;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "sale_order_number")
    Sale sale;

    @OneToMany
    List<Batch> batches;
}

@Entity
public class Batch {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Integer id;

    @ManyToOne
    Shipment shipment;
}

My problem is that when JPA (or hibernate?) tries to remove the orphaned shipment, it just sets all the properties of the shipment to NULL except the id.

Normally null-values is not allowed but i reconfigured the table just to see what it was trying to do in the database.

Per request, here is my database before, expected and actual results. I added some properties to the shipment table to clarify some issues:

sale
order_number
1

shipment
id  sale_order_number   amount  ref
1   1                   54      'foo'
2   1                   62      'bar'

batch
id  shipment_id
1   1
1   2
2   1
2   1
2   2

Then i post the sale for an update, with a list not containing the shipment with id 2, this is what i expect when i save:

sale
order_number
1

shipment
id  sale_order_number   amount  ref
1   1                   54      'foo'

batch
id  shipment_id
1   1
1   NULL
2   1
2   1
2   NULL

The actual output. I got a constraint violation so i allowed the properties to be NULL just to see what happened. The query succeeded afterwards but the result baffles me:

sale
order_number
1

shipment
id  sale_order_number   amount  ref
1   1                   54      'foo'
2   NULL                NULL    NULL

batch
id  shipment_id
1   1
1   NULL
2   1
2   1
2   NULL
jared
  • 473
  • 3
  • 16

1 Answers1

0

I think you should also add orphan removal flag to batches list like that

@Entity
public class Shipment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id;

@ManyToOne
@JoinColumn(name = "sale_order_number")
Sale sale;

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "shipment")
List<Batch> batches;
}

Also it's good if you are creating your database using script or any migration framework to add to the foreign key the attribute "OnDelete=Cascade"

Hesham Osman
  • 111
  • 1
  • 1
  • 5
  • For now i generate the tables needed with spring.jpa.hibernate.ddl-auto – jared Jun 10 '20 at 08:56
  • Actually, I didn't rely on hibernate before to create database schema, but you may find this link helpful https://stackoverflow.com/questions/19686941/can-i-add-on-delete-cascade-to-tables-managed-by-hibernate#:~:text=You%20can%20safely%20use%20ON,CASCADE%20for%20related%20foreign%20keys. – Hesham Osman Jun 10 '20 at 09:49
  • Either way, when i try to submit the sale with one shipment removed i get`Caused by: java.sql.SQLIntegrityConstraintViolationException: (conn=13282) Column 'amount' cannot be null`. `Amount` is one of the properties in a shipment. It just tries to `Null` all properties of the child. – jared Jun 10 '20 at 11:42
  • Try to remove CascadeType.ALL from 'sale', I edited my answer – Hesham Osman Jun 10 '20 at 13:08
  • Removing CascadeType.ALL gives me `org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing` – jared Jun 10 '20 at 21:00