{"id":16702375,"url":"https://github.com/kurtbuilds/ormlite","last_synced_at":"2025-05-15T03:06:35.182Z","repository":{"id":38800544,"uuid":"439743639","full_name":"kurtbuilds/ormlite","owner":"kurtbuilds","description":"An ORM in Rust for developers that love SQL.","archived":false,"fork":false,"pushed_at":"2025-04-23T09:11:50.000Z","size":696,"stargazers_count":254,"open_issues_count":15,"forks_count":14,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-23T10:25:55.417Z","etag":null,"topics":["database","orm","postgresql","rust","sqlite","sqlx"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/ormlite","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kurtbuilds.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-19T00:33:28.000Z","updated_at":"2025-04-23T09:11:53.000Z","dependencies_parsed_at":"2023-02-15T11:31:36.767Z","dependency_job_id":"85e874b5-ee6e-4244-a355-dc8547401efc","html_url":"https://github.com/kurtbuilds/ormlite","commit_stats":{"total_commits":337,"total_committers":10,"mean_commits":33.7,"dds":"0.041543026706231445","last_synced_commit":"f04a90a63260b3bdcf76bf21df08ee745e834ce3"},"previous_names":[],"tags_count":39,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kurtbuilds%2Formlite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kurtbuilds%2Formlite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kurtbuilds%2Formlite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kurtbuilds%2Formlite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kurtbuilds","download_url":"https://codeload.github.com/kurtbuilds/ormlite/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254264766,"owners_count":22041793,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","orm","postgresql","rust","sqlite","sqlx"],"created_at":"2024-10-12T19:04:27.350Z","updated_at":"2025-05-15T03:06:30.172Z","avatar_url":"https://github.com/kurtbuilds.png","language":"Rust","readme":"\u003cdiv id=\"top\"\u003e\u003c/div\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://github.com/kurtbuilds/ormlite/graphs/contributors\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/contributors/kurtbuilds/ormlite.svg?style=flat-square\" alt=\"GitHub Contributors\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/kurtbuilds/ormlite/stargazers\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/stars/kurtbuilds/ormlite.svg?style=flat-square\" alt=\"Stars\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/kurtbuilds/ormlite/actions\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/actions/workflow/status/kurtbuilds/ormlite/test.yaml?style=flat-square\" alt=\"Build Status\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://crates.io/crates/ormlite\"\u003e\n    \u003cimg src=\"https://img.shields.io/crates/d/ormlite?style=flat-square\" alt=\"Downloads\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://crates.io/crates/ormlite\"\u003e\n    \u003cimg src=\"https://img.shields.io/crates/v/ormlite?style=flat-square\" alt=\"Crates.io\" /\u003e\n\u003c/a\u003e\n\n\u003c/p\u003e\n\n# `ormlite`\n\n**`ormlite` is an ORM in Rust for developers that love SQL.** Let's see it in action:\n\n```rust\nuse ormlite::model::*;\nuse ormlite::sqlite::SqliteConnection;\n\n#[derive(Model, Debug)]\npub struct Person {\n    pub id: i32,\n    pub name: String,\n    pub age: i32,\n}\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    /// Start by making a database connection.\n    let mut conn = SqliteConnection::connect(\":memory:\").await.unwrap();\n\n    /// You can insert the model directly.\n    let mut john = Person {\n        id: 1,\n        name: \"John\".to_string(),\n        age: 99,\n    }.insert(\u0026mut conn).await?;\n\n    println!(\"{:?}\", john);\n\n    /// After modifying the object, you can update all its fields.\n    john.age += 1;\n    john.update_all_fields(\u0026mut conn).await?;\n\n    /// Query builder syntax closely follows SQL syntax, translated into chained function calls.\n    let people = Person::select()\n        .where_(\"age \u003e ?\").bind(50)\n        .fetch_all(\u0026mut conn).await?;\n\n    println!(\"{:?}\", people);\n}\n```\n\nYou might like `ormlite` because:\n\n- It auto-generates migrations from Rust structs. To my knowledge, it is the only Rust ORM with this capability.\n- The join API (in alpha) has far fewer moving pieces than any other Rust ORM. It only relies on the table `struct`s themselves, and does not rely on relation traits (SeaORM) or modules (Diesel).\n- There's little to no query builder syntax to learn. The query builder basically joins together \u0026str fragments of raw SQL. It strikes the right level of abstraction between composability, and having near-zero learning curve for anyone who already knows SQL.\n\n# Quickstart\n\n### Installation\n\nInstall with `cargo`:\n\n```ps\n# For postgres\ncargo add ormlite --features postgres\n# For sqlite\ncargo add ormlite --features sqlite\n```\n\nOr update your `Cargo.toml`:\n\n```toml\n[dependencies]\n# For postgres\normlite = { version = \"..\", features = [\"postgres\"] }\n# For sqlite\normlite = { version = \"..\", features = [\"sqlite\"] }\n```\n\nOther databases and runtimes are supported, but are less tested. Please submit an issue if you encounter any.\n\n### Environment Setup\n\nYou need `DATABASE_URL` in your environment. We recommend a tool like [`just`](https://github.com/casey/just), which\ncan pull in a `.env` file, but for simplicity, here we'll use shell directly.\n\n```bash\nexport DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres\n```\n\n### Migrations\n\nIf you are querying a static database and don't need migrations, skip this section. If you want migrations, keep reading.\n\nFirst, install `ormlite-cli`. Currently, the CLI only supports Postgres. While `ormlite-cli` is separate from [`sqlx-cli`](https://github.com/launchbadge/sqlx/blob/master/sqlx-cli/README.md#usage), they are 100% compatible with each other.\n`sqlx-cli` does not support auto-generating migrations or snapshots (to rollback in development without writing down migrations), but it is less bleeding edge and supports more database types.\n\n```bash\ncargo install ormlite-cli\n```\n\nNext, create the database and the migrations table. `init` creates a `_sqlx_migrations` table that tracks your migrations.\n\n```bash\n# Create the database if it doesn't exist. For postgres, that's:\n# createdb \u003cdbname\u003e\normlite init\n```\n\nLet's see migrations in action. Create a Rust struct with `#[derive(Model)]`, which the CLI tool detects to auto-generate migrations:\n\n```\n# src/models.rs\n\nuse ormlite::model::*;\n\n#[derive(Model, Debug)]\npub struct Person {\n    pub id: i32,\n    pub name: String,\n    pub age: i32,\n}\n```\n\nNext, auto-generate the migration.\n\n```bash\normlite migrate initial\n```\n\nThis creates a plain SQL file in `migrations/`. Let's review it before we execute it:\n\n```bash\ncat migrations/*.sql\n```\n\nOnce you're satisfied reviewing it, you can execute it:\n\n```bash\normlite up\n```\n\nBy default, `up` also creates a snapshot, so you can rollback using `ormlite down` if need be. There's also an option to generate paired up/down migrations instead of only up migrations.\n\nThat's the end of setup. Let's now look at how to run queries.\n\n# Insert \u0026 Update\n\nThe insert and update syntax at the top of the README is most effective for UUID primary key tables.\n\n```rust\nuse ormlite::model::*;\nuse uuid::Uuid;\n\n#[derive(Model, Debug)]\npub struct Event {\n    pub id: Uuid,\n    pub name: String,\n}\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let mut conn = ormlite::sqlite::SqliteConnection::connect(\":memory:\").await.unwrap();\n\n    let mut event = Event {\n        id: Uuid::new_v4(),\n        name: \"user_clicked\".to_string(),\n    }.insert(\u0026mut conn).await?;\n\n    println!(\"{:?}\", event);\n}\n```\n\nThis syntax has two possible issues. First, `id` is not `Option`, so it must be set,\ncausing problems for autoincrement id fields. Second, the struct cannot track which fields are modified, so the update\nmethod must update all columns.\n\n### Insertion Struct\n\nTo work around the autoincrement issue, you can use an insertion struct, shown here, or a builder, shown below.\n\n```rust\nuse ormlite::types::Json;\nuse serde_json::Value;\n\n#[derive(Model, Debug)]\n#[ormlite(insert = \"InsertPerson\")]\npub struct Person {\n    pub id: i32,\n    // Because the other fields are the primary key, and marked as default and default_value respectively,\n    // `name` is the only field in the InsertPerson struct.\n    pub name: String,\n    // This field will not be part of the InsertPerson struct,\n    // and rows will take the database-level default upon insertion.\n    #[ormlite(default)]\n    pub archived_at: Option\u003cDateTime\u003cUtc\u003e\u003e,\n    // This field will not be part of the InsertPerson struct,\n    // which will always pass the provided value when inserting.\n    #[ormlite(default_value = \"serde_json::json!({})\")]\n    pub metadata: Json\u003cValue\u003e,\n}\n\nasync fn insertion_struct_example(conn: \u0026mut SqliteConnection) {\n    let john: Person = InsertPerson {\n        name: \"John\".to_string(),\n    }.insert(\u0026mut conn).await?;\n\n    println!(\"{:?}\", john);\n}\n```\n\nIf the derived struct doesn't meet your needs, you can manually define a struct that only contains the fields you want,\nspecifying `table = \"\u003ctable\u003e\"` to route the struct to the same database table.\n\n```rust\n#[derive(Model, Debug)]\n#[ormlite(returns = \"User\")]\npub struct InsertPerson {\n    pub name: String,\n    pub age: i32,\n}\n```\n\n### Insert Builder \u0026 Update Builder\n\nYou can also use builder syntax for insertion or to update only certain fields.\n\n```rust\n#[derive(Model, Debug)]\npub struct Person {\n    pub id: i32,\n    pub name: String,\n    pub age: i32,\n}\n\nasync fn builder_syntax_example() {\n    // builder syntax for insert\n    let john = Person::builder()\n        .name(\"John\".to_string())\n        .age(99)\n        .insert(\u0026mut conn).await?;\n\n    println!(\"{:?}\", john);\n\n    // builder syntax for update\n    let john = john.update_partial()\n        .age(100)\n        .update(\u0026mut conn).await?;\n\n    println!(\"{:?}\", john);\n}\n```\n\n### Upsert\n\nYou can handle insertion on conflict using `OnConflict` ([docs](https://docs.rs/ormlite/latest/ormlite/query_builder/enum.OnConflict.html)).\n\n```rust\nuse ormlite::{\n    model::*,\n    query_builder::OnConflict,\n};\n\n#[derive(Debug, Model)]\npub struct Users {\n    #[ormlite(primary_key)]\n    pub id:    i32,\n    pub name:  String,\n    pub email: String,\n}\n\nasync fn upsert_example(conn: \u0026mut PgConnection) {\n    Users {\n        id: 1,\n        name: String::from(\"New name\"),\n        email: String::from(\"New email\"),\n    }\n        .insert(\u0026mut conn)\n        // update values of all columns on primary key conflict\n        .on_conflict(OnConflict::do_update_on_pkey(\"id\"))\n        .await\n        .unwrap();\n}\n```\n\n# Select Query\n\nYou can use `Model::select` to build a SQL query using Rust logic.\n\n\u003e **Note**: Postgres's approach of using numbered dollar sign placeholders quickly breaks down when building queries. Instead, even with Postgres, use `?` for parameters,\n\u003e and `ormlite` will replace the `?` placeholders with `$` placeholders when it constructs the final query.\n\n```rust\n#[derive(Model, Debug)]\npub struct Person {\n    pub id: i32,\n    pub name: String,\n    pub age: i32,\n}\n\nasync fn query_builder_example() {\n    let people = Person::select()\n        .where_(\"age \u003e ?\")\n        .bind(50i32)\n        .fetch_all(\u0026mut conn)\n        .await?;\n    println!(\"All people over 50: {:?}\", people);\n}\n```\n\n### Raw Query\n\nYou can fall back to raw queries if the ORM methods don't work for you. You can include handwritten strings, or if\nyou want a lower-level query builder, you can use [`sqlmo`](https://github.com/kurtbuilds/sqlmo),\nthe underlying engine that powers `ormlite`'s query builder \u0026 migration auto-generation.\n\n```rust\nasync fn model_query_example() {\n    // Query using the Model to still deserialize results into the struct\n    let _person = Person::query(\"SELECT * FROM person WHERE id = ?\")\n        .bind(1)\n        .fetch_one(\u0026mut conn)\n        .await?;\n}\n\nasync fn raw_query_example() {\n    // You can also use the raw query API, which will return tuples to decode as you like\n    let _used_ids: Vec\u003ci32\u003e = ormlite::query_as(\"SELECT id FROM person\")\n        .fetch_all(pool)\n        .await\n        .unwrap()\n        .into_iter()\n        .map(|row: (i32, )| row.0)\n        .collect();\n}\n```\n\n# Table Customization\n\nAttributes are defined in [these structs](https://github.com/kurtbuilds/ormlite/blob/master/attr/src/attr.rs).\n\nThis example shows them in action:\n\n```rust\n#[derive(Model, Debug)]\n#[ormlite(table = \"people\", insert = \"InsertPerson\")]\npub struct Person {\n    #[ormlite(primary_key)]\n    pub id: i32,\n    pub name: String,\n    #[ormlite(column = \"name_of_db_column\")]\n    pub age: i32,\n}\n```\n\n## Joins\n\nJoin support is alpha stage. Right now, `ormlite` only support many-to-one relations (e.g. Person belongs to Organization).\nSupport for many-to-many and one-to-many is planned. If you use this functionality, please report any bugs you encounter.\n\n```rust\n#[derive(Model, Debug)]\npub struct Person {\n    pub id: Uuid,\n    pub name: String,\n    pub age: i32,\n\n    // Note that we don't declare a separate field `pub organization_id: Uuid`.\n    // It is implicitly defined by the Join and the join_column attribute.\n    #[ormlite(column = \"organization_id\")]\n    pub organization: Join\u003cOrganization\u003e,\n}\n\n#[derive(Model, Debug)]\npub struct Organization {\n    pub id: Uuid,\n    pub name: String,\n}\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    // Note we don't need to insert it.\n    let org = Organization {\n        id: Uuid::new_v4(),\n        name: \"Acme\".to_string(),\n    };\n\n    let user = Person {\n        id: Uuid::new_v4(),\n        name: \"John\".to_string(),\n        age: 99,\n        organization: Join::new(org),\n    };\n\n    let mut conn = ormlite::sqlite::SqliteConnection::connect(\":memory:\").await.unwrap();\n\n    let user = user.insert(\u0026mut conn).await?;\n    assert_eq!(user.organization.loaded(), true);\n    println!(\"{:?}\", user);\n\n    // You can choose whether you want to load the relation or not. The value will be Join::NotQueried if you don't\n    // opt-in to loading it.\n    let users = Person::select()\n        .join(Person::organization())\n        .fetch_all(\u0026mut conn)\n        .await?;\n\n    for user in users {\n        assert!(user.organization.loaded());\n        println!(\"{:?}\", user);\n    }\n}\n```\n\n# Features \u0026 Data Types\n\n## Uuid, Chrono, \u0026 Time\n\nIf you want Uuid or DateTime, combined with serde, you need to depend directly on `uuid`, `time` or `chrono`,\nand add the `serde` feature to each of them.\n\n```\n# Cargo.toml\n[dependencies]\nuuid = { version = \"...\", features = [\"serde\"] }\nchrono = { version = \"...\", features = [\"serde\"] }\ntime = { version = \"...\", features = [\"serde\"] }\n```\n\n```rust\nuse ormlite::model::*;\nuse serde::{Serialize, Deserialize};\nuse ormlite::types::Uuid;\nuse ormlite::types::chrono::{DateTime, Utc};\n\n#[derive(Model, Debug, Serialize, Deserialize)]\npub struct Person {\n    pub uuid: Uuid,\n    pub created_at: DateTime\u003cUtc\u003e,\n    pub name: String,\n}\n```\n\n## Json/Jsonb Columns\n\nYou can either use `ormlite::types::Json` for JSON or JSONB fields, or you can use the `json` attribute.\nFor unstructured data, use `serde_json::Value` as the inner type. Use a struct with `Deserialize + Serialize` as the\ngeneric for structured data.\n\n```rust\nuse ormlite::model::*;\nuse ormlite::types::Json;\nuse serde_json::Value;\n\n#[derive(Debug, Serialize, Deserialize)]\npub struct JobData {\n    pub name: String,\n}\n\n#[derive(Model, Serialize, Deserialize)]\npub struct Job {\n    pub id: i32,\n    pub structured_data: Json\u003cJobData\u003e,\n    pub unstructured_data: Json\u003cValue\u003e,\n    #[ormlite(json)]\n    pub unstructured_data2: Value,\n    #[ormlite(json)]\n    pub structured_data2: JobData,\n}\n```\n\n# Logging\n\nYou can log queries using sqlx's logger: `RUST_LOG=sqlx=info`\n\n# Roadmap\n\n- [x] Insert, update, delete directly on model instances\n- [x] Builder for partial update and insertions\n- [x] User can create insert models that ignore default values\n- [x] Select query builder\n- [x] Build the derive macro\n- [x] Get() function for fetching a single entity.\n- [x] Ability to specify the name of a table and name of primary column\n- [x] Automatically generate insert models\n- [x] Automatically generate migrations\n- [x] Eliminate need for FromRow macro\n- [x] Many to one joins\n- [ ] created_at should naturally default to now()\n- [ ] id: i32 should default to identity by default\n- [ ] Autogenerate indexes for migrations\n- [ ] Many to many joins\n- [ ] One to many joins\n- [ ] Make sure features are wired up correctly to support mysql and different runtimes \u0026 SSL libraries.\n- [ ] Macro option to auto adjust columns like updated_at\n- [x] Upsert functionality\n- [ ] Bulk insertions\n- [ ] Query builder for bulk update\n- [ ] Handle on conflict clauses for bulk update\n- [ ] Benchmarks against raw sql, sqlx, ormx, seaorm, sqlite3-sys, pg, diesel\n- [ ] Support for patch records, i.e. update with static fields.\n- [ ] Consider a blocking interface, perhaps for sqlite/Rusqlite only.\n\n# Contributing\n\nOpen source thrives on contributions, and `ormlite` is a community project. We welcome you to file bugs, feature\nrequests, requests for better docs, pull requests, and more!\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkurtbuilds%2Formlite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkurtbuilds%2Formlite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkurtbuilds%2Formlite/lists"}