★ 12,252PythonMITupdated Oct 2026
best for Python codebases that want raw SQL control and a full ORM without picking just one
Two layers, one library: Core gives you a SQL expression language close to what you'd write by hand, and the ORM sits on top with an identity map that stops you from loading the same row twice in a session. Nothing else here offers that split — GORM and Hibernate commit you to the ORM layer outright, and Drizzle or Kysely give up the ORM half entirely.
The async support is real, built on asyncio and greenlet rather than bolted on after the fact, and dialect coverage across Postgres, MySQL, SQLite, MSSQL, and Oracle goes deep enough that you get dialect-specific types instead of a lowest-common-denominator API.
The 1.x-to-2.0 migration was slow and painful enough that legacy_query shims are still turning up in production codebases years later, so don't be surprised to find two incompatible query styles living in the same project. Session lifecycle — when to commit, when to close, when you've drifted into an expired state — is the other recurring source of bugs, and the library documents the sharp edges more than it removes them.
Our full take →GitHub ↗
★ 14,803C#MITupdated Oct 2026
best for .NET teams who want LINQ queries, change tracking, and migrations built into the runtime itself
LINQ queries translate into SQL you'd recognize — joins, projections, aggregates come out reasonable without hand-tuning — and the provider model means your test suite can run on SQLite while production runs on SQL Server or Postgres without touching a query. Migrations are incremental and source-controlled, closer to Drizzle's inspect-before-apply approach than to GORM's auto-migrate-and-hope.
The change tracker handles graph updates — adding related entities, updating a disconnected graph — in ways that GORM and Hibernate users end up managing by hand.
Two things to watch: the Cosmos DB provider maps a relational model onto a document store badly enough that you're better off using something built for Cosmos directly, and nothing stops you from loading a list of orders and hitting .Customer in a loop — the same N+1 problem Hibernate has. Once you need provider-specific SQL for performance, you're writing FromSqlRaw strings EF can't check at compile time, the exact leak Diesel and Kysely are built to avoid.
Our full take →GitHub ↗
★ 6,477JavaApache-2.0updated Oct 2026
best for Java teams on JPA who need deep dialect coverage and built-in change history
Dialect coverage runs to CockroachDB, Spanner, and TiDB alongside the usual Postgres, MySQL, Oracle, and SQL Server list, each tested in CI rather than assumed to work. Envers gives you full entity audit history as a built-in module, which is more than SQLAlchemy, EF Core, or GORM offer without reaching for a separate library.
Hibernate 6's SQM rewrite fixed the old Criteria API's mess, so type-safe programmatic queries aren't the chore they used to be, and the second-level cache plugs into Ehcache, Infinispan, or Redis without touching entity code.
Lazily-loaded associations default to triggering one query per row the moment you iterate and touch a relation — the same N+1 trap as GORM and EF Core — and it will hit you at least once before you learn to reach for JOIN FETCH or batch fetching. Hibernate also treats schema migration as someone else's problem, handing it to Flyway or Liquibase, and the gap between hbm2ddl.auto=validate and an actual migration plan catches new users regularly.
Our full take →GitHub ↗
★ 39,981GoMITupdated Sep 2026
best for Go developers who want ActiveRecord-style convenience and are fine debugging at runtime instead of compile time
Association handling — preloading, eager loading via joins, many-to-many — works with little ceremony, and hooks like BeforeCreate or AfterSave integrate without forcing you to implement a heavy interface.
The developer-friendly design accepts almost any struct tag and fails at runtime instead of compile time, which is the opposite bet Diesel makes in Rust — a typo in a field name quietly produces bad SQL instead of a build error. db.Save(&user) will zero out any field holding a zero value or false unless you use Select explicitly, a footgun that's bitten most GORM codebases at least once.
Auto-migration is fine for development but won't safely drop or alter columns, so production schema changes still need Atlas or golang-migrate on top — closer to Hibernate's hand-off than to Drizzle's self-contained migration files.
Our full take →GitHub ↗
★ 36,105TypeScriptApache-2.0updated Oct 2026
best for TypeScript projects on serverless or edge runtimes where bundle size and SQL control both matter
Schema definitions produce precise TypeScript types for query results — nullable columns, joined tables, no codegen step — and at roughly 7.4kb gzipped with zero dependencies, it runs in Cloudflare Workers and other edge environments where EF Core or Hibernate wouldn't fit and Prisma's engine architecture would struggle.
The query builder maps to SQL concepts directly — joins, CTEs, window functions, on conflict — so the generated SQL is predictable in a way GORM's chainable API doesn't promise.
There are two overlapping ways to query — the relational API and the SQL-like API — and new users regularly land in the docs trying to work out why. drizzle-kit versions separately from drizzle-orm and the two fall out of sync often enough that mismatched versions dominate the issue tracker, and multi-table updates or deletes with joins either aren't supported or need raw SQL depending on the dialect.
Our full take →GitHub ↗
★ 47,682TypeScriptApache-2.0updated Oct 2026
best for Teams already using Prisma on Postgres or MongoDB who are comfortable building on a pre-1.0 release
The extension SPI is a real architectural change rather than a marketing line — Postgres support itself runs on the same public interface available to third-party plugin authors, which is why pgvector, PostGIS, and full-text search ship as separate packages instead of bloating the core. Prisma 7 stays supported on its own branch for 18 months, so moving over isn't a forced cutover.
It's a release candidate, not a stable release, and several of the flagship extensions — PostGIS, ParadeDB, Supabase — are still marked experimental. SQLite is described as a proof of concept today and MySQL has no support at all, so outside Postgres or MongoDB there's nothing here to build on. It also needs Node.js 24+, which rules out plenty of environments still pinned to LTS 20 or 22.
Our full take →GitHub ↗
★ 14,212RustApache-2.0updated Oct 2026
best for Rust services where you want the compiler to catch query mistakes before they reach production logs
Column type mismatches, joins against the wrong table, selecting a nullable column into a non-Option field — all of it fails at compile time instead of at runtime, the exact opposite trade GORM makes in Go. The schema macro generates a typed DSL straight from your actual schema, so drift between your types and the database shows up immediately instead of in a bug report.
Async isn't in the main crate — diesel-async is a separate project with its own release cadence, so you're tracking two crates instead of one. The type-level machinery that makes it safe also makes error messages hard to parse when something's wrong, and complex queries with several joins can produce types nested deep enough that you need to box them, which undercuts the point. MySQL is also a second-class citizen here: RETURNING, COPY, ranges, and arrays are Postgres-only, and the gap is documented, not small.
Our full take →GitHub ↗
★ 14,292TypeScriptMITupdated Oct 2026
best for Node, Bun, or Deno projects that want full type safety with no ORM layer in between
Column aliases like pet.name as pet_name come through correctly in the result type, and that inference holds through subqueries, joins, and with clauses, not just the simple case. Each dialect carries its own compiler, introspector, and driver, so a community dialect like PGlite slots in without touching core, and the sql template tag gives you an escape hatch that keeps its types instead of dropping you into a plain string.
There's no relation loading here — if you want Prisma's include or GORM's preload, you write the join and shape the result yourself every time. Schema types aren't pulled from a live database automatically either; you write them by hand or add the separate kysely-codegen tool and hope it stays in sync. Optional filters and conditional selects fight the type system hard enough that the docs carry a dedicated recipe for the 'excessively deep types' error TypeScript throws on large query compositions, and connection pooling is left entirely to you.
Our full take →GitHub ↗
// questions people ask
Before you choose
Should I choose a full ORM or a type-safe query builder?
Pick a query builder like Kysely, Drizzle, or Diesel if you want predictable SQL and don't mind writing joins yourself. Pick a full ORM like EF Core, Hibernate, or SQLAlchemy if you want relation loading, change tracking, and migrations handled for you, and you're willing to learn its session or context lifecycle to avoid the bugs that come with it.
Which of these is most likely to generate N+1 queries without warning?
GORM and Hibernate both default to lazy loading relations, so iterating over a list and touching a related field fires one query per row unless you preload or join-fetch explicitly. EF Core carries the same risk through navigation properties. Drizzle and Kysely avoid it entirely because you write the join yourself.
Is Prisma 8 ready to build on right now?
Not for most databases. It's a release candidate rather than a stable release, SQLite support is described as a proof of concept, and MySQL has no support at all. Postgres and MongoDB are the only paths with real coverage, and it requires Node.js 24+.
Which ORM has the safest migration story?
Drizzle and EF Core both generate inspectable, incremental migration files you review before applying, which is safer than tools that auto-apply schema changes. Hibernate hands migrations off to Flyway or Liquibase rather than managing them itself. GORM's auto-migration will add columns but won't drop or alter them, so production changes still need external tooling.
If you don't know which philosophy fits, default to the query builder for your language — Drizzle, Kysely, or Diesel — and reach for a full ORM only once you're sure you want it managing object state for you.