Возможно, вы захотите заглянуть в jOOQ, который даст вам столько гибкости, сколько вы пожелаете, сохраняя, так сказать, ваши запросы "в коде".
Он может генерировать классы Java из схемы базы данных,который вы используете для формирования запросов, используя его DSL.Вот пример: https://github.com/benjamin-bader/droptools/blob/master/droptools-example/src/main/java/com/bendb/example/resources/PostsResource.java#L99
Вот суть кода, который я связал выше:
final Record4<Integer, String, OffsetDateTime, String[]> record = create
.select(BLOG_POST.ID, BLOG_POST.BODY, BLOG_POST.CREATED_AT, arrayAgg(POST_TAG.TAG_NAME))
.from(BLOG_POST)
.leftOuterJoin(POST_TAG)
.on(BLOG_POST.ID.equal(POST_TAG.POST_ID))
.where(BLOG_POST.ID.equal(id.get()))
.groupBy(BLOG_POST.ID, BLOG_POST.BODY, BLOG_POST.CREATED_AT)
.fetchOne();
Это выглядит немного странно, но вот что он генерирует:
SELECT blog_post.id, blog_post.body, blog_post.created_at, array_agg(post_tag.tags)
FROM blog_post
LEFT JOIN post_tag ON blog_post.id = post_tag.post_id
WHERE blog_post.id = ?
GROUP BY blog_post.id, blog_post.body, blog_post.created_at
Вы можете видеть, что код Java близко отражает сгенерированный SQL, но благодаря сгенерированному коду jOOQ он все еще совершенно безопасен для типов.Поскольку это код, вы можете динамически создавать запросы по своему усмотрению.
Это сложная зависимость, но она может многое сделать для вас, если ваши потребности в SQL являются специализированными или динамическими.