How do people that abstract away their database via an ORM feel comfortable about not dealing directly with the data and accidentally dropping a db column via ORM? I know when I am dealing with the database I am a lot more surgical in my approach than when I am writing code.
Schema changes are often (normally?) done using a separate mechanism and operations called migrations rather than in the main code.
In a production environment they should be well tested before deployed to the live system. That plus backups should help prevent calamities.
There are still possibilities of deleting the data whether through the ORM or direct SQL, both could be easily accomplished so test before deployment and keep regular backups in case of disaster.
The same way I feel comfortable that I won't accidentally write and execute a SQL script that does it. ORM doesn't make it especially trivial to corrupt your database.
"dropping a db column via ORM" - prevent this via security configuration, release schema changes to production via plain SQL. ORM is not a complete replacement for dealing with DBMS.
You generally only let the ORM create/update your schema during early development. By the time you get to production (or heck, by the time your team is even seriously engaged), you will have disabled that feature. In pretty much every enterprise-grade ORM I've ever seen, schema modification is disabled by default, and must be explicitly turned on. In most Java shops where I've ever worked... unless it's a quick prototype, the database schema will be developed before you start coding anyway. Also, in a typical enterprise scenario, the username with which you configure the ORM lacks admin-level privileges to alter the database.
Most of the criticisms about ORM's come from people who have never used them, beyond maybe working through a Rails chat-room tutorial once upon a time. It really has nothing to do with "abstracting away the database".
As the name indicates, Object-Relational Mapping is merely about reducing the boilerplate required to map a relational schema to programming language objects. If you do that mapping by hand, then you have to make decisions when a table/object has relationships. Picture a CUSTOMER table, which has a foreign key relationship to an ADDRESS table. When your application loads a "Customer" object:
[1] You could "eager fetch", meaning that you go ahead and retrieve all of the ADDRESS rows related to that CUSTOMER, and attach the Address objects to the Customer object. Eager fetching is wasteful and leads to poor performance, because you're hammering your database for values that you often don't ever use.
[2] You could "lazy load"... meaning that your Customer object has an "addresses" field, but you wait until some code tries to use that field before you actually query the ADDRESS table to populate it. This is much better design, but complicates things. The lazy load logic has to go somewhere. You either have to ensure that every piece of code using that object is aware of the lazy load pattern, or you have to stuff database logic into the "getter" method for each lazy-loaded field.
ORM's give you highly-performant lazy loading, without the buggy boilerplate suggested by #2 above. Moreover, enterprise-class ORM's typically handle caching for you, to avoid hitting the database unnecessarily. Monitoring the state of objects to notice when they've gone stale due to changes on the database side, etc.
Lastly, for complex queries, most ORM's have a query language (e.g. JQL, HQL, etc) that is nothing more than a VERY THIN wrapper around SQL. It merely smooths out differences between various vendor dialects. You're not abstracted away from the database, you still very much need to understand SQL and the underlying structure of your data.
you still very much need to understand SQL and the underlying structure of your data
That's good to hear! Though in my experience of interacting with people who have learned development in last 3-5 years, many of them are very oblivious to basic database concepts. They mostly think in terms of the object in the code, relying on the framework to take care of the database. It might even generate the sql that they just need to execute. But anyone who's worked with production database from before the advent of frameworks like Rails etc. knows that the only thing worse than messing up an sql statement is running auto generated sql.
I think your characterisation of eager vs lazy fetch is oversimplified. Lazy loading can be a source of substantial performance problems as well, since doing larger operations that do hit a lot of data causes enormous chattiness with the DB, causing you to lose a bunch of time to network I/O. Lazy loading was the source of a lot of early 'ORMs are slow' arguments.
A good ORM will likely default to lazy loading but give you good options for eager retrieval when you know you're going to need it.
And what you do if in one scenario you need lazy and in another scenario you need eager fetching? Create two separate mappings and tho separate class hierarchies to hold them? Ignore the problem and pray noone will notice it? What you do if you already closed DB transaction, and you need to access lazy collection?
In my experience, this lazy/eager is the main reason I don't like rich ORM mappings. I usually don't map collections and don't map foreign keys. If I need to fetch something, I just do it. If I need to return a result of a complex query, I just define datatype for it. In general, my ORM just converts recordset to object, and that's it it, besides simple record level CRUD operations. One record operations like findByPK or deletByPK or updateByPK are fine, but for anything else I prefer writing SQL, instead of suffering with some ORM specific SQL replacement or, $deity forbid, some api. SQL is as good DSL as you can get for RDBMS access, and is fairly portable between different programming langagues and ORMs, so no reason not to use it.
But many
Yeah, ORM is really about portability. Your app should be able to plug into whatever database you have drivers for.
And it's also much more convenient to call a find() method than to concatenate together a (vendor-specific) connection string and query string, and iterate over a result set object.
You really should not be doing that. Databases should be nothing but ice-boxes. That's why ORMs. It's a bad practice to spend more tending to your DB than your GUI. And I'm pretty sure it comes directly from using SQL and stuff like that.
This is exactly wrong for the majority of systems. Your application is ephemeral. The data the application works with might last until after you're dead.