Live data from Hacker News

What ORMs have taught me: just learn SQL

wozniak.ca

221–230 of 245 posts

Re: What ORMs have taught me: just learn SQL

#221
post #200

I used to write raw SQL for many years, then, around 2005 switched over to ORMs in order to be able to target different databases, have a nice model, etc. Lets be honest here, the ease of justing doing: p.username = "Carl" p.age = 33 p.save instead of "update users set username=:username, age=:age where id=:id" has a ton of advantages. For one, some sort of syntax or type checker is actually trying to understand your…

>Strongly typed lanaguages are even cooler here ... No expereince with Slick in particular; but I've been using jOOq[1] which I believe is similar. To be honest I'm not entirely sold that these DSLs are what I'd consider "strongly typed." I can get jOOq to pretty easily yield queries that won't work if I switch out database dialects. (Ignoring, for a moment, that jOOq will let you embed SQL fragments as strings.) As…

> To be honest I'm not entirely sold that these DSLs are what I'd consider "strongly typed." I can get jOOq to pretty easily yield queries that won't work if I switch out database dialects.

I can get Hibernate to pretty easily yield queries that won't work if I use a mildly unusual combination of JPA features. weeps

Re: What ORMs have taught me: just learn SQL

#222

Over and over I keep finding that just an ORM is not enough, but raw SQL is hideous in a different way. ORMs map nicely when you are indeed modifying objects, but somethings don't map well that way. So don't map them that way! What we need is a low level abstraction layer alongside the ORM. The main problem with raw SQL is that what you really want is a genuine programming language. You almost want programmatic acces…

> Kind of like a DOM API, but for SQL.

In Java land, the big persistence API is JPA. It's an API for ORMs, but it has features which go a long way towards this - just from the ORM side rather than the SQL side.

Firstly, it has its own query language, JPQL, which is basically a large subset of SQL (on the order of MySQL's SQL rather than PostgreSQL's, eg no window functions) with some added niceties, such as for joining (eg "select u from User u where u.manager.location.country.code = 'UK'").

Secondly, it has something called the 'criteria API' [1] which is basically a DOM for JPQL. So not SQL, but close. I worked on a large, byzantine financial application where we did a lot of building up of queries using this API. It worked pretty well as a way of separating the logic for different aspects of queries: we had one bit of code that set up the core of a query to limit results according to the user's permissions, another bit that would apply temporal filters, another bit that would apply subject filters, an optional bit that would take a query for trades and turn it into a query to find stocks involved in those trades, etc.

The API itself is not massively typesafe (the programmer specifies an attribute with a string name and type, so they can get both the name and the type wrong if they like), and it is rather verbose (of course). There is an extension called the 'metamodel', where you can automatically generate classes which have ready-made bits and bobs which describe your schema, and saves both typing and errors. Using that, a query to find all trades on a particular stock could look like:

        @PersistenceUnit EntityManager em; // someone will dependency inject this

        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery q = cb.createQuery(Trade.class);
        Root trade = q.from(Trade.class);
        q.select(trade).where(cb.equal(trade.get(Trade_.stockTicker), "NXJ"));
        TypedQuery = em.createQuery(q);
Which is is still verbose and stilted in the grand traditions of High Java, of course.

There are some third-party libraries for building JPQL strings which can be used for much the same purposes. Liquidform is quite good:

        Trade t = alias(Trade.class, "t");
        SubQuery q = select(t).from(Trade.class).as(t).where(eq(t.getStockTicker(), "NXJ"));
        TypedQuery = em.createQuery(q.toString(), Trade.class);
A key thing here is that the alias function takes a class and, via bytecode sorcery, produces an object which conforms to the type of that class, but which isn't really an instance of it, but instead has methods which act as query primitives. It works a lot like things like Mockito and so on.

The new hotness, though, is Jinq, which uses Java 8 lambdas to imitate the great .net LINQ. I haven't used it, but i believe the query would look something like:

        List = streams.streamAll(em, Trade.class).where(t -> t.getStockTicker().equals("NXJ")).toList();
Again, this uses bytecode sorcery under the hood, but this time, it does it by pulling apart the lambda to determine the condition it expresses, and then constructing a corresponding query string. A different knid of sorcery - in AD&D terms, divination rather than conjuration.

[1] http://docs.oracle.com/javaee/6/tutorial/doc/gjitv.html

Re: What ORMs have taught me: just learn SQL

#223
post #66

I think the problem is when ORM influences/encourages particular schema designs. When you no longer see tables as tables (which is storage) and rather see your database tables as instances of objects (how you would like to consume the data) ORM (rails/AR in particular) makes it very diffcult to work with joins and build an object that read from multiple tables. One workaround I think is to use database views. And see…

Java's JPA interface, of which Hibernate is an implementation, makes it pretty simple. Straight from the documentation of EclipseLink (another implementation):

  // query for a primitive
  Query query = em.createNativeQuery("SELECT SYSDATE FROM DUAL");
  Date result = (Date)query.getSingleResult();

  // query for a pair of primitives (bletcherous, but not disastrous)
  Query query = em.createNativeQuery("SELECT MAX(SALARY), MIN(SALARY) FROM EMPLOYEE");
  List results = query.getResultList();
  int max = results.get(0)[0];
  int min = results.get(0)[1];

  // query for a mapped object
  Query query = em.createNativeQuery("SELECT * FROM EMPLOYEE", Employee.class);
  List result = query.getResultList();

Re: What ORMs have taught me: just learn SQL

#224

Earlier quoted context omitted.

