How to model Hibernate multi-table inheritance with ActiveRe

Hi

I have this “old” database mainly modeled for Hibernate. The java model
has something called Asset. Asset is inherited by SomeAsset and
AnotherAsset and the similar stuff is in the asset table and the
specific data for SomeAsset is in table some_asset and for AnotherAsset
in another_asset. This is then reflected in hibernate using a
element.

What would be the best way to model this in ActiveRecord? I cannot
change the exisiting database structure beyond adding new columns (ie I
cannot move values from the join tables back to the main table and use
STI).

Could I implement method_missing in Asset to just delegate to methods
that might exist in SomeAsset or AnotherAsset? I only use duck typing so
from a type perspective it would be fine. This would make it hard to
override behaviour from Asset in subclasses though (since they aren’t
subclasses…).

Is there another better solution?

Best Regards,

Marcus

On 6/28/06, Marcus A. [email protected] wrote:

I have this “old” database mainly modeled for Hibernate…
[snip]

Depending on how your model looks, one idea would be to use RBatis
which is a more flexible O/R mapper. If SQL doesn’t scare you that is.
See http://jutopia.tirsen.com/articles/2006/05/23 for more details.

/Peter

Marcus A. wrote:

change the exisiting database structure beyond adding new columns (ie I
cannot move values from the join tables back to the main table and use
STI).

Could I implement method_missing in Asset to just delegate to methods
that might exist in SomeAsset or AnotherAsset? I only use duck typing so
from a type perspective it would be fine. This would make it hard to
override behaviour from Asset in subclasses though (since they aren’t
subclasses…).

Is there another better solution?
I’d have SomeAsset and AnotherAsset instances each hold a delegate Asset
object that they proxy any calls they can’t answer themselves to. I’ve
played around a little with trying to implement a full class table
inheritance scheme for AR on that basis, but never really had enough
need for it myself to polish it up.

Marcus A. schrieb:

STI).
I don’t know ActiveRecord, but are you allowed to add a (SQL) view to
your database schema? This way you might be able to use single table
inheritance. Some databases allow even DML Operations on views.

Regards,
Pit

Alex Y. skrev:

I’d have SomeAsset and AnotherAsset instances each hold a delegate
Asset object that they proxy any calls they can’t answer themselves
to. I’ve played around a little with trying to implement a full class
table inheritance scheme for AR on that basis, but never really had
enough need for it myself to polish it up.

If I do like that I have to search in several different tables when I
want to search for assets when I don’t care about the type? Or did I
misunderstand?

/Marcus

On 6/28/06, Alex Y. [email protected] wrote:

I’d have SomeAsset and AnotherAsset instances each hold a delegate Asset
object that they proxy any calls they can’t answer themselves to. I’ve
played around a little with trying to implement a full class table
inheritance scheme for AR on that basis, but never really had enough
need for it myself to polish it up.

I agree with Alex here. While inheritance was what the tables were
originally trying to emulate, composition was the resulting design
anyways. With Ruby and it’s duck typing, inheritance isn’t required
for the ability to pass a SomeAsset where an Asset is expected. Java’s
static typing would require SomeAsset to “be a” Asset, but Ruby only
needs SomeAsset to “quack like” an asset, which can be covered with
judicious use of composition and method_missing. I’d actually provide
a mixin that sets up this method_missing. Example:

class Asset < ActiveRecord::Base
# we’re leaving out the reverse relationship to the “child” classes
# – just as a parent class implementation wouldn’t really know
about
# the classes descended from it

def xyzzy
  # model specific functionality
end

def hidden
  # model specific functionality that's not delegatable
end

module Delegator
  AssetDelegates = [ :prop1, :prop1=, :xyzzy ]
  def self.included(other)
    other.module_eval <<-END
      # we use belongs_to rather than has_one because the foreign 

