beachguy82 has asked for the wisdom of the Perl Monks concerning the following question:

use Data::Dumper; use Company; use User; my $dbh = User->new(); my $dbh2 = Company->new();
Both the User and Company modules subclass thier new method from the same package( the basically get a database connection) How can I avoid having to create two instances of this object. The User and Company modules contain metods to retrieve data from thier related table. It feels like I'm doing something wrong by creating two objects when the new method is the same. The database has nearly 200 tables so I don't want to have load each related module at startup.

2006-03-02 Retitled by jdporter, as per Monastery guidelines
Original title: 'How to can I avoid creating two instaces of the same object'

Replies are listed 'Best First'.
Re: How can I avoid creating two instances of the same object
by Fletch (Bishop) on Mar 02, 2006 at 01:48 UTC

    Have a singleton hold your database connection (roll your own or use something like Class::Singleton). The User and Company classes use that singleton to get access to the database handle.

    Keep in mind, however, that there may be reentrancy issues if (for example) you need to have multiple active statement handles simultaneously (depending on your database, and presuming you're using DBI).

      This is the first time I've looked at that moudule, but I'm not sure that that's what I'm looking for. Would that allow me to call methods from different subclasses, to be used on one object. I'll write a little sudo code. client will be a method of Company and user will be a method of User.
      use Company; use User; my $dbh = Company->new(); my $client_info = $dbh->client(); my $user_info = $dbh->user();
      Keep in mind that Company has gained it's new method by subclassing a parent class. I don't even know if this is possible, but it sure would be nice!

        Erm, you've just muddied the waters a good bit. Unless Company is a subclass of User why would you be able to call a method from the later on the former?

        At any rate, it looks like you might be more interested in Class::DBI (which I should have mentioned in the first place). Define your DBI connection parameters in a base class and then all of your table classes inherit from that and will do the right thing with regards to sharing a database handle.

Re: How can I avoid creating two instances of the same object
by monkey_boy (Priest) on Mar 02, 2006 at 09:45 UTC
    There is a specific solution for your problem within the DBI module itself, you need to replace your connect with connect_cached, then multiple connects to the same db will just return the same db-handle
    $dbh = DBI->connect_cached($data_source, $username, $password);



    This is not a Signature...
      I have tried that as well. That works as far as limiting one $dbh but I still have to call new for every table I create. Some of the scripts may access over 30 tables. That would mean that I would have to call new over 30 times and that is what I'm trying to avoid. What is the best method for only having to create one object then use that object over without having to call new each time I access a new table.
        What is the best method for only having to create one object then use that object over without having to call new each time I access a new table.

        Pass the $dbh to each new()?

        You might also want to look at dependency injection techniques, see IOC for one way to go about them.

Re: How can I avoid creating two instances of the same object
by Roy Johnson (Monsignor) on Mar 02, 2006 at 15:38 UTC
    I think you want to make an object whose @ISA includes all the relevant class names (whose methods hopefully do not clash). Or you can call the desired subs as class methods directly rather than through the object, passing the object as the first argument:
    # If $i were a RichMan: # $i->deedle; # But since $i isn't: RichMan::deedle($i);
    Regardless, though, you're going to have to load each module you want to use. Maybe not at startup, but somewhere in your code, before you access any of its methods.

    Update: Oh, I'd forgotten about specifying methods that don't belong to the object. That's the way to go:

    $i->RichMan::deedle;

    Caution: Contents may have been coded under pressure.
      "I think you want to make an object whose @ISA includes all the relevant class names (whose methods hopefully do not clash). Or you can call the desired methods as class methods rather than through the object, passing the object as the first argument"

      Yes! that is what I'm looking for. I thought about adding each module to the @ISA for what was the parent module already. If I do this, I will be adding close to 200 modules to the @ISA. Will this preload them each time it is loaded or will it load them as each method is called. With the other route, how do call the methods as class methods? Speed is of moderate concern. It doesn't have to be blazingly fast but it can't be slow either
        @ISA will not load the modules, you have to do that yourself. @ISA merely tells Perl what namespaces to look through to find methods. If the namespace is empty (because you haven't loaded a module), Perl doesn't particularly mind.

        The example I gave you showed you how you would call a method as a class method. The equivalent object-style call was in the comments. Speedwise, I think it's a slight benefit to call things as class methods, because Perl doesn't have to search the inheritance tree.

        Using your original example, your code might looks like this:

        use Data::Dumper; use User; my $dbh = User->new(); use Company; # Pretend that $dbh is a Company and call its do_something method, # equivalent to $dbh->do_something, if $dbh were a Company $dbh->Company::do_something;

        Caution: Contents may have been coded under pressure.
Re: How can I avoid creating two instances of the same object
by mattr (Curate) on Mar 02, 2006 at 16:15 UTC
    Hello,
    It seems you have it slightly backwards from where I sit, since I would expect that User->new would return a new instance of a User object.
      my $newuser = User->new({name="matt"});
    You should only make one database connection when your app runs and then everything uses that. The new method of each object should not be creating a new database handle. With Class::DBI anyway you wouldn't. Making a new object does not make a new database handle, it just points to the one you were using.

    You don't have to use Class::DBI as mentioned above, (i.e. you could just use DBI in the parent object) but that would be my way of doing it. For example you have WebApp::DBI (which is use base 'Class::DBI'). Then WebApp::User and WebApp::Company are both use base 'WebApp::DBI'. In WebApp::DBI I have a getdbh subroutine so all classes and objects inherit a ->getdbh method.

    package WebApp::DBI; use base 'Class::DBI'; use DBD::mysql; use Site; # where I store db account info WebApp::DBI->set_db('Main', "DBI:mysql:database=$dbname;host=$hostname;port=$port", $dbusername, $dbpassword); # you can say __PACKAGE__ instead of "WebApp::DBI" WebApp::DBI->add_constructor(all_reverse_by_id => '1=1 ORDER BY id DESC'); # a query inherited by all objects sub getdbh { # so we don't have to set up our own # db_Main subroutine that would return a dbh my @dbhandles = WebApp::DBI->db_handles; # I use Ima::DBI->db_handles which Class::DBI # inherits instead of making a sub db_Main # as Class::DBI suggests, I'm wierd. my $dbh = $dbhandles[0]; $dbh->{RaiseError} = 1; return $dbh; } 1;

    With this approach I believe you only need to use an object in a subroutine that needs it, otherwise the module is not loaded. Also objects inheriting from Class::DBI load columns lazily based on definitions of Essential columns.

    Also note that you can put generic retrieval code in the parent module (or in that parent's parent as with Class::DBI which provides the retrieve method) and then just do special cases in the child modules.

    Hope this helps. So bottom line is, for all this to be easily cleared up why not read the Class::DBI perldoc a few times and as rhesa notes you can get the db handle from its connection method or better yet as they recommend override db_Main with your own settings.

      Not to detract from your post, but Class::DBI already gives you db_Main for free. Your getdbh == WebApp::DBI->db_Main.
        Thanks you are correct and posted just as I was adding an explanation ("I'm wierd"). This is old code of mine, for some reason that seemed good at the time, but I don't remember instead of overriding db_Main I pulled it from Ima::DBI. I wonder why I did that.. put another way what other db handles would be in that array I wonder.
Re: How can I avoid creating two instances of the same object
by pajout (Curate) on Mar 02, 2006 at 15:07 UTC
    If you are absolutely sure that the two packages will not clash using the same instance of $dbh, you can do something similar to:
    package MyDbConnection; use DBI; #... our $dbh; sub get_dbh { unless $dbh { #real connection here } return $dbh; }
    ...and the User and Company packages should call MyDbConnection::get_dbh()
      The problem isn't that I don't want have multiple dbh's out there. There are several ways to avoid this. I just don't want to have 30 lines of code that says Company->new, User->new, Address->new. How can I avoid having to call new for each package?