sql2o turns SQL result sets into Java objects without writing the SQL for you
sql2o is a small library, which makes it easy to convert the result of your sql-statements into objects. No resultset hacking required. Kind of like an orm, but without the sql-generation capabilities. Supports named parameters.
At a glance
- What is it?
- sql2o is a small Java library that maps query results into POJOs and binds named parameters, and it deliberately stops short of generating SQL. It suits teams that want plain JDBC-style queries with a lighter object mapping layer, and it costs little to learn because the surface area is small.
- Who is it for?
- Adopt sql2o if your team already writes SQL by hand, wants named parameter binding, and dislikes the ceremony of a full ORM. Do not adopt it if you need generated statements, object relationship mapping, or a migration tool, since sql2o deliberately provides none of those.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 57 days ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 15, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Sql2o sits between raw JDBC and a full ORM
The problem sql2o addresses is narrow and ordinary: you have a SQL statement, you run it, and you want plain Java objects back instead of a ResultSet you have to walk column by column. The project's own description calls it a small library that converts the result of SQL statements into objects, with no result set hacking required, and describes it as kind of like an ORM but without the SQL generation capabilities. That boundary is the whole design. You write the statement, sql2o executes it and does the mapping, and nothing in the library invents SQL on your behalf.
That split matters for a specific audience. Teams with an existing schema, hand written queries, and a preference for seeing exactly what reaches the database get a mapping layer without taking on an object graph model, a query DSL, or a schema migration tool. The library is published to Maven Central under the coordinates org.sql2o with the artifact id sql2o, and the repository is laid out with a core module, an extensions module, and a Maven pom at the top level. The licence is MIT, and the project is not archived. The last push was on 2026-08-27, the same day release 1.9.2 was cut, so the 1.9 line is where current work is landing.
Named parameters replace positional question marks
The query API in sql2o is built around named parameters, which is the single most visible difference from a hand rolled JDBC helper. Instead of counting question marks, you write the placeholder name in the SQL and attach a value to it with addParameter. In the quick start example, the insert statement is written as INSERT INTO users (id, name) VALUES (:id, :name), and the two values are attached separately before the call to executeUpdate. The select statement does the same thing with a single placeholder, WHERE id = :id.
The Query object is therefore a small builder that accumulates parameters and then runs. Because the same query text is used for DDL, DML and selects, the createQuery method covers all three: the table creation, the row insert, and the row fetch all go through it and differ only in which terminal call is used. The result of a select is converted directly into the target type, so executeAndFetchFirst(User.class) hands back a User instance rather than a ResultSet. The parameter names also appear in the SQL text, so a statement read in a log or a slow query report still tells you what each placeholder meant, which positional markers do not.
Running the first query against an in-memory H2 database
The README does not list a command line install, so the artifact comes from Maven Central, and the repository itself is built with Maven through the root pom.xml. The example below is the one the README gives, and it runs against H2 in memory so nothing needs installing on the machine. You construct Sql2o with a JDBC URL, a username and a password, then open a Connection from it. Both the Sql2o instance and the Connection are declared in a try with resources block, so they are closed when the block exits.
String url = "jdbc:h2:mem:test"; // Example using H2 in-memory database
try (Sql2o sql2o = new Sql2o(url, "username", "password");
Connection con = sql2o.open()) {
con.createQuery("CREATE TABLE users (id INTEGER PRIMARY KEY, name VARCHAR(50))").executeUpdate();
con.createQuery("INSERT INTO users (id, name) VALUES (:id, :name)")
.addParameter("id", 1)
.addParameter("name", "Alice")
.executeUpdate();
User user = con.createQuery("SELECT * FROM users WHERE id = :id")
.addParameter("id", 1)
.executeAndFetchFirst(User.class);
System.out.println("User: " + user.name);
}What you should see is a single line of output reading User: Alice. The target class is a plain class with public fields named id and name, and the column names in the CREATE TABLE statement line up with those field names. The library matches on names, so the mapping convention is visible in the example even though the README does not spell out the rules for it. Everything else in the example, including the exact URL and the table definition, is copied from the quick start section.
Java version floors split the release line into three tracks
The release line is not a simple upgrade path, because the library ties a minimum Java version to each generation of releases. Sql2o 1.9.0 and newer are only compatible with Java 17 or later, and the stated reason is the addition of support for records. If your project is on Java 11, sql2o 1.8.x is the compatible line. If you are still on Java 8, use sql2o 1.6.x.
That is a three way decision you have to make before anything else, and it is the kind of detail that is easy to miss because the announcement list in the README is short and the most recent item dates to 2024-03-12, when 1.7.0 was released. The release history itself is steadier: 1.9.0 came out on 2025-01-01, 1.9.1 on 2025-01-17, and 1.9.2 on 2026-08-27. The 1.9 line therefore exists for Java 17 projects, and the older lines are there for teams that cannot move off their runtime yet. Records support is a good reason to take 1.9.2 if your build allows it, since the target type can be an immutable record rather than a mutable bean with public fields.
The core module, the extensions module, and the database matrix
The repository layout tells you more about scope than the README does. The top level holds a core directory, an extensions directory, a pom.xml, a docker-compose.yml, and a licence file. The core module holds the library itself. The extensions directory exists as a separate place for optional pieces, which fits a project that keeps database specific behaviour out of the main path. The README does not enumerate what lives inside extensions, so the specific extensions available should be read from that directory or the wiki rather than assumed.
The compose file is the clearest statement of the intended test matrix. It starts two services, a PostgreSQL instance and an Oracle XE 21c instance, and puts them on a bridge network named sql2o-network. Postgres is exposed on host port 15432 against container port 5432, with the credentials testuser and testpassword, so a test run against it uses a non default port. Oracle exposes 1521 and 5500 and mounts a named volume at /opt/oracle/oradata, with ORACLE_PWD set to testpassword and ORACLE_CHARACTERSET set to AL32UTF8. Reading the compose file tells you which databases the project expects to be exercised by its own tests, which is a reasonable proxy for the set of drivers the maintainers keep working against.
The gap that sql2o leaves open, and why that is a feature
The clearest limitation is also the project's defining trade off. There is no SQL generation, so no query builder, no criteria API, and no schema definition that turns into DDL. There is no documented object relationship support, no lazy loading story, and no migration engine. The mapping is a direct conversion of one row into one object, which works well for flat records and for the common case of a table joined to a small number of columns, and which stops being convenient the moment you need a parent object, a child collection, or a polymorphic field.
Other parts of the surrounding practice are simply not covered by the documentation. The README does not document a transaction API, connection pooling configuration, retry behaviour on failure, or a rollback procedure, and it does not describe how the library manages a connection pool when you call open on a shared Sql2o instance. Nor does it document exception mapping, so the error type you catch from a failed query is not something the quick start tells you. The wiki is pointed to for additional information, and the contribution guide lives at a coding guidelines page, but the specifics of those areas are not in the README itself. Teams that need those behaviours should treat them as open questions to check before standardising on the library.
Editorial conclusion
Adopt sql2o if your team already writes SQL by hand, wants named parameter binding, and dislikes the ceremony of a full ORM. Do not adopt it if you need generated statements, object relationship mapping, or a migration tool, since sql2o deliberately provides none of those. Before committing, check the Java floor for your build, because 1.9.0 and newer require Java 17 while older Java 11 and Java 8 projects must stay on the 1.8.x and 1.6.x lines, and verify that your column and field naming lines up with the mapping the quick start example demonstrates.
Frequently asked questions
What Java version does sql2o 1.9.2 require?
Sql2o 1.9.0 and newer require Java 17 or later, because support for records was added in that line. Projects on Java 11 should stay on the 1.8.x line, and projects on Java 8 should use 1.6.x.
Does sql2o generate SQL or manage my schema?
No. The library maps the results of SQL statements you write yourself into objects, and the project describes itself as kind of like an ORM but without the SQL generation capabilities. It ships no query builder and no migration tool.
How do I fetch a single row into a Java object?
Call executeAndFetchFirst(User.class) on a query that has its named parameters attached with addParameter, as in the README example where a select on the users table returns a User instance directly. The class you pass in is a plain Java type whose fields are matched to the columns in the result.
Which databases does the sql2o project test against?
The docker-compose.yml in the repository starts a PostgreSQL service on host port 15432 and an Oracle XE 21c service on ports 1521 and 5500, both on the sql2o-network bridge network. The compose file is the project's own test setup rather than a supported database list.
What licence does sql2o use?
The project is published under the MIT licence, with the licence text kept in a licence.txt file at the repository root. The project is not archived, and the last push was on 2026-08-27.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/aaberg-sql2o)Was this useful?