How to model Hibernate multi-table inheritance with ActiveRecord?

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 <joined-subclass/> 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

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

···

On 6/28/06, Marcus Andersson <m-lists@bristav.se> wrote:

I have this "old" database mainly modeled for Hibernate...
[snip]

Marcus Andersson wrote:

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 <joined-subclass/> 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?

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.

···

--
Alex

Marcus Andersson schrieb:

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 <joined-subclass/> 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).

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

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)

1) 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.
2) 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

Alex Young 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

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 Fugal

···

On 6/28/06, Alex Young <alex@blackkettle.org> 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.

Peter Krantz skrev:

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

···

On 6/28/06, Marcus Andersson <m-lists@bristav.se> wrote:

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 Andersson <m-lists@bristav.se>
An: ruby-talk@ruby-lang.org
Betreff: Re: How to model Hibernate multi-table inheritance with ActiveRecord?

Alex Young 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

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 Fugal <lukfugl@gmail.com>
An: ruby-talk@ruby-lang.org
Betreff: Re: How to model Hibernate multi-table inheritance with ActiveRecord?

On 6/28/06, Alex Young <alex@blackkettle.org> 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 Fugal

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

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 Fugal

···

On 6/28/06, Peter Ertl <pertl@gmx.org> wrote:

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 Fugal

···

On 6/28/06, Marcus Andersson <m-lists@bristav.se> wrote:

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

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 Fugal schrieb:

···

On 6/28/06, Peter Ertl <pertl@gmx.org> 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 Fugal

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

> 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 Fugal

···

On 6/28/06, Pete <pertl@gmx.org> wrote: