Pencils Down

This weblog is about my experiences in software development

Browsing Posts tagged Hibernate

We have several wizards in the application we are developing under JBoss/icefaces/Spring.  We had decided some time ago to provide long running sessions to allow the user to move between wizard screens and then finalize (commit) later in time.

This all works pretty well until you get to a delete operation where there is some non-primary constraint involved.  The typical culprit is a unique ‘name’ for an object.  As you all know Hibernate takes all of the underlying SQL operations and lines them up in roughly the order of INSERT, UPDATE and then the DELETEs.  (There are a few other cases that do not apply here).

So, in a wizard if the user does something that boils down to add a thing called X, delete a thing called X and then add a thing called X again – your app will die.  Using the above Hibernate ordering you end up with INSERT X, INSERT X, and DELETE x throwing a constraint violation exception.

We have played with a few ideas here but none of them ‘feel’ like we are using Hibernate correctly:

  • Don’t delete, just mark the record – doesn’t solve the unique constraint
  • Cache deletes by hand – painful coding
  • Delete forces a flush – violates the whole point of the long transaction/session

Leaning towards the last one now, but client may want #2.

The normal Hibernate entity collection returns a Set<?>.  This is slightly deceptive for Java developers because it would appear that you can perform standard Set operations on the entity collection and have Hibernate do the right thing.  This is an invalid assumption.

For example, Set.clear() would appear to remove all elements in the collection.  In reality it is not clear what Hibernate may do when you perform this step.

Once you realize the clear() call did not work the typical work might be to create a Set in memory from the entity.getCollection() call assuming that your memory Set exists as a separate object.  Again, this is an invalid assumption.  What you have done with the statement Set<?> set = entity.getCollection() is actually copy the Hibernate proxied object into your memory Set object.  As such it will be maintained just as if you left it in the original entity in the first place.

The result is to take the Set and copy all the materialized objects out of the Hibernate Set into some other collection, as in:

List<?> list = new ArrayList<?>();

list.addAll(hibernateSet);

You now finally have a collection that will NOT be maintained by Hibernate that you can operate on directly.

This error message usually means your database has bad references between tables.  As Hibernate will enforce the integrity of these relations this is usually caused via some data load operation.

A recurring situation with us was using a data load script that was O/S dependent.  So, it would work great on Windows and then the same script running on Redhat would throw spurious data into elements.

Particularly hard to detect are String keys where trailing spaces do not normally display when using SQL tools, but are present and cause a mismatch on keys.

One of our developers had the following type of code in place:

Parent parent = new Parent();
dao.create(parent);
Child child = new Child();
parent.getChildren().add(child);
dao.saveOrUpdate(parent);
dao.create(child);

All of this code worked!  We never noticed the error until we put a Hibernate wrapper in play that has asserts for all kinds of extraneous conditions.  In the above code the child has not been created in Hibernate yet.

So, why did it work in straight Hibernate?  We think it didn’t work and only gave a silent error/exception.  All of this would be cleaned up later as a cleanup task by the create(child) call or the even later flush() call.

Of course, once we switched the order of the create(child) call with the saveOrUpdate(parent) it all worked.

We had noticed similar silent errors for delete() calls as well.

One of our developers came up with some sample code like the following:

//create an entity

Entity e = new Entity();

e.setEntityName(“x”);

EntityId id = dao.create(e);

//attempt to find it

Entity e2 = dao.createQuery(“from Entity where entityName=’x').list.get(0);

//???? e2 is null !!!!!!

//look a different way

Entity e3 = dao.findById(id);

//???? e3 is correctly loaded

The Hibernate cache is id based. So, HQL queries ignore the cache when looking around for matches and go direct to the db (e2), unless id’s are involved (e3).

The choices are then:

- If you need to reference cache objects by other than id (as above), keep a map as you progress

HashMap<String,EntityId> map.put(“x”,id);

Entity e4 = dao.findById(map.get(“x”));

- or flush() as needed so the HQL will ‘work’

I think most of the time this (keeping id’s around) works. For example most DTOs and UI representations are maintaining the id of the object and later retrieving by that id. Unfortunately, I know there are a couple of pain points where the id is not the key of interest so additional coding will have to be used.

Also, flushing continually defeats the whole purpose of having a long running session/transaction.  So, that option is of limited use as well.

You may have recieved an exception in Hibernate like:

org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session…..

This is likely caused by your code doing something like:

session.get()

object.setSomething(value)

session.update()

The session.update() call is attempting to attach an object to the Hibernate session.  This is done by the object id.  If you already have an object with the same id associated with the session (i.e. maintained by Hibernate) you get this error.

You have two choices:

1) Don’t call update on the object.  It is already being maintained by Hibernate.  There is no need to do so.  A later flush() will push any changes recorded in the session to the database.

2) If you can not tell if the object is associated with the session (likely in a long user scenario) call merge() instead.  Merge() takes the instance passed and merges it with any object of the same id associated with the session.  Note, if you plan on using the object again you must use the object returned by the call to merge().  The instance object passed to merge will not be updated.

After much searching around the Internet I have come to the conclusion that Hibernate really doesn’t support self-referencing tables. The typical example is something like this table:

Table Category
---------------
categoryId int
name varchar(50)
parent int

In this example the Hibernate code would be something like:

Category parent = ...
Category child = ...
child.setParent(parent)
dao.create(child);
//this does not appear to actually do anything in a self-referencing table
parent.getChildren().add(child);
dao.saveOrUpdate(parent);
session.flush();

//and then later on

Category category = categoryDao.something();
for(Category child:category.getChildren()) {
  //do something with each child
}

Notes:

  • Since the reference is in the same table, the above add() to the parent children does nothing.
  • The setParent() call is just setting the parent column directly in the Category object – no database action is taking place.
  • Calls to getChildren() are doing a simple query unlike other getObject ORM calls in Hibernate
  • Calls to getParent() are also doing abnormal ORM calls

All of the above code ‘works’. It just doesn’t work like anything else really in Hibernate.

In our application we have the typical hierarchical object organization that is reflected in the data store.  This fits nicely into a relational database.  Being a modern shop (surprising given the industry – defense) we use Hibernate for the ORM.  Unfortunately most developers, while somewhat familiar with Hibernate and SQL, have very little experience using HQL.

A typical problem would be list all the level 3 objects in the system.  So, you might find code like:

Set<Level3Id,Level3> hash = new HashSet<Level3Id,Level3>(); 
Set<TopLevel> topLevels = dao.findAll();
for(TopLevel top:topLevels) {
    Set<Level2> level2s = top.getLevel2s();
    for(Level2 level2:level2s) {
        Set<Level3> level3s = level2.getLevel3s();
        for(Level3 level3:level3s) {
            hash.put(level3.id, level3);
        }
    }
}

where the developer is constructing a HashSet to produce a unique list of level 3 objects.  This is a simple example, there are many use cases where we traverse much lower into the object graph using some complicated tests (WHERE clauses).

The alternative HQL is trivial in comparison and the performance improvement is astounding (remember due to lazy loading each of the objects above would have to be materialized even though we never really wanted them in the first place):


List<Level3> level3s = session
        .createQuery("select distinct level3 from Level3 as level3")
        .list();

I guess the message is to try and think about the underlying SQL database that you have available.  While Hibernate obscures access to the underlying database it normally exposes all of the functionality present in the SQL into HQL in some manner.  So, if you could do something neat in SQL chances are you can do the same neat access in HQL.

We have a case where most of our tables have composite ids.  This means a key with multiple parts.  While fairly common in the database world, Hibernate appears to prefer that it’s users NOT have complex keys.  The mapping definitions don’t really allow for this to occur (e.g. you can’t specify a generator class in a composite key)

We had thought about generating a single column key (something like a hash of the composite elements), but that just seemed wrong.  I always thought a database should model reality, not some arbitrary manifestation to get a tool to work.

In this example we have two tables that look like this:

Table A
——-
A_NAME VARCHAR(200)
A_ID INT
attributes ….

Table B
——-
B_NAME VARCHAR(200)
B_ID INT
attributes ….

They both have composite ids.  A helper table is generated that provides the many-to-many mappings for the two:

Table AB
——–
A_NAME VARCHAR(200)
A_ID INT
B_NAME VARCHAR(200)
B_ID INT

Using standard Hibernate reverse engineering we end up with the following mappings:

Table A Mapping
—————

