views:

100

answers:

1

I have an application using Hibernate for data persistence, with Spring on top (for good measure). Until recently, there was one persistent class in the application, A:

@Entity
public class A {
  @Id
  @Column(unique = true, nullable = false, updatable = false)
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;
  public String name;
}

I have since added a subclass of A, called B:

@Entity
public class B extends A {
  public String description;
}

After adding B, I could now not load A's. The following exception was thrown:

class org.springframework.orm.hibernate3.HibernateObjectRetrievalFailureException :: Object with id: 1 was not of the specified subclass: A (Discriminator: null); nested exception is org.hibernate.WrongClassException: Object with id: 1 was not of the specified subclass: A (Discriminator: null)

I added the following annotation and property to B, and it seems to have solved the problem. Is this the right way to solve the issue?

...
@DiscriminatorFormula("(CASE WHEN dtype IS NULL THEN 'A' ELSE dtype END)")
public class A {
    private String dtype = this.getClass().getSimpleName();
    ...
A: 

(...) Until recently, there was one persistent class in the application, A:

With the following database representation:

ID  NAME
--  ----
 1   foo
 2   bar

I have since added a subclass of A, called B (...)

And you didn't specify the Inheritance annotation so the SINGLE_TABLE mapping strategy is used. And In this strategy, all the classes in a hierarchy are mapped to a single table. The table has a column that serves as a “discriminator column”, that is, a column whose value identifies the specific subclass to which the instance that is represented by the row belongs.

The table then became:

ID  NAME DTYPE
--  ---- -----
 1   foo  NULL
 2   bar  NULL

Where DTYPE is the default name of column to be used for the discriminator.

After adding B, I could now not load A's. The following exception was thrown (...)

Indeed, because existing values have a null value in the discriminator column, the provider doesn't know what subclass to instantiate.

I added the following annotation and property to B, and it seems to have solved the problem. Is this the right way to solve the issue?

That's one way but it is intrusive (your entities shouldn't be aware of the dtype column) and Hibernate specific. In other words, it's a hack.

For me, the "right" way to solve this would be to update the DTYPE column of existing A records to set the value to 'A' (with Hibernate, the value defaults to the entity name):

UPDATE A SET DTYPE='A' WHERE DTYPE=NULL

This way, Hibernate would be able to load them properly.

Pascal Thivent
@Pascal thanks for the detailed answer. Unfortunately I won't have direct access to all the installs of the application, so I'd like this schema change to happen transparently to users. I tried adding the DiscriminatorFormula without explicitly defining the dtype field, but this didn't work. It felt like a hack when I was doing it - that's why I asked.
Alison
@Alison You're welcome. I gave you what is IMO the "ideal" solution. If it's not suitable in your context, if you can't provide a migration script to your users (that would perform the `UPDATE` after the various `ALTER`) - or even better an automated migration tool - then your solution is acceptable. At least it work. And now you know it's kinda a hack :)
Pascal Thivent
@Pascal :-) I've found info on migrations in Hibernate/Spring quite hard to come by, unfortunately.
Alison