When I look at SQL through the lenses of hindsight I see a language that's not amenable to IDEs (it's harder to autocomplete columns if you must write those before the table name, as an example), and has questionable and verbose syntax. While straight relational algebra is actually quite readable, despite all the efforts of most the anti-ORM crowd, at the end of the day the business logic that works on business objec…

Sorry Toad or Work manager and the MYSQL tools are perfectly acceptable IDE's for SQL development.

"Perfectly acceptable" doesn't reach the standard of type inference and feedback that you can get with modern IDEs for their supported languages. Visual Studio gives far more feedback for LINQ than SQL, and it's damn more useful for debugging queries.

Re: What ORMs have taught me: just learn SQL

#225
post #2

I've caught a lot of flak for saying this, but I'm convinced that all ORMs are ultimately tech debt. Sure, they get you up and running quickly, but once you're there, you'll invariably find yourself wanting to do things that require you to work against and around your ORM to accomplish. By pretty much any definition I've ever encountered, that's "tech debt"

That's been my experience. From what I can tell the only time ORM tools actually make sense is when you have a lot of tables (or fewer tables with a lot of columns) and your interactions are very simple - i.e. you're mostly dealing with one table at a time.

But this is pretty much an edge case. For simple applications I can write all the SQL in less time than it takes to configure Hibernate, and applications that require me to join seven tables in every query, use analytic functions, or share schemas with other applications, ORM tools don't handle the complexity very well.

Re: What ORMs have taught me: just learn SQL

#226

Here's a question. WHY do we even bother with ORMs? Put another way, what problem are they trying to solve?

Make data layer more testable and refactorable by decoupling from a specific data storage. I would trade a horrendous large SP for a horrendous large C# codebase any day.

I'd choose "Door No. 3": neither C# nor stored procedures, but declarative db business rules using appropriate data types, constraints, default values, views, access controls, and triggers.

Re: What ORMs have taught me: just learn SQL

#227

Earlier quoted context omitted.

ORM is "Object Relational Mapper". If you are taking data out of a relational database and mapping it into objects, you are implementing an ORM. Seems like I'm sticking to the exact definition of an ORM, aren't I?

> If you are taking data out of a relational database and mapping it into objects, you are implementing an ORM. No, ORM is a particular approach to doing that; the query abstraction approach described upthread is closer to the DAO pattern, to which ORM is an alternative. People were using RDBMSs to provide a persistence layer for OO programs before ORM was a thing, but as you have broadened the term any use of an RDB…

Now you're the one using a nonstandard definition of ORM. Here is how Wikipedia defines it:

    Object-relational mapping (ORM, O/RM, and O/R mapping)
    in computer science is a programming technique for
    converting data between incompatible type systems
    in object-oriented programming languages.

Re: What ORMs have taught me: just learn SQL

#228

Earlier quoted context omitted.

> If you are taking data out of a relational database and mapping it into objects, you are implementing an ORM. No, ORM is a particular approach to doing that; the query abstraction approach described upthread is closer to the DAO pattern, to which ORM is an alternative. People were using RDBMSs to provide a persistence layer for OO programs before ORM was a thing, but as you have broadened the term any use of an RDB…

Now you're the one using a nonstandard definition of ORM. Here is how Wikipedia defines it: Object-relational mapping (ORM, O/RM, and O/R mapping) in computer science is a programming technique for converting data between incompatible type systems in object-oriented programming languages.

"is a technique for" does not mean "includes every technique for".

Spearfishing is a technique for catching fish, but not every technique for catching fish is spearfishing.

Re: What ORMs have taught me: just learn SQL

#229

Earlier quoted context omitted.

I believe that C# is a better data processing language than SQL is, assuming it can access the data. That's mostly thanks to the strength of linq.

Linq is just a useful syntax, there's nothing about data processing there. It's only good for small data sets that fit entirely in memory.

I completely disagree. SQL is just syntax too. Syntax for a language that can process data, just like linq. And Linq can also operate on IQueryable, which can represent data structures too large to fit in memory. It functions just fine on infinite data structures for that matter too.

Re: What ORMs have taught me: just learn SQL

#230

Earlier quoted context omitted.

Let's talk practicality rather than sitting in some ivory tower and muttering about best practices With AR, if this is the worst case where you write some raw SQL, what's the alternative? The alternative seems far more painful pragmatically speaking and this, while being a little ugly, seems to work just fine without impacting productivity or performance.

> With AR, if this is the worst case where you write some > raw SQL, what's the alternative? The alternative seems > far more painful pragmatically speaking and this, while > being a little ugly, seems to work just fine without > impacting productivity or performance. Do you know if other ORMs allow this kind of relatively painless use of "raw SQL?" I've only used ActiveRecord and some of the .NET "micro ORMs" like D…

Even the bulkiest ORMs allow you to use raw SQL. That's why you can use 80 - 90% of the features on the regular basis and hand-tweak regions which cause performance problems or places where you just have to write SQL (e.g. recursive queries).

In EF, there's either: Database.SqlQuery - http://msdn.microsoft.com/en-us/library/gg696545%28v=vs.113%...

which can return any object or: DbSet.SqlQuery - http://msdn.microsoft.com/en-us/library/gg696332%28v=vs.113%...

which returns tracked entities, so you can write raw SQL (e.g. call a stored procedure) and just use the 'mapper' part of the framework.

NHibernate has CreateSQLQuery - http://www.nhforge.org/doc/nh/en/#querysql-creating

I like micro-ORMs, but when you want to skip writing tedious INSERT or UPDATE queries, you have to add extensions to them (at least to Dapper); that, and SQL strings do not really lend well to refactoring and type safety...

Post reply on HN