[odb-users] Inheritance

Boris Kolpackov boris at codesynthesis.com
Fri Jul 8 10:15:14 EDT 2011


Hi Jan,

Kuentzer, Jan <jan.kuentzer at roche.com> writes:

> I would think the third (or second) should work fine, since this would
> result in no change of the kernel classes.

The first approach doesn't really require changes to the base classes.
Underneath the ORM layer, however, all these classes are mapped to a
single table, so when you add a new derived class, that table has to
be extended with additional columns.


> However, I cannot image how the performance difference would be? 

With the first approach (single table per hierarchy), all the operations
(load, update, query, etc) will require a single statement (select, 
update, etc). With the second approach, the load/query operations can
be implemented only as several queries or using joins, both of which
are more expensive than a single simple query. For update, we will always
need several update statements. With the third approach the load/query
operations are even more complex/expensive while update goes back to
a single statement.


> The question is after a query do you get the example objects as instance
> of permanent_employee or only employee? We would need to know the type
> of the class.

When polymorphic inheritance is implemented, if you do load (or query) 
like this:

employee* e = db->query<employee> (2);

And the actual object corresponding to id 2 is permanent_employee, you 
will get a dynamic instance of permanent_employee as an employee pointer.
You will then be able to discover the dynamic type of the object at
runtime, for example:

if (permanent_employee* pe = dynamic_cast<permanent_employee*> (e))
{
  // We have a permanent employee.
}
else if (temporary_employee* pe = dynamic_cast<temporary_employee*> (e))
{
  // We have a temporary employee.
}
else
{
  // Some other employee.
}


> If there is a emulation I would be interested since I like your project
> and it would save me a lot of time and effort. But I rely on the predefined
> data model based on the polymorphic inheritance. For the time being the 
> load() would be okay and I could add queries as soon as ODB supports
> polymorphic inheritance.

The idea behind emulation is to encode the object type into the id. This
way, when you load the object, you can first figure out its actual type
and then load it from the database. In this approach you cannot use the
automatically-assigned ids for the hierarchy, and all objects in the
hierarchy must share the same id space.

As an example, let's use a 64-bit integer as an id for our employee
hierarchy. To encode the object type, we will reserve the top byte
and will use 1 for permanent_employee and 2 for temporary_employee
(employee class is abstract and doesn't need a type code).

#pragma db object abstract
class employee
{
public:
  ...

  unsigned long long
  id () const
  {
    return id_;
  }

protected:
  friend class odb::access;
  employee () {} // Default c-tor for ODB.

  // C-tor for derived types.
  //
  employee (unsigned char type_code, unsigned long long id)
  {
    id = (type_code << 56) | (id & 0x00FFFFFFFFFFFFFFUL);
  }

  #pragma db id
  unsigned long long id_;
};

#pragma db object
class permanent_employee
{
public:
  permanent_employee (unsigned long long id)
    : employee (1 /* type code for permanent_employee */, id)
  {
  }

  ...

};

#pragma db object
class temporary_employee
{
public:
  temporary_employee (unsigned long long id)
    : employee (2 /* type code for temporary_employee */, id)
  {
  }

  ...

};

Next we need to wrap the database operation with some extra logic.
Here is polymorphic persist:

unsigned long long
persist_employee (odb::database& db, employee* e)
{
  switch (d->id () >> 56) // Get the type code.
  {
  case 1:
    return db->persist (static_cast<permanent_employee*> (e));
  case 2:
    return db->persist (static_cast<temporary_employee*> (e));
  default:
    assert (false);
  }	
};

And here is polymorphic load() (note that id should be what's returned
by the persist() call or what's stored in employee::id_, not what we
pass to *_employee constructors).

employee*
load_employee (odb::database& db, unsigned long long id)
{
  switch (id >> 56) // Get the type code.
  {
  case 1:
    return db->load<permanent_employee*> (id);
  case 2:
    return db->load<temporary_employee*> (id);
  default:
    assert (false);
  }
}

Finally, in order to have a polymorphic object relationship, you will
need to emulate it by having a persistent member that is the object
id and a transient member which is the actual pointer:

#pragma db object
class employer
{
  ...

  unsigned long long ceo_id_;
  
  #pragma db transient
  employee* ceo_;
};

You will then need to use the load_employee() function to load the ceo_
pointer. For the next release of ODB we have added support for user
callbacks. This feature will allow you to handle such loading 
transparently.

Boris



More information about the odb-users mailing list