key
# is in this table
belongs_to :asset

      alias :__asset_delegator_old_mm :method_missing
      def method_missing(method, *args, &block)
        if AssetDelegates.include?(method)
          self.asset.send(method, *args, &block)
        else
          __asset_delegator_old_mm(method, *args, &block)
        end
      end
    END
  end
end

end

class SomeAsset < ActiveRecord::Base
include Asset::Delegator

def xyzzy
  # override Asset's implementation of xyzzy, feels just like
  # inheritance, except that if we want to call up to the "parent"
  # version, we need to use asset.xyzzy rather than super
end

end

class AnotherAsset < ActiveRecord::Base
include Asset::Delegator

# make the hidden method, which wasn't explicitly delegated, 

available
# from this “subclass”
def hidden
asset.hidden
end
end

Jacob F.

delegation is powerful, no question…

but if SomeAsset and AnotherAsset instances each hold a delegate Asset

why not simply use inheritance?

it’s not that bad :slight_smile:

class SomeAsset < Asset
class AnotherAsset < Asset

waaay tooo much java engineers here :slight_smile:

-------- Original-Nachricht --------
Datum: Thu, 29 Jun 2006 02:17:24 +0900
Von: Jacob F. [email protected]
An: [email protected]
Betreff: Re: How to model Hibernate multi-table inheritance with
ActiveRecord?

Read about polymorphic associations…

here’s an example
http://www.fromdelhi.com/2006/06/15/polymorphic-association-in-rails/

-------- Original-Nachricht --------
Datum: Thu, 29 Jun 2006 02:11:05 +0900
Von: Marcus A. [email protected]
An: [email protected]
Betreff: Re: How to model Hibernate multi-table inheritance with
ActiveRecord?

Particularly because a model class can only map to one table.

Why should that be the case?

Did you try ActiveRecord::Base.set_table_name ?

class SomeAsset < Asset
set_table_name ‘some_asset’
end

Another approach would be to include the Asset base functionality using
‘include’

class SomeAsset < ActiveRecord::Base
include Asset
end

It should be more efficient than delegating methods…

Jacob F. schrieb:

Pete skrev:

Particularly because a model class can only map to one table.

Why should that be the case?

Did you try ActiveRecord::Base.set_table_name ?

class SomeAsset < Asset
set_table_name ‘some_asset’
end

But still, even if you override the default name of the table you map to
you can only map to one table.

Another approach would be to include the Asset base functionality
using ‘include’

class SomeAsset < ActiveRecord::Base
include Asset
end

It should be more efficient than delegating methods…
Huh? I can only include modules as far as I know. And I can’t map a
module to a table using ActiveRecord so I think that would be hard. No?

/Marcus

On 6/28/06, Peter E. [email protected] wrote:

delegation is powerful, no question…

but if SomeAsset and AnotherAsset instances each hold a delegate Asset

why not simply use inheritance?

it’s not that bad :slight_smile:

Woah… no one said inheritance was bad. Indeed, inheritance is what
is desired here. But due to constraints from both ActiveRecord and the
existing DB schema, we’re just trying to find any way that works. And
inheritance won’t work here. Particularly because a model class can
only map to one table. If you subclass an ActiveRecord model, the
subclass is assumed to live in the same table (Single Table
Inheritance). ActiveRecord doesn’t currently support Multiple Table
Inheritance (as far as I know). Thus inheritance, sadly, doesn’t work
for us here.

waaay tooo much java engineers here :slight_smile:

Nope. Not the case. :slight_smile: Just trying to solve a problem under
constraint…

Jacob F.

Hello Jacob,

I sort of like this solution but I have one problem. Querying. Say I
want to query the complete set of Assets and I don’t care what
underlying [other]Asset they have. Then I have to do:

assets = Asset.find(:all, :condition => “somecondition”)

But, I always need to get the delegate from the assets in order to work
with them. Thus the above expression changes to:

assets = Asset.find(:all, :condition =>
“somecondition”).map(&:delegate_asset)

