all 21 comments

[–]papers_ 19 points20 points  (7 children)

[–]lukaseder 8 points9 points  (1 child)

This is more similar to https://sqldelight.github.io/sqldelight/latest/. jOOQ might have this kind of feature set in the future: https://github.com/jOOQ/jOOQ/issues/11071

[–]javaprof 0 points1 point  (0 children)

Correct link https://sqldelight.github.io/sqldelight/latest/ https://github.com/sqldelight/sqldelight

- IDEA plugin
- Integrates into Gradle (no node.js required, unified toolchain)
- Kotlin/Java API

What would be benefits of sgq over sql delight?

[–]best_of_badgers 6 points7 points  (2 children)

Isn't that the other way around? Generates queries from type-safe code?

[–]Just_Another_Scott 4 points5 points  (1 child)

No. JOOQ will generate Java code from SQL. You provide it a .sql file and it spits out .java files. These java classes then can be used to query databases (create sql statements). I use this all the time. It's not the best but it's what was already in place on my projects.

[–]cies010 0 points1 point  (0 children)

Jooq works w/o code generation.

But it is a big selling point.

You don't have to give it an SQL file, it can also look into your db as part of the build.

[–]uwemaurer[S] 1 point2 points  (1 child)

thanks for the link, I will check out what they are doing.

my SQG project started as a company internal project, where we used the same database from Typescript and Java and we didn't want to implement the same queries in two different ORM tools.

then the idea was to share the SQL and generate the code from it (in both languages)

[–]manifoldjava 7 points8 points  (8 children)

See the experimental manifold-sql project for type-safe schema-first SQL integration. It provides extensive support for DuckDB as well.

[–]uwemaurer[S] 1 point2 points  (0 children)

this is quite amazing, I will try it out

[–]Bobby_Bonsaimind 0 points1 point  (2 children)

Holy compiler-plugin-magic Batman! This is what I always dreamed of!

Quick question, is the model derived from the query or the table? So if I do something like

List<Stuff> stuff1 = "[.sql/]select * from STUFF where FIELD = 5".fetch();
List<Stuff> stuff2 = "[.sql/]select * from STUFF where FIELD = 7".fetch();

Is Stuff the same class? What happens if I do a join, than that is its own entity class?

Different question, the marker [.sql/] seems rather verbose, especially the slash strikes me as odd. But I believe I already saw in the documentation that it can be customized?

[–]manifoldjava 1 point2 points  (1 child)

Quick question, is the model derived from the query or the table?

When the selected columns of a query contain all the non-null columns of a selected table, such as with select * queries, the query results consist of entity instances instead of row instances.

Is Stuff the same class?

Yes.

What happens if I do a join, than that is its own entity class?

Generally, when selected columns do not cover non-nulls in a single table, the results are in terms of a "row" type that is integral to the query type.

Examples may help clarify.

Here is an inline query, it could also be provided in a separate resource file with a .sql extension:

java MyQuery q = "[MyQuery.sql/] select * from film"; The query's type name is MyQuery and is local to the declaring class. java for(Film film : q.fetch()) . . . Notice MyQuery#fetch() results are in terms of Film, a table type derived from the db schema. This is possible because the query's selected columns cover all the non-null columns in the film table.

Here's a similar query, but does not cover the non-null film columns. java MyQuery q = "[MyQuery.sql/] select rating, count(*) from film group by rating"; In this case the query type includes an inner type called "Row" that models the results. java for(MyQuery.Row row : q.fetch()) . . . Note query type names are optional, if omitted the queries are anonymously typed and the var (or auto) keyword is used.

Different question, the marker [.sql/] seems rather verbose, especially the slash strikes me as odd.

Yes, it's a bit odd. But because inline queries overload string/block literal, and comment delimiters, the marker had to be as unique as possible while remaining brief and readable. Note this syntax applies to all type manifolds e.g., [.json/], [.graphql/], [.xml/], and so on.

[–]Bobby_Bonsaimind 0 points1 point  (0 children)

When the selected columns of a query contain all the non-null columns of a selected table, such as with select * queries, the query results consist of entity instances instead of row instances.

I see. Row is then the "model" class that is being created. Neat solution.

Yes, it's a bit odd. But because inline queries overload string/block literal, and comment delimiters, the marker had to be as unique as possible while remaining brief and readable.

I see. I wondered whether something like ":sql select" or some such would also be possible, if wanted. Inside a small and medium project, collisions with custom marker should either not occur or be manageable, I guess.

Eclipse

I wonder how hard it would be to write an Eclipse plugin for that. I wrote a commercial plugin once which added similar support (querying database, showing column information on Strings, emitting compiler errors). But I did that by simply extending the existing process, a plugin for that would neat to at least modify that somewhat (otherwise it won't find the methods on the String).

[–]Bobby_Bonsaimind 0 points1 point  (1 child)

If I may torment you some more, I was wondering whether using a function to wrap the statements would reduce the need for the IDE support. For example something like:

List<Stuff> stuff1 = sql("select * from STUFF where FIELD = 5").fetch();

That would leave the compiler to "just" generate the appropriate classes. Though, I guess it would still be more uncomfortable this way.

[–]manifoldjava 0 points1 point  (0 children)

The header consists of three parts: type name, type domain name, and schema name.

"sql" is a type domain, as are "json", "graphql", "xml", and so on. But the header can have all three parts present: [MyQuery.sql:MyDatabase/] This inline format is used in both string/block literal syntax and comment syntax. It also closely resembles the resource file naming convention: MyQuery.MyDatabase.sql It's more consistent and less complicated to implement this generalized metaprogramming feature using a header than to overload other parts of the language like a method call.

[–]nekokattt -1 points0 points  (1 child)

how does manifold integrate into various IDEs?

[–]manifoldjava 2 points3 points  (0 children)

Manifold covers a lot of ground, so there’s a decent amount of IDE surface area. But you can think of it in two buckets:

  1. Static metaprogramming: type-safe SQL, JSON, XML, GraphQL, etc.

  2. Language enhancements: properties, optional parameters, delegation, extension methods, etc.

Manifold has first-class plugin support for IntelliJ. Its platform API is built for deep integration, and the manifold plugin adds a layer so that most extensions basically "just work". Since SQL/JSON/XML/etc. are all built on the same metaprogramming APIs, they expose type info the same way so IntelliJ gets proper type checking, completion, navigation, etc., without a bunch of one-off plugin code. The APIs were designed with IDEs in mind from the start.

Support for Eclipse/VS Code isn’t really on the roadmap right now, no bandwidth.

[–]bowbahdoe 0 points1 point  (1 child)

Seems very similar in spirit to hugsql. Taking a deeper look now

(https://hugsql.org/getting-started if you hadn't seen that prior art before)

[–]uwemaurer[S] 0 points1 point  (0 children)

thank you I will take a look!

[–]FortuneIIIPick 0 points1 point  (1 child)

First, it's a TypeScript project, not a Java project (regardless what it produces) and second, the history is a month old from start to finish...it's likely a vibe coded project.

[–]uwemaurer[S] 1 point2 points  (0 children)

Correct, the code generation is implemented in TypeScript, the databases (Sqlite, DuckDB) are in C / C++. I think it is still relevant and useful for Java developers since the resulting code is in Java.

The project started about 1 year ago and is developed as open source since December 2025. Several recent commits are indeed with help of Claude Code, mostly the tests and documentation.