Show HN: A new way of ORM in Java
github.com
github.com
https://github.com/dieselpoint/norm
Blog post on it:
https://blog.dieselpoint.com/a-minimalist-good-enough-approa...
I think ORMs just bridged that gap of “how my objects look in code” and “how to store them” quite well.
But lately we’ve been removing a lot of the reasons to use ORMs altogether. By using more functional languages and features, we’ve been getting rid of that “O”. By adding more relationship features to databases like jsonb in postgres, and even nosql dbs themselves, we’ve been removing the “R” bit as well.
There’s simply a log less for ORMs left to do in modern codebases.
I’ve personally been using only raw sql with some help for building complex queries - in the JSX way of embedding stuff into the query as opposed to query builders that build it altogether, and couldn’t be happier.
With migrations handled by a separate library, all the db connection lib has to do is handle pools and run queries.
I’ve been looking into software that would statically type the sql queries themselves, which could end up as a way better dev experience.
ORMs helped there too by giving you some piece of mind that what you wrote was correct with regards to the DB schema, but that was only true if no other tool touched your db. But that is very hard for sufficiently large systems, especially in the era of microservices.
With typed sql queries it’s no longer necessary, and is even safer.
The real problems are:
1. Boilerpalte we get in the code which extract query results. This issue is not universal. Psycopg in Python produces a lot less boilerplate than, for instance, JDBC. This problem is a lot more visible in statically typed languages.
2. Boilerplate we get creating queries. Conditionally turning part of queries on and off produces a lot of code. This is mostly due to SQL syntax being more friendly to human rather than machine. Infix logical operators in conditions, commas etc., makes it hard to write code which generates SQL.
3. SQL is not good at reusing query parts. Views don't solve all problems.
The first problem is better to solve with code generation at compile time making it possible to perform all kinds of type check etc. Generating SQL is not that easy without introducing some kind of intermediate language which is more code friendly. I think some kind of templating could solve the problem so that it would look essntialy like SQL but with ability switch part of the query on and off. The hardest part is reuse which I don't know how to address. For instance, adding a join to some ACL table with conditions to exclude entities the client is not authorized to access.
This specific thing is better done with row-level security (in postgres at least). This requires that each user have a role in the db and that all queries be done in transactions that switch to the relevant role; but this seems to be a best practice anyway.
It’s the same as saying that wheel solves the wrong problem, because there are other way to move things. By definition, ORM is the mapping between ER and OO models. Working with ER models directly in object-oriented languages is suboptimal, because ER design addresses the problem of storage and OOD is more efficient in solving the problem of data processing. Hence the need in ORMs, which are mainstream now exactly because they enable the efficient transition from the storage to the processing data model and vice versa.
The problems which exist in modern ORMs are usually related to the complexity of their abstraction and imperfection of the programming language: the first problem is easy to notice in JPA with fetching strategies and caching, the second one is what author of this new ORM is trying to eliminate with the code generation - the need to use the ER namespace both in mapping declarations and in queries. JPA does not solve it well, quite often requiring developers to use native queries, because Criteria API is too abstract for many use cases and too heavy for modeling queries in the code. On the other side, JOOQ is not offering a convenient metamodel and operating on too low level.
To be honest the incentive for me to post this was mostly accumulated irritatation by the fact that a mere side effect of the mapping turned to countless attempts to model SQL in the host language. Really, IMO, all of them are worse than SQL, all of them waste your time spent learning them, with all of them sooner or later you find yourself in a position when you figure how to express the SQL you want with the DSL at hand (again, it is hard to qualify this other than wasting time).
As for points 1 and 2: in languages with rather powerful type-systems, this is not solved by code generation but with mere language features. Examples are Scala or D or also more academic languages like Idris or F*. No intermediate language needed.
Unfortunately many mainstream languages such as Java or C# have to rely on code generation to solve the problem.
Just use JOOQ or any other kind of library that removes the boilerplate and is 100x more maintainable and productive.
Just use an ORM or any other kind of library that removes the boilerplate and is 100x more maintainable and productive.
See how useful it is to not explain anything.
However, your comparison does not make sense. JOOQ and similar libraries don't make you write SQL, even if their API is closer to SQL than what most ORMs do.
If you replace "ORM" in your example with NoSQL or some other technology that directly competes with SQL then it would make sense.
Far easier to write correct sql than to convince the orm how to form correct sql.
For smaller systems it might be ok I guess, but stay alive long enough and you 'll see that sooner or later every small system desires to grow, grow and grow!
good ORMs all have a way to fine-control the underlying statements to avoid querying individual rows one by one.
I'm fairly proficient with sql queries and i still don't like writing sql queries and binding to objects manually
No, that makes no sense.
If you have to investigate 2 places where performance issues have cropped up and hand-write the SQL in those places, while 95% of your application works just fine, how did you lose all the benefits?
My SQL competency was fine from the beginning, thank you very much.
Just my experience so far.
2. ORMs work just fine in lots of applications. There’s no real need to replace them.
3. JOOQ does not remove enough boilerplate and it’s not 100x more maintainable.
You can use something like https://sqitch.org/ or https://flywaydb.org/ or https://www.liquibase.org/ or https://github.com/rust-db/refinery where ideally your migrations should be written in plain sql (or a language that can output plain sql so you can see what it does). Then it's a matter of running those sql queries and knowing which migrations already happened.
> So ideally, the app reflects the DB schema in some type-safe manner.
That's right! But there is no need to use an ORM for that. JooQ and other libraries (in my world it would be Quill https://getquill.io/) do that for you.
Two examples in .NET world: Roundhouse [0] and FluentMigrator [1].
[0] - https://github.com/chucknorris/roundhouse [1] - https://github.com/fluentmigrator/fluentmigrator
Just embed the SQL statement in a string.
In same way you would embed a snippet of HTML, XML or anything else.
(You can remove some of the boiler plate of a classic Java prepared statement using a library though ... nothing wrong in that.)
Instead of something like this:
Query query = create.select(field("BOOK.TITLE"), field("AUTHOR.FIRST_NAME"), field("AUTHOR.LAST_NAME"))
.from(table("BOOK"))
.join(table("AUTHOR"))
.on(field("BOOK.AUTHOR_ID").eq(field("AUTHOR.ID")))
.where(field("BOOK.PUBLISHED_IN").eq(1948));
Plus you may sit with a SQL dialect not supported by the "Fluent API".This makes sense for simple software, but jOOQ generates DB specific SQL queries depending on your DB dialect. E.g. we have a big product which supports nearly all big databases and jOOQ abstracts away the DB specific stuff wonderfully.
I don’t really have a horse in this race (in Clojure there’s no need to map between results and idiomatic data structures in your domain, and you can add as much or as little schema/type checking as you want) but in previous lives doing OOP I have never lamented them 5% of problematic SQL queries to the point I’d throw away the rest that an ORM gives me. Just break out into a more powerful query language, hide it behind an interface and you still get to return meaningful objects in your domain.
If your app does not have a requirement to be DBMS agnostic then DAO (Data Access Objects) frameworks like Spring JDBC or MyBATIS might meet your needs. If you don’t like using an Internal DSL and/or embedding SQL in your code, then a YeSQL framework like HugSQL might work for you.
For a strictly typed object orient language like Java, no single data access framework style has emerged as ideal. All have to address the same issues: integrating DDL/DML into source control, supporting different DBMSes, generating DAOs, and DDL migrations.
It just adds an extra layer of indirection, and another layer of processing that can go wrong. And when things go wrong, you have to dig down to the SQL level to understand it. But the table names are all mangled, which might further add to the complexity of debugging it.
One way that it is better is that it might be a better abstraction. If so, it would be possible to implement just these methods to target an in-memory or on-disk store without the SQL — and get a dramatic performance improvement.
But because data management requires a lot of disciplines and it is easier to test “is this is a better abstraction?” if by creating these kinds of abstractions on abstractions.
Some of them will evolve into “noSQL” but most fail because SQL has a first-movers advantage and they aren’t enough better to warrant further investment. Better may be possible but too few people can afford the luxury of exploring language design — and those that can are scared off easily; People who make bad investments worry about their fellow human and try to prevent them from making the same mistake; and so you worry about what happens when it goes wrong, which is why I say, “what if it doesn’t?”
Features Dynamic code generation with JSR 269 for Java API of database access Full Java API of database access without coding Dynamically SQL programming with Java syntax, and very close to SQL syntax
For example: queryByPrimaryKey query(predicate, params) ...