[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-7193":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":18,"stars30d":19,"stars90d":16,"forks30d":16,"starsTrendScore":20,"compositeScore":21,"rankGlobal":10,"rankLanguage":10,"license":22,"archived":23,"fork":23,"defaultBranch":24,"hasWiki":23,"hasPages":25,"topics":26,"createdAt":10,"pushedAt":10,"updatedAt":31,"readmeContent":32,"aiSummary":33,"trendingCount":16,"starSnapshotCount":16,"syncStatus":34,"lastSyncTime":35,"discoverSource":36},7193,"Exposed","JetBrains\u002FExposed","JetBrains","Kotlin SQL Framework","https:\u002F\u002Fwww.jetbrains.com\u002Fexposed\u002F",null,"Kotlin",9252,781,130,129,0,1,8,29,4,39.68,"Apache License 2.0",false,"main",true,[27,28,29,30],"dao","kotlin","orm","sql","2026-06-12 02:01:35","\u003Cdiv align=\"center\">\n\n  \u003Cpicture>\n    \u003Csource media=\"(prefers-color-scheme: dark)\" srcset=\".\u002Fdocumentation-website\u002FWriterside\u002Fimages\u002Fexposed-text-light.png\">\n    \u003Cimg alt=\"Exposed logo\" src=\".\u002Fdocumentation-website\u002FWriterside\u002Fimages\u002Fexposed-text-dark.png\" width=\"215\">\n  \u003C\u002Fpicture>\n\n\u003C\u002Fdiv>\n\u003Cbr>\n\n\u003Cdiv align=\"center\">\n\n[![Official JetBrains project](https:\u002F\u002Fjb.gg\u002Fbadges\u002Fofficial.svg)](https:\u002F\u002Fconfluence.jetbrains.com\u002Fdisplay\u002FALL\u002FJetBrains+on+GitHub)\n[![Kotlin](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fdynamic\u002Ftoml?url=https:\u002F\u002Fraw.githubusercontent.com\u002FJetBrains\u002FExposed\u002Frefs\u002Fheads\u002Fmain\u002Fgradle\u002Flibs.versions.toml&query=%24.versions.kotlin&logo=kotlin&label=kotlin&color=blue)](http:\u002F\u002Fkotlinlang.org)\n[![Slack Channel](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fchat-exposed-yellow.svg?logo=slack)](https:\u002F\u002Fkotlinlang.slack.com\u002Fmessages\u002Fexposed\u002F)\n[![TC Build status](https:\u002F\u002Fexposed.teamcity.com\u002Fapp\u002Frest\u002Fbuilds\u002FbuildType:id:Exposed_Build\u002FstatusIcon.svg)](https:\u002F\u002Fexposed.teamcity.com\u002FviewType.html?buildTypeId=Exposed_Build&guest=1)\n[![Maven Central](https:\u002F\u002Fimg.shields.io\u002Fmaven-central\u002Fv\u002Forg.jetbrains.exposed\u002Fexposed-core?label=maven+central)](https:\u002F\u002Fcentral.sonatype.com\u002Fsearch?namespace=org.jetbrains.exposed)\n[![GitHub License](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-Apache%20License%202.0-blue.svg?style=flat)](https:\u002F\u002Fwww.apache.org\u002Flicenses\u002FLICENSE-2.0)\n\n\u003C\u002Fdiv>\n\n## Welcome to **Exposed**, an ORM framework for [Kotlin](https:\u002F\u002Fgithub.com\u002FJetBrains\u002Fkotlin).\n\n[Exposed](https:\u002F\u002Fwww.jetbrains.com\u002Fexposed\u002F) is a lightweight SQL library on top of a database connectivity driver for the Kotlin programming language,\nwith support for both JDBC and R2DBC (since version 1.0.0-*) drivers. \nIt offers two approaches for database access: a typesafe SQL-wrapping Domain-Specific Language (DSL) and a lightweight Data Access Object (DAO) API.\n\nOur official mascot is the cuttlefish, which is well-known for its outstanding mimicry ability that enables it to blend seamlessly into any environment.\nSimilar to our mascot, Exposed can be used to mimic a variety of database engines, which helps you to build applications without dependencies on any specific database engine and to switch between them with very little or no changes.\n\n## Supported Databases\n\n- H2 (versions 2.x)\n- [![MariaDB](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FMariaDB-003545?style=for-the-badge&logo=mariadb&logoColor=white)](https:\u002F\u002Fgithub.com\u002Fmariadb-corporation\u002Fmariadb-connector-j)\n- [![MySQL](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fmysql-4479A1.svg?style=for-the-badge&logo=mysql&logoColor=white)](https:\u002F\u002Fgithub.com\u002Fmysql\u002Fmysql-connector-j)\n- [![Oracle](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FOracle-F80000?style=for-the-badge&logo=oracle&logoColor=white)](https:\u002F\u002Fwww.oracle.com\u002Fca-en\u002Fdatabase\u002Ftechnologies\u002Fappdev\u002Fjdbc-downloads.html)\n- [![Postgres](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fpostgres-%23316192.svg?style=for-the-badge&logo=postgresql&logoColor=white)](https:\u002F\u002Fjdbc.postgresql.org\u002F)\n  (Also, PostgreSQL using the [pgjdbc-ng](https:\u002F\u002Fimpossibl.github.io\u002Fpgjdbc-ng\u002F) JDBC driver)\n- [![MicrosoftSQLServer](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FMicrosoft%20SQL%20Server-CC2927?style=for-the-badge&logo=microsoft%20sql%20server&logoColor=white)](https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fmssql-jdbc)\n- [![SQLite](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fsqlite-%2307405e.svg?style=for-the-badge&logo=sqlite&logoColor=white)](https:\u002F\u002Fgithub.com\u002Fxerial\u002Fsqlite-jdbc)\n\n## Dependencies\n\nReleases of Exposed are available in the [Maven Central repository](https:\u002F\u002Fsearch.maven.org\u002Fsearch?q=org.jetbrains.exposed).\nFor details on how to configure this repository and how to add Exposed dependencies to an existing Gradle\u002FMaven project,\nsee the full [guide on modules](https:\u002F\u002Fwww.jetbrains.com\u002Fhelp\u002Fexposed\u002Fexposed-modules.html).\n\n### Exposed modules\n\n`Exposed` consists of the following core modules:\n\n| Module          | Function                                                                                                                                                         |\n|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `exposed-core`  | Provides the foundational components and abstractions needed to work with databases in a type-safe manner and includes the Domain-Specific Language (DSL) API    |\n| `exposed-dao`   | (Optional) Allows you to work with the Data Access Object (DAO) API. \u003Cbr> It is only compatible with `exposed-jdbc` and does not work with `exposed-r2dbc`.\u003C\u002Fbr> |\n| `exposed-jdbc`  | Provides support for Java Database Connectivity (JDBC) with a transport-level implementation based on the Java JDBC API                                          |\n| `exposed-r2dbc` | Provides support for Reactive Relational Database Connectivity (R2DBC)                                                                                           |\n\nAs well as the following extension modules:\n\n| Module                         | Function                                                                                                                                                                        |\n|--------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `exposed-crypt`                | Provides additional column types to store encrypted data in the database and encode\u002Fdecode it on the client-side                                                                |\n| `exposed-java-time`            | Date-time extensions based on the [Java 8 Time API](https:\u002F\u002Fdocs.oracle.com\u002Fjavase\u002F8\u002Fdocs\u002Fapi\u002Fjava\u002Ftime\u002Fpackage-summary.html)                                                   |\n| `exposed-jodatime`             | Date-time extensions based on the [Joda-Time](https:\u002F\u002Fwww.joda.org\u002Fjoda-time\u002F) library                                                                                          |\n| `exposed-json`                 | JSON and JSONB data type extensions                                                                                                                                             |\n| `exposed-kotlin-datetime`      | Date-time extensions based on the [`kotlinx-datetime`](https:\u002F\u002Fkotlinlang.org\u002Fapi\u002Fkotlinx-datetime\u002F) library                                                                    |\n| `exposed-migration-core`       | Provides core common functionality for database schema migrations                                                                                                               |\n| `exposed-migration-jdbc`       | Provides utilities to support database schema migrations, with a reliance on a JDBC driver                                                                                      |\n| `exposed-migration-r2dbc`      | Provides utilities to support database schema migrations, with a reliance on a R2DBC driver                                                                                     |\n| `exposed-money`                | Extensions to support [`MonetaryAmount`](https:\u002F\u002Fjavamoney.github.io\u002Fapidocs\u002Fjava.money\u002Fjavax\u002Fmoney\u002FMonetaryAmount.html) from the [JavaMoney API](https:\u002F\u002Fjavamoney.github.io\u002F) |\n| `exposed-spring-boot-starter`  | A starter for [Spring Boot 3](https:\u002F\u002Fspring.io\u002Fprojects\u002Fspring-boot) to utilize Exposed as the ORM                                                                             |\n| `exposed-spring-boot4-starter` | A starter for [Spring Boot 4](https:\u002F\u002Fspring.io\u002Fprojects\u002Fspring-boot) to utilize Exposed as the ORM                                                                             |\n| `spring-transaction`           | Transaction manager that builds on top of the standard transaction workflow from Spring Framework 6                                                                             |\n| `spring7-transaction`          | Transaction manager that builds on top of the standard transaction workflow from Spring Framework 7                                                                             |\n\n### Requirements\n\nKotlin version 2.1.+\n\nThe following module(s) require JDK 17 or newer:\n* `spring-transaction` - depends on Spring Framework 6\n* `spring7-transaction` - depends on Spring Framework 7\n* `exposed-spring-boot-starter` - depends on Spring Boot 3\n* `exposed-spring-boot4-starter` - depends on Spring Boot 4\n* `exposed-crypt` - depends on Spring Security 7\n\nThe following module(s) require JDK 11 or newer:\n* `exposed-r2dbc`\n* `exposed-migration-r2dbc`\n\nAll other modules have a minimum requirement of JDK 8.\n\n## Samples using Exposed\n\nFollow the [Getting Started with DSL tutorial](https:\u002F\u002Fwww.jetbrains.com\u002Fhelp\u002Fexposed\u002Fgetting-started-with-exposed.html) for a quick start or check out the [samples](samples\u002FREADME.md) for more in-depth projects.\n\n## Documentation\n\nFor complete documentation, samples, and tutorials, see the following links:\n\n-   [Documentation](https:\u002F\u002Fwww.jetbrains.com\u002Fhelp\u002Fexposed\u002Fhome.html)\n-   [Migration Guide](https:\u002F\u002Fwww.jetbrains.com\u002Fhelp\u002Fexposed\u002Fmigration-guide-1-0-0.html)\n-   [Breaking changes](https:\u002F\u002Fwww.jetbrains.com\u002Fhelp\u002Fexposed\u002Fbreaking-changes.html)\n\n## Contributing\n\n### Reporting issues\n\nWe encourage your feedback in any form, such as feature requests, bug reports, documentation updates, and questions.\n\nPlease use [our issue tracker](https:\u002F\u002Fyoutrack.jetbrains.com\u002Fissues\u002FEXPOSED) to report any issues or to log new requests.\n\nWhile issues are visible publicly, either creating a new issue or commenting on an existing one does require logging in to YouTrack.\n\n### Submitting pull requests\n\nWe actively welcome your pull requests and encourage you to link your work to an [existing issue](https:\u002F\u002Fyoutrack.jetbrains.com\u002Fissues\u002FEXPOSED).\n\n\nSee the full [contribution guide](https:\u002F\u002Fwww.jetbrains.com\u002Fhelp\u002Fexposed\u002Fcontributing.html#pull-requests) for more details.\n\nBy contributing to the Exposed project, you agree that your contributions will be licensed under [Apache License, Version 2.0](https:\u002F\u002Fwww.apache.org\u002Flicenses\u002FLICENSE-2.0).\n\u003Cbr>\u003Cbr>\n\n## Support\n\nHave questions or want to contribute to the discussion? Join us in the [#exposed](https:\u002F\u002Fkotlinlang.slack.com\u002Fmessages\u002Fexposed\u002F) channel on the [Kotlin Slack](https:\u002F\u002Fkotlinlang.slack.com\u002F).\nIf you're not a member yet, you can [request an invitation](https:\u002F\u002Fsurveys.jetbrains.com\u002Fs3\u002Fkotlin-slack-sign-up).\n\n## Examples\n\n### SQL DSL\n\n```kotlin\nimport org.jetbrains.exposed.v1.core.*\nimport org.jetbrains.exposed.v1.core.SqlExpressionBuilder.like\nimport org.jetbrains.exposed.v1.jdbc.*\nimport org.jetbrains.exposed.v1.jdbc.transactions.transaction\n\nobject Cities : Table() {\n    val id = integer(\"id\").autoIncrement()\n    val name = varchar(\"name\", 50)\n\n    override val primaryKey = PrimaryKey(id)\n}\n\nobject Users : Table() {\n    val id = varchar(\"id\", 10)\n    val name = varchar(\"name\", length = 50)\n    val cityId = integer(\"city_id\").references(Cities.id).nullable()\n\n    override val primaryKey = PrimaryKey(id, name = \"PK_User_ID\")\n}\n\nfun main() {\n    Database.connect(\"jdbc:h2:mem:test\", driver = \"org.h2.Driver\", user = \"root\", password = \"\")\n\n    transaction {\n        addLogger(StdOutSqlLogger)\n\n        SchemaUtils.create(Cities, Users)\n\n        val saintPetersburgId = Cities.insert {\n            it[name] = \"St. Petersburg\"\n        } get Cities.id\n\n        val munichId = Cities.insert {\n            it[name] = \"Munich\"\n        } get Cities.id\n\n        val pragueId = Cities.insert {\n            it.update(name, stringLiteral(\"   Prague   \").trim().substring(1, 2))\n        }[Cities.id]\n\n        val pragueName = Cities\n            .selectAll()\n            .where { Cities.id eq pragueId }\n            .single()[Cities.name]\n        println(\"pragueName = $pragueName\")\n\n        Users.insert {\n            it[id] = \"andrey\"\n            it[name] = \"Andrey\"\n            it[cityId] = saintPetersburgId\n        }\n\n        Users.insert {\n            it[id] = \"sergey\"\n            it[name] = \"Sergey\"\n            it[cityId] = munichId\n        }\n\n        Users.insert {\n            it[id] = \"eugene\"\n            it[name] = \"Eugene\"\n            it[cityId] = munichId\n        }\n\n        Users.insert {\n            it[id] = \"alex\"\n            it[name] = \"Alex\"\n            it[cityId] = null\n        }\n\n        Users.insert {\n            it[id] = \"smth\"\n            it[name] = \"Something\"\n            it[cityId] = null\n        }\n\n        Users.update(where = { Users.id eq \"alex\" }) {\n            it[name] = \"Alexey\"\n        }\n\n        Users.deleteWhere { Users.name like \"%thing\" }\n\n        println(\"All cities:\")\n\n        Cities\n            .selectAll()\n            .forEach { result ->\n                println(\"${result[Cities.id]}: ${result[Cities.name]}\")\n            }\n\n        println(\"Manual join:\")\n\n        (Users innerJoin Cities)\n            .select(Users.name, Cities.name)\n            .where {\n                (Users.id.eq(\"andrey\") or Users.name.eq(\"Sergey\")) and\n                    Users.id.eq(\"sergey\") and Users.cityId.eq(Cities.id)\n            }.forEach { result ->\n                println(\"${result[Users.name]} lives in ${result[Cities.name]}\")\n            }\n\n        println(\"Join with foreign key:\")\n\n        (Users innerJoin Cities)\n            .select(Users.name, Users.cityId, Cities.name)\n            .where { Cities.name.eq(\"St. Petersburg\") or Users.cityId.isNull() }\n            .forEach { result ->\n                if (result[Users.cityId] != null) {\n                    println(\"${result[Users.name]} lives in ${result[Cities.name]}\")\n                } else {\n                    println(\"${result[Users.name]} lives nowhere\")\n                }\n            }\n\n        println(\"Functions and group by:\")\n\n        (Cities innerJoin Users)\n            .select(Cities.name, Users.id.count())\n            .groupBy(Cities.name)\n            .forEach { result ->\n                val cityName = result[Cities.name]\n                val userCount = result[Users.id.count()]\n\n                if (userCount > 0) {\n                    println(\"$userCount user(s) live(s) in $cityName\")\n                } else {\n                    println(\"Nobody lives in $cityName\")\n                }\n            }\n\n        SchemaUtils.drop(Users, Cities)\n    }\n}\n\n```\n\nGenerated SQL:\n\n```sql\n    SQL: CREATE TABLE IF NOT EXISTS CITIES (ID INT AUTO_INCREMENT PRIMARY KEY, \"name\" VARCHAR(50) NOT NULL)\n    SQL: CREATE TABLE IF NOT EXISTS USERS (ID VARCHAR(10), \"name\" VARCHAR(50) NOT NULL, CITY_ID INT NULL, CONSTRAINT PK_User_ID PRIMARY KEY (ID), CONSTRAINT FK_USERS_CITY_ID__ID FOREIGN KEY (CITY_ID) REFERENCES CITIES(ID) ON DELETE RESTRICT ON UPDATE RESTRICT)\n    SQL: INSERT INTO CITIES (\"name\") VALUES ('St. Petersburg')\n    SQL: INSERT INTO CITIES (\"name\") VALUES ('Munich')\n    SQL: INSERT INTO CITIES (\"name\") VALUES (SUBSTRING(TRIM('   Prague   '), 1, 2))\n    SQL: SELECT CITIES.ID, CITIES.\"name\" FROM CITIES WHERE CITIES.ID = 3\n    pragueName = Pr\n    SQL: INSERT INTO USERS (ID, \"name\", CITY_ID) VALUES ('andrey', 'Andrey', 1)\n    SQL: INSERT INTO USERS (ID, \"name\", CITY_ID) VALUES ('sergey', 'Sergey', 2)\n    SQL: INSERT INTO USERS (ID, \"name\", CITY_ID) VALUES ('eugene', 'Eugene', 2)\n    SQL: INSERT INTO USERS (ID, \"name\", CITY_ID) VALUES ('alex', 'Alex', NULL)\n    SQL: INSERT INTO USERS (ID, \"name\", CITY_ID) VALUES ('smth', 'Something', NULL)\n    SQL: UPDATE USERS SET \"name\"='Alexey' WHERE USERS.ID = 'alex'\n    SQL: DELETE FROM USERS WHERE USERS.\"name\" LIKE '%thing'\n    All cities:\n    SQL: SELECT CITIES.ID, CITIES.\"name\" FROM CITIES\n    1: St. Petersburg\n    2: Munich\n    3: Pr\n    Manual join:\n    SQL: SELECT USERS.\"name\", CITIES.\"name\" FROM USERS INNER JOIN CITIES ON CITIES.ID = USERS.CITY_ID WHERE ((USERS.ID = 'andrey') OR (USERS.\"name\" = 'Sergey')) AND (USERS.ID = 'sergey') AND (USERS.CITY_ID = CITIES.ID)\n    Sergey lives in Munich\n    Join with foreign key:\n    SQL: SELECT USERS.\"name\", USERS.CITY_ID, CITIES.\"name\" FROM USERS INNER JOIN CITIES ON CITIES.ID = USERS.CITY_ID WHERE (CITIES.\"name\" = 'St. Petersburg') OR (USERS.CITY_ID IS NULL)\n    Andrey lives in St. Petersburg\n    Functions and group by:\n    SQL: SELECT CITIES.\"name\", COUNT(USERS.ID) FROM CITIES INNER JOIN USERS ON CITIES.ID = USERS.CITY_ID GROUP BY CITIES.\"name\"\n    2 user(s) live(s) in Munich\n    1 user(s) live(s) in St. Petersburg\n    SQL: DROP TABLE IF EXISTS USERS\n    SQL: DROP TABLE IF EXISTS CITIES\n```\n\n### DAO\n\n```kotlin\nimport org.jetbrains.exposed.v1.core.StdOutSqlLogger\nimport org.jetbrains.exposed.v1.core.dao.id.*\nimport org.jetbrains.exposed.v1.dao.*\nimport org.jetbrains.exposed.v1.jdbc.*\nimport org.jetbrains.exposed.v1.jdbc.transactions.transaction\n\nobject Cities: IntIdTable() {\n    val name = varchar(\"name\", 50)\n}\n\nobject Users : IntIdTable() {\n    val name = varchar(\"name\", length = 50).index()\n    val city = reference(\"city\", Cities)\n    val age = integer(\"age\")\n}\n\nclass City(id: EntityID\u003CInt>) : IntEntity(id) {\n    companion object : IntEntityClass\u003CCity>(Cities)\n\n    var name by Cities.name\n    val users by User referrersOn Users.city\n}\n\nclass User(id: EntityID\u003CInt>) : IntEntity(id) {\n    companion object : IntEntityClass\u003CUser>(Users)\n\n    var name by Users.name\n    var city by City referencedOn Users.city\n    var age by Users.age\n}\n\nfun main() {\n    Database.connect(\"jdbc:h2:mem:test\", driver = \"org.h2.Driver\", user = \"root\", password = \"\")\n\n    transaction {\n        addLogger(StdOutSqlLogger)\n\n        val saintPetersburg = City.new {\n            name = \"St. Petersburg\"\n        }\n\n        val munich = City.new {\n            name = \"Munich\"\n        }\n\n        User.new {\n            name = \"Andrey\"\n            city = saintPetersburg\n            age = 5\n        }\n\n        User.new {\n            name = \"Sergey\"\n            city = saintPetersburg\n            age = 27\n        }\n\n        User.new {\n            name = \"Eugene\"\n            city = munich\n            age = 42\n        }\n\n        val alex = User.new {\n            name = \"alex\"\n            city = munich\n            age = 11\n        }\n\n        alex.name = \"Alexey\"\n\n        println(\"Cities: ${City.all().joinToString { it.name }}\")\n\n        println(\"Users in ${saintPetersburg.name}: ${saintPetersburg.users.joinToString { it.name }}\")\n\n        println(\"Adults: ${User.find { Users.age greaterEq 18 }.joinToString { it.name }}\")\n\n        SchemaUtils.drop(Users, Cities)\n    }\n}\n```\n\nGenerated SQL:\n\n```sql\n    SQL: CREATE TABLE IF NOT EXISTS CITIES (ID INT AUTO_INCREMENT PRIMARY KEY, \"name\" VARCHAR(50) NOT NULL)\n    SQL: CREATE TABLE IF NOT EXISTS USERS (ID INT AUTO_INCREMENT PRIMARY KEY, \"name\" VARCHAR(50) NOT NULL, CITY INT NOT NULL, AGE INT NOT NULL, CONSTRAINT FK_USERS_CITY__ID FOREIGN KEY (CITY) REFERENCES CITIES(ID) ON DELETE RESTRICT ON UPDATE RESTRICT)\n    SQL: CREATE INDEX USERS_NAME ON USERS (\"name\")\n    SQL: INSERT INTO CITIES (\"name\") VALUES ('St. Petersburg')\n    SQL: INSERT INTO CITIES (\"name\") VALUES ('Munich')\n    SQL: SELECT CITIES.ID, CITIES.\"name\" FROM CITIES\n    Cities: St. Petersburg, Munich\n    SQL: INSERT INTO USERS (\"name\", CITY, AGE) VALUES ('Andrey', 1, 5)\n    SQL: INSERT INTO USERS (\"name\", CITY, AGE) VALUES ('Sergey', 1, 27)\n    SQL: INSERT INTO USERS (\"name\", CITY, AGE) VALUES ('Eugene', 2, 42)\n    SQL: INSERT INTO USERS (\"name\", CITY, AGE) VALUES ('Alexey', 2, 11)\n    SQL: SELECT USERS.ID, USERS.\"name\", USERS.CITY, USERS.AGE FROM USERS WHERE USERS.CITY = 1\n    Users in St. Petersburg: Andrey, Sergey\n    SQL: SELECT USERS.ID, USERS.\"name\", USERS.CITY, USERS.AGE FROM USERS WHERE USERS.AGE >= 18\n    Adults: Sergey, Eugene\n    SQL: DROP TABLE IF EXISTS USERS\n    SQL: DROP TABLE IF EXISTS CITIES\n```\n","Exposed 是一个为 Kotlin 语言设计的轻量级 SQL 框架，支持 JDBC 和 R2DBC 驱动。它提供了类型安全的 SQL 包装 DSL 和轻量级 DAO API 两种数据库访问方式，能够帮助开发者以更直观、更安全的方式编写数据库操作代码。Exposed 的一大特色是其高度可移植性，可以轻松地在不同的数据库引擎间切换而无需大幅修改代码，目前支持 H2、MariaDB、MySQL、Oracle 和 PostgreSQL 等多种主流数据库。因此，对于需要构建跨数据库兼容的应用程序或希望降低数据库绑定风险的 Kotlin 项目来说，Exposed 是一个非常理想的选择。",2,"2026-06-11 03:11:05","top_language"]