<composite-id name=”id” class=”path.to.entity.classes.AId”>
 <key-property name=”aId” type=”integer”>
  <column name=”A_ID” precision=”22″ scale=”0″ />
 </key-property>
 <key-property name=”aName” type=”string”>
  <column name=”A_NAME” length=”12″ />
 </key-property>
</composite-id>

<set name=”bs” table=”table_ab” cascade=”all” inverse=”true”>
 <key>
  <column name=”A_NAME” not-null=”true” />
  <column name=”A_ID” not-null=”true” />
 </key>
 <many-to-many class=”path.to.entity.classes.B”>
  <column name=”B_NAME” />
  <column name=”B_ID” />
 </many-to-many>
</set>  

Table B Mapping
—————

<composite-id name=”id” class=”path.to.entity.classes.BId”>
 <key-property name=”bId” type=”integer”>
  <column name=”B_ID” precision=”22″ scale=”0″ />
 </key-property>
 <key-property name=”bName” type=”string”>
  <column name=”B_NAME” length=”12″ />
 </key-property>
</composite-id>

<!– CRITICAL – Only one side can have inverse=true, the other must have inverse=false –>
<set name=”as” table=”table_ab” cascade=”all” inverse=”false”>
 <key>
  <column name=”B_NAME” not-null=”true” />
  <column name=”B_ID” not-null=”true” />
 </key>
 <many-to-many class=”path.to.entity.classes.A”>
  <column name=”A_NAME” />
  <column name=”A_ID” />
 </many-to-many>
</set>  

Table AB Mapping
—————-

<many-to-one name=”a” class=”path.to.entity.classes.A” update=”false” insert=”false” fetch=”select”>
 <column name=”A_NAME” not-null=”true” />
 <column name=”A_ID” not-null=”true” />
</many-to-one>
<many-to-one name=”b” class=”path.to.entity.classes.B” update=”false” insert=”false” fetch=”select”>
 <column name=”B_NAME” not-null=”true” />
 <column name=”B_ID” not-null=”true” />
</many-to-one>

Be sure to note use of the inverse attribute in the A and B mappings: only one side of a many-to-many can be the ‘owner’ of the relationship.  The owner is denoted with inverse=true setting.  The other side of a many-to-many MUST be inverse=false.  It is completely arbitrary which is the owner, but Hibernate will not work unless there is only one.

So, once the mapping is cleared up we produce fairly normal Java code to create a many-to-many relation:

A a = new A();
a.setXYZZY(“various attributes”);
AId aid = new AId();
aid.setPKPart1(“string”);
aid.setPKPart2(4);           
a.setId(aid);

session.save(a)

B b = new B();
b.setXYZZY(“various attributes”);
BId bid = new BId();
bid.setPKPart1(“string”);
bid.setPKPart2(12);           
b.setId(bid);

session.save(b)

a.getBs().add(b);
session.saveOrUpdate(a);

b.getAs().add(a);
session.saveOrUpdate(b)

session.flush();

Just to be sure, note we are setting the composite id fields manually.  In Oracle this can be done by accessing a Sequence.  Other databases have built-in row ids.

I think we have a fairly standard database that looks like a parts BOMB.  Someone had the neato idea to use cascading delete in the project.  Hibernate’s cascading delete will follow any required foreign key and delete the children it finds there in a cyclical manner.  Good idea, huh?

No.  This means parent entity Parent with a primary key of ParentPrimaryKey MUST cascade it’s primary key to all children.  So, entity Child has a primary key of ChildPrimaryKey PLUS ParentPrimaryKey.  This continues all the way down your entity tree.  This example assumes simple row id’s for primary key.  If there was some other overriding attribute which makes every primary key a composite, like SystemThatThisEntityLivesOn, then every level has one of those plus all the parent’s.

Then we realize, we can’t actually delete some of the lower level entities because they are a lot of work to create.  So, we configure Hibernate to stop at those entities.

Now if we step back and look at the entity diagram for our database it is not uncommon for an entity at a lower level to have close to 20 component parts of a primary key that have cascaded down.

But, we only delete via cascade a very small subset of the entity tree.

Now, throw into the mix some developers who don’t understand the above features of a relational database or Hibernate in general and we now can’t use the LowLevelEntityId composite object that Hibernate generates as that is ‘unclean’.  We are to flatten all of these id’s wherever used.