Before I answer the question, I think some background is in order.
The Core of the Problem
After years of interviewing and hiring developers, I've learned two things:
The vast majority of developers have very little experience in
database design.
I've noticed a loose correlation between those who don't understand
databases and those who hate ORMs.
(Note: and yes, I know there are those who understand databases very well who hate ORMs)
When people don't understand why foreign keys are important, why you don't embed the manufacturer name in the item
table, or why customer.address1
, customer.address2
, and customer.address3
fields are not a good idea, adding an ORM to make it easier for them to write database bugs isn't going to help anything.
Instead, with a properly designed database and an OLTP use case, ORMs are golden. Most of the grunt work goes away and with tools such as DBIx::Class::Schema::Loader, I can go from a good database schema to working Perl code in minutes. I would cite the Pareto Rule and say that 80% of my problems have been solved with 20% of the work, but in actuality, I find the benefits even greater than that.
Abusing the Solution
Another reason some people hate ORMs is because they'll let the abstraction leak. Let's consider the common case of MVC web apps. Here's something we commonly see (pseudo-code):
GET '/countries/offices/$company' => sub {
my ( $app, $company_slug ) = @_;
my $company = $app->model('Company')->find({ slug => $company_slug })
or $app->redirect('/');
my $countries = $app->model('Countries')->search(
{
'company.company_id' => $company->company_id,
},
{
join => [ offices => 'company' ],
order_by => 'me.name',
},
);
$app->stash({
company => $company,
countries => $country,
});
}
People write controller routes like that and pat themselves on the back, thinking it's good, clean code. They'd be aghast at hard-coding SQL in their controllers, but they've done little more than expose a different SQL syntax. Their ORM code needs to be pushed down into a model and then they can do this:
GET '/countries/offices/$company' => sub {
my ( $app, $company_slug ) = @_;
my $result = $app->model('Company')->countries($company_slug)
or $app->redirect('/');
$app->stash({ result => $result });
}
You know what's happened now? You've properly encapsulated your model, you've not exposed the ORM, and later, when you find that you could fetch that data from a cache instead of the database, you don't need to change your controller code (and it's easier to write tests for it and to reuse the logic).
In reality, what happens is that people leak their ORM code all over their controllers (and views) and when they hit scalability issues, they start blaming the ORM rather than their architecture. The ORM gets a bad rap (I see this repeatedly for many clients). Instead, hide that abstraction so that when you've genuinely hit ORM limits, you can choose appropriate solutions for your problem rather than let code be so tightly coupled to the ORM that you're hog-tied.
Reporting and Other Limitations
As Rob Kinyon made clear above, reporting tends to be a weakness in ORMs. This is a subset of a larger problem where complicated SQL or SQL which spans multiple tables sometimes doesn't work well with ORMs. For example, sometimes the ORM forces a join type I don't want and I can't tell how to fix that. Or maybe I want to use an index hint in MySQL, but it's not easy. Or sometimes the SQL is just so darned complicated that it would be nicer to write the SQL rather than the abstraction provided.
This is part of the reason I've started writing DBIx::Class::Report. So far it works well and solves the majority of issues people have here (so long as they're OK with a read-only interface). And while it seems like a crutch, in reality, so long as you're not leaking your abstraction (as explained in the previous section), it makes working with DBIx::Class
even easier.
So When Would I Choose DBIx::Class?
For me, I would choose it most of the times that I need an interface to a database. I have been using it for years. However, I might not choose it for an OLAP system, and newer programmers are certainly going to struggle with it. Also, I often find I need meta-programming and while DBIx::Class
provides the tools, they're very poorly documented.
The key to using DBIx::Class
correctly is the same as for most ORMs:
Don't leak the abstraction.
Write your damned tests.
Know how to drop down to SQL, as needed.
Learn how to normalize a database.
DBIx::Class
, once you learn it, will take care of most of your heavy lifting for you and makes it a breeze to quickly write applications.