On 8/17/06 6:56 PM, [EMAIL PROTECTED] wrote:
> Is there a way for Rose to auto-initialize a schema that has a many-to-many
> relationship defined?

Yep.  There are working examples in the test suite.  It all depends on
conforming to the expected conventions (which are determined by the
ConventionManager, which you can subclass and customize).

The problem in your case is MySQL-specific, however.  In order to
auto-initialize ...-to-many relationships, RDBO needs to be able to detect
foreign keys in the database.  Although it looks like you're setting them up
when you do this:

>     CREATE TABLE IF NOT EXISTS first_second_map (
>         first_id INT NOT NULL     REFERENCES firsts (id),
>         second_id INT NOT NULL    REFERENCES seconds (id),
>         PRIMARY KEY (first_id, second_id)
>     );

in reality, MySQL's default ISAM storage engine does not support foreign
keys at all, and the "REFERENCES ..." bits in your CREATE TABLE statements
are ignored.

You need to use the InnoDB storage engine in order to create "real" foreign
keys in MySQL, and even then there are very specific (and annoying) rule
surrounding the procedure.  Basically, you have to put an index on each
column that references another column, and an index on each column that is
referenced, otherwise you get the oh-so-informative "errno: 150" error when
you try to create the table.

So, the first step to getting this to work with MySQL is to change your
table definitions to look like this:

    CREATE TABLE firsts (
        id      INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
        name    VARCHAR(50)
    )
    Type=InnoDB;

    CREATE TABLE seconds (
        id      INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
        path    varchar(255)
    )
    Type=InnoDB;

    CREATE TABLE first_second_map (
        first_id INT NOT NULL,
        second_id INT NOT NULL,
        PRIMARY KEY (first_id, second_id),
        
        FOREIGN KEY (first_id) REFERENCES firsts (id),
        FOREIGN KEY (second_id) REFERENCES seconds (id),
        INDEX(first_id),
        INDEX(second_id)
    )
    Type=InnoDB;

Yes, really :)  The second step is to check that you really have InnoDB
installed and that MySQL actually made InnoDB tables like you asked it to.
If you don't have InnoDB installed, it'll just silently ignore the
"Type=InnoDB" directives.  Good old MySQL.  Anyway, to find out, do this:

    mysql> show table status from test like 'firsts';

You should see a "Type" value of "InnoDB" in the resulting row.

Finally, you'll need to ensure that your map class finds the right table.
Since the default class-to-table mapping convention is to pluralize and
underscore-separate the class name to get the table name, the class
FirstSecondMap will look for a table named "first_second_maps", which
doesn't exist:

> throws an error
> 
>     DBD::mysql::db column_info failed: Table 'testdb.first_second_maps'
>     doesn't exist at Rose/DB/Object/Metadata/Auto.pm line 85. Could not
>     auto-generate columns for class FirstSecondMap

and there you go.  So you have to either rename the mapping table and the
class to something that will convert correctly (e.g. FirstSecond and
first_seconds) or simply tell the map class what its table name is:

    package FirstSecondMap;
    use base 'Object';
    __PACKAGE__->meta->table('first_second_map');
    __PACKAGE__->meta->auto_initialize;

Finally, run the completed code.  It should find all the tables and hook
everything up, creating classes like the ones shown below.

-John

---

  package First;
  use base qw(Object);
  __PACKAGE__->meta->setup(
      table   => 'firsts',
  
      columns => [
          id   => { type => 'integer', not_null => 1 },
          name => { type => 'varchar', length => 50 },
      ],
  
      primary_key_columns => [ 'id' ],
  
      relationships => [
          seconds => {
              column_map    => { first_id => 'id' },
              foreign_class => 'Second',
              map_class     => 'FirstSecond',
              map_from      => 'first',
              map_to        => 'second',
              type          => 'many to many',
          },
      ],
  );
  
  package Second;
  use base qw(Object);
  
  __PACKAGE__->meta->setup(
      table   => 'seconds',
  
      columns => [
          id   => { type => 'integer', not_null => 1 },
          path => { type => 'varchar', length => 255 },
      ],
  
      primary_key_columns => [ 'id' ],
  
      relationships => [
          firsts => {
              column_map    => { second_id => 'id' },
              foreign_class => 'First',
              map_class     => 'FirstSecond',
              map_from      => 'second',
              map_to        => 'first',
              type          => 'many to many',
          },
      ],
  );
  
  package FirstSecond;
  use base qw(Object);
  
  __PACKAGE__->meta->setup(
      table   => 'first_seconds',
  
      columns => [
          first_id  => { type => 'integer', not_null => 1 },
          second_id => { type => 'integer', not_null => 1 },
      ],
  
      primary_key_columns => [ 'first_id', 'second_id' ],
  
      foreign_keys => [
          first => {
              class       => 'First',
              key_columns => { first_id => 'id' },
          },
      
          second => {
              class       => 'Second',
              key_columns => { second_id => 'id' },
          },
      ],
  );



-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Rose-db-object mailing list
Rose-db-object@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/rose-db-object

Reply via email to