Question

On my entity (myGroup : Group) I have a collection property (members : HashMap<Long, GroupMember>) and I want to allow the user to add multiple new (unpersisted) GroupMembers to the myGroup.members map.

  • The reason why I am using a hashmap for the collection property is to ensure the use of the collection is performant (i.e. must be able to get a GroupMember by its ID quickly and multiple times during execution of the business logic).

  • The reason why I don't want to persist them straight away is to support reversion of the additions. The user must be able to manipulate the myGroup.members map, adding and removing members until satisfied, before persisting (saving the entities in the Hibernate session and flushing).

The problem is that new GroupMembers must be assigned an ID of zero for Hibernate to detect that they're unsaved.(*) Because the members are in a map, keyed by ID, that means only one new member can be added prior to either a save or revert. The addition of a second new member with ID zero simply overrides (the reference to) the first.

My question is how can I best adapt my code solution to support the addition of multiple unsaved entities to the collection (myGroup.members) while keeping the performance advantage of using a hashmap or other map/dictionary?

Note: I have already tried a hack whereby a ID is derived from unique combination of data in a GroupMember instance and then, just before a save, the ID is set to zero in order for Hibernate to detect it needs to be persisted, but it is inelegant and I haven't found a way for it to work reliably.

* I don't fully understand Hibernate's behaviour in this regard.

Abbreviated code for the Group entity below:

@Entity
@Table (name="GROUP")
public class Group
{
    private Map<Long,GroupMember> members = new HashMap<Long,GroupMember>();

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "ownerGroup", cascade = { CascadeType.ALL })
    @MapKey(name="id")
    public Map<Long,GroupMember> getMembers() { return this.members; }

    public void setMembers(Map<Long,GroupMember> members) { this.members = members; }

    // ...other properties and methods...
}
Was it helpful?

Solution

I am a bit confused on what you are going to achieve. Members that are not persisted do not have any ID. What do you expect the "lookup" behavior for these members as you are using ID as the key? It don't seems reasonable for me that you can lookup new members by ID, as they do not have any identity yet.

If lookup new members is not required, I will suggest you:

  1. a separate attribute storing "new members"
  2. have method commit() and revert() in your entity
  3. User will invoke commit() when it is done updating, and new entities will then be moved to the actual "mapped" map/list for actual persistence

If lookup of new members is REQUIRED, and from your passage, it seems that you are capable to generate a proven unique key yourself, will you consider NOT having hibernate to generate the ID for you? You have the option in Hibernate to decide whether the ID is generated by Hibernate (as a surrogate key) and provided by you (kind of a natural key). I believe a self-provided ID is more suitable in your case, as you seems not treating the ID as a surrogate key.


Edited: with respect to the comment from the OP about the reason for unable to store it separately, it can be easily solved in this approach (and I think it makes more sense too):

(pusedo code)

class Group {
  private List<Member> members;  // mapped in hibernate
  private List<Member> unsavedMembers;  // unmapped

  public Map<Long, Member> getProposedMemberMap() {
    // returned the combined "view" members and unsavedMembers
  }

  @PreUpdate  // you may consider running it it pre-update too
  public void commit() {
    members.addAll(unsavedMembers);
    unsavedMembers.clear();
  }

  public void revert() {
    unsavedMembers.clear();
  }
}

By doing so, you have a view of the "proposed members" for you to iterate, while storing the actual and unsaved members separately.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top