Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate - map object that belongs to two other objects (object with 2 parents)

I'm having a bit of trouble mapping the following:

public class Operation {
  private Integer id;
  private String name;
  private List<Item> items = new ArrayList<Item>();
  //set/getters/hashcode/etc. omitted

  public void addItem(Item i,Operation end) {
     i.setOperationStart(this);
     i.setOperationEnd(end};
     items.add(i);
     end.getItems().add(i);
   }

public class Item {
  private Integer id;
  private String name;
  private Operation  operationStart;
  private Operation  operationEnd;
  //set/getters/hashcode/etc. omitted
}

So basically an Operation have a bunch of Items, and an Item belongs to 2 Operations. Also, it doesn't make sense for an item to exist if one of the Operations doesn't exist, i.e. if I delete one of the Operations, I want to remove the item from wherever else it's stored as well.

Does anyone have a pointer on how I'd map the above classes, or could point me to some examples showing how to map a child object that has 2 parents ?

like image 956
Anonym Avatar asked Oct 15 '22 01:10

Anonym


2 Answers

From an object oriented point of view what is represented looks like two ManyToOne associations between Item and Operation, one of them being bidirectional. This could be mapped like this:

@Entity
public class Item {
  @Id private Integer id;
  private String name;
  @ManyToOne
  private Operation  operationStart;
  @ManyToOne
  private Operation  operationEnd;

  //set/getters/hashcode/etc. omitted
}

@Entity
public class Operation {
  @Id private Integer id;
  private String name;

  @OneToMany(cascade = CascadeType.REMOVE, mappedBy="operationStart")
  private List<Item> items = new ArrayList<Item>();

  //set/getters/hashcode/etc. omitted
}

This should result in an [ITEM] table having two FKs pointing on [OPERATION]. And populating the items collection would result in a SELECT restricted to one of them (the ID of the start operation in the above example).

I don't know if this scenario makes sense but this is IMO the only scenario Hibernate can handle. If this is not what you want, then I think you should have two collections on the Operation side (that you could maybe hide behind friendly methods).

Whether you use hbm.xml or annotations doesn't make any difference.

like image 144
Pascal Thivent Avatar answered Oct 16 '22 14:10

Pascal Thivent


This sounds like a combination of a many-to-many relation between Items and Operations, and a ternary relation between one Item and two Operations.

Assuming that your business logic is fixed on exactly two Operations per Item, and not more than that, I'd tackle this problem as follows:

  • If you want a clean object model, then create an intermediate object to hold the references to the two operations, and it should hold one item as a component.
  • Map the items in the hbm. Basically, each Operation should have a list of the intermediate object, and each intermediate object has one Item. When you delete an intermediate object, cascade the delete to the item, but not to the operations.

The tricky part, as you said, is when you delete an operation. Whether you use an intermediate object or not, you need to cascade the delete to the list with all-delete-orphan. However, I suspect that you'll have some issues due to 2nd level cache. The only way I know around that is this:

  • before deleting an operation op1, traverse the object graph and detach each intermediate object from its other operation op2, and only then flush. Otherwise hibernate will refuse to delete the intermediate objects because they are still held in some sets in other Operations.
like image 37
Yoni Avatar answered Oct 16 '22 15:10

Yoni