2

Given the following Grails GORM Domain Classes and using table-per-hierarchy inheritance:

class Book {
  static belongsTo = [ parent: Parent ]
  String title
}

abstract class Parent {
  static hasMany = [ books: Book ]
}

class A extends Parent {
  String asset
}

class B extends Parent {
  String asset
}

Say I have retrieved an instance of class A from the database. I want to convert it to an instance of class B. What is the grails idiomatic way to do this?

Without the hasMany relation, I would just delete A and create a new B. But I don't want the overhead of going through a large number of Books and updating their parent_id fields to point to the new B.

Under the hood, I essentially just want to perform an SQL UPDATE to change the database field parent.class from A to B. So what's the recommended way to do this in GORM/Grails?

Hal Black
  • 41
  • 3

2 Answers2

1

There's no support for this in Grails or Hibernate, since it's the analogue of changing an instance of A to a B in-memory, but an object has a fixed type and cannot change. There's no direct access to the discriminator column that you need to change the value of.

So the way to do this is via a SQL update as you say. There are a few options but the best is probably groovy.sql.Sql, e.g.

import groovy.sql.Sql

class FooService {
   def dataSource

   void convertToB(A a) {
      def sql = new Sql(dataSource)
      sql.executeUpdate('update parent set class=? where id=?', [B.name, a.id])
   }
}
Burt Beckwith
  • 75,342
  • 5
  • 143
  • 156
  • I tried the native SQL and it actually didn't work in my integration test. After running the native SQL query, I was still retrieving the old version. I suspect Hibernate's caching has something to do with it. But I've gone to using a workaround instead of replacing A with B in the database. Like you say, this is really going against the grain of GORM and is taking too long as a result. Thanks for the reply! – Hal Black Apr 21 '11 at 04:46
1

Looks like a sign of design flaw.

You could try to replace inheritance with aggregation - "Favor object composition over class inheritance." (Gang of Four 1995:20)" - extract a, say, HasAsset interface from Parent and add a reference to HasAsset.

Groovy delegation could help.

Victor Sergienko
  • 13,115
  • 3
  • 57
  • 91
  • Of course the example above is greatly simplified for posting here, so some meaning has been lost. What this question is really about is replacing an entry with a specific ID with one of a different type. That is, ID #5 is no longer an A. Now it's a B. You can change everything else about A, why not the type? But you make a very good point about the Groovy Delegation, thanks for the suggestion. – Hal Black Apr 21 '11 at 04:34
  • I see you point, do you see mine? If you wish to change everything, why using a language element (class inheritance) that can't be changed? The answer you need is not necessary the one to question you asked. I don't mean you're doing everything wrong, just noted that this *might be a sign* of bad decision. – Victor Sergienko Apr 21 '11 at 08:17