{"id":19064918,"url":"https://github.com/thekeenant/flow","last_synced_at":"2026-05-06T02:35:16.452Z","repository":{"id":77222621,"uuid":"89742273","full_name":"thekeenant/flow","owner":"thekeenant","description":"Seamlessly query and update SQL databases.","archived":false,"fork":false,"pushed_at":"2018-04-23T05:12:09.000Z","size":190,"stargazers_count":1,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-11T04:09:24.222Z","etag":null,"topics":["databases","java-8","library","mysql","sqlite"],"latest_commit_sha":null,"homepage":"","language":"Java","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/thekeenant.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2017-04-28T20:26:03.000Z","updated_at":"2021-07-19T05:30:59.000Z","dependencies_parsed_at":null,"dependency_job_id":"3bb0c0a9-89fe-43f1-9b0b-64d9af54a890","html_url":"https://github.com/thekeenant/flow","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/thekeenant/flow","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thekeenant%2Fflow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thekeenant%2Fflow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thekeenant%2Fflow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thekeenant%2Fflow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thekeenant","download_url":"https://codeload.github.com/thekeenant/flow/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thekeenant%2Fflow/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279006258,"owners_count":26084060,"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","status":"online","status_checked_at":"2025-10-11T02:00:06.511Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["databases","java-8","library","mysql","sqlite"],"created_at":"2024-11-09T00:47:54.339Z","updated_at":"2025-10-11T04:09:24.905Z","avatar_url":"https://github.com/thekeenant.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Flow \n[![Build Status](https://travis-ci.org/thekeenant/flow.svg?branch=master)](https://travis-ci.org/thekeenant/flow)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/ff96f2fbc3894502a5acb846f3813a26)](https://www.codacy.com/app/thekeenant/flow?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=thekeenant/flow\u0026amp;utm_campaign=Badge_Grade)\n\nA simple database library for Java that is SQL first. Flow is designed to be:\n\n* **Powerful**: Flow does not limit you in any respect. You can still do anything you want with your database,\n  even if that means writing raw SQL queries! With a single line you can perform complex operations \n  on a database, taking advantage of Java 8's `Stream`, and functional programming features.\n* **Simple**: Just add Flow as a dependency to your project and you are set. No fancy IDE\n  or setup is required. No code generation is needed. It is just a Java 8 library you can add to your project\n  with Maven, Gradle, etc.\n* **Familiar**: Operations in Java using Flow mimic SQL. This makes it easy to switch over to Flow and feel comfortable\nwhile using it.\n* **Safe**: It is more challenging to make syntactic mistakes when writing SQL queries using Flow. It is also easy to recognize errors\n  in queries during development and fix them before they cost your project or company time and/or money.\n* **Efficient**: Flow is a light-weight abstraction of JDBC. Thus, it inherently has increased time\n  and memory complexity, but it is very slight. The benefits above outweigh this small\n  cost for the vast majority of cases. If you need something more efficient, you are pretty much limited to bare-bones JDBC.\n\nFlow is not complete. It also lacks documentation, and as such, features will break upon every \nversion iteration until a full release.\n\n## Maven\n\nGradle works too!\n\n```xml\n\u003crepositories\u003e\n  \u003crepository\u003e\n    \u003cid\u003ejitpack.io\u003c/id\u003e\n    \u003curl\u003ehttps://jitpack.io\u003c/url\u003e\n  \u003c/repository\u003e\n\u003c/repositories\u003e\n\n\u003cdependencies\u003e\n  \u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.thekeenant.flow\u003c/groupId\u003e\n    \u003cartifactId\u003eflow\u003c/artifactId\u003e\n    \u003cversion\u003e0.1\u003c/version\u003e\n  \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n\n## Example\n\n```java\nAliasExp sectionCountAlias = alias(\"section_count\");\nSelectScoped query = db.\n    select(COURSES.UUID, max(COURSE_OFFERINGS.NAME), count(SECTIONS.NUMBER).as(sectionCountAlias))\n        .from(COURSES)\n        .join(COURSE_OFFERINGS.on(COURSE_OFFERINGS.COURSE_UUID.eq(COURSES.UUID)))\n        .join(SECTIONS.on(SECTIONS.COURSE_OFFERING_UUID.eq(COURSE_OFFERINGS.UUID)))\n        .groupBy(COURSES.UUID)\n        .having(sectionCountAlias.greaterThan(1000));\n\n```\n\nSQL Equivalent:\n\n```sql\nSELECT courses.uuid, MAX(course_offerings.name), COUNT(sections.number) AS section_count \nFROM courses \nJOIN course_offerings ON (course_offerings.course_uuid) = (courses.uuid) \nJOIN sections         ON (sections.course_offering_uuid) = (course_offerings.uuid)\nGROUP BY courses.uuid\nHAVING (section_count) \u003e (1000);\n```\n\nIterate:\n\n```java\ntry (Stream\u003cCursor\u003e stream = query.stream()) {\n  stream.forEach(cursor -\u003e {\n    String courseUuid = cursor.getNonNullString(1);\n    String courseName = cursor.getString(2).orElse(\"Unknown Name\");\n    int sectionCount = cursor.getNonNullNumber(3).intValue();\n\n    // ...\n  });\n}\n```\n\n\n## Roadmap\n\nSupported:\n* Select query\n    * Specify fields\n    * Nested queries\n    * `GROUP BY`, `HAVING`, `JOIN`, `WHERE`, `ORDER BY`,\n* Insert query\n    * Multi insert\n* Update query\n* Delete query\n* Databases (Tested)\n    * MySQL\n    * SQLite\n\nPlanned:\n* UNION/INTERSECT/EXCEPT\n* Create table query\n* Check Postgres/other SQL databases\n* Transactions\n\n## Introduction\n\nConnecting to a database is easy...\n\n\u003e _Note: We assume you have statically imported `com.keenant.flow.Flow.*`.\n\u003e This makes things much less verbose._\n\n```java\nDatabaseContext db = database(SQLDialect.SQLITE, \"jdbc:sqlite:sample.db\")\n\n// access/manipulate 'db' here...\n\ndb.close(); // finally, close the connection(s) to the database\n```\n\nWe tell Flow that this database speaks SQLite (as opposed to MySQL, or Postres for example).\nBehind the scenes, the `database(...)` method constructs a default `Connector` implementation\nthat provides one single connection to a database based on the URL provided. Don't forget to \nclose the database connection when you are done. This can be done with try-with-resources if\nyour prefer.\n\nLet's perform a simple raw SQL query.\n\n\u003e _Note: Try-with-resources in Java 7 makes it easy to cleanly close objects that implement `AutoCloseable`.\n\u003e If you are not using try-with-resources, you must manually call `.close()` on the cursor once you are done._\n\n```java\ntry (Cursor cursor = db.fetch(\"SELECT name FROM users WHERE id \u003c ? AND name = ?\", 10, \"Jonathan\")) {\n    while (cursor.moveNext()) {\n        String name = cursor.getNonNullString(1);\n        System.out.println(name);\n    }\n}\n```\n\nThis looks similar to JDBC's ResultSet, and it should. In order to maintain the performance of JDBC,\nFlow keeps this lower level interface available.\n\nThe above example, as a stream...\n\n```java\ntry (Stream\u003cCursor\u003e stream = db.fetch(\"SELECT name FROM users WHERE id \u003c ? AND name = ?\", 10, \"Jonathan\").stream()) {\n    stream.map(record -\u003e record.getNonNullString(1)).forEach(System.out::println);\n}\n```\n\nThis is pretty nifty, but with Flow you can forget about raw SQL queries. This makes it easier and safer\nto access and change database records.\n\n```java\n// These should probably be static, final somewhere in a \"table\"-like class.\nField users = field(\"users\");\nColumn\u003cInteger\u003e id = column(users, \"id\", Integer.class);\nColumn\u003cString\u003e name = column(users, \"name\", String.class);\n\nSelectScoped select = db.selectFrom(users).fields(name).where(id.lt(50).and(name.eq(\"Jonathan\")));\ntry (Stream\u003cCursor\u003e stream = select.fetch().stream()) {\n    stream.map(record -\u003e record.getNonNullString(1)).forEach(System.out::println);\n}\n```\n\nFilters were used above in the `where` clause: `id.lt(50).and(name.eq(\"Jonathan\"))`. They are pretty\nstraightforward. Here are some examples, with comments showing their SQL equivalent:\n\n```java\n// name = 'Adam'\nFilter adam = name.eq(\"Adam\");\n\n// age \u003c= 21\nFilter young = age.lte(21);\n\n// name = 'Adam' AND age \u003c= 21\nFilter youngAdam = adam.and(young);\n\n// (name = 'Adam' AND age \u003c= 21) OR age \u003e 75\nFilter youngAdamOrElderly = youngAdam.or(age.gt(75));\n\n// LENGTH(name) \u003e= 10\nFilter longNames = length(name).gte(10);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthekeenant%2Fflow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthekeenant%2Fflow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthekeenant%2Fflow/lists"}