1

I'm trying to map two sets to the same id column in an hibernate 3 mapping. The objectSet is "historic" and I was asked to keep it in there. The idSet is the new solution to have better performance if the sets grow large (a million entires).

<set cascade="none" name="objectSet" table="TBL_A_HAS_B" lazy="true" >
  <key column="A_ID" />
  <many-to-many class="test.B" column="B_ID" unique="true" />
</set>
<set name="idSet" table="TBL_A_HAS_B" lazy="true" >
  <key column="A_ID" />
  <element column="B_ID" type="string" not-null="true"/>
</set>

I want the idSet to be the leading one. So inserts and updates should only be done through the idSet. The objectSet should only be a second detailed view of the data.

To achive that I tried to set insert="true" and update="true" on the set or on the many-to-one. However both do not seam to support those attributes in this context.

How can I tell a set or list to not do any updates or inserts?

Holly
  • 1,305
  • 2
  • 15
  • 30

2 Answers2

0

I would expect you should control the behaviour by changing the cascade options. Set the one to "none" and the other to"save-update" or whatever is suitable. I havent tried this but it sounds ok. Just beware of the dirty check mechanism - refer here for further details.

user2046211
  • 356
  • 1
  • 4
  • 20
0

I found a solution that probably makes more sense than what I was originally looking for.

I added mutable="false" to the mapping so changes to the objectSet are no longer allowed. (Look here for more detail Java Immutable Collections.) I can still change the idSet which will however result in the two list being out of sync.

<set cascade="none" name="objectSet" table="TBL_A_HAS_B"
  lazy="true" mutable="false" >
  <key column="A_ID" />
  <many-to-many class="test.B" column="B_ID" unique="true" />
</set>

The sets can be synchronized again by calling: hibernateSession.refresh(instanceOfB);

public class B {

  private Set<C> objectSet = new HashSet<C>();
  private Set<String> idSet = new HashSet<String>();

  public Set<String> getIdSet() {
    return idSet;
  }

  public void setIdSet(Set<String> idSet) {
    this.idSet = idSet;
  }

  @Deprecated
  public Set<C> getObjectSet() {
    return Collections.unmodifiableSet(objectSet);
  }

  protected void setObjectSet(Set<C> objectSet)
  {
    this.objectSet= objectSet;
  }

  @Deprecated
  public final void replaceObjectSet(Set<C> objectSet)
  {
    if (objectSet != null) {
      if (idSet== null) {
        idSet= new HashSet<String>(objectSet.size());
      } else {
        idSet.clear();
      }
      for (C object : objectSet) {
        try {
          idSet.add(object.getId());
        }
        catch (Exception e) {
          continue;
        }
      }
    }
  }

}
  • getObjectSet() returns a set whose elements can be changed but that can not be added to or removed from. Trying to do an add, remove or clear on that collection will throw a JAVA exception. Without that wrap you would only get a hibernate exception once a flush() occurs or is forced. I set it to deprecated (with explaining comments) so one gets a warning and to discourage any use of it since it may be a performance issue when the set gets big and it can get out of sync.

  • setObjectSet(..) is protected so it is not publicly accessible while still allowing hibernate to call it to set the data if it gets requested.

  • replaceObjectSet() is the "new public version" of setObjectSet(..) so old code can be adapted easily but will sill be working with the new system. It too is deprecated with a link to the idSet.

Any comments if that is a good solution or not or if you have a better one are welcome.

EDIT 1:

It seems that was not the whole answer. mutable="false" did (at least by itself) not actually equal a read only. When changes to the idSet were made made and persisted, Hibernate would first add a new id based on the idSet and then delete them again since the objectSet did not contain them and as immutable will never change.

The solution was to add inverse="true".

By telling Hibernate that the other side of the association will deal with the update, it will ignore it for this mapping. Since the other end of the association does not exist nothing else will write the ids of the objectSet. So the behavior now is like a read-only.

Community
  • 1
  • 1
Holly
  • 1,305
  • 2
  • 15
  • 30