0

So I have some entities that are used as the basis for a coordinate system, for the purpose of this post we'll call them A, B, C and D. Each of these entities has multiple @OneToMany relationships, and I want to cascade deletes. i.e. When some A is deleted, all entities in each of the @OneToMany relationships are deleted too. Fairly standard stuff.

However, I don't see the point in having these entities explicitly tracking these relationships when all I want to do is cascade a delete. I don't see the point in loading all these entities (potentially millions!) into memory each time a new entity is added to the @OneToMany relationship (i.e. using lazy loading only loads in when it's accessed, but it's of course accessed when a new entity in the relationship is added).

Let's add a little example:

@Entity
public class A {
    @Id
    private long id;

    // ... other fields ...

    @OneToMany
    private Collection<SomeClass> collection;
}

@Entity
public class SomeClass {
    @Id
    private long id;

    // ... other fields ...

    @ManyToOne
    A a;

    @ManyToOne
    B b;

    // ... likewise for C, D ...
}

There can be multiple classes similar to SomeClass, and so multiple @OneToMany relationships in A (and B,C,D) that require tacking. This gets tedious FAST. Also, every time a new instance of SomeClass is added, I'd need to load the entire collection and this seems exceedingly inefficient (I'd pretty much end up with my entire database loaded into memory just to cascade a delete!!!).

How can I achieve what I want without modifying the underlying database (e.g. specfying ON DELETE CASCADE in the definition), surely the designers of JPA have considered such a use case? Maybe I'm incorrect that I'd need to load the entire collection when adding an entity to the relationship (if so, please explain why :) ).

A similar question was asked here: JPA: unidirectional many-to-one and cascading delete but it doesn't have a satisfactory solution, and it doesn't discuss whether or not the entire relationship gets loaded into memory.

Community
  • 1
  • 1

1 Answers1

0

To achieve a multi-level cascade without initializing all the entities you can only use a DB cascade.

There's no other way! That's why you couldn't find a satisfactory solution.

As for the:

Also, every time a new instance of SomeClass is added, I'd need to load the entire collection and this seems exceedingly inefficient (I'd pretty much end up with my entire database loaded into memory just to cascade a delete!!!).

You need to understand the unidirectional Collections taxonomy:

  1. Adding one element to a Set, requires the whole collection to be initializes to enforce the uniqueness Set contract.

  2. a java.util.Collection or an unindexed List means you have a Bag, which are very inefficient in the unidirectional use case. For inverse collections they are fine, but that's out of your current context.

  3. An indexed List (where the order is materialized in the database) is what you might be looking for:

    @OrderColumn(name="orders_index")
    public List<Order> getOrders() { return orders; }
    

The indexed list will use the index key for add/remove/update operations. As opposed to a Bag which simply deletes all elements and recreates the collection with the remaining elements, an index List will use the index key to only remove the elements that no longer belong to the List.

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911