JOOQ: an alternative approach to traditional ORMs
jooq.org
jooq.org
However, my first impression is very positive.
1. Database First
Great way to distinguish your product from traditional ORMs. I am personally not a fan of code first approach especially as a LOB applications developer. I have been writing code for 15 years and the database/schema has outlived each and every application that I wrote. (I am in full agreement with lukaseder's comment: https://news.ycombinator.com/item?id=10880942) As an experienced developer, they immediately made their value proposition clear to me and I wanted to learn more.
2. Examples
Side by side example comparing jOOQ to SQL. A great way for me to quickly see if I like their DSL design.
3. Convince your manager page.
I absolutely loved this: http://www.jooq.org/why-jOOQ.pdf
We all have worked with 'a technical manager' who isn't really technical. When you need to purchase a tool, you need to convince your manager and this page is as good as I have seen. All commercial software development tool product websites should feature a 'Convince your manager' page.
Agreed. The electrical metaphors and accompanying images are marketing gold!
I'm always astounded how many times this is neglected. It seems pandemic.
I live in the PHP world, and it seems that every hot new framework wants to do its own SQL re-hash. They always seem created by programmers who either hate SQL and want to avoid it as much as possible in favor of OO paradigms, or have never used it beyond its basic features.
Sooner or later, I'll find myself wanting to do something moderately complex, like delete through a join, and all of a sudden the interface breaks down. I'll scour the internet or ask a question, and it either can't be done, or relies on some arcane, poorly documented part of the API, or uses weird, unintuitive syntax that makes you wonder if the API is any improvement over plain SQL at all.
Agreed.
I don't use PostgreSQL and haven't got my head around it fully, but have you seen http://www.pomm-project.org/ ?
From my own experience, and this is general consensus, it's a big win to use it. Its SQL inspired fluent syntax brings you back the power of SQL, including really advanced stuff. But at the same time, in a Java native manner, type safe, by the way.
If you haven't tried it yet, you definitely should.
After dealing with some many hibernate WTF issues jOOQ was a breath of fresh air. I could finally write queries again using a simple DSL and get what I was expecting to happen.
Really? So the compiler will catch this error?
create.select().from(AUTHOR).where(BOOK.LANGUAGE.eq("DE"))
No it won't, because `where` has this type: SelectConditionStep<R> where(Field<Boolean> field)
If it was type safe, `Field<Boolean>` would have to mention the type of the records that can be queried.Type safety is what is being sold on the front page as the main value proposition.
Needless to say, I'm not sold on it.
BOOK.LANGUAGE.eq("DE")
mean something like Book.Language = 'DE'
in SQL? That's certainly a boolean expression. I've only started reading the manual, though, so it's possible I got the semantics wrong, but I've had SSMS point out to me this sort of thing before.The corresponding SQL is nonsensical:
select * from AUTHOR where BOOK.LANGUAGE = 'DE' create.select()
.from(BOOK)
.where(exists(
create.select().from(AUTHOR).where(BOOK.LANGUAGE.eq("DE"))
))
Other frameworks may have jumped to the conclusion that predicates should be strictly tied to whatever is placed in the FROM clause. This is possible only if you completely limit the scope of what your framework can do (namely CRUD).https://db.apache.org/torque/torque-4.0/index.html
Apache Torque is an object-relational mapper for java. In other words, Torque lets you access and manipulate data in a relational database using java objects. Unlike most other object-relational mappers, Torque does not use reflection to access user-provided classes, but it generates the necessary classes (including the Data Objects) from an XML schema describing the database layout. The XML file can either be written by hand or a starting point can be generated from an existing database. The XML schema can also be used to generate and execute a SQL script which creates all the tables in the database.
As Torque hides database-specific implementation details, Torque makes an application independent of a specific database if no exotic features of the database are used.
Usage of code generation eases the customization of the database layer, as you can override the generated methods and thus easily change their behavior. A modularized template structure allows inclusion of your own code generation templates during the code generation process.
Oh and jOOQ has an extremely powerful DSL that is close to 1-1 with SQL and whole bunch of reflection based conversion abilities (including dotted path data binding which I am happy to say I inspired the author of jOOQ to add :) )
https://db.apache.org/torque/torque-4.0/documentation/orm-re...
Regen, then fix compile issues. Thats how I used it a long long (10 years?) time ago.
jOOQ DSL is nice, this is just as readable(ABC is a generated class)
Criteria crit = new Criteria()
.where(ABC.A, 1, Criteria.LESS_THAN)
.and(ABC.B, 2, Criteria.GREATER_THAN)
.or(ABC.A, 5, Criteria.GREATER_THAN);
Working with XML, not the nicest thing, i do agree, but also not the worst thing.Not a flag-bearer in any way, but I just wanted to point out that if code-gen db access is your thing, there are also other tools to consider. It most certainly had its warts. Like issues handling complicated joins. Or more seriously, whos maintaining it. Its been a long time since i've look at it with any seriousness, not sure if they have fixed them or not.
edit: formatting
.select()
.from(
select(
TABLES.TABLE_SCHEMA,
TABLES.TABLE_NAME,
TABLES.TABLE_NAME.as("specific_name"),
inline(false).as("table_valued_function"),
inline(false).as("materialized_view"),
PG_DESCRIPTION.DESCRIPTION)
.from(TABLES)
.join(PG_NAMESPACE)
.on(TABLES.TABLE_SCHEMA.eq(PG_NAMESPACE.NSPNAME))
.join(PG_CLASS)
.on(PG_CLASS.RELNAME.eq(TABLES.TABLE_NAME))
.and(PG_CLASS.RELNAMESPACE.eq(oid(PG_NAMESPACE)))
.leftOuterJoin(PG_DESCRIPTION)
.on(PG_DESCRIPTION.OBJOID.eq(oid(PG_CLASS)))
.and(PG_DESCRIPTION.OBJSUBID.eq(0))
.where(TABLES.TABLE_SCHEMA.in(getInputSchemata()))
// To stay on the safe side, if the INFORMATION_SCHEMA ever
// includs materialised views, let's exclude them from here
.and(row(TABLES.TABLE_SCHEMA, TABLES.TABLE_NAME).notIn(
select(
PG_NAMESPACE.NSPNAME,
PG_CLASS.RELNAME)
.from(PG_CLASS)
.join(PG_NAMESPACE)
.on(PG_CLASS.RELNAMESPACE.eq(oid(PG_NAMESPACE)))
.where(PG_CLASS.RELKIND.eq(inline("m")))
))
// [#3254] Materialised views are reported only in PG_CLASS, not
// in INFORMATION_SCHEMA.TABLES
.unionAll(
select(
PG_NAMESPACE.NSPNAME,
PG_CLASS.RELNAME,
PG_CLASS.RELNAME,
inline(false).as("table_valued_function"),
inline(true).as("materialized_view"),
PG_DESCRIPTION.DESCRIPTION)
.from(PG_CLASS)
.join(PG_NAMESPACE)
.on(PG_CLASS.RELNAMESPACE.eq(oid(PG_NAMESPACE)))
.leftOuterJoin(PG_DESCRIPTION)
.on(PG_DESCRIPTION.OBJOID.eq(oid(PG_CLASS)))
.and(PG_DESCRIPTION.OBJSUBID.eq(0))
.where(PG_NAMESPACE.NSPNAME.in(getInputSchemata()))
.and(PG_CLASS.RELKIND.eq(inline("m"))))
// [#3375] [#3376] Include table-valued functions in the set of tables
.unionAll(
tableValuedFunctions()
? select(
ROUTINES.ROUTINE_SCHEMA,
ROUTINES.ROUTINE_NAME,
ROUTINES.SPECIFIC_NAME,
inline(true).as("table_valued_function"),
inline(false).as("materialized_view"),
inline(""))
.from(ROUTINES)
.join(PG_NAMESPACE).on(ROUTINES.SPECIFIC_SCHEMA.eq(PG_NAMESPACE.NSPNAME))
.join(PG_PROC).on(PG_PROC.PRONAMESPACE.eq(oid(PG_NAMESPACE)))
.and(PG_PROC.PRONAME.concat("_").concat(oid(PG_PROC)).eq(ROUTINES.SPECIFIC_NAME))
.where(ROUTINES.ROUTINE_SCHEMA.in(getInputSchemata()))
.and(PG_PROC.PRORETSET)
: empty)
.asTable("tables"))
.orderBy(1, 2)
.fetch()) {It is free if your database is free (using Apache 2.0): http://www.jooq.org/download/
Also supporting Oracle is a huge PITA. So charging for that is completely understandable :)
I don't recommend its use, both from a "can I trust this" perspective as well as a "do I want to support these guys" one. If you can use Slick, I recommend it, as its developers have in my experience been uniformly solid people; I haven't needed either recently, as I've switched stacks for the project I was going to use one or the other for, but Slick's developers don't make me feel icky to support.
Not sure there's all that much to explain…?
I tried to use JOOQ with Scala in past, it does not feel good solution for it, all that generated code seemed really difficult to keep in order.
JOOQ is clearly Java tool first and foremost.
- Minimal wrapper over JDBC.
- Querying done in SQL. You should not be afraid of SQL. If you are, you should not be doing anything above the trivial CRUD.
- Centralizing object marshaling and unmarshaling - each object should know how to sync itself and its descendents
- Single syntax for inserting and updating
- Ruby-like objectivized JDBC fetching with exception handling
- User-definable deep fetching and updating (almost Hibernate-like).
- Batch API to avoid round-trips when submitting multiple queries.
- Stats collection and similar stuff.
I wrote this for Two Sigma's internal use back in 2010.
The problem with writing your own library is the massive and continuous support that is required. This is where I eventually gave in and used jOOQ. Its an extremely well maintained and documented library. People complain about it not being completely opensource but neither is intellij so I don't have a problem (as well I also only use opensource databases).
* Your library has a very limited set of capabilities
* Your library is not extensively tested on all supported databases all the time and thus bugs are not being found.
Also I find the claim of streaming support sort of disingenuous or at least the word streaming misleading. Real streaming that is required for reactive like programming (ie reactive-streams) is not supported by the JDBC drivers because operations are bound to thread/connection. If you are just talking about Iterator like streaming than you do realize you are at the mercy of the database driver itself. For example Postgres and many other databases will almost always preload a certain amount of the ResultSet regardless (and in my experience a rather large ammount).
So if I block too long while reading an iterator like object because the client is taking to long to read... I think you can imagine what happens. This is why so many of the JDBC wrappers (such as Spring JDBC and JDBI ) do not return iterators or at least do not advertise it as an awesome feature.
Hibernate has iterator methods, but I recall (in 2010) it still loaded the entire result set into memory, with a //TODO comment. I remember thinking "W...T...F..." I can't tell you how many -Xmx16G (or 32/64) flags I deleted...
I find its documentation coverage to be good but its quality to be lacking, being mostly written by non-native English speakers. Which is fine, if there are good examples and tests to use--but jOOQ actually deleted unit tests from the open-source release of the library (declaring them "an enterprise feature") to remove the easiest and best way to investigate the library, as well as the only unambiguous documentation the project has. ("Ask a question on our Google Group" was suggested with a straight face. Because in 2015, it's better to wait on an email than look at code, I guess.)
even the stuff that has been xxxed out: https://github.com/jOOQ/jOOQ/blob/63717b517fccf475089648ca3c... and it's still under the APL.
The advantage is that, as you're writing and embedding actual SQL, you can take advantage of any database-vendor-specific extensions to SQL, plus you can easily run, debug and test such SQL from another tool. Having full control over the actual SQL also means you don't risk potentially horribly inefficiently generated SQL, or risk running into unexpected N+1 scenarios. At the same time, you don't have to deal with JDBC and instead get a DAO layer that uses strongly typed objects.
Of course the disadvantage is that you lose portability; moving for instance a MyBatis/SqlServer app to Oracle will mean verifying, testing and potentially rewriting every single SQL statement in your app. If you're willing to accept that risk though, MyBatis is a great lightweight library to manage your database access layer.
While Spring also often defaults to JPA, it isn't required at all to use JPA with Spring. Spring has always included JdbcTemplate, a JDBC extension for convenience when working with plain SQL.
Apart from that, jOOQ is part of the Spring Boot manual. It cannot be feel that unnatural :)
https://docs.spring.io/spring-boot/docs/current/reference/ht...
This approach has always seemed completely backwards to me. Isn't the database simply a mechanism for persisting records/objects whose structure is determined by domain modeling?
To me, it would make about as much to say of a GUI framework "foobarWidgets is GUI-centric. Your UI comes 'first'.", as though the application itself was just an afterthought.
Don't get me wrong - I like SQL, and I happily use relational databases to store objects. I just see the database as a means, not an end.
Of course, projects are different, and some projects are more user-centric, others are more data-centric, but chances are that you're successful and then you'll regret working with a horrible database schema that you didn't properly design 5 years ago, cause all you cared for were your fancy foobarWidgets that you implemented in a tech that no longer exists...
This cannot be repeated enough. It is an argument I have over and over with people who want to treat the database as a dumb store usually because they do not want to understand databases. Any successful software that stores or generates data will see that data live and used way beyond the original program. The database used will absolutely be the foundation multiple different pieces of software are built on.
DB is what I always start with and it shall stay so.
BTW, is there some jOOQ equivalent for .NET? EntityFramework 7 is all about code-first, which I really don't like.
If you treat a database as "a mechanism for persisting records/objects whose structure is determined by domain modeling" your database will be messy and redundant.
not at all. you're thinking of a file system.
At least in the realm of business applications (i.e. not scientific apps, or games, etc.) the most common approach is to keep data (including data dictionaries and db structures) at the center.