Now I have the correct asset instances to work with. The problem is that
I have to do that extra map() all over the place. 1) It violates DRY and
2) someone will forget it

Is there a solution to this (apart from decreing project wide that you
may never query Asset outside of Asset itself and thereby encapsulate
all queries)?

/Marcus

On 6/28/06, Pete [email protected] wrote:

Particularly because a model class can only map to one table.

Why should that be the case?

Did you try ActiveRecord::Base.set_table_name ?

class SomeAsset < Asset
set_table_name ‘some_asset’
end

As Marcus mentioned, this lets you change the table for the model,
but not aggregate multiple tables for the same model. The case
Marcus proposed in his original post requires an aggregation of
multiple tables to define one model.

Another approach would be to include the Asset base functionality using
‘include’

class SomeAsset < ActiveRecord::Base
include Asset
end

It should be more efficient than delegating methods…

That’s actually pretty much what I’m trying to achieve with my
Asset::Delegator. The reason I can’t just include Asset is 1) only
modules can be included, as Marcus pointed out and 2) importing (or
delegating) all of the methods from Asset can conflict with the
already existing method_missing functionality for SomeAsset provided
by ActiveRecord. So I threw together a quick example of a module that
can be mixed in that feels like you’re mixing in Asset itself, but
with a limited method set. True, the number of methods that need to be
enumerated in Asset::Delegator::AssetDelegates will probably be high,
depending on usage, but I can’t see anyway around that without
polluting method_missing.

Jacob F.

On 6/28/06, Marcus A. [email protected] wrote:

I sort of like this solution but I have one problem. Querying.

This is a good point. I actually noticed it in your earlier reply to
Alex, but couldn’t think of anything to add. As far as I can tell, the
situation is indeed as you tell it. :frowning:

Anyone else have any ideas on this?

Jacob F.

So far I have two solutions:

  1. implement method_missing in Asset and delegate to [other]Asset where
    Asset cannot handle the requested method

  2. implement method_missing in [other]Asset and delegate back to Asset
    where [other]Asset cannot handle the request (or a variant thereof)

  3. gives easy querying (I can just query Asset for all objects) but I
    cannot override methods from Asset in [other]Asset. Not really good.
    This solution could be good if I could somehow make Asset ask
    [other]Asset if they have the requested method before calling the method
    in itself instead? Sort of like a default before handler on every
    method.

  4. In order for this to work I always need to work on instances of
    [other]Asset. That in turns mean that I either have to make the query on
    [other]Asset class to get instances of [other]Asset or make the query on
    Asset and then start off by getting the [other]Asset instance from it
    and work with that (ie using Asset.find(:all).map(&:delegate_asset)).
    Neither of them feels really polymorphic. A solution here would be if
    one could bend ActiveRecord to return [other]Asset instead of Asset
    instances when querying the Asset class. I suppose this would be the
    solution to multi table inheritance though if I could solve it…

What I’m after is a solution to make it transparet for the clients of
the Asset class (and associates). I don’t care how much/ugly code I have
to hide in Asset to just make it transparent to the clients (since I
have control over Asset, not over the clients).

Have I missed anything? 2) sounds easy and good but I can’t grip the
querying situation (beyond what I described above). Have I missed
something there?

Thanks for the answers.

/Marcus

Peter K. skrev:

On 6/28/06, Marcus A. [email protected] wrote:

I have this “old” database mainly modeled for Hibernate…
[snip]

Depending on how your model looks, one idea would be to use RBatis
which is a more flexible O/R mapper. If SQL doesn’t scare you that is.
See http://jutopia.tirsen.com/articles/2006/05/23 for more details.

Heh, I’m not scared of SQL. I’ve actually used iBatis (I used to be a
hardcore java junkie…) in the past so I’m quite familiar with its
concepts. It’s actually possible to use pure SQL queries with
ActiveRecord as well.

The problem is that I don’t know how to solve this problem using
straight SQL either without making the lives of the Asset clients a lot
harder.

/Marcus