Я читал много версий этого холивара, и все-таки решил написать свой. Буду рад комментариям, буду их учитывать и редактировать статью далее... так как ответить на такой сложный вопрос не так-то просто.
Что мы сравниваем? ORM. Object relational mapping - технология, которая позволяет абстрагироваться от базы данных, оперируя объектами в коде.
Могут ли jooq и Hibernate служить абстракцией над базой данных? Определенно да. Как это проверить? Оба фреймворка генерируют sql и разработчику необязательно писать sql запросы в коде.
Sql injection defense. тоже есть у обоих, избитая тема, не буду раскрывать. (Возможность избежать конкатенации в нативных sql запросах имеется и там и там.)
Connection pool. Присутствует в обоих фреймворках.
Static typification/Compile-time check. Тоже есть у обоих. У jooq она лучше потому что интерфейс более гибкий и покрывает намного больше кейсов чем hibernate.
One-to-many, Many-to-one implementation. Есть и там и там с небольшой оговоркой: проблема "n+1". Можно ли ее как-то избежать? в Hibernate придется писать разные реализации для запросов на чтение. В Jooq это тоже нужно делать, но другим более легким путем. В jooq есть multiset
fun fetchPersonbyId( personId: UUID ): PersonApiEntity { return dslContext.select( person.ID, person.NAME, DSL.multiset( dslContext.select( country.asterisk() ) .from(COUNTRY) .where(COUNTRY.PERSON_ID.eq(person.ID).and(COUNTRY.FORBIDDEN.eq(true))) ).`as`("prohibitedCountries") .convertFrom { prohibitedCountries -> prohibitedCountries.map { it.toCountryApiEntity() } }, DSL.multiset( dslContext.select(country.asterisk()) .from(COUNTRY) .where(COUNTRY.PERSON_ID.eq(person.ID).eq(person.ID)).and(COUNTRY.OCEAN.eq(true))) ).`as`("oceanCountries") .convertFrom { oceanCountries -> oceanCountries.map { it.toCountryApiEntity()}}) .from(person) .where(person.ID.eq(personId)) .fetchOne { it.personApiEntity() } ?: throw NotFoundException("Not found person with id: $personId") }
С помощью этой фичи можно писать разные запросы при этом модели данных остаются без изменений.